1/* Lists/Words
2 * #Keywords: GtkListView, GtkFilterListModel
3 *
4 * This demo shows filtering a long list - of words.
5 *
6 * You should have the file `/usr/share/dict/words` installed for
7 * this demo to work.
8 */
9
10#include <gtk/gtk.h>
11
12static GtkWidget *window = NULL;
13static GtkWidget *progress;
14
15const char *factory_text =
16"<?xml version='1.0' encoding='UTF-8'?>\n"
17"<interface>\n"
18" <template class='GtkListItem'>\n"
19" <property name='child'>\n"
20" <object class='GtkLabel'>\n"
21" <property name='ellipsize'>end</property>\n"
22" <property name='xalign'>0</property>\n"
23" <binding name='label'>\n"
24" <lookup name='string' type='GtkStringObject'>\n"
25" <lookup name='item'>GtkListItem</lookup>\n"
26" </lookup>\n"
27" </binding>\n"
28" </object>\n"
29" </property>\n"
30" </template>\n"
31"</interface>\n";
32
33static void
34update_title_cb (GtkFilterListModel *model)
35{
36 guint total;
37 char *title;
38 guint pending;
39
40 total = g_list_model_get_n_items (list: gtk_filter_list_model_get_model (self: model));
41 pending = gtk_filter_list_model_get_pending (self: model);
42
43 title = g_strdup_printf (format: "%u lines", g_list_model_get_n_items (list: G_LIST_MODEL (ptr: model)));
44
45 gtk_widget_set_visible (widget: progress, visible: pending != 0);
46 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), fraction: (total - pending) / (double) total);
47 gtk_window_set_title (GTK_WINDOW (window), title);
48 g_free (mem: title);
49}
50
51static void
52read_lines_cb (GObject *object,
53 GAsyncResult *result,
54 gpointer data)
55{
56 GBufferedInputStream *stream = G_BUFFERED_INPUT_STREAM (object);
57 GtkStringList *stringlist = data;
58 GError *error = NULL;
59 gsize size;
60 GPtrArray *lines;
61 gssize n_filled;
62 const char *buffer, *newline;
63
64 n_filled = g_buffered_input_stream_fill_finish (stream, result, error: &error);
65 if (n_filled < 0)
66 {
67 g_print (format: "Could not read data: %s\n", error->message);
68 g_clear_error (err: &error);
69 g_object_unref (object: stringlist);
70 return;
71 }
72
73 buffer = g_buffered_input_stream_peek_buffer (stream, count: &size);
74
75 if (n_filled == 0)
76 {
77 if (size)
78 gtk_string_list_take (self: stringlist, string: g_utf8_make_valid (str: buffer, len: size));
79 g_object_unref (object: stringlist);
80 return;
81 }
82
83 lines = NULL;
84 while ((newline = memchr (s: buffer, c: '\n', n: size)))
85 {
86 if (newline > buffer)
87 {
88 if (lines == NULL)
89 lines = g_ptr_array_new_with_free_func (element_free_func: g_free);
90 g_ptr_array_add (array: lines, data: g_utf8_make_valid (str: buffer, len: newline - buffer));
91 }
92 if (g_input_stream_skip (G_INPUT_STREAM (stream), count: newline - buffer + 1, NULL, error: &error) < 0)
93 {
94 g_clear_error (err: &error);
95 break;
96 }
97 buffer = g_buffered_input_stream_peek_buffer (stream, count: &size);
98 }
99 if (lines == NULL)
100 {
101 g_buffered_input_stream_set_buffer_size (stream, size: g_buffered_input_stream_get_buffer_size (stream) + 4096);
102 }
103 else
104 {
105 g_ptr_array_add (array: lines, NULL);
106 gtk_string_list_splice (self: stringlist, position: g_list_model_get_n_items (list: G_LIST_MODEL (ptr: stringlist)), n_removals: 0, additions: (const char **) lines->pdata);
107 g_ptr_array_free (array: lines, TRUE);
108 }
109
110 g_buffered_input_stream_fill_async (stream, count: -1, G_PRIORITY_HIGH_IDLE, NULL, callback: read_lines_cb, user_data: data);
111}
112
113static void
114file_is_open_cb (GObject *file,
115 GAsyncResult *result,
116 gpointer data)
117{
118 GError *error = NULL;
119 GFileInputStream *file_stream;
120 GBufferedInputStream *stream;
121
122 file_stream = g_file_read_finish (G_FILE (file), res: result, error: &error);
123 if (file_stream == NULL)
124 {
125 g_print (format: "Could not open file: %s\n", error->message);
126 g_error_free (error);
127 g_object_unref (object: data);
128 return;
129 }
130
131 stream = G_BUFFERED_INPUT_STREAM (g_buffered_input_stream_new (G_INPUT_STREAM (file_stream)));
132 g_buffered_input_stream_fill_async (stream, count: -1, G_PRIORITY_HIGH_IDLE, NULL, callback: read_lines_cb, user_data: data);
133 g_object_unref (object: stream);
134}
135
136static void
137load_file (GtkStringList *list,
138 GFile *file)
139{
140 gtk_string_list_splice (self: list, position: 0, n_removals: g_list_model_get_n_items (list: G_LIST_MODEL (ptr: list)), NULL);
141 g_file_read_async (file, G_PRIORITY_HIGH_IDLE, NULL, callback: file_is_open_cb, g_object_ref (list));
142}
143
144static void
145open_response_cb (GtkNativeDialog *dialog,
146 int response,
147 GtkStringList *stringlist)
148{
149 gtk_native_dialog_hide (self: dialog);
150
151 if (response == GTK_RESPONSE_ACCEPT)
152 {
153 GFile *file;
154
155 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
156 load_file (list: stringlist, file);
157 g_object_unref (object: file);
158 }
159
160 gtk_native_dialog_destroy (self: dialog);
161}
162
163static void
164file_open_cb (GtkWidget *button,
165 GtkStringList *stringlist)
166{
167 GtkFileChooserNative *dialog;
168
169 dialog = gtk_file_chooser_native_new (title: "Open file",
170 GTK_WINDOW (gtk_widget_get_root (button)),
171 action: GTK_FILE_CHOOSER_ACTION_OPEN,
172 accept_label: "_Load",
173 cancel_label: "_Cancel");
174 gtk_native_dialog_set_modal (self: GTK_NATIVE_DIALOG (ptr: dialog), TRUE);
175
176 g_signal_connect (dialog, "response", G_CALLBACK (open_response_cb), stringlist);
177 gtk_native_dialog_show (self: GTK_NATIVE_DIALOG (ptr: dialog));
178}
179
180GtkWidget *
181do_listview_words (GtkWidget *do_widget)
182{
183 if (window == NULL)
184 {
185 GtkWidget *header, *listview, *sw, *vbox, *search_entry, *open_button, *overlay;
186 GtkFilterListModel *filter_model;
187 GtkStringList *stringlist;
188 GtkFilter *filter;
189 GFile *file;
190
191 file = g_file_new_for_path (path: "/usr/share/dict/words");
192 if (g_file_query_exists (file, NULL))
193 {
194 stringlist = gtk_string_list_new (NULL);
195 load_file (list: stringlist, file);
196 }
197 else
198 {
199 char **words;
200 words = g_strsplit (string: "lorem ipsum dolor sit amet consectetur adipisci elit sed eiusmod tempor incidunt labore et dolore magna aliqua ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat", delimiter: " ", max_tokens: -1);
201 stringlist = gtk_string_list_new (strings: (const char **) words);
202 g_strfreev (str_array: words);
203 }
204 g_object_unref (object: file);
205
206 filter = GTK_FILTER (ptr: gtk_string_filter_new (expression: gtk_property_expression_new (GTK_TYPE_STRING_OBJECT, NULL, property_name: "string")));
207 filter_model = gtk_filter_list_model_new (model: G_LIST_MODEL (ptr: stringlist), filter);
208 gtk_filter_list_model_set_incremental (self: filter_model, TRUE);
209
210 window = gtk_window_new ();
211 gtk_window_set_default_size (GTK_WINDOW (window), width: 400, height: 600);
212
213 header = gtk_header_bar_new ();
214 gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), TRUE);
215 open_button = gtk_button_new_with_mnemonic (label: "_Open");
216 g_signal_connect (open_button, "clicked", G_CALLBACK (file_open_cb), stringlist);
217 gtk_header_bar_pack_start (GTK_HEADER_BAR (header), child: open_button);
218 gtk_window_set_titlebar (GTK_WINDOW (window), titlebar: header);
219
220 gtk_window_set_display (GTK_WINDOW (window),
221 display: gtk_widget_get_display (widget: do_widget));
222 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer*)&window);
223
224 vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
225 gtk_window_set_child (GTK_WINDOW (window), child: vbox);
226
227 search_entry = gtk_search_entry_new ();
228 g_object_bind_property (source: search_entry, source_property: "text", target: filter, target_property: "search", flags: 0);
229 gtk_box_append (GTK_BOX (vbox), child: search_entry);
230
231 overlay = gtk_overlay_new ();
232 gtk_box_append (GTK_BOX (vbox), child: overlay);
233
234 progress = gtk_progress_bar_new ();
235 gtk_widget_set_halign (widget: progress, align: GTK_ALIGN_FILL);
236 gtk_widget_set_valign (widget: progress, align: GTK_ALIGN_START);
237 gtk_widget_set_hexpand (widget: progress, TRUE);
238 gtk_overlay_add_overlay (GTK_OVERLAY (overlay), widget: progress);
239
240 sw = gtk_scrolled_window_new ();
241 gtk_widget_set_vexpand (widget: sw, TRUE);
242 gtk_overlay_set_child (GTK_OVERLAY (overlay), child: sw);
243
244 listview = gtk_list_view_new (
245 model: GTK_SELECTION_MODEL (ptr: gtk_no_selection_new (model: G_LIST_MODEL (ptr: filter_model))),
246 factory: gtk_builder_list_item_factory_new_from_bytes (NULL,
247 bytes: g_bytes_new_static (data: factory_text, size: strlen (s: factory_text))));
248 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: listview);
249
250 g_signal_connect (filter_model, "items-changed", G_CALLBACK (update_title_cb), progress);
251 g_signal_connect (filter_model, "notify::pending", G_CALLBACK (update_title_cb), progress);
252 update_title_cb (model: filter_model);
253
254 }
255
256 if (!gtk_widget_get_visible (widget: window))
257 gtk_widget_show (widget: window);
258 else
259 gtk_window_destroy (GTK_WINDOW (window));
260
261 return window;
262}
263

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