1 /*
2 * xed-docinfo-plugin.c
3 *
4 * Copyright (C) 2002-2005 Paolo Maggi
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 */
21
22 #include <config.h>
23 #include <string.h> /* For strlen (...) */
24 #include <glib/gi18n.h>
25 #include <pango/pango-break.h>
26 #include <gmodule.h>
27
28 #include <xed/xed-window.h>
29 #include <xed/xed-window-activatable.h>
30 #include <xed/xed-debug.h>
31 #include <xed/xed-utils.h>
32
33 #include "xed-docinfo-plugin.h"
34
35 #define MENU_PATH "/MenuBar/ToolsMenu/ToolsOps_2"
36
37 struct _XedDocInfoPluginPrivate
38 {
39 XedWindow *window;
40
41 GtkActionGroup *action_group;
42 guint ui_id;
43
44 GtkWidget *dialog;
45 GtkWidget *file_name_label;
46 GtkWidget *lines_label;
47 GtkWidget *words_label;
48 GtkWidget *chars_label;
49 GtkWidget *chars_ns_label;
50 GtkWidget *bytes_label;
51 GtkWidget *selection_vbox;
52 GtkWidget *selected_lines_label;
53 GtkWidget *selected_words_label;
54 GtkWidget *selected_chars_label;
55 GtkWidget *selected_chars_ns_label;
56 GtkWidget *selected_bytes_label;
57 };
58
59 enum
60 {
61 PROP_0,
62 PROP_WINDOW
63 };
64
65 static void xed_window_activatable_iface_init (XedWindowActivatableInterface *iface);
66
67 G_DEFINE_DYNAMIC_TYPE_EXTENDED (XedDocInfoPlugin,
68 xed_docinfo_plugin,
69 PEAS_TYPE_EXTENSION_BASE,
70 0,
71 G_IMPLEMENT_INTERFACE_DYNAMIC (XED_TYPE_WINDOW_ACTIVATABLE,
72 xed_window_activatable_iface_init)
73 G_ADD_PRIVATE_DYNAMIC (XedDocInfoPlugin))
74
75 static void
calculate_info(XedDocument * doc,GtkTextIter * start,GtkTextIter * end,gint * chars,gint * words,gint * white_chars,gint * bytes)76 calculate_info (XedDocument *doc,
77 GtkTextIter *start,
78 GtkTextIter *end,
79 gint *chars,
80 gint *words,
81 gint *white_chars,
82 gint *bytes)
83 {
84 gchar *text;
85
86 xed_debug (DEBUG_PLUGINS);
87
88 text = gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc), start, end, TRUE);
89
90 *chars = g_utf8_strlen (text, -1);
91 *bytes = strlen (text);
92
93 if (*chars > 0)
94 {
95 PangoLogAttr *attrs;
96 gint i;
97
98 attrs = g_new0 (PangoLogAttr, *chars + 1);
99
100 pango_get_log_attrs (text, -1, 0, pango_language_from_string ("C"), attrs, *chars + 1);
101
102 for (i = 0; i < (*chars); i++)
103 {
104 if (attrs[i].is_white)
105 {
106 ++(*white_chars);
107 }
108
109 if (attrs[i].is_word_start)
110 {
111 ++(*words);
112 }
113 }
114
115 g_free (attrs);
116 }
117 else
118 {
119 *white_chars = 0;
120 *words = 0;
121 }
122
123 g_free (text);
124 }
125
126 static void
update_document_info(XedDocInfoPlugin * plugin,XedDocument * doc)127 update_document_info (XedDocInfoPlugin *plugin,
128 XedDocument *doc)
129 {
130 XedDocInfoPluginPrivate *priv;
131 GtkTextIter start, end;
132 gint words = 0;
133 gint chars = 0;
134 gint white_chars = 0;
135 gint lines = 0;
136 gint bytes = 0;
137 gchar *tmp_str;
138 gchar *doc_name;
139
140 xed_debug (DEBUG_PLUGINS);
141
142 priv = plugin->priv;
143
144 gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc), &start, &end);
145
146 lines = gtk_text_buffer_get_line_count (GTK_TEXT_BUFFER (doc));
147
148 calculate_info (doc, &start, &end, &chars, &words, &white_chars, &bytes);
149
150 if (chars == 0)
151 {
152 lines = 0;
153 }
154
155 xed_debug_message (DEBUG_PLUGINS, "Chars: %d", chars);
156 xed_debug_message (DEBUG_PLUGINS, "Lines: %d", lines);
157 xed_debug_message (DEBUG_PLUGINS, "Words: %d", words);
158 xed_debug_message (DEBUG_PLUGINS, "Chars non-space: %d", chars - white_chars);
159 xed_debug_message (DEBUG_PLUGINS, "Bytes: %d", bytes);
160
161 doc_name = xed_document_get_short_name_for_display (doc);
162 tmp_str = g_strdup_printf ("<span weight=\"bold\">%s</span>", doc_name);
163 gtk_label_set_markup (GTK_LABEL (priv->file_name_label), tmp_str);
164 g_free (doc_name);
165 g_free (tmp_str);
166
167 tmp_str = g_strdup_printf("%d", lines);
168 gtk_label_set_text (GTK_LABEL (priv->lines_label), tmp_str);
169 g_free (tmp_str);
170
171 tmp_str = g_strdup_printf("%d", words);
172 gtk_label_set_text (GTK_LABEL (priv->words_label), tmp_str);
173 g_free (tmp_str);
174
175 tmp_str = g_strdup_printf("%d", chars);
176 gtk_label_set_text (GTK_LABEL (priv->chars_label), tmp_str);
177 g_free (tmp_str);
178
179 tmp_str = g_strdup_printf("%d", chars - white_chars);
180 gtk_label_set_text (GTK_LABEL (priv->chars_ns_label), tmp_str);
181 g_free (tmp_str);
182
183 tmp_str = g_strdup_printf("%d", bytes);
184 gtk_label_set_text (GTK_LABEL (priv->bytes_label), tmp_str);
185 g_free (tmp_str);
186 }
187
188 static void
update_selection_info(XedDocInfoPlugin * plugin,XedDocument * doc)189 update_selection_info (XedDocInfoPlugin *plugin,
190 XedDocument *doc)
191 {
192 XedDocInfoPluginPrivate *priv;
193 gboolean sel;
194 GtkTextIter start, end;
195 gint words = 0;
196 gint chars = 0;
197 gint white_chars = 0;
198 gint lines = 0;
199 gint bytes = 0;
200 gchar *tmp_str;
201
202 xed_debug (DEBUG_PLUGINS);
203
204 priv = plugin->priv;
205
206 sel = gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc), &start, &end);
207
208 if (sel)
209 {
210 lines = gtk_text_iter_get_line (&end) - gtk_text_iter_get_line (&start) + 1;
211
212 calculate_info (doc, &start, &end, &chars, &words, &white_chars, &bytes);
213
214 xed_debug_message (DEBUG_PLUGINS, "Selected chars: %d", chars);
215 xed_debug_message (DEBUG_PLUGINS, "Selected lines: %d", lines);
216 xed_debug_message (DEBUG_PLUGINS, "Selected words: %d", words);
217 xed_debug_message (DEBUG_PLUGINS, "Selected chars non-space: %d", chars - white_chars);
218 xed_debug_message (DEBUG_PLUGINS, "Selected bytes: %d", bytes);
219
220 gtk_widget_set_sensitive (priv->selection_vbox, TRUE);
221 }
222 else
223 {
224 gtk_widget_set_sensitive (priv->selection_vbox, FALSE);
225
226 xed_debug_message (DEBUG_PLUGINS, "Selection empty");
227 }
228
229 if (chars == 0)
230 {
231 lines = 0;
232 }
233
234 tmp_str = g_strdup_printf("%d", lines);
235 gtk_label_set_text (GTK_LABEL (priv->selected_lines_label), tmp_str);
236 g_free (tmp_str);
237
238 tmp_str = g_strdup_printf("%d", words);
239 gtk_label_set_text (GTK_LABEL (priv->selected_words_label), tmp_str);
240 g_free (tmp_str);
241
242 tmp_str = g_strdup_printf("%d", chars);
243 gtk_label_set_text (GTK_LABEL (priv->selected_chars_label), tmp_str);
244 g_free (tmp_str);
245
246 tmp_str = g_strdup_printf("%d", chars - white_chars);
247 gtk_label_set_text (GTK_LABEL (priv->selected_chars_ns_label), tmp_str);
248 g_free (tmp_str);
249
250 tmp_str = g_strdup_printf("%d", bytes);
251 gtk_label_set_text (GTK_LABEL (priv->selected_bytes_label), tmp_str);
252 g_free (tmp_str);
253 }
254
255 static void
docinfo_dialog_response_cb(GtkDialog * widget,gint res_id,XedDocInfoPlugin * plugin)256 docinfo_dialog_response_cb (GtkDialog *widget,
257 gint res_id,
258 XedDocInfoPlugin *plugin)
259 {
260 XedDocInfoPluginPrivate *priv;
261
262 priv = plugin->priv;
263
264 switch (res_id)
265 {
266 case GTK_RESPONSE_CLOSE:
267 {
268 xed_debug_message (DEBUG_PLUGINS, "GTK_RESPONSE_CLOSE");
269 gtk_widget_destroy (priv->dialog);
270 break;
271 }
272
273 case GTK_RESPONSE_OK:
274 {
275 XedDocument *doc;
276
277 xed_debug_message (DEBUG_PLUGINS, "GTK_RESPONSE_OK");
278
279 doc = xed_window_get_active_document (priv->window);
280
281 update_document_info (plugin, doc);
282 update_selection_info (plugin, doc);
283 break;
284 }
285 }
286 }
287
288 static void
create_docinfo_dialog(XedDocInfoPlugin * plugin)289 create_docinfo_dialog (XedDocInfoPlugin *plugin)
290 {
291 XedDocInfoPluginPrivate *priv;
292 gchar *data_dir;
293 gchar *ui_file;
294 GtkWidget *content;
295 GtkWidget *error_widget;
296 gboolean ret;
297
298 xed_debug (DEBUG_PLUGINS);
299
300 priv = plugin->priv;
301
302 data_dir = peas_extension_base_get_data_dir (PEAS_EXTENSION_BASE (plugin));
303 ui_file = g_build_filename (data_dir, "docinfo.ui", NULL);
304 ret = xed_utils_get_ui_objects (ui_file,
305 NULL,
306 &error_widget,
307 "dialog", &priv->dialog,
308 "docinfo_dialog_content", &content,
309 "file_name_label", &priv->file_name_label,
310 "words_label", &priv->words_label,
311 "bytes_label", &priv->bytes_label,
312 "lines_label", &priv->lines_label,
313 "chars_label", &priv->chars_label,
314 "chars_ns_label", &priv->chars_ns_label,
315 "selection_vbox", &priv->selection_vbox,
316 "selected_words_label", &priv->selected_words_label,
317 "selected_bytes_label", &priv->selected_bytes_label,
318 "selected_lines_label", &priv->selected_lines_label,
319 "selected_chars_label", &priv->selected_chars_label,
320 "selected_chars_ns_label", &priv->selected_chars_ns_label,
321 NULL);
322
323 g_free (data_dir);
324 g_free (ui_file);
325
326 if (!ret)
327 {
328 const gchar *err_message;
329
330 err_message = gtk_label_get_label (GTK_LABEL (error_widget));
331 xed_warning (GTK_WINDOW (priv->window), "%s", err_message);
332
333 gtk_widget_destroy (error_widget);
334
335 return;
336 }
337
338 gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_OK);
339 gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), GTK_WINDOW (priv->window));
340
341 g_signal_connect (priv->dialog, "destroy",
342 G_CALLBACK (gtk_widget_destroyed), &priv->dialog);
343 g_signal_connect (priv->dialog, "response",
344 G_CALLBACK (docinfo_dialog_response_cb), plugin);
345 }
346
347 static void
docinfo_cb(GtkAction * action,XedDocInfoPlugin * plugin)348 docinfo_cb (GtkAction *action,
349 XedDocInfoPlugin *plugin)
350 {
351 XedDocInfoPluginPrivate *priv;
352 XedDocument *doc;
353
354 xed_debug (DEBUG_PLUGINS);
355
356 priv = plugin->priv;
357
358 doc = xed_window_get_active_document (priv->window);
359
360 if (priv->dialog != NULL)
361 {
362 gtk_window_present (GTK_WINDOW (priv->dialog));
363 gtk_widget_grab_focus (GTK_WIDGET (priv->dialog));
364 }
365 else
366 {
367 create_docinfo_dialog (plugin);
368 gtk_widget_show (GTK_WIDGET (priv->dialog));
369 }
370
371 update_document_info (plugin, doc);
372 update_selection_info (plugin, doc);
373 }
374
375 static const GtkActionEntry action_entries[] =
376 {
377 { "DocumentStatistics",
378 NULL,
379 N_("_Document Statistics"),
380 NULL,
381 N_("Get statistical information on the current document"),
382 G_CALLBACK (docinfo_cb) }
383 };
384
385 static void
xed_docinfo_plugin_init(XedDocInfoPlugin * plugin)386 xed_docinfo_plugin_init (XedDocInfoPlugin *plugin)
387 {
388 xed_debug_message (DEBUG_PLUGINS, "XedDocInfoPlugin initializing");
389 plugin->priv = xed_docinfo_plugin_get_instance_private (plugin);
390 }
391
392 static void
xed_docinfo_plugin_dispose(GObject * object)393 xed_docinfo_plugin_dispose (GObject *object)
394 {
395 XedDocInfoPlugin *plugin = XED_DOCINFO_PLUGIN (object);
396
397 xed_debug_message (DEBUG_PLUGINS, "XedDocInfoPlugin dispose");
398
399 g_clear_object (&plugin->priv->action_group);
400 g_clear_object (&plugin->priv->window);
401
402 G_OBJECT_CLASS (xed_docinfo_plugin_parent_class)->dispose (object);
403 }
404
405 static void
xed_docinfo_plugin_finalize(GObject * object)406 xed_docinfo_plugin_finalize (GObject *object)
407 {
408 xed_debug_message (DEBUG_PLUGINS, "XedDocInfoPlugin finalizing");
409
410 G_OBJECT_CLASS (xed_docinfo_plugin_parent_class)->finalize (object);
411 }
412
413 static void
xed_docinfo_plugin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)414 xed_docinfo_plugin_set_property (GObject *object,
415 guint prop_id,
416 const GValue *value,
417 GParamSpec *pspec)
418 {
419 XedDocInfoPlugin *plugin = XED_DOCINFO_PLUGIN (object);
420
421 switch (prop_id)
422 {
423 case PROP_WINDOW:
424 plugin->priv->window = XED_WINDOW (g_value_dup_object (value));
425 break;
426 default:
427 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
428 break;
429 }
430 }
431
432 static void
xed_docinfo_plugin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)433 xed_docinfo_plugin_get_property (GObject *object,
434 guint prop_id,
435 GValue *value,
436 GParamSpec *pspec)
437 {
438 XedDocInfoPlugin *plugin = XED_DOCINFO_PLUGIN (object);
439
440 switch (prop_id)
441 {
442 case PROP_WINDOW:
443 g_value_set_object (value, plugin->priv->window);
444 break;
445 default:
446 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
447 break;
448 }
449 }
450
451 static void
update_ui(XedDocInfoPlugin * plugin)452 update_ui (XedDocInfoPlugin *plugin)
453 {
454 XedDocInfoPluginPrivate *priv;
455 XedView *view;
456
457 xed_debug (DEBUG_PLUGINS);
458
459 priv = plugin->priv;
460
461 view = xed_window_get_active_view (priv->window);
462
463 gtk_action_group_set_sensitive (priv->action_group, (view != NULL));
464
465 if (priv->dialog != NULL)
466 {
467 gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog), GTK_RESPONSE_OK, (view != NULL));
468 }
469 }
470
471 static void
xed_docinfo_plugin_activate(XedWindowActivatable * activatable)472 xed_docinfo_plugin_activate (XedWindowActivatable *activatable)
473 {
474 XedDocInfoPluginPrivate *priv;
475 GtkUIManager *manager;
476
477 xed_debug (DEBUG_PLUGINS);
478
479 priv = XED_DOCINFO_PLUGIN (activatable)->priv;
480 manager = xed_window_get_ui_manager (priv->window);
481
482 priv->action_group = gtk_action_group_new ("XedDocinfoPluginActions");
483 gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
484 gtk_action_group_add_actions (priv->action_group, action_entries, G_N_ELEMENTS (action_entries), activatable);
485
486 gtk_ui_manager_insert_action_group (manager, priv->action_group, -1);
487
488 priv->ui_id = gtk_ui_manager_new_merge_id (manager);
489
490 gtk_ui_manager_add_ui (manager,
491 priv->ui_id,
492 MENU_PATH,
493 "DocumentStatistics",
494 "DocumentStatistics",
495 GTK_UI_MANAGER_MENUITEM,
496 FALSE);
497
498 update_ui (XED_DOCINFO_PLUGIN (activatable));
499 }
500
501 static void
xed_docinfo_plugin_deactivate(XedWindowActivatable * activatable)502 xed_docinfo_plugin_deactivate (XedWindowActivatable *activatable)
503 {
504 XedDocInfoPluginPrivate *priv;
505 GtkUIManager *manager;
506
507 xed_debug (DEBUG_PLUGINS);
508
509 priv = XED_DOCINFO_PLUGIN (activatable)->priv;
510 manager = xed_window_get_ui_manager (priv->window);
511
512 gtk_ui_manager_remove_ui (manager, priv->ui_id);
513 gtk_ui_manager_remove_action_group (manager, priv->action_group);
514 }
515
516 static void
xed_docinfo_plugin_update_state(XedWindowActivatable * activatable)517 xed_docinfo_plugin_update_state (XedWindowActivatable *activatable)
518 {
519 xed_debug (DEBUG_PLUGINS);
520
521 update_ui (XED_DOCINFO_PLUGIN (activatable));
522 }
523
524 static void
xed_docinfo_plugin_class_init(XedDocInfoPluginClass * klass)525 xed_docinfo_plugin_class_init (XedDocInfoPluginClass *klass)
526 {
527 GObjectClass *object_class = G_OBJECT_CLASS (klass);
528
529 object_class->dispose = xed_docinfo_plugin_dispose;
530 object_class->finalize = xed_docinfo_plugin_finalize;
531 object_class->set_property = xed_docinfo_plugin_set_property;
532 object_class->get_property = xed_docinfo_plugin_get_property;
533
534 g_object_class_override_property (object_class, PROP_WINDOW, "window");
535 }
536
537 static void
xed_window_activatable_iface_init(XedWindowActivatableInterface * iface)538 xed_window_activatable_iface_init (XedWindowActivatableInterface *iface)
539 {
540 iface->activate = xed_docinfo_plugin_activate;
541 iface->deactivate = xed_docinfo_plugin_deactivate;
542 iface->update_state = xed_docinfo_plugin_update_state;
543 }
544
545 static void
xed_docinfo_plugin_class_finalize(XedDocInfoPluginClass * klass)546 xed_docinfo_plugin_class_finalize (XedDocInfoPluginClass *klass)
547 {
548 }
549
550 G_MODULE_EXPORT void
peas_register_types(PeasObjectModule * module)551 peas_register_types (PeasObjectModule *module)
552 {
553 xed_docinfo_plugin_register_type (G_TYPE_MODULE (module));
554
555 peas_object_module_register_extension_type (module,
556 XED_TYPE_WINDOW_ACTIVATABLE,
557 XED_TYPE_DOCINFO_PLUGIN);
558 }
559