From 8c07f1ac4150d3c37888a281f2e6b031a6dc41b0 Mon Sep 17 00:00:00 2001 From: Stephen Boddy Date: Wed, 30 Oct 2013 17:07:23 +0100 Subject: [PATCH 1/7] Allow the UUID to return a uuid object based on a provided UUID string --- terminatorlib/util.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/terminatorlib/util.py b/terminatorlib/util.py index c9143df2..8cb1b554 100755 --- a/terminatorlib/util.py +++ b/terminatorlib/util.py @@ -277,8 +277,10 @@ def enumerate_descendants(parent): len(terminals), parent)) return(containers, terminals) -def make_uuid(): +def make_uuid(str_uuid=None): """Generate a UUID for an object""" + if str_uuid: + return uuid.UUID(str_uuid) return uuid.uuid4() def inject_uuid(target): From f0e3cb7fe5ff7ef1fdc836dcbfbe2f1adda8b654 Mon Sep 17 00:00:00 2001 From: Stephen Boddy Date: Wed, 30 Oct 2013 17:12:00 +0100 Subject: [PATCH 2/7] Terminals now save and load their UUID to and from the layout --- terminatorlib/terminal.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index 7929d98f..690630f1 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -1490,6 +1490,7 @@ class Terminal(gtk.VBox): title = self.titlebar.get_custom_string() if title: layout['title'] = title + layout['uuid'] = self.uuid name = 'terminal%d' % count count = count + 1 global_layout[name] = layout @@ -1511,6 +1512,8 @@ class Terminal(gtk.VBox): self.titlebar.set_custom_string(layout['title']) if layout.has_key('directory') and layout['directory'] != '': self.directory = layout['directory'] + if layout.has_key('uuid') and layout['uuid'] != '': + self.uuid = make_uuid(layout['uuid']) def scroll_by_page(self, pages): """Scroll up or down in pages""" From ef57c5a5f69600330767279882c373b8fe47e374 Mon Sep 17 00:00:00 2001 From: Stephen Boddy Date: Thu, 31 Oct 2013 17:39:16 +0100 Subject: [PATCH 3/7] Missed import of util function in previous commit --- terminatorlib/terminal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index 690630f1..ce42dbed 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -15,7 +15,7 @@ import pango import subprocess import urllib -from util import dbg, err, gerr, spawn_new_terminator +from util import dbg, err, gerr, spawn_new_terminator, make_uuid import util from config import Config from cwd import get_default_cwd From d265dd57243ec205830119d85f6d24feda792b76 Mon Sep 17 00:00:00 2001 From: Stephen Boddy Date: Thu, 31 Oct 2013 18:50:21 +0100 Subject: [PATCH 4/7] Active Window saved to layouts, and made active when loaded --- terminatorlib/container.py | 6 ++++++ terminatorlib/terminator.py | 5 +++++ terminatorlib/window.py | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/terminatorlib/container.py b/terminatorlib/container.py index cecd8588..aabd454a 100755 --- a/terminatorlib/container.py +++ b/terminatorlib/container.py @@ -282,6 +282,12 @@ the %s will also close all terminals within it.') % (reqtype, reqtype)) if len(labels) > 0: layout['labels'] = labels + if mytype == 'Window': + if self.uuid == self.terminator.last_active_window: + layout['last_active_window'] = True + else: + layout['last_active_window'] = False + name = 'child%d' % count count = count + 1 diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index f66e0602..add1ad68 100755 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -34,6 +34,7 @@ class Terminator(Borg): debug_address = None doing_layout = None + last_active_window = None groupsend = None groupsend_type = {'all':0, 'group':1, 'off':2} @@ -292,6 +293,10 @@ class Terminator(Borg): """Layout operations have finished, record that fact""" self.doing_layout = False + for window in self.windows: + if window.uuid == self.last_active_window: + window.show() + for terminal in self.terminals: if not terminal.pid: terminal.spawn_child() diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 0f5ab34f..14829191 100755 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -239,6 +239,7 @@ class Window(Container, gtk.Window): def on_focus_in(self, window, event): """Focus has entered the window""" self.set_urgency_hint(False) + self.terminator.last_active_window = self.uuid # FIXME: Cause the terminal titlebars to update here def is_child_notebook(self): @@ -865,6 +866,9 @@ class Window(Container, gtk.Window): self.get_children()[0].create_layout(child) + if layout.has_key('last_active_window') and layout['last_active_window'] == 'True': + self.terminator.last_active_window = self.uuid + class WindowTitle(object): """Class to handle the setting of the window title""" From e66a522555c34ca2ff3f3a0e24c106e7598fb4f8 Mon Sep 17 00:00:00 2001 From: Stephen Boddy Date: Fri, 15 Nov 2013 13:21:24 +0100 Subject: [PATCH 5/7] Cleanup the describe_layout labels code to make some future changes easier to add in seperate commits --- terminatorlib/container.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/terminatorlib/container.py b/terminatorlib/container.py index aabd454a..0c47e933 100755 --- a/terminatorlib/container.py +++ b/terminatorlib/container.py @@ -263,6 +263,7 @@ the %s will also close all terminals within it.') % (reqtype, reqtype)) if hasattr(self, 'isfullscreen'): layout['fullscreen'] = self.isfullscreen + if hasattr(self, 'ratio'): layout['ratio'] = self.ratio @@ -272,16 +273,15 @@ the %s will also close all terminals within it.') % (reqtype, reqtype)) if hasattr(self, 'title'): layout['title'] = self.title.text - labels = [] if mytype == 'Notebook': + labels = [] for tabnum in xrange(0, self.get_n_pages()): page = self.get_nth_page(tabnum) label = self.get_tab_label(page) labels.append(label.get_custom_label()) - layout['active_page'] = self.get_current_page() - if len(labels) > 0: layout['labels'] = labels - + layout['active_page'] = self.get_current_page() + if mytype == 'Window': if self.uuid == self.terminator.last_active_window: layout['last_active_window'] = True From 0272c78739438677c8d556b4a52aab163e9798c3 Mon Sep 17 00:00:00 2001 From: Stephen Boddy Date: Tue, 19 Nov 2013 12:46:11 +0100 Subject: [PATCH 6/7] Add layout save and load of last active term for windows without tabs --- terminatorlib/container.py | 5 ++++- terminatorlib/terminal.py | 8 +++++++- terminatorlib/terminator.py | 15 +++++++++++++-- terminatorlib/window.py | 19 +++++++++++++++++-- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/terminatorlib/container.py b/terminatorlib/container.py index 0c47e933..5630c910 100755 --- a/terminatorlib/container.py +++ b/terminatorlib/container.py @@ -281,7 +281,10 @@ the %s will also close all terminals within it.') % (reqtype, reqtype)) labels.append(label.get_custom_label()) layout['labels'] = labels layout['active_page'] = self.get_current_page() - + else: + if hasattr(self, 'last_active_term') and self.last_active_term is not None: + layout['last_active_term'] = self.last_active_term + if mytype == 'Window': if self.uuid == self.terminator.last_active_window: layout['last_active_window'] = True diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index ce42dbed..a31558eb 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -1088,7 +1088,13 @@ class Terminal(gtk.VBox): self.vte.set_colors(self.fgcolor_active, self.bgcolor, self.palette_active) self.set_cursor_color() - self.terminator.last_focused_term = self + if not self.terminator.doing_layout: + self.terminator.last_focused_term = self + if self.get_toplevel().is_child_notebook(): + # TODO: Will need some code for the tabs active terms to work + self.get_toplevel().last_active_term = None + else: + self.get_toplevel().last_active_term = self.uuid self.emit('focus-in') def on_vte_focus_out(self, _widget, _event): diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index add1ad68..22565d4f 100755 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -293,14 +293,25 @@ class Terminator(Borg): """Layout operations have finished, record that fact""" self.doing_layout = False + window_last_active_term_mapping={} for window in self.windows: - if window.uuid == self.last_active_window: - window.show() + # TODO: Will need some code for the tabs active terms to work + window_last_active_term_mapping[window]=copy.deepcopy(window.last_active_term) for terminal in self.terminals: if not terminal.pid: terminal.spawn_child() + for window in self.windows: + if window.last_active_term: + # TODO: Will need some code for the tabs active terms to work + term = self.find_terminal_by_uuid(window_last_active_term_mapping[window].urn) + term.ensure_visible_and_focussed() + + for window in self.windows: + if window.uuid == self.last_active_window: + window.show() + def reconfigure(self): """Update configuration for the whole application""" diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 14829191..2cb6f67e 100755 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -5,12 +5,13 @@ import copy import time +import uuid import pygtk pygtk.require('2.0') import gobject import gtk -from util import dbg, err +from util import dbg, err, make_uuid import util from translation import _ from version import APP_NAME @@ -38,6 +39,7 @@ class Window(Container, gtk.Window): position = None ignore_startup_show = None set_pos_by_ratio = None + last_active_term = None zoom_data = None @@ -239,7 +241,17 @@ class Window(Container, gtk.Window): def on_focus_in(self, window, event): """Focus has entered the window""" self.set_urgency_hint(False) - self.terminator.last_active_window = self.uuid + term = None + if self.is_child_notebook(): + # TODO: Will need some code for the tabs active terms to work + pass + else: + if isinstance(self.last_active_term, uuid.UUID): + term = self.terminator.find_terminal_by_uuid(self.last_active_term.urn) + if term: + gobject.idle_add(term.ensure_visible_and_focussed) + if not self.terminator.doing_layout: + self.terminator.last_active_window = self.uuid # FIXME: Cause the terminal titlebars to update here def is_child_notebook(self): @@ -866,6 +878,9 @@ class Window(Container, gtk.Window): self.get_children()[0].create_layout(child) + if layout.has_key('last_active_term') and layout['last_active_term'] not in ['', None]: + self.last_active_term = make_uuid(layout['last_active_term']) + if layout.has_key('last_active_window') and layout['last_active_window'] == 'True': self.terminator.last_active_window = self.uuid From 581b6eeb928a5775f4c838753f5ae61db8f18124 Mon Sep 17 00:00:00 2001 From: Stephen Boddy Date: Wed, 18 Dec 2013 18:06:59 +0100 Subject: [PATCH 7/7] Add layout save and load of active term for tabs, remember active term on switching tabs, plus more. Too inter-dependant to split into smaller commits. --- terminatorlib/container.py | 5 +- terminatorlib/notebook.py | 103 +++++++++++++++++++++++++++++++----- terminatorlib/terminal.py | 17 +++--- terminatorlib/terminator.py | 47 +++++++++++++--- 4 files changed, 142 insertions(+), 30 deletions(-) diff --git a/terminatorlib/container.py b/terminatorlib/container.py index 5630c910..ce5f7243 100755 --- a/terminatorlib/container.py +++ b/terminatorlib/container.py @@ -275,16 +275,19 @@ the %s will also close all terminals within it.') % (reqtype, reqtype)) if mytype == 'Notebook': labels = [] + last_active_term = [] for tabnum in xrange(0, self.get_n_pages()): page = self.get_nth_page(tabnum) label = self.get_tab_label(page) labels.append(label.get_custom_label()) + last_active_term.append(self.last_active_term[self.get_nth_page(tabnum)]) layout['labels'] = labels + layout['last_active_term'] = last_active_term layout['active_page'] = self.get_current_page() else: if hasattr(self, 'last_active_term') and self.last_active_term is not None: layout['last_active_term'] = self.last_active_term - + if mytype == 'Window': if self.uuid == self.terminator.last_active_window: layout['last_active_window'] = True diff --git a/terminatorlib/notebook.py b/terminatorlib/notebook.py index d9ba9018..931bf83a 100755 --- a/terminatorlib/notebook.py +++ b/terminatorlib/notebook.py @@ -12,11 +12,14 @@ from factory import Factory from container import Container from editablelabel import EditableLabel from translation import _ -from util import err, dbg, enumerate_descendants +from util import err, dbg, enumerate_descendants, make_uuid class Notebook(Container, gtk.Notebook): """Class implementing a gtk.Notebook container""" window = None + last_active_term = None + pending_on_tab_switch = None + pending_on_tab_switch_args = None def __init__(self, window): """Class initialiser""" @@ -30,12 +33,16 @@ class Notebook(Container, gtk.Notebook): self.window = window gobject.type_register(Notebook) self.register_signals(Notebook) + self.connect('switch-page', self.deferred_on_tab_switch) self.configure() child = window.get_child() window.remove(child) window.add(self) self.newtab(widget=child) + if window.last_active_term: + self.set_last_active_term(window.last_active_term) + window.last_active_term = None self.show_all() @@ -63,6 +70,7 @@ class Notebook(Container, gtk.Notebook): style.xthickness = 0 style.ythickness = 0 self.modify_style(style) + self.last_active_term = {} def create_layout(self, layout): """Apply layout configuration""" @@ -117,10 +125,14 @@ class Notebook(Container, gtk.Notebook): label = self.get_tab_label(page) label.set_custom_label(labeltext) page.create_layout(children[child_key]) + + if layout.get('last_active_term', None): + self.last_active_term[page] = make_uuid(layout['last_active_term'][num]) num = num + 1 if layout.has_key('active_page'): - self.set_current_page(int(layout['active_page'])) + # Need to do it later, or layout changes result + gobject.idle_add(self.set_current_page, int(layout['active_page'])) else: self.set_current_page(0) @@ -167,12 +179,13 @@ class Notebook(Container, gtk.Notebook): self.set_current_page(page_num) self.show_all() - terminal.grab_focus() while gtk.events_pending(): gtk.main_iteration_do(False) self.get_toplevel().set_pos_by_ratio = False + gobject.idle_add(terminal.ensure_visible_and_focussed) + def add(self, widget, metadata=None): """Add a widget to the container""" dbg('adding a new tab') @@ -268,15 +281,22 @@ class Notebook(Container, gtk.Notebook): dbg('inserting page at position: %s' % tabpos) self.insert_page(widget, None, tabpos) - self.set_tab_label(widget, label) - self.set_tab_label_packing(widget, not self.config['scroll_tabbar'], - not self.config['scroll_tabbar'], - gtk.PACK_START) + child_widgets = [widget] + child_widgets .extend(enumerate_descendants(widget)) + term_widget = None + for term_widget in child_widgets: + if maker.isinstance(term_widget, 'Terminal'): + self.set_last_active_term(term_widget.uuid) + self.set_tab_label(term_widget, label) + self.set_tab_label_packing(term_widget, not self.config['scroll_tabbar'], + not self.config['scroll_tabbar'], + gtk.PACK_START) + break self.set_tab_reorderable(widget, True) self.set_current_page(tabpos) self.show_all() - if maker.isinstance(widget, 'Terminal'): + if maker.isinstance(term_widget, 'Terminal'): widget.grab_focus() def wrapcloseterm(self, widget): @@ -311,6 +331,7 @@ class Notebook(Container, gtk.Notebook): if maker.isinstance(child, 'Terminal'): dbg('Notebook::closetab: child is a single Terminal') + del nb.last_active_term[child] child.close() # FIXME: We only do this del and return here to avoid removing the # page below, which child.close() implicitly does @@ -318,9 +339,7 @@ class Notebook(Container, gtk.Notebook): return elif maker.isinstance(child, 'Container'): dbg('Notebook::closetab: child is a Container') - dialog = self.construct_confirm_close(self.window, _('tab')) - result = dialog.run() - dialog.destroy() + result = self.construct_confirm_close(self.window, _('tab')) if result == gtk.RESPONSE_ACCEPT: containers = None @@ -340,9 +359,6 @@ class Notebook(Container, gtk.Notebook): err('Notebook::closetab: child is unknown type %s' % child) return - nb.remove_page(tabnum) - del(label) - def resizeterm(self, widget, keyname): """Handle a keyboard event requesting a terminal resize""" raise NotImplementedError('resizeterm') @@ -403,6 +419,65 @@ class Notebook(Container, gtk.Notebook): terms = parent.get_visible_terminals() terms.keys()[-1].grab_focus() + def page_num_descendant(self, widget): + """Find the tabnum of the tab containing a widget at any level""" + tabnum = self.page_num(widget) + dbg("widget is direct child if not equal -1 - tabnum: %d" % tabnum) + while tabnum == -1 and widget.get_parent(): + widget = widget.get_parent() + tabnum = self.page_num(widget) + dbg("found tabnum containing widget: %d" % tabnum) + return tabnum + + def set_last_active_term(self, uuid): + """Set the last active term for uuid""" + widget = self.terminator.find_terminal_by_uuid(uuid.urn) + if not widget: + err("Cannot find terminal with uuid: %s, so cannot make it active" % (uuid.urn)) + return + tabnum = self.page_num_descendant(widget) + if tabnum == -1: + err("No tabnum found for terminal with uuid: %s" % (uuid.urn)) + return + nth_page = self.get_nth_page(tabnum) + self.last_active_term[nth_page] = uuid + + def clean_last_active_term(self): + """Clean up old entries in last_active_term""" + if self.terminator.doing_layout == True: + return + last_active_term = {} + for tabnum in xrange(0, self.get_n_pages()): + nth_page = self.get_nth_page(tabnum) + if nth_page in self.last_active_term: + last_active_term[nth_page] = self.last_active_term[nth_page] + self.last_active_term = last_active_term + + def deferred_on_tab_switch(self, notebook, page, page_num, data=None): + """Prime a single idle tab switch signal, using the most recent set of params""" + tabs_last_active_term = self.last_active_term.get(self.get_nth_page(page_num), None) + data = {'tabs_last_active_term':tabs_last_active_term} + + self.pending_on_tab_switch_args = (notebook, page, page_num, data) + if self.pending_on_tab_switch == True: + return + gobject.idle_add(self.do_deferred_on_tab_switch) + self.pending_on_tab_switch = True + + def do_deferred_on_tab_switch(self): + """Perform the latest tab switch signal, and resetting the pending flag""" + self.on_tab_switch(*self.pending_on_tab_switch_args) + self.pending_on_tab_switch = False + self.pending_on_tab_switch_args = None + + def on_tab_switch(self, notebook, page, page_num, data=None): + """Do the real work for a tab switch""" + tabs_last_active_term = data['tabs_last_active_term'] + if tabs_last_active_term: + term = self.terminator.find_terminal_by_uuid(tabs_last_active_term.urn) + gobject.idle_add(term.ensure_visible_and_focussed) + return True + class TabLabel(gtk.HBox): """Class implementing a label widget for Notebook tabs""" notebook = None diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index a31558eb..aa9c86f8 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -1069,13 +1069,12 @@ class Terminal(gtk.VBox): maker = Factory() if maker.isinstance(topchild, 'Notebook'): - prevtmp = None - tmp = self.get_parent() - while tmp != topchild: - prevtmp = tmp - tmp = tmp.get_parent() - page = topchild.page_num(prevtmp) - topchild.set_current_page(page) + # Find which page number this term is on + tabnum = topchild.page_num_descendant(self) + # If terms page number is not the current one, switch to it + current_page = topchild.get_current_page() + if tabnum != current_page: + topchild.set_current_page(tabnum) self.grab_focus() @@ -1091,7 +1090,9 @@ class Terminal(gtk.VBox): if not self.terminator.doing_layout: self.terminator.last_focused_term = self if self.get_toplevel().is_child_notebook(): - # TODO: Will need some code for the tabs active terms to work + notebook = self.get_toplevel().get_children()[0] + notebook.set_last_active_term(self.uuid) + notebook.clean_last_active_term() self.get_toplevel().last_active_term = None else: self.get_toplevel().last_active_term = self.uuid diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index 22565d4f..c0c6af6c 100755 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -292,21 +292,54 @@ class Terminator(Borg): def layout_done(self): """Layout operations have finished, record that fact""" self.doing_layout = False + maker = Factory() - window_last_active_term_mapping={} + window_last_active_term_mapping = {} for window in self.windows: - # TODO: Will need some code for the tabs active terms to work - window_last_active_term_mapping[window]=copy.deepcopy(window.last_active_term) + if window.is_child_notebook(): + source = window.get_toplevel().get_children()[0] + else: + source = window + window_last_active_term_mapping[window] = copy.copy(source.last_active_term) for terminal in self.terminals: if not terminal.pid: terminal.spawn_child() for window in self.windows: - if window.last_active_term: - # TODO: Will need some code for the tabs active terms to work - term = self.find_terminal_by_uuid(window_last_active_term_mapping[window].urn) - term.ensure_visible_and_focussed() + if window.is_child_notebook(): + # For windows with a notebook + notebook = window.get_toplevel().get_children()[0] + # Cycle through pages by number + for page in xrange(0, notebook.get_n_pages()): + # Try and get the entry in the previously saved mapping + mapping = window_last_active_term_mapping[window] + page_last_active_term = mapping.get(notebook.get_nth_page(page), None) + if page_last_active_term is None: + # Couldn't find entry, so we find the first child of type Terminal + children = notebook.get_nth_page(page).get_children() + for page_last_active_term in children: + if maker.isinstance(page_last_active_term, 'Terminal'): + page_last_active_term = page_last_active_term.uuid + break + else: + err('Should never reach here!') + page_last_active_term = None + if page_last_active_term is None: + # Bail on this tab as we're having no luck here, continue with the next + continue + # Set the notebook entry, then ensure Terminal is visible and focussed + urn = page_last_active_term.urn + notebook.last_active_term[notebook.get_nth_page(page)] = page_last_active_term + if urn: + term = self.find_terminal_by_uuid(urn) + if term: + term.ensure_visible_and_focussed() + else: + # For windows without a notebook ensure Terminal is visible and focussed + if window_last_active_term_mapping[window]: + term = self.find_terminal_by_uuid(window_last_active_term_mapping[window].urn) + term.ensure_visible_and_focussed() for window in self.windows: if window.uuid == self.last_active_window: