1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
2  *
3  * This file is part of GtkSourceView
4  *
5  * Copyright (C) 2007 - Jesús Barbero Rodríguez <chuchiperriman@gmail.com>
6  * Copyright (C) 2013 - Sébastien Wilmet <swilmet@gnome.org>
7  *
8  * GtkSourceView is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * GtkSourceView is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this library; if not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <gtk/gtk.h>
23 #include <gtksourceview/gtksource.h>
24 
25 typedef struct _TestProvider TestProvider;
26 typedef struct _TestProviderClass TestProviderClass;
27 
28 static GtkSourceCompletionWords *word_provider;
29 static TestProvider *fixed_provider;
30 static TestProvider *random_provider;
31 
32 struct _TestProvider
33 {
34 	GObject parent;
35 
36 	GList *proposals;
37 	gint priority;
38 	gchar *name;
39 
40 	GdkPixbuf *provider_icon;
41 
42 	GdkPixbuf *item_icon;
43 	GIcon *item_gicon;
44 
45 	/* If it's a random provider, a subset of 'proposals' are choosen on
46 	 * each populate. Otherwise, all the proposals are shown. */
47 	guint is_random : 1;
48 };
49 
50 struct _TestProviderClass
51 {
52 	GObjectClass parent_class;
53 };
54 
55 static void test_provider_iface_init (GtkSourceCompletionProviderIface *iface);
56 GType test_provider_get_type (void);
57 
G_DEFINE_TYPE_WITH_CODE(TestProvider,test_provider,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROVIDER,test_provider_iface_init))58 G_DEFINE_TYPE_WITH_CODE (TestProvider,
59 			 test_provider,
60 			 G_TYPE_OBJECT,
61 			 G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROVIDER,
62 				 		test_provider_iface_init))
63 
64 static gchar *
65 test_provider_get_name (GtkSourceCompletionProvider *provider)
66 {
67 	return g_strdup (((TestProvider *)provider)->name);
68 }
69 
70 static gint
test_provider_get_priority(GtkSourceCompletionProvider * provider)71 test_provider_get_priority (GtkSourceCompletionProvider *provider)
72 {
73 	return ((TestProvider *)provider)->priority;
74 }
75 
76 static GList *
select_random_proposals(GList * all_proposals)77 select_random_proposals (GList *all_proposals)
78 {
79 	GList *selection = NULL;
80 	GList *prop;
81 
82 	for (prop = all_proposals; prop != NULL; prop = g_list_next (prop))
83 	{
84 		if (g_random_boolean ())
85 		{
86 			selection = g_list_prepend (selection, prop->data);
87 		}
88 	}
89 
90 	return selection;
91 }
92 
93 static void
test_provider_populate(GtkSourceCompletionProvider * completion_provider,GtkSourceCompletionContext * context)94 test_provider_populate (GtkSourceCompletionProvider *completion_provider,
95                         GtkSourceCompletionContext  *context)
96 {
97 	TestProvider *provider = (TestProvider *)completion_provider;
98 	GList *proposals;
99 
100 	if (provider->is_random)
101 	{
102 		proposals = select_random_proposals (provider->proposals);
103 	}
104 	else
105 	{
106 		proposals = provider->proposals;
107 	}
108 
109 	gtk_source_completion_context_add_proposals (context,
110 						     completion_provider,
111 						     proposals,
112 						     TRUE);
113 }
114 
115 static GdkPixbuf *
test_provider_get_icon(GtkSourceCompletionProvider * provider)116 test_provider_get_icon (GtkSourceCompletionProvider *provider)
117 {
118 	TestProvider *tp = (TestProvider *)provider;
119 
120 	return tp->is_random ? NULL : tp->provider_icon;
121 }
122 
123 static void
test_provider_iface_init(GtkSourceCompletionProviderIface * iface)124 test_provider_iface_init (GtkSourceCompletionProviderIface *iface)
125 {
126 	iface->get_name = test_provider_get_name;
127 	iface->populate = test_provider_populate;
128 	iface->get_priority = test_provider_get_priority;
129 	iface->get_icon = test_provider_get_icon;
130 }
131 
132 static void
test_provider_dispose(GObject * gobject)133 test_provider_dispose (GObject *gobject)
134 {
135 	TestProvider *self = (TestProvider *)gobject;
136 
137 	g_list_free_full (self->proposals, g_object_unref);
138 	self->proposals = NULL;
139 
140 	g_clear_object (&self->provider_icon);
141 	g_clear_object (&self->item_icon);
142 	g_clear_object (&self->item_gicon);
143 
144 	G_OBJECT_CLASS (test_provider_parent_class)->dispose (gobject);
145 }
146 
147 static void
test_provider_finalize(GObject * gobject)148 test_provider_finalize (GObject *gobject)
149 {
150 	TestProvider *self = (TestProvider *)gobject;
151 
152 	g_free (self->name);
153 	self->name = NULL;
154 
155 	G_OBJECT_CLASS (test_provider_parent_class)->finalize (gobject);
156 }
157 
158 static void
test_provider_class_init(TestProviderClass * klass)159 test_provider_class_init (TestProviderClass *klass)
160 {
161 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
162 
163 	gobject_class->dispose = test_provider_dispose;
164 	gobject_class->finalize = test_provider_finalize;
165 }
166 
167 static void
test_provider_init(TestProvider * self)168 test_provider_init (TestProvider *self)
169 {
170 	GtkIconTheme *theme;
171 	GIcon *icon;
172 	GIcon *emblem_icon;
173 	GEmblem *emblem;
174 
175 	theme = gtk_icon_theme_get_default ();
176 	self->provider_icon = gtk_icon_theme_load_icon (theme, "dialog-information", 16, 0, NULL);
177 
178 	self->item_icon = gtk_icon_theme_load_icon (theme, "trophy-gold", 16, 0, NULL);
179 
180 	icon = g_themed_icon_new ("trophy-silver");
181 	emblem_icon = g_themed_icon_new ("emblem-urgent");
182 	emblem = g_emblem_new (emblem_icon);
183 	self->item_gicon = g_emblemed_icon_new (icon, emblem);
184 	g_object_unref (icon);
185 	g_object_unref (emblem_icon);
186 	g_object_unref (emblem);
187 }
188 
189 static void
test_provider_set_fixed(TestProvider * provider,gint nb_proposals)190 test_provider_set_fixed (TestProvider *provider,
191 			 gint          nb_proposals)
192 {
193 	GtkSourceCompletionItem *item;
194 	GList *proposals = NULL;
195 	gint i;
196 
197 	g_list_free_full (provider->proposals, g_object_unref);
198 
199 	item = gtk_source_completion_item_new ();
200 	gtk_source_completion_item_set_markup (item, "A very <b>long</b> proposal. I <i>repeat</i>, a very long proposal!");
201 	gtk_source_completion_item_set_text (item, "A very long proposal. I repeat, a very long proposal!");
202 	gtk_source_completion_item_set_icon (item, provider->item_icon);
203 	gtk_source_completion_item_set_info (item, "To test the horizontal scrollbar and the markup.");
204 	proposals = g_list_prepend (proposals, item);
205 
206 	item = gtk_source_completion_item_new ();
207 	gtk_source_completion_item_set_markup (item, "A proposal with a <b>symbolic</b> icon");
208 	gtk_source_completion_item_set_text (item, "Test setting the icon-name property");
209 	gtk_source_completion_item_set_icon_name (item, "face-cool-symbolic");
210 	proposals = g_list_prepend (proposals, item);
211 
212 	item = gtk_source_completion_item_new ();
213 	gtk_source_completion_item_set_markup (item, "A proposal with an emblem <b>GIcon</b>");
214 	gtk_source_completion_item_set_text (item, "Test setting the GIcon property");
215 	gtk_source_completion_item_set_gicon (item, provider->item_gicon);
216 	proposals = g_list_prepend (proposals, item);
217 
218 	for (i = nb_proposals - 1; i > 0; i--)
219 	{
220 		gchar *name = g_strdup_printf ("Proposal %d", i);
221 
222 		item = gtk_source_completion_item_new ();
223 		gtk_source_completion_item_set_label (item, name);
224 		gtk_source_completion_item_set_text (item, name);
225 		gtk_source_completion_item_set_icon (item, provider->item_icon);
226 		gtk_source_completion_item_set_info (item, "The extra info of the proposal.\nA second line.");
227 		proposals = g_list_prepend (proposals, item);
228 
229 		g_free (name);
230 	}
231 
232 	provider->proposals = proposals;
233 	provider->is_random = 0;
234 }
235 
236 static void
test_provider_set_random(TestProvider * provider,gint nb_proposals)237 test_provider_set_random (TestProvider *provider,
238 			  gint          nb_proposals)
239 {
240 	GList *proposals = NULL;
241 	gint i;
242 
243 	g_list_free_full (provider->proposals, g_object_unref);
244 
245 	for (i = 0; i < nb_proposals; i++)
246 	{
247 		GtkSourceCompletionItem *item;
248 		gchar *padding = g_strnfill ((i * 3) % 10, 'o');
249 		gchar *name = g_strdup_printf ("Propo%ssal %d", padding, i);
250 
251 		item = gtk_source_completion_item_new ();
252 		gtk_source_completion_item_set_label (item, name);
253 		gtk_source_completion_item_set_text (item, name);
254 		gtk_source_completion_item_set_icon (item, provider->item_icon);
255 		proposals = g_list_prepend (proposals, item);
256 
257 		g_free (padding);
258 		g_free (name);
259 	}
260 
261 	provider->proposals = proposals;
262 	provider->is_random = 1;
263 }
264 
265 static void
add_remove_provider(GtkToggleButton * button,GtkSourceCompletion * completion,GtkSourceCompletionProvider * provider)266 add_remove_provider (GtkToggleButton             *button,
267 		     GtkSourceCompletion         *completion,
268 		     GtkSourceCompletionProvider *provider)
269 {
270 	g_return_if_fail (provider != NULL);
271 
272 	if (gtk_toggle_button_get_active (button))
273 	{
274 		gtk_source_completion_add_provider (completion, provider, NULL);
275 	}
276 	else
277 	{
278 		gtk_source_completion_remove_provider (completion, provider, NULL);
279 	}
280 }
281 
282 static void
enable_word_provider_toggled_cb(GtkToggleButton * button,GtkSourceCompletion * completion)283 enable_word_provider_toggled_cb (GtkToggleButton     *button,
284 				 GtkSourceCompletion *completion)
285 {
286 	add_remove_provider (button,
287 			     completion,
288 			     GTK_SOURCE_COMPLETION_PROVIDER (word_provider));
289 }
290 
291 static void
enable_fixed_provider_toggled_cb(GtkToggleButton * button,GtkSourceCompletion * completion)292 enable_fixed_provider_toggled_cb (GtkToggleButton     *button,
293 				  GtkSourceCompletion *completion)
294 {
295 	add_remove_provider (button,
296 			     completion,
297 			     GTK_SOURCE_COMPLETION_PROVIDER (fixed_provider));
298 }
299 
300 static void
enable_random_provider_toggled_cb(GtkToggleButton * button,GtkSourceCompletion * completion)301 enable_random_provider_toggled_cb (GtkToggleButton     *button,
302 				   GtkSourceCompletion *completion)
303 {
304 	add_remove_provider (button,
305 			     completion,
306 			     GTK_SOURCE_COMPLETION_PROVIDER (random_provider));
307 }
308 
309 static void
nb_proposals_changed_cb(GtkSpinButton * spin_button,TestProvider * provider)310 nb_proposals_changed_cb (GtkSpinButton *spin_button,
311 			 TestProvider  *provider)
312 {
313 	gint nb_proposals = gtk_spin_button_get_value_as_int (spin_button);
314 
315 	if (provider->is_random)
316 	{
317 		test_provider_set_random (provider, nb_proposals);
318 	}
319 	else
320 	{
321 		test_provider_set_fixed (provider, nb_proposals);
322 	}
323 }
324 
325 static void
create_completion(GtkSourceView * source_view,GtkSourceCompletion * completion)326 create_completion (GtkSourceView       *source_view,
327 		   GtkSourceCompletion *completion)
328 {
329 	/* Words completion provider */
330 	word_provider = gtk_source_completion_words_new (NULL, NULL);
331 
332 	gtk_source_completion_words_register (word_provider,
333 	                                      gtk_text_view_get_buffer (GTK_TEXT_VIEW (source_view)));
334 
335 	gtk_source_completion_add_provider (completion,
336 	                                    GTK_SOURCE_COMPLETION_PROVIDER (word_provider),
337 	                                    NULL);
338 
339 	g_object_set (word_provider, "priority", 10, NULL);
340 
341 	/* Fixed provider: the proposals don't change */
342 	fixed_provider = g_object_new (test_provider_get_type (), NULL);
343 	test_provider_set_fixed (fixed_provider, 3);
344 	fixed_provider->priority = 5;
345 	fixed_provider->name = g_strdup ("Fixed Provider");
346 
347 	gtk_source_completion_add_provider (completion,
348 	                                    GTK_SOURCE_COMPLETION_PROVIDER (fixed_provider),
349 	                                    NULL);
350 
351 	/* Random provider: the proposals vary on each populate */
352 	random_provider = g_object_new (test_provider_get_type (), NULL);
353 	test_provider_set_random (random_provider, 10);
354 	random_provider->priority = 1;
355 	random_provider->name = g_strdup ("Random Provider");
356 
357 	gtk_source_completion_add_provider (completion,
358 	                                    GTK_SOURCE_COMPLETION_PROVIDER (random_provider),
359 	                                    NULL);
360 }
361 
362 static void
create_window(void)363 create_window (void)
364 {
365 	GtkBuilder *builder;
366 	GError *error = NULL;
367 	GtkWindow *window;
368 	GtkSourceView *source_view;
369 	GtkSourceCompletion *completion;
370 	GtkCheckButton *remember_info_visibility;
371 	GtkCheckButton *select_on_show;
372 	GtkCheckButton *show_headers;
373 	GtkCheckButton *show_icons;
374 	GtkCheckButton *enable_word_provider;
375 	GtkCheckButton *enable_fixed_provider;
376 	GtkCheckButton *enable_random_provider;
377 	GtkSpinButton *nb_fixed_proposals;
378 	GtkSpinButton *nb_random_proposals;
379 
380 	builder = gtk_builder_new ();
381 
382 	gtk_builder_add_from_resource (builder,
383 				       "/org/gnome/gtksourceview/tests/ui/test-completion.ui",
384 				       &error);
385 
386 	if (error != NULL)
387 	{
388 		g_error ("Impossible to load test-completion.ui: %s", error->message);
389 	}
390 
391 	window = GTK_WINDOW (gtk_builder_get_object (builder, "window"));
392 	source_view = GTK_SOURCE_VIEW (gtk_builder_get_object (builder, "source_view"));
393 	remember_info_visibility = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, "checkbutton_remember_info_visibility"));
394 	select_on_show = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, "checkbutton_select_on_show"));
395 	show_headers = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, "checkbutton_show_headers"));
396 	show_icons = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, "checkbutton_show_icons"));
397 	enable_word_provider = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, "checkbutton_word_provider"));
398 	enable_fixed_provider = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, "checkbutton_fixed_provider"));
399 	enable_random_provider = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, "checkbutton_random_provider"));
400 	nb_fixed_proposals = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "spinbutton_nb_fixed_proposals"));
401 	nb_random_proposals = GTK_SPIN_BUTTON (gtk_builder_get_object (builder, "spinbutton_nb_random_proposals"));
402 
403 	completion = gtk_source_view_get_completion (source_view);
404 
405 	g_signal_connect (window,
406 			  "destroy",
407 			  G_CALLBACK (gtk_main_quit),
408 			  NULL);
409 
410 	g_object_bind_property (completion, "remember-info-visibility",
411 				remember_info_visibility, "active",
412 				G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
413 
414 	g_object_bind_property (completion, "select-on-show",
415 				select_on_show, "active",
416 				G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
417 
418 	g_object_bind_property (completion, "show-headers",
419 				show_headers, "active",
420 				G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
421 
422 	g_object_bind_property (completion, "show-icons",
423 				show_icons, "active",
424 				G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
425 
426 	create_completion (source_view, completion);
427 
428 	g_signal_connect (enable_word_provider,
429 			  "toggled",
430 			  G_CALLBACK (enable_word_provider_toggled_cb),
431 			  completion);
432 
433 	g_signal_connect (enable_fixed_provider,
434 			  "toggled",
435 			  G_CALLBACK (enable_fixed_provider_toggled_cb),
436 			  completion);
437 
438 	g_signal_connect (enable_random_provider,
439 			  "toggled",
440 			  G_CALLBACK (enable_random_provider_toggled_cb),
441 			  completion);
442 
443 	g_signal_connect (nb_fixed_proposals,
444 			  "value-changed",
445 			  G_CALLBACK (nb_proposals_changed_cb),
446 			  fixed_provider);
447 
448 	g_signal_connect (nb_random_proposals,
449 			  "value-changed",
450 			  G_CALLBACK (nb_proposals_changed_cb),
451 			  random_provider);
452 
453 	g_object_unref (builder);
454 }
455 
456 int
main(int argc,char * argv[])457 main (int argc, char *argv[])
458 {
459 	gtk_init (&argc, &argv);
460 
461 	create_window ();
462 
463 	gtk_main ();
464 
465 	/* Not really useful, except for debugging memory leaks. */
466 	g_object_unref (word_provider);
467 	g_object_unref (fixed_provider);
468 	g_object_unref (random_provider);
469 
470 	return 0;
471 }
472