1 /*
2 * pluma-sort-plugin.c
3 *
4 * Original author: Carlo Borreo <borreo@softhome.net>
5 * Ported to Pluma2 by Lee Mallabone <mate@fonicmonkey.net>
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 St, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 * $Id$
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include "pluma-sort-plugin.h"
29
30 #include <string.h>
31 #include <glib/gi18n-lib.h>
32 #include <gmodule.h>
33
34 #include <pluma/pluma-window-activatable.h>
35 #include <pluma/pluma-window.h>
36 #include <pluma/pluma-debug.h>
37 #include <pluma/pluma-utils.h>
38 #include <pluma/pluma-help.h>
39
40 #define MENU_PATH "/MenuBar/EditMenu/EditOps_6"
41
42 static void peas_activatable_iface_init (PlumaWindowActivatableInterface *iface);
43
44 enum {
45 PROP_0,
46 PROP_WINDOW
47 };
48
49 struct _PlumaSortPluginPrivate
50 {
51 PlumaWindow *window;
52
53 GtkActionGroup *ui_action_group;
54 guint ui_id;
55
56 GtkWidget *dialog;
57 GtkWidget *col_num_spinbutton;
58 GtkWidget *reverse_order_checkbutton;
59 GtkWidget *ignore_case_checkbutton;
60 GtkWidget *remove_dups_checkbutton;
61
62 GtkTextIter start, end; /* selection */
63 };
64
65 G_DEFINE_DYNAMIC_TYPE_EXTENDED (PlumaSortPlugin,
66 pluma_sort_plugin,
67 PEAS_TYPE_EXTENSION_BASE,
68 0,
69 G_ADD_PRIVATE_DYNAMIC (PlumaSortPlugin)
70 G_IMPLEMENT_INTERFACE_DYNAMIC (PLUMA_TYPE_WINDOW_ACTIVATABLE,
71 peas_activatable_iface_init))
72
73 static void sort_cb (GtkAction *action, PlumaSortPlugin *plugin);
74
75 static const GtkActionEntry action_entries[] =
76 {
77 { "Sort",
78 "view-sort-ascending",
79 N_("S_ort..."),
80 NULL,
81 N_("Sort the current document or selection"),
82 G_CALLBACK (sort_cb) }
83 };
84
85 static void
do_sort(PlumaSortPlugin * plugin)86 do_sort (PlumaSortPlugin *plugin)
87 {
88 PlumaSortPluginPrivate *priv;
89 PlumaDocument *doc;
90 GtkSourceSortFlags sort_flags = 0;
91 gint starting_column;
92
93 pluma_debug (DEBUG_PLUGINS);
94
95 priv = plugin->priv;
96
97 doc = pluma_window_get_active_document (priv->window);
98 g_return_if_fail (doc != NULL);
99
100 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->ignore_case_checkbutton)))
101 {
102 sort_flags |= GTK_SOURCE_SORT_FLAGS_CASE_SENSITIVE;
103 }
104
105 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->reverse_order_checkbutton)))
106 {
107 sort_flags |= GTK_SOURCE_SORT_FLAGS_REVERSE_ORDER;
108 }
109
110 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->remove_dups_checkbutton)))
111 {
112 sort_flags |= GTK_SOURCE_SORT_FLAGS_REMOVE_DUPLICATES;
113 }
114
115 starting_column = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->col_num_spinbutton)) - 1;
116
117 gtk_source_buffer_sort_lines (GTK_SOURCE_BUFFER (doc),
118 &priv->start,
119 &priv->end,
120 sort_flags,
121 starting_column);
122
123 pluma_debug_message (DEBUG_PLUGINS, "Done.");
124 }
125
126 static void
sort_dialog_response_handler(GtkDialog * dialog,gint res_id,PlumaSortPlugin * plugin)127 sort_dialog_response_handler (GtkDialog *dialog,
128 gint res_id,
129 PlumaSortPlugin *plugin)
130 {
131 pluma_debug (DEBUG_PLUGINS);
132
133 switch (res_id)
134 {
135 case GTK_RESPONSE_OK:
136 do_sort (plugin);
137 gtk_widget_destroy (GTK_WIDGET(dialog));
138 break;
139
140 case GTK_RESPONSE_HELP:
141 pluma_help_display (GTK_WINDOW (dialog),
142 NULL,
143 "pluma-sort-plugin");
144 break;
145
146 case GTK_RESPONSE_CANCEL:
147 gtk_widget_destroy (GTK_WIDGET(dialog));
148 break;
149 }
150 }
151
152 /* NOTE: we store the current selection in the dialog since focusing
153 * the text field (like the combo box) looses the documnent selection.
154 * Storing the selection ONLY works because the dialog is modal */
155 static void
get_current_selection(PlumaSortPlugin * plugin)156 get_current_selection (PlumaSortPlugin *plugin)
157 {
158 PlumaSortPluginPrivate *priv;
159 PlumaDocument *doc;
160
161 pluma_debug (DEBUG_PLUGINS);
162
163 priv = plugin->priv;
164 doc = pluma_window_get_active_document (priv->window);
165
166 if (!gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc),
167 &priv->start,
168 &priv->end))
169 {
170 /* No selection, get the whole document. */
171 gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc),
172 &priv->start,
173 &priv->end);
174 }
175 }
176
177 static void
create_sort_dialog(PlumaSortPlugin * plugin)178 create_sort_dialog (PlumaSortPlugin *plugin)
179 {
180 PlumaSortPluginPrivate *priv;
181 GtkWidget *error_widget;
182 gboolean ret;
183 gchar *data_dir;
184 gchar *ui_file;
185
186 pluma_debug (DEBUG_PLUGINS);
187
188 priv = plugin->priv;
189
190 data_dir = peas_extension_base_get_data_dir (PEAS_EXTENSION_BASE (plugin));
191 ui_file = g_build_filename (data_dir, "sort.ui", NULL);
192 ret = pluma_utils_get_ui_objects (ui_file,
193 NULL,
194 &error_widget,
195 "sort_dialog", &priv->dialog,
196 "reverse_order_checkbutton", &priv->reverse_order_checkbutton,
197 "col_num_spinbutton", &priv->col_num_spinbutton,
198 "ignore_case_checkbutton", &priv->ignore_case_checkbutton,
199 "remove_dups_checkbutton", &priv->remove_dups_checkbutton,
200 NULL);
201 g_free (data_dir);
202 g_free (ui_file);
203
204 if (!ret)
205 {
206 const gchar *err_message;
207
208 err_message = gtk_label_get_label (GTK_LABEL (error_widget));
209 pluma_warning (GTK_WINDOW (priv->window),
210 "%s", err_message);
211
212 gtk_widget_destroy (error_widget);
213
214 return;
215 }
216
217 g_signal_connect (priv->dialog,
218 "destroy",
219 G_CALLBACK (gtk_widget_destroyed),
220 &priv->dialog);
221
222 g_signal_connect (priv->dialog,
223 "response",
224 G_CALLBACK (sort_dialog_response_handler),
225 plugin);
226
227 get_current_selection (plugin);
228 }
229
230 static void
sort_cb(GtkAction * action,PlumaSortPlugin * plugin)231 sort_cb (GtkAction *action,
232 PlumaSortPlugin *plugin)
233 {
234 PlumaSortPluginPrivate *priv;
235 GtkWindowGroup *wg;
236
237 pluma_debug (DEBUG_PLUGINS);
238
239 priv = plugin->priv;
240
241 create_sort_dialog (plugin);
242
243 wg = pluma_window_get_group (priv->window);
244 gtk_window_group_add_window (wg,
245 GTK_WINDOW (priv->dialog));
246
247 gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
248 GTK_WINDOW (priv->window));
249
250 gtk_window_set_modal (GTK_WINDOW (priv->dialog),
251 TRUE);
252
253 gtk_widget_show (GTK_WIDGET (priv->dialog));
254 }
255
256 static void
pluma_sort_plugin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)257 pluma_sort_plugin_set_property (GObject *object,
258 guint prop_id,
259 const GValue *value,
260 GParamSpec *pspec)
261 {
262 PlumaSortPlugin *plugin = PLUMA_SORT_PLUGIN (object);
263
264 switch (prop_id)
265 {
266 case PROP_WINDOW:
267 plugin->priv->window = PLUMA_WINDOW (g_value_dup_object (value));
268 break;
269
270 default:
271 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
272 break;
273 }
274 }
275
276 static void
pluma_sort_plugin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)277 pluma_sort_plugin_get_property (GObject *object,
278 guint prop_id,
279 GValue *value,
280 GParamSpec *pspec)
281 {
282 PlumaSortPlugin *plugin = PLUMA_SORT_PLUGIN (object);
283
284 switch (prop_id)
285 {
286 case PROP_WINDOW:
287 g_value_set_object (value, plugin->priv->window);
288 break;
289
290 default:
291 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292 break;
293 }
294 }
295
296 static void
update_ui(PlumaSortPlugin * plugin)297 update_ui (PlumaSortPlugin *plugin)
298 {
299 PlumaView *view;
300
301 pluma_debug (DEBUG_PLUGINS);
302
303 view = pluma_window_get_active_view (plugin->priv->window);
304
305 gtk_action_group_set_sensitive (plugin->priv->ui_action_group,
306 (view != NULL) &&
307 gtk_text_view_get_editable (GTK_TEXT_VIEW (view)));
308 }
309
310 static void
pluma_sort_plugin_activate(PlumaWindowActivatable * activatable)311 pluma_sort_plugin_activate (PlumaWindowActivatable *activatable)
312 {
313 PlumaSortPluginPrivate *priv;
314 GtkUIManager *manager;
315
316 pluma_debug (DEBUG_PLUGINS);
317
318 priv = PLUMA_SORT_PLUGIN (activatable)->priv;
319
320 manager = pluma_window_get_ui_manager (priv->window);
321
322 priv->ui_action_group = gtk_action_group_new ("PlumaSortPluginActions");
323 gtk_action_group_set_translation_domain (priv->ui_action_group,
324 GETTEXT_PACKAGE);
325 gtk_action_group_add_actions (priv->ui_action_group,
326 action_entries,
327 G_N_ELEMENTS (action_entries),
328 activatable);
329
330 gtk_ui_manager_insert_action_group (manager,
331 priv->ui_action_group,
332 -1);
333
334 priv->ui_id = gtk_ui_manager_new_merge_id (manager);
335
336 gtk_ui_manager_add_ui (manager,
337 priv->ui_id,
338 MENU_PATH,
339 "Sort",
340 "Sort",
341 GTK_UI_MANAGER_MENUITEM,
342 FALSE);
343
344 update_ui (PLUMA_SORT_PLUGIN (activatable));
345 }
346
347 static void
pluma_sort_plugin_deactivate(PlumaWindowActivatable * activatable)348 pluma_sort_plugin_deactivate (PlumaWindowActivatable *activatable)
349 {
350 PlumaSortPluginPrivate *priv;
351 GtkUIManager *manager;
352
353 pluma_debug (DEBUG_PLUGINS);
354
355 priv = PLUMA_SORT_PLUGIN (activatable)->priv;
356
357 manager = pluma_window_get_ui_manager (priv->window);
358
359 gtk_ui_manager_remove_ui (manager,
360 priv->ui_id);
361 gtk_ui_manager_remove_action_group (manager,
362 priv->ui_action_group);
363 }
364
365 static void
pluma_sort_plugin_update_state(PlumaWindowActivatable * activatable)366 pluma_sort_plugin_update_state (PlumaWindowActivatable *activatable)
367 {
368 pluma_debug (DEBUG_PLUGINS);
369
370 update_ui (PLUMA_SORT_PLUGIN (activatable));
371 }
372
373 static void
pluma_sort_plugin_init(PlumaSortPlugin * plugin)374 pluma_sort_plugin_init (PlumaSortPlugin *plugin)
375 {
376 pluma_debug_message (DEBUG_PLUGINS, "PlumaSortPlugin initializing");
377
378 plugin->priv = pluma_sort_plugin_get_instance_private (plugin);
379 }
380
381 static void
pluma_sort_plugin_dispose(GObject * object)382 pluma_sort_plugin_dispose (GObject *object)
383 {
384 PlumaSortPlugin *plugin = PLUMA_SORT_PLUGIN (object);
385
386 pluma_debug_message (DEBUG_PLUGINS, "PlumaSortPlugin disposing");
387
388 g_clear_object (&plugin->priv->window);
389 g_clear_object (&plugin->priv->ui_action_group);
390
391 G_OBJECT_CLASS (pluma_sort_plugin_parent_class)->dispose (object);
392 }
393
394 static void
pluma_sort_plugin_class_init(PlumaSortPluginClass * klass)395 pluma_sort_plugin_class_init (PlumaSortPluginClass *klass)
396 {
397 GObjectClass *object_class = G_OBJECT_CLASS (klass);
398
399 object_class->dispose = pluma_sort_plugin_dispose;
400 object_class->set_property = pluma_sort_plugin_set_property;
401 object_class->get_property = pluma_sort_plugin_get_property;
402
403 g_object_class_override_property (object_class, PROP_WINDOW, "window");
404 }
405
406 static void
pluma_sort_plugin_class_finalize(PlumaSortPluginClass * klass)407 pluma_sort_plugin_class_finalize (PlumaSortPluginClass *klass)
408 {
409 /* dummy function - used by G_DEFINE_DYNAMIC_TYPE_EXTENDED */
410 }
411
412 static void
peas_activatable_iface_init(PlumaWindowActivatableInterface * iface)413 peas_activatable_iface_init (PlumaWindowActivatableInterface *iface)
414 {
415 iface->activate = pluma_sort_plugin_activate;
416 iface->deactivate = pluma_sort_plugin_deactivate;
417 iface->update_state = pluma_sort_plugin_update_state;
418 }
419
420 G_MODULE_EXPORT void
peas_register_types(PeasObjectModule * module)421 peas_register_types (PeasObjectModule *module)
422 {
423 pluma_sort_plugin_register_type (G_TYPE_MODULE (module));
424
425 peas_object_module_register_extension_type (module,
426 PLUMA_TYPE_WINDOW_ACTIVATABLE,
427 PLUMA_TYPE_SORT_PLUGIN);
428 }
429