def nb(mocker): targets = [ 'guake.notebook.TerminalNotebook.terminal_spawn', 'guake.notebook.TerminalNotebook.terminal_attached', 'guake.notebook.TerminalNotebook.guake', 'guake.notebook.TerminalBox.set_terminal' ] for target in targets: mocker.patch(target, create=True) return TerminalNotebook()
def notebook(self): try: return self.notebooks[self.current_workspace] except KeyError: notebook = TerminalNotebook(self) notebook.connect('terminal-spawned', self.terminal_spawned) notebook.connect('page-deleted', self.page_deleted) self.mainframe.add(notebook) self.notebooks[self.current_workspace] = notebook log.info("created fresh notebook for workspace %d", self.current_workspace) # add a tab if there is none if not self.notebook.has_page(): self.add_tab() return self.notebooks[self.current_workspace]
def __init__(self): def load_schema(): return Gio.SettingsSchemaSource.new_from_directory( SCHEMA_DIR, Gio.SettingsSchemaSource.get_default(), False) try: schema_source = load_schema() except GLib.Error: # pylint: disable=catching-non-exception log.exception("Unable to load the GLib schema, try to compile it") try_to_compile_glib_schemas() schema_source = load_schema() self.settings = Settings(schema_source) super(Guake, self).__init__(gladefile('guake.glade')) select_gtk_theme(self.settings) patch_gtk_theme( self.get_widget("window-root").get_style_context(), self.settings) self.add_callbacks(self) self.debug_mode = self.settings.general.get_boolean('debug-mode') setupLogging(self.debug_mode) log.info('Guake Terminal %s', guake_version()) log.info('VTE %s', vte_version()) log.info('Gtk %s', gtk_version()) self.hidden = True self.forceHide = False # trayicon! Using SVG handles better different OS trays # img = pixmapfile('guake-tray.svg') # trayicon! img = pixmapfile('guake-tray.png') try: import appindicator except ImportError: self.tray_icon = Gtk.StatusIcon() self.tray_icon.set_from_file(img) self.tray_icon.set_tooltip_text(_("Guake Terminal")) self.tray_icon.connect('popup-menu', self.show_menu) self.tray_icon.connect('activate', self.show_hide) else: # TODO PORT test this on a system with app indicator self.tray_icon = appindicator.Indicator( _("guake-indicator"), _("guake-tray"), appindicator.CATEGORY_OTHER) self.tray_icon.set_icon(img) self.tray_icon.set_status(appindicator.STATUS_ACTIVE) menu = self.get_widget('tray-menu') show = Gtk.MenuItem(_('Show')) show.set_sensitive(True) show.connect('activate', self.show_hide) show.show() menu.prepend(show) self.tray_icon.set_menu(menu) # important widgets self.window = self.get_widget('window-root') self.window.set_keep_above(True) self.mainframe = self.get_widget('mainframe') self.mainframe.remove(self.get_widget('notebook-teminals')) self.notebook = TerminalNotebook(self) self.notebook.connect('terminal-spawned', self.terminal_spawned) self.notebook.connect('page-deleted', self.page_deleted) self.mainframe.add(self.notebook) self.set_tab_position() # check and set ARGB for real transparency color = self.window.get_style_context().get_background_color( Gtk.StateFlags.NORMAL) self.window.set_app_paintable(True) def draw_callback(widget, cr): if widget.transparency: cr.set_source_rgba(color.red, color.green, color.blue, 1) else: cr.set_source_rgb(0, 0, 0) cr.set_operator(cairo.OPERATOR_SOURCE) cr.paint() cr.set_operator(cairo.OPERATOR_OVER) screen = self.window.get_screen() visual = screen.get_rgba_visual() if visual and screen.is_composited(): self.window.set_visual(visual) self.window.transparency = True else: log.warn('System doesn\'t support transparency') self.window.transparency = False self.window.set_visual(screen.get_system_visual()) self.window.connect('draw', draw_callback) # holds the timestamp of the losefocus event self.losefocus_time = 0 # holds the timestamp of the previous show/hide action self.prev_showhide_time = 0 # Controls the transparency state needed for function accel_toggle_transparency self.transparency_toggled = False # store the default window title to reset it when update is not wanted self.default_window_title = self.window.get_title() self.abbreviate = False self.window.connect('focus-out-event', self.on_window_losefocus) # Handling the delete-event of the main window to avoid # problems when closing it. def destroy(*args): self.hide() return True def window_event(*args): return self.window_event(*args) self.window.connect('delete-event', destroy) self.window.connect('window-state-event', window_event) # this line is important to resize the main window and make it # smaller. # TODO PORT do we still need this? # self.window.set_geometry_hints(min_width=1, min_height=1) # special trick to avoid the "lost guake on Ubuntu 'Show Desktop'" problem. # DOCK makes the window foundable after having being "lost" after "Show # Desktop" self.window.set_type_hint(Gdk.WindowTypeHint.DOCK) # Restore back to normal behavior self.window.set_type_hint(Gdk.WindowTypeHint.NORMAL) # loading and setting up configuration stuff GSettingHandler(self) Keybinder.init() self.hotkeys = Keybinder Keybindings(self) self.load_config() # adding the first tab on guake self.add_tab() if self.settings.general.get_boolean('start-fullscreen'): self.fullscreen() refresh_user_start(self.settings) # Pop-up that shows that guake is working properly (if not # unset in the preferences windows) if self.settings.general.get_boolean('use-popup'): key = self.settings.keybindingsGlobal.get_string('show-hide') keyval, mask = Gtk.accelerator_parse(key) label = Gtk.accelerator_get_label(keyval, mask) filename = pixmapfile('guake-notification.png') notifier.showMessage( _("Guake Terminal"), _("Guake is now running,\n" "press <b>{!s}</b> to use it.").format(xml_escape(label)), filename) log.info("Guake initialized")
class Guake(SimpleGladeApp): """Guake main class. Handles specialy the main window. """ def __init__(self): def load_schema(): return Gio.SettingsSchemaSource.new_from_directory( SCHEMA_DIR, Gio.SettingsSchemaSource.get_default(), False) try: schema_source = load_schema() except GLib.Error: # pylint: disable=catching-non-exception log.exception("Unable to load the GLib schema, try to compile it") try_to_compile_glib_schemas() schema_source = load_schema() self.settings = Settings(schema_source) super(Guake, self).__init__(gladefile('guake.glade')) select_gtk_theme(self.settings) patch_gtk_theme( self.get_widget("window-root").get_style_context(), self.settings) self.add_callbacks(self) self.debug_mode = self.settings.general.get_boolean('debug-mode') setupLogging(self.debug_mode) log.info('Guake Terminal %s', guake_version()) log.info('VTE %s', vte_version()) log.info('Gtk %s', gtk_version()) self.hidden = True self.forceHide = False # trayicon! Using SVG handles better different OS trays # img = pixmapfile('guake-tray.svg') # trayicon! img = pixmapfile('guake-tray.png') try: import appindicator except ImportError: self.tray_icon = Gtk.StatusIcon() self.tray_icon.set_from_file(img) self.tray_icon.set_tooltip_text(_("Guake Terminal")) self.tray_icon.connect('popup-menu', self.show_menu) self.tray_icon.connect('activate', self.show_hide) else: # TODO PORT test this on a system with app indicator self.tray_icon = appindicator.Indicator( _("guake-indicator"), _("guake-tray"), appindicator.CATEGORY_OTHER) self.tray_icon.set_icon(img) self.tray_icon.set_status(appindicator.STATUS_ACTIVE) menu = self.get_widget('tray-menu') show = Gtk.MenuItem(_('Show')) show.set_sensitive(True) show.connect('activate', self.show_hide) show.show() menu.prepend(show) self.tray_icon.set_menu(menu) # important widgets self.window = self.get_widget('window-root') self.window.set_keep_above(True) self.mainframe = self.get_widget('mainframe') self.mainframe.remove(self.get_widget('notebook-teminals')) self.notebook = TerminalNotebook(self) self.notebook.connect('terminal-spawned', self.terminal_spawned) self.notebook.connect('page-deleted', self.page_deleted) self.mainframe.add(self.notebook) self.set_tab_position() # check and set ARGB for real transparency color = self.window.get_style_context().get_background_color( Gtk.StateFlags.NORMAL) self.window.set_app_paintable(True) def draw_callback(widget, cr): if widget.transparency: cr.set_source_rgba(color.red, color.green, color.blue, 1) else: cr.set_source_rgb(0, 0, 0) cr.set_operator(cairo.OPERATOR_SOURCE) cr.paint() cr.set_operator(cairo.OPERATOR_OVER) screen = self.window.get_screen() visual = screen.get_rgba_visual() if visual and screen.is_composited(): self.window.set_visual(visual) self.window.transparency = True else: log.warn('System doesn\'t support transparency') self.window.transparency = False self.window.set_visual(screen.get_system_visual()) self.window.connect('draw', draw_callback) # holds the timestamp of the losefocus event self.losefocus_time = 0 # holds the timestamp of the previous show/hide action self.prev_showhide_time = 0 # Controls the transparency state needed for function accel_toggle_transparency self.transparency_toggled = False # store the default window title to reset it when update is not wanted self.default_window_title = self.window.get_title() self.abbreviate = False self.window.connect('focus-out-event', self.on_window_losefocus) # Handling the delete-event of the main window to avoid # problems when closing it. def destroy(*args): self.hide() return True def window_event(*args): return self.window_event(*args) self.window.connect('delete-event', destroy) self.window.connect('window-state-event', window_event) # this line is important to resize the main window and make it # smaller. # TODO PORT do we still need this? # self.window.set_geometry_hints(min_width=1, min_height=1) # special trick to avoid the "lost guake on Ubuntu 'Show Desktop'" problem. # DOCK makes the window foundable after having being "lost" after "Show # Desktop" self.window.set_type_hint(Gdk.WindowTypeHint.DOCK) # Restore back to normal behavior self.window.set_type_hint(Gdk.WindowTypeHint.NORMAL) # loading and setting up configuration stuff GSettingHandler(self) Keybinder.init() self.hotkeys = Keybinder Keybindings(self) self.load_config() # adding the first tab on guake self.add_tab() if self.settings.general.get_boolean('start-fullscreen'): self.fullscreen() refresh_user_start(self.settings) # Pop-up that shows that guake is working properly (if not # unset in the preferences windows) if self.settings.general.get_boolean('use-popup'): key = self.settings.keybindingsGlobal.get_string('show-hide') keyval, mask = Gtk.accelerator_parse(key) label = Gtk.accelerator_get_label(keyval, mask) filename = pixmapfile('guake-notification.png') notifier.showMessage( _("Guake Terminal"), _("Guake is now running,\n" "press <b>{!s}</b> to use it.").format(xml_escape(label)), filename) log.info("Guake initialized") # new color methods should be moved to the GuakeTerminal class def _load_palette(self): colorRGBA = Gdk.RGBA(0, 0, 0, 0) paletteList = list() for color in self.settings.styleFont.get_string("palette").split(':'): colorRGBA.parse(color) paletteList.append(colorRGBA.copy()) return paletteList def _get_background_color(self, palette_list): if len(palette_list) > 16: bg_color = palette_list[17] else: bg_color = Gdk.RGBA(0, 0, 0, 0.9) return self._apply_transparency_to_color(bg_color) def _apply_transparency_to_color(self, bg_color): transparency = self.settings.styleBackground.get_int('transparency') if not self.transparency_toggled: bg_color.alpha = 1 / 100 * transparency else: bg_color.alpha = 1 return bg_color def set_background_color_from_settings(self): self.set_colors_from_settings() def get_bgcolor(self): palette_list = self._load_palette() return self._get_background_color(palette_list) def get_fgcolor(self): palette_list = self._load_palette() if len(palette_list) > 16: font_color = palette_list[16] else: font_color = Gdk.RGBA(0, 0, 0, 0) return font_color def set_colors_from_settings(self): bg_color = self.get_bgcolor() font_color = self.get_fgcolor() palette_list = self._load_palette() for i in self.notebook.iter_terminals(): i.set_color_foreground(font_color) i.set_color_bold(font_color) i.set_colors(font_color, bg_color, palette_list[:16]) def set_bgcolor(self, bgcolor): if isinstance(bgcolor, str): c = Gdk.RGBA(0, 0, 0, 0) log.debug("Building Gdk Color from: %r", bgcolor) c.parse("#" + bgcolor) bgcolor = c if not isinstance(bgcolor, Gdk.RGBA): raise TypeError( "color should be Gdk.RGBA, is: {!r}".format(bgcolor)) bgcolor = self._apply_transparency_to_color(bgcolor) log.debug("setting background color to: %r", bgcolor) page_num = self.notebook.get_current_page() for terminal in self.notebook.get_nth_page(page_num).iter_terminals(): terminal.set_color_background(bgcolor) def set_fgcolor(self, fgcolor): if isinstance(fgcolor, str): c = Gdk.RGBA(0, 0, 0, 0) log.debug("Building Gdk Color from: %r", fgcolor) c.parse("#" + fgcolor) fgcolor = c if not isinstance(fgcolor, Gdk.RGBA): raise TypeError( "color should be Gdk.RGBA, is: {!r}".format(fgcolor)) log.debug("setting background color to: %r", fgcolor) page_num = self.notebook.get_current_page() for terminal in self.notebook.get_nth_page(page_num).iter_terminals(): terminal.set_color_foreground(fgcolor) def execute_command(self, command, tab=None): # TODO DBUS_ONLY """Execute the `command' in the `tab'. If tab is None, the command will be executed in the currently selected tab. Command should end with '\n', otherwise it will be appended to the string. """ # TODO CONTEXTMENU this has to be rewriten and only serves the # dbus interface, maybe this should be moved to dbusinterface.py if not self.notebook.has_page(): self.add_tab() if command[-1] != '\n': command += '\n' terminal = self.notebook.get_last_terminal_focused() terminal.feed_child(command) def execute_command_by_uuid(self, tab_uuid, command): # TODO DBUS_ONLY """Execute the `command' in the tab whose terminal has the `tab_uuid' uuid """ if command[-1] != '\n': command += '\n' try: tab_uuid = uuid.UUID(tab_uuid) page_index, = ( index for index, t in enumerate(self.notebook.iter_terminals()) if t.get_uuid() == tab_uuid) except ValueError: pass else: terminals = self.notebook.get_terminals_for_page(page_index) for current_vte in terminals: current_vte.feed_child(command) def on_window_losefocus(self, window, event): """Hides terminal main window when it loses the focus and if the window_losefocus gconf variable is True. """ if not HidePrevention(self.window).may_hide(): return value = self.settings.general.get_boolean('window-losefocus') visible = window.get_property('visible') self.losefocus_time = get_server_time(self.window) if visible and value: log.info("Hiding on focus lose") self.hide() def show_menu(self, status_icon, button, activate_time): """Show the tray icon menu. """ menu = self.get_widget('tray-menu') menu.popup(None, None, None, Gtk.StatusIcon.position_menu, button, activate_time) def show_about(self, *args): # TODO DBUS ONLY # TODO TRAY ONLY """Hides the main window and creates an instance of the About Dialog. """ self.hide() AboutDialog() def show_prefs(self, *args): # TODO DBUS ONLY # TODO TRAY ONLY """Hides the main window and creates an instance of the Preferences window. """ self.hide() PrefsDialog(self.settings).show() def is_iconified(self): # TODO this is "dead" code only gets called to log output or in out commented code if self.window: cur_state = int(self.window.get_state()) return bool(cur_state & GDK_WINDOW_STATE_ICONIFIED) return False def window_event(self, window, event): state = event.new_window_state log.debug("Received window state event: %s", state) def show_hide(self, *args): """Toggles the main window visibility """ log.debug("Show_hide called") if self.forceHide: self.forceHide = False return if not HidePrevention(self.window).may_hide(): return if not self.win_prepare(): return if not self.window.get_property('visible'): log.info("Showing the terminal") self.show() self.set_terminal_focus() return # Disable the focus_if_open feature # - if doesn't work seamlessly on all system # - self.window.window.get_state doesn't provides us the right information on all # systems, especially on MATE/XFCE # # if self.client.get_bool(KEY('/general/focus_if_open')): # restore_focus = False # if self.window.window: # state = int(self.window.window.get_state()) # if ((state & GDK_WINDOW_STATE_STICKY or # state & GDK_WINDOW_STATE_WITHDRAWN # )): # restore_focus = True # else: # restore_focus = True # if not self.hidden: # restore_focus = True # if restore_focus: # log.debug("DBG: Restoring the focus to the terminal") # self.hide() # self.show() # self.window.window.focus() # self.set_terminal_focus() # return log.info("hiding the terminal") self.hide() def show_focus(self, *args): self.win_prepare() self.show() self.set_terminal_focus() def win_prepare(self, *args): event_time = self.hotkeys.get_current_event_time() if not self.settings.general.get_boolean('window-refocus') and \ self.window.get_window() and self.window.get_property('visible'): pass elif not self.settings.general.get_boolean('window-losefocus'): if self.losefocus_time and self.losefocus_time < event_time: if self.window.get_window() and self.window.get_property( 'visible'): log.debug("DBG: Restoring the focus to the terminal") self.window.get_window().focus(event_time) self.set_terminal_focus() self.losefocus_time = 0 return False elif self.losefocus_time and self.settings.general.get_boolean( 'window-losefocus'): if self.losefocus_time >= event_time and \ (self.losefocus_time - event_time) < 10: self.losefocus_time = 0 return False # limit rate at which the visibility can be toggled. if self.prev_showhide_time and event_time and \ (event_time - self.prev_showhide_time) < 65: return False self.prev_showhide_time = event_time log.debug("") log.debug("=" * 80) log.debug("Window display") if self.window: cur_state = int(self.window.get_state()) is_sticky = bool(cur_state & GDK_WINDOW_STATE_STICKY) is_withdrawn = bool(cur_state & GDK_WINDOW_STATE_WITHDRAWN) is_above = bool(cur_state & GDK_WINDOW_STATE_ABOVE) is_iconified = self.is_iconified() log.debug("gtk.gdk.WindowState = %s", cur_state) log.debug("GDK_WINDOW_STATE_STICKY? %s", is_sticky) log.debug("GDK_WINDOW_STATE_WITHDRAWN? %s", is_withdrawn) log.debug("GDK_WINDOW_STATE_ABOVE? %s", is_above) log.debug("GDK_WINDOW_STATE_ICONIFIED? %s", is_iconified) return True return False def show(self): """Shows the main window and grabs the focus on it. """ self.hidden = False # setting window in all desktops window_rect = RectCalculator.set_final_window_rect( self.settings, self.window) self.get_widget('window-root').stick() # add tab must be called before window.show to avoid a # blank screen before adding the tab. if not self.notebook.has_page(): self.add_tab() self.window.set_keep_below(False) self.window.show_all() # this is needed because self.window.show_all() results in showing every # thing which includes the scrollbar too self.settings.general.triggerOnChangedValue(self.settings.general, "use-scrollbar") # move the window even when in fullscreen-mode log.debug("Moving window to: %r", window_rect) self.window.move(window_rect.x, window_rect.y) # this works around an issue in fluxbox if not FullscreenManager(self.settings, self.window).is_fullscreen(): self.settings.general.triggerOnChangedValue( self.settings.general, 'window-height') time = get_server_time(self.window) # TODO PORT this # When minized, the window manager seems to refuse to resume # log.debug("self.window: %s. Dir=%s", type(self.window), dir(self.window)) # is_iconified = self.is_iconified() # if is_iconified: # log.debug("Is iconified. Ubuntu Trick => " # "removing skip_taskbar_hint and skip_pager_hint " # "so deiconify can work!") # self.get_widget('window-root').set_skip_taskbar_hint(False) # self.get_widget('window-root').set_skip_pager_hint(False) # self.get_widget('window-root').set_urgency_hint(False) # log.debug("get_skip_taskbar_hint: {}".format( # self.get_widget('window-root').get_skip_taskbar_hint())) # log.debug("get_skip_pager_hint: {}".format( # self.get_widget('window-root').get_skip_pager_hint())) # log.debug("get_urgency_hint: {}".format( # self.get_widget('window-root').get_urgency_hint())) # glib.timeout_add_seconds(1, lambda: self.timeout_restore(time)) # log.debug("order to present and deiconify") self.window.present() self.window.deiconify() self.window.show() self.window.get_window().focus(time) self.window.set_type_hint(Gdk.WindowTypeHint.DOCK) self.window.set_type_hint(Gdk.WindowTypeHint.NORMAL) # log.debug("Restoring skip_taskbar_hint and skip_pager_hint") # if is_iconified: # self.get_widget('window-root').set_skip_taskbar_hint(False) # self.get_widget('window-root').set_skip_pager_hint(False) # self.get_widget('window-root').set_urgency_hint(False) # This is here because vte color configuration works only after the # widget is shown. self.settings.styleFont.triggerOnChangedValue(self.settings.styleFont, 'color') self.settings.styleBackground.triggerOnChangedValue( self.settings.styleBackground, 'color') log.debug("Current window position: %r", self.window.get_position()) self.execute_hook('show') def hide_from_remote(self): """ Hides the main window of the terminal and sets the visible flag to False. """ log.debug("hide from remote") self.forceHide = True self.hide() def show_from_remote(self): """Show the main window of the terminal and sets the visible flag to False. """ log.debug("show from remote") self.forceHide = True self.show() def hide(self): """Hides the main window of the terminal and sets the visible flag to False. """ if not HidePrevention(self.window).may_hide(): return self.hidden = True self.get_widget('window-root').unstick() self.window.hide() # Don't use hide_all here! def force_move_if_shown(self): if not self.hidden: # when displayed, GTK might refuse to move the window (X or Y position). Just hide and # redisplay it so the final position is correct log.debug("FORCING HIDE") self.hide() log.debug("FORCING SHOW") self.show() # -- configuration -- def load_config(self): """"Just a proxy for all the configuration stuff. """ self.settings.general.triggerOnChangedValue(self.settings.general, 'use-trayicon') self.settings.general.triggerOnChangedValue(self.settings.general, 'prompt-on-quit') self.settings.general.triggerOnChangedValue(self.settings.general, 'prompt-on-close-tab') self.settings.general.triggerOnChangedValue(self.settings.general, 'window-tabbar') self.settings.general.triggerOnChangedValue(self.settings.general, 'mouse-display') self.settings.general.triggerOnChangedValue(self.settings.general, 'display-n') self.settings.general.triggerOnChangedValue(self.settings.general, 'window-ontop') if not FullscreenManager(self.settings, self.window).is_fullscreen(): self.settings.general.triggerOnChangedValue( self.settings.general, 'window-height') self.settings.general.triggerOnChangedValue( self.settings.general, 'window-width') self.settings.general.triggerOnChangedValue(self.settings.general, 'use-scrollbar') self.settings.general.triggerOnChangedValue(self.settings.general, 'history-size') self.settings.general.triggerOnChangedValue(self.settings.general, 'infinite-history') self.settings.general.triggerOnChangedValue(self.settings.general, 'use-vte-titles') self.settings.general.triggerOnChangedValue(self.settings.general, 'set-window-title') self.settings.general.triggerOnChangedValue(self.settings.general, 'abbreviate-tab-names') self.settings.general.triggerOnChangedValue(self.settings.general, 'max-tab-name-length') self.settings.general.triggerOnChangedValue(self.settings.general, 'quick-open-enable') self.settings.general.triggerOnChangedValue(self.settings.general, 'quick-open-command-line') self.settings.style.triggerOnChangedValue(self.settings.style, 'cursor-shape') self.settings.styleFont.triggerOnChangedValue(self.settings.styleFont, 'style') self.settings.styleFont.triggerOnChangedValue(self.settings.styleFont, 'palette') self.settings.styleFont.triggerOnChangedValue(self.settings.styleFont, 'palette-name') self.settings.styleFont.triggerOnChangedValue(self.settings.styleFont, 'allow-bold') self.settings.styleBackground.triggerOnChangedValue( self.settings.styleBackground, 'transparency') self.settings.general.triggerOnChangedValue(self.settings.general, 'use-default-font') self.settings.general.triggerOnChangedValue(self.settings.general, 'compat-backspace') self.settings.general.triggerOnChangedValue(self.settings.general, 'compat-delete') def accel_quit(self, *args): """Callback to prompt the user whether to quit Guake or not. """ procs = self.notebook.get_running_fg_processes_count() tabs = self.notebook.get_n_pages() prompt_cfg = self.settings.general.get_boolean('prompt-on-quit') prompt_tab_cfg = self.settings.general.get_int('prompt-on-close-tab') # "Prompt on tab close" config overrides "prompt on quit" config if prompt_cfg or (prompt_tab_cfg == 1 and procs > 0) or (prompt_tab_cfg == 2): log.debug("Remaining procs=%r", procs) if PromptQuitDialog(self.window, procs, tabs).quit(): log.info("Quitting Guake") Gtk.main_quit() else: log.info("Quitting Guake") Gtk.main_quit() def accel_reset_terminal(self, *args): # TODO KEYBINDINGS ONLY """Callback to reset and clean the terminal""" HidePrevention(self.window).prevent() current_term = self.notebook.get_current_terminal() current_term.reset(True, True) HidePrevention(self.window).allow() return True def accel_zoom_in(self, *args): """Callback to zoom in. """ for term in self.notebook.iter_terminals(): term.increase_font_size() return True def accel_zoom_out(self, *args): """Callback to zoom out. """ for term in self.notebook.iter_terminals(): term.decrease_font_size() return True def accel_increase_height(self, *args): """Callback to increase height. """ height = self.settings.general.get_int('window-height') self.settings.general.set_int('window-height', min(height + 2, 100)) return True def accel_decrease_height(self, *args): """Callback to decrease height. """ height = self.settings.general.get_int('window-height') self.settings.general.set_int('window-height', max(height - 2, 0)) return True def accel_increase_transparency(self, *args): """Callback to increase transparency. """ transparency = self.settings.styleBackground.get_int('transparency') if int(transparency) - 2 > 0: self.settings.styleBackground.set_int('transparency', int(transparency) - 2) return True def accel_decrease_transparency(self, *args): """Callback to decrease transparency. """ transparency = self.settings.styleBackground.get_int('transparency') if int(transparency) + 2 < MAX_TRANSPARENCY: self.settings.styleBackground.set_int('transparency', int(transparency) + 2) return True def accel_toggle_transparency(self, *args): """Callback to toggle transparency. """ self.transparency_toggled = not self.transparency_toggled self.settings.styleBackground.triggerOnChangedValue( self.settings.styleBackground, 'transparency') return True def accel_add(self, *args): """Callback to add a new tab. Called by the accel key. """ self.add_tab() return True def accel_prev(self, *args): """Callback to go to the previous tab. Called by the accel key. """ if self.notebook.get_current_page() == 0: self.notebook.set_current_page(self.notebook.get_n_pages() - 1) else: self.notebook.prev_page() return True def accel_next(self, *args): """Callback to go to the next tab. Called by the accel key. """ if self.notebook.get_current_page() + 1 == self.notebook.get_n_pages(): self.notebook.set_current_page(0) else: self.notebook.next_page() return True def accel_move_tab_left(self, *args): # TODO KEYBINDINGS ONLY """ Callback to move a tab to the left """ pos = self.notebook.get_current_page() if pos != 0: self.move_tab(pos, pos - 1) return True def accel_move_tab_right(self, *args): # TODO KEYBINDINGS ONLY """ Callback to move a tab to the right """ pos = self.notebook.get_current_page() if pos != self.notebook.get_n_pages() - 1: self.move_tab(pos, pos + 1) return True def move_tab(self, old_tab_pos, new_tab_pos): self.notebook.reorder_child(self.notebook.get_nth_page(old_tab_pos), new_tab_pos) self.notebook.set_current_page(new_tab_pos) def gen_accel_switch_tabN(self, N): """Generates callback (which called by accel key) to go to the Nth tab. """ def callback(*args): if 0 <= N < self.notebook.get_n_pages(): self.notebook.set_current_page(N) return True return callback def accel_switch_tab_last(self, *args): last_tab = self.notebook.get_n_pages() - 1 self.notebook.set_current_page(last_tab) return True def accel_rename_current_tab(self, *args): """Callback to show the rename tab dialog. Called by the accel key. """ page_num = self.notebook.get_current_page() page = self.notebook.get_nth_page(page_num) self.notebook.get_tab_label(page).on_rename(None) return True def accel_copy_clipboard(self, *args): # TODO KEYBINDINGS ONLY """Callback to copy text in the shown terminal. Called by the accel key. """ self.notebook.get_current_terminal().copy_clipboard() return True def accel_paste_clipboard(self, *args): # TODO KEYBINDINGS ONLY """Callback to paste text in the shown terminal. Called by the accel key. """ self.notebook.get_current_terminal().paste_clipboard() return True def accel_toggle_hide_on_lose_focus(self, *args): """Callback toggle whether the window should hide when it loses focus. Called by the accel key. """ if self.settings.general.get_boolean('window-losefocus'): self.settings.general.set_boolean('window-losefocus', False) else: self.settings.general.set_boolean('window-losefocus', True) return True def accel_toggle_fullscreen(self, *args): FullscreenManager(self.settings, self.window).toggle() return True def fullscreen(self): FullscreenManager(self.settings, self.window).fullscreen() def unfullscreen(self): FullscreenManager(self.settings, self.window).unfullscreen() # -- callbacks -- def recompute_tabs_titles(self): """Updates labels on all tabs. This is required when `self.abbreviate` changes """ use_vte_titles = self.settings.general.get_boolean("use-vte-titles") if not use_vte_titles: return # TODO NOTEBOOK this code only works if there is only one terminal in a # page, this need to be rewritten for terminal in self.notebook.iter_terminals(): page_num = self.notebook.page_num(terminal.get_parent()) self.notebook.rename_page(page_num, self.compute_tab_title(terminal), False) def compute_tab_title(self, vte): """Abbreviate and cut vte terminal title when necessary """ vte_title = vte.get_window_title() or _("Terminal") try: current_directory = vte.get_current_directory() if self.abbreviate and vte_title.endswith(current_directory): parts = current_directory.split('/') parts = [s[:1] for s in parts[:-1]] + [parts[-1]] vte_title = vte_title[:len(vte_title) - len(current_directory)] + '/'.join(parts) except OSError: pass return TabNameUtils.shorten(vte_title, self.settings) def on_terminal_title_changed(self, vte, term): # box must be a page box = term.get_parent().get_root_box() use_vte_titles = self.settings.general.get_boolean('use-vte-titles') if not use_vte_titles: return # this may return -1, should be checked ;) page_num = self.notebook.page_num(box) # if tab has been renamed by user, don't override. if not getattr(box, 'custom_label_set', False): title = self.compute_tab_title(vte) self.notebook.rename_page(page_num, title, False) self.update_window_title(title) else: text = self.notebook.get_tab_text_page(box) if text: self.update_window_title(text) def update_window_title(self, title): if self.settings.general.get_boolean('set-window-title') is True: self.window.set_title(title) else: self.window.set_title(self.default_window_title) # TODO PORT reimplement drag and drop text on terminal # -- tab related functions -- def close_tab(self, *args): """Closes the current tab. """ self.notebook.delete_page_current() def delete_tab(self, page_num, kill=True, prompt=True): """This function will destroy the notebook page, terminal and tab widgets and will call the function to kill interpreter forked by vte. """ self.notebook.delete_page(page_num, kill=kill, prompt=prompt) def rename_tab_uuid(self, term_uuid, new_text, user_set=True): """Rename an already added tab by its UUID """ term_uuid = uuid.UUID(term_uuid) page_index, = ( index for index, t in enumerate(self.notebook.iter_terminals()) if t.get_uuid() == term_uuid) self.notebook.rename_page(page_index, new_text, user_set) def rename_current_tab(self, new_text, user_set=False): page_num = self.notebook.get_current_page() self.notebook.rename_page(page_num, new_text, user_set) def terminal_spawned(self, notebook, terminal, pid): self.load_config() terminal.connect('window-title-changed', self.on_terminal_title_changed, terminal) def add_tab(self, directory=None): """Adds a new tab to the terminal notebook. """ self.notebook.new_page_with_focus(directory) def find_tab(self, directory=None): log.debug("find") # TODO SEARCH HidePrevention(self.window).prevent() search_text = Gtk.TextView() dialog = Gtk.Dialog( _("Find"), self.window, Gtk.DialogFlags.DESTROY_WITH_PARENT, (_("Forward"), RESPONSE_FORWARD, _("Backward"), RESPONSE_BACKWARD, Gtk.STOCK_CANCEL, Gtk.ResponseType.NONE)) dialog.vbox.pack_end(search_text, True, True, 0) dialog.buffer = search_text.get_buffer() dialog.connect("response", self._dialog_response_callback) search_text.show() search_text.grab_focus() dialog.show_all() # Note: beware to reset preventHide when closing the find dialog def _dialog_response_callback(self, dialog, response_id): if response_id not in (RESPONSE_FORWARD, RESPONSE_BACKWARD): dialog.destroy() HidePrevention(self.window).allow() return start, end = dialog.buffer.get_bounds() search_string = start.get_text(end) log.debug("Searching for %r %s\n", search_string, "forward" if response_id == RESPONSE_FORWARD else "backward") current_term = self.notebook.get_current_terminal() log.debug("type: %r", type(current_term)) log.debug("dir: %r", dir(current_term)) current_term.search_set_gregex() current_term.search_get_gregex() # buffer = self.text_view.get_buffer() # if response_id == RESPONSE_FORWARD: # buffer.search_forward(search_string, self) # elif response_id == RESPONSE_BACKWARD: # buffer.search_backward(search_string, self) def page_deleted(self, *args): if not self.notebook.has_page(): self.hide() # avoiding the delay on next Guake show request self.add_tab() else: self.set_terminal_focus() self.was_deleted_tab = True abbreviate_tab_names = self.settings.general.get_boolean( 'abbreviate-tab-names') if abbreviate_tab_names: self.abbreviate = False self.recompute_tabs_titles() def set_terminal_focus(self): """Grabs the focus on the current tab. """ self.notebook.set_current_page(self.notebook.get_current_page()) def get_selected_uuidtab(self): # TODO DBUS ONLY """Returns the uuid of the current selected terminal """ page_num = self.notebook.get_current_page() terminals = self.notebook.get_terminals_for_page(page_num) return str(terminals[0].get_uuid()) def search_on_web(self, *args): """Search for the selected text on the web """ # TODO KEYBINDINGS ONLY current_term = self.notebook.get_current_terminal() if current_term.get_has_selection(): current_term.copy_clipboard() guake_clipboard = Gtk.Clipboard.get_default( self.window.get_display()) search_query = guake_clipboard.wait_for_text() search_query = quote_plus(search_query) if search_query: # TODO search provider should be selectable (someone might # prefer bing.com, the internet is a strange place ¯\_(ツ)_/¯ ) search_url = "https://www.google.com/#q={!s}&safe=off".format( search_query, ) Gtk.show_uri(self.window.get_screen(), search_url, get_server_time(self.window)) return True def set_tab_position(self, *args): if self.settings.general.get_boolean('tab-ontop'): self.notebook.set_tab_pos(Gtk.PositionType.TOP) else: self.notebook.set_tab_pos(Gtk.PositionType.BOTTOM) def execute_hook(self, event_name): """Execute shell commands related to current event_name""" hook = self.settings.hooks.get_string('{!s}'.format(event_name)) if hook is not None and hook != "": hook = hook.split() try: subprocess.Popen(hook) except OSError as oserr: if oserr.errno == 8: log.error( "Hook execution failed! Check shebang at first line of %s!", hook) log.debug(traceback.format_exc()) else: log.error(str(oserr)) except Exception as e: log.error("hook execution failed! %s", e) log.debug(traceback.format_exc()) else: log.debug("hook on event %s has been executed", event_name)