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