diff --git a/Changelog.md b/Changelog.md index 5e274d1..e40f9f6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,13 @@ # Changelog +## 0.2.0 (2019-03-22) +* Restore windows on startup, and save windows for restoring, only for + the primary instance, i.e. when gedit is not in standalone mode +* When restoring windows on startup, reuse the new window/tab (#4) +* Fixed adding "Reopen Closed Window" menu item on platforms with no app + menu or menu bar (#5) +* Added Russian translation (#6, thanks Habetdin!) + ## 0.1.2 (2018-03-13) * Fixed auto-closing gedit if there are no windows to restore (#1) * Fixed (potentially) affecting the translations of other plugins diff --git a/README.md b/README.md index 5c47b28..c415eba 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Reopen closed windows and optionally restore windows between sessions -0.1.2 +0.2.0 All bug reports, feature requests and miscellaneous comments are welcome at the [project issue tracker][]. @@ -52,7 +52,10 @@ i.e. only unsaved or blank documents, will not be reopenable. open in the previous session will be reopened when gedit is started again. (Default: Disabled) -## Development +## Contributing + +Please base changes on, and open pull requests against, the `develop` +branch. The code in `ex-mortis/utils` comes from [python-gtk-utils][]; changes should ideally be contributed to that project, then pulled back into @@ -66,7 +69,7 @@ Inspired by: ## License -Copyright © 2017-2018 Jeffery To +Copyright © 2017-2019 Jeffery To Available under GNU General Public License version 3 diff --git a/ex-mortis.plugin b/ex-mortis.plugin index 1a30904..84f2eaa 100644 --- a/ex-mortis.plugin +++ b/ex-mortis.plugin @@ -4,8 +4,9 @@ Module=ex-mortis IAge=3 Name=Ex-Mortis Description=Reopen closed windows and optionally restore windows between sessions +Description[ru]=Переоткрывает закрытые окна и восстанавливает окна между сессиями Icon=window-new Authors=Jeffery To -Copyright=Copyright © 2017-2018 Jeffery To +Copyright=Copyright © 2017-2019 Jeffery To Website=https://github.com/jefferyto/gedit-ex-mortis -Version=0.1.2 +Version=0.2.0 diff --git a/ex-mortis/__init__.py b/ex-mortis/__init__.py index 27c5d64..1054cf7 100644 --- a/ex-mortis/__init__.py +++ b/ex-mortis/__init__.py @@ -3,7 +3,7 @@ # __init__.py # This file is part of Ex-Mortis, a plugin for gedit # -# Copyright (C) 2017-2018 Jeffery To +# Copyright (C) 2017-2019 Jeffery To # https://github.com/jefferyto/gedit-ex-mortis # # This program is free software: you can redistribute it and/or modify @@ -61,8 +61,9 @@ def do_activate(self): Gedit.debug_plugin_message(log.format("")) app = self.app + is_primary = not (app.get_flags() & Gio.ApplicationFlags.NON_UNIQUE) window_manager = ExMortisWindowManager() - settings = ExMortisSettings() + settings = ExMortisSettings(is_primary) # app connect_handlers( @@ -102,6 +103,8 @@ def do_activate(self): 'app.reopen-closed-window', ['N'] ) menu_ext = self.extend_menu('app-commands-section') + if not menu_ext: + menu_ext = self.extend_menu('file-section') menu_item = Gio.MenuItem.new( _("Reopen Closed _Window"), 'app.reopen-closed-window' ) @@ -133,7 +136,7 @@ def do_activate(self): # windows windows = app.get_main_windows() - self.restore_windows( + self.handle_restore_data( window_manager, settings, settings.restore_between_sessions and not windows ) @@ -220,7 +223,7 @@ def setup_window(self, window, is_existing=False): window_manager.track_window(window) - self.setup_restore_window(window) + self.setup_restore_window(window_manager, window) if self.is_saving_window_states(): self.bind_window_settings(window_manager, settings, window) @@ -438,6 +441,8 @@ def do_create_configure_widget(self): if log.query(log.INFO): Gedit.debug_plugin_message(log.format("")) + app = Gedit.App.get_default() + is_primary = not (app.get_flags() & Gio.ApplicationFlags.NON_UNIQUE) settings = ExMortisSettings() if settings.can_save: @@ -445,6 +450,9 @@ def do_create_configure_widget(self): _("Restore windows between sessions") ) + if not is_primary: + widget.set_sensitive(False) + create_bindings( self, settings, widget, {'restore_between_sessions': 'active'}, diff --git a/ex-mortis/closingmixin.py b/ex-mortis/closingmixin.py index 16b73f5..bc7f70f 100644 --- a/ex-mortis/closingmixin.py +++ b/ex-mortis/closingmixin.py @@ -3,7 +3,7 @@ # closingmixin.py # This file is part of Ex-Mortis, a plugin for gedit # -# Copyright (C) 2017-2018 Jeffery To +# Copyright (C) 2017-2019 Jeffery To # https://github.com/jefferyto/gedit-ex-mortis # # This program is free software: you can redistribute it and/or modify diff --git a/ex-mortis/existingmixin.py b/ex-mortis/existingmixin.py index dad5a71..63b0f08 100644 --- a/ex-mortis/existingmixin.py +++ b/ex-mortis/existingmixin.py @@ -3,7 +3,7 @@ # existingmixin.py # This file is part of Ex-Mortis, a plugin for gedit # -# Copyright (C) 2017-2018 Jeffery To +# Copyright (C) 2017-2019 Jeffery To # https://github.com/jefferyto/gedit-ex-mortis # # This program is free software: you can redistribute it and/or modify diff --git a/ex-mortis/locale/gedit-ex-mortis.pot b/ex-mortis/locale/gedit-ex-mortis.pot index 8cc1db8..7ab1e4b 100644 --- a/ex-mortis/locale/gedit-ex-mortis.pot +++ b/ex-mortis/locale/gedit-ex-mortis.pot @@ -1,30 +1,30 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: gedit-ex-mortis 0.1.2\n" -"POT-Creation-Date: 2018-03-13 21:31+0800\n" +"Project-Id-Version: gedit-ex-mortis 0.2.0\n" +"POT-Creation-Date: 2019-03-22 04:12+0800\n" "PO-Revision-Date: 2017-10-13 22:50+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.4\n" +"X-Generator: Poedit 2.1.1\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:106 +#: __init__.py:109 msgid "Reopen Closed _Window" msgstr "" -#: __init__.py:445 existingmixin.py:87 +#: __init__.py:450 existingmixin.py:87 msgid "Restore windows between sessions" msgstr "" -#: __init__.py:458 +#: __init__.py:466 msgid "Could not load settings schema" msgstr "" diff --git a/ex-mortis/locale/ru/LC_MESSAGES/gedit-ex-mortis.mo b/ex-mortis/locale/ru/LC_MESSAGES/gedit-ex-mortis.mo new file mode 100644 index 0000000..e98543e Binary files /dev/null and b/ex-mortis/locale/ru/LC_MESSAGES/gedit-ex-mortis.mo differ diff --git a/ex-mortis/locale/ru/LC_MESSAGES/gedit-ex-mortis.po b/ex-mortis/locale/ru/LC_MESSAGES/gedit-ex-mortis.po new file mode 100644 index 0000000..2723376 --- /dev/null +++ b/ex-mortis/locale/ru/LC_MESSAGES/gedit-ex-mortis.po @@ -0,0 +1,58 @@ +msgid "" +msgstr "" +"Project-Id-Version: gedit-ex-mortis 0.2.0\n" +"POT-Creation-Date: 2019-03-22 04:12+0800\n" +"PO-Revision-Date: 2019-03-22 04:15+0800\n" +"Last-Translator: Habetdin\n" +"Language-Team: Russian\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.1.1\n" + +#: __init__.py:109 +msgid "Reopen Closed _Window" +msgstr "Пе_реоткрыть закрытое окно" + +#: __init__.py:450 existingmixin.py:87 +msgid "Restore windows between sessions" +msgstr "Восстанавливать окна между сессиями" + +#: __init__.py:466 +msgid "Could not load settings schema" +msgstr "Ошибка при загрузке схемы настроек" + +#: existingmixin.py:67 +msgid "_Quit" +msgstr "З_авершить" + +#: existingmixin.py:68 +msgid "_Ignore" +msgstr "И_гнорировать" + +#: existingmixin.py:77 +msgid "" +"This window cannot be reopened if closed. Restart gedit to fully enable Ex-" +"Mortis." +msgstr "" +"Это окно не может быть восстановлено после закрытия. Перезапустите gedit для " +"полноценной активации Ex-Mortis." + +#: existingmixin.py:88 +msgid "Application menu" +msgstr "меню приложения" + +#: existingmixin.py:88 +msgid "File menu" +msgstr "меню Файл" + +#: existingmixin.py:89 +#, python-brace-format +msgid "" +"To restore this window, enable \"{pref_name}\" in Ex-Mortis' preferences, " +"and quit gedit by selecting Quit in the {menu_name} or in this message." +msgstr "" +"Для восстановления данного окна включите в настройках Ex-Mortis " +"\"{pref_name}\" и выйдите из gedit, выбрав Завершить в {menu_name} или в " +"этом сообщении." diff --git a/ex-mortis/log.py b/ex-mortis/log.py index fd6959d..24ebf87 100644 --- a/ex-mortis/log.py +++ b/ex-mortis/log.py @@ -3,7 +3,7 @@ # log.py # This file is part of Ex-Mortis, a plugin for gedit # -# Copyright (C) 2017-2018 Jeffery To +# Copyright (C) 2017-2019 Jeffery To # https://github.com/jefferyto/gedit-ex-mortis # # This program is free software: you can redistribute it and/or modify @@ -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 diff --git a/ex-mortis/quittingmixin.py b/ex-mortis/quittingmixin.py index 4767810..a4df476 100644 --- a/ex-mortis/quittingmixin.py +++ b/ex-mortis/quittingmixin.py @@ -3,7 +3,7 @@ # quittingmixin.py # This file is part of Ex-Mortis, a plugin for gedit # -# Copyright (C) 2017-2018 Jeffery To +# Copyright (C) 2017-2019 Jeffery To # https://github.com/jefferyto/gedit-ex-mortis # # This program is free software: you can redistribute it and/or modify @@ -32,6 +32,7 @@ def do_activate_quitting(self, is_saving_window_states): self._window_ids = {} if is_saving_window_states else None self._quitting = None + self._restore_states = None self._restore_windows = None def do_deactivate_quitting(self): @@ -42,6 +43,7 @@ def do_deactivate_quitting(self): self._window_ids = None self._quitting = None + self._restore_states = None self._restore_windows = None @@ -304,12 +306,11 @@ def end_quitting(self, settings, do_save): # restoring - def restore_windows(self, window_manager, settings, do_restore): + def handle_restore_data(self, window_manager, settings, do_restore): if log.query(log.INFO): Gedit.debug_plugin_message(log.format("do_restore=%s", do_restore)) states = [] - windows = [] for window_id in list(settings.restore_windows): if do_restore: @@ -339,48 +340,44 @@ def restore_windows(self, window_manager, settings, do_restore): settings.remove_window(window_id) - if do_restore and states: - if log.query(log.MESSAGE): - Gedit.debug_plugin_message(log.format("restoring %s windows", len(states))) - - screen_width = window_manager.get_screen_width() - screen_height = window_manager.get_screen_height() - - for state in states: - # when gedit goes to open the first blank tab, - # it tries to find an active window first - # but it tests for windows in the current screen/workspace/viewport - # which is in part based on the size of the window - # so we need to shrink our windows here to fit the screen, - # otherwise gedit will think they are in a different viewport - # (if the window is too large for the screen, - # the window manager will probably resize the window to fit anyway) - if state.width > screen_width: - state.side_panel_size = round((state.side_panel_size / state.width) * screen_width) - state.width = screen_width - if state.height > screen_height: - state.bottom_panel_size = round((state.bottom_panel_size / state.height) * screen_height) - state.height = screen_height - - windows.append(window_manager.open_new_window_with_window_state(state)) - - self._restore_windows = {} - - # the window manager can choose to make another window active - # rather than the active window at the end of this process - # so listen for new tab on all windows - for window, state in zip(windows, states): - self.setup_restore_window(window, state) - - elif not do_restore: + if not do_restore: if log.query(log.MESSAGE): Gedit.debug_plugin_message(log.format("not restoring windows")) - else: + return + + if not states: if log.query(log.MESSAGE): Gedit.debug_plugin_message(log.format("no windows to restore")) - def setup_restore_window(self, window, state=None): + return + + if log.query(log.MESSAGE): + Gedit.debug_plugin_message(log.format("will restore %s windows", len(states))) + + screen_width = window_manager.get_screen_width() + screen_height = window_manager.get_screen_height() + + for state in states: + # when gedit goes to open the first blank tab, + # it tries to find an active window first + # but it tests for windows in the current screen/workspace/viewport + # which is in part based on the size of the window + # so we need to shrink our windows here to fit the screen, + # otherwise gedit will think they are in a different viewport + # (if the window is too large for the screen, + # the window manager will probably resize the window to fit anyway) + if state.width > screen_width: + state.side_panel_size = round((state.side_panel_size / state.width) * screen_width) + state.width = screen_width + if state.height > screen_height: + state.bottom_panel_size = round((state.bottom_panel_size / state.height) * screen_height) + state.height = screen_height + + self._restore_states = states + self._restore_windows = {} + + def setup_restore_window(self, window_manager, window): if log.query(log.INFO): Gedit.debug_plugin_message(log.format("%s", window)) @@ -400,7 +397,7 @@ def setup_restore_window(self, window, state=None): Gedit.debug_plugin_message(log.format("setting up restore window")) self._restore_windows[window] = window.connect( - 'tab-added', self.on_restore_window_tab_added, state + 'tab-added', self.on_restore_window_tab_added, window_manager ) def teardown_restore_windows(self): @@ -441,32 +438,51 @@ def teardown_restore_window(self, window): del self._restore_windows[window] - def on_restore_window_tab_added(self, window, tab, state): + def on_restore_window_tab_added(self, window, tab, window_manager): if log.query(log.INFO): Gedit.debug_plugin_message(log.format("%s, %s", window, tab)) - if (tab.get_document().is_untouched() - and tab.get_state() == Gedit.TabState.STATE_NORMAL): - if log.query(log.DEBUG): - Gedit.debug_plugin_message(log.format("closing untouched tab")) + self.teardown_restore_windows() - def close_tab(): - window.close_tab(tab) + def do_restore_windows(): + self.restore_windows(window_manager, window, tab) - if not window.get_active_tab(): - window.close() + return False - elif state: - state.apply_active_uri(window) - state.apply_notebook_widths(window) + GLib.idle_add(do_restore_windows) - return False + def restore_windows(self, window_manager, window, tab): + if log.query(log.INFO): + Gedit.debug_plugin_message(log.format("%s, %s", window, tab)) - GLib.idle_add(close_tab) + active_tab = window.get_active_tab() + num_tabs = len(active_tab.get_parent().get_children()) + is_single_empty_tab = ( + num_tabs == 1 + and tab == active_tab + and tab.get_document().is_untouched() + and tab.get_state() == Gedit.TabState.STATE_NORMAL + ) - else: - if log.query(log.DEBUG): - Gedit.debug_plugin_message(log.format("new tab is not untouched")) + # if there is only one empty tab, let gedit reuse it when opening files + # otherwise, open a new tab to be (re)used + # this protects the new tab that was added if gedit was run with + # --new-document and one or more files to open - self.teardown_restore_windows() + if log.query(log.DEBUG): + Gedit.debug_plugin_message(log.format("is_single_empty_tab=%s", is_single_empty_tab)) + + if not is_single_empty_tab: + window.create_tab(True) + + state = self._restore_states.pop() + window_manager.import_window_state(window, state) + + for state in self._restore_states: + window_manager.open_new_window_with_window_state(state) + + self._restore_states = None + if not is_single_empty_tab: + window.set_active_tab(active_tab) + window.present() diff --git a/ex-mortis/settings.py b/ex-mortis/settings.py index 6e7eb34..a74c945 100644 --- a/ex-mortis/settings.py +++ b/ex-mortis/settings.py @@ -3,7 +3,7 @@ # settings.py # This file is part of Ex-Mortis, a plugin for gedit # -# Copyright (C) 2017-2018 Jeffery To +# Copyright (C) 2017-2019 Jeffery To # https://github.com/jefferyto/gedit-ex-mortis # # This program is free software: you can redistribute it and/or modify @@ -36,11 +36,11 @@ class ExMortisSettings(GObject.Object): restore_windows = GObject.Property(type=GObject.GType.from_name('GStrv'), default=[]) - def __init__(self): + def __init__(self, is_enabled=True): GObject.Object.__init__(self) if log.query(log.INFO): - Gedit.debug_plugin_message(log.format("")) + Gedit.debug_plugin_message(log.format("is_enabled=%s", is_enabled)) try: schema_source = Gio.SettingsSchemaSource.new_from_directory( @@ -55,11 +55,14 @@ def __init__(self): schema_source = None - settings = get_settings( - schema_source, - 'com.thingsthemselves.gedit.plugins.ex-mortis', - '/com/thingsthemselves/gedit/plugins/ex-mortis/' - ) + if is_enabled: + settings = get_settings( + schema_source, + 'com.thingsthemselves.gedit.plugins.ex-mortis', + '/com/thingsthemselves/gedit/plugins/ex-mortis/' + ) + else: + settings = None if settings: try: diff --git a/ex-mortis/windowmanager.py b/ex-mortis/windowmanager.py index 9cd161e..6191690 100644 --- a/ex-mortis/windowmanager.py +++ b/ex-mortis/windowmanager.py @@ -3,7 +3,7 @@ # windowmanager.py # This file is part of Ex-Mortis, a plugin for gedit # -# Copyright (C) 2017-2018 Jeffery To +# Copyright (C) 2017-2019 Jeffery To # https://github.com/jefferyto/gedit-ex-mortis # # This program is free software: you can redistribute it and/or modify diff --git a/ex-mortis/windowstate.py b/ex-mortis/windowstate.py index b7da88a..900e748 100644 --- a/ex-mortis/windowstate.py +++ b/ex-mortis/windowstate.py @@ -3,7 +3,7 @@ # windowstate.py # This file is part of Ex-Mortis, a plugin for gedit # -# Copyright (C) 2017-2018 Jeffery To +# Copyright (C) 2017-2019 Jeffery To # https://github.com/jefferyto/gedit-ex-mortis # # This program is free software: you can redistribute it and/or modify