1 /*
2 * Copyright (C) 2009 Ignacio Casal Quinteiro <icq@gnome.org>
3 * 2009 Jesse van den Kieboom <jesse@gnome.org>
4 * 2013 Sébastien Wilmet <swilmet@gnome.org>
5 * 2017 Mickael Albertus <mickael.albertus@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This program 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
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #include <config.h>
23 #include <glib/gi18n-lib.h>
24 #include <glib.h>
25 #include <gio/gio.h>
26 #include <xed/xed-debug.h>
27 #include <xed/xed-window.h>
28 #include <xed/xed-window-activatable.h>
29 #include <xed/xed-utils.h>
30 #include <xed/xed-view.h>
31 #include <xed/xed-view-activatable.h>
32 #include <libpeas-gtk/peas-gtk-configurable.h>
33 #include <gtksourceview/gtksource.h>
34 #include <gtksourceview/completion-providers/words/gtksourcecompletionwords.h>
35
36 #include "xed-wordcompletion-plugin.h"
37
38 #define WINDOW_PROVIDER "XedWordCompletionPluginProvider"
39
40 #define WORDCOMPLETION_SETTINGS_BASE "org.x.editor.plugins.wordcompletion"
41 #define SETTINGS_KEY_INTERACTIVE_COMPLETION "interactive-completion"
42 #define SETTINGS_KEY_MINIMUM_WORD_SIZE "minimum-word-size"
43
44 static void xed_window_activatable_iface_init (XedWindowActivatableInterface *iface);
45 static void xed_view_activatable_iface_init (XedViewActivatableInterface *iface);
46 static void peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface);
47
48 struct _XedWordCompletionPluginPrivate
49 {
50 GtkWidget *window;
51 XedView *view;
52 GtkSourceCompletionProvider *provider;
53 GSettings *settings;
54 };
55
56 enum
57 {
58 PROP_0,
59 PROP_WINDOW,
60 PROP_VIEW
61 };
62
63
64 typedef struct _WordCompletionConfigureWidget WordCompletionConfigureWidget;
65
66 struct _WordCompletionConfigureWidget
67 {
68 GtkWidget *dialog;
69 GtkWidget *min_word_size;
70 GtkWidget *interactive_completion;
71
72 GSettings *settings;
73 };
74
75 G_DEFINE_DYNAMIC_TYPE_EXTENDED (XedWordCompletionPlugin,
76 xed_wordcompletion_plugin,
77 PEAS_TYPE_EXTENSION_BASE,
78 0,
79 G_IMPLEMENT_INTERFACE_DYNAMIC (XED_TYPE_WINDOW_ACTIVATABLE,
80 xed_window_activatable_iface_init)
81 G_IMPLEMENT_INTERFACE_DYNAMIC (XED_TYPE_VIEW_ACTIVATABLE,
82 xed_view_activatable_iface_init)
83 G_IMPLEMENT_INTERFACE_DYNAMIC (PEAS_GTK_TYPE_CONFIGURABLE,
84 peas_gtk_configurable_iface_init)
85 G_ADD_PRIVATE_DYNAMIC (XedWordCompletionPlugin))
86
87 static void
xed_wordcompletion_plugin_init(XedWordCompletionPlugin * plugin)88 xed_wordcompletion_plugin_init (XedWordCompletionPlugin *plugin)
89 {
90 xed_debug_message (DEBUG_PLUGINS, "XedWordCompletionPlugin initializing");
91
92 plugin->priv = xed_wordcompletion_plugin_get_instance_private (plugin);
93
94 plugin->priv->settings = g_settings_new (WORDCOMPLETION_SETTINGS_BASE);
95 }
96
97 static void
xed_wordcompletion_plugin_finalize(GObject * object)98 xed_wordcompletion_plugin_finalize (GObject *object)
99 {
100 XedWordCompletionPlugin *plugin = XED_WORDCOMPLETION_PLUGIN (object);
101
102 xed_debug_message (DEBUG_PLUGINS, "XedWordCompletionPlugin finalizing");
103
104 g_object_unref (G_OBJECT (plugin->priv->settings));
105
106 G_OBJECT_CLASS (xed_wordcompletion_plugin_parent_class)->finalize (object);
107 }
108
109
110 static void
xed_wordcompletion_plugin_dispose(GObject * object)111 xed_wordcompletion_plugin_dispose (GObject *object)
112 {
113 XedWordCompletionPlugin *plugin = XED_WORDCOMPLETION_PLUGIN (object);
114
115 if (plugin->priv->window != NULL)
116 {
117 g_object_unref (plugin->priv->window);
118 plugin->priv->window = NULL;
119 }
120
121 if (plugin->priv->view != NULL)
122 {
123 g_object_unref (plugin->priv->view);
124 plugin->priv->view = NULL;
125 }
126
127 if (plugin->priv->provider != NULL)
128 {
129 g_object_unref (plugin->priv->provider);
130 plugin->priv->provider = NULL;
131 }
132
133 G_OBJECT_CLASS (xed_wordcompletion_plugin_parent_class)->dispose (object);
134 }
135
136 static void
xed_wordcompletion_plugin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)137 xed_wordcompletion_plugin_set_property (GObject *object,
138 guint prop_id,
139 const GValue *value,
140 GParamSpec *pspec)
141 {
142 XedWordCompletionPlugin *plugin = XED_WORDCOMPLETION_PLUGIN (object);
143
144 switch (prop_id)
145 {
146 case PROP_WINDOW:
147 plugin->priv->window = g_value_dup_object (value);
148 break;
149 case PROP_VIEW:
150 plugin->priv->view = XED_VIEW (g_value_dup_object (value));
151 break;
152 default:
153 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
154 break;
155 }
156 }
157
158 static void
xed_wordcompletion_plugin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)159 xed_wordcompletion_plugin_get_property (GObject *object,
160 guint prop_id,
161 GValue *value,
162 GParamSpec *pspec)
163 {
164 XedWordCompletionPlugin *plugin = XED_WORDCOMPLETION_PLUGIN (object);
165
166 switch (prop_id)
167 {
168 case PROP_WINDOW:
169 g_value_set_object (value, plugin->priv->window);
170 break;
171 case PROP_VIEW:
172 g_value_set_object (value, plugin->priv->view);
173 break;
174 default:
175 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
176 break;
177 }
178 }
179
180 static void
update_activation(GtkSourceCompletionWords * provider,GSettings * settings)181 update_activation (GtkSourceCompletionWords *provider,
182 GSettings *settings)
183 {
184 GtkSourceCompletionActivation activation;
185
186 g_object_get (provider, "activation", &activation, NULL);
187
188 if (g_settings_get_boolean (settings, SETTINGS_KEY_INTERACTIVE_COMPLETION))
189 {
190 activation |= GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE;
191 }
192 else
193 {
194 activation &= ~GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE;
195 }
196
197 g_object_set (provider, "activation", activation, NULL);
198 }
199
200 static void
on_interactive_completion_changed_cb(GSettings * settings,gchar * key,GtkSourceCompletionWords * provider)201 on_interactive_completion_changed_cb (GSettings *settings,
202 gchar *key,
203 GtkSourceCompletionWords *provider)
204 {
205 update_activation (provider, settings);
206 }
207
208 static GtkSourceCompletionWords *
create_provider(void)209 create_provider (void)
210 {
211 GtkSourceCompletionWords *provider;
212 GSettings *settings;
213
214 provider = gtk_source_completion_words_new (_("Word completion"), NULL);
215
216 settings = g_settings_new (WORDCOMPLETION_SETTINGS_BASE);
217
218 g_settings_bind (settings, SETTINGS_KEY_MINIMUM_WORD_SIZE,
219 provider, "minimum-word-size",
220 G_SETTINGS_BIND_GET);
221
222 update_activation (provider, settings);
223
224 g_signal_connect_object (settings,
225 "changed::" SETTINGS_KEY_INTERACTIVE_COMPLETION,
226 G_CALLBACK (on_interactive_completion_changed_cb),
227 provider,
228 0);
229
230 g_object_unref (settings);
231
232 return provider;
233 }
234
235 static void
xed_wordcompletion_window_activate(XedWindowActivatable * activatable)236 xed_wordcompletion_window_activate (XedWindowActivatable *activatable)
237 {
238 XedWordCompletionPluginPrivate *priv;
239 GtkSourceCompletionWords *provider;
240
241 xed_debug (DEBUG_PLUGINS);
242
243 priv = XED_WORDCOMPLETION_PLUGIN (activatable)->priv;
244
245 provider = create_provider ();
246
247 g_object_set_data_full (G_OBJECT (priv->window),
248 WINDOW_PROVIDER,
249 provider,
250 (GDestroyNotify)g_object_unref);
251 }
252
253 static void
xed_wordcompletion_window_deactivate(XedWindowActivatable * activatable)254 xed_wordcompletion_window_deactivate (XedWindowActivatable *activatable)
255 {
256 XedWordCompletionPluginPrivate *priv;
257
258 xed_debug (DEBUG_PLUGINS);
259
260 priv = XED_WORDCOMPLETION_PLUGIN (activatable)->priv;
261
262 g_object_set_data (G_OBJECT (priv->window), WINDOW_PROVIDER, NULL);
263 }
264
265 static void
xed_wordcompletion_view_activate(XedViewActivatable * activatable)266 xed_wordcompletion_view_activate (XedViewActivatable *activatable)
267 {
268 XedWordCompletionPluginPrivate *priv;
269 GtkSourceCompletion *completion;
270 GtkSourceCompletionProvider *provider;
271 GtkTextBuffer *buf;
272
273 xed_debug (DEBUG_PLUGINS);
274
275 priv = XED_WORDCOMPLETION_PLUGIN (activatable)->priv;
276
277 priv->window = gtk_widget_get_toplevel (GTK_WIDGET (priv->view));
278
279 /* We are disposing the window */
280 g_object_ref (priv->window);
281
282 completion = gtk_source_view_get_completion (GTK_SOURCE_VIEW (priv->view));
283 buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->view));
284
285 provider = g_object_get_data (G_OBJECT (priv->window), WINDOW_PROVIDER);
286
287 if (provider == NULL)
288 {
289 /* Standalone provider */
290 provider = GTK_SOURCE_COMPLETION_PROVIDER (create_provider ());
291 }
292
293 priv->provider = g_object_ref (provider);
294
295 gtk_source_completion_add_provider (completion, provider, NULL);
296 gtk_source_completion_words_register (GTK_SOURCE_COMPLETION_WORDS (provider), buf);
297 }
298
299 static void
xed_wordcompletion_view_deactivate(XedViewActivatable * activatable)300 xed_wordcompletion_view_deactivate (XedViewActivatable *activatable)
301 {
302 XedWordCompletionPluginPrivate *priv;
303 GtkSourceCompletion *completion;
304 GtkTextBuffer *buf;
305
306 xed_debug (DEBUG_PLUGINS);
307
308 priv = XED_WORDCOMPLETION_PLUGIN (activatable)->priv;
309
310 completion = gtk_source_view_get_completion (GTK_SOURCE_VIEW (priv->view));
311 buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->view));
312
313 gtk_source_completion_remove_provider (completion,
314 priv->provider,
315 NULL);
316
317 gtk_source_completion_words_unregister (GTK_SOURCE_COMPLETION_WORDS (priv->provider), buf);
318 }
319
320 static void
dialog_response_cb(GtkWidget * widget,gint response,gpointer data)321 dialog_response_cb (GtkWidget *widget,
322 gint response,
323 gpointer data)
324 {
325 gtk_widget_destroy (widget);
326 }
327
328 static void
configure_widget_destroyed(GtkWidget * widget,gpointer data)329 configure_widget_destroyed (GtkWidget *widget,
330 gpointer data)
331 {
332 WordCompletionConfigureWidget *conf_widget = (WordCompletionConfigureWidget *) data;
333
334 xed_debug (DEBUG_PLUGINS);
335
336 g_object_unref (conf_widget->settings);
337 g_slice_free (WordCompletionConfigureWidget, data);
338
339 xed_debug_message (DEBUG_PLUGINS, "END");
340 }
341
342 static WordCompletionConfigureWidget *
get_configure_widget(XedWordCompletionPlugin * plugin)343 get_configure_widget (XedWordCompletionPlugin *plugin)
344 {
345 WordCompletionConfigureWidget *widget;
346 gchar *data_dir;
347 gchar *ui_file;
348 GtkWidget *error_widget;
349 gboolean ret;
350
351 xed_debug (DEBUG_PLUGINS);
352
353 widget = g_slice_new (WordCompletionConfigureWidget);
354 widget->settings = g_object_ref (plugin->priv->settings);
355
356 data_dir = peas_extension_base_get_data_dir (PEAS_EXTENSION_BASE (plugin));
357 ui_file = g_build_filename (data_dir, "xed-wordcompletion-configure.ui", NULL);
358 ret = xed_utils_get_ui_objects (ui_file,
359 NULL,
360 &error_widget,
361 "configure_dialog", &widget->dialog,
362 "spin_button_min_word_size", &widget->min_word_size,
363 "check_button_interactive_completion", &widget->interactive_completion,
364 NULL);
365
366 g_free (data_dir);
367 g_free (ui_file);
368
369 if (!ret)
370 {
371 return NULL;
372 }
373
374 gtk_window_set_modal (GTK_WINDOW (widget->dialog), TRUE);
375
376 g_settings_bind (widget->settings, SETTINGS_KEY_INTERACTIVE_COMPLETION,
377 widget->interactive_completion, "active",
378 G_SETTINGS_BIND_DEFAULT | G_SETTINGS_BIND_GET_NO_CHANGES);
379
380 g_settings_bind (widget->settings, SETTINGS_KEY_MINIMUM_WORD_SIZE,
381 widget->min_word_size, "value",
382 G_SETTINGS_BIND_DEFAULT | G_SETTINGS_BIND_GET_NO_CHANGES);
383
384 g_signal_connect (widget->dialog, "destroy",
385 G_CALLBACK (configure_widget_destroyed), widget);
386
387 gtk_widget_show (GTK_WIDGET (widget->dialog));
388 g_signal_connect (widget->dialog, "response",
389 G_CALLBACK (dialog_response_cb), widget);
390
391 return widget;
392 }
393
394 static GtkWidget *
xed_wordcompletion_create_configure_widget(PeasGtkConfigurable * configurable)395 xed_wordcompletion_create_configure_widget (PeasGtkConfigurable *configurable)
396 {
397
398 WordCompletionConfigureWidget *widget;
399
400 widget = get_configure_widget (XED_WORDCOMPLETION_PLUGIN (configurable));
401
402 return widget->dialog;
403 }
404
405 static void
xed_wordcompletion_plugin_class_init(XedWordCompletionPluginClass * klass)406 xed_wordcompletion_plugin_class_init (XedWordCompletionPluginClass *klass)
407 {
408 GObjectClass *object_class = G_OBJECT_CLASS (klass);
409
410 object_class->finalize = xed_wordcompletion_plugin_finalize;
411 object_class->dispose = xed_wordcompletion_plugin_dispose;
412 object_class->set_property = xed_wordcompletion_plugin_set_property;
413 object_class->get_property = xed_wordcompletion_plugin_get_property;
414
415 g_object_class_override_property (object_class, PROP_WINDOW, "window");
416 g_object_class_override_property (object_class, PROP_VIEW, "view");
417 }
418
419 static void
xed_wordcompletion_plugin_class_finalize(XedWordCompletionPluginClass * klass)420 xed_wordcompletion_plugin_class_finalize (XedWordCompletionPluginClass *klass)
421 {
422 }
423
424 static void
xed_window_activatable_iface_init(XedWindowActivatableInterface * iface)425 xed_window_activatable_iface_init (XedWindowActivatableInterface *iface)
426 {
427 iface->activate = xed_wordcompletion_window_activate;
428 iface->deactivate = xed_wordcompletion_window_deactivate;
429 }
430
431 static void
xed_view_activatable_iface_init(XedViewActivatableInterface * iface)432 xed_view_activatable_iface_init (XedViewActivatableInterface *iface)
433 {
434 iface->activate = xed_wordcompletion_view_activate;
435 iface->deactivate = xed_wordcompletion_view_deactivate;
436 }
437
438 static void
peas_gtk_configurable_iface_init(PeasGtkConfigurableInterface * iface)439 peas_gtk_configurable_iface_init (PeasGtkConfigurableInterface *iface)
440 {
441 iface->create_configure_widget = xed_wordcompletion_create_configure_widget;
442 }
443
444 G_MODULE_EXPORT void
peas_register_types(PeasObjectModule * module)445 peas_register_types (PeasObjectModule *module)
446 {
447 xed_wordcompletion_plugin_register_type (G_TYPE_MODULE (module));
448
449 peas_object_module_register_extension_type (module,
450 XED_TYPE_WINDOW_ACTIVATABLE,
451 XED_TYPE_WORDCOMPLETION_PLUGIN);
452
453 peas_object_module_register_extension_type (module,
454 XED_TYPE_VIEW_ACTIVATABLE,
455 XED_TYPE_WORDCOMPLETION_PLUGIN);
456
457 peas_object_module_register_extension_type (module,
458 PEAS_GTK_TYPE_CONFIGURABLE,
459 XED_TYPE_WORDCOMPLETION_PLUGIN);
460 }
461