- 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:
gm10 2019-02-11 15:31:43 +01:00
parent 7504a11bba
commit bf8d3ff697
No known key found for this signature in database
GPG Key ID: A981D4EA8CF993A9
8 changed files with 450 additions and 21 deletions

1
debian/control vendored
View File

@ -25,6 +25,7 @@ Depends:
gir1.2-gtk-3.0, gir1.2-gtk-3.0,
gir1.2-mate-desktop, gir1.2-mate-desktop,
mozo mozo
Recommends: gir1.2-vte-2.91
Description: Advanced MATE menu Description: Advanced MATE menu
One of the most advanced menus under Linux. MintMenu supports filtering, One of the most advanced menus under Linux. MintMenu supports filtering,
favorites, easy-uninstallation, autosession, and many other features. favorites, easy-uninstallation, autosession, and many other features.

View File

@ -471,7 +471,7 @@ class MenuWin(object):
self.keybinder.connect("activate", self.onBindingPress) self.keybinder.connect("activate", self.onBindingPress)
self.keybinder.start() self.keybinder.start()
self.settings.connect("changed::hot-key", self.hotkeyChanged) 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: except Exception as e:
self.keybinder = None self.keybinder = None
print("** WARNING ** - Keybinder Error") print("** WARNING ** - Keybinder Error")

View File

@ -12,6 +12,18 @@
<property name="value">1</property> <property name="value">1</property>
<property name="step_increment">1</property> <property name="step_increment">1</property>
</object> </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"> <object class="GtkAdjustment" id="adjustment2">
<property name="upper">100</property> <property name="upper">100</property>
<property name="step_increment">1</property> <property name="step_increment">1</property>
@ -750,8 +762,8 @@
<property name="margin_left">7</property> <property name="margin_left">7</property>
<property name="margin_right">7</property> <property name="margin_right">7</property>
<property name="margin_bottom">7</property> <property name="margin_bottom">7</property>
<property name="row_spacing">5</property> <property name="row_spacing">6</property>
<property name="column_spacing">2</property> <property name="column_spacing">12</property>
<child> <child>
<object class="GtkCheckButton" id="showAppComments"> <object class="GtkCheckButton" id="showAppComments">
<property name="label" translatable="yes">Show application comments</property> <property name="label" translatable="yes">Show application comments</property>
@ -759,8 +771,8 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</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="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="margin_top">11</property>
<property name="xalign">0.5</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -776,7 +788,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</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="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> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -787,12 +799,12 @@
</child> </child>
<child> <child>
<object class="GtkCheckButton" id="hover"> <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="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</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="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> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -823,7 +835,7 @@
<property name="primary_icon_activatable">False</property> <property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property>
<property name="adjustment">adjustment4</property> <property name="adjustment">adjustment4</property>
<property name="value">100</property> <property name="value">99.999999999776477</property>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
@ -893,7 +905,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</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="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> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -909,7 +921,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</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="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> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -925,7 +937,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</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="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> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
@ -934,6 +946,101 @@
<property name="width">2</property> <property name="width">2</property>
</packing> </packing>
</child> </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> </object>
</child> </child>
</object> </object>

View File

@ -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", "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.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.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) self.bindGSettingsValueToWidget(self.settingsSystem, "int", "icon-size", self.systemIconSize, "value-changed", self.systemIconSize.set_value, self.systemIconSize.get_value)

View File

@ -362,7 +362,7 @@
<object class="GtkLabel" id="searchLabel"> <object class="GtkLabel" id="searchLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Search</property> <property name="label" translatable="yes">Search/Run:</property>
<attributes> <attributes>
<attribute name="weight" value="bold"/> <attribute name="weight" value="bold"/>
</attributes> </attributes>
@ -393,6 +393,33 @@
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </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> <child>
<object class="GtkButton" id="searchButton"> <object class="GtkButton" id="searchButton">
<property name="width_request">28</property> <property name="width_request">28</property>
@ -420,7 +447,7 @@
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">False</property>
<property name="pack_type">end</property> <property name="pack_type">end</property>
<property name="position">2</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
</object> </object>

View File

