diff --git a/terminatorlib/optionparse.py b/terminatorlib/optionparse.py index befafa6e..ff9b7433 100644 --- a/terminatorlib/optionparse.py +++ b/terminatorlib/optionparse.py @@ -56,6 +56,18 @@ def parse_options(): parser.add_argument('--geometry', dest='geometry', type=str, help=_('Set the preferred size and position of the window' '(see X man page)')) + parser.add_argument('--guake-key', dest='guake_key', type=str, + help=_('If set, toggle window with given key' + '(see X man page)')) + parser.add_argument('--guake-side', dest='guake_side', default="top", type=str, + help=_('When using --guake-key, set argument to top, bottom, left, or right to bind to screen edge. Default: top' + '(see X man page)')) + parser.add_argument('--guake-width', dest='guake_width', default=800, type=int, + help=_('Set the preferred width when using Guake like mode. Default: 800' + '(see X man page)')) + parser.add_argument('--guake-height', dest='guake_height', default=600, type=int, + help=_('Set the preferred height when using Guake like mode. Default: 600' + '(see X man page)')) if not is_x_terminal_emulator: parser.add_argument('-e', '--command', dest='command', help=_('Specify a command to execute inside the terminal')) diff --git a/terminatorlib/window.py b/terminatorlib/window.py index 04c60a4c..3605e99a 100644 --- a/terminatorlib/window.py +++ b/terminatorlib/window.py @@ -77,13 +77,16 @@ def __init__(self): # self.set_property('allow-shrink', True) # FIXME FOR GTK3, or do we need this actually? icon_to_apply='' - self.register_callbacks() + self.apply_config() self.title = WindowTitle(self) self.title.update() - - self.preventHide = False + + self.preventHide = False + + self.display = Gdk.Display().get_default() + self.mouse = self.display.get_default_seat().get_pointer() options = self.config.options_get() if options: @@ -92,15 +95,49 @@ def __init__(self): if options.role: self.set_role(options.role) - + if options.forcedicon is not None: icon_to_apply = options.forcedicon if options.geometry: if not self.parse_geometry(options.geometry): - err('Window::__init__: Unable to parse geometry: %s' % + err('Window::__init__: Unable to parse geometry: %s' % options.geometry) + if options.guake_key: + self.guake_key = options.guake_key + if options.guake_side and options.guake_width and options.guake_height: + proceed_undecorated = True + + if options.guake_side in ["top", "bottom", "left", "right"]: + self.guake_side = options.guake_side + else: + proceed_undecorated = False + err('Window::__init__: Unable to parse guake_side: %s' % + options.guake_side) + + if type(options.guake_width) == int: + self.guake_width = options.guake_width + else: + proceed_undecorated = False + err('Window::__init__: Unable to parse guake_width: %s' % + options.guake_width) + + if type(options.guake_height) == int: + self.guake_height = options.guake_height + else: + proceed_undecorated = False + err('Window::__init__: Unable to parse guake_height: %s' % + options.guake_height) + + if proceed_undecorated: + self.set_decorated(False) + self.set_default_size(self.guake_width, self.guake_height) + else: + self.guake_key = None + + self.register_callbacks() + self.apply_icon(icon_to_apply) self.pending_set_rough_geometry_hint = False @@ -118,6 +155,31 @@ def do_set_property(self, prop, value): else: raise AttributeError('unknown property %s' % prop.name) + # NOTE: Gdk.VisibilityState.UNOBSCURED presumably isn't reliable due to transparency in moddern wms. + # Seems to work okay for our needs but should be kept in mind for future changes.... + def _bind_window_to_position(self, widget, eve): + if eve.state == Gdk.VisibilityState.UNOBSCURED: + screen, mouse_x, mouse_y = self.mouse.get_position() + monitor = self.display.get_monitor_at_point(mouse_x, mouse_y) + window_w, window_h = self.guake_width, self.guake_height + geom_rect = monitor.get_geometry() + x, y, w, h = geom_rect.x, geom_rect.y, geom_rect.width, geom_rect.height + + if self.guake_side == "top": + new_x = (w - (window_w + ((w - window_w)/2) )) + x + new_y = y + if self.guake_side == "bottom": + new_x = (w - (window_w + ((w - window_w)/2) )) + x + new_y = (h - window_h) + y + if self.guake_side == "left": + new_x = x + new_y = (h - (window_h + ((h - window_h)/2) )) + y + if self.guake_side == "right": + new_x = (w - window_w) + x + new_y = (h - (window_h + ((h - window_h)/2) )) + y + + self.move(new_x, new_y) + def register_callbacks(self): """Connect the GTK+ signals we care about""" self.connect('key-press-event', self.on_key_press) @@ -128,22 +190,29 @@ def register_callbacks(self): self.connect('focus-out-event', self.on_focus_out) self.connect('focus-in-event', self.on_focus_in) + if self.guake_key not in ('', None): + guake_unload_id = self.connect('visibility-notify-event', self._bind_window_to_position) + + _hide_window = self.config['keybindings']['hide_window'] + bind_key = self.guake_key if self.guake_key not in ('', None) else _hide_window if _hide_window not in ('', None) else None + # Attempt to grab a global hotkey for hiding the window. # If we fail, we'll never hide the window, iconifying instead. - if self.config['keybindings']['hide_window'] not in ('', None): - if display_manager() == 'X11': - try: - self.hidebound = Keybinder.bind( - self.config['keybindings']['hide_window'], - self.on_hide_window) - except (KeyError, NameError): - pass - - if not self.hidebound: - err('Unable to bind hide_window key, another instance/window has it.') - self.hidefunc = self.iconify - else: - self.hidefunc = self.hide + if bind_key and display_manager() == 'X11': + try: + self.hidebound = Keybinder.bind(bind_key, self.on_hide_window) + except (KeyError, NameError): + pass + + if not self.hidebound: + err('Unable to bind hide_window key, another instance/window has it.') + self.hidefunc = self.iconify + + if self.guake_key not in ('', None): + GObject.signal_handler_disconnect(self, guake_unload_id) + self.set_decorated(True) + else: + self.hidefunc = self.hide def apply_config(self): """Apply various configuration options""" @@ -293,7 +362,7 @@ def on_delete_event(self, window, event, data=None): def confirm_close(self, window, type): """Display a confirmation dialog when the user is closing multiple terminals in one window""" - + return(not (self.construct_confirm_close(window, type) == Gtk.ResponseType.ACCEPT)) def on_destroy_event(self, widget, data=None): @@ -317,8 +386,10 @@ def on_hide_window(self, data=None): if (time.time() - self.losefocus_time < 0.1) and \ self.config['hide_on_lose_focus']: return + if self.position: self.move(self.position[0], self.position[1]) + self.show() self.grab_focus() try: @@ -333,7 +404,7 @@ def on_hide_window(self, data=None): # pylint: disable-msg=W0613 def on_window_state_changed(self, window, event): """Handle the state of the window changing""" - self.isfullscreen = bool(event.new_window_state & + self.isfullscreen = bool(event.new_window_state & Gdk.WindowState.FULLSCREEN) self.ismaximised = bool(event.new_window_state & Gdk.WindowState.MAXIMIZED) @@ -392,7 +463,7 @@ def set_real_transparency(self, value=True): visual = screen.get_rgba_visual() if visual: self.set_visual(visual) - + def show(self, startup=False): """Undo the startup show request if started in hidden mode""" #Present is necessary to grab focus when window is hidden from taskbar. @@ -481,7 +552,7 @@ def split_axis(self, widget, vertical=True, cwd=None, sibling=None, widgetfirst= container = maker.make('VPaned') else: container = maker.make('HPaned') - + self.set_pos_by_ratio = True if not sibling: @@ -505,7 +576,7 @@ def split_axis(self, widget, vertical=True, cwd=None, sibling=None, widgetfirst= for term in order: container.add(term) container.show_all() - + while Gtk.events_pending(): Gtk.main_iteration_do(False) sibling.grab_focus() @@ -547,7 +618,7 @@ def zoom(self, widget, font_scale=True): self.set_property('term_zoomed', True) if font_scale: - widget.cnxids.new(widget, 'size-allocate', + widget.cnxids.new(widget, 'size-allocate', widget.zoom_scale, self.zoom_data) widget.grab_focus() @@ -603,7 +674,7 @@ def rotate(self, widget, clockwise): def get_terminals(self): return(util.enumerate_descendants(self)[1]) - + def get_visible_terminals(self): """Walk down the widget tree to find all of the visible terminals. Mostly using Container::get_visible_terminals()""" @@ -693,7 +764,7 @@ def set_rough_geometry_hints(self): extra_height = win_height - total_font_height dbg('setting geometry hints: (ewidth:%s)(eheight:%s),\ -(fwidth:%s)(fheight:%s)' % (extra_width, extra_height, +(fwidth:%s)(fheight:%s)' % (extra_width, extra_height, font_width, font_height)) geometry = Gdk.Geometry() geometry.base_width = extra_width @@ -817,7 +888,7 @@ def ungroup_tab(self, widget): if not maker.isinstance(notebook, 'Notebook'): dbg('note in a notebook, refusing to ungroup tab') return - + self.set_groups(None, self.get_visible_terminals()) def move_tab(self, widget, direction): @@ -850,7 +921,7 @@ def move_tab(self, widget, direction): else: err('unknown direction: %s' % direction) return - + notebook.reorder_child(child, page) def navigate_terminal(self, terminal, direction):