Blame plugins/terminal/terminal.py

Packit 6978fb
# -*- coding: utf8 -*-
Packit 6978fb
Packit 6978fb
# terminal.py - Embeded VTE terminal for gedit
Packit 6978fb
# This file is part of gedit
Packit 6978fb
#
Packit 6978fb
# Copyright (C) 2005-2006 - Paolo Borelli
Packit 6978fb
#
Packit 6978fb
# gedit is free software; you can redistribute it and/or modify
Packit 6978fb
# it under the terms of the GNU General Public License as published by
Packit 6978fb
# the Free Software Foundation; either version 2 of the License, or
Packit 6978fb
# (at your option) any later version.
Packit 6978fb
#
Packit 6978fb
# gedit is distributed in the hope that it will be useful,
Packit 6978fb
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6978fb
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6978fb
# GNU General Public License for more details.
Packit 6978fb
#
Packit 6978fb
# You should have received a copy of the GNU General Public License
Packit 6978fb
# along with gedit; if not, write to the Free Software
Packit 6978fb
# Foundation, Inc., 51 Franklin St, Fifth Floor,
Packit 6978fb
# Boston, MA  02110-1301  USA
Packit 6978fb
Packit 6978fb
import os
Packit 6978fb
Packit 6978fb
import gi
Packit 6978fb
gi.require_version('Gedit', '3.0')
Packit 6978fb
gi.require_version('Gtk', '3.0')
Packit 6978fb
gi.require_version('Vte', '2.91')
Packit 6978fb
from gi.repository import GObject, GLib, Gio, Pango, Gdk, Gtk, Gedit, Vte
Packit 6978fb
Packit 6978fb
from gpdefs import *
Packit 6978fb
Packit 6978fb
try:
Packit 6978fb
    import gettext
Packit 6978fb
    gettext.bindtextdomain('gedit-plugins')
Packit 6978fb
    gettext.textdomain('gedit-plugins')
Packit 6978fb
    _ = gettext.gettext
Packit 6978fb
except:
Packit 6978fb
    _ = lambda s: s
Packit 6978fb
Packit 6978fb
class GeditTerminal(Vte.Terminal):
Packit 6978fb
Packit 6978fb
    defaults = {
Packit 6978fb
        'audible_bell'          : False,
Packit 6978fb
    }
Packit 6978fb
Packit 6978fb
    TARGET_URI_LIST = 200
Packit 6978fb
Packit 6978fb
    def __init__(self):
Packit 6978fb
        Vte.Terminal.__init__(self)
Packit 6978fb
Packit 6978fb
        self.set_size(self.get_column_count(), 5)
Packit 6978fb
        self.set_size_request(200, 50)
Packit 6978fb
Packit 6978fb
        tl = Gtk.TargetList.new([])
Packit 6978fb
        tl.add_uri_targets(self.TARGET_URI_LIST)
Packit 6978fb
Packit 6978fb
        self.drag_dest_set(Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP,
Packit 6978fb
                           [], Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY)
Packit 6978fb
        self.drag_dest_set_target_list(tl)
Packit 6978fb
Packit 6978fb
        self.profile_settings = self.get_profile_settings()
Packit 6978fb
        self.profile_settings.connect("changed", self.on_profile_settings_changed)
Packit 6978fb
        self.system_settings = Gio.Settings.new("org.gnome.desktop.interface")
Packit 6978fb
        self.system_settings.connect("changed::monospace-font-name", self.font_changed)
Packit 6978fb
Packit 6978fb
        self.reconfigure_vte()
Packit 6978fb
Packit 6978fb
        self.spawn_sync(Vte.PtyFlags.DEFAULT, None, [Vte.get_user_shell()], None, GLib.SpawnFlags.SEARCH_PATH, None, None, None)
Packit 6978fb
Packit 6978fb
    def do_drag_data_received(self, drag_context, x, y, data, info, time):
Packit 6978fb
        if info == self.TARGET_URI_LIST:
