From b18846a1b49329fcd3601e95c064260ab02c3f57 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 04:22:56 +0800 Subject: [PATCH 01/41] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ From 35de39545463e4cec3b05dd6cac8ee2ce6d92cd2 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 04:25:08 +0800 Subject: [PATCH 02/41] simplify gettext a bit --- controlyourtabs/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index bcf3eac..f7c2325 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -23,20 +23,20 @@ gi.require_version('Gtk', '3.0') gi.require_version('Gedit', '3.0') -import gettext import math import os.path from gi.repository import GObject, GLib, Gtk, Gdk, GdkPixbuf, Gio, GtkSource, Gedit, PeasGtk from xml.sax.saxutils import escape from .utils import connect_handlers, disconnect_handlers -GETTEXT_PACKAGE = 'gedit-control-your-tabs' BASE_PATH = os.path.dirname(os.path.realpath(__file__)) LOCALE_PATH = os.path.join(BASE_PATH, 'locale') try: - gettext.bindtextdomain(GETTEXT_PACKAGE, LOCALE_PATH) - _ = lambda s: gettext.dgettext(GETTEXT_PACKAGE, s); + import gettext + gettext.bindtextdomain('gedit-control-your-tabs', LOCALE_PATH) + gettext.textdomain('gedit-control-your-tabs') + _ = gettext.gettext except: _ = lambda s: s From 7ac43463db5d4d8256b6ca969b86c79f32ac1d4b Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 05:14:11 +0800 Subject: [PATCH 03/41] split tab info functions into separate modules --- controlyourtabs/__init__.py | 191 ++---------------------------- controlyourtabs/tabinfo.py | 91 ++++++++++++++ controlyourtabs/tabinfo_pre312.py | 145 +++++++++++++++++++++++ 3 files changed, 247 insertions(+), 180 deletions(-) create mode 100644 controlyourtabs/tabinfo.py create mode 100644 controlyourtabs/tabinfo_pre312.py diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index f7c2325..a6ea9df 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -25,9 +25,9 @@ import math import os.path -from gi.repository import GObject, GLib, Gtk, Gdk, GdkPixbuf, Gio, GtkSource, Gedit, PeasGtk -from xml.sax.saxutils import escape +from gi.repository import GObject, GLib, Gtk, Gdk, GdkPixbuf, Gio, Gedit, PeasGtk from .utils import connect_handlers, disconnect_handlers +from . import tabinfo, tabinfo_pre312 BASE_PATH = os.path.dirname(os.path.realpath(__file__)) LOCALE_PATH = os.path.join(BASE_PATH, 'locale') @@ -60,48 +60,6 @@ class ControlYourTabsPlugin(GObject.Object, Gedit.WindowActivatable, PeasGtk.Con MAX_TAB_WINDOW_HEIGHT_PERCENTAGE = 0.5 - # based on MAX_DOC_NAME_LENGTH in gedit-documents-panel.c - MAX_DOC_NAME_LENGTH = 60 - - # based on formats in tab_get_name() in gedit-documents-panel.c < 3.12 - TAB_NAME_GEDITPANEL_FORMATS = { - 'modified': "%s", - 'readonly': " [%s]" - } - - # based on formats in document_row_sync_tab_name_and_icon() in gedit-documents-panel.c >= 3.12 - TAB_NAME_LISTBOX_FORMATS = { - 'modified': "%s", - 'readonly': " [%s]" - } - - # based on switch statement in _gedit_tab_get_icon() in gedit-tab.c < 3.12 - TAB_STATE_TO_STOCK_ICON = { - Gedit.TabState.STATE_LOADING: Gtk.STOCK_OPEN, - Gedit.TabState.STATE_REVERTING: Gtk.STOCK_REVERT_TO_SAVED, - Gedit.TabState.STATE_SAVING: Gtk.STOCK_SAVE, - Gedit.TabState.STATE_PRINTING: Gtk.STOCK_PRINT, - Gedit.TabState.STATE_PRINT_PREVIEWING: Gtk.STOCK_PRINT_PREVIEW, - Gedit.TabState.STATE_SHOWING_PRINT_PREVIEW: Gtk.STOCK_PRINT_PREVIEW, - Gedit.TabState.STATE_LOADING_ERROR: Gtk.STOCK_DIALOG_ERROR, - Gedit.TabState.STATE_REVERTING_ERROR: Gtk.STOCK_DIALOG_ERROR, - Gedit.TabState.STATE_SAVING_ERROR: Gtk.STOCK_DIALOG_ERROR, - Gedit.TabState.STATE_GENERIC_ERROR: Gtk.STOCK_DIALOG_ERROR, - Gedit.TabState.STATE_EXTERNALLY_MODIFIED_NOTIFICATION: Gtk.STOCK_DIALOG_WARNING - } - - # based on switch statement in _gedit_tab_get_icon() in gedit-tab.c >= 3.12 - TAB_STATE_TO_NAMED_ICON = { - Gedit.TabState.STATE_PRINTING: 'printer-printing-symbolic', - Gedit.TabState.STATE_PRINT_PREVIEWING: 'printer-symbolic', - Gedit.TabState.STATE_SHOWING_PRINT_PREVIEW: 'printer-symbolic', - Gedit.TabState.STATE_LOADING_ERROR: 'dialog-error-symbolic', - Gedit.TabState.STATE_REVERTING_ERROR: 'dialog-error-symbolic', - Gedit.TabState.STATE_SAVING_ERROR: 'dialog-error-symbolic', - Gedit.TabState.STATE_GENERIC_ERROR: 'dialog-error-symbolic', - Gedit.TabState.STATE_EXTERNALLY_MODIFIED_NOTIFICATION: 'dialog-warning-symbolic' - } - SETTINGS_SCHEMA_ID = 'com.thingsthemselves.gedit.plugins.controlyourtabs' USE_TABBAR_ORDER = 'use-tabbar-order' @@ -183,7 +141,7 @@ def do_activate(self): self._space_cell = space_cell self._tabwin_resize_id = None self._settings = self._get_settings() - self._is_side_panel_stack = is_side_panel_stack + self._tabinfo = tabinfo if is_side_panel_stack else tabinfo_pre312 tab = window.get_active_tab() if tab: @@ -227,7 +185,7 @@ def do_deactivate(self): self._space_cell = None self._tabwin_resize_id = None self._settings = None - self._is_side_panel_stack = None + self._tabinfo = None def do_update_state(self): pass @@ -262,13 +220,10 @@ def on_window_tab_added(self, window, tab): self._setup(window, tab, self._notebooks, self._view) def _setup(self, window, tab, notebooks, view): - if self._is_side_panel_stack: - is_valid_size, icon_size_width, icon_size_height = Gtk.icon_size_lookup(Gtk.IconSize.MENU) - else: - is_valid_size, icon_size_width, icon_size_height = Gtk.icon_size_lookup_for_settings(tab.get_settings(), Gtk.IconSize.MENU) + icon_size = self._tabinfo.get_tab_icon_size(tab) - self._icon_cell.set_fixed_size(icon_size_height, icon_size_height) - self._space_cell.set_fixed_size(icon_size_height, icon_size_height) + self._icon_cell.set_fixed_size(icon_size, icon_size) + self._space_cell.set_fixed_size(icon_size, icon_size) multi = self._get_multi_notebook(tab) @@ -314,7 +269,7 @@ def on_multi_notebook_tab_added(self, multi, notebook, tab, notebooks, view): stack, model = notebooks[notebook] if tab not in stack: stack.append(tab) - model.append((self._get_tab_icon(tab), self._get_tab_name(tab), tab, False)) + model.append((self._tabinfo.get_tab_icon(tab), self._tabinfo.get_tab_name(tab), tab, False)) connect_handlers(self, tab, ['notify::name', 'notify::state'], self.on_sync_icon_and_name, notebooks) def on_multi_notebook_tab_removed(self, multi, notebook, tab, notebooks, view): @@ -354,7 +309,7 @@ def on_window_active_tab_changed(self, window, tab, notebooks, view): model.move_after(model.get_iter(stack.index(tab)), None) stack.remove(tab) else: - model.insert(0, (self._get_tab_icon(tab), self._get_tab_name(tab), tab, False)) + model.insert(0, (self._tabinfo.get_tab_icon(tab), self._tabinfo.get_tab_name(tab), tab, False)) stack.insert(0, tab) model[0][self.SELECTED_TAB_COLUMN] = True @@ -473,132 +428,8 @@ def on_sync_icon_and_name(self, tab, pspec, notebooks): stack, model = notebooks[tab.get_parent()] if tab in stack: path = stack.index(tab) - model[path][0] = self._get_tab_icon(tab) - model[path][1] = self._get_tab_name(tab) - - - # tab name / icon - - # based on - # < 3.12: tab_get_name() in gedit-documents-panel.c - # >= 3.12: doc_get_name() and document_row_sync_tab_name_and_icon() in gedit-documents-panel.c - def _get_tab_name(self, tab): - doc = tab.get_document() - name = doc.get_short_name_for_display() - docname = Gedit.utils_str_middle_truncate(name, self.MAX_DOC_NAME_LENGTH) - tab_name_formats = self.TAB_NAME_LISTBOX_FORMATS if self._is_side_panel_stack else self.TAB_NAME_GEDITPANEL_FORMATS - - if not doc.get_modified(): - tab_name = escape(docname) - else: - tab_name = tab_name_formats['modified'] % escape(docname) - - try: - file = doc.get_file() - is_readonly = GtkSource.File.is_readonly(file) - except AttributeError: - is_readonly = doc.get_readonly() # deprecated since 3.18 - - if is_readonly: - tab_name += tab_name_formats['readonly'] % escape(_("Read-Only")) - - return tab_name - - def _get_tab_icon(self, tab): - if self._is_side_panel_stack: - icon = self._get_named_tab_icon(tab) - else: - icon = self._get_stock_tab_icon(tab) - return icon - - # based on _gedit_tab_get_icon() in gedit-tab.c >= 3.12 - def _get_named_tab_icon(self, tab): - icon_name = None - pixbuf = None - state = tab.get_state() - - if state in self.TAB_STATE_TO_NAMED_ICON: - icon_name = self.TAB_STATE_TO_NAMED_ICON[state] - - if icon_name: - theme = Gtk.IconTheme.get_for_screen(tab.get_screen()) - is_valid_size, icon_size_width, icon_size_height = Gtk.icon_size_lookup(Gtk.IconSize.MENU) - pixbuf = Gtk.IconTheme.load_icon(theme, icon_name, icon_size_height, 0) - - return pixbuf - - # based on _gedit_tab_get_icon() in gedit-tab.c < 3.12 - def _get_stock_tab_icon(self, tab): - theme = Gtk.IconTheme.get_for_screen(tab.get_screen()) - is_valid_size, icon_size_width, icon_size_height = Gtk.icon_size_lookup_for_settings(tab.get_settings(), Gtk.IconSize.MENU) - state = tab.get_state() - - if state in self.TAB_STATE_TO_STOCK_ICON: - try: - pixbuf = self._get_stock_icon(theme, self.TAB_STATE_TO_STOCK_ICON[state], icon_size_height) - except GObject.GError: - pixbuf = None - else: - pixbuf = None - - if not pixbuf: - pixbuf = self._get_icon(theme, tab.get_document().get_location(), icon_size_height) - - return pixbuf - - # based on get_stock_icon() in gedit-tab.c in < 3.12 - def _get_stock_icon(self, theme, stock, size): - pixbuf = theme.load_icon(stock, size, 0) - return self._resize_icon(pixbuf, size) - - # based on get_icon() in gedit-tab.c in < 3.12 - def _get_icon(self, theme, location, size): - if not location: - return self._get_stock_icon(theme, Gtk.STOCK_FILE, size) - - # FIXME: Doing a sync stat is bad, this should be fixed - try: - info = location.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None) - except GObject.GError: - info = None - - if not info: - return self._get_stock_icon(theme, Gtk.STOCK_FILE, size) - - icon = info.get_icon() - - if not icon: - return self._get_stock_icon(theme, Gtk.STOCK_FILE, size) - - icon_info = theme.lookup_by_gicon(icon, size, 0); - - if not icon_info: - return self._get_stock_icon(theme, Gtk.STOCK_FILE, size) - - pixbuf = icon_info.load_icon() - - if not pixbuf: - return self._get_stock_icon(theme, Gtk.STOCK_FILE, size) - - return self._resize_icon(pixbuf, size) - - # based on resize_icon() in gedit-tab.c in < 3.12 - def _resize_icon(self, pixbuf, size): - width = pixbuf.get_width() - height = pixbuf.get_height() - - # if the icon is larger than the nominal size, scale down - if max(width, height) > size: - if width > height: - height = height * size / width - width = size - else: - width = width * size / height - height = size - - pixbuf = pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR) - - return pixbuf + model[path][0] = self._tabinfo.get_tab_icon(tab) + model[path][1] = self._tabinfo.get_tab_name(tab) # tab window resizing diff --git a/controlyourtabs/tabinfo.py b/controlyourtabs/tabinfo.py new file mode 100644 index 0000000..0bbd76c --- /dev/null +++ b/controlyourtabs/tabinfo.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# +# tabinfo.py +# This file is part of Control Your Tabs, a plugin for gedit +# +# Copyright (C) 2010-2014, 2016-2017 Jeffery To +# https://github.com/jefferyto/gedit-control-your-tabs +# +# 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 3 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, see . + +import os.path +from gi.repository import Gtk, GtkSource, Gedit +from xml.sax.saxutils import escape + +BASE_PATH = os.path.dirname(os.path.realpath(__file__)) +LOCALE_PATH = os.path.join(BASE_PATH, 'locale') + +try: + import gettext + gettext.bindtextdomain('gedit-control-your-tabs', LOCALE_PATH) + gettext.textdomain('gedit-control-your-tabs') + _ = gettext.gettext +except: + _ = lambda s: s + + +# based on switch statement in _gedit_tab_get_icon() in gedit-tab.c +TAB_STATE_TO_NAMED_ICON = { + Gedit.TabState.STATE_PRINTING: 'printer-printing-symbolic', + Gedit.TabState.STATE_PRINT_PREVIEWING: 'printer-symbolic', + Gedit.TabState.STATE_SHOWING_PRINT_PREVIEW: 'printer-symbolic', + Gedit.TabState.STATE_LOADING_ERROR: 'dialog-error-symbolic', + Gedit.TabState.STATE_REVERTING_ERROR: 'dialog-error-symbolic', + Gedit.TabState.STATE_SAVING_ERROR: 'dialog-error-symbolic', + Gedit.TabState.STATE_GENERIC_ERROR: 'dialog-error-symbolic', + Gedit.TabState.STATE_EXTERNALLY_MODIFIED_NOTIFICATION: 'dialog-warning-symbolic' +} + +# based on doc_get_name() and document_row_sync_tab_name_and_icon() in gedit-documents-panel.c +def get_tab_name(tab): + doc = tab.get_document() + name = doc.get_short_name_for_display() + docname = Gedit.utils_str_middle_truncate(name, 60) # based on MAX_DOC_NAME_LENGTH in gedit-documents-panel.c + + if not doc.get_modified(): + tab_name = escape(docname) + else: + tab_name = "%s" % escape(docname) + + try: + file = doc.get_file() + is_readonly = GtkSource.File.is_readonly(file) + except AttributeError: + is_readonly = doc.get_readonly() # deprecated since 3.18 + + if is_readonly: + tab_name += " [%s]" % escape(_("Read-Only")) + + return tab_name + +# based on _gedit_tab_get_icon() in gedit-tab.c +def get_tab_icon(tab): + icon_name = None + pixbuf = None + state = tab.get_state() + + if state in TAB_STATE_TO_NAMED_ICON: + icon_name = TAB_STATE_TO_NAMED_ICON[state] + + if icon_name: + theme = Gtk.IconTheme.get_for_screen(tab.get_screen()) + icon_size = get_tab_icon_size(tab) + pixbuf = Gtk.IconTheme.load_icon(theme, icon_name, icon_size, 0) + + return pixbuf + +def get_tab_icon_size(tab): + is_valid_size, icon_size_width, icon_size_height = Gtk.icon_size_lookup(Gtk.IconSize.MENU) + return icon_size_height + diff --git a/controlyourtabs/tabinfo_pre312.py b/controlyourtabs/tabinfo_pre312.py new file mode 100644 index 0000000..bf91e3a --- /dev/null +++ b/controlyourtabs/tabinfo_pre312.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +# +# tabinfo_pre312.py +# This file is part of Control Your Tabs, a plugin for gedit +# +# Copyright (C) 2010-2014, 2016-2017 Jeffery To +# https://github.com/jefferyto/gedit-control-your-tabs +# +# 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 3 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, see . + +import os.path +from gi.repository import GObject, Gtk, GdkPixbuf, Gio, Gedit +from xml.sax.saxutils import escape + +BASE_PATH = os.path.dirname(os.path.realpath(__file__)) +LOCALE_PATH = os.path.join(BASE_PATH, 'locale') + +try: + import gettext + gettext.bindtextdomain('gedit-control-your-tabs', LOCALE_PATH) + gettext.textdomain('gedit-control-your-tabs') + _ = gettext.gettext +except: + _ = lambda s: s + + +# based on switch statement in _gedit_tab_get_icon() in gedit-tab.c +TAB_STATE_TO_STOCK_ICON = { + Gedit.TabState.STATE_LOADING: Gtk.STOCK_OPEN, + Gedit.TabState.STATE_REVERTING: Gtk.STOCK_REVERT_TO_SAVED, + Gedit.TabState.STATE_SAVING: Gtk.STOCK_SAVE, + Gedit.TabState.STATE_PRINTING: Gtk.STOCK_PRINT, + Gedit.TabState.STATE_PRINT_PREVIEWING: Gtk.STOCK_PRINT_PREVIEW, + Gedit.TabState.STATE_SHOWING_PRINT_PREVIEW: Gtk.STOCK_PRINT_PREVIEW, + Gedit.TabState.STATE_LOADING_ERROR: Gtk.STOCK_DIALOG_ERROR, + Gedit.TabState.STATE_REVERTING_ERROR: Gtk.STOCK_DIALOG_ERROR, + Gedit.TabState.STATE_SAVING_ERROR: Gtk.STOCK_DIALOG_ERROR, + Gedit.TabState.STATE_GENERIC_ERROR: Gtk.STOCK_DIALOG_ERROR, + Gedit.TabState.STATE_EXTERNALLY_MODIFIED_NOTIFICATION: Gtk.STOCK_DIALOG_WARNING +} + +# based on tab_get_name() in gedit-documents-panel.c +def get_tab_name(tab): + doc = tab.get_document() + name = doc.get_short_name_for_display() + docname = Gedit.utils_str_middle_truncate(name, 60) # based on MAX_DOC_NAME_LENGTH in gedit-documents-panel.c + + if not doc.get_modified(): + tab_name = escape(docname) + else: + tab_name = "%s" % escape(docname) + + if doc.get_readonly(): + tab_name += " [%s]" % escape(_("Read-Only")) + + return tab_name + +# based on _gedit_tab_get_icon() in gedit-tab.c +def get_tab_icon(tab): + theme = Gtk.IconTheme.get_for_screen(tab.get_screen()) + icon_size = get_tab_icon_size(tab) + state = tab.get_state() + + if state in TAB_STATE_TO_STOCK_ICON: + try: + pixbuf = get_stock_icon(theme, TAB_STATE_TO_STOCK_ICON[state], icon_size) + except GObject.GError: + pixbuf = None + else: + pixbuf = None + + if not pixbuf: + pixbuf = get_icon(theme, tab.get_document().get_location(), icon_size) + + return pixbuf + +def get_tab_icon_size(tab): + is_valid_size, icon_size_width, icon_size_height = Gtk.icon_size_lookup_for_settings(tab.get_settings(), Gtk.IconSize.MENU) + return icon_size_height + +# based on get_stock_icon() in gedit-tab.c +def get_stock_icon(theme, stock, size): + pixbuf = theme.load_icon(stock, size, 0) + return resize_icon(pixbuf, size) + +# based on get_icon() in gedit-tab.c +def get_icon(theme, location, size): + if not location: + return get_stock_icon(theme, Gtk.STOCK_FILE, size) + + # FIXME: Doing a sync stat is bad, this should be fixed + try: + info = location.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None) + except GObject.GError: + info = None + + if not info: + return get_stock_icon(theme, Gtk.STOCK_FILE, size) + + icon = info.get_icon() + + if not icon: + return get_stock_icon(theme, Gtk.STOCK_FILE, size) + + icon_info = theme.lookup_by_gicon(icon, size, 0); + + if not icon_info: + return get_stock_icon(theme, Gtk.STOCK_FILE, size) + + pixbuf = icon_info.load_icon() + + if not pixbuf: + return get_stock_icon(theme, Gtk.STOCK_FILE, size) + + return resize_icon(pixbuf, size) + +# based on resize_icon() in gedit-tab.c +def resize_icon(pixbuf, size): + width = pixbuf.get_width() + height = pixbuf.get_height() + + # if the icon is larger than the nominal size, scale down + if max(width, height) > size: + if width > height: + height = height * size / width + width = size + else: + width = width * size / height + height = size + + pixbuf = pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR) + + return pixbuf + From 74189ce01866012c154cae871ab97a45cc615d1c Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 05:41:06 +0800 Subject: [PATCH 04/41] make it look nicer --- controlyourtabs/tabinfo.py | 21 +++++-------- controlyourtabs/tabinfo_pre312.py | 50 ++++++++++++------------------- 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/controlyourtabs/tabinfo.py b/controlyourtabs/tabinfo.py index 0bbd76c..3b93a30 100644 --- a/controlyourtabs/tabinfo.py +++ b/controlyourtabs/tabinfo.py @@ -53,10 +53,8 @@ def get_tab_name(tab): name = doc.get_short_name_for_display() docname = Gedit.utils_str_middle_truncate(name, 60) # based on MAX_DOC_NAME_LENGTH in gedit-documents-panel.c - if not doc.get_modified(): - tab_name = escape(docname) - else: - tab_name = "%s" % escape(docname) + tab_format = "%s" if doc.get_modified() else "%s" + tab_name = tab_format % escape(docname) try: file = doc.get_file() @@ -71,19 +69,16 @@ def get_tab_name(tab): # based on _gedit_tab_get_icon() in gedit-tab.c def get_tab_icon(tab): - icon_name = None - pixbuf = None state = tab.get_state() - if state in TAB_STATE_TO_NAMED_ICON: - icon_name = TAB_STATE_TO_NAMED_ICON[state] + if state not in TAB_STATE_TO_NAMED_ICON: + return None - if icon_name: - theme = Gtk.IconTheme.get_for_screen(tab.get_screen()) - icon_size = get_tab_icon_size(tab) - pixbuf = Gtk.IconTheme.load_icon(theme, icon_name, icon_size, 0) + theme = Gtk.IconTheme.get_for_screen(tab.get_screen()) + icon_name = TAB_STATE_TO_NAMED_ICON[state] + icon_size = get_tab_icon_size(tab) - return pixbuf + return Gtk.IconTheme.load_icon(theme, icon_name, icon_size, 0) def get_tab_icon_size(tab): is_valid_size, icon_size_width, icon_size_height = Gtk.icon_size_lookup(Gtk.IconSize.MENU) diff --git a/controlyourtabs/tabinfo_pre312.py b/controlyourtabs/tabinfo_pre312.py index bf91e3a..59cde3b 100644 --- a/controlyourtabs/tabinfo_pre312.py +++ b/controlyourtabs/tabinfo_pre312.py @@ -56,10 +56,8 @@ def get_tab_name(tab): name = doc.get_short_name_for_display() docname = Gedit.utils_str_middle_truncate(name, 60) # based on MAX_DOC_NAME_LENGTH in gedit-documents-panel.c - if not doc.get_modified(): - tab_name = escape(docname) - else: - tab_name = "%s" % escape(docname) + tab_format = "%s" if doc.get_modified() else "%s" + tab_name = tab_format % escape(docname) if doc.get_readonly(): tab_name += " [%s]" % escape(_("Read-Only")) @@ -68,17 +66,16 @@ def get_tab_name(tab): # based on _gedit_tab_get_icon() in gedit-tab.c def get_tab_icon(tab): + state = tab.get_state() theme = Gtk.IconTheme.get_for_screen(tab.get_screen()) icon_size = get_tab_icon_size(tab) - state = tab.get_state() + pixbuf = None if state in TAB_STATE_TO_STOCK_ICON: try: pixbuf = get_stock_icon(theme, TAB_STATE_TO_STOCK_ICON[state], icon_size) except GObject.GError: pixbuf = None - else: - pixbuf = None if not pixbuf: pixbuf = get_icon(theme, tab.get_document().get_location(), icon_size) @@ -96,34 +93,25 @@ def get_stock_icon(theme, stock, size): # based on get_icon() in gedit-tab.c def get_icon(theme, location, size): - if not location: - return get_stock_icon(theme, Gtk.STOCK_FILE, size) - - # FIXME: Doing a sync stat is bad, this should be fixed - try: - info = location.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None) - except GObject.GError: - info = None - - if not info: - return get_stock_icon(theme, Gtk.STOCK_FILE, size) - - icon = info.get_icon() - - if not icon: - return get_stock_icon(theme, Gtk.STOCK_FILE, size) + pixbuf = None - icon_info = theme.lookup_by_gicon(icon, size, 0); - - if not icon_info: - return get_stock_icon(theme, Gtk.STOCK_FILE, size) + if location: + # FIXME: Doing a sync stat is bad, this should be fixed + try: + info = location.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None) + except GObject.GError: + info = None - pixbuf = icon_info.load_icon() + icon = info.get_icon() if info else None + icon_info = theme.lookup_by_gicon(icon, size, 0) if icon else None + pixbuf = icon_info.load_icon() if icon_info else None - if not pixbuf: - return get_stock_icon(theme, Gtk.STOCK_FILE, size) + if pixbuf: + pixbuf = resize_icon(pixbuf, size) + else: + pixbuf = get_stock_icon(theme, Gtk.STOCK_FILE, size) - return resize_icon(pixbuf, size) + return pixbuf # based on resize_icon() in gedit-tab.c def resize_icon(pixbuf, size): From 6f242a44064afb953ee778864669dadcfc142ad2 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 06:05:42 +0800 Subject: [PATCH 05/41] split configurable into separate class, move misc methods into top level functions, move schema path from xml file into code --- controlyourtabs/__init__.py | 132 +++++++++++------- ....gedit.plugins.controlyourtabs.gschema.xml | 2 +- controlyourtabs/schemas/gschemas.compiled | Bin 328 -> 240 bytes 3 files changed, 83 insertions(+), 51 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index a6ea9df..38eca90 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -41,7 +41,7 @@ _ = lambda s: s -class ControlYourTabsPlugin(GObject.Object, Gedit.WindowActivatable, PeasGtk.Configurable): +class ControlYourTabsPlugin(GObject.Object, Gedit.WindowActivatable): __gtype_name__ = 'ControlYourTabsPlugin' window = GObject.property(type=Gedit.Window) @@ -60,8 +60,6 @@ class ControlYourTabsPlugin(GObject.Object, Gedit.WindowActivatable, PeasGtk.Con MAX_TAB_WINDOW_HEIGHT_PERCENTAGE = 0.5 - SETTINGS_SCHEMA_ID = 'com.thingsthemselves.gedit.plugins.controlyourtabs' - USE_TABBAR_ORDER = 'use-tabbar-order' @@ -140,7 +138,7 @@ def do_activate(self): self._icon_cell = icon_cell self._space_cell = space_cell self._tabwin_resize_id = None - self._settings = self._get_settings() + self._settings = get_settings() self._tabinfo = tabinfo if is_side_panel_stack else tabinfo_pre312 tab = window.get_active_tab() @@ -191,28 +189,6 @@ def do_update_state(self): pass - # settings ui - - def do_create_configure_widget(self): - settings = self._get_settings() - if settings: - widget = Gtk.CheckButton(_("Use tabbar order for Ctrl+Tab / Ctrl+Shift+Tab")) - widget.set_active(settings.get_boolean(self.USE_TABBAR_ORDER)) - connect_handlers(self, widget, ['toggled'], 'configure_check_button', settings) - connect_handlers(self, settings, ['changed::' + self.USE_TABBAR_ORDER], 'configure_settings', widget) - else: - widget = Gtk.Box() - widget.add(Gtk.Label(_("Sorry, no preferences are available for this version of gedit."))) - widget.set_border_width(5) - return widget - - def on_configure_check_button_toggled(self, widget, settings): - settings.set_boolean(self.USE_TABBAR_ORDER, widget.get_active()) - - def on_configure_settings_changed_use_tabbar_order(self, settings, prop, widget): - widget.set_active(settings.get_boolean(self.USE_TABBAR_ORDER)) - - # plugin setup def on_window_tab_added(self, window, tab): @@ -225,7 +201,7 @@ def _setup(self, window, tab, notebooks, view): self._icon_cell.set_fixed_size(icon_size, icon_size) self._space_cell.set_fixed_size(icon_size, icon_size) - multi = self._get_multi_notebook(tab) + multi = get_multi_notebook(tab) if multi: self._multi = multi @@ -491,30 +467,86 @@ def _do_tabwin_resize(self): return False - # misc +class ControlYourTabsConfigurable(GObject.Object, PeasGtk.Configurable): + + __gtype_name__ = 'ControlYourTabsConfigurable' + + + def do_create_configure_widget(self): + settings = get_settings() + + if settings: + widget = Gtk.CheckButton(_("Use tabbar order for Ctrl+Tab / Ctrl+Shift+Tab")) + + settings.bind( + 'use-tabbar-order', + widget, 'active', + Gio.SettingsBindFlags.DEFAULT + ) - # this is a /hack/ - def _get_multi_notebook(self, tab): - multi = tab.get_parent() - while multi: - if multi.__gtype__.name == 'GeditMultiNotebook': - break - multi = multi.get_parent() - return multi + else: + widget = Gtk.Box() + widget.add(Gtk.Label(_("Sorry, no preferences are available for this version of gedit."))) - def _get_settings(self): - schemas_path = os.path.join(BASE_PATH, 'schemas') + widget.set_border_width(5) + + widget._settings = settings + + return widget + + +# this is a /hack/ +# can do window.get_template_child(Gedit.Window, 'multi_notebook') since 3.12 +def get_multi_notebook(tab): + multi = tab.get_parent() + + while multi: + if multi.__gtype__.name == 'GeditMultiNotebook': + break + + multi = multi.get_parent() + + return multi + +def get_settings(): + schemas_path = os.path.join(BASE_PATH, 'schemas') + + try: + schema_source = Gio.SettingsSchemaSource.new_from_directory( + schemas_path, + Gio.SettingsSchemaSource.get_default(), + False + ) + except AttributeError: # gedit < 3.4 try: - # available in gedit >= 3.4 - schema_source = Gio.SettingsSchemaSource.new_from_directory(schemas_path, Gio.SettingsSchemaSource.get_default(), False) - schema = Gio.SettingsSchemaSource.lookup(schema_source, self.SETTINGS_SCHEMA_ID, False) - settings = Gio.Settings.new_full(schema, None, None) if schema else None + Gedit.debug_plugin_message("relocatable schemas not supported") except AttributeError: - settings = None - except: - try: - Gedit.debug_plugin_message("could not load settings schema from %s", schemas_path) - except AttributeError: - pass - settings = None - return settings + pass + + schema_source = None + + except: + try: + Gedit.debug_plugin_message("could not load settings schema source from %s", schemas_path) + except AttributeError: + pass + + schema_source = None + + if not schema_source: + return None + + schema = schema_source.lookup( + 'com.thingsthemselves.gedit.plugins.controlyourtabs', + False + ) + + if not schema: + return None + + return Gio.Settings.new_full( + schema, + None, + '/com/thingsthemselves/gedit/plugins/controlyourtabs/' + ) + diff --git a/controlyourtabs/schemas/com.thingsthemselves.gedit.plugins.controlyourtabs.gschema.xml b/controlyourtabs/schemas/com.thingsthemselves.gedit.plugins.controlyourtabs.gschema.xml index cc4f2ae..dc68539 100644 --- a/controlyourtabs/schemas/com.thingsthemselves.gedit.plugins.controlyourtabs.gschema.xml +++ b/controlyourtabs/schemas/com.thingsthemselves.gedit.plugins.controlyourtabs.gschema.xml @@ -1,6 +1,6 @@ - + false Use tabbar order diff --git a/controlyourtabs/schemas/gschemas.compiled b/controlyourtabs/schemas/gschemas.compiled index 1c62ca1772c645aa5acc04c0371e7f23c5477ffd..097915c01db8eba69e4d689ec4273c46ac2940bf 100644 GIT binary patch delta 133 zcmX@X^nr1Ljno+i1_oA928RDY@CL|cVDMo8(H|HlhFK*rfkhY?7=iQ^j{RaF8l*#j xp^V`HkbVlpFx?rW)EnO(aPR4hrtRlRks^5I%r|i|C!L zg9a|&k=*fr)48xpXb;utA{S-ft5)>DE9KcH%M+y?TN>@H$+jl9Uc`>}q1X7JyeR+LJavea)rJBd$S2l$ zw{KA{^#Q&&F>{3V^`V}8#z=C?og83FEbPG8m9!WY_t{4Hl;!|93iE$q{t@PtFpm5P JbAVEs_TQG>QK|p{ From 79e88d164e5417bdcf51b246f7b4c9006f84e900 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 06:27:17 +0800 Subject: [PATCH 06/41] use explicit constructors --- controlyourtabs/__init__.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 38eca90..573dc3b 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -72,7 +72,7 @@ def do_activate(self): window = self.window notebooks = {} - tabwin = Gtk.Window(type=Gtk.WindowType.POPUP) + tabwin = Gtk.Window.new(Gtk.WindowType.POPUP) tabwin.set_transient_for(window) tabwin.set_destroy_with_parent(True) tabwin.set_accept_focus(False) @@ -83,25 +83,26 @@ def do_activate(self): tabwin.set_skip_taskbar_hint(False) tabwin.set_skip_pager_hint(False) - sw = Gtk.ScrolledWindow() + sw = Gtk.ScrolledWindow.new(None, None) sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) sw.show() tabwin.add(sw) - view = Gtk.TreeView() + view = Gtk.TreeView.new() view.set_enable_search(False) view.set_headers_visible(False) view.show() sw.add(view) - col = Gtk.TreeViewColumn(_("Documents")) + col = Gtk.TreeViewColumn.new() + col.set_title(_("Documents")) col.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) - icon_cell = Gtk.CellRendererPixbuf() - name_cell = Gtk.CellRendererText() - space_cell = Gtk.CellRendererPixbuf() + icon_cell = Gtk.CellRendererPixbuf.new() + name_cell = Gtk.CellRendererText.new() + space_cell = Gtk.CellRendererPixbuf.new() col.pack_start(icon_cell, False) col.pack_start(name_cell, True) @@ -223,7 +224,7 @@ def _setup(self, window, tab, notebooks, view): def on_multi_notebook_notebook_added(self, multi, notebook, notebooks, view): if notebook not in notebooks: - model = Gtk.ListStore(GdkPixbuf.Pixbuf, str, Gedit.Tab, 'gboolean') + model = Gtk.ListStore.new([GdkPixbuf.Pixbuf, str, Gedit.Tab, bool]) connect_handlers(self, model, ['row-inserted', 'row-deleted', 'row-changed'], 'model', view, view.get_selection()) notebooks[notebook] = ([], model) @@ -476,7 +477,9 @@ def do_create_configure_widget(self): settings = get_settings() if settings: - widget = Gtk.CheckButton(_("Use tabbar order for Ctrl+Tab / Ctrl+Shift+Tab")) + widget = Gtk.CheckButton.new_with_label( + _("Use tabbar order for Ctrl+Tab / Ctrl+Shift+Tab") + ) settings.bind( 'use-tabbar-order', @@ -485,8 +488,12 @@ def do_create_configure_widget(self): ) else: - widget = Gtk.Box() - widget.add(Gtk.Label(_("Sorry, no preferences are available for this version of gedit."))) + label = Gtk.Label.new( + _("Sorry, no preferences are available for this version of gedit.") + ) + + widget = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) + widget.add(label) widget.set_border_width(5) From 58c6a86d737a73c470894cadaa81e824af4e9f90 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 06:31:43 +0800 Subject: [PATCH 07/41] tweak class name, use simpler settings access --- controlyourtabs/__init__.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 573dc3b..20f1888 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -41,10 +41,11 @@ _ = lambda s: s -class ControlYourTabsPlugin(GObject.Object, Gedit.WindowActivatable): - __gtype_name__ = 'ControlYourTabsPlugin' +class ControlYourTabsWindowActivatable(GObject.Object, Gedit.WindowActivatable): - window = GObject.property(type=Gedit.Window) + __gtype_name__ = 'ControlYourTabsWindowActivatable' + + window = GObject.Property(type=Gedit.Window) SELECTED_TAB_COLUMN = 3 @@ -60,10 +61,6 @@ class ControlYourTabsPlugin(GObject.Object, Gedit.WindowActivatable): MAX_TAB_WINDOW_HEIGHT_PERCENTAGE = 0.5 - USE_TABBAR_ORDER = 'use-tabbar-order' - - - # gedit plugin api def __init__(self): GObject.Object.__init__(self) @@ -322,7 +319,7 @@ def on_window_key_press_event(self, window, event, notebooks, view): settings = self._settings notebook = cur.get_parent() stack, model = notebooks[notebook] - is_tabbing = is_tab_key and not (settings and settings.get_boolean(self.USE_TABBAR_ORDER)) + is_tabbing = is_tab_key and not (settings and settings['use-tabbar-order']) tabs = stack if is_tabbing else notebook.get_children() tlen = len(tabs) From f8b608733731b8299f6cefba678f6ddc8b9cb49e Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 06:46:01 +0800 Subject: [PATCH 08/41] spacing / wrapping --- controlyourtabs/__init__.py | 109 ++++++++++++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 11 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 20f1888..ffc4792 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -140,10 +140,13 @@ def do_activate(self): self._tabinfo = tabinfo if is_side_panel_stack else tabinfo_pre312 tab = window.get_active_tab() + if tab: self._setup(window, tab, notebooks, view) + if self._multi: self.on_window_active_tab_changed(window, tab, notebooks, view) + else: connect_handlers(self, window, ['tab-added'], 'window') @@ -191,6 +194,7 @@ def do_update_state(self): def on_window_tab_added(self, window, tab): disconnect_handlers(self, window) + self._setup(window, tab, self._notebooks, self._view) def _setup(self, window, tab, notebooks, view): @@ -205,10 +209,33 @@ def _setup(self, window, tab, notebooks, view): self._multi = multi for doc in window.get_documents(): - self.on_multi_notebook_notebook_added(multi, Gedit.Tab.get_from_document(doc).get_parent(), notebooks, view) - - connect_handlers(self, multi, ['notebook-added', 'notebook-removed', 'tab-added', 'tab-removed'], 'multi_notebook', notebooks, view) - connect_handlers(self, window, ['tabs-reordered', 'active-tab-changed', 'key-press-event', 'key-release-event', 'focus-out-event', 'configure-event'], 'window', notebooks, view) + notebook = Gedit.Tab.get_from_document(doc).get_parent() + self.on_multi_notebook_notebook_added(multi, notebook, notebooks, view) + + connect_handlers( + self, multi, + [ + 'notebook-added', + 'notebook-removed', + 'tab-added', + 'tab-removed' + ], + 'multi_notebook', + notebooks, view + ) + connect_handlers( + self, window, + [ + 'tabs-reordered', + 'active-tab-changed', + 'key-press-event', + 'key-release-event', + 'focus-out-event', + 'configure-event' + ], + 'window', + notebooks, view + ) else: try: @@ -221,8 +248,19 @@ def _setup(self, window, tab, notebooks, view): def on_multi_notebook_notebook_added(self, multi, notebook, notebooks, view): if notebook not in notebooks: - model = Gtk.ListStore.new([GdkPixbuf.Pixbuf, str, Gedit.Tab, bool]) - connect_handlers(self, model, ['row-inserted', 'row-deleted', 'row-changed'], 'model', view, view.get_selection()) + model = Gtk.ListStore.new((GdkPixbuf.Pixbuf, str, Gedit.Tab, bool)) + + connect_handlers( + self, model, + [ + 'row-inserted', + 'row-deleted', + 'row-changed' + ], + 'model', + view, view.get_selection() + ) + notebooks[notebook] = ([], model) for tab in notebook.get_children(): @@ -234,37 +272,65 @@ def on_multi_notebook_notebook_removed(self, multi, notebook, notebooks, view): self.on_multi_notebook_tab_removed(multi, notebook, tab, notebooks, view) stack, model = notebooks[notebook] + if view.get_model() is model: view.set_model(None) + disconnect_handlers(self, model) + del notebooks[notebook] def on_multi_notebook_tab_added(self, multi, notebook, tab, notebooks, view): stack, model = notebooks[notebook] + if tab not in stack: stack.append(tab) - model.append((self._tabinfo.get_tab_icon(tab), self._tabinfo.get_tab_name(tab), tab, False)) - connect_handlers(self, tab, ['notify::name', 'notify::state'], self.on_sync_icon_and_name, notebooks) + + model.append( + ( + self._tabinfo.get_tab_icon(tab), + self._tabinfo.get_tab_name(tab), + tab, + False + ) + ) + + connect_handlers( + self, tab, + [ + 'notify::name', + 'notify::state' + ], + self.on_sync_icon_and_name, + notebooks + ) def on_multi_notebook_tab_removed(self, multi, notebook, tab, notebooks, view): stack, model = notebooks[notebook] + if tab in stack: disconnect_handlers(self, tab) + model.remove(model.get_iter(stack.index(tab))) + stack.remove(tab) def on_window_tabs_reordered(self, window, notebooks, view): multi = self._multi tab = window.get_active_tab() new_notebook = tab.get_parent() + if tab not in notebooks[new_notebook][0]: old_notebook = None + for notebook in notebooks: if tab in notebooks[notebook][0]: old_notebook = notebook break + if old_notebook: self.on_multi_notebook_tab_removed(multi, old_notebook, tab, notebooks, view) + self.on_multi_notebook_tab_added(multi, new_notebook, tab, notebooks, view) def on_window_active_tab_changed(self, window, tab, notebooks, view): @@ -282,8 +348,17 @@ def on_window_active_tab_changed(self, window, tab, notebooks, view): if tab in stack: model.move_after(model.get_iter(stack.index(tab)), None) stack.remove(tab) + else: - model.insert(0, (self._tabinfo.get_tab_icon(tab), self._tabinfo.get_tab_name(tab), tab, False)) + model.insert( + 0, + ( + self._tabinfo.get_tab_icon(tab), + self._tabinfo.get_tab_name(tab), + tab, + False + ) + ) stack.insert(0, tab) model[0][self.SELECTED_TAB_COLUMN] = True @@ -336,10 +411,12 @@ def on_window_key_press_event(self, window, event, notebooks, view): if not self._tabbing: view.scroll_to_cell(Gtk.TreePath.new_first(), None, True, 0, 0) tabwin.show_all() + else: tabwin.present_with_time(event.time) self._tabbing = True + else: self._paging = True @@ -374,10 +451,12 @@ def _end_switching(self): self._switching = False self._ctrl_l = False self._ctrl_r = False + self._tabwin.hide() window = self.window tab = window.get_active_tab() + if tab: self.on_window_active_tab_changed(window, tab, self._notebooks, self._view) @@ -394,12 +473,15 @@ def on_model_row_changed(self, model, path, iter, view, sel): if model[path][self.SELECTED_TAB_COLUMN]: sel.select_path(path) view.scroll_to_cell(path, None, True, 0.5, 0) + else: sel.unselect_path(path) + self._schedule_tabwin_resize() def on_sync_icon_and_name(self, tab, pspec, notebooks): stack, model = notebooks[tab.get_parent()] + if tab in stack: path = stack.index(tab) model[path][0] = self._tabinfo.get_tab_icon(tab) @@ -415,8 +497,8 @@ def _schedule_tabwin_resize(self): # this feels like a giant hack try: resize_id = GLib.idle_add(self._do_tabwin_resize) - except TypeError: - # gedit 3.0 + + except TypeError: # gedit 3.0 resize_id = GObject.idle_add(self._do_tabwin_resize) self._tabwin_resize_id = resize_id @@ -424,6 +506,7 @@ def _schedule_tabwin_resize(self): def _cancel_tabwin_resize(self): if self._tabwin_resize_id: GLib.source_remove(self._tabwin_resize_id) + self._tabwin_resize_id = None def _do_tabwin_resize(self): @@ -462,6 +545,7 @@ def _do_tabwin_resize(self): self._tabwin.set_size_request(tabwin_width, tabwin_height) self._tabwin_resize_id = None + return False @@ -521,9 +605,11 @@ def get_settings(): Gio.SettingsSchemaSource.get_default(), False ) + except AttributeError: # gedit < 3.4 try: Gedit.debug_plugin_message("relocatable schemas not supported") + except AttributeError: pass @@ -532,6 +618,7 @@ def get_settings(): except: try: Gedit.debug_plugin_message("could not load settings schema source from %s", schemas_path) + except AttributeError: pass From 63ae0fabe77ba6d12ef7d3bf86100efdced5b208 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 06:59:43 +0800 Subject: [PATCH 09/41] reduce indentation --- controlyourtabs/__init__.py | 402 +++++++++++++++++++----------------- 1 file changed, 217 insertions(+), 185 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index ffc4792..28d4c70 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -205,166 +205,179 @@ def _setup(self, window, tab, notebooks, view): multi = get_multi_notebook(tab) - if multi: - self._multi = multi - - for doc in window.get_documents(): - notebook = Gedit.Tab.get_from_document(doc).get_parent() - self.on_multi_notebook_notebook_added(multi, notebook, notebooks, view) - - connect_handlers( - self, multi, - [ - 'notebook-added', - 'notebook-removed', - 'tab-added', - 'tab-removed' - ], - 'multi_notebook', - notebooks, view - ) - connect_handlers( - self, window, - [ - 'tabs-reordered', - 'active-tab-changed', - 'key-press-event', - 'key-release-event', - 'focus-out-event', - 'configure-event' - ], - 'window', - notebooks, view - ) - - else: + if not multi: try: Gedit.debug_plugin_message("cannot find multi notebook from %s", tab) except AttributeError: pass + return + + self._multi = multi + + for doc in window.get_documents(): + notebook = Gedit.Tab.get_from_document(doc).get_parent() + self.on_multi_notebook_notebook_added(multi, notebook, notebooks, view) + + connect_handlers( + self, multi, + [ + 'notebook-added', + 'notebook-removed', + 'tab-added', + 'tab-removed' + ], + 'multi_notebook', + notebooks, view + ) + connect_handlers( + self, window, + [ + 'tabs-reordered', + 'active-tab-changed', + 'key-press-event', + 'key-release-event', + 'focus-out-event', + 'configure-event' + ], + 'window', + notebooks, view + ) + # signal handlers / main logic def on_multi_notebook_notebook_added(self, multi, notebook, notebooks, view): - if notebook not in notebooks: - model = Gtk.ListStore.new((GdkPixbuf.Pixbuf, str, Gedit.Tab, bool)) - - connect_handlers( - self, model, - [ - 'row-inserted', - 'row-deleted', - 'row-changed' - ], - 'model', - view, view.get_selection() - ) + if notebook in notebooks: + return + + model = Gtk.ListStore.new((GdkPixbuf.Pixbuf, str, Gedit.Tab, bool)) + + connect_handlers( + self, model, + [ + 'row-inserted', + 'row-deleted', + 'row-changed' + ], + 'model', + view, view.get_selection() + ) - notebooks[notebook] = ([], model) + notebooks[notebook] = ([], model) - for tab in notebook.get_children(): - self.on_multi_notebook_tab_added(multi, notebook, tab, notebooks, view) + for tab in notebook.get_children(): + self.on_multi_notebook_tab_added(multi, notebook, tab, notebooks, view) def on_multi_notebook_notebook_removed(self, multi, notebook, notebooks, view): - if notebook in notebooks: - for tab in notebook.get_children(): - self.on_multi_notebook_tab_removed(multi, notebook, tab, notebooks, view) + if notebook not in notebooks: + return - stack, model = notebooks[notebook] + for tab in notebook.get_children(): + self.on_multi_notebook_tab_removed(multi, notebook, tab, notebooks, view) - if view.get_model() is model: - view.set_model(None) + stack, model = notebooks[notebook] - disconnect_handlers(self, model) + if view.get_model() is model: + view.set_model(None) + + disconnect_handlers(self, model) - del notebooks[notebook] + del notebooks[notebook] def on_multi_notebook_tab_added(self, multi, notebook, tab, notebooks, view): stack, model = notebooks[notebook] - if tab not in stack: - stack.append(tab) - - model.append( - ( - self._tabinfo.get_tab_icon(tab), - self._tabinfo.get_tab_name(tab), - tab, - False - ) - ) + if tab in stack: + return + + stack.append(tab) - connect_handlers( - self, tab, - [ - 'notify::name', - 'notify::state' - ], - self.on_sync_icon_and_name, - notebooks + model.append( + ( + self._tabinfo.get_tab_icon(tab), + self._tabinfo.get_tab_name(tab), + tab, + False ) + ) + + connect_handlers( + self, tab, + [ + 'notify::name', + 'notify::state' + ], + self.on_sync_icon_and_name, + notebooks + ) def on_multi_notebook_tab_removed(self, multi, notebook, tab, notebooks, view): stack, model = notebooks[notebook] - if tab in stack: - disconnect_handlers(self, tab) + if tab not in stack: + return + + disconnect_handlers(self, tab) - model.remove(model.get_iter(stack.index(tab))) + model.remove(model.get_iter(stack.index(tab))) - stack.remove(tab) + stack.remove(tab) def on_window_tabs_reordered(self, window, notebooks, view): multi = self._multi tab = window.get_active_tab() new_notebook = tab.get_parent() - if tab not in notebooks[new_notebook][0]: - old_notebook = None + if tab in notebooks[new_notebook][0]: + return - for notebook in notebooks: - if tab in notebooks[notebook][0]: - old_notebook = notebook - break + old_notebook = None - if old_notebook: - self.on_multi_notebook_tab_removed(multi, old_notebook, tab, notebooks, view) + for notebook in notebooks: + if tab in notebooks[notebook][0]: + old_notebook = notebook + break - self.on_multi_notebook_tab_added(multi, new_notebook, tab, notebooks, view) + if old_notebook: + self.on_multi_notebook_tab_removed(multi, old_notebook, tab, notebooks, view) + + self.on_multi_notebook_tab_added(multi, new_notebook, tab, notebooks, view) def on_window_active_tab_changed(self, window, tab, notebooks, view): - if not self._switching: - stack, model = notebooks[tab.get_parent()] - - if view.get_model() is not model: - view.set_model(model) - self._schedule_tabwin_resize() - - for row in model: - row[self.SELECTED_TAB_COLUMN] = False - - if not self._tabbing and not self._paging: - if tab in stack: - model.move_after(model.get_iter(stack.index(tab)), None) - stack.remove(tab) - - else: - model.insert( - 0, - ( - self._tabinfo.get_tab_icon(tab), - self._tabinfo.get_tab_name(tab), - tab, - False - ) - ) + if self._switching: + return - stack.insert(0, tab) - model[0][self.SELECTED_TAB_COLUMN] = True + stack, model = notebooks[tab.get_parent()] + + if view.get_model() is not model: + view.set_model(model) + self._schedule_tabwin_resize() + + for row in model: + row[self.SELECTED_TAB_COLUMN] = False + + if not self._tabbing and not self._paging: + if tab in stack: + model.move_after(model.get_iter(stack.index(tab)), None) + stack.remove(tab) else: - model[stack.index(tab)][self.SELECTED_TAB_COLUMN] = True + model.insert( + 0, + ( + self._tabinfo.get_tab_icon(tab), + self._tabinfo.get_tab_name(tab), + tab, + False + ) + ) + + stack.insert(0, tab) + model[0][self.SELECTED_TAB_COLUMN] = True + + else: + model[stack.index(tab)][self.SELECTED_TAB_COLUMN] = True def on_window_key_press_event(self, window, event, notebooks, view): key = Gdk.keyval_name(event.keyval) @@ -390,39 +403,44 @@ def on_window_key_press_event(self, window, event, notebooks, view): return False cur = window.get_active_tab() - if cur: - settings = self._settings - notebook = cur.get_parent() - stack, model = notebooks[notebook] - is_tabbing = is_tab_key and not (settings and settings['use-tabbar-order']) - tabs = stack if is_tabbing else notebook.get_children() - tlen = len(tabs) - if tlen > 1 and cur in tabs: - i = -1 if is_up_dir else 1 - next = tabs[(tabs.index(cur) + i) % tlen] + if not cur: + return True - model[stack.index(cur)][self.SELECTED_TAB_COLUMN] = False - model[stack.index(next)][self.SELECTED_TAB_COLUMN] = True + settings = self._settings + notebook = cur.get_parent() + stack, model = notebooks[notebook] + is_tabbing = is_tab_key and not (settings and settings['use-tabbar-order']) + tabs = stack if is_tabbing else notebook.get_children() + tlen = len(tabs) - if is_tabbing: - tabwin = self._tabwin + if tlen < 2 or cur not in tabs: + return True - if not self._tabbing: - view.scroll_to_cell(Gtk.TreePath.new_first(), None, True, 0, 0) - tabwin.show_all() + i = -1 if is_up_dir else 1 + next = tabs[(tabs.index(cur) + i) % tlen] - else: - tabwin.present_with_time(event.time) + model[stack.index(cur)][self.SELECTED_TAB_COLUMN] = False + model[stack.index(next)][self.SELECTED_TAB_COLUMN] = True - self._tabbing = True + if is_tabbing: + tabwin = self._tabwin - else: - self._paging = True + if not self._tabbing: + view.scroll_to_cell(Gtk.TreePath.new_first(), None, True, 0, 0) + tabwin.show_all() - self._switching = True - window.set_active_tab(next) - self._switching = False + else: + tabwin.present_with_time(event.time) + + self._tabbing = True + + else: + self._paging = True + + self._switching = True + window.set_active_tab(next) + self._switching = False return True @@ -445,69 +463,83 @@ def on_window_configure_event(self, window, event, notebooks, view): self._schedule_tabwin_resize() def _end_switching(self): - if self._tabbing or self._paging: - self._tabbing = False - self._paging = False - self._switching = False - self._ctrl_l = False - self._ctrl_r = False + if not self._tabbing and not self._paging: + return + + self._tabbing = False + self._paging = False + self._switching = False + self._ctrl_l = False + self._ctrl_r = False - self._tabwin.hide() + self._tabwin.hide() - window = self.window - tab = window.get_active_tab() + window = self.window + tab = window.get_active_tab() - if tab: - self.on_window_active_tab_changed(window, tab, self._notebooks, self._view) + if tab: + self.on_window_active_tab_changed(window, tab, self._notebooks, self._view) def on_model_row_inserted(self, model, path, iter, view, sel): - if view.get_model() is model: - self._schedule_tabwin_resize() + if view.get_model() is not model: + return + + self._schedule_tabwin_resize() def on_model_row_deleted(self, model, path, view, sel): - if view.get_model() is model: - self._schedule_tabwin_resize() + if view.get_model() is not model: + return + + self._schedule_tabwin_resize() def on_model_row_changed(self, model, path, iter, view, sel): - if view.get_model() is model: - if model[path][self.SELECTED_TAB_COLUMN]: - sel.select_path(path) - view.scroll_to_cell(path, None, True, 0.5, 0) + if view.get_model() is not model: + return - else: - sel.unselect_path(path) + if model[path][self.SELECTED_TAB_COLUMN]: + sel.select_path(path) + view.scroll_to_cell(path, None, True, 0.5, 0) - self._schedule_tabwin_resize() + else: + sel.unselect_path(path) + + self._schedule_tabwin_resize() def on_sync_icon_and_name(self, tab, pspec, notebooks): stack, model = notebooks[tab.get_parent()] - if tab in stack: - path = stack.index(tab) - model[path][0] = self._tabinfo.get_tab_icon(tab) - model[path][1] = self._tabinfo.get_tab_name(tab) + if tab not in stack: + return + + path = stack.index(tab) + model[path][0] = self._tabinfo.get_tab_icon(tab) + model[path][1] = self._tabinfo.get_tab_name(tab) # tab window resizing def _schedule_tabwin_resize(self): - if not self._tabwin_resize_id: - # need to wait a little before asking the treeview for its preferred size - # maybe because treeview rendering is async? - # this feels like a giant hack - try: - resize_id = GLib.idle_add(self._do_tabwin_resize) + if self._tabwin_resize_id: + return + + # need to wait a little before asking the treeview for its preferred size + # maybe because treeview rendering is async? + # this feels like a giant hack + try: + resize_id = GLib.idle_add(self._do_tabwin_resize) - except TypeError: # gedit 3.0 - resize_id = GObject.idle_add(self._do_tabwin_resize) + except TypeError: # gedit 3.0 + resize_id = GObject.idle_add(self._do_tabwin_resize) - self._tabwin_resize_id = resize_id + self._tabwin_resize_id = resize_id def _cancel_tabwin_resize(self): - if self._tabwin_resize_id: - GLib.source_remove(self._tabwin_resize_id) + if not self._tabwin_resize_id: + return - self._tabwin_resize_id = None + GLib.source_remove(self._tabwin_resize_id) + + self._tabwin_resize_id = None def _do_tabwin_resize(self): view = self._view @@ -586,15 +618,15 @@ def do_create_configure_widget(self): # this is a /hack/ # can do window.get_template_child(Gedit.Window, 'multi_notebook') since 3.12 def get_multi_notebook(tab): - multi = tab.get_parent() + widget = tab.get_parent() - while multi: - if multi.__gtype__.name == 'GeditMultiNotebook': + while widget: + if widget.__gtype__.name == 'GeditMultiNotebook': break - multi = multi.get_parent() + widget = widget.get_parent() - return multi + return widget def get_settings(): schemas_path = os.path.join(BASE_PATH, 'schemas') From fb40114759dd9c33447bb6efc3000df6a09b78d6 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 22:16:36 +0800 Subject: [PATCH 10/41] small p --- controlyourtabs/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 28d4c70..fd6e434 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -45,7 +45,7 @@ class ControlYourTabsWindowActivatable(GObject.Object, Gedit.WindowActivatable): __gtype_name__ = 'ControlYourTabsWindowActivatable' - window = GObject.Property(type=Gedit.Window) + window = GObject.property(type=Gedit.Window) # lowercase 'p' for gedit < 3.4 SELECTED_TAB_COLUMN = 3 @@ -208,7 +208,7 @@ def _setup(self, window, tab, notebooks, view): if not multi: try: Gedit.debug_plugin_message("cannot find multi notebook from %s", tab) - except AttributeError: + except AttributeError: # gedit < 3.4 pass return @@ -642,7 +642,7 @@ def get_settings(): try: Gedit.debug_plugin_message("relocatable schemas not supported") - except AttributeError: + except AttributeError: # gedit < 3.4 pass schema_source = None @@ -651,7 +651,7 @@ def get_settings(): try: Gedit.debug_plugin_message("could not load settings schema source from %s", schemas_path) - except AttributeError: + except AttributeError: # gedit < 3.4 pass schema_source = None From 2ddbce0ea6da35e8dd9f0b968842deeb2ce4ab61 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 17 Oct 2017 22:18:57 +0800 Subject: [PATCH 11/41] already scrolled when selected tab column was set --- controlyourtabs/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index fd6e434..6e59c92 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -427,7 +427,6 @@ def on_window_key_press_event(self, window, event, notebooks, view): tabwin = self._tabwin if not self._tabbing: - view.scroll_to_cell(Gtk.TreePath.new_first(), None, True, 0, 0) tabwin.show_all() else: From ce43ba11b68a3f38c82d8bfb9b5127caec5a53e8 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 18 Oct 2017 05:44:57 +0800 Subject: [PATCH 12/41] refactored tab switching code, moved key-specific functions into separate file, added ctrl-esc to cancel switching, block all input during switching instead of cancelling switching --- Changelog.md | 5 + README.md | 3 + controlyourtabs/__init__.py | 198 +++++++++++++++++------------------- controlyourtabs/keyinfo.py | 69 +++++++++++++ 4 files changed, 169 insertions(+), 106 deletions(-) create mode 100644 controlyourtabs/keyinfo.py diff --git a/Changelog.md b/Changelog.md index f64f0b1..a49bcc1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased +* Added Ctrl-Esc to cancel tab switching, causes the initial tab (before + switching began) to be active again +* Prevent all input during tab switching instead of cancelling switching + ## v0.3.1 (2017-10-17) * Show a debug message if the settings schema could not be loaded * Fixed division by zero error diff --git a/README.md b/README.md index d048ddc..397ce6b 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,9 @@ gedit 2 is [v0.1.2][]. * Ctrl+Page Up / Ctrl+Page Down - Switch tabs in tabbar order. +Hold down Ctrl to continue tab switching. Press +Esc while switching to cancel and return to the initial tab. + ## Preferences In gedit 3.4 or later, the plugin supports these preferences: diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 6e59c92..3be07c5 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -27,7 +27,7 @@ import os.path from gi.repository import GObject, GLib, Gtk, Gdk, GdkPixbuf, Gio, Gedit, PeasGtk from .utils import connect_handlers, disconnect_handlers -from . import tabinfo, tabinfo_pre312 +from . import keyinfo, tabinfo, tabinfo_pre312 BASE_PATH = os.path.dirname(os.path.realpath(__file__)) LOCALE_PATH = os.path.join(BASE_PATH, 'locale') @@ -49,14 +49,6 @@ class ControlYourTabsWindowActivatable(GObject.Object, Gedit.WindowActivatable): SELECTED_TAB_COLUMN = 3 - META_KEYS = ['Shift_L', 'Shift_R', - 'Control_L', 'Control_R', - 'Meta_L', 'Meta_R', - 'Super_L', 'Super_R', - 'Hyper_L', 'Hyper_R', - 'Alt_L', 'Alt_R'] - # Compose, Apple? - MAX_TAB_WINDOW_ROWS = 9 MAX_TAB_WINDOW_HEIGHT_PERCENTAGE = 0.5 @@ -123,11 +115,10 @@ def do_activate(self): else: is_side_panel_stack = isinstance(window.get_side_panel(), GtkStack) # since 3.12 - self._tabbing = False - self._paging = False - self._switching = False - self._ctrl_l = False - self._ctrl_r = False + self._is_switching = False + self._is_tabwin_visible = False + self._is_control_held = keyinfo.default_control_held() + self._initial_tab = None self._multi = None self._notebooks = notebooks self._tabwin = tabwin @@ -166,15 +157,14 @@ def do_deactivate(self): disconnect_handlers(self, Gedit.Tab.get_from_document(doc)) self._cancel_tabwin_resize() - self._end_switching() + self.end_switching() self._tabwin.destroy() - self._tabbing = None - self._paging = None - self._switching = None - self._ctrl_l = None - self._ctrl_r = None + self._is_switching = None + self._is_tabwin_visible = None + self._is_control_held = None + self._initial_tab = None self._multi = None self._notebooks = None self._tabwin = None @@ -313,6 +303,9 @@ def on_multi_notebook_tab_added(self, multi, notebook, tab, notebooks, view): ) def on_multi_notebook_tab_removed(self, multi, notebook, tab, notebooks, view): + if tab == self._initial_tab: + self._initial_tab = None + stack, model = notebooks[notebook] if tab not in stack: @@ -345,9 +338,6 @@ def on_window_tabs_reordered(self, window, notebooks, view): self.on_multi_notebook_tab_added(multi, new_notebook, tab, notebooks, view) def on_window_active_tab_changed(self, window, tab, notebooks, view): - if self._switching: - return - stack, model = notebooks[tab.get_parent()] if view.get_model() is not model: @@ -357,7 +347,7 @@ def on_window_active_tab_changed(self, window, tab, notebooks, view): for row in model: row[self.SELECTED_TAB_COLUMN] = False - if not self._tabbing and not self._paging: + if not self._is_switching: if tab in stack: model.move_after(model.get_iter(stack.index(tab)), None) stack.remove(tab) @@ -380,105 +370,39 @@ def on_window_active_tab_changed(self, window, tab, notebooks, view): model[stack.index(tab)][self.SELECTED_TAB_COLUMN] = True def on_window_key_press_event(self, window, event, notebooks, view): - key = Gdk.keyval_name(event.keyval) - state = event.state & Gtk.accelerator_get_default_mod_mask() - - if key == 'Control_L': - self._ctrl_l = True - - if key == 'Control_R': - self._ctrl_r = True - - if key in self.META_KEYS or not state & Gdk.ModifierType.CONTROL_MASK: - return False - - is_ctrl = state == Gdk.ModifierType.CONTROL_MASK - is_ctrl_shift = state == Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK - is_tab_key = key in ['ISO_Left_Tab', 'Tab'] - is_page_key = key in ['Page_Up', 'Page_Down'] - is_up_dir = key in ['ISO_Left_Tab', 'Page_Up'] - - if not (((is_ctrl or is_ctrl_shift) and is_tab_key) or (is_ctrl and is_page_key)): - self._end_switching() - return False - - cur = window.get_active_tab() - - if not cur: - return True + self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, True) settings = self._settings - notebook = cur.get_parent() - stack, model = notebooks[notebook] - is_tabbing = is_tab_key and not (settings and settings['use-tabbar-order']) - tabs = stack if is_tabbing else notebook.get_children() - tlen = len(tabs) - - if tlen < 2 or cur not in tabs: - return True - - i = -1 if is_up_dir else 1 - next = tabs[(tabs.index(cur) + i) % tlen] - - model[stack.index(cur)][self.SELECTED_TAB_COLUMN] = False - model[stack.index(next)][self.SELECTED_TAB_COLUMN] = True - - if is_tabbing: - tabwin = self._tabwin + is_control_tab, is_control_page, is_control_escape = keyinfo.is_control_keys(event) + block_event = True - if not self._tabbing: - tabwin.show_all() + if is_control_tab and settings and settings['use-tabbar-order']: + is_control_tab = False + is_control_page = True - else: - tabwin.present_with_time(event.time) + if self._is_switching and is_control_escape: + self.end_switching(True) - self._tabbing = True + elif is_control_tab or is_control_page: + self.switch_tab(is_control_tab, keyinfo.is_next_key(event), event.time) else: - self._paging = True - - self._switching = True - window.set_active_tab(next) - self._switching = False + block_event = self._is_switching - return True + return block_event def on_window_key_release_event(self, window, event, notebooks, view): - key = Gdk.keyval_name(event.keyval) + self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, False) - if key == 'Control_L': - self._ctrl_l = False - - if key == 'Control_R': - self._ctrl_r = False - - if not self._ctrl_l and not self._ctrl_r: - self._end_switching() + if not any(self._is_control_held): + self.end_switching() def on_window_focus_out_event(self, window, event, notebooks, view): - self._end_switching() + self.end_switching() def on_window_configure_event(self, window, event, notebooks, view): self._schedule_tabwin_resize() - def _end_switching(self): - if not self._tabbing and not self._paging: - return - - self._tabbing = False - self._paging = False - self._switching = False - self._ctrl_l = False - self._ctrl_r = False - - self._tabwin.hide() - - window = self.window - tab = window.get_active_tab() - - if tab: - self.on_window_active_tab_changed(window, tab, self._notebooks, self._view) - def on_model_row_inserted(self, model, path, iter, view, sel): if view.get_model() is not model: return @@ -515,6 +439,68 @@ def on_sync_icon_and_name(self, tab, pspec, notebooks): model[path][1] = self._tabinfo.get_tab_name(tab) + # tab switching + + def switch_tab(self, use_mru_order, to_next_tab, time): + window = self.window + current_tab = window.get_active_tab() + + if not current_tab: + return + + notebook = current_tab.get_parent() + stack, model = self._notebooks[notebook] + + tabs = stack if use_mru_order else notebook.get_children() + num_tabs = len(tabs) + + if num_tabs < 2 or current_tab not in tabs: + return + + step = 1 if to_next_tab else -1 + next_tab = tabs[(tabs.index(current_tab) + step) % num_tabs] + + if use_mru_order: + tabwin = self._tabwin + + if not self._is_tabwin_visible: + tabwin.show_all() + + else: + tabwin.present_with_time(time) + + self._is_tabwin_visible = True + + if not self._is_switching: + self._initial_tab = current_tab + + self._is_switching = True + + window.set_active_tab(next_tab) + + def end_switching(self, do_revert=False): + if not self._is_switching: + return + + window = self.window + initial_tab = self._initial_tab + + self._tabwin.hide() + + self._is_switching = False + self._is_tabwin_visible = False + self._initial_tab = None + + if do_revert and initial_tab: + window.set_active_tab(initial_tab) + + else: + tab = window.get_active_tab() + + if tab: + self.on_window_active_tab_changed(window, tab, self._notebooks, self._view) + + # tab window resizing def _schedule_tabwin_resize(self): diff --git a/controlyourtabs/keyinfo.py b/controlyourtabs/keyinfo.py new file mode 100644 index 0000000..519cc02 --- /dev/null +++ b/controlyourtabs/keyinfo.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# +# keyinfo.py +# This file is part of Control Your Tabs, a plugin for gedit +# +# Copyright (C) 2010-2014, 2016-2017 Jeffery To +# https://github.com/jefferyto/gedit-control-your-tabs +# +# 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 3 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, see . + +from gi.repository import Gtk, Gdk + +CONTROL_MASK = Gdk.ModifierType.CONTROL_MASK + +CONTROL_SHIFT_MASK = Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK + +CONTROL_KEY_LIST = [Gdk.KEY_Control_L, Gdk.KEY_Control_R] # will need to iterate through this list + +TAB_KEY_SET = set([Gdk.KEY_ISO_Left_Tab, Gdk.KEY_Tab]) + +PAGE_KEY_SET = set([Gdk.KEY_Page_Up, Gdk.KEY_Page_Down]) + +NEXT_KEY_SET = set([Gdk.KEY_Tab, Gdk.KEY_Page_Down]) + +ESCAPE_KEY = Gdk.KEY_Escape + + +def default_control_held(): + return [False for control_key in CONTROL_KEY_LIST] + +def updated_control_held(event, prev_statuses, new_status): + keyval = event.keyval + + return [ + new_status if keyval == control_key else prev_status + for control_key, prev_status in zip(CONTROL_KEY_LIST, prev_statuses) + ] + +def is_control_keys(event): + keyval = event.keyval + state = event.state & Gtk.accelerator_get_default_mod_mask() + + is_control = state == CONTROL_MASK + is_control_shift = state == CONTROL_SHIFT_MASK + + is_tab = keyval in TAB_KEY_SET + is_page = keyval in PAGE_KEY_SET + is_escape = keyval == ESCAPE_KEY + + is_control_tab = (is_control or is_control_shift) and is_tab + is_control_page = is_control and is_page + is_control_escape = (is_control or is_control_shift) and is_escape + + return (is_control_tab, is_control_page, is_control_escape) + +def is_next_key(event): + return event.keyval in NEXT_KEY_SET + From 6720fa188fd25fceb21b3d9a486ec2bbf8f10aa4 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 18 Oct 2017 05:54:07 +0800 Subject: [PATCH 13/41] removed underscores from tabwin resize methods --- controlyourtabs/__init__.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 3be07c5..df4ebe8 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -156,7 +156,7 @@ def do_deactivate(self): for doc in window.get_documents(): disconnect_handlers(self, Gedit.Tab.get_from_document(doc)) - self._cancel_tabwin_resize() + self.cancel_tabwin_resize() self.end_switching() self._tabwin.destroy() @@ -342,7 +342,7 @@ def on_window_active_tab_changed(self, window, tab, notebooks, view): if view.get_model() is not model: view.set_model(model) - self._schedule_tabwin_resize() + self.schedule_tabwin_resize() for row in model: row[self.SELECTED_TAB_COLUMN] = False @@ -401,19 +401,19 @@ def on_window_focus_out_event(self, window, event, notebooks, view): self.end_switching() def on_window_configure_event(self, window, event, notebooks, view): - self._schedule_tabwin_resize() + self.schedule_tabwin_resize() def on_model_row_inserted(self, model, path, iter, view, sel): if view.get_model() is not model: return - self._schedule_tabwin_resize() + self.schedule_tabwin_resize() def on_model_row_deleted(self, model, path, view, sel): if view.get_model() is not model: return - self._schedule_tabwin_resize() + self.schedule_tabwin_resize() def on_model_row_changed(self, model, path, iter, view, sel): if view.get_model() is not model: @@ -426,7 +426,7 @@ def on_model_row_changed(self, model, path, iter, view, sel): else: sel.unselect_path(path) - self._schedule_tabwin_resize() + self.schedule_tabwin_resize() def on_sync_icon_and_name(self, tab, pspec, notebooks): stack, model = notebooks[tab.get_parent()] @@ -503,7 +503,7 @@ def end_switching(self, do_revert=False): # tab window resizing - def _schedule_tabwin_resize(self): + def schedule_tabwin_resize(self): if self._tabwin_resize_id: return @@ -511,14 +511,14 @@ def _schedule_tabwin_resize(self): # maybe because treeview rendering is async? # this feels like a giant hack try: - resize_id = GLib.idle_add(self._do_tabwin_resize) + resize_id = GLib.idle_add(self.do_tabwin_resize) except TypeError: # gedit 3.0 - resize_id = GObject.idle_add(self._do_tabwin_resize) + resize_id = GObject.idle_add(self.do_tabwin_resize) self._tabwin_resize_id = resize_id - def _cancel_tabwin_resize(self): + def cancel_tabwin_resize(self): if not self._tabwin_resize_id: return @@ -526,7 +526,7 @@ def _cancel_tabwin_resize(self): self._tabwin_resize_id = None - def _do_tabwin_resize(self): + def do_tabwin_resize(self): view = self._view sw = self._sw From d5a6ef55c7e339e2bbec923a79761d37ef18b580 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 18 Oct 2017 07:39:53 +0800 Subject: [PATCH 14/41] extract model manipulation into separate methods, show tabwin after selecting next tab --- controlyourtabs/__init__.py | 282 ++++++++++++++++++++++-------------- 1 file changed, 173 insertions(+), 109 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index df4ebe8..9b85b6f 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -47,8 +47,6 @@ class ControlYourTabsWindowActivatable(GObject.Object, Gedit.WindowActivatable): window = GObject.property(type=Gedit.Window) # lowercase 'p' for gedit < 3.4 - SELECTED_TAB_COLUMN = 3 - MAX_TAB_WINDOW_ROWS = 9 MAX_TAB_WINDOW_HEIGHT_PERCENTAGE = 0.5 @@ -59,7 +57,7 @@ def __init__(self): def do_activate(self): window = self.window - notebooks = {} + models = {} tabwin = Gtk.Window.new(Gtk.WindowType.POPUP) tabwin.set_transient_for(window) @@ -97,8 +95,8 @@ def do_activate(self): col.pack_start(name_cell, True) col.pack_start(space_cell, False) - col.add_attribute(icon_cell, 'pixbuf', 0) - col.add_attribute(name_cell, 'markup', 1) + col.add_attribute(icon_cell, 'pixbuf', 2) + col.add_attribute(name_cell, 'markup', 3) view.append_column(col) @@ -120,7 +118,7 @@ def do_activate(self): self._is_control_held = keyinfo.default_control_held() self._initial_tab = None self._multi = None - self._notebooks = notebooks + self._models = models self._tabwin = tabwin self._view = view self._sw = sw @@ -133,10 +131,10 @@ def do_activate(self): tab = window.get_active_tab() if tab: - self._setup(window, tab, notebooks, view) + self._setup(window, tab, models, view) if self._multi: - self.on_window_active_tab_changed(window, tab, notebooks, view) + self.on_window_active_tab_changed(window, tab, models, view) else: connect_handlers(self, window, ['tab-added'], 'window') @@ -149,9 +147,9 @@ def do_deactivate(self): if self._multi: disconnect_handlers(self, self._multi) - notebooks = self._notebooks - for notebook in notebooks: - disconnect_handlers(self, notebooks[notebook][1]) + models = self._models + for notebook in models: + disconnect_handlers(self, models[notebook]) for doc in window.get_documents(): disconnect_handlers(self, Gedit.Tab.get_from_document(doc)) @@ -166,7 +164,7 @@ def do_deactivate(self): self._is_control_held = None self._initial_tab = None self._multi = None - self._notebooks = None + self._models = None self._tabwin = None self._view = None self._sw = None @@ -185,9 +183,9 @@ def do_update_state(self): def on_window_tab_added(self, window, tab): disconnect_handlers(self, window) - self._setup(window, tab, self._notebooks, self._view) + self._setup(window, tab, self._models, self._view) - def _setup(self, window, tab, notebooks, view): + def _setup(self, window, tab, models, view): icon_size = self._tabinfo.get_tab_icon_size(tab) self._icon_cell.set_fixed_size(icon_size, icon_size) @@ -207,7 +205,7 @@ def _setup(self, window, tab, notebooks, view): for doc in window.get_documents(): notebook = Gedit.Tab.get_from_document(doc).get_parent() - self.on_multi_notebook_notebook_added(multi, notebook, notebooks, view) + self.on_multi_notebook_notebook_added(multi, notebook, models, view) connect_handlers( self, multi, @@ -218,7 +216,7 @@ def _setup(self, window, tab, notebooks, view): 'tab-removed' ], 'multi_notebook', - notebooks, view + models, view ) connect_handlers( self, window, @@ -231,17 +229,17 @@ def _setup(self, window, tab, notebooks, view): 'configure-event' ], 'window', - notebooks, view + models, view ) # signal handlers / main logic - def on_multi_notebook_notebook_added(self, multi, notebook, notebooks, view): - if notebook in notebooks: + def on_multi_notebook_notebook_added(self, multi, notebook, models, view): + if notebook in models: return - model = Gtk.ListStore.new((GdkPixbuf.Pixbuf, str, Gedit.Tab, bool)) + model = self.new_model() connect_handlers( self, model, @@ -254,43 +252,34 @@ def on_multi_notebook_notebook_added(self, multi, notebook, notebooks, view): view, view.get_selection() ) - notebooks[notebook] = ([], model) + models[notebook] = model for tab in notebook.get_children(): - self.on_multi_notebook_tab_added(multi, notebook, tab, notebooks, view) + self.on_multi_notebook_tab_added(multi, notebook, tab, models, view) - def on_multi_notebook_notebook_removed(self, multi, notebook, notebooks, view): - if notebook not in notebooks: + def on_multi_notebook_notebook_removed(self, multi, notebook, models, view): + if notebook not in models: return for tab in notebook.get_children(): - self.on_multi_notebook_tab_removed(multi, notebook, tab, notebooks, view) + self.on_multi_notebook_tab_removed(multi, notebook, tab, models, view) - stack, model = notebooks[notebook] + model = models[notebook] - if view.get_model() is model: + if self.is_active_model(model): view.set_model(None) disconnect_handlers(self, model) - del notebooks[notebook] + del models[notebook] - def on_multi_notebook_tab_added(self, multi, notebook, tab, notebooks, view): - stack, model = notebooks[notebook] + def on_multi_notebook_tab_added(self, multi, notebook, tab, models, view): + model = models[notebook] - if tab in stack: + if self.tab_in_model(model, tab): return - stack.append(tab) - - model.append( - ( - self._tabinfo.get_tab_icon(tab), - self._tabinfo.get_tab_name(tab), - tab, - False - ) - ) + self.append_model_row(model, tab) connect_handlers( self, tab, @@ -299,77 +288,60 @@ def on_multi_notebook_tab_added(self, multi, notebook, tab, notebooks, view): 'notify::state' ], self.on_sync_icon_and_name, - notebooks + models ) - def on_multi_notebook_tab_removed(self, multi, notebook, tab, notebooks, view): + def on_multi_notebook_tab_removed(self, multi, notebook, tab, models, view): if tab == self._initial_tab: self._initial_tab = None - stack, model = notebooks[notebook] + model = models[notebook] - if tab not in stack: + if not self.tab_in_model(model, tab): return disconnect_handlers(self, tab) - model.remove(model.get_iter(stack.index(tab))) + self.remove_model_row(model, tab) - stack.remove(tab) - - def on_window_tabs_reordered(self, window, notebooks, view): + def on_window_tabs_reordered(self, window, models, view): multi = self._multi tab = window.get_active_tab() new_notebook = tab.get_parent() - if tab in notebooks[new_notebook][0]: + if self.tab_in_model(models[new_notebook], tab): return old_notebook = None - for notebook in notebooks: - if tab in notebooks[notebook][0]: + for notebook in models: + if self.tab_in_model(models[notebook], tab): old_notebook = notebook break if old_notebook: - self.on_multi_notebook_tab_removed(multi, old_notebook, tab, notebooks, view) + self.on_multi_notebook_tab_removed(multi, old_notebook, tab, models, view) - self.on_multi_notebook_tab_added(multi, new_notebook, tab, notebooks, view) + self.on_multi_notebook_tab_added(multi, new_notebook, tab, models, view) - def on_window_active_tab_changed(self, window, tab, notebooks, view): - stack, model = notebooks[tab.get_parent()] + def on_window_active_tab_changed(self, window, tab, models, view): + model = models[tab.get_parent()] - if view.get_model() is not model: + if not self.is_active_model(model): view.set_model(model) self.schedule_tabwin_resize() - for row in model: - row[self.SELECTED_TAB_COLUMN] = False + self.unselect_model_rows(model) if not self._is_switching: - if tab in stack: - model.move_after(model.get_iter(stack.index(tab)), None) - stack.remove(tab) - + if self.tab_in_model(model, tab): + self.move_model_row_to_top(model, tab) else: - model.insert( - 0, - ( - self._tabinfo.get_tab_icon(tab), - self._tabinfo.get_tab_name(tab), - tab, - False - ) - ) - - stack.insert(0, tab) - model[0][self.SELECTED_TAB_COLUMN] = True + self.prepend_model_row(model, tab) - else: - model[stack.index(tab)][self.SELECTED_TAB_COLUMN] = True + self.select_model_row(model, tab) - def on_window_key_press_event(self, window, event, notebooks, view): + def on_window_key_press_event(self, window, event, models, view): self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, True) settings = self._settings @@ -391,35 +363,35 @@ def on_window_key_press_event(self, window, event, notebooks, view): return block_event - def on_window_key_release_event(self, window, event, notebooks, view): + def on_window_key_release_event(self, window, event, models, view): self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, False) if not any(self._is_control_held): self.end_switching() - def on_window_focus_out_event(self, window, event, notebooks, view): + def on_window_focus_out_event(self, window, event, models, view): self.end_switching() - def on_window_configure_event(self, window, event, notebooks, view): + def on_window_configure_event(self, window, event, models, view): self.schedule_tabwin_resize() def on_model_row_inserted(self, model, path, iter, view, sel): - if view.get_model() is not model: + if not self.is_active_model(model): return self.schedule_tabwin_resize() def on_model_row_deleted(self, model, path, view, sel): - if view.get_model() is not model: + if not self.is_active_model(model): return self.schedule_tabwin_resize() def on_model_row_changed(self, model, path, iter, view, sel): - if view.get_model() is not model: + if not self.is_active_model(model): return - if model[path][self.SELECTED_TAB_COLUMN]: + if self.is_model_row_selected(model, path): sel.select_path(path) view.scroll_to_cell(path, None, True, 0.5, 0) @@ -428,15 +400,95 @@ def on_model_row_changed(self, model, path, iter, view, sel): self.schedule_tabwin_resize() - def on_sync_icon_and_name(self, tab, pspec, notebooks): - stack, model = notebooks[tab.get_parent()] + def on_sync_icon_and_name(self, tab, pspec, models): + model = models[tab.get_parent()] - if tab not in stack: + if not self.tab_in_model(model, tab): return - path = stack.index(tab) - model[path][0] = self._tabinfo.get_tab_icon(tab) - model[path][1] = self._tabinfo.get_tab_name(tab) + self.update_model_row(model, tab) + + + # treeview model + + def new_model(self): + return Gtk.ListStore.new((Gedit.Tab, bool, GdkPixbuf.Pixbuf, str)) + + def is_active_model(self, model): + return self._view.get_model() is model + + def get_model_tab(self, model, index): + return model[index][0] + + def model_tab_index(self, model, tab_or_index): + if not isinstance(tab_or_index, Gedit.Tab): + return tab_or_index + + index = 0 + + for row in model: + if row[0] == tab_or_index: + break + index = index + 1 + + if index == len(model): + raise ValueError("tab is not in model") + + return index + + def tab_in_model(self, model, tab): + try: + index = self.model_tab_index(model, tab) + except ValueError: + found = False + else: + found = True + + return found + + def new_model_row(self, tab): + tabinfo = self._tabinfo + + return ( + tab, + False, + tabinfo.get_tab_icon(tab), + tabinfo.get_tab_name(tab) + ) + + def append_model_row(self, model, tab): + model.append(self.new_model_row(tab)) + + def prepend_model_row(self, model, tab): + model.prepend(self.new_model_row(tab)) + + def remove_model_row(self, model, tab_or_index): + index = self.model_tab_index(model, tab_or_index) + del model[index] + + def move_model_row_to_top(self, model, tab_or_index): + index = self.model_tab_index(model, tab_or_index) + model.move_after(model.get_iter(index), None) + + def unselect_model_rows(self, model): + for row in model: + row[1] = False + + def select_model_row(self, model, tab_or_index): + index = self.model_tab_index(model, tab_or_index) + model[index][1] = True + + def is_model_row_selected(self, model, tab_or_index): + index = self.model_tab_index(model, tab_or_index) + return model[index][1] + + def update_model_row(self, model, tab_or_index): + tabinfo = self._tabinfo + index = self.model_tab_index(model, tab_or_index) + tab = model[index][0] + + model[index][2] = tabinfo.get_tab_icon(tab) + model[index][3] = tabinfo.get_tab_name(tab) # tab switching @@ -449,27 +501,31 @@ def switch_tab(self, use_mru_order, to_next_tab, time): return notebook = current_tab.get_parent() - stack, model = self._notebooks[notebook] + step = 1 if to_next_tab else -1 - tabs = stack if use_mru_order else notebook.get_children() - num_tabs = len(tabs) + if use_mru_order: + model = self._models[notebook] + num_tabs = len(model) - if num_tabs < 2 or current_tab not in tabs: - return + if num_tabs < 2 or not self.tab_in_model(model, current_tab): + return - step = 1 if to_next_tab else -1 - next_tab = tabs[(tabs.index(current_tab) + step) % num_tabs] + current_index = self.model_tab_index(model, current_tab) + next_index = (current_index + step) % num_tabs - if use_mru_order: - tabwin = self._tabwin + next_tab = self.get_model_tab(model, next_index) - if not self._is_tabwin_visible: - tabwin.show_all() + else: + tabs = notebook.get_children() + num_tabs = len(tabs) - else: - tabwin.present_with_time(time) + if num_tabs < 2: + return - self._is_tabwin_visible = True + current_index = tabs.index(current_tab) + next_index = (current_index + step) % num_tabs + + next_tab = tabs[next_index] if not self._is_switching: self._initial_tab = current_tab @@ -478,6 +534,17 @@ def switch_tab(self, use_mru_order, to_next_tab, time): window.set_active_tab(next_tab) + if use_mru_order: + tabwin = self._tabwin + + if not self._is_tabwin_visible: + tabwin.show_all() + + else: + tabwin.present_with_time(time) + + self._is_tabwin_visible = True + def end_switching(self, do_revert=False): if not self._is_switching: return @@ -498,7 +565,7 @@ def end_switching(self, do_revert=False): tab = window.get_active_tab() if tab: - self.on_window_active_tab_changed(window, tab, self._notebooks, self._view) + self.on_window_active_tab_changed(window, tab, self._models, self._view) # tab window resizing @@ -512,7 +579,6 @@ def schedule_tabwin_resize(self): # this feels like a giant hack try: resize_id = GLib.idle_add(self.do_tabwin_resize) - except TypeError: # gedit 3.0 resize_id = GObject.idle_add(self.do_tabwin_resize) @@ -626,7 +692,6 @@ def get_settings(): except AttributeError: # gedit < 3.4 try: Gedit.debug_plugin_message("relocatable schemas not supported") - except AttributeError: # gedit < 3.4 pass @@ -635,7 +700,6 @@ def get_settings(): except: try: Gedit.debug_plugin_message("could not load settings schema source from %s", schemas_path) - except AttributeError: # gedit < 3.4 pass From 7425c774b1727c9dcf151363a78a0e2eee1e8bd5 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 18 Oct 2017 07:42:41 +0800 Subject: [PATCH 15/41] renamed setup signal handler --- controlyourtabs/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 9b85b6f..4e1af51 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -137,7 +137,7 @@ def do_activate(self): self.on_window_active_tab_changed(window, tab, models, view) else: - connect_handlers(self, window, ['tab-added'], 'window') + connect_handlers(self, window, ['tab-added'], 'setup') def do_deactivate(self): window = self.window @@ -180,7 +180,7 @@ def do_update_state(self): # plugin setup - def on_window_tab_added(self, window, tab): + def on_setup_tab_added(self, window, tab): disconnect_handlers(self, window) self._setup(window, tab, self._models, self._view) From 5c0185de5618bed3bbe2c96f4391436281c9ae05 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 18 Oct 2017 18:43:26 +0800 Subject: [PATCH 16/41] don't change the default textdomain --- controlyourtabs/__init__.py | 3 +-- controlyourtabs/tabinfo.py | 3 +-- controlyourtabs/tabinfo_pre312.py | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 4e1af51..7cbc7a0 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -35,8 +35,7 @@ try: import gettext gettext.bindtextdomain('gedit-control-your-tabs', LOCALE_PATH) - gettext.textdomain('gedit-control-your-tabs') - _ = gettext.gettext + _ = lambda s: gettext.dgettext('gedit-control-your-tabs', s) except: _ = lambda s: s diff --git a/controlyourtabs/tabinfo.py b/controlyourtabs/tabinfo.py index 3b93a30..74f9fb7 100644 --- a/controlyourtabs/tabinfo.py +++ b/controlyourtabs/tabinfo.py @@ -29,8 +29,7 @@ try: import gettext gettext.bindtextdomain('gedit-control-your-tabs', LOCALE_PATH) - gettext.textdomain('gedit-control-your-tabs') - _ = gettext.gettext + _ = lambda s: gettext.dgettext('gedit-control-your-tabs', s) except: _ = lambda s: s diff --git a/controlyourtabs/tabinfo_pre312.py b/controlyourtabs/tabinfo_pre312.py index 59cde3b..db1480a 100644 --- a/controlyourtabs/tabinfo_pre312.py +++ b/controlyourtabs/tabinfo_pre312.py @@ -29,8 +29,7 @@ try: import gettext gettext.bindtextdomain('gedit-control-your-tabs', LOCALE_PATH) - gettext.textdomain('gedit-control-your-tabs') - _ = gettext.gettext + _ = lambda s: gettext.dgettext('gedit-control-your-tabs', s) except: _ = lambda s: s From 14eca80de335ae59b5af90fd4b4c967d6cf78311 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 18 Oct 2017 18:48:53 +0800 Subject: [PATCH 17/41] changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index a49bcc1..073e508 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ * Added Ctrl-Esc to cancel tab switching, causes the initial tab (before switching began) to be active again * Prevent all input during tab switching instead of cancelling switching +* Fixed (potentially) affecting the translations of other plugins ## v0.3.1 (2017-10-17) * Show a debug message if the settings schema could not be loaded From 9d7c350a5d8ba9a833545eb0c6c74a35a03f5dbd Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 18 Oct 2017 22:29:38 +0800 Subject: [PATCH 18/41] moved more code out of signal handlers --- controlyourtabs/__init__.py | 256 ++++++++++++++++++++++-------------- 1 file changed, 158 insertions(+), 98 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 7cbc7a0..6b5defb 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -130,28 +130,25 @@ def do_activate(self): tab = window.get_active_tab() if tab: - self._setup(window, tab, models, view) + self._setup(window, tab, models) if self._multi: - self.on_window_active_tab_changed(window, tab, models, view) + self.active_tab_changed(tab, models) else: - connect_handlers(self, window, ['tab-added'], 'setup') + connect_handlers(self, window, ['tab-added'], 'setup', models) def do_deactivate(self): - window = self.window - - disconnect_handlers(self, window) + multi = self._multi + models = self._models - if self._multi: - disconnect_handlers(self, self._multi) + for notebook in list(models.keys()): + self.untrack_notebook(notebook, models) - models = self._models - for notebook in models: - disconnect_handlers(self, models[notebook]) + if multi: + disconnect_handlers(self, multi) - for doc in window.get_documents(): - disconnect_handlers(self, Gedit.Tab.get_from_document(doc)) + disconnect_handlers(self, self.window) self.cancel_tabwin_resize() self.end_switching() @@ -179,12 +176,12 @@ def do_update_state(self): # plugin setup - def on_setup_tab_added(self, window, tab): + def on_setup_tab_added(self, window, tab, models): disconnect_handlers(self, window) - self._setup(window, tab, self._models, self._view) + self._setup(window, tab, models) - def _setup(self, window, tab, models, view): + def _setup(self, window, tab, models): icon_size = self._tabinfo.get_tab_icon_size(tab) self._icon_cell.set_fixed_size(icon_size, icon_size) @@ -200,12 +197,6 @@ def _setup(self, window, tab, models, view): return - self._multi = multi - - for doc in window.get_documents(): - notebook = Gedit.Tab.get_from_document(doc).get_parent() - self.on_multi_notebook_notebook_added(multi, notebook, models, view) - connect_handlers( self, multi, [ @@ -215,7 +206,7 @@ def _setup(self, window, tab, models, view): 'tab-removed' ], 'multi_notebook', - models, view + models ) connect_handlers( self, window, @@ -228,13 +219,19 @@ def _setup(self, window, tab, models, view): 'configure-event' ], 'window', - models, view + models ) + self._multi = multi + + for document in window.get_documents(): + notebook = Gedit.Tab.get_from_document(document).get_parent() + self.track_notebook(notebook, models) - # signal handlers / main logic - def on_multi_notebook_notebook_added(self, multi, notebook, models, view): + # tracking notebooks / tabs + + def track_notebook(self, notebook, models): if notebook in models: return @@ -246,33 +243,35 @@ def on_multi_notebook_notebook_added(self, multi, notebook, models, view): 'row-inserted', 'row-deleted', 'row-changed' + # the only time we reorder rows is when the active tab changes + # when we do that, we unselect all rows first, then select the active row + # so we don't need to listen to rows-reordered ], - 'model', - view, view.get_selection() + 'model' ) models[notebook] = model for tab in notebook.get_children(): - self.on_multi_notebook_tab_added(multi, notebook, tab, models, view) + self.track_tab(notebook, tab, models) - def on_multi_notebook_notebook_removed(self, multi, notebook, models, view): + def untrack_notebook(self, notebook, models): if notebook not in models: return for tab in notebook.get_children(): - self.on_multi_notebook_tab_removed(multi, notebook, tab, models, view) + self.untrack_tab(notebook, tab, models) model = models[notebook] - if self.is_active_model(model): - view.set_model(None) + if self.is_active_view_model(model): + self.set_active_view_model(None) disconnect_handlers(self, model) del models[notebook] - def on_multi_notebook_tab_added(self, multi, notebook, tab, models, view): + def track_tab(self, notebook, tab, models): model = models[notebook] if self.tab_in_model(model, tab): @@ -290,7 +289,7 @@ def on_multi_notebook_tab_added(self, multi, notebook, tab, models, view): models ) - def on_multi_notebook_tab_removed(self, multi, notebook, tab, models, view): + def untrack_tab(self, notebook, tab, models): if tab == self._initial_tab: self._initial_tab = None @@ -303,7 +302,42 @@ def on_multi_notebook_tab_removed(self, multi, notebook, tab, models, view): self.remove_model_row(model, tab) - def on_window_tabs_reordered(self, window, models, view): + def active_tab_changed(self, tab, models): + model = models[tab.get_parent()] + + self.unselect_model_rows(model) + + if not self._is_switching: + if self.tab_in_model(model, tab): + self.move_model_row_to_top(model, tab) + else: + self.prepend_model_row(model, tab) + + self.select_model_row(model, tab) + + if not self.is_active_view_model(model): + self.set_active_view_model(model) + self.schedule_tabwin_resize() + + + # signal handlers + + def on_multi_notebook_notebook_added(self, multi, notebook, models): + self.track_notebook(notebook, models) + + def on_multi_notebook_notebook_removed(self, multi, notebook, models): + self.untrack_notebook(notebook, models) + + def on_multi_notebook_tab_added(self, multi, notebook, tab, models): + self.track_tab(notebook, tab, models) + + def on_multi_notebook_tab_removed(self, multi, notebook, tab, models): + self.untrack_tab(notebook, tab, models) + + def on_window_tabs_reordered(self, window, models): + # XXX reordering tabs doesn't necessarily involve the active tab + # and reordering only happens within a notebook, not across notebooks + # ... multi = self._multi tab = window.get_active_tab() new_notebook = tab.get_parent() @@ -319,103 +353,99 @@ def on_window_tabs_reordered(self, window, models, view): break if old_notebook: - self.on_multi_notebook_tab_removed(multi, old_notebook, tab, models, view) + self.untrack_tab(old_notebook, tab, models) - self.on_multi_notebook_tab_added(multi, new_notebook, tab, models, view) - - def on_window_active_tab_changed(self, window, tab, models, view): - model = models[tab.get_parent()] - - if not self.is_active_model(model): - view.set_model(model) - self.schedule_tabwin_resize() - - self.unselect_model_rows(model) - - if not self._is_switching: - if self.tab_in_model(model, tab): - self.move_model_row_to_top(model, tab) - else: - self.prepend_model_row(model, tab) + self.track_tab(new_notebook, tab, models) - self.select_model_row(model, tab) + def on_window_active_tab_changed(self, window, tab, models): + self.active_tab_changed(tab, models) - def on_window_key_press_event(self, window, event, models, view): + def on_window_key_press_event(self, window, event, models): self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, True) - settings = self._settings - is_control_tab, is_control_page, is_control_escape = keyinfo.is_control_keys(event) - block_event = True - - if is_control_tab and settings and settings['use-tabbar-order']: - is_control_tab = False - is_control_page = True - - if self._is_switching and is_control_escape: - self.end_switching(True) - - elif is_control_tab or is_control_page: - self.switch_tab(is_control_tab, keyinfo.is_next_key(event), event.time) + return self.key_press_event(event) - else: - block_event = self._is_switching - - return block_event - - def on_window_key_release_event(self, window, event, models, view): + def on_window_key_release_event(self, window, event, models): self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, False) if not any(self._is_control_held): self.end_switching() - def on_window_focus_out_event(self, window, event, models, view): + def on_window_focus_out_event(self, window, event, models): self.end_switching() - def on_window_configure_event(self, window, event, models, view): + def on_window_configure_event(self, window, event, models): self.schedule_tabwin_resize() - def on_model_row_inserted(self, model, path, iter, view, sel): - if not self.is_active_model(model): + def on_sync_icon_and_name(self, tab, pspec, models): + model = models[tab.get_parent()] + + if not self.tab_in_model(model, tab): return - self.schedule_tabwin_resize() + self.update_model_row(model, tab) - def on_model_row_deleted(self, model, path, view, sel): - if not self.is_active_model(model): + def on_model_row_inserted(self, model, path, iter): + if not self.is_active_view_model(model): return + # rows that are inserted are always not selected self.schedule_tabwin_resize() - def on_model_row_changed(self, model, path, iter, view, sel): - if not self.is_active_model(model): + def on_model_row_deleted(self, model, path): + if not self.is_active_view_model(model): return - if self.is_model_row_selected(model, path): - sel.select_path(path) - view.scroll_to_cell(path, None, True, 0.5, 0) + self.update_view_selection() + self.schedule_tabwin_resize() - else: - sel.unselect_path(path) + def on_model_row_changed(self, model, path, iter): + if not self.is_active_view_model(model): + return + + selected = self.is_model_row_selected(model, path) + self.set_view_selection(path, selected) self.schedule_tabwin_resize() - def on_sync_icon_and_name(self, tab, pspec, models): - model = models[tab.get_parent()] - if not self.tab_in_model(model, tab): - return + # tree view - self.update_model_row(model, tab) + def is_active_view_model(self, model): + return self._view.get_model() is model + + def set_active_view_model(self, model): + self._view.set_model(model) + + self.update_view_selection() + + def update_view_selection(self): + model = self._view.get_model() + + index = self.get_model_row_selected(model) if model else -1 + + self._view.get_selection().unselect_all() + + if index > 0: + self.set_view_selection(index, True) + + def set_view_selection(self, index, selected): + view = self._view + selection = view.get_selection() + + if selected: + selection.select_path(index) + view.scroll_to_cell(index, None, True, 0.5, 0) + + else: + selection.unselect_path(index) - # treeview model + # tree model def new_model(self): return Gtk.ListStore.new((Gedit.Tab, bool, GdkPixbuf.Pixbuf, str)) - def is_active_model(self, model): - return self._view.get_model() is model - def get_model_tab(self, model, index): return model[index][0] @@ -481,6 +511,16 @@ def is_model_row_selected(self, model, tab_or_index): index = self.model_tab_index(model, tab_or_index) return model[index][1] + def get_model_row_selected(self, model): + index = 0 + + for row in model: + if row[1]: + break + index = index + 1 + + return index if index < len(model) else -1 + def update_model_row(self, model, tab_or_index): tabinfo = self._tabinfo index = self.model_tab_index(model, tab_or_index) @@ -492,6 +532,26 @@ def update_model_row(self, model, tab_or_index): # tab switching + def key_press_event(self, event): + settings = self._settings + is_control_tab, is_control_page, is_control_escape = keyinfo.is_control_keys(event) + block_event = True + + if is_control_tab and settings and settings['use-tabbar-order']: + is_control_tab = False + is_control_page = True + + if self._is_switching and is_control_escape: + self.end_switching(True) + + elif is_control_tab or is_control_page: + self.switch_tab(is_control_tab, keyinfo.is_next_key(event), event.time) + + else: + block_event = self._is_switching + + return block_event + def switch_tab(self, use_mru_order, to_next_tab, time): window = self.window current_tab = window.get_active_tab() @@ -564,7 +624,7 @@ def end_switching(self, do_revert=False): tab = window.get_active_tab() if tab: - self.on_window_active_tab_changed(window, tab, self._models, self._view) + self.active_tab_changed(tab, self._models) # tab window resizing From f41d9ff9b67d115255908443dd1f53e37a759424 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 03:58:14 +0800 Subject: [PATCH 19/41] moved model and model methods into a custom object --- controlyourtabs/__init__.py | 494 +++++++++++++++++++----------------- 1 file changed, 265 insertions(+), 229 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 6b5defb..2301ec3 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -25,6 +25,7 @@ import math import os.path +from functools import wraps from gi.repository import GObject, GLib, Gtk, Gdk, GdkPixbuf, Gio, Gedit, PeasGtk from .utils import connect_handlers, disconnect_handlers from . import keyinfo, tabinfo, tabinfo_pre312 @@ -56,7 +57,7 @@ def __init__(self): def do_activate(self): window = self.window - models = {} + tab_models = {} tabwin = Gtk.Window.new(Gtk.WindowType.POPUP) tabwin.set_transient_for(window) @@ -94,8 +95,8 @@ def do_activate(self): col.pack_start(name_cell, True) col.pack_start(space_cell, False) - col.add_attribute(icon_cell, 'pixbuf', 2) - col.add_attribute(name_cell, 'markup', 3) + col.add_attribute(icon_cell, 'pixbuf', 0) + col.add_attribute(name_cell, 'markup', 1) view.append_column(col) @@ -117,7 +118,7 @@ def do_activate(self): self._is_control_held = keyinfo.default_control_held() self._initial_tab = None self._multi = None - self._models = models + self._tab_models = tab_models self._tabwin = tabwin self._view = view self._sw = sw @@ -130,20 +131,20 @@ def do_activate(self): tab = window.get_active_tab() if tab: - self._setup(window, tab, models) + self._setup(window, tab, tab_models) if self._multi: - self.active_tab_changed(tab, models) + self.active_tab_changed(tab, tab_models[tab.get_parent()]) else: - connect_handlers(self, window, ['tab-added'], 'setup', models) + connect_handlers(self, window, ['tab-added'], 'setup', tab_models) def do_deactivate(self): multi = self._multi - models = self._models + tab_models = self._tab_models - for notebook in list(models.keys()): - self.untrack_notebook(notebook, models) + for notebook in list(tab_models.keys()): + self.untrack_notebook(notebook, tab_models) if multi: disconnect_handlers(self, multi) @@ -160,7 +161,7 @@ def do_deactivate(self): self._is_control_held = None self._initial_tab = None self._multi = None - self._models = None + self._tab_models = None self._tabwin = None self._view = None self._sw = None @@ -176,12 +177,12 @@ def do_update_state(self): # plugin setup - def on_setup_tab_added(self, window, tab, models): + def on_setup_tab_added(self, window, tab, tab_models): disconnect_handlers(self, window) - self._setup(window, tab, models) + self._setup(window, tab, tab_models) - def _setup(self, window, tab, models): + def _setup(self, window, tab, tab_models): icon_size = self._tabinfo.get_tab_icon_size(tab) self._icon_cell.set_fixed_size(icon_size, icon_size) @@ -206,7 +207,7 @@ def _setup(self, window, tab, models): 'tab-removed' ], 'multi_notebook', - models + tab_models ) connect_handlers( self, window, @@ -219,65 +220,67 @@ def _setup(self, window, tab, models): 'configure-event' ], 'window', - models + tab_models ) self._multi = multi for document in window.get_documents(): notebook = Gedit.Tab.get_from_document(document).get_parent() - self.track_notebook(notebook, models) + self.track_notebook(notebook, tab_models) # tracking notebooks / tabs - def track_notebook(self, notebook, models): - if notebook in models: + def track_notebook(self, notebook, tab_models): + if notebook in tab_models: return - model = self.new_model() + tab_model = ControlYourTabsTabModel(self._tabinfo) connect_handlers( - self, model, + self, tab_model, [ 'row-inserted', 'row-deleted', 'row-changed' - # the only time we reorder rows is when the active tab changes - # when we do that, we unselect all rows first, then select the active row - # so we don't need to listen to rows-reordered ], - 'model' + self.on_tab_model_row_changed + ) + connect_handlers( + self, tab_model, + [ + 'selected-path-changed' + ], + 'tab_model' ) - models[notebook] = model + tab_models[notebook] = tab_model for tab in notebook.get_children(): - self.track_tab(notebook, tab, models) + self.track_tab(tab, tab_model) - def untrack_notebook(self, notebook, models): - if notebook not in models: + def untrack_notebook(self, notebook, tab_models): + if notebook not in tab_models: return - for tab in notebook.get_children(): - self.untrack_tab(notebook, tab, models) + tab_model = tab_models[notebook] - model = models[notebook] + for tab in notebook.get_children(): + self.untrack_tab(tab, tab_model) - if self.is_active_view_model(model): + if self.is_active_view_model(tab_model): self.set_active_view_model(None) - disconnect_handlers(self, model) - - del models[notebook] + disconnect_handlers(self, tab_model) - def track_tab(self, notebook, tab, models): - model = models[notebook] + del tab_models[notebook] - if self.tab_in_model(model, tab): + def track_tab(self, tab, tab_model): + if tab in tab_model: return - self.append_model_row(model, tab) + tab_model.append(tab) connect_handlers( self, tab, @@ -285,56 +288,51 @@ def track_tab(self, notebook, tab, models): 'notify::name', 'notify::state' ], - self.on_sync_icon_and_name, - models + self.on_tab_notify_name_state, + tab_model ) - def untrack_tab(self, notebook, tab, models): + def untrack_tab(self, tab, tab_model): if tab == self._initial_tab: self._initial_tab = None - model = models[notebook] - - if not self.tab_in_model(model, tab): + if tab not in tab_model: return disconnect_handlers(self, tab) - self.remove_model_row(model, tab) - - def active_tab_changed(self, tab, models): - model = models[tab.get_parent()] - - self.unselect_model_rows(model) + tab_model.remove(tab) + def active_tab_changed(self, tab, tab_model): if not self._is_switching: - if self.tab_in_model(model, tab): - self.move_model_row_to_top(model, tab) - else: - self.prepend_model_row(model, tab) + if tab not in tab_model: + # can this happen? + self.track_tab(tab, tab_model) - self.select_model_row(model, tab) + tab_model.move_after(tab) - if not self.is_active_view_model(model): - self.set_active_view_model(model) + tab_model.select(tab) + + if not self.is_active_view_model(tab_model): + self.set_active_view_model(tab_model) self.schedule_tabwin_resize() # signal handlers - def on_multi_notebook_notebook_added(self, multi, notebook, models): - self.track_notebook(notebook, models) + def on_multi_notebook_notebook_added(self, multi, notebook, tab_models): + self.track_notebook(notebook, tab_models) - def on_multi_notebook_notebook_removed(self, multi, notebook, models): - self.untrack_notebook(notebook, models) + def on_multi_notebook_notebook_removed(self, multi, notebook, tab_models): + self.untrack_notebook(notebook, tab_models) - def on_multi_notebook_tab_added(self, multi, notebook, tab, models): - self.track_tab(notebook, tab, models) + def on_multi_notebook_tab_added(self, multi, notebook, tab, tab_models): + self.track_tab(tab, tab_models[notebook]) - def on_multi_notebook_tab_removed(self, multi, notebook, tab, models): - self.untrack_tab(notebook, tab, models) + def on_multi_notebook_tab_removed(self, multi, notebook, tab, tab_models): + self.untrack_tab(tab, tab_models[notebook]) - def on_window_tabs_reordered(self, window, models): + def on_window_tabs_reordered(self, window, tab_models): # XXX reordering tabs doesn't necessarily involve the active tab # and reordering only happens within a notebook, not across notebooks # ... @@ -342,192 +340,90 @@ def on_window_tabs_reordered(self, window, models): tab = window.get_active_tab() new_notebook = tab.get_parent() - if self.tab_in_model(models[new_notebook], tab): + tab_model = tab_models[new_notebook] + + if tab in tab_model: return old_notebook = None - for notebook in models: - if self.tab_in_model(models[notebook], tab): + for notebook in tab_models: + tab_model = tab_models[notebook] + if tab in tab_model: old_notebook = notebook break if old_notebook: - self.untrack_tab(old_notebook, tab, models) + self.untrack_tab(tab, tab_models[old_notebook]) - self.track_tab(new_notebook, tab, models) + self.track_tab(tab, tab_models[new_notebook]) - def on_window_active_tab_changed(self, window, tab, models): - self.active_tab_changed(tab, models) + def on_window_active_tab_changed(self, window, tab, tab_models): + self.active_tab_changed(tab, tab_models[tab.get_parent()]) - def on_window_key_press_event(self, window, event, models): + def on_window_key_press_event(self, window, event, tab_models): self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, True) return self.key_press_event(event) - def on_window_key_release_event(self, window, event, models): + def on_window_key_release_event(self, window, event, tab_models): self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, False) if not any(self._is_control_held): self.end_switching() - def on_window_focus_out_event(self, window, event, models): + def on_window_focus_out_event(self, window, event, tab_models): self.end_switching() - def on_window_configure_event(self, window, event, models): + def on_window_configure_event(self, window, event, tab_models): self.schedule_tabwin_resize() - def on_sync_icon_and_name(self, tab, pspec, models): - model = models[tab.get_parent()] - - if not self.tab_in_model(model, tab): + def on_tab_notify_name_state(self, tab, pspec, tab_model): + if tab not in tab_model: return - self.update_model_row(model, tab) + tab_model.update(tab) - def on_model_row_inserted(self, model, path, iter): - if not self.is_active_view_model(model): + def on_tab_model_row_changed(self, tab_model, path): + if not self.is_active_view_model(tab_model): return - # rows that are inserted are always not selected self.schedule_tabwin_resize() - def on_model_row_deleted(self, model, path): - if not self.is_active_view_model(model): - return - - self.update_view_selection() - self.schedule_tabwin_resize() - - def on_model_row_changed(self, model, path, iter): - if not self.is_active_view_model(model): + def on_tab_model_selected_path_changed(self, tab_model, path): + if not self.is_active_view_model(tab_model): return - selected = self.is_model_row_selected(model, path) - self.set_view_selection(path, selected) - - self.schedule_tabwin_resize() + self.set_view_selection(path) # tree view - def is_active_view_model(self, model): + def is_active_view_model(self, tab_model): + model = tab_model.model if tab_model else None return self._view.get_model() is model - def set_active_view_model(self, model): - self._view.set_model(model) + def set_active_view_model(self, tab_model): + model = None + selected_path = None - self.update_view_selection() + if tab_model: + model = tab_model.model + selected_path = tab_model.get_selected_path() - def update_view_selection(self): - model = self._view.get_model() - - index = self.get_model_row_selected(model) if model else -1 - - self._view.get_selection().unselect_all() - - if index > 0: - self.set_view_selection(index, True) + self._view.set_model(model) + self.set_view_selection(selected_path) - def set_view_selection(self, index, selected): + def set_view_selection(self, path): view = self._view selection = view.get_selection() - if selected: - selection.select_path(index) - view.scroll_to_cell(index, None, True, 0.5, 0) - - else: - selection.unselect_path(index) - + if path: + selection.select_path(path) + view.scroll_to_cell(path, None, True, 0.5, 0) - # tree model - - def new_model(self): - return Gtk.ListStore.new((Gedit.Tab, bool, GdkPixbuf.Pixbuf, str)) - - def get_model_tab(self, model, index): - return model[index][0] - - def model_tab_index(self, model, tab_or_index): - if not isinstance(tab_or_index, Gedit.Tab): - return tab_or_index - - index = 0 - - for row in model: - if row[0] == tab_or_index: - break - index = index + 1 - - if index == len(model): - raise ValueError("tab is not in model") - - return index - - def tab_in_model(self, model, tab): - try: - index = self.model_tab_index(model, tab) - except ValueError: - found = False else: - found = True - - return found - - def new_model_row(self, tab): - tabinfo = self._tabinfo - - return ( - tab, - False, - tabinfo.get_tab_icon(tab), - tabinfo.get_tab_name(tab) - ) - - def append_model_row(self, model, tab): - model.append(self.new_model_row(tab)) - - def prepend_model_row(self, model, tab): - model.prepend(self.new_model_row(tab)) - - def remove_model_row(self, model, tab_or_index): - index = self.model_tab_index(model, tab_or_index) - del model[index] - - def move_model_row_to_top(self, model, tab_or_index): - index = self.model_tab_index(model, tab_or_index) - model.move_after(model.get_iter(index), None) - - def unselect_model_rows(self, model): - for row in model: - row[1] = False - - def select_model_row(self, model, tab_or_index): - index = self.model_tab_index(model, tab_or_index) - model[index][1] = True - - def is_model_row_selected(self, model, tab_or_index): - index = self.model_tab_index(model, tab_or_index) - return model[index][1] - - def get_model_row_selected(self, model): - index = 0 - - for row in model: - if row[1]: - break - index = index + 1 - - return index if index < len(model) else -1 - - def update_model_row(self, model, tab_or_index): - tabinfo = self._tabinfo - index = self.model_tab_index(model, tab_or_index) - tab = model[index][0] - - model[index][2] = tabinfo.get_tab_icon(tab) - model[index][3] = tabinfo.get_tab_name(tab) + selection.unselect_all() # tab switching @@ -560,31 +456,18 @@ def switch_tab(self, use_mru_order, to_next_tab, time): return notebook = current_tab.get_parent() - step = 1 if to_next_tab else -1 - - if use_mru_order: - model = self._models[notebook] - num_tabs = len(model) - if num_tabs < 2 or not self.tab_in_model(model, current_tab): - return + tabs = self._tab_models[notebook] if use_mru_order else notebook.get_children() + num_tabs = len(tabs) - current_index = self.model_tab_index(model, current_tab) - next_index = (current_index + step) % num_tabs - - next_tab = self.get_model_tab(model, next_index) - - else: - tabs = notebook.get_children() - num_tabs = len(tabs) - - if num_tabs < 2: - return + if num_tabs < 2 or current_tab not in tabs: + return - current_index = tabs.index(current_tab) - next_index = (current_index + step) % num_tabs + current_index = tabs.index(current_tab) + step = 1 if to_next_tab else -1 + next_index = (current_index + step) % num_tabs - next_tab = tabs[next_index] + next_tab = tabs[next_index] if not self._is_switching: self._initial_tab = current_tab @@ -624,7 +507,7 @@ def end_switching(self, do_revert=False): tab = window.get_active_tab() if tab: - self.active_tab_changed(tab, self._models) + self.active_tab_changed(tab, self._tab_models[tab.get_parent()]) # tab window resizing @@ -691,6 +574,159 @@ def do_tabwin_resize(self): return False +class ControlYourTabsTabModel(GObject.Object): + + __gtype_name__ = 'ControlYourTabsTabModel' + + __gsignals__ = { # gedit < 3.6 + 'row-inserted': (GObject.SignalFlags.RUN_FIRST, None, (Gtk.TreePath,)), + 'row-deleted': (GObject.SignalFlags.RUN_FIRST, None, (Gtk.TreePath,)), + 'row-changed': (GObject.SignalFlags.RUN_FIRST, None, (Gtk.TreePath,)), + 'rows-reordered': (GObject.SignalFlags.RUN_FIRST, None, ()), + 'selected-path-changed': (GObject.SignalFlags.RUN_FIRST, None, (Gtk.TreePath,)) + } + + + def _model_modifier(fn): + @wraps(fn) + def wrapper(self, *args, **kwargs): + prev_path = self.get_selected_path() + + result = fn(self, *args, **kwargs) + + cur_path = self.get_selected_path() + + if cur_path != prev_path: + self.emit('selected-path-changed', cur_path) + + return result + + return wrapper + + + def __init__(self, tabinfo): + GObject.Object.__init__(self) + + self._model = Gtk.ListStore.new((GdkPixbuf.Pixbuf, str, Gedit.Tab)) + self._references = {} + self._selected = None + self._tabinfo = tabinfo + + connect_handlers( + self, self._model, + [ + 'row-inserted', + 'row-deleted', + 'row-changed', + 'rows-reordered' + ], + 'model' + ) + + def __len__(self): + return len(self._model) + + def __getitem__(self, key): + return self._model[key][2] + + @_model_modifier + def __delitem__(self, key): + tab = self._model[key][2] + + if self._selected == tab: + self._selected = None + + del self._references[tab] + del self._model[key] + + def __iter__(self): + return [row[0] for row in self._model] + + def __contains__(self, item): + return item in self._references + + @property + def model(self): + return self._model + + def on_model_row_inserted(self, model, path, iter_): + self.emit('row-inserted', path) + + def on_model_row_deleted(self, model, path): + self.emit('row-deleted', path) + + def on_model_row_changed(self, model, path, iter_): + self.emit('row-changed', path) + + def on_model_rows_reordered(self, model, path, iter_, new_order): + self.emit('rows-reordered') + + @_model_modifier + def insert(self, position, tab): + tab_iter = self._model.insert( + position, + ( + self._tabinfo.get_tab_icon(tab), + self._tabinfo.get_tab_name(tab), + tab + ) + ) + self._references[tab] = Gtk.TreeRowReference.new(self._model, self._model.get_path(tab_iter)) + + def append(self, tab): + self.insert(-1, tab) + + def prepend(self, tab): + self.insert(0, tab) + + def remove(self, tab): + del self[self.get_path(tab)] + + @_model_modifier + def move(self, tab, sibling, move_before): + tab_iter = self._get_iter(tab) + sibling_iter = self._get_iter(sibling) if sibling else None + + if move_before: + self._model.move_before(tab_iter, sibling_iter) + else: + self._model.move_after(tab_iter, sibling_iter) + + def move_before(self, tab, sibling=None): + self.move(tab, sibling, True) + + def move_after(self, tab, sibling=None): + self.move(tab, sibling, False) + + def get_path(self, tab): + return self._references[tab].get_path() + + def index(self, tab): + return int(self.get_path(tab).to_string()) + + def _get_iter(self, tab): + return self._model.get_iter(self.get_path(tab)) + + @_model_modifier + def select(self, tab): + self._selected = tab + + def unselect(self): + self.select(None) + + def get_selected(self): + return self._selected + + def get_selected_path(self): + return self.get_path(self._selected) if self._selected else None + + def update(self, tab): + path = self.get_path(tab) + + self._model[path][0] = self._tabinfo.get_tab_icon(tab) + self._model[path][1] = self._tabinfo.get_tab_name(tab) + + class ControlYourTabsConfigurable(GObject.Object, PeasGtk.Configurable): __gtype_name__ = 'ControlYourTabsConfigurable' From b06038d384379bca4a0722e7da1adbac652c3641 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 03:59:06 +0800 Subject: [PATCH 20/41] removed underscore from setup --- controlyourtabs/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 2301ec3..d6c92ab 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -131,7 +131,7 @@ def do_activate(self): tab = window.get_active_tab() if tab: - self._setup(window, tab, tab_models) + self.setup(window, tab, tab_models) if self._multi: self.active_tab_changed(tab, tab_models[tab.get_parent()]) @@ -180,9 +180,9 @@ def do_update_state(self): def on_setup_tab_added(self, window, tab, tab_models): disconnect_handlers(self, window) - self._setup(window, tab, tab_models) + self.setup(window, tab, tab_models) - def _setup(self, window, tab, tab_models): + def setup(self, window, tab, tab_models): icon_size = self._tabinfo.get_tab_icon_size(tab) self._icon_cell.set_fixed_size(icon_size, icon_size) From 59dff91816dffb3acea876fa0f84aac52b40415a Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 04:01:19 +0800 Subject: [PATCH 21/41] tweaked function name --- controlyourtabs/__init__.py | 4 ++-- controlyourtabs/keyinfo.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index d6c92ab..51c5f21 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -362,12 +362,12 @@ def on_window_active_tab_changed(self, window, tab, tab_models): self.active_tab_changed(tab, tab_models[tab.get_parent()]) def on_window_key_press_event(self, window, event, tab_models): - self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, True) + self._is_control_held = keyinfo.update_control_held(event, self._is_control_held, True) return self.key_press_event(event) def on_window_key_release_event(self, window, event, tab_models): - self._is_control_held = keyinfo.updated_control_held(event, self._is_control_held, False) + self._is_control_held = keyinfo.update_control_held(event, self._is_control_held, False) if not any(self._is_control_held): self.end_switching() diff --git a/controlyourtabs/keyinfo.py b/controlyourtabs/keyinfo.py index 519cc02..76ef77e 100644 --- a/controlyourtabs/keyinfo.py +++ b/controlyourtabs/keyinfo.py @@ -39,7 +39,7 @@ def default_control_held(): return [False for control_key in CONTROL_KEY_LIST] -def updated_control_held(event, prev_statuses, new_status): +def update_control_held(event, prev_statuses, new_status): keyval = event.keyval return [ From 0eb65f3d70029865ec56113401c7281b7c2aa54d Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 04:02:47 +0800 Subject: [PATCH 22/41] spacing --- controlyourtabs/keyinfo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/controlyourtabs/keyinfo.py b/controlyourtabs/keyinfo.py index 76ef77e..cd9d79b 100644 --- a/controlyourtabs/keyinfo.py +++ b/controlyourtabs/keyinfo.py @@ -21,6 +21,7 @@ from gi.repository import Gtk, Gdk + CONTROL_MASK = Gdk.ModifierType.CONTROL_MASK CONTROL_SHIFT_MASK = Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK From c0f443a8324dc9ccca20e275bba5dac0f4910412 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 04:14:52 +0800 Subject: [PATCH 23/41] use noop function for when Gedit.debug_plugin_message isn't available --- controlyourtabs/__init__.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 51c5f21..2b3b452 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -40,6 +40,11 @@ except: _ = lambda s: s +try: + debug_plugin_message = Gedit.debug_plugin_message +except: # before 3.4 + debug_plugin_message = lambda fmt, *fmt_args: None + class ControlYourTabsWindowActivatable(GObject.Object, Gedit.WindowActivatable): @@ -191,10 +196,7 @@ def setup(self, window, tab, tab_models): multi = get_multi_notebook(tab) if not multi: - try: - Gedit.debug_plugin_message("cannot find multi notebook from %s", tab) - except AttributeError: # gedit < 3.4 - pass + debug_plugin_message("cannot find multi notebook from %s", tab) return @@ -785,18 +787,12 @@ def get_settings(): ) except AttributeError: # gedit < 3.4 - try: - Gedit.debug_plugin_message("relocatable schemas not supported") - except AttributeError: # gedit < 3.4 - pass + debug_plugin_message("relocatable schemas not supported") schema_source = None except: - try: - Gedit.debug_plugin_message("could not load settings schema source from %s", schemas_path) - except AttributeError: # gedit < 3.4 - pass + debug_plugin_message("could not load settings schema source from %s", schemas_path) schema_source = None From a8dcf3002633a01957066e32c786948a2b757313 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 19:09:11 +0800 Subject: [PATCH 24/41] because I didn't publish any changes in 2014 and 2016, I can't claim copyright on those years (I think?) --- Changelog.md | 2 +- README.md | 2 +- controlyourtabs.plugin | 2 +- controlyourtabs.plugin.python2 | 2 +- controlyourtabs/__init__.py | 2 +- controlyourtabs/keyinfo.py | 2 +- controlyourtabs/tabinfo.py | 2 +- controlyourtabs/tabinfo_pre312.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index 073e508..1998931 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,7 +4,7 @@ * Added Ctrl-Esc to cancel tab switching, causes the initial tab (before switching began) to be active again * Prevent all input during tab switching instead of cancelling switching -* Fixed (potentially) affecting the translations of other plugins +* Fixed copyright notices ## v0.3.1 (2017-10-17) * Show a debug message if the settings schema could not be loaded diff --git a/README.md b/README.md index 397ce6b..d53fb01 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Inspired by: ## License -Copyright © 2010-2014, 2016-2017 Jeffery To +Copyright © 2010-2013, 2017 Jeffery To Available under GNU General Public License version 3 diff --git a/controlyourtabs.plugin b/controlyourtabs.plugin index 2b6a22d..cfd1a5d 100644 --- a/controlyourtabs.plugin +++ b/controlyourtabs.plugin @@ -6,6 +6,6 @@ IAge=3 Name=Control Your Tabs Description=Switch between document tabs using Ctrl+Tab / Ctrl+Shift+Tab and Ctrl+PageUp / Ctrl+PageDown Authors=Jeffery To -Copyright=Copyright © 2010-2014, 2016-2017 Jeffery To +Copyright=Copyright © 2010-2013, 2017 Jeffery To Website=https://github.com/jefferyto/gedit-control-your-tabs Version=0.3.1 diff --git a/controlyourtabs.plugin.python2 b/controlyourtabs.plugin.python2 index 3523085..34929da 100644 --- a/controlyourtabs.plugin.python2 +++ b/controlyourtabs.plugin.python2 @@ -6,6 +6,6 @@ IAge=3 Name=Control Your Tabs Description=Switch between document tabs using Ctrl+Tab / Ctrl+Shift+Tab and Ctrl+PageUp / Ctrl+PageDown Authors=Jeffery To -Copyright=Copyright © 2010-2014, 2016-2017 Jeffery To +Copyright=Copyright © 2010-2013, 2017 Jeffery To Website=https://github.com/jefferyto/gedit-control-your-tabs Version=0.3.1 diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 2b3b452..3b97142 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -3,7 +3,7 @@ # __init__.py # This file is part of Control Your Tabs, a plugin for gedit # -# Copyright (C) 2010-2014, 2016-2017 Jeffery To +# Copyright (C) 2010-2013, 2017 Jeffery To # https://github.com/jefferyto/gedit-control-your-tabs # # This program is free software: you can redistribute it and/or modify diff --git a/controlyourtabs/keyinfo.py b/controlyourtabs/keyinfo.py index cd9d79b..701dc56 100644 --- a/controlyourtabs/keyinfo.py +++ b/controlyourtabs/keyinfo.py @@ -3,7 +3,7 @@ # keyinfo.py # This file is part of Control Your Tabs, a plugin for gedit # -# Copyright (C) 2010-2014, 2016-2017 Jeffery To +# Copyright (C) 2010-2013, 2017 Jeffery To # https://github.com/jefferyto/gedit-control-your-tabs # # This program is free software: you can redistribute it and/or modify diff --git a/controlyourtabs/tabinfo.py b/controlyourtabs/tabinfo.py index 74f9fb7..017efb9 100644 --- a/controlyourtabs/tabinfo.py +++ b/controlyourtabs/tabinfo.py @@ -3,7 +3,7 @@ # tabinfo.py # This file is part of Control Your Tabs, a plugin for gedit # -# Copyright (C) 2010-2014, 2016-2017 Jeffery To +# Copyright (C) 2010-2013, 2017 Jeffery To # https://github.com/jefferyto/gedit-control-your-tabs # # This program is free software: you can redistribute it and/or modify diff --git a/controlyourtabs/tabinfo_pre312.py b/controlyourtabs/tabinfo_pre312.py index db1480a..9d93170 100644 --- a/controlyourtabs/tabinfo_pre312.py +++ b/controlyourtabs/tabinfo_pre312.py @@ -3,7 +3,7 @@ # tabinfo_pre312.py # This file is part of Control Your Tabs, a plugin for gedit # -# Copyright (C) 2010-2014, 2016-2017 Jeffery To +# Copyright (C) 2010-2013, 2017 Jeffery To # https://github.com/jefferyto/gedit-control-your-tabs # # This program is free software: you can redistribute it and/or modify From f4598fa4306fe6af5cf481676523e40c8f93c950 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 19:33:13 +0800 Subject: [PATCH 25/41] block input only when tabwin is visible --- Changelog.md | 3 ++- controlyourtabs/__init__.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 1998931..c4a057f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,7 +3,8 @@ ## Unreleased * Added Ctrl-Esc to cancel tab switching, causes the initial tab (before switching began) to be active again -* Prevent all input during tab switching instead of cancelling switching +* Prevent all input during tab switching, if the tab switching window is + visible, instead of cancelling switching * Fixed copyright notices ## v0.3.1 (2017-10-17) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 3b97142..43bc58f 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -445,6 +445,10 @@ def key_press_event(self, event): elif is_control_tab or is_control_page: self.switch_tab(is_control_tab, keyinfo.is_next_key(event), event.time) + elif self._is_switching and not self._is_tabwin_visible: + self.end_switching() + block_event = False + else: block_event = self._is_switching From 67060f595a745d50ef821553fbcaadce0fbe1ee4 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 19:35:25 +0800 Subject: [PATCH 26/41] Squashed 'controlyourtabs/utils/' changes from 03cd05c..0c0bd37 0c0bd37 Merge branch 'develop' 51eabe4 today is the day 30e6ec3 don't convert other values to str 8525606 even flags is optional 28f0cc6 added to_name da3ad7d accept both dicts and lists/tuples 1e5ef6a save the endangered octothorp b6e28a3 dot files 5bb6789 giant gap 47d8c5b added debug_str d62ce2e added create_bindings / release_bindings 7bcedcd minor formatting 1d63786 changed changelog format git-subtree-dir: controlyourtabs/utils git-subtree-split: 0c0bd37829d073e01931b72d61381bf2eab73de0 --- .editorconfig | 12 +++++++ .gitattributes | 1 + Changelog | 5 --- Changelog.md | 8 +++++ README.md | 12 +++---- __init__.py | 92 ++++++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes delete mode 100644 Changelog create mode 100644 Changelog.md diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..092187d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/Changelog b/Changelog deleted file mode 100644 index 9f63c14..0000000 --- a/Changelog +++ /dev/null @@ -1,5 +0,0 @@ -2013-10-31 Jeffery To - - 0.1.0: - - * Initial release diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000..9784238 --- /dev/null +++ b/Changelog.md @@ -0,0 +1,8 @@ +# Changelog + +## 0.2.0 (2017-10-13) +* Added create_bindings / release_bindings +* Added to_name, debug_str + +## 0.1.0 (2013-10-31) +* Initial release diff --git a/README.md b/README.md index 3d9b017..5740d5d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# python-gtk-utils # +# python-gtk-utils A collection of utilities ready to be `git subtree`-ed into a Python GTK+ project -0.1.0 +0.2.0 All bug reports, feature requests and miscellaneous comments are welcome at the [project issue tracker][]. -## Installation ## +## Installation Use `git subtree` to pull this sub-project into your project: @@ -29,13 +29,13 @@ Pull for updates: git subtree pull --prefix=path/to/code/utils --squash python-gtk-utils master ``` -## Documentation ## +## Documentation ...would be a good idea ;-) -## License ## +## License -Copyright © 2013 Jeffery To +Copyright © 2013, 2017 Jeffery To Available under GNU General Public License version 3 diff --git a/__init__.py b/__init__.py index 878d857..b2f9a08 100644 --- a/__init__.py +++ b/__init__.py @@ -3,7 +3,7 @@ # __init__.py # This file is part of python-gtk-utils # -# Copyright (C) 2013 Jeffery To +# Copyright (C) 2013, 2017 Jeffery To # https://github.com/jefferyto/python-gtk-utils # # This program is free software: you can redistribute it and/or modify @@ -19,12 +19,26 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from gi.repository import GObject + + +# from future.utils + +def _iteritems(obj, **kwargs): + func = getattr(obj, 'iteritems', None) + if not func: + func = obj.items + return func(**kwargs) + + +# signal handlers + def _get_handler_ids_name(ns): return ns.__class__.__name__ + 'HandlerIds' def _get_handler_ids(ns, target): name = _get_handler_ids_name(ns) - return getattr(target, name) if hasattr(target, name) else [] + return getattr(target, name, []) def _set_handler_ids(ns, target, ids): name = _get_handler_ids_name(ns) @@ -42,7 +56,8 @@ def connect_handlers(ns, target, signals, prefix_or_fn, *args): if hasattr(prefix_or_fn, '__call__'): fn = prefix_or_fn else: - fn = getattr(ns, 'on_%s_%s' % (prefix_or_fn, signal.replace('-', '_').replace('::', '_'))) + fn = getattr(ns, 'on_%s_%s' % (prefix_or_fn, to_name(signal))) + handler_ids.append(target.connect(signal, fn, *args)) _set_handler_ids(ns, target, handler_ids) @@ -60,3 +75,74 @@ def block_handlers(ns, target): def unblock_handlers(ns, target): for handler_id in _get_handler_ids(ns, target): target.handler_unblock(handler_id) + + +# bindings + +def _get_bindings_name(ns): + return ns.__class__.__name__ + 'Bindings' + +def _get_bindings(ns, source, target): + name = _get_bindings_name(ns) + binding_map = getattr(source, name, {}) + return binding_map[target] if target in binding_map else [] + +def _set_bindings(ns, source, target, bindings): + name = _get_bindings_name(ns) + binding_map = getattr(source, name, {}) + binding_map[target] = bindings + setattr(source, name, binding_map) + +def _del_bindings(ns, source, target): + name = _get_bindings_name(ns) + binding_map = getattr(source, name, {}) + if target in binding_map: + del binding_map[target] + if not binding_map and hasattr(source, name): + delattr(source, name) + +def create_bindings(ns, source, target, properties, *args): + bindings = _get_bindings(ns, source, target) + + if isinstance(properties, dict): + for (source_property, target_property) in _iteritems(properties): + binding = source.bind_property( + source_property, + target, target_property, + *args + ) + bindings.append(binding) + + else: + for prop in properties: + binding = source.bind_property( + prop, + target, prop, + flags, + transform_to, transform_from, user_data + ) + bindings.append(binding) + + _set_bindings(ns, source, target, bindings) + +def release_bindings(ns, source, target): + for binding in _get_bindings(ns, source, target): + GObject.Binding.unbind(binding) + + _del_bindings(ns, source, target) + + +# misc + +def to_name(value): + return str(value).replace('-', '_').replace('::', '_') + +def debug_str(value): + if isinstance(value, GObject.Object): + # hash(value) is the memory address of the underlying gobject + result = value.__gtype__.name + ': ' + hex(hash(value)) + else: + result = value + + return result + From bb29989a4ca8168165e9f8fe7f10543f222ebba7 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 23:20:45 +0800 Subject: [PATCH 27/41] added logging, let exceptions get thrown if tabs aren't in tab models --- controlyourtabs/__init__.py | 229 ++++++++++++++++++++++++++++-- controlyourtabs/keyinfo.py | 34 ++++- controlyourtabs/log.py | 110 ++++++++++++++ controlyourtabs/tabinfo.py | 19 +++ controlyourtabs/tabinfo_pre312.py | 59 +++++++- 5 files changed, 434 insertions(+), 17 deletions(-) create mode 100644 controlyourtabs/log.py diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 43bc58f..13edd66 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -28,7 +28,7 @@ from functools import wraps from gi.repository import GObject, GLib, Gtk, Gdk, GdkPixbuf, Gio, Gedit, PeasGtk from .utils import connect_handlers, disconnect_handlers -from . import keyinfo, tabinfo, tabinfo_pre312 +from . import keyinfo, log, tabinfo, tabinfo_pre312 BASE_PATH = os.path.dirname(os.path.realpath(__file__)) LOCALE_PATH = os.path.join(BASE_PATH, 'locale') @@ -61,6 +61,9 @@ def __init__(self): GObject.Object.__init__(self) def do_activate(self): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", self.window)) + window = self.window tab_models = {} @@ -118,6 +121,9 @@ def do_activate(self): else: is_side_panel_stack = isinstance(window.get_side_panel(), GtkStack) # since 3.12 + if log.query(log.DEBUG): + debug_plugin_message(log.format("using %s tab names/icons", "current" if is_side_panel_stack else "pre-3.12")) + self._is_switching = False self._is_tabwin_visible = False self._is_control_held = keyinfo.default_control_held() @@ -136,15 +142,24 @@ def do_activate(self): tab = window.get_active_tab() if tab: + if log.query(log.DEBUG): + debug_plugin_message(log.format("found active tab %s, setting up now", tab)) + self.setup(window, tab, tab_models) if self._multi: self.active_tab_changed(tab, tab_models[tab.get_parent()]) else: + if log.query(log.DEBUG): + debug_plugin_message(log.format("waiting for new tab")) + connect_handlers(self, window, ['tab-added'], 'setup', tab_models) def do_deactivate(self): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", self.window)) + multi = self._multi tab_models = self._tab_models @@ -183,11 +198,17 @@ def do_update_state(self): # plugin setup def on_setup_tab_added(self, window, tab, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", window, tab)) + disconnect_handlers(self, window) self.setup(window, tab, tab_models) def setup(self, window, tab, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", window, tab)) + icon_size = self._tabinfo.get_tab_icon_size(tab) self._icon_cell.set_fixed_size(icon_size, icon_size) @@ -196,7 +217,8 @@ def setup(self, window, tab, tab_models): multi = get_multi_notebook(tab) if not multi: - debug_plugin_message("cannot find multi notebook from %s", tab) + if log.query(log.ERROR): + debug_plugin_message(log.format("cannot find multi notebook from %s", tab)) return @@ -235,7 +257,13 @@ def setup(self, window, tab, tab_models): # tracking notebooks / tabs def track_notebook(self, notebook, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self.window, notebook)) + if notebook in tab_models: + if log.query(log.WARNING): + debug_plugin_message(log.format("already tracking notebook")) + return tab_model = ControlYourTabsTabModel(self._tabinfo) @@ -263,7 +291,13 @@ def track_notebook(self, notebook, tab_models): self.track_tab(tab, tab_model) def untrack_notebook(self, notebook, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self.window, notebook)) + if notebook not in tab_models: + if log.query(log.WARNING): + debug_plugin_message(log.format("not tracking notebook")) + return tab_model = tab_models[notebook] @@ -279,7 +313,13 @@ def untrack_notebook(self, notebook, tab_models): del tab_models[notebook] def track_tab(self, tab, tab_model): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self.window, tab)) + if tab in tab_model: + if log.query(log.WARNING): + debug_plugin_message(log.format("already tracking tab")) + return tab_model.append(tab) @@ -295,10 +335,19 @@ def track_tab(self, tab, tab_model): ) def untrack_tab(self, tab, tab_model): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self.window, tab)) + if tab == self._initial_tab: + if log.query(log.DEBUG): + debug_plugin_message(log.format("tab is initial tab, clearing")) + self._initial_tab = None if tab not in tab_model: + if log.query(log.WARNING): + debug_plugin_message(log.format("not tracking tab")) + return disconnect_handlers(self, tab) @@ -306,11 +355,10 @@ def untrack_tab(self, tab, tab_model): tab_model.remove(tab) def active_tab_changed(self, tab, tab_model): - if not self._is_switching: - if tab not in tab_model: - # can this happen? - self.track_tab(tab, tab_model) + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self.window, tab)) + if not self._is_switching: tab_model.move_after(tab) tab_model.select(tab) @@ -323,18 +371,33 @@ def active_tab_changed(self, tab, tab_model): # signal handlers def on_multi_notebook_notebook_added(self, multi, notebook, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self.window, notebook)) + self.track_notebook(notebook, tab_models) def on_multi_notebook_notebook_removed(self, multi, notebook, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self.window, notebook)) + self.untrack_notebook(notebook, tab_models) def on_multi_notebook_tab_added(self, multi, notebook, tab, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, %s", self.window, notebook, tab)) + self.track_tab(tab, tab_models[notebook]) def on_multi_notebook_tab_removed(self, multi, notebook, tab, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, %s", self.window, notebook, tab)) + self.untrack_tab(tab, tab_models[notebook]) def on_window_tabs_reordered(self, window, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", window)) + # XXX reordering tabs doesn't necessarily involve the active tab # and reordering only happens within a notebook, not across notebooks # ... @@ -345,6 +408,9 @@ def on_window_tabs_reordered(self, window, tab_models): tab_model = tab_models[new_notebook] if tab in tab_model: + if log.query(log.DEBUG): + debug_plugin_message(log.format("active tab did not change notebooks")) + return old_notebook = None @@ -356,44 +422,84 @@ def on_window_tabs_reordered(self, window, tab_models): break if old_notebook: + if log.query(log.DEBUG): + debug_plugin_message(log.format("untracking tab from old notebook %s", old_notebook)) + self.untrack_tab(tab, tab_models[old_notebook]) + if log.query(log.DEBUG): + debug_plugin_message(log.format("tracking tab in new notebook %s", new_notebook)) + self.track_tab(tab, tab_models[new_notebook]) def on_window_active_tab_changed(self, window, tab, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", window, tab)) + self.active_tab_changed(tab, tab_models[tab.get_parent()]) def on_window_key_press_event(self, window, event, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, key=%s", window, Gdk.keyval_name(event.keyval))) + self._is_control_held = keyinfo.update_control_held(event, self._is_control_held, True) return self.key_press_event(event) def on_window_key_release_event(self, window, event, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, key=%s", self.window, Gdk.keyval_name(event.keyval))) + self._is_control_held = keyinfo.update_control_held(event, self._is_control_held, False) if not any(self._is_control_held): + if log.query(log.INFO): + debug_plugin_message(log.format("no control keys held down")) + self.end_switching() + else: + if log.query(log.DEBUG): + debug_plugin_message(log.format("one or more control keys held down")) + def on_window_focus_out_event(self, window, event, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", window)) + self.end_switching() def on_window_configure_event(self, window, event, tab_models): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", window)) + self.schedule_tabwin_resize() def on_tab_notify_name_state(self, tab, pspec, tab_model): - if tab not in tab_model: - return + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self.window, tab)) tab_model.update(tab) def on_tab_model_row_changed(self, tab_model, path): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, path=%s", self.window, path)) + if not self.is_active_view_model(tab_model): + if log.query(log.DEBUG): + debug_plugin_message(log.format("tab model not active")) + return self.schedule_tabwin_resize() def on_tab_model_selected_path_changed(self, tab_model, path): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, path=%s", self.window, path)) + if not self.is_active_view_model(tab_model): + if log.query(log.DEBUG): + debug_plugin_message(log.format("tab model not active")) + return self.set_view_selection(path) @@ -406,6 +512,9 @@ def is_active_view_model(self, tab_model): return self._view.get_model() is model def set_active_view_model(self, tab_model): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self.window, tab_model)) + model = None selected_path = None @@ -417,6 +526,9 @@ def set_active_view_model(self, tab_model): self.set_view_selection(selected_path) def set_view_selection(self, path): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, path=%s", self.window, path)) + view = self._view selection = view.get_selection() @@ -431,34 +543,58 @@ def set_view_selection(self, path): # tab switching def key_press_event(self, event): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, key=%s", self.window, Gdk.keyval_name(event.keyval))) + settings = self._settings is_control_tab, is_control_page, is_control_escape = keyinfo.is_control_keys(event) block_event = True if is_control_tab and settings and settings['use-tabbar-order']: + if log.query(log.DEBUG): + debug_plugin_message(log.format("coercing ctrl-tab into ctrl-page because of settings")) + is_control_tab = False is_control_page = True if self._is_switching and is_control_escape: + if log.query(log.DEBUG): + debug_plugin_message(log.format("ctrl-esc while switching")) + self.end_switching(True) elif is_control_tab or is_control_page: + if log.query(log.DEBUG): + debug_plugin_message(log.format("ctrl-tab or ctrl-page")) + self.switch_tab(is_control_tab, keyinfo.is_next_key(event), event.time) elif self._is_switching and not self._is_tabwin_visible: + if log.query(log.DEBUG): + debug_plugin_message(log.format("normal key while switching and tabwin not visible")) + self.end_switching() block_event = False else: + if log.query(log.DEBUG): + debug_plugin_message(log.format("normal key while %s", "switching" if self._is_switching else "not switching")) + block_event = self._is_switching return block_event def switch_tab(self, use_mru_order, to_next_tab, time): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, use_mru_order=%s, to_next_tab=%s, time=%s", self.window, use_mru_order, to_next_tab, time)) + window = self.window current_tab = window.get_active_tab() if not current_tab: + if log.query(log.DEBUG): + debug_plugin_message(log.format("no tabs")) + return notebook = current_tab.get_parent() @@ -466,7 +602,10 @@ def switch_tab(self, use_mru_order, to_next_tab, time): tabs = self._tab_models[notebook] if use_mru_order else notebook.get_children() num_tabs = len(tabs) - if num_tabs < 2 or current_tab not in tabs: + if num_tabs < 2: + if log.query(log.DEBUG): + debug_plugin_message(log.format("only 1 tab")) + return current_index = tabs.index(current_tab) @@ -475,7 +614,13 @@ def switch_tab(self, use_mru_order, to_next_tab, time): next_tab = tabs[next_index] + if log.query(log.DEBUG): + debug_plugin_message(log.format("switching from %s to %s", current_tab, next_tab)) + if not self._is_switching: + if log.query(log.DEBUG): + debug_plugin_message(log.format("saving %s as initial tab", current_tab)) + self._initial_tab = current_tab self._is_switching = True @@ -486,15 +631,27 @@ def switch_tab(self, use_mru_order, to_next_tab, time): tabwin = self._tabwin if not self._is_tabwin_visible: + if log.query(log.DEBUG): + debug_plugin_message(log.format("showing tabwin")) + tabwin.show_all() else: + if log.query(log.DEBUG): + debug_plugin_message(log.format("presenting tabwin")) + tabwin.present_with_time(time) self._is_tabwin_visible = True def end_switching(self, do_revert=False): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, do_revert=%s", self.window, do_revert)) + if not self._is_switching: + if log.query(log.DEBUG): + debug_plugin_message(log.format("not switching")) + return window = self.window @@ -507,6 +664,9 @@ def end_switching(self, do_revert=False): self._initial_tab = None if do_revert and initial_tab: + if log.query(log.DEBUG): + debug_plugin_message(log.format("switching to initial tab %s", initial_tab)) + window.set_active_tab(initial_tab) else: @@ -519,7 +679,13 @@ def end_switching(self, do_revert=False): # tab window resizing def schedule_tabwin_resize(self): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", self.window)) + if self._tabwin_resize_id: + if log.query(log.INFO): + debug_plugin_message(log.format("already scheduled")) + return # need to wait a little before asking the treeview for its preferred size @@ -533,7 +699,13 @@ def schedule_tabwin_resize(self): self._tabwin_resize_id = resize_id def cancel_tabwin_resize(self): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", self.window)) + if not self._tabwin_resize_id: + if log.query(log.DEBUG): + debug_plugin_message(log.format("not scheduled")) + return GLib.source_remove(self._tabwin_resize_id) @@ -541,6 +713,9 @@ def cancel_tabwin_resize(self): self._tabwin_resize_id = None def do_tabwin_resize(self): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", self.window)) + view = self._view sw = self._sw @@ -573,6 +748,13 @@ def do_tabwin_resize(self): tabwin_width = max(sw_min_size.width, sw_nat_size.width) tabwin_height = min(view_height, max_height) + if log.query(log.DEBUG): + debug_plugin_message(log.format("view height = %s", view_height)) + debug_plugin_message(log.format("max rows height = %s", max_rows_height)) + debug_plugin_message(log.format("max win height = %s", max_win_height)) + debug_plugin_message(log.format("tabwin height = %s", tabwin_height)) + debug_plugin_message(log.format("tabwin width = %s", tabwin_width)) + self._tabwin.set_size_request(tabwin_width, tabwin_height) self._tabwin_resize_id = None @@ -739,9 +921,15 @@ class ControlYourTabsConfigurable(GObject.Object, PeasGtk.Configurable): def do_create_configure_widget(self): + if log.query(log.INFO): + debug_plugin_message(log.format("")) + settings = get_settings() if settings: + if log.query(log.DEBUG): + debug_plugin_message(log.format("have settings")) + widget = Gtk.CheckButton.new_with_label( _("Use tabbar order for Ctrl+Tab / Ctrl+Shift+Tab") ) @@ -753,6 +941,9 @@ def do_create_configure_widget(self): ) else: + if log.query(log.DEBUG): + debug_plugin_message(log.format("no settings")) + label = Gtk.Label.new( _("Sorry, no preferences are available for this version of gedit.") ) @@ -770,6 +961,9 @@ def do_create_configure_widget(self): # this is a /hack/ # can do window.get_template_child(Gedit.Window, 'multi_notebook') since 3.12 def get_multi_notebook(tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", tab)) + widget = tab.get_parent() while widget: @@ -781,6 +975,9 @@ def get_multi_notebook(tab): return widget def get_settings(): + if log.query(log.INFO): + debug_plugin_message(log.format("")) + schemas_path = os.path.join(BASE_PATH, 'schemas') try: @@ -790,17 +987,22 @@ def get_settings(): False ) - except AttributeError: # gedit < 3.4 - debug_plugin_message("relocatable schemas not supported") + except AttributeError: # before 3.4 + if log.query(log.DEBUG): + debug_plugin_message(log.format("relocatable schemas not supported")) schema_source = None except: - debug_plugin_message("could not load settings schema source from %s", schemas_path) + if log.query(log.WARNING): + debug_plugin_message(log.format("could not load settings schema source from %s", schemas_path)) schema_source = None if not schema_source: + if log.query(log.DEBUG): + debug_plugin_message(log.format("no schema source")) + return None schema = schema_source.lookup( @@ -809,6 +1011,9 @@ def get_settings(): ) if not schema: + if log.query(log.WARNING): + debug_plugin_message(log.format("could not lookup schema")) + return None return Gio.Settings.new_full( diff --git a/controlyourtabs/keyinfo.py b/controlyourtabs/keyinfo.py index 701dc56..25608ee 100644 --- a/controlyourtabs/keyinfo.py +++ b/controlyourtabs/keyinfo.py @@ -19,7 +19,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from gi.repository import Gtk, Gdk +from gi.repository import Gtk, Gdk, Gedit +from . import log + +try: + debug_plugin_message = Gedit.debug_plugin_message +except: # before 3.4 + debug_plugin_message = lambda fmt, *fmt_args: None CONTROL_MASK = Gdk.ModifierType.CONTROL_MASK @@ -43,15 +49,26 @@ def default_control_held(): def update_control_held(event, prev_statuses, new_status): keyval = event.keyval - return [ + if log.query(log.INFO): + debug_plugin_message(log.format("key=%s, %s, new_status=%s", Gdk.keyval_name(keyval), prev_statuses, new_status)) + + new_statuses = [ new_status if keyval == control_key else prev_status for control_key, prev_status in zip(CONTROL_KEY_LIST, prev_statuses) ] + if log.query(log.DEBUG): + debug_plugin_message(log.format("new_statuses=%s", new_statuses)) + + return new_statuses + def is_control_keys(event): keyval = event.keyval state = event.state & Gtk.accelerator_get_default_mod_mask() + if log.query(log.INFO): + debug_plugin_message(log.format("key=%s, state=%s", Gdk.keyval_name(keyval), state)) + is_control = state == CONTROL_MASK is_control_shift = state == CONTROL_SHIFT_MASK @@ -63,8 +80,19 @@ def is_control_keys(event): is_control_page = is_control and is_page is_control_escape = (is_control or is_control_shift) and is_escape + if log.query(log.DEBUG): + debug_plugin_message(log.format("is_control_tab=%s, is_control_page=%s, is_control_escape=%s", is_control_tab, is_control_page, is_control_escape)) + return (is_control_tab, is_control_page, is_control_escape) def is_next_key(event): - return event.keyval in NEXT_KEY_SET + if log.query(log.INFO): + debug_plugin_message(log.format("key=%s", Gdk.keyval_name(event.keyval))) + + result = event.keyval in NEXT_KEY_SET + + if log.query(log.DEBUG): + debug_plugin_message(log.format("result=%s", result)) + + return result diff --git a/controlyourtabs/log.py b/controlyourtabs/log.py new file mode 100644 index 0000000..70d935a --- /dev/null +++ b/controlyourtabs/log.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# +# log.py +# This file is part of Control Your Tabs, a plugin for gedit +# +# Copyright (C) 2010-2013, 2017 Jeffery To +# https://github.com/jefferyto/gedit-control-your-tabs +# +# 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 3 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, see . + +import os +from gi.repository import GLib +from .utils import debug_str + + +# for convenience, in decreasing order of severity +ERROR = GLib.LogLevelFlags.LEVEL_ERROR +CRITICAL = GLib.LogLevelFlags.LEVEL_CRITICAL +WARNING = GLib.LogLevelFlags.LEVEL_WARNING +MESSAGE = GLib.LogLevelFlags.LEVEL_MESSAGE +INFO = GLib.LogLevelFlags.LEVEL_INFO +DEBUG = GLib.LogLevelFlags.LEVEL_DEBUG + +LEVELS_TO_NAMES = { + ERROR: "error", + CRITICAL: "critical", + WARNING: "warning", + MESSAGE: "message", + INFO: "info", + DEBUG: "debug" +} + +NAMES_TO_LEVELS = {} + +for (level, name) in LEVELS_TO_NAMES.items(): + NAMES_TO_LEVELS[name] = level + +# messages equal or higher in severity will be printed +output_level = MESSAGE + +name = os.getenv('GEDIT_CONTROL_YOUR_TABS_DEBUG_LEVEL', '').lower() +if name in NAMES_TO_LEVELS: + output_level = NAMES_TO_LEVELS[name] + +# set by query(), used by prefix() +last_queried_level = None + + +def is_error(log_level): + return bool(log_level & ERROR) + +def is_critical(log_level): + return bool(log_level & CRITICAL) + +def is_warning(log_level): + return bool(log_level & WARNING) + +def is_message(log_level): + return bool(log_level & MESSAGE) + +def is_info(log_level): + return bool(log_level & INFO) + +def is_debug(log_level): + return bool(log_level & DEBUG) + +def highest(log_level): + if log_level < ERROR or is_error(log_level): + highest = ERROR + elif is_critical(log_level): + highest = CRITICAL + elif is_warning(log_level): + highest = WARNING + elif is_message(log_level): + highest = MESSAGE + elif is_info(log_level): + highest = INFO + else: + highest = DEBUG + + return highest + +def query(log_level): + global last_queried_level + last_queried_level = log_level + + return highest(log_level) <= output_level + +def prefix(log_level=None): + if log_level is None: + log_level = last_queried_level + + name = LEVELS_TO_NAMES[highest(log_level)] if log_level is not None else 'unknown' + + return '[' + name + '] ' + +def format(message, *args): + return prefix() + (message % tuple(debug_str(arg) for arg in args)) + diff --git a/controlyourtabs/tabinfo.py b/controlyourtabs/tabinfo.py index 017efb9..ee6f0cf 100644 --- a/controlyourtabs/tabinfo.py +++ b/controlyourtabs/tabinfo.py @@ -22,6 +22,7 @@ import os.path from gi.repository import Gtk, GtkSource, Gedit from xml.sax.saxutils import escape +from . import log BASE_PATH = os.path.dirname(os.path.realpath(__file__)) LOCALE_PATH = os.path.join(BASE_PATH, 'locale') @@ -33,6 +34,11 @@ except: _ = lambda s: s +try: + debug_plugin_message = Gedit.debug_plugin_message +except: # before 3.4 + debug_plugin_message = lambda fmt, *fmt_args: None + # based on switch statement in _gedit_tab_get_icon() in gedit-tab.c TAB_STATE_TO_NAMED_ICON = { @@ -48,6 +54,9 @@ # based on doc_get_name() and document_row_sync_tab_name_and_icon() in gedit-documents-panel.c def get_tab_name(tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", tab)) + doc = tab.get_document() name = doc.get_short_name_for_display() docname = Gedit.utils_str_middle_truncate(name, 60) # based on MAX_DOC_NAME_LENGTH in gedit-documents-panel.c @@ -64,10 +73,16 @@ def get_tab_name(tab): if is_readonly: tab_name += " [%s]" % escape(_("Read-Only")) + if log.query(log.DEBUG): + debug_plugin_message(log.format("tab_name=%s", tab_name)) + return tab_name # based on _gedit_tab_get_icon() in gedit-tab.c def get_tab_icon(tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", tab)) + state = tab.get_state() if state not in TAB_STATE_TO_NAMED_ICON: @@ -80,6 +95,10 @@ def get_tab_icon(tab): return Gtk.IconTheme.load_icon(theme, icon_name, icon_size, 0) def get_tab_icon_size(tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", tab)) + is_valid_size, icon_size_width, icon_size_height = Gtk.icon_size_lookup(Gtk.IconSize.MENU) + return icon_size_height diff --git a/controlyourtabs/tabinfo_pre312.py b/controlyourtabs/tabinfo_pre312.py index 9d93170..0f393d7 100644 --- a/controlyourtabs/tabinfo_pre312.py +++ b/controlyourtabs/tabinfo_pre312.py @@ -22,6 +22,7 @@ import os.path from gi.repository import GObject, Gtk, GdkPixbuf, Gio, Gedit from xml.sax.saxutils import escape +from . import log BASE_PATH = os.path.dirname(os.path.realpath(__file__)) LOCALE_PATH = os.path.join(BASE_PATH, 'locale') @@ -33,6 +34,11 @@ except: _ = lambda s: s +try: + debug_plugin_message = Gedit.debug_plugin_message +except: # before 3.4 + debug_plugin_message = lambda fmt, *fmt_args: None + # based on switch statement in _gedit_tab_get_icon() in gedit-tab.c TAB_STATE_TO_STOCK_ICON = { @@ -51,6 +57,9 @@ # based on tab_get_name() in gedit-documents-panel.c def get_tab_name(tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", tab)) + doc = tab.get_document() name = doc.get_short_name_for_display() docname = Gedit.utils_str_middle_truncate(name, 60) # based on MAX_DOC_NAME_LENGTH in gedit-documents-panel.c @@ -61,44 +70,80 @@ def get_tab_name(tab): if doc.get_readonly(): tab_name += " [%s]" % escape(_("Read-Only")) + if log.query(log.DEBUG): + debug_plugin_message(log.format("tab_name=%s", tab_name)) + return tab_name # based on _gedit_tab_get_icon() in gedit-tab.c def get_tab_icon(tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", tab)) + state = tab.get_state() theme = Gtk.IconTheme.get_for_screen(tab.get_screen()) icon_size = get_tab_icon_size(tab) pixbuf = None if state in TAB_STATE_TO_STOCK_ICON: + stock = TAB_STATE_TO_STOCK_ICON[state] + + if log.query(log.DEBUG): + debug_plugin_message(log.format("getting stock icon %s", stock)) + try: - pixbuf = get_stock_icon(theme, TAB_STATE_TO_STOCK_ICON[state], icon_size) + pixbuf = get_stock_icon(theme, stock, icon_size) except GObject.GError: + if log.query(log.WARNING): + debug_plugin_message(log.format("could not get stock icon %s", stock)) + pixbuf = None if not pixbuf: - pixbuf = get_icon(theme, tab.get_document().get_location(), icon_size) + location = tab.get_document().get_location() + + if log.query(log.DEBUG): + debug_plugin_message(log.format("getting icon for location %s", location)) + + pixbuf = get_icon(theme, location, icon_size) return pixbuf def get_tab_icon_size(tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", tab)) + is_valid_size, icon_size_width, icon_size_height = Gtk.icon_size_lookup_for_settings(tab.get_settings(), Gtk.IconSize.MENU) + return icon_size_height # based on get_stock_icon() in gedit-tab.c def get_stock_icon(theme, stock, size): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, size=%s", theme, stock, size)) + pixbuf = theme.load_icon(stock, size, 0) + return resize_icon(pixbuf, size) # based on get_icon() in gedit-tab.c def get_icon(theme, location, size): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, size=%s", theme, location, size)) + pixbuf = None if location: + if log.query(log.DEBUG): + debug_plugin_message(log.format("querying info for location %s", location)) + # FIXME: Doing a sync stat is bad, this should be fixed try: info = location.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None) except GObject.GError: + if log.query(log.WARNING): + debug_plugin_message(log.format("could not query info for location %s", location)) + info = None icon = info.get_icon() if info else None @@ -106,14 +151,24 @@ def get_icon(theme, location, size): pixbuf = icon_info.load_icon() if icon_info else None if pixbuf: + if log.query(log.DEBUG): + debug_plugin_message(log.format("have pixbuf")) + pixbuf = resize_icon(pixbuf, size) + else: + if log.query(log.DEBUG): + debug_plugin_message(log.format("no pixbuf, getting stock icon %s", Gtk.STOCK_FILE)) + pixbuf = get_stock_icon(theme, Gtk.STOCK_FILE, size) return pixbuf # based on resize_icon() in gedit-tab.c def resize_icon(pixbuf, size): + if log.query(log.INFO): + debug_plugin_message(log.format("size=%s", size)) + width = pixbuf.get_width() height = pixbuf.get_height() From 03da15fae269aa8a72b30116dfa1569878635ed8 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 23:22:30 +0800 Subject: [PATCH 28/41] wrong column --- controlyourtabs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 13edd66..9907ced 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -828,7 +828,7 @@ def __delitem__(self, key): del self._model[key] def __iter__(self): - return [row[0] for row in self._model] + return [row[2] for row in self._model] def __contains__(self, item): return item in self._references From 778d0a7d0d9900b586e4abbaf4af16d34aa5e241 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 25 Oct 2017 23:24:43 +0800 Subject: [PATCH 29/41] cast instead of explicitly calling a method --- controlyourtabs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 9907ced..7669719 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -890,7 +890,7 @@ def get_path(self, tab): return self._references[tab].get_path() def index(self, tab): - return int(self.get_path(tab).to_string()) + return int(str(self.get_path(tab))) def _get_iter(self, tab): return self._model.get_iter(self.get_path(tab)) From 80fa67734e5636dd89a21ec756acbc230a822d0b Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Thu, 26 Oct 2017 00:37:34 +0800 Subject: [PATCH 30/41] more logging --- controlyourtabs/__init__.py | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 7669719..2eed98b 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -795,6 +795,9 @@ def wrapper(self, *args, **kwargs): def __init__(self, tabinfo): GObject.Object.__init__(self) + if log.query(log.INFO): + debug_plugin_message(log.format("%s", self)) + self._model = Gtk.ListStore.new((GdkPixbuf.Pixbuf, str, Gedit.Tab)) self._references = {} self._selected = None @@ -819,6 +822,9 @@ def __getitem__(self, key): @_model_modifier def __delitem__(self, key): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, key=%s", self, key)) + tab = self._model[key][2] if self._selected == tab: @@ -838,19 +844,57 @@ def model(self): return self._model def on_model_row_inserted(self, model, path, iter_): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, path=%s", self, model, path)) + self.emit('row-inserted', path) def on_model_row_deleted(self, model, path): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, path=%s", self, model, path)) + self.emit('row-deleted', path) def on_model_row_changed(self, model, path, iter_): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, path=%s", self, model, path)) + self.emit('row-changed', path) def on_model_rows_reordered(self, model, path, iter_, new_order): + if log.query(log.INFO): + # path is suppose to point to the parent node of the reordered rows + # if top level rows are reordered, path is invalid (null?) + # so don't print it out here, because will throw an error + debug_plugin_message(log.format("%s, %s", self, model)) + self.emit('rows-reordered') + def do_row_inserted(self, path): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, path=%s", self, path)) + + def do_row_deleted(self, path): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, path=%s", self, path)) + + def do_row_changed(self, path): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, path=%s", self, path)) + + def do_rows_reordered(self): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", self)) + + def do_selected_path_changed(self, path): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, path=%s", self, path)) + @_model_modifier def insert(self, position, tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, position=%s, %s", self, position, tab)) + tab_iter = self._model.insert( position, ( @@ -859,19 +903,32 @@ def insert(self, position, tab): tab ) ) + self._references[tab] = Gtk.TreeRowReference.new(self._model, self._model.get_path(tab_iter)) def append(self, tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self, tab)) + self.insert(-1, tab) def prepend(self, tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self, tab)) + self.insert(0, tab) def remove(self, tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self, tab)) + del self[self.get_path(tab)] @_model_modifier def move(self, tab, sibling, move_before): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, %s, move_before=%s", self, tab, sibling, move_before)) + tab_iter = self._get_iter(tab) sibling_iter = self._get_iter(sibling) if sibling else None @@ -881,9 +938,15 @@ def move(self, tab, sibling, move_before): self._model.move_after(tab_iter, sibling_iter) def move_before(self, tab, sibling=None): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, %s", self, tab, sibling)) + self.move(tab, sibling, True) def move_after(self, tab, sibling=None): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s, %s", self, tab, sibling)) + self.move(tab, sibling, False) def get_path(self, tab): @@ -897,9 +960,15 @@ def _get_iter(self, tab): @_model_modifier def select(self, tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self, tab)) + self._selected = tab def unselect(self): + if log.query(log.INFO): + debug_plugin_message(log.format("%s", self)) + self.select(None) def get_selected(self): @@ -909,6 +978,9 @@ def get_selected_path(self): return self.get_path(self._selected) if self._selected else None def update(self, tab): + if log.query(log.INFO): + debug_plugin_message(log.format("%s, %s", self, tab)) + path = self.get_path(tab) self._model[path][0] = self._tabinfo.get_tab_icon(tab) From 267c421745fc601e18476d3e25de53660c714312 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Thu, 26 Oct 2017 01:16:00 +0800 Subject: [PATCH 31/41] fixed error in gedit < 3.4 --- controlyourtabs/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 2eed98b..57d184e 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -910,7 +910,7 @@ def append(self, tab): if log.query(log.INFO): debug_plugin_message(log.format("%s, %s", self, tab)) - self.insert(-1, tab) + self.insert(len(self._model), tab) # before 3.4, -1 position does not work def prepend(self, tab): if log.query(log.INFO): From d0f97160432b31b92f190e45c2695a03409054af Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Thu, 26 Oct 2017 02:14:59 +0800 Subject: [PATCH 32/41] fixed del error, identify sources of pain --- controlyourtabs/__init__.py | 20 +++++++++++--------- controlyourtabs/keyinfo.py | 2 +- controlyourtabs/tabinfo.py | 4 ++-- controlyourtabs/tabinfo_pre312.py | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 57d184e..936ad3b 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -42,7 +42,7 @@ try: debug_plugin_message = Gedit.debug_plugin_message -except: # before 3.4 +except: # before gedit 3.4 debug_plugin_message = lambda fmt, *fmt_args: None @@ -50,7 +50,7 @@ class ControlYourTabsWindowActivatable(GObject.Object, Gedit.WindowActivatable): __gtype_name__ = 'ControlYourTabsWindowActivatable' - window = GObject.property(type=Gedit.Window) # lowercase 'p' for gedit < 3.4 + window = GObject.property(type=Gedit.Window) # before pygobject 3.2, lowercase 'p' MAX_TAB_WINDOW_ROWS = 9 @@ -119,7 +119,7 @@ def do_activate(self): except AttributeError: is_side_panel_stack = False else: - is_side_panel_stack = isinstance(window.get_side_panel(), GtkStack) # since 3.12 + is_side_panel_stack = isinstance(window.get_side_panel(), GtkStack) # since gedit 3.12 if log.query(log.DEBUG): debug_plugin_message(log.format("using %s tab names/icons", "current" if is_side_panel_stack else "pre-3.12")) @@ -693,7 +693,7 @@ def schedule_tabwin_resize(self): # this feels like a giant hack try: resize_id = GLib.idle_add(self.do_tabwin_resize) - except TypeError: # gedit 3.0 + except TypeError: # before pygobject 3.0 resize_id = GObject.idle_add(self.do_tabwin_resize) self._tabwin_resize_id = resize_id @@ -766,7 +766,7 @@ class ControlYourTabsTabModel(GObject.Object): __gtype_name__ = 'ControlYourTabsTabModel' - __gsignals__ = { # gedit < 3.6 + __gsignals__ = { # before pygobject 3.4 'row-inserted': (GObject.SignalFlags.RUN_FIRST, None, (Gtk.TreePath,)), 'row-deleted': (GObject.SignalFlags.RUN_FIRST, None, (Gtk.TreePath,)), 'row-changed': (GObject.SignalFlags.RUN_FIRST, None, (Gtk.TreePath,)), @@ -831,7 +831,9 @@ def __delitem__(self, key): self._selected = None del self._references[tab] - del self._model[key] + + # before pygobject 3.2, cannot del model[path] + self._model.remove(self._model.get_iter(key)) def __iter__(self): return [row[2] for row in self._model] @@ -910,7 +912,7 @@ def append(self, tab): if log.query(log.INFO): debug_plugin_message(log.format("%s, %s", self, tab)) - self.insert(len(self._model), tab) # before 3.4, -1 position does not work + self.insert(len(self._model), tab) # before pygobject 3.2, -1 position does not work def prepend(self, tab): if log.query(log.INFO): @@ -1031,7 +1033,7 @@ def do_create_configure_widget(self): # this is a /hack/ -# can do window.get_template_child(Gedit.Window, 'multi_notebook') since 3.12 +# can do window.get_template_child(Gedit.Window, 'multi_notebook') since gedit 3.12 def get_multi_notebook(tab): if log.query(log.INFO): debug_plugin_message(log.format("%s", tab)) @@ -1059,7 +1061,7 @@ def get_settings(): False ) - except AttributeError: # before 3.4 + except AttributeError: # before gedit 3.4 if log.query(log.DEBUG): debug_plugin_message(log.format("relocatable schemas not supported")) diff --git a/controlyourtabs/keyinfo.py b/controlyourtabs/keyinfo.py index 25608ee..a654dba 100644 --- a/controlyourtabs/keyinfo.py +++ b/controlyourtabs/keyinfo.py @@ -24,7 +24,7 @@ try: debug_plugin_message = Gedit.debug_plugin_message -except: # before 3.4 +except: # before gedit 3.4 debug_plugin_message = lambda fmt, *fmt_args: None diff --git a/controlyourtabs/tabinfo.py b/controlyourtabs/tabinfo.py index ee6f0cf..e6e5944 100644 --- a/controlyourtabs/tabinfo.py +++ b/controlyourtabs/tabinfo.py @@ -36,7 +36,7 @@ try: debug_plugin_message = Gedit.debug_plugin_message -except: # before 3.4 +except: # before gedit 3.4 debug_plugin_message = lambda fmt, *fmt_args: None @@ -68,7 +68,7 @@ def get_tab_name(tab): file = doc.get_file() is_readonly = GtkSource.File.is_readonly(file) except AttributeError: - is_readonly = doc.get_readonly() # deprecated since 3.18 + is_readonly = doc.get_readonly() # deprecated since gedit 3.18 if is_readonly: tab_name += " [%s]" % escape(_("Read-Only")) diff --git a/controlyourtabs/tabinfo_pre312.py b/controlyourtabs/tabinfo_pre312.py index 0f393d7..efb1f40 100644 --- a/controlyourtabs/tabinfo_pre312.py +++ b/controlyourtabs/tabinfo_pre312.py @@ -36,7 +36,7 @@ try: debug_plugin_message = Gedit.debug_plugin_message -except: # before 3.4 +except: # before gedit 3.4 debug_plugin_message = lambda fmt, *fmt_args: None From 42c178fd238b04d7369cf1dfc0076f09221f8e32 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Thu, 26 Oct 2017 02:33:29 +0800 Subject: [PATCH 33/41] don't need to track tabs-reordered --- controlyourtabs/__init__.py | 39 ------------------------------------- 1 file changed, 39 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 936ad3b..0d3a459 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -236,7 +236,6 @@ def setup(self, window, tab, tab_models): connect_handlers( self, window, [ - 'tabs-reordered', 'active-tab-changed', 'key-press-event', 'key-release-event', @@ -394,44 +393,6 @@ def on_multi_notebook_tab_removed(self, multi, notebook, tab, tab_models): self.untrack_tab(tab, tab_models[notebook]) - def on_window_tabs_reordered(self, window, tab_models): - if log.query(log.INFO): - debug_plugin_message(log.format("%s", window)) - - # XXX reordering tabs doesn't necessarily involve the active tab - # and reordering only happens within a notebook, not across notebooks - # ... - multi = self._multi - tab = window.get_active_tab() - new_notebook = tab.get_parent() - - tab_model = tab_models[new_notebook] - - if tab in tab_model: - if log.query(log.DEBUG): - debug_plugin_message(log.format("active tab did not change notebooks")) - - return - - old_notebook = None - - for notebook in tab_models: - tab_model = tab_models[notebook] - if tab in tab_model: - old_notebook = notebook - break - - if old_notebook: - if log.query(log.DEBUG): - debug_plugin_message(log.format("untracking tab from old notebook %s", old_notebook)) - - self.untrack_tab(tab, tab_models[old_notebook]) - - if log.query(log.DEBUG): - debug_plugin_message(log.format("tracking tab in new notebook %s", new_notebook)) - - self.track_tab(tab, tab_models[new_notebook]) - def on_window_active_tab_changed(self, window, tab, tab_models): if log.query(log.INFO): debug_plugin_message(log.format("%s, %s", window, tab)) From 5dca039fc157d0e92700f1226cd3bae07330656d Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 1 Nov 2017 21:42:42 +0800 Subject: [PATCH 34/41] actually fix the divide by zero error, even though there is no functional difference --- controlyourtabs/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 0d3a459..b93c3b9 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -683,9 +683,12 @@ def do_tabwin_resize(self): view_min_size, view_nat_size = view.get_preferred_size() view_height = max(view_min_size.height, view_nat_size.height) - num_rows = max(len(view.get_model()), 2) - row_height = math.ceil(view_height / num_rows) - max_rows_height = self.MAX_TAB_WINDOW_ROWS * row_height + num_rows = len(view.get_model()) + if num_rows: + row_height = math.ceil(view_height / num_rows) + max_rows_height = self.MAX_TAB_WINDOW_ROWS * row_height + else: + max_rows_height = float('inf') win_width, win_height = self.window.get_size() max_win_height = round(self.MAX_TAB_WINDOW_HEIGHT_PERCENTAGE * win_height) From e0b946748da2fbec427b459290002b62bf99d702 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 1 Nov 2017 21:56:17 +0800 Subject: [PATCH 35/41] log tweak --- controlyourtabs/log.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/controlyourtabs/log.py b/controlyourtabs/log.py index 70d935a..befdb9f 100644 --- a/controlyourtabs/log.py +++ b/controlyourtabs/log.py @@ -53,7 +53,7 @@ if name in NAMES_TO_LEVELS: output_level = NAMES_TO_LEVELS[name] -# set by query(), used by prefix() +# set by query(), used by name() last_queried_level = None @@ -97,14 +97,13 @@ def query(log_level): return highest(log_level) <= output_level -def prefix(log_level=None): +def name(log_level=None): if log_level is None: log_level = last_queried_level - name = LEVELS_TO_NAMES[highest(log_level)] if log_level is not None else 'unknown' - - return '[' + name + '] ' + return LEVELS_TO_NAMES[highest(log_level)] if log_level is not None else 'unknown' def format(message, *args): - return prefix() + (message % tuple(debug_str(arg) for arg in args)) + msg = message % tuple(debug_str(arg) for arg in args) + return '[%s] %s' % (name(), msg) From 7d96d436f8fd21ff4b421ac2cbe986b4501b8f8d Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Wed, 1 Nov 2017 22:01:32 +0800 Subject: [PATCH 36/41] double quotes --- controlyourtabs/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controlyourtabs/log.py b/controlyourtabs/log.py index befdb9f..b099bc2 100644 --- a/controlyourtabs/log.py +++ b/controlyourtabs/log.py @@ -105,5 +105,5 @@ def name(log_level=None): def format(message, *args): msg = message % tuple(debug_str(arg) for arg in args) - return '[%s] %s' % (name(), msg) + return "[%s] %s" % (name(), msg) From 93aade8d9c4600b6e34a926745c5713e8e469bb7 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 13 Mar 2018 20:54:48 +0800 Subject: [PATCH 37/41] made pref pane nicer --- controlyourtabs/__init__.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index b93c3b9..226fa2c 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -978,22 +978,21 @@ def do_create_configure_widget(self): Gio.SettingsBindFlags.DEFAULT ) + widget._settings = settings + else: if log.query(log.DEBUG): debug_plugin_message(log.format("no settings")) - label = Gtk.Label.new( + widget = Gtk.Label.new( _("Sorry, no preferences are available for this version of gedit.") ) - widget = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) - widget.add(label) - - widget.set_border_width(5) - - widget._settings = settings + box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) + box.set_border_width(5) + box.add(widget) - return widget + return box # this is a /hack/ From 7f9686ff9ba78a5a1c465fd625d2f4cbd3928033 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 13 Mar 2018 20:58:10 +0800 Subject: [PATCH 38/41] new year --- README.md | 2 +- controlyourtabs.plugin | 2 +- controlyourtabs.plugin.python2 | 2 +- controlyourtabs/__init__.py | 2 +- controlyourtabs/keyinfo.py | 2 +- controlyourtabs/log.py | 2 +- controlyourtabs/tabinfo.py | 2 +- controlyourtabs/tabinfo_pre312.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d53fb01..6aa814b 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Inspired by: ## License -Copyright © 2010-2013, 2017 Jeffery To +Copyright © 2010-2013, 2017-2018 Jeffery To Available under GNU General Public License version 3 diff --git a/controlyourtabs.plugin b/controlyourtabs.plugin index cfd1a5d..fe4282c 100644 --- a/controlyourtabs.plugin +++ b/controlyourtabs.plugin @@ -6,6 +6,6 @@ IAge=3 Name=Control Your Tabs Description=Switch between document tabs using Ctrl+Tab / Ctrl+Shift+Tab and Ctrl+PageUp / Ctrl+PageDown Authors=Jeffery To -Copyright=Copyright © 2010-2013, 2017 Jeffery To +Copyright=Copyright © 2010-2013, 2017-2018 Jeffery To Website=https://github.com/jefferyto/gedit-control-your-tabs Version=0.3.1 diff --git a/controlyourtabs.plugin.python2 b/controlyourtabs.plugin.python2 index 34929da..2a4ba48 100644 --- a/controlyourtabs.plugin.python2 +++ b/controlyourtabs.plugin.python2 @@ -6,6 +6,6 @@ IAge=3 Name=Control Your Tabs Description=Switch between document tabs using Ctrl+Tab / Ctrl+Shift+Tab and Ctrl+PageUp / Ctrl+PageDown Authors=Jeffery To -Copyright=Copyright © 2010-2013, 2017 Jeffery To +Copyright=Copyright © 2010-2013, 2017-2018 Jeffery To Website=https://github.com/jefferyto/gedit-control-your-tabs Version=0.3.1 diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index 226fa2c..c57bcb6 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -3,7 +3,7 @@ # __init__.py # This file is part of Control Your Tabs, a plugin for gedit # -# Copyright (C) 2010-2013, 2017 Jeffery To +# Copyright (C) 2010-2013, 2017-2018 Jeffery To # https://github.com/jefferyto/gedit-control-your-tabs # # This program is free software: you can redistribute it and/or modify diff --git a/controlyourtabs/keyinfo.py b/controlyourtabs/keyinfo.py index a654dba..1531abf 100644 --- a/controlyourtabs/keyinfo.py +++ b/controlyourtabs/keyinfo.py @@ -3,7 +3,7 @@ # keyinfo.py # This file is part of Control Your Tabs, a plugin for gedit # -# Copyright (C) 2010-2013, 2017 Jeffery To +# Copyright (C) 2010-2013, 2017-2018 Jeffery To # https://github.com/jefferyto/gedit-control-your-tabs # # This program is free software: you can redistribute it and/or modify diff --git a/controlyourtabs/log.py b/controlyourtabs/log.py index b099bc2..633046c 100644 --- a/controlyourtabs/log.py +++ b/controlyourtabs/log.py @@ -3,7 +3,7 @@ # log.py # This file is part of Control Your Tabs, a plugin for gedit # -# Copyright (C) 2010-2013, 2017 Jeffery To +# Copyright (C) 2010-2013, 2017-2018 Jeffery To # https://github.com/jefferyto/gedit-control-your-tabs # # This program is free software: you can redistribute it and/or modify diff --git a/controlyourtabs/tabinfo.py b/controlyourtabs/tabinfo.py index e6e5944..a3f913b 100644 --- a/controlyourtabs/tabinfo.py +++ b/controlyourtabs/tabinfo.py @@ -3,7 +3,7 @@ # tabinfo.py # This file is part of Control Your Tabs, a plugin for gedit # -# Copyright (C) 2010-2013, 2017 Jeffery To +# Copyright (C) 2010-2013, 2017-2018 Jeffery To # https://github.com/jefferyto/gedit-control-your-tabs # # This program is free software: you can redistribute it and/or modify diff --git a/controlyourtabs/tabinfo_pre312.py b/controlyourtabs/tabinfo_pre312.py index efb1f40..88b5e72 100644 --- a/controlyourtabs/tabinfo_pre312.py +++ b/controlyourtabs/tabinfo_pre312.py @@ -3,7 +3,7 @@ # tabinfo_pre312.py # This file is part of Control Your Tabs, a plugin for gedit # -# Copyright (C) 2010-2013, 2017 Jeffery To +# Copyright (C) 2010-2013, 2017-2018 Jeffery To # https://github.com/jefferyto/gedit-control-your-tabs # # This program is free software: you can redistribute it and/or modify From 5a192247380f2ef33b88144da2430843c4c45eba Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 13 Mar 2018 21:19:56 +0800 Subject: [PATCH 39/41] tabbar => tab row --- README.md | 6 +++--- controlyourtabs/__init__.py | 2 +- controlyourtabs/locale/gedit-control-your-tabs.pot | 2 +- ...ingsthemselves.gedit.plugins.controlyourtabs.gschema.xml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6aa814b..a9eafac 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ gedit 2 is [v0.1.2][]. Ctrl+Shift+Tab - Switch tabs in most recently used order. * Ctrl+Page Up / - Ctrl+Page Down - Switch tabs in tabbar order. + Ctrl+Page Down - Switch tabs in tab row order. Hold down Ctrl to continue tab switching. Press Esc while switching to cancel and return to the initial tab. @@ -39,10 +39,10 @@ Hold down Ctrl to continue tab switching. Press In gedit 3.4 or later, the plugin supports these preferences: -* `Use tabbar order for Ctrl+Tab / Ctrl+Shift+Tab` - Change +* `Use tab row order for Ctrl+Tab / Ctrl+Shift+Tab` - Change Ctrl+Tab / Ctrl+Shift+Tab to switch tabs in - tabbar order instead of most recently used order. + tab row order instead of most recently used order. ## Development diff --git a/controlyourtabs/__init__.py b/controlyourtabs/__init__.py index c57bcb6..ef9772c 100644 --- a/controlyourtabs/__init__.py +++ b/controlyourtabs/__init__.py @@ -969,7 +969,7 @@ def do_create_configure_widget(self): debug_plugin_message(log.format("have settings")) widget = Gtk.CheckButton.new_with_label( - _("Use tabbar order for Ctrl+Tab / Ctrl+Shift+Tab") + _("Use tab row order for Ctrl+Tab / Ctrl+Shift+Tab") ) settings.bind( diff --git a/controlyourtabs/locale/gedit-control-your-tabs.pot b/controlyourtabs/locale/gedit-control-your-tabs.pot index 8328feb..da84954 100644 --- a/controlyourtabs/locale/gedit-control-your-tabs.pot +++ b/controlyourtabs/locale/gedit-control-your-tabs.pot @@ -21,7 +21,7 @@ msgid "Documents" msgstr "" #: __init__.py:241 -msgid "Use tabbar order for Ctrl+Tab / Ctrl+Shift+Tab" +msgid "Use tab row order for Ctrl+Tab / Ctrl+Shift+Tab" msgstr "" #: __init__.py:247 diff --git a/controlyourtabs/schemas/com.thingsthemselves.gedit.plugins.controlyourtabs.gschema.xml b/controlyourtabs/schemas/com.thingsthemselves.gedit.plugins.controlyourtabs.gschema.xml index dc68539..1e2eb1d 100644 --- a/controlyourtabs/schemas/com.thingsthemselves.gedit.plugins.controlyourtabs.gschema.xml +++ b/controlyourtabs/schemas/com.thingsthemselves.gedit.plugins.controlyourtabs.gschema.xml @@ -3,8 +3,8 @@ false - Use tabbar order - Use tabbar order when switching tabs with Ctrl+Tab / Ctrl+Shift+Tab. + Use tab row order + Use tab row order when switching tabs with Ctrl+Tab / Ctrl+Shift+Tab. From a13c021e2af6cfcf2882f6378dbacf6e54c756d2 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 13 Mar 2018 21:22:47 +0800 Subject: [PATCH 40/41] new version --- Changelog.md | 2 +- README.md | 2 +- controlyourtabs.plugin | 2 +- controlyourtabs.plugin.python2 | 2 +- controlyourtabs/locale/gedit-control-your-tabs.pot | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index c4a057f..14c05ea 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## v0.3.2 (2018-03-13) * Added Ctrl-Esc to cancel tab switching, causes the initial tab (before switching began) to be active again * Prevent all input during tab switching, if the tab switching window is diff --git a/README.md b/README.md index a9eafac..2ba1829 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Switch between document tabs using Ctrl+Tab / Ctrl+Shift+Tab and Ctrl+PageUp / Ctrl+PageDown -v0.3.1 +v0.3.2 All bug reports, feature requests and miscellaneous comments are welcome at the [project issue tracker][]. diff --git a/controlyourtabs.plugin b/controlyourtabs.plugin index fe4282c..3ae8816 100644 --- a/controlyourtabs.plugin +++ b/controlyourtabs.plugin @@ -8,4 +8,4 @@ Description=Switch between document tabs using Ctrl+Tab / Ctrl+Shift+Tab and Ctr Authors=Jeffery To Copyright=Copyright © 2010-2013, 2017-2018 Jeffery To Website=https://github.com/jefferyto/gedit-control-your-tabs -Version=0.3.1 +Version=0.3.2 diff --git a/controlyourtabs.plugin.python2 b/controlyourtabs.plugin.python2 index 2a4ba48..cf903e8 100644 --- a/controlyourtabs.plugin.python2 +++ b/controlyourtabs.plugin.python2 @@ -8,4 +8,4 @@ Description=Switch between document tabs using Ctrl+Tab / Ctrl+Shift+Tab and Ctr Authors=Jeffery To Copyright=Copyright © 2010-2013, 2017-2018 Jeffery To Website=https://github.com/jefferyto/gedit-control-your-tabs -Version=0.3.1 +Version=0.3.2 diff --git a/controlyourtabs/locale/gedit-control-your-tabs.pot b/controlyourtabs/locale/gedit-control-your-tabs.pot index da84954..c1f33b0 100644 --- a/controlyourtabs/locale/gedit-control-your-tabs.pot +++ b/controlyourtabs/locale/gedit-control-your-tabs.pot @@ -1,7 +1,7 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: gedit-control-your-tabs 0.3.1\n" +"Project-Id-Version: gedit-control-your-tabs 0.3.2\n" "POT-Creation-Date: 2017-10-17 03:56+0800\n" "PO-Revision-Date: 2014-12-03 21:27+0800\n" "Last-Translator: Jeffery To \n" From b46dbd190a3fd1848ab2e57d9b0e67f59c8b2554 Mon Sep 17 00:00:00 2001 From: Jeffery To Date: Tue, 13 Mar 2018 21:23:26 +0800 Subject: [PATCH 41/41] refresh .pot --- controlyourtabs/locale/gedit-control-your-tabs.pot | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/controlyourtabs/locale/gedit-control-your-tabs.pot b/controlyourtabs/locale/gedit-control-your-tabs.pot index c1f33b0..8d977b6 100644 --- a/controlyourtabs/locale/gedit-control-your-tabs.pot +++ b/controlyourtabs/locale/gedit-control-your-tabs.pot @@ -2,32 +2,32 @@ msgid "" msgstr "" "Project-Id-Version: gedit-control-your-tabs 0.3.2\n" -"POT-Creation-Date: 2017-10-17 03:56+0800\n" +"POT-Creation-Date: 2018-03-13 21:23+0800\n" "PO-Revision-Date: 2014-12-03 21:27+0800\n" "Last-Translator: Jeffery To \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.0.1\n" +"X-Generator: Poedit 2.0.4\n" "X-Poedit-KeywordsList: _;gettext;gettext_noop\n" "X-Poedit-Basepath: ..\n" "X-Poedit-SearchPath-0: .\n" "X-Poedit-SearchPathExcluded-0: schemas\n" "X-Poedit-SearchPathExcluded-1: utils\n" -#: __init__.py:143 +#: __init__.py:95 msgid "Documents" msgstr "" -#: __init__.py:241 +#: __init__.py:972 msgid "Use tab row order for Ctrl+Tab / Ctrl+Shift+Tab" msgstr "" -#: __init__.py:247 +#: __init__.py:988 msgid "Sorry, no preferences are available for this version of gedit." msgstr "" -#: __init__.py:503 +#: tabinfo.py:74 tabinfo_pre312.py:71 msgid "Read-Only" msgstr ""