1/* Lists/Application launcher
2 * #Keywords: GtkListItemFactory, GListModel
3 *
4 * This demo uses the GtkListView widget as a fancy application launcher.
5 *
6 * It is also a very small introduction to listviews.
7 */
8
9#include <gtk/gtk.h>
10
11/* This is the function that creates the GListModel that we need.
12 * GTK list widgets need a GListModel to display, as models support change
13 * notifications.
14 * Unfortunately various older APIs do not provide list models, so we create
15 * our own.
16 */
17static GListModel *
18create_application_list (void)
19{
20 GListStore *store;
21 GList *apps, *l;
22
23 /* We use a GListStore here, which is a simple array-like list implementation
24 * for manual management.
25 * List models need to know what type of data they provide, so we need to
26 * provide the type here. As we want to do a list of applications, GAppInfo
27 * is the object we provide.
28 */
29 store = g_list_store_new (G_TYPE_APP_INFO);
30
31 apps = g_app_info_get_all ();
32
33 for (l = apps; l; l = l->next)
34 g_list_store_append (store, item: l->data);
35
36 g_list_free_full (list: apps, free_func: g_object_unref);
37
38 return G_LIST_MODEL (ptr: store);
39}
40
41/* This is the function we use for setting up new listitems to display.
42 * We add just an GtkImage and a GtkLabel here to display the application's
43 * icon and name, as this is just a simple demo.
44 */
45static void
46setup_listitem_cb (GtkListItemFactory *factory,
47 GtkListItem *list_item)
48{
49 GtkWidget *box;
50 GtkWidget *image;
51 GtkWidget *label;
52
53 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 12);
54 image = gtk_image_new ();
55 gtk_image_set_icon_size (GTK_IMAGE (image), icon_size: GTK_ICON_SIZE_LARGE);
56 gtk_box_append (GTK_BOX (box), child: image);
57 label = gtk_label_new (str: "");
58 gtk_box_append (GTK_BOX (box), child: label);
59 gtk_list_item_set_child (self: list_item, child: box);
60}
61
62/* Here we need to prepare the listitem for displaying its item. We get the
63 * listitem already set up from the previous function, so we can reuse the
64 * GtkImage widget we set up above.
65 * We get the item - which we know is a GAppInfo because it comes out of
66 * the model we set up above, grab its icon and display it.
67 */
68static void
69bind_listitem_cb (GtkListItemFactory *factory,
70 GtkListItem *list_item)
71{
72 GtkWidget *image;
73 GtkWidget *label;
74 GAppInfo *app_info;
75
76 image = gtk_widget_get_first_child (widget: gtk_list_item_get_child (self: list_item));
77 label = gtk_widget_get_next_sibling (widget: image);
78 app_info = gtk_list_item_get_item (self: list_item);
79
80 gtk_image_set_from_gicon (GTK_IMAGE (image), icon: g_app_info_get_icon (appinfo: app_info));
81 gtk_label_set_label (GTK_LABEL (label), str: g_app_info_get_display_name (appinfo: app_info));
82}
83
84/* In more complex code, we would also need functions to unbind and teardown
85 * the listitem, but this is simple code, so the default implementations are
86 * enough. If we had connected signals, this step would have been necessary.
87 *
88 * The GtkSignalListItemFactory documentation contains more information about
89 * this step.
90 */
91
92/* This function is called whenever an item in the list is activated. This is
93 * the simple way to allow reacting to the Enter key or double-clicking on a
94 * listitem.
95 * Of course, it is possible to use far more complex interactions by turning
96 * off activation and adding buttons or other widgets in the setup function
97 * above, but this is a simple demo, so we'll use the simple way.
98 */
99static void
100activate_cb (GtkListView *list,
101 guint position,
102 gpointer unused)
103{
104 GAppInfo *app_info;
105 GdkAppLaunchContext *context;
106 GError *error = NULL;
107
108 app_info = g_list_model_get_item (list: G_LIST_MODEL (ptr: gtk_list_view_get_model (self: list)), position);
109
110 /* Prepare the context for launching the application and launch it. This
111 * code is explained in detail in the documentation for GdkAppLaunchContext
112 * and GAppInfo.
113 */
114 context = gdk_display_get_app_launch_context (display: gtk_widget_get_display (GTK_WIDGET (list)));
115 if (!g_app_info_launch (appinfo: app_info,
116 NULL,
117 G_APP_LAUNCH_CONTEXT (context),
118 error: &error))
119 {
120 GtkWidget *dialog;
121
122 /* And because error handling is important, even a simple demo has it:
123 * We display an error dialog that something went wrong.
124 */
125 dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (list))),
126 flags: GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
127 type: GTK_MESSAGE_ERROR,
128 buttons: GTK_BUTTONS_CLOSE,
129 message_format: "Could not launch %s", g_app_info_get_display_name (appinfo: app_info));
130 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), message_format: "%s", error->message);
131 g_clear_error (err: &error);
132 gtk_widget_show (widget: dialog);
133 }
134
135 g_object_unref (object: context);
136 g_object_unref (object: app_info);
137}
138
139static GtkWidget *window = NULL;
140
141GtkWidget *
142do_listview_applauncher (GtkWidget *do_widget)
143{
144 if (window == NULL)
145 {
146 GtkWidget *list, *sw;
147 GListModel *model;
148 GtkListItemFactory *factory;
149
150 /* Create a window and set a few defaults */
151 window = gtk_window_new ();
152 gtk_window_set_default_size (GTK_WINDOW (window), width: 640, height: 320);
153 gtk_window_set_display (GTK_WINDOW (window),
154 display: gtk_widget_get_display (widget: do_widget));
155 gtk_window_set_title (GTK_WINDOW (window), title: "Application Launcher");
156 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *) &window);
157
158 /* The GtkListitemFactory is what is used to create GtkListItems
159 * to display the data from the model. So it is absolutely necessary
160 * to create one.
161 * We will use a GtkSignalListItemFactory because it is the simplest
162 * one to use. Different ones are available for different use cases.
163 * The most powerful one is GtkBuilderListItemFactory which uses
164 * GtkBuilder .ui files, so it requires little code.
165 */
166 factory = gtk_signal_list_item_factory_new ();
167 g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem_cb), NULL);
168 g_signal_connect (factory, "bind", G_CALLBACK (bind_listitem_cb), NULL);
169
170 /* And of course we need to set the data model. Here we call the function
171 * we wrote above that gives us the list of applications. Then we set
172 * it on the list widget.
173 * The list will now take items from the model and use the factory
174 * to create as many listitems as it needs to show itself to the user.
175 */
176 model = create_application_list ();
177
178 /* Create the list widget here.
179 */
180 list = gtk_list_view_new (model: GTK_SELECTION_MODEL (ptr: gtk_single_selection_new (model)), factory);
181
182 /* We connect the activate signal here. It's the function we defined
183 * above for launching the selected application.
184 */
185 g_signal_connect (list, "activate", G_CALLBACK (activate_cb), NULL);
186
187 /* List widgets should always be contained in a GtkScrolledWindow,
188 * because otherwise they might get too large or they might not
189 * be scrollable.
190 */
191 sw = gtk_scrolled_window_new ();
192 gtk_window_set_child (GTK_WINDOW (window), child: sw);
193 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: list);
194 }
195
196 if (!gtk_widget_get_visible (widget: window))
197 gtk_widget_show (widget: window);
198 else
199 gtk_window_destroy (GTK_WINDOW (window));
200
201 return window;
202}
203

source code of gtk/demos/gtk-demo/listview_applauncher.c