1 | /* |
2 | * gtkappchooserwidget.c: an app-chooser widget |
3 | * |
4 | * Copyright (C) 2004 Novell, Inc. |
5 | * Copyright (C) 2007, 2010 Red Hat, Inc. |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Library General Public License as |
9 | * published by the Free Software Foundation; either version 2 of the |
10 | * License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Library General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Library General Public |
18 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
19 | * |
20 | * Authors: Dave Camp <dave@novell.com> |
21 | * Alexander Larsson <alexl@redhat.com> |
22 | * Cosimo Cecchi <ccecchi@redhat.com> |
23 | */ |
24 | |
25 | #include "config.h" |
26 | |
27 | #include "gtkappchooserwidget.h" |
28 | |
29 | #include "gtkintl.h" |
30 | #include "gtkmarshalers.h" |
31 | #include "gtkappchooserwidget.h" |
32 | #include "gtkappchooserprivate.h" |
33 | #include "gtkliststore.h" |
34 | #include "gtktreeview.h" |
35 | #include "gtktreeselection.h" |
36 | #include "gtktreemodelsort.h" |
37 | #include "gtkorientable.h" |
38 | #include "gtkscrolledwindow.h" |
39 | #include "gtklabel.h" |
40 | #include "gtkgestureclick.h" |
41 | #include "gtkwidgetprivate.h" |
42 | |
43 | #include <string.h> |
44 | #include <glib/gi18n-lib.h> |
45 | #include <gio/gio.h> |
46 | |
47 | /** |
48 | * GtkAppChooserWidget: |
49 | * |
50 | * `GtkAppChooserWidget` is a widget for selecting applications. |
51 | * |
52 | * It is the main building block for [class@Gtk.AppChooserDialog]. |
53 | * Most applications only need to use the latter; but you can use |
54 | * this widget as part of a larger widget if you have special needs. |
55 | * |
56 | * `GtkAppChooserWidget` offers detailed control over what applications |
57 | * are shown, using the |
58 | * [property@Gtk.AppChooserWidget:show-default], |
59 | * [property@Gtk.AppChooserWidget:show-recommended], |
60 | * [property@Gtk.AppChooserWidget:show-fallback], |
61 | * [property@Gtk.AppChooserWidget:show-other] and |
62 | * [property@Gtk.AppChooserWidget:show-all] properties. See the |
63 | * [iface@Gtk.AppChooser] documentation for more information about these |
64 | * groups of applications. |
65 | * |
66 | * To keep track of the selected application, use the |
67 | * [signal@Gtk.AppChooserWidget::application-selected] and |
68 | * [signal@Gtk.AppChooserWidget::application-activated] signals. |
69 | * |
70 | * # CSS nodes |
71 | * |
72 | * `GtkAppChooserWidget` has a single CSS node with name appchooser. |
73 | */ |
74 | |
75 | typedef struct _GtkAppChooserWidgetClass GtkAppChooserWidgetClass; |
76 | |
77 | struct _GtkAppChooserWidget { |
78 | GtkWidget parent_instance; |
79 | |
80 | GAppInfo *selected_app_info; |
81 | |
82 | GtkWidget *overlay; |
83 | |
84 | char *content_type; |
85 | char *default_text; |
86 | |
87 | guint show_default : 1; |
88 | guint show_recommended : 1; |
89 | guint show_fallback : 1; |
90 | guint show_other : 1; |
91 | guint show_all : 1; |
92 | |
93 | GtkWidget *program_list; |
94 | GtkListStore *program_list_store; |
95 | GtkWidget *no_apps_label; |
96 | GtkWidget *no_apps; |
97 | |
98 | GtkTreeViewColumn *column; |
99 | GtkCellRenderer *padding_renderer; |
100 | GtkCellRenderer *secondary_padding; |
101 | |
102 | GAppInfoMonitor *monitor; |
103 | |
104 | GtkWidget *; |
105 | }; |
106 | |
107 | struct _GtkAppChooserWidgetClass { |
108 | GtkWidgetClass parent_class; |
109 | |
110 | void (* application_selected) (GtkAppChooserWidget *self, |
111 | GAppInfo *app_info); |
112 | |
113 | void (* application_activated) (GtkAppChooserWidget *self, |
114 | GAppInfo *app_info); |
115 | }; |
116 | |
117 | enum { |
118 | COLUMN_APP_INFO, |
119 | COLUMN_GICON, |
120 | COLUMN_NAME, |
121 | COLUMN_DESC, |
122 | COLUMN_EXEC, |
123 | COLUMN_DEFAULT, |
124 | COLUMN_HEADING, |
125 | COLUMN_HEADING_TEXT, |
126 | COLUMN_RECOMMENDED, |
127 | COLUMN_FALLBACK, |
128 | NUM_COLUMNS |
129 | }; |
130 | |
131 | |
132 | enum { |
133 | PROP_CONTENT_TYPE = 1, |
134 | PROP_GFILE, |
135 | PROP_SHOW_DEFAULT, |
136 | PROP_SHOW_RECOMMENDED, |
137 | PROP_SHOW_FALLBACK, |
138 | PROP_SHOW_OTHER, |
139 | PROP_SHOW_ALL, |
140 | PROP_DEFAULT_TEXT, |
141 | N_PROPERTIES |
142 | }; |
143 | |
144 | enum { |
145 | SIGNAL_APPLICATION_SELECTED, |
146 | SIGNAL_APPLICATION_ACTIVATED, |
147 | N_SIGNALS |
148 | }; |
149 | |
150 | static guint signals[N_SIGNALS] = { 0, }; |
151 | |
152 | static void gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface); |
153 | |
154 | G_DEFINE_TYPE_WITH_CODE (GtkAppChooserWidget, gtk_app_chooser_widget, GTK_TYPE_WIDGET, |
155 | G_IMPLEMENT_INTERFACE (GTK_TYPE_APP_CHOOSER, |
156 | gtk_app_chooser_widget_iface_init)); |
157 | |
158 | static void |
159 | refresh_and_emit_app_selected (GtkAppChooserWidget *self, |
160 | GtkTreeSelection *selection) |
161 | { |
162 | GtkTreeModel *model; |
163 | GtkTreeIter iter; |
164 | GAppInfo *info = NULL; |
165 | gboolean should_emit = FALSE; |
166 | |
167 | if (gtk_tree_selection_get_selected (selection, model: &model, iter: &iter)) |
168 | gtk_tree_model_get (tree_model: model, iter: &iter, COLUMN_APP_INFO, &info, -1); |
169 | |
170 | if (info == NULL) |
171 | return; |
172 | |
173 | if (self->selected_app_info) |
174 | { |
175 | if (!g_app_info_equal (appinfo1: self->selected_app_info, appinfo2: info)) |
176 | { |
177 | should_emit = TRUE; |
178 | g_set_object (&self->selected_app_info, info); |
179 | } |
180 | } |
181 | else |
182 | { |
183 | should_emit = TRUE; |
184 | g_set_object (&self->selected_app_info, info); |
185 | } |
186 | |
187 | g_object_unref (object: info); |
188 | |
189 | if (should_emit) |
190 | g_signal_emit (instance: self, signal_id: signals[SIGNAL_APPLICATION_SELECTED], detail: 0, |
191 | self->selected_app_info); |
192 | } |
193 | |
194 | static gboolean |
195 | path_is_heading (GtkTreeView *view, |
196 | GtkTreePath *path) |
197 | { |
198 | GtkTreeIter iter; |
199 | GtkTreeModel *model; |
200 | gboolean res; |
201 | |
202 | model = gtk_tree_view_get_model (tree_view: view); |
203 | gtk_tree_model_get_iter (tree_model: model, iter: &iter, path); |
204 | gtk_tree_model_get (tree_model: model, iter: &iter, |
205 | COLUMN_HEADING, &res, |
206 | -1); |
207 | |
208 | return res; |
209 | } |
210 | |
211 | static void |
212 | program_list_selection_activated (GtkTreeView *view, |
213 | GtkTreePath *path, |
214 | GtkTreeViewColumn *column, |
215 | gpointer user_data) |
216 | { |
217 | GtkAppChooserWidget *self = user_data; |
218 | GtkTreeSelection *selection; |
219 | |
220 | if (path_is_heading (view, path)) |
221 | return; |
222 | |
223 | selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->program_list)); |
224 | |
225 | refresh_and_emit_app_selected (self, selection); |
226 | |
227 | g_signal_emit (instance: self, signal_id: signals[SIGNAL_APPLICATION_ACTIVATED], detail: 0, |
228 | self->selected_app_info); |
229 | } |
230 | |
231 | static gboolean |
232 | gtk_app_chooser_search_equal_func (GtkTreeModel *model, |
233 | int column, |
234 | const char *key, |
235 | GtkTreeIter *iter, |
236 | gpointer user_data) |
237 | { |
238 | char *name; |
239 | char *exec_name; |
240 | gboolean ret; |
241 | |
242 | if (key != NULL) |
243 | { |
244 | ret = TRUE; |
245 | |
246 | gtk_tree_model_get (tree_model: model, iter, |
247 | COLUMN_NAME, &name, |
248 | COLUMN_EXEC, &exec_name, |
249 | -1); |
250 | |
251 | if ((name != NULL && g_str_match_string (search_term: key, potential_hit: name, TRUE)) || |
252 | (exec_name != NULL && g_str_match_string (search_term: key, potential_hit: exec_name, FALSE))) |
253 | ret = FALSE; |
254 | |
255 | g_free (mem: name); |
256 | g_free (mem: exec_name); |
257 | |
258 | return ret; |
259 | } |
260 | else |
261 | { |
262 | return TRUE; |
263 | } |
264 | } |
265 | |
266 | static int |
267 | gtk_app_chooser_sort_func (GtkTreeModel *model, |
268 | GtkTreeIter *a, |
269 | GtkTreeIter *b, |
270 | gpointer user_data) |
271 | { |
272 | gboolean a_recommended, b_recommended; |
273 | gboolean a_fallback, b_fallback; |
274 | gboolean a_heading, b_heading; |
275 | gboolean a_default, b_default; |
276 | char *a_name, *b_name, *a_casefold, *b_casefold; |
277 | int retval = 0; |
278 | |
279 | /* this returns: |
280 | * - <0 if a should show before b |
281 | * - =0 if a is the same as b |
282 | * - >0 if a should show after b |
283 | */ |
284 | |
285 | gtk_tree_model_get (tree_model: model, iter: a, |
286 | COLUMN_NAME, &a_name, |
287 | COLUMN_RECOMMENDED, &a_recommended, |
288 | COLUMN_FALLBACK, &a_fallback, |
289 | COLUMN_HEADING, &a_heading, |
290 | COLUMN_DEFAULT, &a_default, |
291 | -1); |
292 | |
293 | gtk_tree_model_get (tree_model: model, iter: b, |
294 | COLUMN_NAME, &b_name, |
295 | COLUMN_RECOMMENDED, &b_recommended, |
296 | COLUMN_FALLBACK, &b_fallback, |
297 | COLUMN_HEADING, &b_heading, |
298 | COLUMN_DEFAULT, &b_default, |
299 | -1); |
300 | |
301 | /* the default one always wins */ |
302 | if (a_default && !b_default) |
303 | { |
304 | retval = -1; |
305 | goto out; |
306 | } |
307 | |
308 | if (b_default && !a_default) |
309 | { |
310 | retval = 1; |
311 | goto out; |
312 | } |
313 | |
314 | /* the recommended one always wins */ |
315 | if (a_recommended && !b_recommended) |
316 | { |
317 | retval = -1; |
318 | goto out; |
319 | } |
320 | |
321 | if (b_recommended && !a_recommended) |
322 | { |
323 | retval = 1; |
324 | goto out; |
325 | } |
326 | |
327 | /* the recommended one always wins */ |
328 | if (a_fallback && !b_fallback) |
329 | { |
330 | retval = -1; |
331 | goto out; |
332 | } |
333 | |
334 | if (b_fallback && !a_fallback) |
335 | { |
336 | retval = 1; |
337 | goto out; |
338 | } |
339 | |
340 | /* they're both recommended/fallback or not, so if one is a heading, wins */ |
341 | if (a_heading) |
342 | { |
343 | retval = -1; |
344 | goto out; |
345 | } |
346 | |
347 | if (b_heading) |
348 | { |
349 | retval = 1; |
350 | goto out; |
351 | } |
352 | |
353 | /* don't order by name recommended applications, but use GLib's ordering */ |
354 | if (!a_recommended) |
355 | { |
356 | a_casefold = a_name != NULL ? |
357 | g_utf8_casefold (str: a_name, len: -1) : NULL; |
358 | b_casefold = b_name != NULL ? |
359 | g_utf8_casefold (str: b_name, len: -1) : NULL; |
360 | |
361 | retval = g_strcmp0 (str1: a_casefold, str2: b_casefold); |
362 | |
363 | g_free (mem: a_casefold); |
364 | g_free (mem: b_casefold); |
365 | } |
366 | |
367 | out: |
368 | g_free (mem: a_name); |
369 | g_free (mem: b_name); |
370 | |
371 | return retval; |
372 | } |
373 | |
374 | static void |
375 | padding_cell_renderer_func (GtkTreeViewColumn *column, |
376 | GtkCellRenderer *cell, |
377 | GtkTreeModel *model, |
378 | GtkTreeIter *iter, |
379 | gpointer user_data) |
380 | { |
381 | gboolean heading; |
382 | |
383 | gtk_tree_model_get (tree_model: model, iter, |
384 | COLUMN_HEADING, &heading, |
385 | -1); |
386 | if (heading) |
387 | g_object_set (object: cell, |
388 | first_property_name: "visible" , FALSE, |
389 | "xpad" , 0, |
390 | "ypad" , 0, |
391 | NULL); |
392 | else |
393 | g_object_set (object: cell, |
394 | first_property_name: "visible" , TRUE, |
395 | "xpad" , 3, |
396 | "ypad" , 3, |
397 | NULL); |
398 | } |
399 | |
400 | static gboolean |
401 | gtk_app_chooser_selection_func (GtkTreeSelection *selection, |
402 | GtkTreeModel *model, |
403 | GtkTreePath *path, |
404 | gboolean path_currently_selected, |
405 | gpointer user_data) |
406 | { |
407 | GtkTreeIter iter; |
408 | gboolean heading; |
409 | |
410 | gtk_tree_model_get_iter (tree_model: model, iter: &iter, path); |
411 | gtk_tree_model_get (tree_model: model, iter: &iter, |
412 | COLUMN_HEADING, &heading, |
413 | -1); |
414 | |
415 | return !heading; |
416 | } |
417 | |
418 | static int |
419 | compare_apps_func (gconstpointer a, |
420 | gconstpointer b) |
421 | { |
422 | return !g_app_info_equal (G_APP_INFO (a), G_APP_INFO (b)); |
423 | } |
424 | |
425 | static gboolean |
426 | gtk_app_chooser_widget_add_section (GtkAppChooserWidget *self, |
427 | const char *heading_title, |
428 | gboolean show_headings, |
429 | gboolean recommended, |
430 | gboolean fallback, |
431 | GList *applications, |
432 | GList *exclude_apps) |
433 | { |
434 | gboolean heading_added, unref_icon; |
435 | GtkTreeIter iter; |
436 | GAppInfo *app; |
437 | char *app_string, *bold_string; |
438 | GIcon *icon; |
439 | GList *l; |
440 | gboolean retval; |
441 | |
442 | retval = FALSE; |
443 | heading_added = FALSE; |
444 | bold_string = g_strdup_printf (format: "<b>%s</b>" , heading_title); |
445 | |
446 | for (l = applications; l != NULL; l = l->next) |
447 | { |
448 | app = l->data; |
449 | |
450 | if (self->content_type != NULL && |
451 | !g_app_info_supports_uris (appinfo: app) && |
452 | !g_app_info_supports_files (appinfo: app)) |
453 | continue; |
454 | |
455 | if (g_list_find_custom (list: exclude_apps, data: app, |
456 | func: (GCompareFunc) compare_apps_func)) |
457 | continue; |
458 | |
459 | if (!heading_added && show_headings) |
460 | { |
461 | gtk_list_store_append (list_store: self->program_list_store, iter: &iter); |
462 | gtk_list_store_set (list_store: self->program_list_store, iter: &iter, |
463 | COLUMN_HEADING_TEXT, bold_string, |
464 | COLUMN_HEADING, TRUE, |
465 | COLUMN_RECOMMENDED, recommended, |
466 | COLUMN_FALLBACK, fallback, |
467 | -1); |
468 | |
469 | heading_added = TRUE; |
470 | } |
471 | |
472 | app_string = g_markup_printf_escaped (format: "%s" , |
473 | g_app_info_get_name (appinfo: app) != NULL ? |
474 | g_app_info_get_name (appinfo: app) : "" ); |
475 | |
476 | icon = g_app_info_get_icon (appinfo: app); |
477 | unref_icon = FALSE; |
478 | if (icon == NULL) |
479 | { |
480 | icon = g_themed_icon_new (iconname: "application-x-executable" ); |
481 | unref_icon = TRUE; |
482 | } |
483 | |
484 | gtk_list_store_append (list_store: self->program_list_store, iter: &iter); |
485 | gtk_list_store_set (list_store: self->program_list_store, iter: &iter, |
486 | COLUMN_APP_INFO, app, |
487 | COLUMN_GICON, icon, |
488 | COLUMN_NAME, g_app_info_get_name (appinfo: app), |
489 | COLUMN_DESC, app_string, |
490 | COLUMN_EXEC, g_app_info_get_executable (appinfo: app), |
491 | COLUMN_HEADING, FALSE, |
492 | COLUMN_RECOMMENDED, recommended, |
493 | COLUMN_FALLBACK, fallback, |
494 | -1); |
495 | |
496 | retval = TRUE; |
497 | |
498 | g_free (mem: app_string); |
499 | if (unref_icon) |
500 | g_object_unref (object: icon); |
501 | } |
502 | |
503 | g_free (mem: bold_string); |
504 | |
505 | return retval; |
506 | } |
507 | |
508 | |
509 | static void |
510 | gtk_app_chooser_add_default (GtkAppChooserWidget *self, |
511 | GAppInfo *app) |
512 | { |
513 | GtkTreeIter iter; |
514 | GIcon *icon; |
515 | char *string; |
516 | gboolean unref_icon; |
517 | |
518 | unref_icon = FALSE; |
519 | string = g_strdup_printf (format: "<b>%s</b>" , _("Default Application" )); |
520 | |
521 | gtk_list_store_append (list_store: self->program_list_store, iter: &iter); |
522 | gtk_list_store_set (list_store: self->program_list_store, iter: &iter, |
523 | COLUMN_HEADING_TEXT, string, |
524 | COLUMN_HEADING, TRUE, |
525 | COLUMN_DEFAULT, TRUE, |
526 | -1); |
527 | |
528 | g_free (mem: string); |
529 | |
530 | string = g_markup_printf_escaped (format: "%s" , |
531 | g_app_info_get_name (appinfo: app) != NULL ? |
532 | g_app_info_get_name (appinfo: app) : "" ); |
533 | |
534 | icon = g_app_info_get_icon (appinfo: app); |
535 | if (icon == NULL) |
536 | { |
537 | icon = g_themed_icon_new (iconname: "application-x-executable" ); |
538 | unref_icon = TRUE; |
539 | } |
540 | |
541 | gtk_list_store_append (list_store: self->program_list_store, iter: &iter); |
542 | gtk_list_store_set (list_store: self->program_list_store, iter: &iter, |
543 | COLUMN_APP_INFO, app, |
544 | COLUMN_GICON, icon, |
545 | COLUMN_NAME, g_app_info_get_name (appinfo: app), |
546 | COLUMN_DESC, string, |
547 | COLUMN_EXEC, g_app_info_get_executable (appinfo: app), |
548 | COLUMN_HEADING, FALSE, |
549 | COLUMN_DEFAULT, TRUE, |
550 | -1); |
551 | |
552 | g_free (mem: string); |
553 | |
554 | if (unref_icon) |
555 | g_object_unref (object: icon); |
556 | } |
557 | |
558 | static void |
559 | update_no_applications_label (GtkAppChooserWidget *self) |
560 | { |
561 | char *text = NULL, *desc = NULL; |
562 | const char *string; |
563 | |
564 | if (self->default_text == NULL) |
565 | { |
566 | if (self->content_type) |
567 | desc = g_content_type_get_description (type: self->content_type); |
568 | |
569 | string = text = g_strdup_printf (_("No applications found for “%s”." ), desc); |
570 | g_free (mem: desc); |
571 | } |
572 | else |
573 | { |
574 | string = self->default_text; |
575 | } |
576 | |
577 | gtk_label_set_text (GTK_LABEL (self->no_apps_label), str: string); |
578 | |
579 | g_free (mem: text); |
580 | } |
581 | |
582 | static void |
583 | gtk_app_chooser_widget_select_first (GtkAppChooserWidget *self) |
584 | { |
585 | GtkTreeIter iter; |
586 | GAppInfo *info = NULL; |
587 | GtkTreeModel *model; |
588 | |
589 | model = gtk_tree_view_get_model (GTK_TREE_VIEW (self->program_list)); |
590 | if (!gtk_tree_model_get_iter_first (tree_model: model, iter: &iter)) |
591 | return; |
592 | |
593 | while (info == NULL) |
594 | { |
595 | gtk_tree_model_get (tree_model: model, iter: &iter, |
596 | COLUMN_APP_INFO, &info, |
597 | -1); |
598 | |
599 | if (info != NULL) |
600 | break; |
601 | |
602 | if (!gtk_tree_model_iter_next (tree_model: model, iter: &iter)) |
603 | break; |
604 | } |
605 | |
606 | if (info != NULL) |
607 | { |
608 | GtkTreeSelection *selection; |
609 | |
610 | selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->program_list)); |
611 | gtk_tree_selection_select_iter (selection, iter: &iter); |
612 | |
613 | g_object_unref (object: info); |
614 | } |
615 | } |
616 | |
617 | static void |
618 | gtk_app_chooser_widget_real_add_items (GtkAppChooserWidget *self) |
619 | { |
620 | GList *all_applications = NULL; |
621 | GList *recommended_apps = NULL; |
622 | GList *fallback_apps = NULL; |
623 | GList *exclude_apps = NULL; |
624 | GAppInfo *default_app = NULL; |
625 | gboolean show_headings; |
626 | gboolean apps_added; |
627 | |
628 | show_headings = TRUE; |
629 | apps_added = FALSE; |
630 | |
631 | if (self->show_all) |
632 | show_headings = FALSE; |
633 | |
634 | if (self->show_default && self->content_type) |
635 | { |
636 | default_app = g_app_info_get_default_for_type (content_type: self->content_type, FALSE); |
637 | |
638 | if (default_app != NULL) |
639 | { |
640 | gtk_app_chooser_add_default (self, app: default_app); |
641 | apps_added = TRUE; |
642 | exclude_apps = g_list_prepend (list: exclude_apps, data: default_app); |
643 | } |
644 | } |
645 | |
646 | #ifndef G_OS_WIN32 |
647 | if ((self->content_type && self->show_recommended) || self->show_all) |
648 | { |
649 | if (self->content_type) |
650 | recommended_apps = g_app_info_get_recommended_for_type (content_type: self->content_type); |
651 | |
652 | apps_added |= gtk_app_chooser_widget_add_section (self, _("Recommended Applications" ), |
653 | show_headings, |
654 | recommended: !self->show_all, /* mark as recommended */ |
655 | FALSE, /* mark as fallback */ |
656 | applications: recommended_apps, exclude_apps); |
657 | |
658 | exclude_apps = g_list_concat (list1: exclude_apps, |
659 | list2: g_list_copy (list: recommended_apps)); |
660 | } |
661 | |
662 | if ((self->content_type && self->show_fallback) || self->show_all) |
663 | { |
664 | if (self->content_type) |
665 | fallback_apps = g_app_info_get_fallback_for_type (content_type: self->content_type); |
666 | |
667 | apps_added |= gtk_app_chooser_widget_add_section (self, _("Related Applications" ), |
668 | show_headings, |
669 | FALSE, /* mark as recommended */ |
670 | fallback: !self->show_all, /* mark as fallback */ |
671 | applications: fallback_apps, exclude_apps); |
672 | exclude_apps = g_list_concat (list1: exclude_apps, |
673 | list2: g_list_copy (list: fallback_apps)); |
674 | } |
675 | #endif |
676 | |
677 | if (self->show_other || self->show_all) |
678 | { |
679 | all_applications = g_app_info_get_all (); |
680 | |
681 | apps_added |= gtk_app_chooser_widget_add_section (self, _("Other Applications" ), |
682 | show_headings, |
683 | FALSE, |
684 | FALSE, |
685 | applications: all_applications, exclude_apps); |
686 | } |
687 | |
688 | if (!apps_added) |
689 | update_no_applications_label (self); |
690 | |
691 | gtk_widget_set_visible (widget: self->no_apps, visible: !apps_added); |
692 | |
693 | gtk_app_chooser_widget_select_first (self); |
694 | |
695 | if (default_app != NULL) |
696 | g_object_unref (object: default_app); |
697 | |
698 | g_list_free_full (list: all_applications, free_func: g_object_unref); |
699 | g_list_free_full (list: recommended_apps, free_func: g_object_unref); |
700 | g_list_free_full (list: fallback_apps, free_func: g_object_unref); |
701 | g_list_free (list: exclude_apps); |
702 | } |
703 | |
704 | static void |
705 | gtk_app_chooser_widget_initialize_items (GtkAppChooserWidget *self) |
706 | { |
707 | /* initial padding */ |
708 | g_object_set (object: self->padding_renderer, |
709 | first_property_name: "xpad" , self->show_all ? 0 : 6, |
710 | NULL); |
711 | |
712 | /* populate the widget */ |
713 | gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); |
714 | } |
715 | |
716 | static void |
717 | app_info_changed (GAppInfoMonitor *monitor, |
718 | GtkAppChooserWidget *self) |
719 | { |
720 | gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); |
721 | } |
722 | |
723 | static void |
724 | gtk_app_chooser_widget_set_property (GObject *object, |
725 | guint property_id, |
726 | const GValue *value, |
727 | GParamSpec *pspec) |
728 | { |
729 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); |
730 | |
731 | switch (property_id) |
732 | { |
733 | case PROP_CONTENT_TYPE: |
734 | self->content_type = g_value_dup_string (value); |
735 | break; |
736 | case PROP_SHOW_DEFAULT: |
737 | gtk_app_chooser_widget_set_show_default (self, setting: g_value_get_boolean (value)); |
738 | break; |
739 | case PROP_SHOW_RECOMMENDED: |
740 | gtk_app_chooser_widget_set_show_recommended (self, setting: g_value_get_boolean (value)); |
741 | break; |
742 | case PROP_SHOW_FALLBACK: |
743 | gtk_app_chooser_widget_set_show_fallback (self, setting: g_value_get_boolean (value)); |
744 | break; |
745 | case PROP_SHOW_OTHER: |
746 | gtk_app_chooser_widget_set_show_other (self, setting: g_value_get_boolean (value)); |
747 | break; |
748 | case PROP_SHOW_ALL: |
749 | gtk_app_chooser_widget_set_show_all (self, setting: g_value_get_boolean (value)); |
750 | break; |
751 | case PROP_DEFAULT_TEXT: |
752 | gtk_app_chooser_widget_set_default_text (self, text: g_value_get_string (value)); |
753 | break; |
754 | default: |
755 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
756 | break; |
757 | } |
758 | } |
759 | |
760 | static void |
761 | gtk_app_chooser_widget_get_property (GObject *object, |
762 | guint property_id, |
763 | GValue *value, |
764 | GParamSpec *pspec) |
765 | { |
766 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); |
767 | |
768 | switch (property_id) |
769 | { |
770 | case PROP_CONTENT_TYPE: |
771 | g_value_set_string (value, v_string: self->content_type); |
772 | break; |
773 | case PROP_SHOW_DEFAULT: |
774 | g_value_set_boolean (value, v_boolean: self->show_default); |
775 | break; |
776 | case PROP_SHOW_RECOMMENDED: |
777 | g_value_set_boolean (value, v_boolean: self->show_recommended); |
778 | break; |
779 | case PROP_SHOW_FALLBACK: |
780 | g_value_set_boolean (value, v_boolean: self->show_fallback); |
781 | break; |
782 | case PROP_SHOW_OTHER: |
783 | g_value_set_boolean (value, v_boolean: self->show_other); |
784 | break; |
785 | case PROP_SHOW_ALL: |
786 | g_value_set_boolean (value, v_boolean: self->show_all); |
787 | break; |
788 | case PROP_DEFAULT_TEXT: |
789 | g_value_set_string (value, v_string: self->default_text); |
790 | break; |
791 | default: |
792 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
793 | break; |
794 | } |
795 | } |
796 | |
797 | static void |
798 | gtk_app_chooser_widget_constructed (GObject *object) |
799 | { |
800 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); |
801 | |
802 | if (G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed != NULL) |
803 | G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->constructed (object); |
804 | |
805 | gtk_app_chooser_widget_initialize_items (self); |
806 | } |
807 | |
808 | static void |
809 | gtk_app_chooser_widget_finalize (GObject *object) |
810 | { |
811 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); |
812 | |
813 | g_free (mem: self->content_type); |
814 | g_free (mem: self->default_text); |
815 | g_signal_handlers_disconnect_by_func (self->monitor, app_info_changed, self); |
816 | g_object_unref (object: self->monitor); |
817 | |
818 | G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->finalize (object); |
819 | } |
820 | |
821 | static void |
822 | gtk_app_chooser_widget_dispose (GObject *object) |
823 | { |
824 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); |
825 | |
826 | g_clear_object (&self->selected_app_info); |
827 | |
828 | if (self->overlay) |
829 | { |
830 | gtk_widget_unparent (widget: self->overlay); |
831 | self->overlay = NULL; |
832 | } |
833 | |
834 | G_OBJECT_CLASS (gtk_app_chooser_widget_parent_class)->dispose (object); |
835 | } |
836 | |
837 | static void |
838 | gtk_app_chooser_widget_measure (GtkWidget *widget, |
839 | GtkOrientation orientation, |
840 | int for_size, |
841 | int *minimum, |
842 | int *natural, |
843 | int *minimum_baseline, |
844 | int *natural_baseline) |
845 | { |
846 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (widget); |
847 | |
848 | gtk_widget_measure (widget: self->overlay, orientation, for_size, |
849 | minimum, natural, |
850 | minimum_baseline, natural_baseline); |
851 | } |
852 | |
853 | static void |
854 | gtk_app_chooser_widget_snapshot (GtkWidget *widget, |
855 | GtkSnapshot *snapshot) |
856 | { |
857 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (widget); |
858 | |
859 | gtk_widget_snapshot_child (widget, child: self->overlay, snapshot); |
860 | } |
861 | |
862 | static void |
863 | gtk_app_chooser_widget_size_allocate (GtkWidget *widget, |
864 | int width, |
865 | int height, |
866 | int baseline) |
867 | { |
868 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (widget); |
869 | |
870 | GTK_WIDGET_CLASS (gtk_app_chooser_widget_parent_class)->size_allocate (widget, width, height, baseline); |
871 | |
872 | gtk_widget_size_allocate (widget: self->overlay, |
873 | allocation: &(GtkAllocation) { |
874 | 0, 0, |
875 | width, height |
876 | },baseline); |
877 | } |
878 | |
879 | static void |
880 | gtk_app_chooser_widget_class_init (GtkAppChooserWidgetClass *klass) |
881 | { |
882 | GtkWidgetClass *widget_class; |
883 | GObjectClass *gobject_class; |
884 | GParamSpec *pspec; |
885 | |
886 | gobject_class = G_OBJECT_CLASS (klass); |
887 | gobject_class->dispose = gtk_app_chooser_widget_dispose; |
888 | gobject_class->finalize = gtk_app_chooser_widget_finalize; |
889 | gobject_class->set_property = gtk_app_chooser_widget_set_property; |
890 | gobject_class->get_property = gtk_app_chooser_widget_get_property; |
891 | gobject_class->constructed = gtk_app_chooser_widget_constructed; |
892 | |
893 | widget_class = GTK_WIDGET_CLASS (klass); |
894 | widget_class->measure = gtk_app_chooser_widget_measure; |
895 | widget_class->size_allocate = gtk_app_chooser_widget_size_allocate; |
896 | widget_class->snapshot = gtk_app_chooser_widget_snapshot; |
897 | |
898 | g_object_class_override_property (oclass: gobject_class, property_id: PROP_CONTENT_TYPE, name: "content-type" ); |
899 | |
900 | /** |
901 | * GtkAppChooserWidget:show-default: (attributes org.gtk.Property.get=gtk_app_chooser_widget_get_show_default org.gtk.Property.set=gtk_app_chooser_widget_set_show_default) |
902 | * |
903 | * Determines whether the app chooser should show the default |
904 | * handler for the content type in a separate section. |
905 | * |
906 | * If %FALSE, the default handler is listed among the recommended |
907 | * applications. |
908 | */ |
909 | pspec = g_param_spec_boolean (name: "show-default" , |
910 | P_("Show default app" ), |
911 | P_("Whether the widget should show the default application" ), |
912 | FALSE, |
913 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
914 | g_object_class_install_property (oclass: gobject_class, property_id: PROP_SHOW_DEFAULT, pspec); |
915 | |
916 | /** |
917 | * GtkAppChooserWidget:show-recommended: (attributes org.gtk.Property.get=gtk_app_chooser_widget_get_show_recommended org.gtk.Property.set=gtk_app_chooser_widget_set_show_recommended) |
918 | * |
919 | * Determines whether the app chooser should show a section |
920 | * for recommended applications. |
921 | * |
922 | * If %FALSE, the recommended applications are listed |
923 | * among the other applications. |
924 | */ |
925 | pspec = g_param_spec_boolean (name: "show-recommended" , |
926 | P_("Show recommended apps" ), |
927 | P_("Whether the widget should show recommended applications" ), |
928 | TRUE, |
929 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
930 | g_object_class_install_property (oclass: gobject_class, property_id: PROP_SHOW_RECOMMENDED, pspec); |
931 | |
932 | /** |
933 | * GtkAppChooserWidget:show-fallback: (attributes org.gtk.Property.get=gtk_app_chooser_widget_get_show_fallback org.gtk.Property.set=gtk_app_chooser_widget_set_show_fallback) |
934 | * |
935 | * Determines whether the app chooser should show a section |
936 | * for fallback applications. |
937 | * |
938 | * If %FALSE, the fallback applications are listed among the |
939 | * other applications. |
940 | */ |
941 | pspec = g_param_spec_boolean (name: "show-fallback" , |
942 | P_("Show fallback apps" ), |
943 | P_("Whether the widget should show fallback applications" ), |
944 | FALSE, |
945 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
946 | g_object_class_install_property (oclass: gobject_class, property_id: PROP_SHOW_FALLBACK, pspec); |
947 | |
948 | /** |
949 | * GtkAppChooserWidget:show-other: (attributes org.gtk.Property.get=gtk_app_chooser_widget_get_show_other org.gtk.Property.set=gtk_app_chooser_widget_set_show_other) |
950 | * |
951 | * Determines whether the app chooser should show a section |
952 | * for other applications. |
953 | */ |
954 | pspec = g_param_spec_boolean (name: "show-other" , |
955 | P_("Show other apps" ), |
956 | P_("Whether the widget should show other applications" ), |
957 | FALSE, |
958 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
959 | g_object_class_install_property (oclass: gobject_class, property_id: PROP_SHOW_OTHER, pspec); |
960 | |
961 | /** |
962 | * GtkAppChooserWidget:show-all: (attributes org.gtk.Property.get=gtk_app_chooser_widget_get_show_all org.gtk.Property.set=gtk_app_chooser_widget_set_show_all) |
963 | * |
964 | * If %TRUE, the app chooser presents all applications |
965 | * in a single list, without subsections for default, |
966 | * recommended or related applications. |
967 | */ |
968 | pspec = g_param_spec_boolean (name: "show-all" , |
969 | P_("Show all apps" ), |
970 | P_("Whether the widget should show all applications" ), |
971 | FALSE, |
972 | flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
973 | g_object_class_install_property (oclass: gobject_class, property_id: PROP_SHOW_ALL, pspec); |
974 | |
975 | /** |
976 | * GtkAppChooserWidget:default-text: (attributes org.gtk.Property.get=gtk_app_chooser_widget_get_default_text org.gtk.Property.set=gtk_app_chooser_widget_set_default_text) |
977 | * |
978 | * The text that appears in the widget when there are no applications |
979 | * for the given content type. |
980 | */ |
981 | pspec = g_param_spec_string (name: "default-text" , |
982 | P_("Widget’s default text" ), |
983 | P_("The default text appearing when there are no applications" ), |
984 | NULL, |
985 | flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
986 | g_object_class_install_property (oclass: gobject_class, property_id: PROP_DEFAULT_TEXT, pspec); |
987 | |
988 | /** |
989 | * GtkAppChooserWidget::application-selected: |
990 | * @self: the object which received the signal |
991 | * @application: the selected `GAppInfo` |
992 | * |
993 | * Emitted when an application item is selected from the widget's list. |
994 | */ |
995 | signals[SIGNAL_APPLICATION_SELECTED] = |
996 | g_signal_new (I_("application-selected" ), |
997 | GTK_TYPE_APP_CHOOSER_WIDGET, |
998 | signal_flags: G_SIGNAL_RUN_FIRST, |
999 | G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_selected), |
1000 | NULL, NULL, |
1001 | NULL, |
1002 | G_TYPE_NONE, |
1003 | n_params: 1, G_TYPE_APP_INFO); |
1004 | |
1005 | /** |
1006 | * GtkAppChooserWidget::application-activated: |
1007 | * @self: the object which received the signal |
1008 | * @application: the activated `GAppInfo` |
1009 | * |
1010 | * Emitted when an application item is activated from the widget's list. |
1011 | * |
1012 | * This usually happens when the user double clicks an item, or an item |
1013 | * is selected and the user presses one of the keys Space, Shift+Space, |
1014 | * Return or Enter. |
1015 | */ |
1016 | signals[SIGNAL_APPLICATION_ACTIVATED] = |
1017 | g_signal_new (I_("application-activated" ), |
1018 | GTK_TYPE_APP_CHOOSER_WIDGET, |
1019 | signal_flags: G_SIGNAL_RUN_FIRST, |
1020 | G_STRUCT_OFFSET (GtkAppChooserWidgetClass, application_activated), |
1021 | NULL, NULL, |
1022 | NULL, |
1023 | G_TYPE_NONE, |
1024 | n_params: 1, G_TYPE_APP_INFO); |
1025 | |
1026 | /* Bind class to template |
1027 | */ |
1028 | gtk_widget_class_set_template_from_resource (widget_class, |
1029 | resource_name: "/org/gtk/libgtk/ui/gtkappchooserwidget.ui" ); |
1030 | gtk_widget_class_bind_template_child (widget_class, GtkAppChooserWidget, program_list); |
1031 | gtk_widget_class_bind_template_child (widget_class, GtkAppChooserWidget, program_list_store); |
1032 | gtk_widget_class_bind_template_child (widget_class, GtkAppChooserWidget, column); |
1033 | gtk_widget_class_bind_template_child (widget_class, GtkAppChooserWidget, padding_renderer); |
1034 | gtk_widget_class_bind_template_child (widget_class, GtkAppChooserWidget, secondary_padding); |
1035 | gtk_widget_class_bind_template_child (widget_class, GtkAppChooserWidget, no_apps_label); |
1036 | gtk_widget_class_bind_template_child (widget_class, GtkAppChooserWidget, no_apps); |
1037 | gtk_widget_class_bind_template_child (widget_class, GtkAppChooserWidget, overlay); |
1038 | gtk_widget_class_bind_template_callback (widget_class, refresh_and_emit_app_selected); |
1039 | gtk_widget_class_bind_template_callback (widget_class, program_list_selection_activated); |
1040 | |
1041 | gtk_widget_class_set_css_name (widget_class, I_("appchooser" )); |
1042 | } |
1043 | |
1044 | static void |
1045 | gtk_app_chooser_widget_init (GtkAppChooserWidget *self) |
1046 | { |
1047 | GtkTreeSelection *selection; |
1048 | GtkTreeModel *sort; |
1049 | |
1050 | gtk_widget_init_template (GTK_WIDGET (self)); |
1051 | |
1052 | /* Various parts of the GtkTreeView code need custom code to setup, mostly |
1053 | * because we lack signals to connect to, or properties to set. |
1054 | */ |
1055 | selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->program_list)); |
1056 | gtk_tree_selection_set_select_function (selection, func: gtk_app_chooser_selection_func, |
1057 | data: self, NULL); |
1058 | |
1059 | sort = gtk_tree_view_get_model (GTK_TREE_VIEW (self->program_list)); |
1060 | gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort), |
1061 | sort_column_id: COLUMN_NAME, |
1062 | order: GTK_SORT_ASCENDING); |
1063 | gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort), |
1064 | sort_column_id: COLUMN_NAME, |
1065 | sort_func: gtk_app_chooser_sort_func, |
1066 | user_data: self, NULL); |
1067 | |
1068 | gtk_tree_view_set_search_column (GTK_TREE_VIEW (self->program_list), column: COLUMN_NAME); |
1069 | gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (self->program_list), |
1070 | search_equal_func: gtk_app_chooser_search_equal_func, |
1071 | NULL, NULL); |
1072 | |
1073 | gtk_tree_view_column_set_cell_data_func (tree_column: self->column, |
1074 | cell_renderer: self->secondary_padding, |
1075 | func: padding_cell_renderer_func, |
1076 | NULL, NULL); |
1077 | |
1078 | self->monitor = g_app_info_monitor_get (); |
1079 | g_signal_connect (self->monitor, "changed" , |
1080 | G_CALLBACK (app_info_changed), self); |
1081 | } |
1082 | |
1083 | static GAppInfo * |
1084 | gtk_app_chooser_widget_get_app_info (GtkAppChooser *object) |
1085 | { |
1086 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); |
1087 | |
1088 | if (self->selected_app_info == NULL) |
1089 | return NULL; |
1090 | |
1091 | return g_object_ref (self->selected_app_info); |
1092 | } |
1093 | |
1094 | static void |
1095 | gtk_app_chooser_widget_refresh (GtkAppChooser *object) |
1096 | { |
1097 | GtkAppChooserWidget *self = GTK_APP_CHOOSER_WIDGET (object); |
1098 | |
1099 | if (self->program_list_store != NULL) |
1100 | { |
1101 | gtk_list_store_clear (list_store: self->program_list_store); |
1102 | |
1103 | /* don't add additional xpad if we don't have headings */ |
1104 | g_object_set (object: self->padding_renderer, |
1105 | first_property_name: "visible" , !self->show_all, |
1106 | NULL); |
1107 | |
1108 | gtk_app_chooser_widget_real_add_items (self); |
1109 | } |
1110 | } |
1111 | |
1112 | static void |
1113 | gtk_app_chooser_widget_iface_init (GtkAppChooserIface *iface) |
1114 | { |
1115 | iface->get_app_info = gtk_app_chooser_widget_get_app_info; |
1116 | iface->refresh = gtk_app_chooser_widget_refresh; |
1117 | } |
1118 | |
1119 | /** |
1120 | * gtk_app_chooser_widget_new: |
1121 | * @content_type: the content type to show applications for |
1122 | * |
1123 | * Creates a new `GtkAppChooserWidget` for applications |
1124 | * that can handle content of the given type. |
1125 | * |
1126 | * Returns: a newly created `GtkAppChooserWidget` |
1127 | */ |
1128 | GtkWidget * |
1129 | gtk_app_chooser_widget_new (const char *content_type) |
1130 | { |
1131 | return g_object_new (GTK_TYPE_APP_CHOOSER_WIDGET, |
1132 | first_property_name: "content-type" , content_type, |
1133 | NULL); |
1134 | } |
1135 | |
1136 | /** |
1137 | * gtk_app_chooser_widget_set_show_default: (attributes org.gtk.Method.set_property=show-default) |
1138 | * @self: a `GtkAppChooserWidget` |
1139 | * @setting: the new value for [property@Gtk.AppChooserWidget:show-default] |
1140 | * |
1141 | * Sets whether the app chooser should show the default handler |
1142 | * for the content type in a separate section. |
1143 | */ |
1144 | void |
1145 | gtk_app_chooser_widget_set_show_default (GtkAppChooserWidget *self, |
1146 | gboolean setting) |
1147 | { |
1148 | g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); |
1149 | |
1150 | if (self->show_default != setting) |
1151 | { |
1152 | self->show_default = setting; |
1153 | |
1154 | g_object_notify (G_OBJECT (self), property_name: "show-default" ); |
1155 | |
1156 | gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); |
1157 | } |
1158 | } |
1159 | |
1160 | /** |
1161 | * gtk_app_chooser_widget_get_show_default: (attributes org.gtk.Method.get_property=show-default) |
1162 | * @self: a `GtkAppChooserWidget` |
1163 | * |
1164 | * Gets whether the app chooser should show the default handler |
1165 | * for the content type in a separate section. |
1166 | * |
1167 | * Returns: the value of [property@Gtk.AppChooserWidget:show-default] |
1168 | */ |
1169 | gboolean |
1170 | gtk_app_chooser_widget_get_show_default (GtkAppChooserWidget *self) |
1171 | { |
1172 | g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); |
1173 | |
1174 | return self->show_default; |
1175 | } |
1176 | |
1177 | /** |
1178 | * gtk_app_chooser_widget_set_show_recommended: (attributes org.gtk.Method.set_property=show-recommended) |
1179 | * @self: a `GtkAppChooserWidget` |
1180 | * @setting: the new value for [property@Gtk.AppChooserWidget:show-recommended] |
1181 | * |
1182 | * Sets whether the app chooser should show recommended applications |
1183 | * for the content type in a separate section. |
1184 | */ |
1185 | void |
1186 | gtk_app_chooser_widget_set_show_recommended (GtkAppChooserWidget *self, |
1187 | gboolean setting) |
1188 | { |
1189 | g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); |
1190 | |
1191 | if (self->show_recommended != setting) |
1192 | { |
1193 | self->show_recommended = setting; |
1194 | |
1195 | g_object_notify (G_OBJECT (self), property_name: "show-recommended" ); |
1196 | |
1197 | gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); |
1198 | } |
1199 | } |
1200 | |
1201 | /** |
1202 | * gtk_app_chooser_widget_get_show_recommended: (attributes org.gtk.Method.get_property=show-recommended) |
1203 | * @self: a `GtkAppChooserWidget` |
1204 | * |
1205 | * Gets whether the app chooser should show recommended applications |
1206 | * for the content type in a separate section. |
1207 | * |
1208 | * Returns: the value of [property@Gtk.AppChooserWidget:show-recommended] |
1209 | */ |
1210 | gboolean |
1211 | gtk_app_chooser_widget_get_show_recommended (GtkAppChooserWidget *self) |
1212 | { |
1213 | g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); |
1214 | |
1215 | return self->show_recommended; |
1216 | } |
1217 | |
1218 | /** |
1219 | * gtk_app_chooser_widget_set_show_fallback: (attributes org.gtk.Method.set_property=show-fallback) |
1220 | * @self: a `GtkAppChooserWidget` |
1221 | * @setting: the new value for [property@Gtk.AppChooserWidget:show-fallback] |
1222 | * |
1223 | * Sets whether the app chooser should show related applications |
1224 | * for the content type in a separate section. |
1225 | */ |
1226 | void |
1227 | gtk_app_chooser_widget_set_show_fallback (GtkAppChooserWidget *self, |
1228 | gboolean setting) |
1229 | { |
1230 | g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); |
1231 | |
1232 | if (self->show_fallback != setting) |
1233 | { |
1234 | self->show_fallback = setting; |
1235 | |
1236 | g_object_notify (G_OBJECT (self), property_name: "show-fallback" ); |
1237 | |
1238 | gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); |
1239 | } |
1240 | } |
1241 | |
1242 | /** |
1243 | * gtk_app_chooser_widget_get_show_fallback: (attributes org.gtk.Method.get_property=show-fallback) |
1244 | * @self: a `GtkAppChooserWidget` |
1245 | * |
1246 | * Gets whether the app chooser should show related applications |
1247 | * for the content type in a separate section. |
1248 | * |
1249 | * Returns: the value of [property@Gtk.AppChooserWidget:show-fallback] |
1250 | */ |
1251 | gboolean |
1252 | gtk_app_chooser_widget_get_show_fallback (GtkAppChooserWidget *self) |
1253 | { |
1254 | g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); |
1255 | |
1256 | return self->show_fallback; |
1257 | } |
1258 | |
1259 | /** |
1260 | * gtk_app_chooser_widget_set_show_other: (attributes org.gtk.Method.set_property=show-other) |
1261 | * @self: a `GtkAppChooserWidget` |
1262 | * @setting: the new value for [property@Gtk.AppChooserWidget:show-other] |
1263 | * |
1264 | * Sets whether the app chooser should show applications |
1265 | * which are unrelated to the content type. |
1266 | */ |
1267 | void |
1268 | gtk_app_chooser_widget_set_show_other (GtkAppChooserWidget *self, |
1269 | gboolean setting) |
1270 | { |
1271 | g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); |
1272 | |
1273 | if (self->show_other != setting) |
1274 | { |
1275 | self->show_other = setting; |
1276 | |
1277 | g_object_notify (G_OBJECT (self), property_name: "show-other" ); |
1278 | |
1279 | gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); |
1280 | } |
1281 | } |
1282 | |
1283 | /** |
1284 | * gtk_app_chooser_widget_get_show_other: (attributes org.gtk.Method.get_property=show-other) |
1285 | * @self: a `GtkAppChooserWidget` |
1286 | * |
1287 | * Gets whether the app chooser should show applications |
1288 | * which are unrelated to the content type. |
1289 | * |
1290 | * Returns: the value of [property@Gtk.AppChooserWidget:show-other] |
1291 | */ |
1292 | gboolean |
1293 | gtk_app_chooser_widget_get_show_other (GtkAppChooserWidget *self) |
1294 | { |
1295 | g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); |
1296 | |
1297 | return self->show_other; |
1298 | } |
1299 | |
1300 | /** |
1301 | * gtk_app_chooser_widget_set_show_all: (attributes org.gtk.Method.set_property=show-all) |
1302 | * @self: a `GtkAppChooserWidget` |
1303 | * @setting: the new value for [property@Gtk.AppChooserWidget:show-all] |
1304 | * |
1305 | * Sets whether the app chooser should show all applications |
1306 | * in a flat list. |
1307 | */ |
1308 | void |
1309 | gtk_app_chooser_widget_set_show_all (GtkAppChooserWidget *self, |
1310 | gboolean setting) |
1311 | { |
1312 | g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); |
1313 | |
1314 | if (self->show_all != setting) |
1315 | { |
1316 | self->show_all = setting; |
1317 | |
1318 | g_object_notify (G_OBJECT (self), property_name: "show-all" ); |
1319 | |
1320 | gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); |
1321 | } |
1322 | } |
1323 | |
1324 | /** |
1325 | * gtk_app_chooser_widget_get_show_all: (attributes org.gtk.Method.get_property=show-all) |
1326 | * @self: a `GtkAppChooserWidget` |
1327 | * |
1328 | * Gets whether the app chooser should show all applications |
1329 | * in a flat list. |
1330 | * |
1331 | * Returns: the value of [property@Gtk.AppChooserWidget:show-all] |
1332 | */ |
1333 | gboolean |
1334 | gtk_app_chooser_widget_get_show_all (GtkAppChooserWidget *self) |
1335 | { |
1336 | g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), FALSE); |
1337 | |
1338 | return self->show_all; |
1339 | } |
1340 | |
1341 | /** |
1342 | * gtk_app_chooser_widget_set_default_text: (attributes org.gtk.Method.set_property=default-text) |
1343 | * @self: a `GtkAppChooserWidget` |
1344 | * @text: the new value for [property@Gtk.AppChooserWidget:default-text] |
1345 | * |
1346 | * Sets the text that is shown if there are not applications |
1347 | * that can handle the content type. |
1348 | */ |
1349 | void |
1350 | gtk_app_chooser_widget_set_default_text (GtkAppChooserWidget *self, |
1351 | const char *text) |
1352 | { |
1353 | g_return_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self)); |
1354 | |
1355 | if (g_strcmp0 (str1: text, str2: self->default_text) != 0) |
1356 | { |
1357 | g_free (mem: self->default_text); |
1358 | self->default_text = g_strdup (str: text); |
1359 | |
1360 | g_object_notify (G_OBJECT (self), property_name: "default-text" ); |
1361 | |
1362 | gtk_app_chooser_refresh (GTK_APP_CHOOSER (self)); |
1363 | } |
1364 | } |
1365 | |
1366 | /** |
1367 | * gtk_app_chooser_widget_get_default_text: (attributes org.gtk.Method.get_property=default-text) |
1368 | * @self: a `GtkAppChooserWidget` |
1369 | * |
1370 | * Returns the text that is shown if there are not applications |
1371 | * that can handle the content type. |
1372 | * |
1373 | * Returns: (nullable): the value of [property@Gtk.AppChooserWidget:default-text] |
1374 | */ |
1375 | const char * |
1376 | gtk_app_chooser_widget_get_default_text (GtkAppChooserWidget *self) |
1377 | { |
1378 | g_return_val_if_fail (GTK_IS_APP_CHOOSER_WIDGET (self), NULL); |
1379 | |
1380 | return self->default_text; |
1381 | } |
1382 | |
1383 | void |
1384 | _gtk_app_chooser_widget_set_search_entry (GtkAppChooserWidget *self, |
1385 | GtkEditable *entry) |
1386 | { |
1387 | gtk_tree_view_set_search_entry (GTK_TREE_VIEW (self->program_list), entry); |
1388 | |
1389 | g_object_bind_property (source: self->no_apps, source_property: "visible" , |
1390 | target: entry, target_property: "sensitive" , |
1391 | flags: G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); |
1392 | } |
1393 | |