Packit 6978fb
            self.feed_child(' '.join(["'" + Gio.file_new_for_uri(item).get_path() + "'" for item in Gedit.utils_drop_get_uris(data)]), -1)
Packit 6978fb
            Gtk.drag_finish(drag_context, True, False, time);
Packit 6978fb
        else:
Packit 6978fb
            Vte.Terminal.do_drag_data_received(self, drag_context, x, y, data, info, time)
Packit 6978fb
Packit 6978fb
    def settings_try_new(self, schema):
Packit 6978fb
        schemas = Gio.Settings.list_schemas()
Packit 6978fb
        if not schemas:
Packit 6978fb
            return None
Packit 6978fb
Packit 6978fb
        for s in schemas:
Packit 6978fb
            if s == schema:
Packit 6978fb
                return Gio.Settings.new(schema)
Packit 6978fb
Packit 6978fb
        return None
Packit 6978fb
Packit 6978fb
    def get_profile_settings(self):
Packit 6978fb
        profiles = self.settings_try_new("org.gnome.Terminal.ProfilesList")
Packit 6978fb
Packit 6978fb
        if profiles:
Packit 6978fb
            default_path = "/org/gnome/terminal/legacy/profiles:/:" + profiles.get_string("default") + "/"
Packit 6978fb
            settings = Gio.Settings.new_with_path("org.gnome.Terminal.Legacy.Profile",
Packit 6978fb
                                                  default_path)
Packit 6978fb
        else:
Packit 6978fb
            settings = Gio.Settings.new("org.gnome.gedit.plugins.terminal")
Packit 6978fb
Packit 6978fb
        return settings
Packit 6978fb
Packit 6978fb
    def get_font(self):
Packit 6978fb
        if self.profile_settings.get_boolean("use-system-font"):
Packit 6978fb
            font = self.system_settings.get_string("monospace-font-name")
Packit 6978fb
        else:
Packit 6978fb
            font = self.profile_settings.get_string("font")
Packit 6978fb
Packit 6978fb
        return font
Packit 6978fb
Packit 6978fb
    def font_changed(self, settings=None, key=None):
Packit 6978fb
        font = self.get_font()
Packit 6978fb
        font_desc = Pango.font_description_from_string(font)
Packit 6978fb
Packit 6978fb
        self.set_font(font_desc)
Packit 6978fb
Packit 6978fb
    def reconfigure_vte(self):
Packit 6978fb
        # Fonts
Packit 6978fb
        self.font_changed()
Packit 6978fb
Packit 6978fb
        # colors
Packit 6978fb
        context = self.get_style_context()
Packit 6978fb
        fg = context.get_color(Gtk.StateFlags.NORMAL)
Packit 6978fb
        bg = context.get_background_color(Gtk.StateFlags.NORMAL)
Packit 6978fb
        palette = []
Packit 6978fb
Packit 6978fb
        if not self.profile_settings.get_boolean("use-theme-colors"):
Packit 6978fb
            fg_color = self.profile_settings.get_string("foreground-color")
Packit 6978fb
            if fg_color != "":
Packit 6978fb
                fg = Gdk.RGBA()
Packit 6978fb
                parsed = fg.parse(fg_color)
Packit 6978fb
            bg_color = self.profile_settings.get_string("background-color")
Packit 6978fb
            if bg_color != "":
Packit 6978fb
                bg = Gdk.RGBA()
Packit 6978fb
                parsed = bg.parse(bg_color)
Packit 6978fb
        str_colors = self.profile_settings.get_strv("palette")
Packit 6978fb
        if str_colors:
Packit 6978fb
            for str_color in str_colors:
Packit 6978fb
                try:
Packit 6978fb
                    rgba = Gdk.RGBA()
Packit 6978fb
                    rgba.parse(str_color)
Packit 6978fb
                    palette.append(rgba)
Packit 6978fb
                except:
Packit 6978fb
                    palette = []
