/*
* gedit-docinfo-plugin.c
*
* Copyright (C) 2002-2005 Paolo Maggi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*
*/
#include "config.h"
#include "gedit-docinfo-plugin.h"
#include /* For strlen (...) */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct _GeditDocinfoPluginPrivate
{
GeditWindow *window;
GSimpleAction *action;
GtkWidget *dialog;
GtkWidget *header_bar;
GtkWidget *lines_label;
GtkWidget *words_label;
GtkWidget *chars_label;
GtkWidget *chars_ns_label;
GtkWidget *bytes_label;
GtkWidget *document_label;
GtkWidget *document_lines_label;
GtkWidget *document_words_label;
GtkWidget *document_chars_label;
GtkWidget *document_chars_ns_label;
GtkWidget *document_bytes_label;
GtkWidget *selection_label;
GtkWidget *selected_lines_label;
GtkWidget *selected_words_label;
GtkWidget *selected_chars_label;
GtkWidget *selected_chars_ns_label;
GtkWidget *selected_bytes_label;
GeditApp *app;
GeditMenuExtension *menu_ext;
};
enum
{
PROP_0,
PROP_WINDOW,
PROP_APP
};
static void gedit_app_activatable_iface_init (GeditAppActivatableInterface *iface);
static void gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface);
G_DEFINE_DYNAMIC_TYPE_EXTENDED (GeditDocinfoPlugin,
gedit_docinfo_plugin,
PEAS_TYPE_EXTENSION_BASE,
0,
G_IMPLEMENT_INTERFACE_DYNAMIC (GEDIT_TYPE_APP_ACTIVATABLE,
gedit_app_activatable_iface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC (GEDIT_TYPE_WINDOW_ACTIVATABLE,
gedit_window_activatable_iface_init)
G_ADD_PRIVATE_DYNAMIC (GeditDocinfoPlugin))
static void
calculate_info (GeditDocument *doc,
GtkTextIter *start,
GtkTextIter *end,
gint *chars,
gint *words,
gint *white_chars,
gint *bytes)
{
gchar *text;
gedit_debug (DEBUG_PLUGINS);
text = gtk_text_buffer_get_slice (GTK_TEXT_BUFFER (doc),
start,
end,
TRUE);
*chars = g_utf8_strlen (text, -1);
*bytes = strlen (text);
if (*chars > 0)
{
PangoLogAttr *attrs;
gint i;
attrs = g_new0 (PangoLogAttr, *chars + 1);
pango_get_log_attrs (text,
-1,
0,
pango_language_from_string ("C"),
attrs,
*chars + 1);
for (i = 0; i < (*chars); i++)
{
if (attrs[i].is_white)
++(*white_chars);
if (attrs[i].is_word_start)
++(*words);
}
g_free (attrs);
}
else
{
*white_chars = 0;
*words = 0;
}
g_free (text);
}
static void
update_document_info (GeditDocinfoPlugin *plugin,
GeditDocument *doc)
{
GeditDocinfoPluginPrivate *priv;
GtkTextIter start, end;
gint words = 0;
gint chars = 0;
gint white_chars = 0;
gint lines = 0;
gint bytes = 0;
gchar *doc_name;
gchar *tmp_str;
gedit_debug (DEBUG_PLUGINS);
priv = plugin->priv;
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (doc),
&start,
&end);
lines = gtk_text_buffer_get_line_count (GTK_TEXT_BUFFER (doc));
calculate_info (doc,
&start, &end,
&chars, &words, &white_chars, &bytes);
if (chars == 0)
{
lines = 0;
}
gedit_debug_message (DEBUG_PLUGINS, "Chars: %d", chars);
gedit_debug_message (DEBUG_PLUGINS, "Lines: %d", lines);
gedit_debug_message (DEBUG_PLUGINS, "Words: %d", words);
gedit_debug_message (DEBUG_PLUGINS, "Chars non-space: %d", chars - white_chars);
gedit_debug_message (DEBUG_PLUGINS, "Bytes: %d", bytes);
doc_name = gedit_document_get_short_name_for_display (doc);
gtk_header_bar_set_subtitle (GTK_HEADER_BAR (priv->header_bar), doc_name);
g_free (doc_name);
tmp_str = g_strdup_printf("%d", lines);
gtk_label_set_text (GTK_LABEL (priv->document_lines_label), tmp_str);
g_free (tmp_str);
tmp_str = g_strdup_printf("%d", words);
gtk_label_set_text (GTK_LABEL (priv->document_words_label), tmp_str);
g_free (tmp_str);
tmp_str = g_strdup_printf("%d", chars);
gtk_label_set_text (GTK_LABEL (priv->document_chars_label), tmp_str);
g_free (tmp_str);
tmp_str = g_strdup_printf("%d", chars - white_chars);
gtk_label_set_text (GTK_LABEL (priv->document_chars_ns_label), tmp_str);
g_free (tmp_str);
tmp_str = g_strdup_printf("%d", bytes);
gtk_label_set_text (GTK_LABEL (priv->document_bytes_label), tmp_str);
g_free (tmp_str);
}
static void
update_selection_info (GeditDocinfoPlugin *plugin,
GeditDocument *doc)
{
GeditDocinfoPluginPrivate *priv;
gboolean sel;
GtkTextIter start, end;
gint words = 0;
gint chars = 0;
gint white_chars = 0;
gint lines = 0;
gint bytes = 0;
gchar *tmp_str;
gedit_debug (DEBUG_PLUGINS);
priv = plugin->priv;
sel = gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (doc),
&start,
&end);
if (sel)
{
lines = gtk_text_iter_get_line (&end) - gtk_text_iter_get_line (&start) + 1;
calculate_info (doc,
&start, &end,
&chars, &words, &white_chars, &bytes);
gedit_debug_message (DEBUG_PLUGINS, "Selected chars: %d", chars);
gedit_debug_message (DEBUG_PLUGINS, "Selected lines: %d", lines);
gedit_debug_message (DEBUG_PLUGINS, "Selected words: %d", words);
gedit_debug_message (DEBUG_PLUGINS, "Selected chars non-space: %d", chars - white_chars);
gedit_debug_message (DEBUG_PLUGINS, "Selected bytes: %d", bytes);
gtk_widget_set_sensitive (priv->selection_label, TRUE);
gtk_widget_set_sensitive (priv->selected_words_label, TRUE);
gtk_widget_set_sensitive (priv->selected_bytes_label, TRUE);
gtk_widget_set_sensitive (priv->selected_lines_label, TRUE);
gtk_widget_set_sensitive (priv->selected_chars_label, TRUE);
gtk_widget_set_sensitive (priv->selected_chars_ns_label, TRUE);
}
else
{
gedit_debug_message (DEBUG_PLUGINS, "Selection empty");
gtk_widget_set_sensitive (priv->selection_label, FALSE);
gtk_widget_set_sensitive (priv->selected_words_label, FALSE);
gtk_widget_set_sensitive (priv->selected_bytes_label, FALSE);
gtk_widget_set_sensitive (priv->selected_lines_label, FALSE);
gtk_widget_set_sensitive (priv->selected_chars_label, FALSE);
gtk_widget_set_sensitive (priv->selected_chars_ns_label, FALSE);
}
if (chars == 0)
lines = 0;
tmp_str = g_strdup_printf("%d", lines);
gtk_label_set_text (GTK_LABEL (priv->selected_lines_label), tmp_str);
g_free (tmp_str);
tmp_str = g_strdup_printf("%d", words);
gtk_label_set_text (GTK_LABEL (priv->selected_words_label), tmp_str);
g_free (tmp_str);
tmp_str = g_strdup_printf("%d", chars);
gtk_label_set_text (GTK_LABEL (priv->selected_chars_label), tmp_str);
g_free (tmp_str);
tmp_str = g_strdup_printf("%d", chars - white_chars);
gtk_label_set_text (GTK_LABEL (priv->selected_chars_ns_label), tmp_str);
g_free (tmp_str);
tmp_str = g_strdup_printf("%d", bytes);
gtk_label_set_text (GTK_LABEL (priv->selected_bytes_label), tmp_str);
g_free (tmp_str);
}
static void
docinfo_dialog_response_cb (GtkDialog *widget,
gint res_id,
GeditDocinfoPlugin *plugin)
{
GeditDocinfoPluginPrivate *priv;
gedit_debug (DEBUG_PLUGINS);
priv = plugin->priv;
switch (res_id)
{
case GTK_RESPONSE_CLOSE:
{
gedit_debug_message (DEBUG_PLUGINS, "GTK_RESPONSE_CLOSE");
gtk_widget_destroy (priv->dialog);
break;
}
case GTK_RESPONSE_OK:
{
GeditDocument *doc;
gedit_debug_message (DEBUG_PLUGINS, "GTK_RESPONSE_OK");
doc = gedit_window_get_active_document (priv->window);
update_document_info (plugin, doc);
update_selection_info (plugin, doc);
break;
}
}
}
static void
create_docinfo_dialog (GeditDocinfoPlugin *plugin)
{
GeditDocinfoPluginPrivate *priv;
GtkBuilder *builder;
gedit_debug (DEBUG_PLUGINS);
priv = plugin->priv;
builder = gtk_builder_new ();
gtk_builder_add_from_resource (builder, "/org/gnome/gedit/plugins/docinfo/ui/gedit-docinfo-plugin.ui", NULL);
priv->dialog = GTK_WIDGET (gtk_builder_get_object (builder, "dialog"));
priv->header_bar = GTK_WIDGET (gtk_builder_get_object (builder, "header_bar"));
priv->words_label = GTK_WIDGET (gtk_builder_get_object (builder, "words_label"));
priv->bytes_label = GTK_WIDGET (gtk_builder_get_object (builder, "bytes_label"));
priv->lines_label = GTK_WIDGET (gtk_builder_get_object (builder, "lines_label"));
priv->chars_label = GTK_WIDGET (gtk_builder_get_object (builder, "chars_label"));
priv->chars_ns_label = GTK_WIDGET (gtk_builder_get_object (builder, "chars_ns_label"));
priv->document_label = GTK_WIDGET (gtk_builder_get_object (builder, "document_label"));
priv->document_words_label = GTK_WIDGET (gtk_builder_get_object (builder, "document_words_label"));
priv->document_bytes_label = GTK_WIDGET (gtk_builder_get_object (builder, "document_bytes_label"));
priv->document_lines_label = GTK_WIDGET (gtk_builder_get_object (builder, "document_lines_label"));
priv->document_chars_label = GTK_WIDGET (gtk_builder_get_object (builder, "document_chars_label"));
priv->document_chars_ns_label = GTK_WIDGET (gtk_builder_get_object (builder, "document_chars_ns_label"));
priv->selection_label = GTK_WIDGET (gtk_builder_get_object (builder, "selection_label"));
priv->selected_words_label = GTK_WIDGET (gtk_builder_get_object (builder, "selected_words_label"));
priv->selected_bytes_label = GTK_WIDGET (gtk_builder_get_object (builder, "selected_bytes_label"));
priv->selected_lines_label = GTK_WIDGET (gtk_builder_get_object (builder, "selected_lines_label"));
priv->selected_chars_label = GTK_WIDGET (gtk_builder_get_object (builder, "selected_chars_label"));
priv->selected_chars_ns_label = GTK_WIDGET (gtk_builder_get_object (builder, "selected_chars_ns_label"));
g_object_unref (builder);
gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
GTK_RESPONSE_OK);
gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
GTK_WINDOW (priv->window));
g_signal_connect (priv->dialog,
"destroy",
G_CALLBACK (gtk_widget_destroyed),
&priv->dialog);
g_signal_connect (priv->dialog,
"response",
G_CALLBACK (docinfo_dialog_response_cb),
plugin);
/* We set this explictely with code since glade does not
* save the can_focus property when set to false :(
* Making sure the labels are not focusable is needed to
* prevent loosing the selection in the document when
* creating the dialog.
*/
gtk_widget_set_can_focus (priv->words_label, FALSE);
gtk_widget_set_can_focus (priv->bytes_label, FALSE);
gtk_widget_set_can_focus (priv->lines_label, FALSE);
gtk_widget_set_can_focus (priv->chars_label, FALSE);
gtk_widget_set_can_focus (priv->chars_ns_label, FALSE);
gtk_widget_set_can_focus (priv->document_label, FALSE);
gtk_widget_set_can_focus (priv->document_words_label, FALSE);
gtk_widget_set_can_focus (priv->document_bytes_label, FALSE);
gtk_widget_set_can_focus (priv->document_lines_label, FALSE);
gtk_widget_set_can_focus (priv->document_chars_label, FALSE);
gtk_widget_set_can_focus (priv->document_chars_ns_label, FALSE);
gtk_widget_set_can_focus (priv->selection_label, FALSE);
gtk_widget_set_can_focus (priv->selected_words_label, FALSE);
gtk_widget_set_can_focus (priv->selected_bytes_label, FALSE);
gtk_widget_set_can_focus (priv->selected_lines_label, FALSE);
gtk_widget_set_can_focus (priv->selected_chars_label, FALSE);
gtk_widget_set_can_focus (priv->selected_chars_ns_label, FALSE);
}
static void
docinfo_cb (GAction *action,
GVariant *parameter,
GeditDocinfoPlugin *plugin)
{
GeditDocinfoPluginPrivate *priv;
GeditDocument *doc;
gedit_debug (DEBUG_PLUGINS);
priv = plugin->priv;
doc = gedit_window_get_active_document (priv->window);
if (priv->dialog != NULL)
{
gtk_window_present (GTK_WINDOW (priv->dialog));
gtk_widget_grab_focus (GTK_WIDGET (priv->dialog));
}
else
{
create_docinfo_dialog (plugin);
gtk_widget_show (GTK_WIDGET (priv->dialog));
}
update_document_info (plugin, doc);
update_selection_info (plugin, doc);
}
static void
gedit_docinfo_plugin_init (GeditDocinfoPlugin *plugin)
{
gedit_debug_message (DEBUG_PLUGINS, "GeditDocinfoPlugin initializing");
plugin->priv = gedit_docinfo_plugin_get_instance_private (plugin);
}
static void
gedit_docinfo_plugin_dispose (GObject *object)
{
GeditDocinfoPlugin *plugin = GEDIT_DOCINFO_PLUGIN (object);
gedit_debug_message (DEBUG_PLUGINS, "GeditDocinfoPlugin dispose");
g_clear_object (&plugin->priv->action);
g_clear_object (&plugin->priv->window);
g_clear_object (&plugin->priv->menu_ext);
g_clear_object (&plugin->priv->app);
G_OBJECT_CLASS (gedit_docinfo_plugin_parent_class)->dispose (object);
}
static void
gedit_docinfo_plugin_finalize (GObject *object)
{
gedit_debug_message (DEBUG_PLUGINS, "GeditDocinfoPlugin finalizing");
G_OBJECT_CLASS (gedit_docinfo_plugin_parent_class)->finalize (object);
}
static void
gedit_docinfo_plugin_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GeditDocinfoPlugin *plugin = GEDIT_DOCINFO_PLUGIN (object);
switch (prop_id)
{
case PROP_WINDOW:
plugin->priv->window = GEDIT_WINDOW (g_value_dup_object (value));
break;
case PROP_APP:
plugin->priv->app = GEDIT_APP (g_value_dup_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gedit_docinfo_plugin_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GeditDocinfoPlugin *plugin = GEDIT_DOCINFO_PLUGIN (object);
switch (prop_id)
{
case PROP_WINDOW:
g_value_set_object (value, plugin->priv->window);
break;
case PROP_APP:
g_value_set_object (value, plugin->priv->app);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
update_ui (GeditDocinfoPlugin *plugin)
{
GeditDocinfoPluginPrivate *priv;
GeditView *view;
gedit_debug (DEBUG_PLUGINS);
priv = plugin->priv;
view = gedit_window_get_active_view (priv->window);
g_simple_action_set_enabled (G_SIMPLE_ACTION (priv->action), view != NULL);
if (priv->dialog != NULL)
{
gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog),
GTK_RESPONSE_OK,
(view != NULL));
}
}
static void
gedit_docinfo_plugin_app_activate (GeditAppActivatable *activatable)
{
GeditDocinfoPluginPrivate *priv;
GMenuItem *item;
gedit_debug (DEBUG_PLUGINS);
priv = GEDIT_DOCINFO_PLUGIN (activatable)->priv;
priv->menu_ext = gedit_app_activatable_extend_menu (activatable, "tools-section");
item = g_menu_item_new (_("_Document Statistics"), "win.docinfo");
gedit_menu_extension_append_menu_item (priv->menu_ext, item);
g_object_unref (item);
}
static void
gedit_docinfo_plugin_app_deactivate (GeditAppActivatable *activatable)
{
GeditDocinfoPluginPrivate *priv;
gedit_debug (DEBUG_PLUGINS);
priv = GEDIT_DOCINFO_PLUGIN (activatable)->priv;
g_clear_object (&priv->menu_ext);
}
static void
gedit_docinfo_plugin_window_activate (GeditWindowActivatable *activatable)
{
GeditDocinfoPluginPrivate *priv;
gedit_debug (DEBUG_PLUGINS);
priv = GEDIT_DOCINFO_PLUGIN (activatable)->priv;
priv->action = g_simple_action_new ("docinfo", NULL);
g_signal_connect (priv->action, "activate",
G_CALLBACK (docinfo_cb), activatable);
g_action_map_add_action (G_ACTION_MAP (priv->window),
G_ACTION (priv->action));
update_ui (GEDIT_DOCINFO_PLUGIN (activatable));
}
static void
gedit_docinfo_plugin_window_deactivate (GeditWindowActivatable *activatable)
{
GeditDocinfoPluginPrivate *priv;
gedit_debug (DEBUG_PLUGINS);
priv = GEDIT_DOCINFO_PLUGIN (activatable)->priv;
g_action_map_remove_action (G_ACTION_MAP (priv->window), "docinfo");
}
static void
gedit_docinfo_plugin_window_update_state (GeditWindowActivatable *activatable)
{
gedit_debug (DEBUG_PLUGINS);
update_ui (GEDIT_DOCINFO_PLUGIN (activatable));
}
static void
gedit_docinfo_plugin_class_init (GeditDocinfoPluginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gedit_docinfo_plugin_dispose;
object_class->finalize = gedit_docinfo_plugin_finalize;
object_class->set_property = gedit_docinfo_plugin_set_property;
object_class->get_property = gedit_docinfo_plugin_get_property;
g_object_class_override_property (object_class, PROP_WINDOW, "window");
g_object_class_override_property (object_class, PROP_APP, "app");
}
static void
gedit_app_activatable_iface_init (GeditAppActivatableInterface *iface)
{
iface->activate = gedit_docinfo_plugin_app_activate;
iface->deactivate = gedit_docinfo_plugin_app_deactivate;
}
static void
gedit_window_activatable_iface_init (GeditWindowActivatableInterface *iface)
{
iface->activate = gedit_docinfo_plugin_window_activate;
iface->deactivate = gedit_docinfo_plugin_window_deactivate;
iface->update_state = gedit_docinfo_plugin_window_update_state;
}
static void
gedit_docinfo_plugin_class_finalize (GeditDocinfoPluginClass *klass)
{
}
G_MODULE_EXPORT void
peas_register_types (PeasObjectModule *module)
{
gedit_docinfo_plugin_register_type (G_TYPE_MODULE (module));
peas_object_module_register_extension_type (module,
GEDIT_TYPE_APP_ACTIVATABLE,
GEDIT_TYPE_DOCINFO_PLUGIN);
peas_object_module_register_extension_type (module,
GEDIT_TYPE_WINDOW_ACTIVATABLE,
GEDIT_TYPE_DOCINFO_PLUGIN);
}
/* ex:set ts=8 noet: */