/usr/lib/gedit/plugins/terminal.py
# -*- coding: utf8 -*- # terminal.py - Embeded VTE terminal for gedit # This file is part of gedit # # Copyright (C) 2005-2006 - Paolo Borelli # # gedit is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # gedit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with gedit; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, # Boston, MA 02110-1301 USA from gi.repository import GObject, GLib, Gio, Pango, Gdk, Gtk, Gedit, Vte import os import gettext from gpdefs import * try: gettext.bindtextdomain(GETTEXT_PACKAGE, GP_LOCALEDIR) _ = lambda s: gettext.dgettext(GETTEXT_PACKAGE, s); except: _ = lambda s: s class GeditTerminal(Gtk.Box): """VTE terminal which follows gnome-terminal default profile options""" __gsignals__ = { "populate-popup": ( GObject.SIGNAL_RUN_LAST, None, (GObject.TYPE_OBJECT,) ) } defaults = { 'emulation' : 'xterm', 'visible_bell' : False, } def __init__(self): Gtk.Box.__init__(self) self.profile_settings = self.get_profile_settings() self.profile_settings.connect("changed", self.on_profile_settings_changed) self.system_settings = Gio.Settings.new("org.gnome.desktop.interface") self.system_settings.connect("changed::monospace-font-name", self.font_changed) self._vte = Vte.Terminal() self.reconfigure_vte() self._vte.set_size(self._vte.get_column_count(), 5) self._vte.set_size_request(200, 50) self._vte.show() self.pack_start(self._vte, True, True, 0) scrollbar = Gtk.Scrollbar.new(Gtk.Orientation.VERTICAL, self._vte.get_vadjustment()) scrollbar.show() self.pack_start(scrollbar, False, False, 0) # we need to reconf colors if the style changes #FIXME: why? #self._vte.connect("style-update", lambda term, oldstyle: self.reconfigure_vte()) self._vte.connect("key-press-event", self.on_vte_key_press) self._vte.connect("button-press-event", self.on_vte_button_press) self._vte.connect("popup-menu", self.on_vte_popup_menu) self._vte.connect("child-exited", self.on_child_exited) self._accel_base = '<gedit>/plugins/terminal' self._accels = { 'copy-clipboard': [Gdk.KEY_C, Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK, self.copy_clipboard], 'paste-clipboard': [Gdk.KEY_V, Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK, self.paste_clipboard] } for name in self._accels: path = self._accel_base + '/' + name accel = Gtk.AccelMap.lookup_entry(path) if not accel[0]: Gtk.AccelMap.add_entry(path, self._accels[name][0], self._accels[name][1]) self._vte.fork_command_full(Vte.PtyFlags.DEFAULT, None, [Vte.get_user_shell()], None, GLib.SpawnFlags.SEARCH_PATH, None, None) def on_child_exited(self): self._vte.fork_command_full(Vte.PtyFlags.DEFAULT, None, [Vte.get_user_shell()], None, GLib.SpawnFlags.SEARCH_PATH, None, None) def do_grab_focus(self): self._vte.grab_focus() def get_profile_settings(self): #FIXME return either the gnome-terminal settings or the gedit one return Gio.Settings.new("org.gnome.gedit.plugins.terminal") def get_font(self): if self.profile_settings.get_boolean("use-system-font"): font = self.system_settings.get_string("monospace-font-name") else: font = self.profile_settings.get_string("font") return font def font_changed(self, settings=None, key=None): font = self.get_font() font_desc = Pango.font_description_from_string("19") self._vte.set_font(font_desc) def reconfigure_vte(self): # Fonts self.font_changed() # colors context = self._vte.get_style_context() #fg = context.get_color(Gtk.StateFlags.NORMAL) #bg = context.get_background_color(Gtk.StateFlags.NORMAL) bg = Gdk.RGBA(0, 0, 0, 1) fg = Gdk.RGBA(1, 1, 1, 1) palette = [] if not self.profile_settings.get_boolean("use-theme-colors"): fg_color = self.profile_settings.get_string("foreground-color") if fg_color != "": fg = Gdk.RGBA() parsed = fg.parse(fg_color) bg_color = self.profile_settings.get_string("background-color") if bg_color != "": bg = Gdk.RGBA() parsed = bg.parse(bg_color) str_colors = self.profile_settings.get_string("palette") if str_colors != "": for str_color in str_colors.split(':'): try: rgba = Gdk.RGBA() rgba.parse(str_color) palette.append(rgba) except: palette = [] break if (len(palette) not in (0, 8, 16, 24)): palette = [] self._vte.set_colors_rgba(fg, bg, palette) self._vte.set_cursor_blink_mode(self.profile_settings.get_enum("cursor-blink-mode")) self._vte.set_cursor_shape(self.profile_settings.get_enum("cursor-shape")) self._vte.set_audible_bell(not self.profile_settings.get_boolean("silent-bell")) self._vte.set_allow_bold(self.profile_settings.get_boolean("allow-bold")) self._vte.set_scroll_on_keystroke(self.profile_settings.get_boolean("scrollback-on-keystroke")) self._vte.set_scroll_on_output(self.profile_settings.get_boolean("scrollback-on-output")) self._vte.set_word_chars(self.profile_settings.get_string("word-chars")) self._vte.set_emulation(self.defaults['emulation']) self._vte.set_visible_bell(self.defaults['visible_bell']) if self.profile_settings.get_boolean("scrollback-unlimited"): lines = -1 else: lines = self.profile_settings.get_int("scrollback-lines") self._vte.set_scrollback_lines(lines) def on_profile_settings_changed(self, settings, key): self.reconfigure_vte() def on_vte_key_press(self, term, event): modifiers = event.state & Gtk.accelerator_get_default_mod_mask() if event.keyval in (Gdk.KEY_Tab, Gdk.KEY_KP_Tab, Gdk.KEY_ISO_Left_Tab): if modifiers == Gdk.ModifierType.CONTROL_MASK: self.get_toplevel().child_focus(Gtk.DirectionType.TAB_FORWARD) return True elif modifiers == Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK: self.get_toplevel().child_focus(Gtk.DirectionType.TAB_BACKWARD) return True for name in self._accels: path = self._accel_base + '/' + name entry = Gtk.AccelMap.lookup_entry(path) if entry and entry[0] and entry[1].accel_key == event.keyval and entry[1].accel_mods == modifiers: self._accels[name][2]() return True return False def on_vte_button_press(self, term, event): if event.button == 3: self._vte.grab_focus() self.make_popup(event) return True return False def on_vte_popup_menu(self, term): self.make_popup() def create_popup_menu(self): menu = Gtk.Menu() item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_COPY, None) item.connect("activate", lambda menu_item: self.copy_clipboard()) item.set_accel_path(self._accel_base + '/copy-clipboard') item.set_sensitive(self._vte.get_has_selection()) menu.append(item) item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_PASTE, None) item.connect("activate", lambda menu_item: self.paste_clipboard()) item.set_accel_path(self._accel_base + '/paste-clipboard') menu.append(item) self.emit("populate-popup", menu) menu.show_all() return menu def make_popup(self, event = None): menu = self.create_popup_menu() menu.attach_to_widget(self, None) if event is not None: menu.popup(None, None, None, None, event.button, event.time) else: menu.popup(None, None, lambda m: Gedit.utils_menu_position_under_widget(m, self), None, 0, Gtk.get_current_event_time()) menu.select_first(False) def copy_clipboard(self): self._vte.copy_clipboard() self._vte.grab_focus() def paste_clipboard(self): self._vte.paste_clipboard() self._vte.grab_focus() def change_directory(self, path): path = path.replace('\\', '\\\\').replace('"', '\\"') self._vte.feed_child('cd "%s"\n' % path, -1) self._vte.grab_focus() class TerminalPlugin(GObject.Object, Gedit.WindowActivatable): __gtype_name__ = "TerminalPlugin" window = GObject.property(type=Gedit.Window) def __init__(self): GObject.Object.__init__(self) def do_activate(self): self._panel = GeditTerminal() self._panel.connect("populate-popup", self.on_panel_populate_popup) self._panel.show() image = Gtk.Image.new_from_icon_name("utilities-terminal", Gtk.IconSize.MENU) bottom = self.window.get_bottom_panel() bottom.add_item(self._panel, "GeditTerminalPanel", _("Terminal"), image) def do_deactivate(self): bottom = self.window.get_bottom_panel() bottom.remove_item(self._panel) def do_update_state(self): pass def get_active_document_directory(self): doc = self.window.get_active_document() if doc is None: return None location = doc.get_location() if location is not None and Gedit.utils_location_has_file_scheme(location): directory = location.get_parent() return directory.get_path() return None def on_panel_populate_popup(self, panel, menu): menu.prepend(Gtk.SeparatorMenuItem()) path = self.get_active_document_directory() item = Gtk.MenuItem.new_with_mnemonic(_("C_hange Directory")) item.connect("activate", lambda menu_item: panel.change_directory(path)) item.set_sensitive(path is not None) menu.prepend(item) # Let's conform to PEP8 # ex:ts=4:et: