diff --git a/terminatorlib/container.py b/terminatorlib/container.py index cecd8588..ce5f7243 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,15 +273,26 @@ 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 = [] + 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()) - layout['active_page'] = self.get_current_page() - if len(labels) > 0: + 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 + else: + layout['last_active_window'] = False name = 'child%d' % count count = count + 1 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 7929d98f..aa9c86f8 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 @@ -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() @@ -1088,7 +1087,15 @@ 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(): + 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 self.emit('focus-in') def on_vte_focus_out(self, _widget, _event): @@ -1490,6 +1497,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 +1519,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""" diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index f66e0602..c0c6af6c 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} @@ -291,11 +292,59 @@ 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 = {} + for window in self.windows: + 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.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: + window.show() + def reconfigure(self): """Update configuration for the whole application""" 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): diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 0f5ab34f..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,6 +241,17 @@ class Window(Container, gtk.Window): def on_focus_in(self, window, event): """Focus has entered the window""" self.set_urgency_hint(False) + 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): @@ -865,6 +878,12 @@ 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 + class WindowTitle(object): """Class to handle the setting of the window title"""