diff --git a/configure.ac b/configure.ac index 29e9783..8aa24b9 100644 --- a/configure.ac +++ b/configure.ac @@ -173,6 +173,8 @@ else enable_introspection=no fi +AM_PATH_PYTHON([3.2.3]) + dnl ================================================================ dnl GSettings related settings dnl ================================================================ @@ -236,6 +238,8 @@ plugins/sort/Makefile plugins/spell/Makefile plugins/spell/org.x.editor.plugins.spell.gschema.xml plugins/taglist/Makefile +plugins/textsize/Makefile +plugins/textsize/textsize/Makefile plugins/time/Makefile plugins/time/org.x.editor.plugins.time.gschema.xml plugins/trailsave/Makefile diff --git a/debian/xed.install b/debian/xed.install index 664190c..5871df7 100644 --- a/debian/xed.install +++ b/debian/xed.install @@ -1,8 +1,7 @@ usr/bin/xed usr/lib/*/xed/girepository-1.0/ usr/lib/xed/xed-bugreport.sh -usr/lib/*/xed/plugins/*.plugin -usr/lib/*/xed/plugins/*.so +usr/lib/*/xed/plugins/ usr/lib/*/xed/*.so usr/share/applications/xed.desktop usr/share/dbus-1/ diff --git a/plugins/Makefile.am b/plugins/Makefile.am index a162a09..2525b12 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -5,6 +5,7 @@ DIST_SUBDIRS = \ sort \ spell \ taglist \ + textsize \ time \ trailsave @@ -15,6 +16,7 @@ SUBDIRS = \ sort \ spell \ taglist \ + textsize \ time \ trailsave diff --git a/plugins/sort/Makefile.am b/plugins/sort/Makefile.am index 13e65f8..075c56e 100644 --- a/plugins/sort/Makefile.am +++ b/plugins/sort/Makefile.am @@ -16,16 +16,13 @@ libsort_la_SOURCES = \ libsort_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS) libsort_la_LIBADD = $(XED_LIBS) -uidir = $(XED_PLUGINS_DATA_DIR)/sort -ui_DATA = sort.ui - plugin_in_files = sort.plugin.desktop.in %.plugin: %.plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache plugin_DATA = $(plugin_in_files:.plugin.desktop.in=.plugin) -EXTRA_DIST = $(ui_DATA) $(plugin_in_files) +EXTRA_DIST = $(plugin_in_files) CLEANFILES = $(plugin_DATA) DISTCLEANFILES = $(plugin_DATA) diff --git a/plugins/sort/sort.plugin.desktop.in b/plugins/sort/sort.plugin.desktop.in index 4b56701..b8b47e6 100644 --- a/plugins/sort/sort.plugin.desktop.in +++ b/plugins/sort/sort.plugin.desktop.in @@ -7,3 +7,4 @@ Icon=gtk-sort-ascending Authors=Carlo Borreo ;Lee Mallabone ;Paolo Maggi ;Jorge Alberto Torres H. Copyright=Copyright © 2001 Carlo Borreo\nCopyright © 2002-2003 Lee Mallabone, Paolo Maggi\nCopyright © 2004-2005 Paolo Maggi Website=http://www.mate-desktop.org +Builtin=true diff --git a/plugins/sort/sort.ui b/plugins/sort/sort.ui deleted file mode 100644 index a74b713..0000000 --- a/plugins/sort/sort.ui +++ /dev/null @@ -1,274 +0,0 @@ - - - - - 100 - 1 - 10 - 1 - 0 - 1 - - - gtk-sort-ascending - 4 - - - Sort - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - False - True - True - False - False - GDK_WINDOW_TYPE_HINT_DIALOG - GDK_GRAVITY_NORTH_WEST - True - - - True - False - 0 - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-cancel - True - GTK_RELIEF_NORMAL - True - - - - - True - True - True - GTK_RELIEF_NORMAL - True - sort_image - _Sort - True - - - - - True - True - True - gtk-help - True - GTK_RELIEF_NORMAL - True - - - - - 0 - False - True - GTK_PACK_END - - - - - 10 - True - False - 18 - - - True - False - 12 - - - True - True - _Reverse order - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - True - True - R_emove duplicates - True - GTK_RELIEF_NORMAL - True - False - False - True - - - 0 - False - False - - - - - True - True - _Ignore case - True - GTK_RELIEF_NORMAL - True - True - False - True - - - 0 - False - False - - - - - True - False - 6 - - - True - S_tart at column: - True - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - col_num_spinbutton - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - True - True - 1 - 0 - True - GTK_UPDATE_ALWAYS - False - False - adjustment1 - - - 0 - False - True - - - - - 0 - True - True - - - - - 0 - True - True - - - - - True - False - 6 - - - True - gtk-dialog-warning - 4 - 0.5 - 0.5 - 0 - 0 - - - 0 - False - True - - - - - True - You cannot undo a sort operation - False - True - GTK_JUSTIFY_LEFT - True - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - True - True - - - - - 0 - True - True - - - - - 0 - True - True - - - - - - button1 - button2 - button3 - - - diff --git a/plugins/sort/xed-sort-plugin.c b/plugins/sort/xed-sort-plugin.c index 80f33b0..6de63ea 100644 --- a/plugins/sort/xed-sort-plugin.c +++ b/plugins/sort/xed-sort-plugin.c @@ -28,6 +28,7 @@ #include "xed-sort-plugin.h" #include +#include #include #include @@ -56,24 +57,9 @@ struct _XedSortPluginPrivate GtkActionGroup *ui_action_group; guint ui_id; - GtkWidget *dialog; - GtkWidget *col_num_spinbutton; - GtkWidget *reverse_order_checkbutton; - GtkWidget *ignore_case_checkbutton; - GtkWidget *remove_dups_checkbutton; - GtkTextIter start, end; /* selection */ }; -typedef struct -{ - gint starting_column; - - guint ignore_case : 1; - guint reverse_order : 1; - guint remove_duplicates : 1; -} SortInfo; - enum { PROP_0, @@ -82,43 +68,22 @@ enum static void sort_cb (GtkAction *action, XedSortPlugin *plugin); -static void sort_real (XedSortPlugin *plugin); + +static void buffer_sort_lines (GtkSourceBuffer *buffer, + GtkTextIter *start, + GtkTextIter *end); static const GtkActionEntry action_entries[] = { { "Sort", - "view-sort-ascending-symbolic", - N_("S_ort..."), NULL, + N_("S_ort lines"), + "F10", N_("Sort the current document or selection"), G_CALLBACK (sort_cb) } }; -static void -sort_dialog_response_handler (GtkDialog *dlg, - gint res_id, - XedSortPlugin *plugin) -{ - xed_debug (DEBUG_PLUGINS); - - switch (res_id) - { - case GTK_RESPONSE_OK: - sort_real (plugin); - gtk_widget_destroy (GTK_WIDGET (dlg)); - break; - - case GTK_RESPONSE_HELP: - xed_app_show_help (XED_APP (g_application_get_default ()), GTK_WINDOW (dlg), NULL, "xed-sort-plugin"); - break; - - case GTK_RESPONSE_CANCEL: - gtk_widget_destroy (GTK_WIDGET (dlg)); - break; - } -} - /* NOTE: we store the current selection in the dialog since focusing * the text field (like the combo box) looses the documnent selection. * Storing the selection ONLY works because the dialog is modal */ @@ -141,158 +106,25 @@ get_current_selection (XedSortPlugin *plugin) } } -static void -create_sort_dialog (XedSortPlugin *plugin) -{ - XedSortPluginPrivate *priv; - GtkWidget *error_widget; - gboolean ret; - gchar *data_dir; - gchar *ui_file; - - xed_debug (DEBUG_PLUGINS); - - priv = plugin->priv; - - data_dir = peas_extension_base_get_data_dir (PEAS_EXTENSION_BASE (plugin)); - ui_file = g_build_filename (data_dir, "sort.ui", NULL); - ret = xed_utils_get_ui_objects (ui_file, - NULL, - &error_widget, - "sort_dialog", &priv->dialog, - "reverse_order_checkbutton", &priv->reverse_order_checkbutton, - "col_num_spinbutton", &priv->col_num_spinbutton, - "ignore_case_checkbutton", &priv->ignore_case_checkbutton, - "remove_dups_checkbutton", &priv->remove_dups_checkbutton, - NULL); - g_free (data_dir); - g_free (ui_file); - - if (!ret) - { - const gchar *err_message; - - err_message = gtk_label_get_label (GTK_LABEL (error_widget)); - xed_warning (GTK_WINDOW (priv->window), "%s", err_message); - - gtk_widget_destroy (error_widget); - - return; - } - - gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_OK); - - g_signal_connect (priv->dialog, "destroy", G_CALLBACK (gtk_widget_destroyed), &priv->dialog); - g_signal_connect (priv->dialog, "response", G_CALLBACK (sort_dialog_response_handler), plugin); - - get_current_selection (plugin); -} - static void sort_cb (GtkAction *action, XedSortPlugin *plugin) { XedSortPluginPrivate *priv; - GtkWindowGroup *wg; + XedDocument *doc; xed_debug (DEBUG_PLUGINS); priv = plugin->priv; - create_sort_dialog (plugin); + doc = xed_window_get_active_document (priv->window); + g_return_if_fail (doc != NULL); - wg = xed_window_get_group (priv->window); - gtk_window_group_add_window (wg, GTK_WINDOW (priv->dialog)); + get_current_selection (plugin); - gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), GTK_WINDOW (priv->window)); - gtk_window_set_modal (GTK_WINDOW (priv->dialog), TRUE); - - gtk_widget_show (GTK_WIDGET (priv->dialog)); -} - -/* Compares two strings for the sorting algorithm. Uses the UTF-8 processing - * functions in GLib to be as correct as possible.*/ -static gint -compare_algorithm (gconstpointer s1, - gconstpointer s2, - gpointer data) -{ - gint length1, length2; - gint ret; - gchar *string1, *string2; - gchar *substring1, *substring2; - gchar *key1, *key2; - SortInfo *sort_info; - - xed_debug (DEBUG_PLUGINS); - - sort_info = (SortInfo *) data; - g_return_val_if_fail (sort_info != NULL, -1); - - if (!sort_info->ignore_case) - { - string1 = *((gchar **) s1); - string2 = *((gchar **) s2); - } - else - { - string1 = g_utf8_casefold (*((gchar **) s1), -1); - string2 = g_utf8_casefold (*((gchar **) s2), -1); - } - - length1 = g_utf8_strlen (string1, -1); - length2 = g_utf8_strlen (string2, -1); - - if ((length1 < sort_info->starting_column) && - (length2 < sort_info->starting_column)) - { - ret = 0; - } - else if (length1 < sort_info->starting_column) - { - ret = -1; - } - else if (length2 < sort_info->starting_column) - { - ret = 1; - } - else if (sort_info->starting_column < 1) - { - key1 = g_utf8_collate_key (string1, -1); - key2 = g_utf8_collate_key (string2, -1); - ret = strcmp (key1, key2); - - g_free (key1); - g_free (key2); - } - else - { - /* A character column offset is required, so figure out - * the correct offset into the UTF-8 string. */ - substring1 = g_utf8_offset_to_pointer (string1, sort_info->starting_column); - substring2 = g_utf8_offset_to_pointer (string2, sort_info->starting_column); - - key1 = g_utf8_collate_key (substring1, -1); - key2 = g_utf8_collate_key (substring2, -1); - ret = strcmp (key1, key2); - - g_free (key1); - g_free (key2); - } - - /* Do the necessary cleanup. */ - if (sort_info->ignore_case) - { - g_free (string1); - g_free (string2); - } - - if (sort_info->reverse_order) - { - ret = -1 * ret; - } - - return ret; + buffer_sort_lines (GTK_SOURCE_BUFFER (doc), + &priv->start, + &priv->end); } static gchar * @@ -300,7 +132,6 @@ get_line_slice (GtkTextBuffer *buf, gint line) { GtkTextIter start, end; - char *ret; gtk_text_buffer_get_iter_at_line (buf, &start, line); end = start; @@ -310,97 +141,108 @@ get_line_slice (GtkTextBuffer *buf, gtk_text_iter_forward_to_line_end (&end); } - ret= gtk_text_buffer_get_slice (buf, &start, &end, TRUE); + return gtk_text_buffer_get_slice (buf, &start, &end, TRUE); +} - g_assert (ret != NULL); +typedef struct { + gchar *line; /* the original text to re-insert */ + gchar *key; /* the key to use for the comparison */ +} SortLine; - return ret; +static gint +compare_line (gconstpointer aptr, + gconstpointer bptr) +{ + const SortLine *a = aptr; + const SortLine *b = bptr; + + return g_strcmp0 (a->key, b->key); } static void -sort_real (XedSortPlugin *plugin) +buffer_sort_lines (GtkSourceBuffer *buffer, + GtkTextIter *start, + GtkTextIter *end) { - XedSortPluginPrivate *priv; - XedDocument *doc; - GtkTextIter start, end; - gint start_line, end_line; - gint i; - gchar *last_row = NULL; + GtkTextBuffer *text_buffer; + gint start_line; + gint end_line; gint num_lines; - gchar **lines; - SortInfo *sort_info; + SortLine *lines; + gchar *last_line = NULL; + gint i; - xed_debug (DEBUG_PLUGINS); + g_return_if_fail (GTK_SOURCE_IS_BUFFER (buffer)); + g_return_if_fail (start != NULL); + g_return_if_fail (end != NULL); - priv = plugin->priv; + text_buffer = GTK_TEXT_BUFFER (buffer); - doc = xed_window_get_active_document (priv->window); - g_return_if_fail (doc != NULL); + gtk_text_iter_order (start, end); - sort_info = g_slice_new (SortInfo); - sort_info->ignore_case = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->ignore_case_checkbutton)); - sort_info->reverse_order = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->reverse_order_checkbutton)); - sort_info->remove_duplicates = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->remove_dups_checkbutton)); - sort_info->starting_column = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (priv->col_num_spinbutton)) - 1; + start_line = gtk_text_iter_get_line (start); + end_line = gtk_text_iter_get_line (end); - start = priv->start; - end = priv->end; - start_line = gtk_text_iter_get_line (&start); - end_line = gtk_text_iter_get_line (&end); + /* Required for gtk_text_buffer_delete() */ + if (!gtk_text_iter_starts_line (start)) + { + gtk_text_iter_set_line_offset (start, 0); + } /* if we are at line start our last line is the previus one. * Otherwise the last line is the current one but we try to * move the iter after the line terminator */ - if (gtk_text_iter_get_line_offset (&end) == 0) + if (gtk_text_iter_starts_line (end)) { end_line = MAX (start_line, end_line - 1); } else { - gtk_text_iter_forward_line (&end); + gtk_text_iter_forward_line (end); + } + + if (start_line == end_line) + { + return; } num_lines = end_line - start_line + 1; - lines = g_new0 (gchar *, num_lines + 1); - - xed_debug_message (DEBUG_PLUGINS, "Building list..."); + lines = g_new0 (SortLine, num_lines); for (i = 0; i < num_lines; i++) { - lines[i] = get_line_slice (GTK_TEXT_BUFFER (doc), start_line + i); + gchar *line; + + lines[i].line = get_line_slice (text_buffer, start_line + i); + line = g_utf8_casefold (lines[i].line, -1); + lines[i].key = g_utf8_collate_key (line, -1); + + g_free (line); } - lines[num_lines] = NULL; + qsort (lines, num_lines, sizeof (SortLine), compare_line); - xed_debug_message (DEBUG_PLUGINS, "Sort list..."); + gtk_text_buffer_begin_user_action (text_buffer); - g_qsort_with_data (lines, num_lines, sizeof (gpointer), compare_algorithm, sort_info); - - xed_debug_message (DEBUG_PLUGINS, "Rebuilding document..."); - - gtk_source_buffer_begin_not_undoable_action (GTK_SOURCE_BUFFER (doc)); - - gtk_text_buffer_delete (GTK_TEXT_BUFFER (doc), &start, &end); + gtk_text_buffer_delete (text_buffer, start, end); for (i = 0; i < num_lines; i++) { - if (sort_info->remove_duplicates && last_row != NULL && (strcmp (last_row, lines[i]) == 0)) - { - continue; - } + gtk_text_buffer_insert (text_buffer, start, lines[i].line, -1); + gtk_text_buffer_insert (text_buffer, start, "\n", -1); - gtk_text_buffer_insert (GTK_TEXT_BUFFER (doc), &start, lines[i], -1); - gtk_text_buffer_insert (GTK_TEXT_BUFFER (doc), &start, "\n", -1); - - last_row = lines[i]; + last_line = lines[i].line; } - gtk_source_buffer_end_not_undoable_action (GTK_SOURCE_BUFFER (doc)); + gtk_text_buffer_end_user_action (text_buffer); - g_strfreev (lines); - g_slice_free (SortInfo, sort_info); + for (i = 0; i < num_lines; i++) + { + g_free (lines[i].line); + g_free (lines[i].key); + } - xed_debug_message (DEBUG_PLUGINS, "Done."); + g_free (lines); } static void diff --git a/plugins/textsize/Makefile.am b/plugins/textsize/Makefile.am new file mode 100644 index 0000000..7088890 --- /dev/null +++ b/plugins/textsize/Makefile.am @@ -0,0 +1,16 @@ +# Textsize Plugin +SUBDIRS = textsize +plugindir = $(XED_PLUGINS_LIBS_DIR) + +plugin_in_files = textsize.plugin.desktop.in +%.plugin: %.plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache + +plugin_DATA = $(plugin_in_files:.plugin.desktop.in=.plugin) + +EXTRA_DIST = \ + $(plugin_in_files) + +CLEANFILES = $(plugin_DATA) +DISTCLEANFILES = $(plugin_DATA) + +-include $(top_srcdir)/git.mk diff --git a/plugins/textsize/textsize.plugin.desktop.in b/plugins/textsize/textsize.plugin.desktop.in new file mode 100644 index 0000000..75ee9f0 --- /dev/null +++ b/plugins/textsize/textsize.plugin.desktop.in @@ -0,0 +1,11 @@ +[Plugin] +Loader=python3 +Module=textsize +IAge=3 +_Name=Text Size +_Description=Allow controlling the zoom levels of the text +Icon=gnome-mime-text-x-python +Authors=Steve Frécinaux ;Linux Mint team +Copyright=Copyright © 2017 by the authors +Website=https://github.com/linuxmint +Builtin=true \ No newline at end of file diff --git a/plugins/textsize/textsize/Makefile.am b/plugins/textsize/textsize/Makefile.am new file mode 100644 index 0000000..2999d65 --- /dev/null +++ b/plugins/textsize/textsize/Makefile.am @@ -0,0 +1,11 @@ +plugindir = $(XED_PLUGINS_LIBS_DIR)/textsize + +plugin_PYTHON = \ + __init__.py \ + signals.py \ + documenthelper.py + +CLEANFILES = +DISTCLEANFILES = + +-include $(top_srcdir)/git.mk diff --git a/plugins/textsize/textsize/__init__.py b/plugins/textsize/textsize/__init__.py new file mode 100644 index 0000000..d9b36bf --- /dev/null +++ b/plugins/textsize/textsize/__init__.py @@ -0,0 +1,233 @@ +# -*- coding: utf-8 -*- +# +# __init__.py - Text size plugin +# +# Copyright (C) 2008 - Konstantin Mikhaylov +# Copyright (C) 2009 - Wouter Bolsterlee +# Copyright (C) 2010 - Ignacio Casal Quinteiro +# Copyright (C) 2010 - Jesse van den Kieboom +# Copyright (C) 2017 - Linux Mint team +# +# 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 of the License, 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +from gi.repository import GObject, Gio, Gtk, Gdk, Xed +from .documenthelper import DocumentHelper + +MENU_PATH = "/MenuBar/ViewMenu/ViewOps_1" + +class TextSizePlugin(GObject.Object, Xed.WindowActivatable): + __gtype_name__ = "TextSizePlugin" + + window = GObject.property(type=Xed.Window) + + def __init__(self): + GObject.Object.__init__(self) + + def do_activate(self): + self._views = {} + + # Insert menu items + self._insert_menu() + + # Insert document helpers + for view in self.window.get_views(): + self.add_document_helper(view) + + self.window.connect('tab-added', self.on_tab_added) + self.window.connect('tab-removed', self.on_tab_removed) + + self._accel_group = Gtk.AccelGroup() + self.window.add_accel_group(self._accel_group) + + self._proxy_callback_map = { + 'LargerTextAction': self.on_larger_text_accel, + 'SmallerTextAction': self.on_smaller_text_accel, + 'NormalSizeAction': self.on_normal_size_accel + } + + self._proxy_mapping = {} + self._init_proxy_accels() + self._accel_map_handler_id = Gtk.AccelMap.get().connect('changed', self.on_accel_map_changed) + + def _install_proxy(self, action): + if not isinstance(action, Gtk.Action): + action = self._action_group.get_action(str(action)) + + if not action: + return + + entry = Gtk.AccelMap.lookup_entry(action.get_accel_path()) + + if not entry: + return + + mapping = { + Gdk.KEY_equal: Gdk.KEY_KP_Equal, + Gdk.KEY_KP_Equal: Gdk.KEY_equal, + Gdk.KEY_minus: Gdk.KEY_KP_Subtract, + Gdk.KEY_KP_Subtract: Gdk.KEY_minus, + Gdk.KEY_0: Gdk.KEY_KP_0, + Gdk.KEY_KP_0: Gdk.KEY_0 + } + + if entry[0] in mapping: + key = mapping[entry[0]] + mod = entry[1] + + callback = self._proxy_callback_map[action.get_name()] + + self._accel_group.connect_group(key, mod, Gtk.ACCEL_LOCKED, callback) + self._proxy_mapping[action] = (key, mod) + + def _init_proxy_accels(self): + self._install_proxy('LargerTextAction') + self._install_proxy('SmallerTextAction') + self._install_proxy('NormalSizeAction') + + def do_deactivate(self): + # Remove any installed menu items + self._remove_menu() + + for view in self.window.get_views(): + self.remove_document_helper(view) + + self.window.remove_accel_group(self._accel_group) + + Gtk.AccelMap.get().disconnect(self._accel_map_handler_id) + + self._accel_group = None + self._action_group = None + + def _insert_menu(self): + # Get the GtkUIManager + manager = self.window.get_ui_manager() + + # Create a new action group + self._action_group = Gtk.ActionGroup("XedTextSizePluginActions") + self._action_group.add_actions([("LargerTextAction", None, _("_Larger Text"), + "equal", None, + self.on_larger_text_activate), + ("SmallerTextAction", None, _("S_maller Text"), + "minus", None, + self.on_smaller_text_activate), + ("NormalSizeAction", None, _("_Normal size"), + "0", None, + self.on_normal_size_activate)]) + + # Insert the action group + manager.insert_action_group(self._action_group) + + self._ui_id = manager.new_merge_id(); + + manager.add_ui(self._ui_id, + MENU_PATH, + "LargerTextAction", + "LargerTextAction", + Gtk.UIManagerItemType.MENUITEM, + False) + + manager.add_ui(self._ui_id, + MENU_PATH, + "SmallerTextAction", + "SmallerTextAction", + Gtk.UIManagerItemType.MENUITEM, + False) + + manager.add_ui(self._ui_id, + MENU_PATH, + "NormalSizeAction", + "NormalSizeAction", + Gtk.UIManagerItemType.MENUITEM, + False) + + def _remove_menu(self): + # Get the GtkUIManager + manager = self.window.get_ui_manager() + + # Remove the ui + manager.remove_ui(self._ui_id) + + # Remove the action group + manager.remove_action_group(self._action_group) + + # Make sure the manager updates + manager.ensure_update() + + def do_update_state(self): + self._action_group.set_sensitive(self.window.get_active_document() != None) + + def get_helper(self, view): + if not hasattr(view, "textsize_document_helper"): + return None + return view.textsize_document_helper + + def add_document_helper(self, view): + if self.get_helper(view) != None: + return + + DocumentHelper(view) + + def remove_document_helper(self, view): + helper = self.get_helper(view) + + if helper != None: + helper.stop() + + def call_helper(self, cb): + view = self.window.get_active_view() + + if view: + cb(self.get_helper(view)) + + # Menu activate handlers + def on_larger_text_activate(self, action, user_data=None): + self.call_helper(lambda helper: helper.larger_text()) + + def on_smaller_text_activate(self, action, user_data=None): + self.call_helper(lambda helper: helper.smaller_text()) + + def on_normal_size_activate(self, action, user_data=None): + self.call_helper(lambda helper: helper.normal_size()) + + def on_larger_text_accel(self, group, accel, key, mod): + self.call_helper(lambda helper: helper.larger_text()) + + def on_smaller_text_accel(self, group, accel, key, mod): + self.call_helper(lambda helper: helper.smaller_text()) + + def on_normal_size_accel(self, group, accel, key, mod): + self.call_helper(lambda helper: helper.normal_size()) + + def on_tab_added(self, window, tab): + self.add_document_helper(tab.get_view()) + + def on_tab_removed(self, window, tab): + self.remove_document_helper(tab.get_view()) + + def _remap_proxy(self, action): + # Remove previous proxy + + if action in self._proxy_mapping: + item = self._proxy_mapping[action] + self._accel_group.disconnect_key(item[0], item[1]) + + self._install_proxy(action) + + def on_accel_map_changed(self, accelmap, path, key, mod): + for action in self._action_group.list_actions(): + if action.get_accel_path() == path: + self._remap_proxy(action) + return diff --git a/plugins/textsize/textsize/documenthelper.py b/plugins/textsize/textsize/documenthelper.py new file mode 100644 index 0000000..0fb580d --- /dev/null +++ b/plugins/textsize/textsize/documenthelper.py @@ -0,0 +1,189 @@ +# -*- coding: utf-8 -*- +# +# documenthelper.py - Document helper +# +# Copyright (C) 2010 - Jesse van den Kieboom +# Copyright (C) 2017 - Linux Mint team +# +# 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 of the License, 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + + +from .signals import Signals +from gi.repository import Gtk, Gdk, Pango + +class DocumentHelper(Signals): + def __init__(self, view): + Signals.__init__(self) + + self._view = view + + self.connect_signal(self._view, 'scroll-event', self.on_scroll_event) + self.connect_signal(self._view, 'button-press-event', self.on_button_press_event) + + self._view.textsize_document_helper = self + + self._default_font = None + self._last_font = None + self._font_tags = {} + + def stop(self): + if self._default_font: + self._view.override_font(self._default_font) + + self.remove_font_tags() + self.disconnect_signals(self._view) + + self._view.textsize_document_helper = None + + def remove_font_tags(self): + buf = self._view.get_buffer() + table = buf.get_tag_table() + + # Remove all the font tags + for size in self._font_tags: + tag = self._font_tags[size] + table.remove(tag) + + self._font_tags = {} + + def update_default_font(self): + context = self._view.get_style_context() + description = context.get_font(context.get_state()).copy() + + if not self._last_font or description.hash() != self._last_font.hash(): + self._default_font = description + + def get_font_tags(self, start, end): + tags = set() + + # Check all the know font tags + for size in self._font_tags: + tag = self._font_tags[size] + + if start.has_tag(tag): + tags.add(tag) + else: + cp = start.copy() + + if cp.forward_to_tag_toggle(tag) and cp.compare(end) < 0: + tags.add(tag) + + return list(tags) + + def set_font_size(self, amount): + self.update_default_font() + + context = self._view.get_style_context() + description = context.get_font(context.get_state()).copy() + + buf = self._view.get_buffer() + bounds = buf.get_selection_bounds() + size = description.get_size() / Pango.SCALE + + if not bounds: + description.set_size(max(1, (size + amount)) * Pango.SCALE) + + self._view.override_font(description) + self._last_font = description + else: + start = bounds[0] + end = bounds[1] + + tags = self.get_font_tags(start, end) + + if not tags: + # Simply use the overall font size as the base + newsize = size + amount + elif len(tags) == 1: + newsize = tags[0].props.font_desc.get_size() / Pango.SCALE + amount + else: + newsize = 0 + + for tag in tags: + newsize += tag.props.font_desc.get_size() / Pango.SCALE + + newsize = round(newsize / len(tags)) + + newsize = int(max(1, newsize)) + + if not newsize in self._font_tags: + newtag = buf.create_tag(None) + + desc = description + desc.set_size(newsize * Pango.SCALE) + + newtag.props.font_desc = desc + self._font_tags[newsize] = newtag + else: + newtag = self._font_tags[newsize] + + # Remove all the previous mix of tags + for tag in tags: + buf.remove_tag(tag, start, end) + + buf.apply_tag(newtag, start, end) + + def larger_text(self): + self.set_font_size(1) + + def smaller_text(self): + self.set_font_size(-1) + + def normal_size(self): + self.update_default_font() + + buf = self._view.get_buffer() + bounds = buf.get_selection_bounds() + + if not bounds: + self.remove_font_tags() + + self._view.override_font(self._default_font) + self._last_font = self._default_font + else: + tags = self.get_font_tags(bounds[0], bounds[1]) + + for tag in tags: + buf.remove_tag(tag, bounds[0], bounds[1]) + + def on_scroll_event(self, view, event): + state = event.state & Gtk.accelerator_get_default_mod_mask() + + if state != Gdk.ModifierType.CONTROL_MASK: + return False + + if event.direction == Gdk.ScrollDirection.UP: + self.larger_text() + return True + elif event.direction == Gdk.ScrollDirection.DOWN: + self.smaller_text() + return True + elif event.direction == Gdk.ScrollDirection.SMOOTH: + if event.delta_y > 0: + self.smaller_text() + elif event.delta_y < 0: + self.larger_text() + + return False + + def on_button_press_event(self, view, event): + state = event.state & Gtk.accelerator_get_default_mod_mask() + + if state == Gdk.ModifierType.CONTROL_MASK and event.button == 2: + self.normal_size() + return True + else: + return False diff --git a/plugins/textsize/textsize/signals.py b/plugins/textsize/textsize/signals.py new file mode 100644 index 0000000..2996a12 --- /dev/null +++ b/plugins/textsize/textsize/signals.py @@ -0,0 +1,69 @@ +class Signals(object): + def __init__(self): + self._signals = {} + + def _connect(self, obj, name, handler, connector): + ret = self._signals.setdefault(obj, {}) + + hid = connector(name, handler) + ret.setdefault(name, []).append(hid) + + return hid + + def connect_signal(self, obj, name, handler): + return self._connect(obj, name, handler, obj.connect) + + def connect_signal_after(self, obj, name, handler): + return self._connect(obj, name, handler, obj.connect_after) + + def disconnect_signals(self, obj): + if obj not in self._signals: + return False + + for name in self._signals[obj]: + for hid in self._signals[obj][name]: + obj.disconnect(hid) + + del self._signals[obj] + return True + + def block_signal(self, obj, name): + if obj not in self._signals: + return False + + if name not in self._signals[obj]: + return False + + for hid in self._signals[obj][name]: + obj.handler_block(hid) + + return True + + def unblock_signal(self, obj, name): + if obj not in self._signals: + return False + + if name not in self._signals[obj]: + return False + + for hid in self._signals[obj][name]: + obj.handler_unblock(hid) + + return True + + def disconnect_signal(self, obj, name): + if obj not in self._signals: + return False + + if name not in self._signals[obj]: + return False + + for hid in self._signals[obj][name]: + obj.disconnect(hid) + + del self._signals[obj][name] + + if len(self._signals[obj]) == 0: + del self._signals[obj] + + return True \ No newline at end of file diff --git a/xed/resources/ui/xed-ui.xml b/xed/resources/ui/xed-ui.xml index 6b5fe27..4da5c7a 100644 --- a/xed/resources/ui/xed-ui.xml +++ b/xed/resources/ui/xed-ui.xml @@ -56,6 +56,9 @@ + + + diff --git a/xed/xed-plugins-engine.c b/xed/xed-plugins-engine.c index 6b96972..c293c1e 100644 --- a/xed/xed-plugins-engine.c +++ b/xed/xed-plugins-engine.c @@ -58,6 +58,7 @@ xed_plugins_engine_init (XedPluginsEngine *engine) { gchar *typelib_dir; GError *error = NULL; + const GList *all_plugins, *l; xed_debug (DEBUG_PLUGINS); @@ -93,17 +94,6 @@ xed_plugins_engine_init (XedPluginsEngine *engine) error = NULL; } - // private_path = g_build_filename (LIBDIR, "girepository-1.0", NULL); - - // if (!g_irepository_require_private (g_irepository_get_default (), private_path, "Xed", "1.0", 0, &error)) - // { - // g_warning ("Could not load Xed repository: %s", error->message); - // g_error_free (error); - // error = NULL; - // } - - // g_free (private_path); - peas_engine_add_search_path (PEAS_ENGINE (engine), xed_dirs_get_user_plugins_dir (), xed_dirs_get_user_plugins_dir ()); @@ -117,6 +107,23 @@ xed_plugins_engine_init (XedPluginsEngine *engine) engine, "loaded-plugins", G_SETTINGS_BIND_DEFAULT); + + /* Load our builtin plugins */ + all_plugins = peas_engine_get_plugin_list (PEAS_ENGINE (engine)); + + for (l = all_plugins; l != NULL; l = l->next) + { + if (peas_plugin_info_is_builtin (l->data)) + { + gboolean loaded; + + loaded = peas_engine_load_plugin (PEAS_ENGINE (engine), l->data); + if (!loaded) + { + g_warning ("Failed to load builtin plugin: %s", peas_plugin_info_get_name (l->data)); + } + } + } } static void