Packit 6978fb
                    break
Packit 6978fb
Packit 6978fb
        self.set_colors(fg, bg, palette)
Packit 6978fb
        self.set_cursor_blink_mode(self.profile_settings.get_enum("cursor-blink-mode"))
Packit 6978fb
        self.set_cursor_shape(self.profile_settings.get_enum("cursor-shape"))
Packit 6978fb
        self.set_audible_bell(self.profile_settings.get_boolean("audible-bell"))
Packit 6978fb
        self.set_allow_bold(self.profile_settings.get_boolean("allow-bold"))
Packit 6978fb
        self.set_scroll_on_keystroke(self.profile_settings.get_boolean("scroll-on-keystroke"))
Packit 6978fb
        self.set_scroll_on_output(self.profile_settings.get_boolean("scroll-on-output"))
Packit 6978fb
        self.set_audible_bell(self.defaults['audible_bell'])
Packit 6978fb
Packit 6978fb
        if self.profile_settings.get_boolean("scrollback-unlimited"):
Packit 6978fb
            lines = -1
Packit 6978fb
        else:
Packit 6978fb
            lines = self.profile_settings.get_int("scrollback-lines")
Packit 6978fb
        self.set_scrollback_lines(lines)
Packit 6978fb
Packit 6978fb
    def on_profile_settings_changed(self, settings, key):
Packit 6978fb
        self.reconfigure_vte()
Packit 6978fb
Packit 6978fb
class GeditTerminalPanel(Gtk.Box):
Packit 6978fb
    """VTE terminal which follows gnome-terminal default profile options"""
Packit 6978fb
Packit 6978fb
    __gsignals__ = {
Packit 6978fb
        "populate-popup": (
Packit 6978fb
            GObject.SignalFlags.RUN_LAST,
Packit 6978fb
            None,
Packit 6978fb
            (GObject.TYPE_OBJECT,)
Packit 6978fb
        )
Packit 6978fb
    }
Packit 6978fb
Packit 6978fb
    def __init__(self):
Packit 6978fb
        Gtk.Box.__init__(self)
Packit 6978fb
Packit 6978fb
        self._accel_base = '<gedit>/plugins/terminal'
Packit 6978fb
        self._accels = {
Packit 6978fb
            'copy-clipboard': [Gdk.KEY_C, Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK, self.copy_clipboard],
Packit 6978fb
            'paste-clipboard': [Gdk.KEY_V, Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK, self.paste_clipboard]
Packit 6978fb
        }
Packit 6978fb
Packit 6978fb
        for name in self._accels:
Packit 6978fb
            path = self._accel_base + '/' + name
Packit 6978fb
            accel = Gtk.AccelMap.lookup_entry(path)
Packit 6978fb
Packit 6978fb
            if not accel[0]:
Packit 6978fb
                 Gtk.AccelMap.add_entry(path, self._accels[name][0], self._accels[name][1])
Packit 6978fb
Packit 6978fb
        self.add_terminal()
Packit 6978fb
Packit 6978fb
    def add_terminal(self):
Packit 6978fb
        self._vte = GeditTerminal()
Packit 6978fb
        self._vte.show()
Packit 6978fb
        self.pack_start(self._vte, True, True, 0)
Packit 6978fb
Packit 6978fb
        self._vte.connect("child-exited", self.on_vte_child_exited)
Packit 6978fb
        self._vte.connect("key-press-event", self.on_vte_key_press)
Packit 6978fb
        self._vte.connect("button-press-event", self.on_vte_button_press)
Packit 6978fb
        self._vte.connect("popup-menu", self.on_vte_popup_menu)
Packit 6978fb
Packit 6978fb
        scrollbar = Gtk.Scrollbar.new(Gtk.Orientation.VERTICAL, self._vte.get_vadjustment())
Packit 6978fb
        scrollbar.show()
Packit 6978fb
        self.pack_start(scrollbar, False, False, 0)