@ -19,6 +19,12 @@ from plugins.easybuttons import (ApplicationLauncher, CategoryButton,
MenuApplicationLauncher) MenuApplicationLauncher)
from plugins.easygsettings import EasyGSettings from plugins.easygsettings import EasyGSettings
try:
from plugins.terminal import IntegratedTerminal
hasVte = True
except:
hasVte = False
# i18n # i18n
gettext.install("mintmenu", "/usr/share/linuxmint/locale") gettext.install("mintmenu", "/usr/share/linuxmint/locale")
home = os.path.expanduser("~") home = os.path.expanduser("~")
@ -29,6 +35,23 @@ class PackageDescriptor():
self.summary = summary self.summary = summary
self.description = description 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 # import time
# def print_timing(func): # def print_timing(func):
# def wrapper(*arg): # def wrapper(*arg):
@ -181,9 +204,8 @@ class pluginclass(object):
self.favoritesBox = self.builder.get_object("favoritesBox") self.favoritesBox = self.builder.get_object("favoritesBox")
self.applicationsScrolledWindow = self.builder.get_object("applicationsScrolledWindow") self.applicationsScrolledWindow = self.builder.get_object("applicationsScrolledWindow")
self.headingstocolor = [self.builder.get_object("label6"), self.builder.get_object("label2")]
self.numApps = 0 self.numApps = 0
# These properties are NECESSARY to maintain consistency # These properties are NECESSARY to maintain consistency
# Set 'window' property for the plugin (Must be the root widget) # 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("use-apt", self.switchAPTUsage)
self.settings.notifyAdd("fav-cols", self.changeFavCols) self.settings.notifyAdd("fav-cols", self.changeFavCols)
self.settings.notifyAdd("remember-filter", self.changeRememberFilter) 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("int", "category-hover-delay", self, "categoryhoverdelay")
self.settings.bindGSettingsEntryToVar("bool", "do-not-filter", self, "donotfilterapps") self.settings.bindGSettingsEntryToVar("bool", "do-not-filter", self, "donotfilterapps")
self.settings.bindGSettingsEntryToVar("bool", "enable-internet-search", self, "enableInternetSearch") self.settings.bindGSettingsEntryToVar("bool", "enable-internet-search", self, "enableInternetSearch")
self.settings.bindGSettingsEntryToVar("string", "search-command", self, "searchtool") self.settings.bindGSettingsEntryToVar("string", "search-command", self, "searchtool")
self.settings.bindGSettingsEntryToVar("int", "default-tab", self, "defaultTab") self.settings.bindGSettingsEntryToVar("int", "default-tab", self, "defaultTab")
self.settings.bindGSettingsEntryToVar("bool", "integrated-terminal-enabled", self, "integrated_terminal_enabled")
except Exception as e: except Exception as e:
print(e) print(e)
@ -275,6 +298,8 @@ class pluginclass(object):
self.panel_position = -1 self.panel_position = -1
self.builder.get_object("searchButton").connect("button-press-event", self.searchPopup) 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 = Gtk.IconTheme.get_default()
# self.icon_theme.connect("changed", self.on_icon_theme_changed) # self.icon_theme.connect("changed", self.on_icon_theme_changed)
@ -383,8 +408,19 @@ class pluginclass(object):
def changeRememberFilter(self, settings, key, args): def changeRememberFilter(self, settings, key, args):
self.rememberFilter = settings.get_boolean(key) self.rememberFilter = settings.get_boolean(key)
def changeEnableInternetSearch(self, settings, key, args): def update_allow_execute(self, settings=None, key=None, args=None):
self.enableInternetSearch = settings.get_boolean(key) 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): def changeShowApplicationComments(self, settings, key, args):
self.showapplicationcomments = settings.get_boolean(key) self.showapplicationcomments = settings.get_boolean(key)
@ -465,6 +501,10 @@ class pluginclass(object):
self.useAPT = self.settings.get("bool", "use-apt") self.useAPT = self.settings.get("bool", "use-apt")
self.rememberFilter = self.settings.get("bool", "remember-filter") self.rememberFilter = self.settings.get("bool", "remember-filter")
self.enableInternetSearch = self.settings.get("bool", "enable-internet-search") 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.lastActiveTab = self.settings.get("int", "last-active-tab")
self.defaultTab = self.settings.get("int", "default-tab") self.defaultTab = self.settings.get("int", "default-tab")
@ -607,6 +647,22 @@ class pluginclass(object):
self.applicationsBox.get_children()[-1].grab_focus() 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): def add_apt_filter_results(self, keyword):
try: try:
# Wait to see if the keyword has changed.. before doing anything # Wait to see if the keyword has changed.. before doing anything
@ -660,7 +716,7 @@ class pluginclass(object):
for word in keywords: for word in keywords:
if word != "": if word != "":
name = name.replace(word, "<b>%s</b>" % 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, _("Install package '%s'") % name,
"%s\n\n%s\n\n%s" % (pkg.name, pkg.summary, pkg.description), "%s\n\n%s\n\n%s" % (pkg.name, pkg.summary, pkg.description),
self.apturl_install, pkg.name) self.apturl_install, pkg.name)
@ -725,7 +781,7 @@ class pluginclass(object):
if self.donotfilterapps: if self.donotfilterapps:
widget.set_text("") widget.set_text("")
else: else:
text = widget.get_text() text = widget.get_text().strip()
if self.lastActiveTab != 1: if self.lastActiveTab != 1:
self.changeTab(1, clear = False) self.changeTab(1, clear = False)
text = widget.get_text() text = widget.get_text()
@ -750,8 +806,11 @@ class pluginclass(object):
if not showns: if not showns:
if len(text) >= 3: if len(text) >= 3:
self.add_search_suggestions(text) self.add_search_suggestions(text)
if self.allow_execute:
GLib.timeout_add(150, self.add_execute_suggestions, text)
if self.useAPT: if self.useAPT:
GLib.timeout_add(300, self.add_apt_filter_results, text) GLib.timeout_add(300, self.add_apt_filter_results, text)
for i in self.categoriesBox.get_children(): for i in self.categoriesBox.get_children():
i.released() i.released()
i.set_relief(Gtk.ReliefStyle.NONE) i.set_relief(Gtk.ReliefStyle.NONE)
@ -786,10 +845,17 @@ class pluginclass(object):
self.Filter(widget, category) self.Filter(widget, category)
def keyPress(self, widget, event): 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: if event.string.strip() or event.keyval == Gdk.KEY_space:
self.searchEntry.event(event) self.searchEntry.event(event)
return True 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 return False
def favPopup(self, widget, event): def favPopup(self, widget, event):
@ -931,6 +997,102 @@ class pluginclass(object):
mTree.attach_to_widget(widget, None) mTree.attach_to_widget(widget, None)
mTree.popup(None, None, None, None, event.button, event.time) 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 searchPopup(self, widget, event):
def add_menu_item(icon=None, text=None, callback=None): def add_menu_item(icon=None, text=None, callback=None):
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)

View 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()

View File

@ -307,6 +307,28 @@
<summary></summary> <summary></summary>
<description></description> <description></description>
</key> </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>
<schema id="com.linuxmint.mintmenu.plugins.system_management" path="/com/linuxmint/mintmenu/plugins/system_management/"> <schema id="com.linuxmint.mintmenu.plugins.system_management" path="/com/linuxmint/mintmenu/plugins/system_management/">