class MenuHideCallback:
    def __init__(self, window):
        self.window = window

    def on_hide(self, *args):
        HidePrevention(self.window).allow() class TerminalContextMenuCallbacks:
    def __init__(self, terminal, window, settings, notebook):
        self.terminal = terminal
        self.window = window
        self.settings = settings
        self.notebook = notebook

    def on_copy_clipboard(self, *args):
        self.terminal.copy_clipboard()

    def on_copy_url_clipboard(self, *args):
        url = self.terminal.get_link_under_cursor()
        if url is not None:
            clipboard = Gtk.Clipboard.get_default(self.window.get_display())
            clipboard.set_text(url, len(url))

    def on_paste_clipboard(self, *args):
        self.terminal.paste_clipboard()

    def on_toggle_fullscreen(self, *args):
        FullscreenManager(self.settings, self.window).toggle()

    def on_save_to_file(self, *args):
        SaveTerminalDialog(self.terminal, self.window).run()

    def on_reset_terminal(self, *args):
        self.terminal.reset(True, True)

    def on_find(self):
        # this is not implemented jet
        pass

    def on_open_link(self, *args):
        self.terminal.browse_link_under_cursor()

    def on_search_on_web(self, *args):
        if self.terminal.get_has_selection():
            self.terminal.copy_clipboard()
            clipboard = Gtk.Clipboard.get_default(self.window.get_display())
            query = clipboard.wait_for_text()
            query = quote_plus(query)
            if query:
                search_url = f"https://www.google.com/search?q={query}&safe=off"
                Gtk.show_uri(self.window.get_screen(), search_url, get_server_time(self.window))

    def on_quick_open(self, *args):
        if self.terminal.get_has_selection():
            self.terminal.quick_open()

    def on_command_selected(self, command):
        self.terminal.execute_command(command)

    def on_show_preferences(self, *args):
        self.notebook.guake.hide()
        PrefsDialog(self.settings).show()

    def on_show_about(self, *args):
        self.notebook.guake.hide()
        AboutDialog()

    def on_quit(self, *args):
        self.notebook.guake.accel_quit()

    def on_split_vertical(self, *args):
        self.terminal.get_parent().split_v()

    def on_split_horizontal(self, *args):
        self.terminal.get_parent().split_h()

    def on_close_terminal(self, *args):
        self.terminal.kill() class PromptResetColorsDialog(Gtk.MessageDialog):

    """Prompts the user whether to reset tab colors."""

    def __init__(self, parent):
        super().__init__(
            parent,
            Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
            Gtk.MessageType.QUESTION,
            Gtk.ButtonsType.YES_NO,
        )

        primary_msg = _("Do you want to reset custom colors for this tab?")

        self.set_markup(primary_msg)

    def reset_tab_custom_colors(self):
        """Run the "are you sure" dialog for resetting tab colors"""
        # Stop an open "close tab" dialog from obstructing a quit
        response = self.run() == Gtk.ResponseType.YES
        self.destroy()
        # Keep Guake focussed after dismissing tab-close prompt
        # if tab == -1:
        #     self.window.present()
        return response class RenameDialog(Gtk.Dialog):
    def __init__(self, window, current_name):
        super().__init__(
            _("Rename tab"),
            window,
            Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
            (
                Gtk.STOCK_CANCEL,
                Gtk.ResponseType.REJECT,
                Gtk.STOCK_OK,
                Gtk.ResponseType.ACCEPT,
            ),
        )
        self.entry = Gtk.Entry()
        self.entry.set_text(current_name)
        self.entry.set_property("can-default", True)
        self.entry.show()

        vbox = Gtk.VBox()
        vbox.set_border_width(6)
        vbox.show()

        self.set_size_request(300, -1)
        self.vbox.pack_start(vbox, True, True, 0)
        self.set_border_width(4)
        self.set_default_response(Gtk.ResponseType.ACCEPT)
        self.add_action_widget(self.entry, Gtk.ResponseType.ACCEPT)
        self.entry.reparent(vbox)

    def get_text(self):
        return self.entry.get_text() PCRE2_MULTILINE = 0x00000400 def mk_tab_context_menu(callback_object):
    """Create the context menu for a notebook tab"""
    # Store the menu in a temp variable in terminal so that popup() is happy. See:
    #   https://stackoverflow.com/questions/28465956/
    callback_object.context_menu = Gtk.Menu()
    menu = callback_object.context_menu
    mi_new_tab = Gtk.MenuItem(_("New Tab"))
    mi_new_tab.connect("activate", callback_object.on_new_tab)
    menu.add(mi_new_tab)
    mi_rename = Gtk.MenuItem(_("Rename"))
    mi_rename.connect("activate", callback_object.on_rename)
    menu.add(mi_rename)
    mi_reset_custom_colors = Gtk.MenuItem(_("Reset custom colors"))
    mi_reset_custom_colors.connect("activate", callback_object.on_reset_custom_colors)
    menu.add(mi_reset_custom_colors)
    mi_close = Gtk.MenuItem(_("Close"))
    mi_close.connect("activate", callback_object.on_close)
    menu.add(mi_close)
    menu.show_all()
    return menu def mk_terminal_context_menu(terminal, window, settings, callback_object):
    """Create the context menu for a terminal."""
    # Store the menu in a temp variable in terminal so that popup() is happy. See:
    #   https://stackoverflow.com/questions/28465956/
    terminal.context_menu = Gtk.Menu()
    menu = terminal.context_menu
    mi = Gtk.MenuItem(_("Copy"))
    mi.connect("activate", callback_object.on_copy_clipboard)
    menu.add(mi)
    if get_link_under_cursor(terminal) is not None:
        mi = Gtk.MenuItem(_("Copy URL"))
        mi.connect("activate", callback_object.on_copy_url_clipboard)
        menu.add(mi)
    mi = Gtk.MenuItem(_("Paste"))
    mi.connect("activate", callback_object.on_paste_clipboard)
    # check if clipboard has text, if not disable the paste menuitem
    clipboard = Gtk.Clipboard.get_default(window.get_display())
    mi.set_sensitive(clipboard.wait_is_text_available())
    menu.add(mi)
    menu.add(Gtk.SeparatorMenuItem())
    mi = Gtk.MenuItem(_("Toggle Fullscreen"))
    mi.connect("activate", callback_object.on_toggle_fullscreen)
    menu.add(mi)
    menu.add(Gtk.SeparatorMenuItem())
    mi = Gtk.MenuItem(_("Split ―"))
    mi.connect("activate", callback_object.on_split_horizontal)
    menu.add(mi)
    mi = Gtk.MenuItem(_("Split |"))
    mi.connect("activate", callback_object.on_split_vertical)
    menu.add(mi)
    mi = Gtk.MenuItem(_("Close terminal"))
    mi.connect("activate", callback_object.on_close_terminal)
    menu.add(mi)
    menu.add(Gtk.SeparatorMenuItem())
    mi = Gtk.MenuItem(_("Save content..."))
    mi.connect("activate", callback_object.on_save_to_file)
    menu.add(mi)
    mi = Gtk.MenuItem(_("Reset terminal"))
    mi.connect("activate", callback_object.on_reset_terminal)
    menu.add(mi)
    # TODO SEARCH uncomment menu.add()
    mi = Gtk.MenuItem(_("Find..."))
    mi.connect("activate", callback_object.on_find)
    # menu.add(mi)
    menu.add(Gtk.SeparatorMenuItem())
    mi = Gtk.MenuItem(_("Open link..."))
    mi.connect("activate", callback_object.on_open_link)
    link = get_link_under_cursor(terminal)
    # TODO CONTEXTMENU this is a mess Quick open should also be sensible
    # if the text in the selection is a url the current terminal
    # implementation does not support this at the moment
    if link:
        if len(link) >= FILE_SELECTION_LENGTH:
            mi.set_label(_("Open Link: {!s}...").format(link[: FILE_SELECTION_LENGTH - 3]))
        else:
            mi.set_label(_("Open Link: {!s}").format(link))
        mi.set_sensitive(True)
    else:
        mi.set_sensitive(False)
    menu.add(mi)
    mi = Gtk.MenuItem(_("Search on Web"))
    mi.connect("activate", callback_object.on_search_on_web)
    selection = get_current_selection(terminal, window)
    if selection:
        search_text = selection.rstrip()
        if len(search_text) > SEARCH_SELECTION_LENGTH:
            search_text = search_text[: SEARCH_SELECTION_LENGTH - 3] + "..."
        mi.set_label(_("Search on Web: '%s'") % search_text)
        mi.set_sensitive(True)
    else:
        mi.set_sensitive(False)
    menu.add(mi)
    mi = Gtk.MenuItem(_("Quick Open..."))
    mi.connect("activate", callback_object.on_quick_open)
    if selection:
        filename = get_filename_under_cursor(terminal, selection)
        if filename:
            filename_str = str(filename)
            if len(filename_str) > FILE_SELECTION_LENGTH:
                mi.set_label(
                    _("Quick Open: {!s}...").format(filename_str[: FILE_SELECTION_LENGTH - 3])
                )
            else:
                mi.set_label(_("Quick Open: {!s}").format(filename_str))
            mi.set_sensitive(True)
        else:
            mi.set_sensitive(False)
    else:
        mi.set_sensitive(False)
    menu.add(mi)
    customcommands = CustomCommands(settings, callback_object)
    if customcommands.should_load():
        submen = customcommands.build_menu()
        if submen:
            menu.add(Gtk.SeparatorMenuItem())
            mi = Gtk.MenuItem(_("Custom Commands"))
            mi.set_submenu(submen)
            menu.add(mi)
    menu.add(Gtk.SeparatorMenuItem())
    mi = Gtk.ImageMenuItem("gtk-preferences")
    mi.set_use_stock(True)
    mi.connect("activate", callback_object.on_show_preferences)
    menu.add(mi)
    mi = Gtk.ImageMenuItem("gtk-about")
    mi.set_use_stock(True)
    mi.connect("activate", callback_object.on_show_about)
    menu.add(mi)
    menu.add(Gtk.SeparatorMenuItem())
    mi = Gtk.ImageMenuItem(_("Quit"))
    mi.connect("activate", callback_object.on_quit)
    menu.add(mi)
    menu.show_all()
    return menu class HidePrevention:
    def __init__(self, window):
        """Create a new HidePrevention object like `HidePrevention(window)`"""
        if not isinstance(window, Gtk.Window):
            raise ValueError(f"window must be of type Gtk.Window, not of type {type(window)}")
        self.window = window

    def may_hide(self):
        """returns True if the window is allowed to hide and
        False if `prevent()` is called from some where
        """
        return getattr(self.window, "can_hide", True)

    def prevent(self):
        """sets a flag on the window object which indicates to
        may_hide that the window is NOT allowed to be hidden.
        """
        setattr(self.window, "can_hide", False)

    def allow(self):
        """sets the flag so that it indicates to may_hide that the window is allowed to be hidden"""
        setattr(self.window, "can_hide", True) class TabNameUtils:
    @classmethod
    def shorten(cls, text, settings):
        use_vte_titles = settings.general.get_boolean("use-vte-titles")
        if not use_vte_titles:
            return text
        max_name_length = settings.general.get_int("max-tab-name-length")
        if max_name_length != 0 and len(text) > max_name_length:
            text = "..." + text[-max_name_length:]
        return text def get_server_time(widget):
    try:
        return GdkX11.x11_get_server_time(widget.get_window())
    except (TypeError, AttributeError):
        # Issue: https://github.com/Guake/guake/issues/1071
        # Wayland does not seem to like `x11_get_server_time`.
        # Use local timestamp instead
        ts = time.time()
        return ts def save_tabs_when_changed(func):
    """Decorator for save-tabs-when-changed"""

    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        log.debug("mom, I've been called: %s %s", func.__name__, func)

        # Find me the Guake!
        clsname = args[0].__class__.__name__
        g = None
        if clsname == "Guake":
            g = args[0]
        elif getattr(args[0], "get_guake", None):
            g = args[0].get_guake()
        elif getattr(args[0], "get_notebook", None):
            g = args[0].get_notebook().guake
        elif getattr(args[0], "guake", None):
            g = args[0].guake
        elif getattr(args[0], "notebook", None):
            g = args[0].notebook.guake

        # Tada!
        if g and g.settings.general.get_boolean("save-tabs-when-changed"):
            g.save_tabs()

    return wrapper
import logging
import time
import gi
from gi.repository import GObject
from gi.repository import Gdk
from gi.repository import Gio
from gi.repository import Gtk
from gi.repository import Vte
from guake.callbacks import MenuHideCallback
from guake.callbacks import TerminalContextMenuCallbacks
from guake.dialogs import PromptResetColorsDialog
from guake.dialogs import RenameDialog
from guake.globals import PCRE2_MULTILINE
from guake.menus import mk_tab_context_menu
from guake.menus import mk_terminal_context_menu
from guake.utils import HidePrevention
from guake.utils import TabNameUtils
from guake.utils import get_server_time
from guake.utils import save_tabs_when_changed
        self.terminal.grab_focus()
        return False


class DualTerminalBox(Gtk.Paned, TerminalHolder):

    ORIENT_H = 0
    ORIENT_V = 1

    def __init__(self, orientation):
        super().__init__()

        self.orient = orientation
        if orientation is DualTerminalBox.ORIENT_H:
            self.set_orientation(orientation=Gtk.Orientation.HORIZONTAL)
        else:
            self.set_orientation(orientation=Gtk.Orientation.VERTICAL)

    def set_child_first(self, terminal_holder):
        if isinstance(terminal_holder, TerminalHolder):
            self.add1(terminal_holder)
        else:
            print("wtf, what have you added to me???")

    def set_child_second(self, terminal_holder):
        if isinstance(terminal_holder, TerminalHolder):
            self.add2(terminal_holder)
        else:
            print("wtf, what have you added to me???")

    def get_terminals(self):
        return self.get_child1().get_terminals() + self.get_child2().get_terminals()

    def iter_terminals(self):
        for t in self.get_child1().iter_terminals():
            yield t
        for t in self.get_child2().iter_terminals():
            yield t

    def replace_child(self, old, new):
        if self.get_child1() is old:
            self.remove(old)
            self.set_child_first(new)
        elif self.get_child2() is old:
            self.remove(old)
            self.set_child_second(new)
        else:
            print("I have never seen this widget!")

    def get_guake(self):
        return self.get_parent().get_guake()

    def get_window(self):
        return self.get_parent().get_window()

    def get_settings(self):
        return self.get_parent().get_settings()

    def get_root_box(self):
        return self.get_parent().get_root_box()

    def get_notebook(self):
        return self.get_parent().get_notebook()

    def grab_box_terminal_focus(self, box):
        if isinstance(box, DualTerminalBox):
            try:
                next(box.iter_terminals()).grab_focus()
            except StopIteration:
                log.error("Both panes are empty")
        else:
            box.get_terminal().grab_focus()

    def remove_dead_child(self, child):
        if self.get_child1() is child:
            living_child = self.get_child2()
            self.remove(living_child)
            self.get_parent().replace_child(self, living_child)
            self.grab_box_terminal_focus(living_child)
        elif self.get_child2() is child:
            living_child = self.get_child1()
            self.remove(living_child)
            self.get_parent().replace_child(self, living_child)
            self.grab_box_terminal_focus(living_child)
        else:
            print("I have never seen this widget!")


class TabLabelEventBox(Gtk.EventBox):
    def __init__(self, notebook, text, settings):
        super().__init__()
        self.notebook = notebook
        self.box = Gtk.Box(Gtk.Orientation.HORIZONTAL, 0, visible=True)
        self.label = Gtk.Label(text, visible=True)
        self.close_button = Gtk.Button(
            image=Gtk.Image.new_from_icon_name("window-close", Gtk.IconSize.MENU),
            relief=Gtk.ReliefStyle.NONE,
        )
        self.close_button.connect("clicked", self.on_close)
        settings.general.bind(
            "tab-close-buttons", self.close_button, "visible", Gio.SettingsBindFlags.GET
        )
        self.box.pack_start(self.label, True, True, 0)
        self.box.pack_end(self.close_button, False, False, 0)
        self.add(self.box)
        self.connect("button-press-event", self.on_button_press, self.label)

    def set_text(self, text):
        self.label.set_text(text)

    def get_text(self):
        return self.label.get_text()

    def grab_focus_on_last_focused_terminal(self):
        server_time = get_server_time(self.notebook.guake.window)
        self.notebook.guake.window.get_window().focus(server_time)
        self.notebook.get_current_terminal().grab_focus()

    def on_button_press(self, target, event, user_data):
        if event.button == 3:

            menu = mk_tab_context_menu(self)