Packit 6978fb
Packit 6978fb
    def on_vte_child_exited(self, term, status):
Packit 6978fb
        for child in self.get_children():
Packit 6978fb
            child.destroy()
Packit 6978fb
Packit 6978fb
        self.add_terminal()
Packit 6978fb
        self._vte.grab_focus()
Packit 6978fb
Packit 6978fb
    def do_grab_focus(self):
Packit 6978fb
        self._vte.grab_focus()
Packit 6978fb
Packit 6978fb
    def on_vte_key_press(self, term, event):
Packit 6978fb
        modifiers = event.state & Gtk.accelerator_get_default_mod_mask()
Packit 6978fb
        if event.keyval in (Gdk.KEY_Tab, Gdk.KEY_KP_Tab, Gdk.KEY_ISO_Left_Tab):
Packit 6978fb
            if modifiers == Gdk.ModifierType.CONTROL_MASK:
Packit 6978fb
                self.get_toplevel().child_focus(Gtk.DirectionType.TAB_FORWARD)
Packit 6978fb
                return True
Packit 6978fb
            elif modifiers == Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK:
Packit 6978fb
                self.get_toplevel().child_focus(Gtk.DirectionType.TAB_BACKWARD)
Packit 6978fb
                return True
Packit 6978fb
Packit 6978fb
        for name in self._accels:
Packit 6978fb
            path = self._accel_base + '/' + name
Packit 6978fb
            entry = Gtk.AccelMap.lookup_entry(path)
Packit 6978fb
Packit 6978fb
            if entry and entry[0] and entry[1].accel_key == event.keyval and entry[1].accel_mods == modifiers:
Packit 6978fb
                self._accels[name][2]()
Packit 6978fb
                return True
Packit 6978fb
Packit 6978fb
        keyval_name = Gdk.keyval_name(Gdk.keyval_to_upper(event.keyval))
Packit 6978fb
Packit 6978fb
        # Special case some Vte.Terminal shortcuts
Packit 6978fb
        # so the global shortcuts do not override them
Packit 6978fb
        if modifiers == Gdk.ModifierType.CONTROL_MASK and keyval_name in 'ACDEHKLRTUWZ':
Packit 6978fb
            return False
Packit 6978fb
Packit 6978fb
        if modifiers == Gdk.ModifierType.MOD1_MASK and keyval_name in 'BF':
Packit 6978fb
            return False
Packit 6978fb
Packit 6978fb
        return Gtk.accel_groups_activate(self.get_toplevel(),
Packit 6978fb
                                         event.keyval, modifiers)
Packit 6978fb
Packit 6978fb
    def on_vte_button_press(self, term, event):
Packit 6978fb
        if event.button == 3:
Packit 6978fb
            self._vte.grab_focus()
Packit 6978fb
            self.make_popup(event)
Packit 6978fb
            return True
Packit 6978fb
Packit 6978fb
        return False
Packit 6978fb
Packit 6978fb
    def on_vte_popup_menu(self, term):
Packit 6978fb
        self.make_popup()
Packit 6978fb
Packit 6978fb
    def create_popup_menu(self):
Packit 6978fb
        menu = Gtk.Menu()
Packit 6978fb
Packit 6978fb
        item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_COPY, None)
Packit 6978fb
        item.connect("activate", lambda menu_item: self.copy_clipboard())
Packit 6978fb
        item.set_accel_path(self._accel_base + '/copy-clipboard')
Packit 6978fb
        item.set_sensitive(self._vte.get_has_selection())
Packit 6978fb
        menu.append(item)
Packit 6978fb
Packit 6978fb
        item = Gtk.ImageMenuItem.new_from_stock(Gtk.STOCK_PASTE, None)
Packit 6978fb
        item.connect("activate", lambda menu_item: self.paste_clipboard())
Packit 6978fb
        item.set_accel_path(self._accel_base + '/paste-clipboard')
Packit 6978fb
        menu.append(item)
Packit 6978fb
Packit 6978fb
        self.emit("populate-popup", menu)
Packit 6978fb
        menu.show_all()
Packit 6978fb
        return menu
Packit 6978fb
Packit 6978fb
    def make_popup(self, event = None):
Packit 6978fb
        menu = self.create_popup_menu()
Packit 6978fb
        menu.attach_to_widget(self, None)
Packit 6978fb
Packit 6978fb
        if event is not None:
Packit 6978fb
            menu.popup(None, None, None, None, event.button, event.time)
Packit 6978fb
        else:
Packit 6978fb
            menu.popup(None, None,
Packit 6978fb
                       lambda m: Gedit.utils_menu_position_under_widget(m, self),
Packit 6978fb
                       None,
Packit 6978fb
                       0, Gtk.get_current_event_time())
Packit 6978fb
            menu.select_first(False)
Packit 6978fb
Packit 6978fb
    def copy_clipboard(self):
Packit 6978fb
        self._vte.copy_clipboard()
Packit 6978fb
        self._vte.grab_focus()
Packit 6978fb
Packit 6978fb
    def paste_clipboard(self):
Packit 6978fb
        self._vte.paste_clipboard()
Packit 6978fb
        self._vte.grab_focus()
Packit 6978fb
Packit 6978fb
    def change_directory(self, path):
Packit 6978fb
        path = path.replace('\\', '\\\\').replace('"', '\\"')
Packit 6978fb
        self._vte.feed_child('cd "%s"\n' % path, -1)
Packit 6978fb
        self._vte.grab_focus()
Packit 6978fb
Packit 6978fb
class TerminalPlugin(GObject.Object, Gedit.WindowActivatable):
Packit 6978fb
    __gtype_name__ = "TerminalPlugin"
Packit 6978fb
Packit 6978fb
    window = GObject.Property(type=Gedit.Window)
Packit 6978fb
Packit 6978fb
    def __init__(self):
Packit 6978fb
        GObject.Object.__init__(self)
Packit 6978fb
Packit 6978fb
    def do_activate(self):
Packit 6978fb
        self._panel = GeditTerminalPanel()
Packit 6978fb
        self._panel.connect("populate-popup", self.on_panel_populate_popup)
Packit 6978fb
        self._panel.show()
Packit 6978fb
Packit 6978fb
        bottom = self.window.get_bottom_panel()
Packit 6978fb
        bottom.add_titled(self._panel, "GeditTerminalPanel", _("Terminal"))
Packit 6978fb
Packit 6978fb
    def do_deactivate(self):
Packit 6978fb
        bottom = self.window.get_bottom_panel()
Packit 6978fb
        bottom.remove(self._panel)
Packit 6978fb
Packit 6978fb
    def do_update_state(self):
Packit 6978fb
        pass
Packit 6978fb
Packit 6978fb
    def get_active_document_directory(self):
Packit 6978fb
        doc = self.window.get_active_document()
Packit 6978fb
        if doc:
Packit 6978fb
            location = doc.get_file().get_location()
Packit 6978fb
            if location and location.has_uri_scheme("file"):
Packit 6978fb
                directory = location.get_parent()
Packit 6978fb
                return directory.get_path()
Packit 6978fb
        return None
Packit 6978fb
Packit 6978fb
    def on_panel_populate_popup(self, panel, menu):
Packit 6978fb
        menu.prepend(Gtk.SeparatorMenuItem())
Packit 6978fb
        path = self.get_active_document_directory()
Packit 6978fb
        item = Gtk.MenuItem.new_with_mnemonic(_("C_hange Directory"))
Packit 6978fb
        item.connect("activate", lambda menu_item: panel.change_directory(path))
Packit 6978fb
        item.set_sensitive(path is not None)
Packit 6978fb
        menu.prepend(item)
Packit 6978fb
Packit 6978fb
# Let's conform to PEP8
Packit 6978fb
# ex:ts=4:et: