- Add run or open to search suggestions where appropriate
- Add tooltips to search and execute buttons - Replace the deprecated icon for adding packages with package-x-generic - Remove unnecessary i18n duplication in python, leave it to makepot
This commit is contained in:
parent
7504a11bba
commit
bf8d3ff697
1
debian/control
vendored
1
debian/control
vendored
@ -25,6 +25,7 @@ Depends:
|
||||
gir1.2-gtk-3.0,
|
||||
gir1.2-mate-desktop,
|
||||
mozo
|
||||
Recommends: gir1.2-vte-2.91
|
||||
Description: Advanced MATE menu
|
||||
One of the most advanced menus under Linux. MintMenu supports filtering,
|
||||
favorites, easy-uninstallation, autosession, and many other features.
|
||||
|
@ -471,7 +471,7 @@ class MenuWin(object):
|
||||
self.keybinder.connect("activate", self.onBindingPress)
|
||||
self.keybinder.start()
|
||||
self.settings.connect("changed::hot-key", self.hotkeyChanged)
|
||||
print("Binding to Hot Key: %s" % self.hotkeyText)
|
||||
# print("Binding to Hot Key: %s" % self.hotkeyText)
|
||||
except Exception as e:
|
||||
self.keybinder = None
|
||||
print("** WARNING ** - Keybinder Error")
|
||||
|
@ -12,6 +12,18 @@
|
||||
<property name="value">1</property>
|
||||
<property name="step_increment">1</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment11">
|
||||
<property name="lower">200</property>
|
||||
<property name="upper">10000</property>
|
||||
<property name="value">600</property>
|
||||
<property name="step_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment12">
|
||||
<property name="lower">200</property>
|
||||
<property name="upper">10000</property>
|
||||
<property name="value">500</property>
|
||||
<property name="step_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment2">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
@ -750,8 +762,8 @@
|
||||
<property name="margin_left">7</property>
|
||||
<property name="margin_right">7</property>
|
||||
<property name="margin_bottom">7</property>
|
||||
<property name="row_spacing">5</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="showAppComments">
|
||||
<property name="label" translatable="yes">Show application comments</property>
|
||||
@ -759,8 +771,8 @@
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_top">11</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@ -776,7 +788,7 @@
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@ -787,12 +799,12 @@
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="hover">
|
||||
<property name="label" translatable="yes">Hover</property>
|
||||
<property name="label" translatable="yes">Open categories on hover</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@ -823,7 +835,7 @@
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="adjustment">adjustment4</property>
|
||||
<property name="value">100</property>
|
||||
<property name="value">99.999999999776477</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
@ -893,7 +905,7 @@
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@ -909,7 +921,7 @@
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@ -925,7 +937,7 @@
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
@ -934,6 +946,101 @@
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="allow-execute">
|
||||
<property name="label" translatable="yes">Enable run/open features for search entry</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">9</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="integrated-terminal-height">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="text">500</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="adjustment">adjustment12</property>
|
||||
<property name="value">500</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="integrated-terminal-width">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="text">600</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="adjustment">adjustment11</property>
|
||||
<property name="value">600</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="integrated-terminal-height-label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_left">18</property>
|
||||
<property name="label" translatable="yes">Output/terminal height (pixels):</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">12</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="integrated-terminal-width-label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_left">18</property>
|
||||
<property name="label" translatable="yes">Output/terminal width (pixels):</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">11</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="integrated-terminal-enabled">
|
||||
<property name="label" translatable="yes">Enable integrated terminal</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_left">18</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">10</property>
|
||||
<property name="width">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -134,6 +134,16 @@ class mintMenuConfig(object):
|
||||
self.bindGSettingsValueToWidget(self.settingsApplications, "bool", "remember-filter", self.rememberFilter, "toggled", self.rememberFilter.set_active, self.rememberFilter.get_active)
|
||||
self.bindGSettingsValueToWidget(self.settingsApplications, "bool", "enable-internet-search", self.enableInternetSearch, "toggled", self.enableInternetSearch.set_active, self.enableInternetSearch.get_active)
|
||||
|
||||
self.allow_execute = self.builder.get_object("allow-execute")
|
||||
self.integrated_terminal_enabled = self.builder.get_object("integrated-terminal-enabled")
|
||||
self.integrated_terminal_width = self.builder.get_object("integrated-terminal-width")
|
||||
self.integrated_terminal_height = self.builder.get_object("integrated-terminal-height")
|
||||
|
||||
self.bindGSettingsValueToWidget(self.settingsApplications, "bool", "allow-execute", self.allow_execute, "toggled", self.allow_execute.set_active, self.allow_execute.get_active)
|
||||
self.bindGSettingsValueToWidget(self.settingsApplications, "bool", "integrated-terminal-enabled", self.integrated_terminal_enabled, "toggled", self.integrated_terminal_enabled.set_active, self.integrated_terminal_enabled.get_active)
|
||||
self.bindGSettingsValueToWidget(self.settingsApplications, "int", "integrated-terminal-width", self.integrated_terminal_width, "value-changed", self.integrated_terminal_width.set_value, self.integrated_terminal_width.get_value)
|
||||
self.bindGSettingsValueToWidget(self.settingsApplications, "int", "integrated-terminal-height", self.integrated_terminal_height, "value-changed", self.integrated_terminal_height.set_value, self.integrated_terminal_height.get_value)
|
||||
|
||||
self.bindGSettingsValueToWidget(self.settingsPlaces, "int", "icon-size", self.placesIconSize, "value-changed", self.placesIconSize.set_value, self.placesIconSize.get_value)
|
||||
self.bindGSettingsValueToWidget(self.settingsSystem, "int", "icon-size", self.systemIconSize, "value-changed", self.systemIconSize.set_value, self.systemIconSize.get_value)
|
||||
|
||||
|
@ -362,7 +362,7 @@
|
||||
<object class="GtkLabel" id="searchLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Search</property>
|
||||
<property name="label" translatable="yes">Search/Run:</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
@ -393,6 +393,33 @@
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="executeButton">
|
||||
<property name="width_request">28</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="focus_on_click">False</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Try to execute or open your entry</property>
|
||||
<property name="relief">none</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="image2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="has_default">True</property>
|
||||
<property name="stock">gtk-execute</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="searchButton">
|
||||
<property name="width_request">28</property>
|
||||
@ -420,7 +447,7 @@
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -19,6 +19,12 @@ from plugins.easybuttons import (ApplicationLauncher, CategoryButton,
|
||||
MenuApplicationLauncher)
|
||||
from plugins.easygsettings import EasyGSettings
|
||||
|
||||
try:
|
||||
from plugins.terminal import IntegratedTerminal
|
||||
hasVte = True
|
||||
except:
|
||||
hasVte = False
|
||||
|
||||
# i18n
|
||||
gettext.install("mintmenu", "/usr/share/linuxmint/locale")
|
||||
home = os.path.expanduser("~")
|
||||
@ -29,6 +35,23 @@ class PackageDescriptor():
|
||||
self.summary = summary
|
||||
self.description = description
|
||||
|
||||
class subprocess_thread(threading.Thread):
|
||||
|
||||
def __init__(self, _cmd, parent):
|
||||
threading.Thread.__init__(self)
|
||||
self.cmd = _cmd
|
||||
self.parent = parent
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
output = subprocess.check_output(self.cmd,
|
||||
stderr=subprocess.STDOUT, cwd=home, shell=True)
|
||||
if output:
|
||||
GLib.timeout_add(0, self.parent.subprocess_thread_output, output, self.cmd)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if e.output:
|
||||
GLib.timeout_add(0, self.parent.subprocess_thread_output, e.output, self.cmd)
|
||||
|
||||
# import time
|
||||
# def print_timing(func):
|
||||
# def wrapper(*arg):
|
||||
@ -181,9 +204,8 @@ class pluginclass(object):
|
||||
self.favoritesBox = self.builder.get_object("favoritesBox")
|
||||
self.applicationsScrolledWindow = self.builder.get_object("applicationsScrolledWindow")
|
||||
|
||||
|
||||
self.headingstocolor = [self.builder.get_object("label6"), self.builder.get_object("label2")]
|
||||
self.numApps = 0
|
||||
|
||||
# These properties are NECESSARY to maintain consistency
|
||||
|
||||
# Set 'window' property for the plugin (Must be the root widget)
|
||||
@ -233,13 +255,14 @@ class pluginclass(object):
|
||||
self.settings.notifyAdd("use-apt", self.switchAPTUsage)
|
||||
self.settings.notifyAdd("fav-cols", self.changeFavCols)
|
||||
self.settings.notifyAdd("remember-filter", self.changeRememberFilter)
|
||||
self.settings.notifyAdd("enable-internet-search", self.changeEnableInternetSearch)
|
||||
self.settings.notifyAdd("allow-execute", self.update_allow_execute)
|
||||
|
||||
self.settings.bindGSettingsEntryToVar("int", "category-hover-delay", self, "categoryhoverdelay")
|
||||
self.settings.bindGSettingsEntryToVar("bool", "do-not-filter", self, "donotfilterapps")
|
||||
self.settings.bindGSettingsEntryToVar("bool", "enable-internet-search", self, "enableInternetSearch")
|
||||
self.settings.bindGSettingsEntryToVar("string", "search-command", self, "searchtool")
|
||||
self.settings.bindGSettingsEntryToVar("int", "default-tab", self, "defaultTab")
|
||||
self.settings.bindGSettingsEntryToVar("bool", "integrated-terminal-enabled", self, "integrated_terminal_enabled")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
@ -275,6 +298,8 @@ class pluginclass(object):
|
||||
self.panel_position = -1
|
||||
|
||||
self.builder.get_object("searchButton").connect("button-press-event", self.searchPopup)
|
||||
self.builder.get_object("executeButton").connect("button-press-event", self.on_execute_button_pressed)
|
||||
self.update_allow_execute()
|
||||
|
||||
# self.icon_theme = Gtk.IconTheme.get_default()
|
||||
# self.icon_theme.connect("changed", self.on_icon_theme_changed)
|
||||
@ -383,8 +408,19 @@ class pluginclass(object):
|
||||
def changeRememberFilter(self, settings, key, args):
|
||||
self.rememberFilter = settings.get_boolean(key)
|
||||
|
||||
def changeEnableInternetSearch(self, settings, key, args):
|
||||
self.enableInternetSearch = settings.get_boolean(key)
|
||||
def update_allow_execute(self, settings=None, key=None, args=None):
|
||||
if settings and key:
|
||||
self.allow_execute = settings.get_boolean(key)
|
||||
#self.builder.get_object("executeButton").set_visible(self.allow_execute)
|
||||
if self.allow_execute:
|
||||
self.builder.get_object("executeButton").show()
|
||||
searchLabel_text = _("Search/Run:")
|
||||
else:
|
||||
self.builder.get_object("executeButton").hide()
|
||||
searchLabel_text = _("Search:")
|
||||
searchLabel = self.builder.get_object("searchLabel")
|
||||
searchLabel.set_markup("<b>%s</b>" % searchLabel_text)
|
||||
searchLabel.show()
|
||||
|
||||
def changeShowApplicationComments(self, settings, key, args):
|
||||
self.showapplicationcomments = settings.get_boolean(key)
|
||||
@ -465,6 +501,10 @@ class pluginclass(object):
|
||||
self.useAPT = self.settings.get("bool", "use-apt")
|
||||
self.rememberFilter = self.settings.get("bool", "remember-filter")
|
||||
self.enableInternetSearch = self.settings.get("bool", "enable-internet-search")
|
||||
self.allow_execute = self.settings.get("bool", "allow-execute")
|
||||
self.integrated_terminal_enabled = self.settings.get("bool", "integrated-terminal-enabled")
|
||||
self.integrated_terminal_width = self.settings.get("int", "integrated-terminal-width")
|
||||
self.integrated_terminal_height = self.settings.get("int", "integrated-terminal-height")
|
||||
|
||||
self.lastActiveTab = self.settings.get("int", "last-active-tab")
|
||||
self.defaultTab = self.settings.get("int", "default-tab")
|
||||
@ -607,6 +647,22 @@ class pluginclass(object):
|
||||
|
||||
self.applicationsBox.get_children()[-1].grab_focus()
|
||||
|
||||
def add_execute_suggestions(self, user_text):
|
||||
# Wait to see if the keyword has changed.. before doing anything
|
||||
text = self.searchEntry.get_text()
|
||||
if user_text != text:
|
||||
return
|
||||
commands = user_text.split()
|
||||
cmd = self.verify_command(commands[0])
|
||||
if cmd:
|
||||
text = "<b>%s</b>" % cgi.escape(text)
|
||||
commands[0] = cmd
|
||||
if cmd.startswith("xdg-open"):
|
||||
self.add_suggestion("document-open", _("Try to open %s") % text, None, self.execute_user_input, commands, False)
|
||||
else:
|
||||
self.add_suggestion("application-x-executable", _("Run %s") % text, None, self.execute_user_input, commands, False)
|
||||
self.applicationsBox.get_children()[-1].grab_focus()
|
||||
|
||||
def add_apt_filter_results(self, keyword):
|
||||
try:
|
||||
# Wait to see if the keyword has changed.. before doing anything
|
||||
@ -660,7 +716,7 @@ class pluginclass(object):
|
||||
for word in keywords:
|
||||
if word != "":
|
||||
name = name.replace(word, "<b>%s</b>" % word)
|
||||
self.add_suggestion(Gtk.STOCK_ADD,
|
||||
self.add_suggestion("package-x-generic",
|
||||
_("Install package '%s'") % name,
|
||||
"%s\n\n%s\n\n%s" % (pkg.name, pkg.summary, pkg.description),
|
||||
self.apturl_install, pkg.name)
|
||||
@ -725,7 +781,7 @@ class pluginclass(object):
|
||||
if self.donotfilterapps:
|
||||
widget.set_text("")
|
||||
else:
|
||||
text = widget.get_text()
|
||||
text = widget.get_text().strip()
|
||||
if self.lastActiveTab != 1:
|
||||
self.changeTab(1, clear = False)
|
||||
text = widget.get_text()
|
||||
@ -750,8 +806,11 @@ class pluginclass(object):
|
||||
if not showns:
|
||||
if len(text) >= 3:
|
||||
self.add_search_suggestions(text)
|
||||
if self.allow_execute:
|
||||
GLib.timeout_add(150, self.add_execute_suggestions, text)
|
||||
if self.useAPT:
|
||||
GLib.timeout_add(300, self.add_apt_filter_results, text)
|
||||
|
||||
for i in self.categoriesBox.get_children():
|
||||
i.released()
|
||||
i.set_relief(Gtk.ReliefStyle.NONE)
|
||||
@ -786,10 +845,17 @@ class pluginclass(object):
|
||||
self.Filter(widget, category)
|
||||
|
||||
def keyPress(self, widget, event):
|
||||
""" Forward all text to the search box """
|
||||
# Forward all text to the search box
|
||||
if event.string.strip() or event.keyval == Gdk.KEY_space:
|
||||
self.searchEntry.event(event)
|
||||
return True
|
||||
|
||||
# Ctrl+Enter for the Run button
|
||||
if self.allow_execute and \
|
||||
(event.state & Gdk.ModifierType.CONTROL_MASK) and \
|
||||
event.keyval == Gdk.KEY_Return:
|
||||
self.on_execute_button_pressed(widget, event)
|
||||
return True
|
||||
return False
|
||||
|
||||
def favPopup(self, widget, event):
|
||||
@ -931,6 +997,102 @@ class pluginclass(object):
|
||||
mTree.attach_to_widget(widget, None)
|
||||
mTree.popup(None, None, None, None, event.button, event.time)
|
||||
|
||||
def subprocess_thread_output(self, output, command):
|
||||
output = output.strip()
|
||||
if not output:
|
||||
return
|
||||
self.mintMenuWin.hide()
|
||||
|
||||
def on_key_press_event(widget, event):
|
||||
if event.keyval == Gdk.KEY_Escape or \
|
||||
event.keyval == Gdk.KEY_Return or \
|
||||
event.keyval == Gdk.KEY_KP_Enter:
|
||||
widget.destroy()
|
||||
|
||||
window = Gtk.Window()
|
||||
window.set_title(_("Output from your command: %s" % command))
|
||||
window.set_transient_for(self.window)
|
||||
window.set_icon_from_file("/usr/lib/linuxmint/mintMenu/icon.svg")
|
||||
#window.set_skip_taskbar_hint(True)
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
output_box = Gtk.TextView()
|
||||
output_box.set_editable(False)
|
||||
output_box.set_monospace(True)
|
||||
output_box.set_right_margin(16)
|
||||
output_box.set_wrap_mode(Gtk.WrapMode.WORD)
|
||||
scrolled_window = Gtk.ScrolledWindow()
|
||||
scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
||||
scrolled_window.set_hexpand(True)
|
||||
scrolled_window.set_vexpand(True)
|
||||
scrolled_window.set_size_request(self.integrated_terminal_width, self.integrated_terminal_height)
|
||||
scrolled_window.add(output_box)
|
||||
box.pack_start(scrolled_window, False, True, 0)
|
||||
window.add(box)
|
||||
|
||||
output_box.get_buffer().set_text(output)
|
||||
window.show_all()
|
||||
window.connect("key-press-event", on_key_press_event)
|
||||
|
||||
@staticmethod
|
||||
def find_file_in_path(filename):
|
||||
if os.path.isfile(filename):
|
||||
return filename
|
||||
path = os.environ.get("PATH", os.defpath)
|
||||
paths = path.split(os.pathsep)
|
||||
paths.append(home)
|
||||
for p in paths:
|
||||
f = os.path.join(p, filename)
|
||||
if os.path.isfile(f):
|
||||
return f
|
||||
return None
|
||||
|
||||
def verify_command(self, cmd):
|
||||
cmd = os.path.expanduser(os.path.expandvars(cmd))
|
||||
cmd = self.find_file_in_path(cmd)
|
||||
if cmd and not os.access(cmd, os.X_OK):
|
||||
cmd = "xdg-open %s" % cmd
|
||||
# TODO: We could check mime type first to ensure this goes through
|
||||
return cmd
|
||||
|
||||
def on_execute_button_pressed(self, widget, event):
|
||||
command = self.searchEntry.get_text().strip()
|
||||
if command:
|
||||
self.execute_user_input(widget, command.split())
|
||||
|
||||
def execute_user_input(self, widget, commands, verify=True):
|
||||
self.mintMenuWin.hide()
|
||||
if verify:
|
||||
cmd = self.verify_command(commands[0])
|
||||
if not cmd:
|
||||
# file not found
|
||||
dialog = Gtk.MessageDialog(self.window,
|
||||
Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
||||
Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
|
||||
_("Invalid command or file reference."))
|
||||
dialog.set_title("mintMenu")
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
return
|
||||
commands[0] = cmd
|
||||
command = " ".join(commands)
|
||||
try:
|
||||
global hasVte
|
||||
if hasVte and self.integrated_terminal_enabled:
|
||||
try:
|
||||
IntegratedTerminal(command,
|
||||
_("mintMenu Integrated Terminal"),
|
||||
width=self.integrated_terminal_width,
|
||||
height=self.integrated_terminal_height
|
||||
)
|
||||
except Exception as e:
|
||||
print("IntegratedTerminal exception:", e)
|
||||
hasVte = False
|
||||
if not hasVte or not self.integrated_terminal_enabled:
|
||||
thread = subprocess_thread(command, self)
|
||||
thread.start()
|
||||
except:
|
||||
pass
|
||||
|
||||
def searchPopup(self, widget, event):
|
||||
def add_menu_item(icon=None, text=None, callback=None):
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
|
100
usr/lib/linuxmint/mintMenu/plugins/terminal.py
Normal file
100
usr/lib/linuxmint/mintMenu/plugins/terminal.py
Normal file
@ -0,0 +1,100 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
|
||||
import gi
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version('Vte', '2.91')
|
||||
from gi.repository import Gtk, Gdk, GLib, Vte
|
||||
|
||||
class IntegratedTerminal(Gtk.Window):
|
||||
|
||||
def __init__(self, command, title=None, shell=None, cwd=None, width=600, height=500):
|
||||
try:
|
||||
self.terminal=Vte.Terminal()
|
||||
self.terminal.set_scrollback_lines(-1)
|
||||
# Setting the font doesn't work for some reason, let's leave it
|
||||
# self.terminal.set_font(Pango.FontDescription(string='Monospace'))
|
||||
self.command = command
|
||||
self.ready = False
|
||||
self.output_handler = self.terminal.connect("cursor-moved",
|
||||
self.on_cursor_moved)
|
||||
# apparently Vte.Terminal.spawn_sync() is deprecated in favour of the
|
||||
# non-existent Vte.Terminal.spawn_async()...
|
||||
self.terminal.spawn_sync(
|
||||
Vte.PtyFlags.DEFAULT, # pty_flags
|
||||
cwd or os.environ.get("HOME"), # working_directory
|
||||
shell or [os.environ.get("SHELL")], # argv
|
||||
[], # envv
|
||||
GLib.SpawnFlags.DO_NOT_REAP_CHILD, # spawn_flags
|
||||
None, # child_setup
|
||||
None, # child_setup_data
|
||||
None # cancellable
|
||||
)
|
||||
except Exception as e:
|
||||
self.close()
|
||||
raise Exception(e)
|
||||
|
||||
Gtk.Window.__init__(self, title=title or "mintMenu Integrated Terminal")
|
||||
self.set_icon_name("utilities-terminal")
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
self.scrolled_window = Gtk.ScrolledWindow()
|
||||
self.scrolled_window.set_hexpand(True)
|
||||
self.scrolled_window.set_vexpand(True)
|
||||
self.scrolled_window.set_size_request(width, height)
|
||||
self.scrolled_window.add(self.terminal)
|
||||
box.pack_start(self.scrolled_window, False, True, 0)
|
||||
self.add(box)
|
||||
self.connect("key-press-event", self.on_key_press_event)
|
||||
self.terminal.connect("child-exited", self.exit)
|
||||
self.terminal.connect("eof", self.exit)
|
||||
self.terminal.set_rewrap_on_resize(True)
|
||||
|
||||
def on_cursor_moved(self, terminal):
|
||||
if not self.ready:
|
||||
# we have to run the command on a callback because the
|
||||
# spawn_sync() method doesn't wait for the shell to load,
|
||||
# so instead we have to wait for the shell to create a prompt
|
||||
self.ready = True
|
||||
# if we don't show the terminal once after this it won't
|
||||
# always receive output, so we show it and hide it again right
|
||||
# away. Whatever works...
|
||||
self.show_all()
|
||||
self.hide()
|
||||
# Now we can go:
|
||||
command = "%s\n" % self.command
|
||||
self.terminal.feed_child(command, len(command))
|
||||
return
|
||||
# Unfortunately we cannot guarantee that the command is on the first
|
||||
# line (the shell may display somethinge else first), so we have to
|
||||
# search for it:
|
||||
x, y = terminal.get_cursor_position()
|
||||
contents, dummy = terminal.get_text_range(0, 0, x, y, None, None)
|
||||
lines = contents.split("\n")
|
||||
prefix = ""
|
||||
command_found = False
|
||||
for line in lines:
|
||||
if not line:
|
||||
continue
|
||||
if command_found:
|
||||
if not line.lstrip(prefix):
|
||||
# we got a command prompt, exit
|
||||
self.exit()
|
||||
break
|
||||
# the command generated output, show the terminal
|
||||
terminal.disconnect(self.output_handler)
|
||||
self.show_all()
|
||||
break
|
||||
if self.command in line:
|
||||
# this is our command line
|
||||
prefix = line.split(self.command, 1)[0]
|
||||
command_found = True
|
||||
|
||||
def on_key_press_event(self, widget, event):
|
||||
if event.keyval == Gdk.KEY_Escape:
|
||||
self.exit()
|
||||
|
||||
def exit(self, *args):
|
||||
# Gtk.main_quit()
|
||||
self.close()
|
@ -307,6 +307,28 @@
|
||||
<summary></summary>
|
||||
<description></description>
|
||||
</key>
|
||||
|
||||
<key type="b" name="allow-execute">
|
||||
<default>true</default>
|
||||
<summary></summary>
|
||||
<description></description>
|
||||
</key>
|
||||
<key type="b" name="integrated-terminal-enabled">
|
||||
<default>true</default>
|
||||
<summary></summary>
|
||||
<description></description>
|
||||
</key>
|
||||
<key type="i" name="integrated-terminal-width">
|
||||
<default>600</default>
|
||||
<summary></summary>
|
||||
<description></description>
|
||||
</key>
|
||||
|
||||
<key type="i" name="integrated-terminal-height">
|
||||
<default>500</default>
|
||||
<summary></summary>
|
||||
<description></description>
|
||||
</key>
|
||||
</schema>
|
||||
|
||||
<schema id="com.linuxmint.mintmenu.plugins.system_management" path="/com/linuxmint/mintmenu/plugins/system_management/">
|
||||
|
Loading…
Reference in New Issue
Block a user