From 581b6eeb928a5775f4c838753f5ae61db8f18124 Mon Sep 17 00:00:00 2001 From: Stephen Boddy Date: Wed, 18 Dec 2013 18:06:59 +0100 Subject: [PATCH] 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: