From a8898e734fc9778765785024cedd4f005b0fe9b4 Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 01:25:49 +0000 Subject: [PATCH 01/13] Fixed popup menus behaviour Some popups, like the right click menus on the Apps page or the Search button, pop up and back down immediately. Now they work as they should. Fixes #28 --- usr/lib/linuxmint/mintMenu/plugins/applications.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/usr/lib/linuxmint/mintMenu/plugins/applications.py b/usr/lib/linuxmint/mintMenu/plugins/applications.py index c41ebcf..3d36e85 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/applications.py +++ b/usr/lib/linuxmint/mintMenu/plugins/applications.py @@ -341,7 +341,7 @@ class pluginclass( object ): self.panel = "top" self.panel_position = -1 - self.builder.get_object("searchButton").connect( "button-release-event", self.searchPopup ) + self.builder.get_object("searchButton").connect( "button-press-event", self.searchPopup ) def refresh_apt_cache(self): if self.useAPT: @@ -1035,7 +1035,7 @@ class pluginclass( object ): startupMenuItem.connect( "toggled", self.onAddToStartup, widget ) mTree.connect( 'deactivate', self.onMenuPopupDeactivate) - gtk.gtk_menu_popup(hash(mTree), None, None, None, None, 0, 0) + gtk.gtk_menu_popup(hash(mTree), None, None, None, None, event.button, event.time) def onMenuPopupDeactivate( self, widget): self.mintMenuWin.grab() @@ -1348,7 +1348,7 @@ class pluginclass( object ): space = Gtk.EventBox() space.set_size_request( -1, 20 ) space.set_visible_window(False) - space.connect( "button_release_event", self.favPopup ) + space.connect( "button-press-event", self.favPopup ) space.type = "space" self.mintMenuWin.SetPaneColors( [ space ] ) @@ -1367,7 +1367,7 @@ class pluginclass( object ): box.type = "separator" box.add(separator) box.set_visible_window(False) - box.connect( "button_release_event", self.favPopup ) + box.connect( "button-press-event", self.favPopup ) box.show_all() return box @@ -1405,7 +1405,7 @@ class pluginclass( object ): if favButton.appExec: favButton.show() favButton.connect( "popup-menu", self.favPopup ) - favButton.connect( "button_release_event", self.favPopup ) + favButton.connect( "button-press-event", self.favPopup ) favButton.connect( "focus-in-event", self.scrollItemIntoView ) favButton.connect( "clicked", lambda w: self.mintMenuWin.hide() ) From 2283c4f36a458c1ebb5ed3f3c96321a75ef8e0fa Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 01:27:26 +0000 Subject: [PATCH 02/13] Search button was clearing the search entry preemptively --- usr/lib/linuxmint/mintMenu/plugins/applications.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/lib/linuxmint/mintMenu/plugins/applications.py b/usr/lib/linuxmint/mintMenu/plugins/applications.py index 3d36e85..fa0c961 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/applications.py +++ b/usr/lib/linuxmint/mintMenu/plugins/applications.py @@ -1114,14 +1114,14 @@ class pluginclass( object ): menu.show_all() - gtk.gtk_menu_popup(hash(menu), None, None, None, None, 3, 0) + gtk.gtk_menu_popup(hash(menu), None, None, None, None, event.button, event.time) #menu.attach_to_widget(self.searchButton, None) #menu.reposition() #menu.reposition() #self.mintMenuWin.grab() menu.connect( 'deactivate', self.onMenuPopupDeactivate) - self.focusSearchEntry() + #self.focusSearchEntry() return True def pos_func(self, menu=None): From e6c3158afcf4c06a7b6ad059be474f8b750e7794 Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 01:28:06 +0000 Subject: [PATCH 03/13] Removed useless gobject import --- usr/lib/linuxmint/mintMenu/plugins/recent.py | 1 - 1 file changed, 1 deletion(-) diff --git a/usr/lib/linuxmint/mintMenu/plugins/recent.py b/usr/lib/linuxmint/mintMenu/plugins/recent.py index a1eefa0..9733bfd 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/recent.py +++ b/usr/lib/linuxmint/mintMenu/plugins/recent.py @@ -4,7 +4,6 @@ gi.require_version("Gtk", "2.0") from gi.repository import Gtk, Pango import sys import os -import gobject import datetime import fcntl from easygsettings import EasyGSettings From 7ef97626baf9b22322fe151f1d62f866d4255dde Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 02:30:26 +0000 Subject: [PATCH 04/13] Solves app and category buttons focus issues The buttons were grabbing the focus out of the search entry on hover, inducing strange behaviour. Fixes #23 --- usr/lib/linuxmint/mintMenu/plugins/applications.py | 4 ---- usr/lib/linuxmint/mintMenu/plugins/easybuttons.py | 4 ---- 2 files changed, 8 deletions(-) diff --git a/usr/lib/linuxmint/mintMenu/plugins/applications.py b/usr/lib/linuxmint/mintMenu/plugins/applications.py index fa0c961..dd466e0 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/applications.py +++ b/usr/lib/linuxmint/mintMenu/plugins/applications.py @@ -878,9 +878,6 @@ class pluginclass( object ): i.released() i.set_relief( Gtk.ReliefStyle.NONE ) widget.set_relief( Gtk.ReliefStyle.HALF ) - widget.grab_focus() - - self.searchEntry.set_text( "" ) self.applicationsScrolledWindow.get_vadjustment().set_value( 0 ) @@ -1121,7 +1118,6 @@ class pluginclass( object ): #menu.reposition() #self.mintMenuWin.grab() menu.connect( 'deactivate', self.onMenuPopupDeactivate) - #self.focusSearchEntry() return True def pos_func(self, menu=None): diff --git a/usr/lib/linuxmint/mintMenu/plugins/easybuttons.py b/usr/lib/linuxmint/mintMenu/plugins/easybuttons.py index 2d0d90d..1f1d799 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/easybuttons.py +++ b/usr/lib/linuxmint/mintMenu/plugins/easybuttons.py @@ -283,7 +283,6 @@ class ApplicationLauncher( easyButton ): self.connectSelf( "focus-in-event", self.onFocusIn ) self.connectSelf( "focus-out-event", self.onFocusOut ) - self.connectSelf( "enter-notify-event", self.onEnterNotify ) self.connectSelf( "clicked", self.execute ) @@ -332,9 +331,6 @@ class ApplicationLauncher( easyButton ): def onFocusOut( self, widget, event ): self.set_relief( Gtk.ReliefStyle.NONE ) - def onEnterNotify( self, widget, event ): - self.grab_focus() - def setupLabels( self ): self.addLabel( self.appName ) From 451011bfe87066a486d5e53c020d21da5cc229a9 Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 03:15:15 +0000 Subject: [PATCH 05/13] Correction of previous commit - clear when clicked Now category buttons only clear text when clicked, not when hovered. It seems more natural, because a now the mouse position is irrelevant when trying to type on the search entry. --- usr/lib/linuxmint/mintMenu/plugins/applications.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/usr/lib/linuxmint/mintMenu/plugins/applications.py b/usr/lib/linuxmint/mintMenu/plugins/applications.py index dd466e0..0cc0475 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/applications.py +++ b/usr/lib/linuxmint/mintMenu/plugins/applications.py @@ -880,6 +880,10 @@ class pluginclass( object ): widget.set_relief( Gtk.ReliefStyle.HALF ) self.applicationsScrolledWindow.get_vadjustment().set_value( 0 ) + + def FilterAndClear( self, widget, category = None ): + self.searchEntry.set_text( "" ) + self.Filter( widget, category ) # Forward all text to the search box def keyPress( self, widget, event ): @@ -1673,7 +1677,7 @@ class pluginclass( object ): else: item["button"].mouseOverHandlerIds = None - item["button"].connect( "clicked", self.Filter, item["filter"] ) + item["button"].connect( "clicked", self.FilterAndClear, item["filter"] ) item["button"].connect( "focus-in-event", self.categoryBtnFocus, item["filter"] ) item["button"].show() From b5845b0676fd5d48532a54f39a3713710bbff9d9 Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 03:20:56 +0000 Subject: [PATCH 06/13] Fixes search entry losing typing focus When the search entry was out of focus and types were redirected to it, if the current selected application was hidden by the filtering, both the holder and entry lost focus. This stopped the typing event redirection. Now the entry is always focused when typing. --- usr/lib/linuxmint/mintMenu/plugins/applications.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/usr/lib/linuxmint/mintMenu/plugins/applications.py b/usr/lib/linuxmint/mintMenu/plugins/applications.py index 0cc0475..857b1bb 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/applications.py +++ b/usr/lib/linuxmint/mintMenu/plugins/applications.py @@ -888,6 +888,8 @@ class pluginclass( object ): # Forward all text to the search box def keyPress( self, widget, event ): if event.string.strip() != "" or event.keyval == Gdk.KEY_BackSpace: + self.searchEntry.grab_focus() + gtk.gtk_editable_set_position(hash(self.searchEntry), -1) self.searchEntry.event( event ) return True From b5d0c7257fa8c8d3571e64afde7f169ccd446948 Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 03:28:13 +0000 Subject: [PATCH 07/13] Removed invisible widgets from coloring --- usr/lib/linuxmint/mintMenu/plugins/applications.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/usr/lib/linuxmint/mintMenu/plugins/applications.py b/usr/lib/linuxmint/mintMenu/plugins/applications.py index 857b1bb..d613df9 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/applications.py +++ b/usr/lib/linuxmint/mintMenu/plugins/applications.py @@ -268,7 +268,7 @@ class pluginclass( object ): self.content_holder =self.builder.get_object( "Applications" ) # Items to get custom colors - self.itemstocolor = [self.builder.get_object( "viewport1" ),self.builder.get_object( "viewport2" ),self.builder.get_object( "viewport3" ),self.builder.get_object( "notebook2" ) ] + self.itemstocolor = [self.builder.get_object( "viewport1" ),self.builder.get_object( "viewport2" ),self.builder.get_object( "viewport3" ) ] # Unset all timers self.filterTimer = None @@ -640,7 +640,6 @@ class pluginclass( object ): separator.set_visible_window(False) separator.set_size_request(-1, 20) separator.type = "separator" - self.mintMenuWin.SetPaneColors( [ separator ] ) separator.show_all() self.applicationsBox.add(separator) self.suggestions.append(separator) @@ -718,8 +717,7 @@ class pluginclass( object ): last_separator.add(Gtk.HSeparator()) last_separator.set_visible_window(False) last_separator.set_size_request(-1, 20) - last_separator.type = "separator" - self.mintMenuWin.SetPaneColors( [ last_separator ] ) + last_separator.type = "separator" last_separator.show_all() self.applicationsBox.add(last_separator) self.suggestions.append(last_separator) @@ -774,8 +772,7 @@ class pluginclass( object ): last_separator.add(Gtk.HSeparator()) last_separator.set_visible_window(False) last_separator.set_size_request(-1, 20) - last_separator.type = "separator" - self.mintMenuWin.SetPaneColors( [ last_separator ] ) + last_separator.type = "separator" last_separator.show_all() self.applicationsBox.add(last_separator) self.suggestions.append(last_separator) @@ -1353,7 +1350,6 @@ class pluginclass( object ): space.connect( "button-press-event", self.favPopup ) space.type = "space" - self.mintMenuWin.SetPaneColors( [ space ] ) space.show() return space @@ -1363,7 +1359,6 @@ class pluginclass( object ): separator.set_size_request( -1, 20 ) separator.type = "separator" - self.mintMenuWin.SetPaneColors( [ separator ] ) separator.show_all() box = Gtk.EventBox() box.type = "separator" From 6766ff6eddeb4da438a0e3ca82cf08483c4ad164 Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 03:56:22 +0000 Subject: [PATCH 08/13] Clear search entry when switching from Apps to Favorites Took two clicks because of a loop in the logic (setting to empty also invokes setting the tab). Fixes #44 --- usr/lib/linuxmint/mintMenu/plugins/applications.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/usr/lib/linuxmint/mintMenu/plugins/applications.py b/usr/lib/linuxmint/mintMenu/plugins/applications.py index d613df9..9bec58b 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/applications.py +++ b/usr/lib/linuxmint/mintMenu/plugins/applications.py @@ -571,8 +571,9 @@ class pluginclass( object ): elif tabNum == 1: notebook.set_current_page( 1 ) - self.lastActiveTab = tabNum self.focusSearchEntry() + self.lastActiveTab = tabNum + def Todos( self ): @@ -639,7 +640,7 @@ class pluginclass( object ): separator.add(Gtk.HSeparator()) separator.set_visible_window(False) separator.set_size_request(-1, 20) - separator.type = "separator" + separator.type = "separator" separator.show_all() self.applicationsBox.add(separator) self.suggestions.append(separator) @@ -717,7 +718,7 @@ class pluginclass( object ): last_separator.add(Gtk.HSeparator()) last_separator.set_visible_window(False) last_separator.set_size_request(-1, 20) - last_separator.type = "separator" + last_separator.type = "separator" last_separator.show_all() self.applicationsBox.add(last_separator) self.suggestions.append(last_separator) @@ -772,7 +773,7 @@ class pluginclass( object ): last_separator.add(Gtk.HSeparator()) last_separator.set_visible_window(False) last_separator.set_size_request(-1, 20) - last_separator.type = "separator" + last_separator.type = "separator" last_separator.show_all() self.applicationsBox.add(last_separator) self.suggestions.append(last_separator) From 39690a47d1d716b5320f747e532ece7241dffb67 Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 16:15:58 +0000 Subject: [PATCH 09/13] Fixes transparency issues on first composited draw When using compositing and transparency, the first draw was only showing the shadow borders on the window. To fix this the opacity has to be set after the first show. Don't know why this is needed, but it's probably a bug in the underlying toolkits (maybe GTK or GDK). It still looks a little jerky the first time the window is drawn. --- usr/lib/linuxmint/mintMenu/mintMenu.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/usr/lib/linuxmint/mintMenu/mintMenu.py b/usr/lib/linuxmint/mintMenu/mintMenu.py index 0fd119e..9b6bf44 100755 --- a/usr/lib/linuxmint/mintMenu/mintMenu.py +++ b/usr/lib/linuxmint/mintMenu/mintMenu.py @@ -101,7 +101,6 @@ class MainWindow( object ): self.getSetGSettingEntries() self.SetupMintMenuBorder() - self.SetupMintMenuOpacity() self.tooltips = Gtk.Tooltips() if self.globalEnableTooltips and self.enableTooltips: @@ -124,6 +123,8 @@ class MainWindow( object ): self.settings.connect( "changed::custom-color", self.toggleCustomBackgroundColor ) self.settings.connect( "changed::border-width", self.toggleBorderWidth ) self.settings.connect( "changed::opacity", self.toggleOpacity ) + + self.firstTime = True; def on_window1_destroy (self, widget, data=None): Gtk.main_quit() @@ -436,6 +437,11 @@ class MainWindow( object ): def show( self ): self.window.present() + + # Hack for opacity not showing on first composited draw + if self.firstTime: + self.firstTime = False + self.SetupMintMenuOpacity() if ( "applications" in self.plugins ) and ( hasattr( self.plugins["applications"], "focusSearchEntry" ) ): if (self.startWithFavorites): From d7500875ec2d202c121c0ed5fea627dc0221155b Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 17:51:49 +0000 Subject: [PATCH 10/13] Several theming and color fixes Restructured a bit how theming and coloring works. Now theme and color changes work on the fly. No need to restart mintMenu anymore. Note that changing theme no longer regenerates plugins, so it's faster. Fixes issue #47 --- usr/lib/linuxmint/mintMenu/mintMenu.py | 103 +++++++++--------- .../mintMenu/plugins/applications.py | 2 +- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/usr/lib/linuxmint/mintMenu/mintMenu.py b/usr/lib/linuxmint/mintMenu/mintMenu.py index 9b6bf44..1155243 100755 --- a/usr/lib/linuxmint/mintMenu/mintMenu.py +++ b/usr/lib/linuxmint/mintMenu/mintMenu.py @@ -98,9 +98,21 @@ class MainWindow( object ): plugindir = os.path.join( os.path.expanduser( "~" ), ".linuxmint/mintMenu/plugins" ) sys.path.append( plugindir ) - + + self.panelSettings = Gio.Settings.new("org.mate.panel") + self.panelSettings.connect( "changed::tooltips-enabled", self.toggleTooltipsEnabled ) + + self.settings.connect( "changed::plugins-list", self.RegenPlugins ) + self.settings.connect( "changed::start-with-favorites", self.toggleStartWithFavorites ) + self.settings.connect( "changed::tooltips-enabled", self.toggleTooltipsEnabled ) + self.settings.connect( "changed::use-custom-color", self.toggleUseCustomColor ) + self.settings.connect( "changed::custom-border-color", self.toggleCustomBorderColor ) + self.settings.connect( "changed::custom-heading-color", self.toggleCustomHeadingColor ) + self.settings.connect( "changed::custom-color", self.toggleCustomBackgroundColor ) + self.settings.connect( "changed::border-width", self.toggleBorderWidth ) + self.settings.connect( "changed::opacity", self.toggleOpacity ) + self.getSetGSettingEntries() - self.SetupMintMenuBorder() self.tooltips = Gtk.Tooltips() if self.globalEnableTooltips and self.enableTooltips: @@ -109,21 +121,7 @@ class MainWindow( object ): self.tooltips.disable() self.PopulatePlugins(); - - self.settings.connect( "changed::plugins-list", self.RegenPlugins ) - - self.settings.connect( "changed::start-with-favorites", self.toggleStartWithFavorites ) - globalsettings = Gio.Settings.new("org.mate.panel") - globalsettings.connect( "changed::tooltips-enabled", self.toggleTooltipsEnabled ) - self.settings.connect( "changed::tooltips-enabled", self.toggleTooltipsEnabled ) - - self.settings.connect( "changed::use-custom-color", self.toggleUseCustomColor ) - self.settings.connect( "changed::custom-border-color", self.toggleCustomBorderColor ) - self.settings.connect( "changed::custom-heading-color", self.toggleCustomHeadingColor ) - self.settings.connect( "changed::custom-color", self.toggleCustomBackgroundColor ) - self.settings.connect( "changed::border-width", self.toggleBorderWidth ) - self.settings.connect( "changed::opacity", self.toggleOpacity ) - + self.loadTheme() self.firstTime = True; def on_window1_destroy (self, widget, data=None): @@ -160,9 +158,7 @@ class MainWindow( object ): def toggleUseCustomColor( self, settings, key, args = None ): self.usecustomcolor = settings.get_boolean(key) - self.SetupMintMenuBorder() - self.SetPaneColors( self.panesToColor ) - self.SetHeadingStyle( self.headingsToColor ) + self.loadTheme() def toggleCustomBorderColor( self, settings, key, args = None ): self.custombordercolor = settings.get_string(key) @@ -190,14 +186,13 @@ class MainWindow( object ): self.enableTooltips = self.settings.get_boolean( "tooltips-enabled" ) self.startWithFavorites = self.settings.get_boolean( "start-with-favorites" ) - mate_settings = Gio.Settings.new("org.mate.panel") - self.globalEnableTooltips = mate_settings.get_boolean( "tooltips-enabled" ) + self.globalEnableTooltips = self.panelSettings.get_boolean( "tooltips-enabled" ) - def SetupMintMenuBorder( self ): + def SetupMintMenuBorder( self, defaultStyle = None ): if self.usecustomcolor: self.window.modify_bg( Gtk.StateType.NORMAL, Gdk.color_parse( self.custombordercolor ) ) - # else: - # self.window.modify_bg( Gtk.StateType.NORMAL, self.window.rc_get_style().bg[ Gtk.StateType.SELECTED ] ) + elif defaultStyle is not None: + self.window.modify_bg( Gtk.StateType.NORMAL, defaultStyle.lookup_color('bg_color')[1] ) self.border.set_padding( self.borderwidth, self.borderwidth, self.borderwidth, self.borderwidth ) def SetupMintMenuOpacity( self ): @@ -228,10 +223,9 @@ class MainWindow( object ): PluginPane.show() PaneLadder = Gtk.VBox( False, 0 ) PluginPane.add( PaneLadder ) - self.SetPaneColors( [ PluginPane ] ) ImageBox = Gtk.EventBox() - self.SetPaneColors( [ ImageBox ] ) ImageBox.show() + self.panesToColor.extend( [ PluginPane, ImageBox ] ) seperatorImage = GdkPixbuf.Pixbuf.new_from_file( self.dottedfile ) @@ -285,9 +279,7 @@ class MainWindow( object ): print u"Unable to load " + plugin + " plugin :-(" - self.SetPaneColors( [MyPlugin.content_holder] ) - - + self.panesToColor.append( MyPlugin.content_holder ) MyPlugin.content_holder.show() VBox1 = Gtk.VBox( False, 0 ) @@ -296,14 +288,14 @@ class MainWindow( object ): Align1 = Gtk.Alignment.new( 0, 0, 0, 0 ) Align1.set_padding( 10, 5, 10, 0 ) Align1.add( Label1 ) - self.SetHeadingStyle( [Label1] ) + self.headingsToColor.append( Label1 ) Align1.show() Label1.show() if not hasattr( MyPlugin, 'sticky' ) or MyPlugin.sticky == True: heading = Gtk.EventBox() Align1.set_padding( 0, 0, 10, 0 ) - self.SetPaneColors( [heading] ) + heading.set_visible_window( False ) heading.set_size_request( MyPlugin.width, 30 ) else: heading = Gtk.HBox() @@ -332,7 +324,9 @@ class MainWindow( object ): if hasattr( MyPlugin, 'height' ): MyPlugin.content_holder.set_size_request( -1, MyPlugin.height ) if hasattr( MyPlugin, 'itemstocolor' ): - self.SetPaneColors( MyPlugin.itemstocolor ) + self.panesToColor.extend( MyPlugin.itemstocolor ) + if hasattr( MyPlugin, 'headingstocolor' ): + self.headingsToColor.extend( MyPlugin.headingstocolor ) except: # create traceback info = sys.exc_info() @@ -350,9 +344,8 @@ class MainWindow( object ): PluginPane = Gtk.EventBox() PaneLadder = Gtk.VBox( False, 0 ) PluginPane.add( PaneLadder ) - self.SetPaneColors( [PluginPane] ) ImageBox = Gtk.EventBox() - self.SetPaneColors( [ImageBox] ) + self.panesToColor.extend( [ PluginPane, ImageBox ] ) ImageBox.show() PluginPane.show_all() @@ -374,22 +367,30 @@ class MainWindow( object ): self.tooltips.disable() #print u"Loading", (time.time() - start), "s" - def SetPaneColors( self, items ): - for item in items: - if item not in self.panesToColor: - self.panesToColor.append( item ) + # A little hacky but works + def getDefaultStyle( self ): + widget = Gtk.EventBox() + widget.show() + return Gtk.rc_get_style(widget) + + def loadTheme( self ): + defaultStyle = self.getDefaultStyle() + self.SetPaneColors( self.panesToColor, defaultStyle ) + self.SetupMintMenuBorder( defaultStyle ) + self.SetHeadingStyle( self.headingsToColor ) + + def SetPaneColors( self, items, defaultStyle = None ): if self.usecustomcolor: for item in items: item.modify_bg( Gtk.StateType.NORMAL, Gdk.color_parse( self.customcolor ) ) - # else: - # for item in items: - # item.modify_bg( Gtk.StateType.NORMAL, self.paneholder.rc_get_style().bg[ Gtk.StateType.NORMAL ] ) + # TODO: Changing background color isn't working for pixmaps! The following does not work: + item.get_style().bg_pixmap[Gtk.StateType.NORMAL] = None + elif defaultStyle is not None: + for item in items: + item.modify_bg( Gtk.StateType.NORMAL, defaultStyle.lookup_color('bg_color')[1] ) + item.get_style().bg_pixmap[Gtk.StateType.NORMAL] = defaultStyle.bg_pixmap[Gtk.StateType.NORMAL] def SetHeadingStyle( self, items ): - for item in items: - if item not in self.headingsToColor: - self.headingsToColor.append( item ) - if self.usecustomcolor: color = self.customheadingcolor else: @@ -431,6 +432,7 @@ class MainWindow( object ): self.getSetGSettingEntries() self.PopulatePlugins() + self.loadTheme() #print NAME+u" reloaded" @@ -529,8 +531,8 @@ class MenuWin( object ): self.settings.connect( "changed::hot-key", self.hotkeyChanged ) self.loadSettings() - mate_settings = Gio.Settings.new("org.mate.interface") - mate_settings.connect( "changed::gtk-theme", self.changeTheme ) + self.mate_settings = Gio.Settings.new("org.mate.interface") + self.mate_settings.connect( "changed::gtk-theme", self.changeTheme ) self.createPanelButton() @@ -625,12 +627,11 @@ class MenuWin( object ): def changeTheme(self, *args): self.reloadSettings() self.applyTheme() - self.mainwin.RegenPlugins() + self.mainwin.loadTheme() def applyTheme(self): style_settings = Gtk.Settings.get_default() - mate_settings = Gio.Settings.new("org.mate.interface") - desktop_theme = mate_settings.get_string('gtk-theme') + desktop_theme = self.mate_settings.get_string('gtk-theme') if self.theme_name == "default": style_settings.set_property("gtk-theme-name", desktop_theme) else: diff --git a/usr/lib/linuxmint/mintMenu/plugins/applications.py b/usr/lib/linuxmint/mintMenu/plugins/applications.py index 9bec58b..112a59a 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/applications.py +++ b/usr/lib/linuxmint/mintMenu/plugins/applications.py @@ -253,7 +253,7 @@ class pluginclass( object ): self.builder.get_object("label7").set_text(_("All applications")) self.builder.get_object("label2").set_text(_("Applications")) - self.mintMenuWin.SetHeadingStyle( [self.builder.get_object("label6"),self.builder.get_object("label2")] ) + self.headingstocolor = [self.builder.get_object("label6"),self.builder.get_object("label2")] self.numApps = 0 # These properties are NECESSARY to maintain consistency From 19d92c0bd135c60b4e9118dd36d81d2e2016833c Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 18:23:23 +0000 Subject: [PATCH 11/13] Fixed some panel orientation issues --- usr/lib/linuxmint/mintMenu/mintMenu.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/usr/lib/linuxmint/mintMenu/mintMenu.py b/usr/lib/linuxmint/mintMenu/mintMenu.py index 1155243..d73bd20 100755 --- a/usr/lib/linuxmint/mintMenu/mintMenu.py +++ b/usr/lib/linuxmint/mintMenu/mintMenu.py @@ -592,20 +592,19 @@ class MenuWin( object ): self.button_box = Gtk.HBox() self.button_box.pack_start( self.button_icon, False, False, 0 ) self.button_box.pack_start( self.systemlabel, False, False, 0 ) - self.button_icon.set_padding( 5, 0 ) # if we have a vertical panel elif self.applet.get_orient() == MatePanelApplet.AppletOrient.LEFT: self.button_box = Gtk.VBox() self.systemlabel.set_angle( 270 ) - self.button_box.pack_start( self.systemlabel , True, True, 0) - self.button_box.pack_start( self.button_icon , True, True, 0) - self.button_icon.set_padding( 5, 0 ) + self.button_box.pack_start( self.button_icon , False, False, 0) + self.button_box.pack_start( self.systemlabel , False, False, 0) + self.button_icon.set_padding( 0, 5 ) elif self.applet.get_orient() == MatePanelApplet.AppletOrient.RIGHT: self.button_box = Gtk.VBox() self.systemlabel.set_angle( 90 ) - self.button_box.pack_start( self.button_icon , True, True, 0) - self.button_box.pack_start( self.systemlabel , True, True, 0) + self.button_box.pack_start( self.systemlabel , False, False, 0) + self.button_box.pack_start( self.button_icon , False, False, 0) self.button_icon.set_padding( 0, 5 ) self.button_box.set_homogeneous( False ) @@ -650,12 +649,12 @@ class MenuWin( object ): elif self.applet.get_orient() == MatePanelApplet.AppletOrient.LEFT: tmpbox = Gtk.VBox() self.systemlabel.set_angle( 270 ) - self.button_box.reorder_child( self.button_icon, 1 ) + self.button_box.reorder_child( self.button_icon, 0 ) self.button_icon.set_padding( 0, 5 ) elif self.applet.get_orient() == MatePanelApplet.AppletOrient.RIGHT: tmpbox = Gtk.VBox() self.systemlabel.set_angle( 90 ) - self.button_box.reorder_child( self.button_icon, 0 ) + self.button_box.reorder_child( self.button_icon, 1 ) self.button_icon.set_padding( 0, 5 ) tmpbox.set_homogeneous( False ) @@ -713,8 +712,7 @@ class MenuWin( object ): self.systemlabel.size_request(sl_req) if self.applet.get_orient() == MatePanelApplet.AppletOrient.UP or self.applet.get_orient() == MatePanelApplet.AppletOrient.DOWN: if self.hideIcon: - self.systemlabel.size_request(sl_req) - self.applet.set_size_request( sl_req.width + 2, -1 ) + self.applet.set_size_request( sl_req.width + 2, bi_req.height ) else: self.applet.set_size_request( sl_req.width + bi_req.width + 5, bi_req.height ) else: From 893fd879a21038f49ff82be6bc6b88a9b9196f85 Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 18:26:01 +0000 Subject: [PATCH 12/13] Increased panel icon hover shine --- usr/lib/linuxmint/mintMenu/mintMenu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/lib/linuxmint/mintMenu/mintMenu.py b/usr/lib/linuxmint/mintMenu/mintMenu.py index d73bd20..d0eafcb 100755 --- a/usr/lib/linuxmint/mintMenu/mintMenu.py +++ b/usr/lib/linuxmint/mintMenu/mintMenu.py @@ -574,7 +574,7 @@ class MenuWin( object ): def do_image(self, image_file, saturate): pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_file) if saturate: - GdkPixbuf.Pixbuf.saturate_and_pixelate(pixbuf, pixbuf, 1.5, False) + pixbuf.saturate_and_pixelate(pixbuf, 50.0, False) self.button_icon.set_from_pixbuf(pixbuf) def createPanelButton( self ): From d567469f4d6d40301e46dafe9a2cffdb292b07af Mon Sep 17 00:00:00 2001 From: hordepfo Date: Wed, 22 Jan 2014 20:16:27 +0000 Subject: [PATCH 13/13] Changed all showing/hiding logic It seems most GTK apps like mintMenu and the default Gnome2/Mate menu use the GDK grab focus functions to grab input (pointer/keyboard). These functions appear to suck all input to the app like a blackhole. This patch gives more interactivity to the mintMenu window, making it a first class object in the desktop. Things that work now: * When the window is open, hovering on other desktop icons highlights them * Clicking outside the window propagates the event (e.g. select another window) * Alt-tab and other events unfocus and hide the window * Scrollbars in the mintMenu finally work right * Probably several other small things --- usr/lib/linuxmint/mintMenu/keybinding.py | 27 ++- usr/lib/linuxmint/mintMenu/mintMenu.glade | 2 +- usr/lib/linuxmint/mintMenu/mintMenu.py | 159 +++++++++--------- .../mintMenu/plugins/applications.py | 29 ++-- usr/lib/linuxmint/mintMenu/pointerMonitor.py | 84 +++++++++ 5 files changed, 200 insertions(+), 101 deletions(-) create mode 100644 usr/lib/linuxmint/mintMenu/pointerMonitor.py diff --git a/usr/lib/linuxmint/mintMenu/keybinding.py b/usr/lib/linuxmint/mintMenu/keybinding.py index 80f19c0..2a8eec3 100644 --- a/usr/lib/linuxmint/mintMenu/keybinding.py +++ b/usr/lib/linuxmint/mintMenu/keybinding.py @@ -58,10 +58,11 @@ class GlobalKeyBinding(GObject.GObject, threading.Thread): self.keymap = capi.get_widget (gdk.gdk_keymap_get_default()) self.display = Display() self.screen = self.display.screen() - self.root = self.screen.root + self.window = self.screen.root self.ignored_masks = self.get_mask_combinations(X.LockMask | X.Mod2Mask | X.Mod5Mask) self.map_modifiers() self.raw_keyval = None + self.keytext = "" def is_hotkey(self, key, modifier): keymatch = False @@ -101,21 +102,39 @@ class GlobalKeyBinding(GObject.GObject, threading.Thread): self.modifiers = None return False + self.keytext = key self.keycode = self.get_keycode(keyval) self.modifiers = int(modifiers) catch = error.CatchError(error.BadAccess) for ignored_mask in self.ignored_masks: mod = modifiers | ignored_mask - result = self.root.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) - self.display.sync() + result = self.window.grab_key(self.keycode, mod, True, X.GrabModeAsync, X.GrabModeSync, onerror=catch) + self.display.flush() + # sync has been blocking. Don't know why. + #self.display.sync() if catch.get_error(): return False return True def ungrab(self): if self.keycode: - self.root.ungrab_key(self.keycode, X.AnyModifier, self.root) + self.window.ungrab_key(self.keycode, X.AnyModifier, self.window) + + def rebind(self, key): + self.ungrab() + if key != "": + self.grab(key) + else: + self.keytext = "" + + def set_focus_window(self, window = None): + self.ungrab() + if window is None: + self.window = self.screen.root + else: + self.window = self.display.create_resource_object("window", gdk.gdk_x11_drawable_get_xid(hash(window))) + self.grab(self.keytext) def get_mask_combinations(self, mask): return [x for x in xrange(mask+1) if not (x & ~mask)] diff --git a/usr/lib/linuxmint/mintMenu/mintMenu.glade b/usr/lib/linuxmint/mintMenu/mintMenu.glade index 641a9b8..c8ef20d 100644 --- a/usr/lib/linuxmint/mintMenu/mintMenu.glade +++ b/usr/lib/linuxmint/mintMenu/mintMenu.glade @@ -4,7 +4,7 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK - popup + toplevel False menu True diff --git a/usr/lib/linuxmint/mintMenu/mintMenu.py b/usr/lib/linuxmint/mintMenu/mintMenu.py index d0eafcb..2c1eb59 100755 --- a/usr/lib/linuxmint/mintMenu/mintMenu.py +++ b/usr/lib/linuxmint/mintMenu/mintMenu.py @@ -21,6 +21,7 @@ try: import capi import xdg.Config import keybinding + import pointerMonitor except Exception, e: print e sys.exit( 1 ) @@ -87,12 +88,9 @@ class MainWindow( object ): self.panesToColor = [ ] self.headingsToColor = [ ] - self.window.connect( "map-event", self.onMap ) - self.window.connect( "show", self.onShow ) - self.window.connect( "unmap-event", self.onUnmap ) - self.window.connect( "button-press-event", self.onButtonPress ) self.window.connect( "key-press-event", self.onKeyPress ) - self.window.connect( "grab-broken-event", self.onGrabBroken ) + self.window.connect( "focus-in-event", self.onFocusIn ) + self.loseFocusId = self.window.connect( "focus-out-event", self.onFocusOut ) self.window.stick() @@ -436,86 +434,59 @@ class MainWindow( object ): #print NAME+u" reloaded" + def onKeyPress( self, widget, event ): + if event.keyval == Gdk.KEY_Escape: + self.hide() + return True + return False def show( self ): self.window.present() - + self.window.window.focus( Gdk.CURRENT_TIME ) + # Hack for opacity not showing on first composited draw if self.firstTime: self.firstTime = False self.SetupMintMenuOpacity() + + for plugin in self.plugins.values(): + if hasattr( plugin, "onShowMenu" ): + plugin.onShowMenu() if ( "applications" in self.plugins ) and ( hasattr( self.plugins["applications"], "focusSearchEntry" ) ): if (self.startWithFavorites): self.plugins["applications"].changeTab(0) self.plugins["applications"].focusSearchEntry() - def grab( self ): - gdk.gdk_pointer_grab (hash(self.window.window), True, Gdk.EventMask.BUTTON_PRESS_MASK, None, None, Gdk.CURRENT_TIME) - Gdk.keyboard_grab( self.window.window, False, Gdk.CURRENT_TIME ) - Gtk.grab_add(self.window) - - def ungrab( self ): - Gtk.grab_remove(self.window) - self.window.hide() - Gdk.pointer_ungrab(Gdk.CURRENT_TIME) - Gdk.keyboard_ungrab(Gdk.CURRENT_TIME) - - def onMap( self, widget, event ): - self.grab() - - def onShow( self, widget ): - for plugin in self.plugins.values(): - if hasattr( plugin, "onShowMenu" ): - plugin.onShowMenu() - - def onUnmap( self, widget, event ): - self.ungrab() - + def hide( self, forceHide = False ): for plugin in self.plugins.values(): if hasattr( plugin, "onHideMenu" ): plugin.onHideMenu() - - def onKeyPress( self, widget, event ): - if event.keyval == Gdk.KEY_Escape or self.keybinder.is_hotkey(event.keyval, event.get_state()): - self.hide() - return True - return False - - def onButtonPress( self, widget, event ): - # Check if the pointer is within the menu, else hide the menu - winatptr = Gdk.window_at_pointer() - if winatptr: - win = winatptr[0] - while win: - if win == self.window.window: - break - win = capi.get_widget(gdk.gdk_window_get_parent (hash(win))) - if not win: - self.hide( True ) - else: - self.hide( True ) - - return True - - def onGrabBroken( self, widget, event ): - if event.grab_broken.grab_window: - try: - win = event.grab_broken.grab_window - data = c_void_p() - gdk.gdk_window_get_user_data(hash(win), byref(data)) - theft = capi.get_widget(ctypes.cast(data, POINTER(capi._PyGObject_Functions))) - theft.connect( "event", self.onGrabTheftEvent ) - except Exception, detail: - print detail - self.window.hide() - - def onGrabTheftEvent( self, widget, event ): - if event.type == Gdk.EventType.UNMAP or event.type == Gdk.EventType.SELECTION_CLEAR: - self.grab() - - def hide(self, forceHide = False): + self.window.hide() + + def onFocusIn( self, *args ): + def dummy( *args ): pass + + signalId = GObject.signal_lookup( "focus-out-event", self.window ) + while True: + result = GObject.signal_handler_find( self.window, + GObject.SignalMatchType.ID | GObject.SignalMatchType.UNBLOCKED, + signalId, 0, None, dummy, dummy ) + if result == 0: + self.window.handler_unblock( self.loseFocusId ) + else: + break + + return False + + def onFocusOut( self, *args): + if self.window.get_visible(): + self.hide() + return False + + def stopHiding( self ): + self.window.handler_block( self.loseFocusId ) class MenuWin( object ): def __init__( self, applet, iid ): @@ -542,8 +513,9 @@ class MenuWin( object ): self.applet.connect("enter-notify-event", self.enter_notify) self.applet.connect("leave-notify-event", self.leave_notify) self.mainwin = MainWindow( self.button_box, self.settings, self.keybinder ) - self.mainwin.window.connect( "map-event", lambda *args: self.applet.set_state( Gtk.StateType.SELECTED ) ) - self.mainwin.window.connect( "unmap-event", lambda *args: self.applet.set_state( Gtk.StateType.NORMAL ) ) + self.mainwin.window.connect( "map-event", self.onWindowMap ) + self.mainwin.window.connect( "unmap-event", self.onWindowUnmap ) + self.mainwin.window.connect( "realize", self.onRealize ) self.mainwin.window.connect( "size-allocate", lambda *args: self.positionMenu() ) self.mainwin.window.set_name("mintmenu") # Name used in Gtk RC files @@ -552,23 +524,46 @@ class MenuWin( object ): Gtk.Window.set_default_icon_name( self.mainwin.icon ) self.bind_hot_key() + self.applet.set_can_focus(False) + + self.pointerMonitor = pointerMonitor.PointerMonitor() + self.pointerMonitor.connect("activate", self.onPointerOutside) + + def onWindowMap( self, *args ): + self.applet.set_state( Gtk.StateType.SELECTED ) + self.keybinder.set_focus_window( self.mainwin.window.window ) + self.pointerMonitor.grabPointer() + return False + + def onWindowUnmap( self, *args ): + self.applet.set_state( Gtk.StateType.NORMAL ) + self.keybinder.set_focus_window() + self.pointerMonitor.ungrabPointer() + return False + + def onRealize( self, *args): + self.pointerMonitor.addWindowToMonitor( self.mainwin.window.window ) + self.pointerMonitor.addWindowToMonitor( self.applet.window ) + self.pointerMonitor.start() + return False + + def onPointerOutside(self, *args): + self.mainwin.hide() + return True def onBindingPress(self, binder): - try: - if self.mainwin.window.get_visible(): - self.mainwin.window.hide() - self.mainwin.toggle.set_active(False) - else: - MenuWin.showMenu(self,self.mainwin.toggle) - self.mainwin.window.show() - #self.mainwin.wTree.get_widget( 'PluginTabs' ).set_curremenu_editor = SetGconf( self.client, "string", "/apps/usp/menu_editor", "mozo" ) - except Exception, cause: - print cause + self.toggleMenu() + return True def enter_notify(self, applet, event): self.do_image(self.buttonIcon, True) def leave_notify(self, applet, event): + # Hack for mate-panel-test-applets focus issue (this can be commented) + if event.state & Gdk.ModifierType.BUTTON1_MASK and applet.state & Gtk.StateType.SELECTED: + if event.x >= 0 and event.y >= 0 and event.x < applet.window.get_width() and event.y < applet.window.get_height(): + self.mainwin.stopHiding() + self.do_image(self.buttonIcon, False) def do_image(self, image_file, saturate): @@ -694,10 +689,8 @@ class MenuWin( object ): pass def hotkeyChanged (self, schema, key): - self.keybinder.ungrab() self.hotkeyText = self.settings.get_string( "hot-key" ) - if self.hotkeyText != "": - self.keybinder.grab(self.hotkeyText) + self.keybinder.rebind(self.hotkeyText) def sizeButton( self ): if self.hideIcon: diff --git a/usr/lib/linuxmint/mintMenu/plugins/applications.py b/usr/lib/linuxmint/mintMenu/plugins/applications.py index 112a59a..b8d7b80 100755 --- a/usr/lib/linuxmint/mintMenu/plugins/applications.py +++ b/usr/lib/linuxmint/mintMenu/plugins/applications.py @@ -576,12 +576,22 @@ class pluginclass( object ): def Todos( self ): - + self.searchEntry.connect( "popup-menu", self.blockOnPopup ) + self.searchEntry.connect( "button-press-event", self.blockOnRightPress ) self.searchEntry.connect( "changed", self.Filter ) self.searchEntry.connect( "activate", self.Search ) self.showAllAppsButton.connect( "clicked", lambda widget: self.changeTab( 1 ) ) self.showFavoritesButton.connect( "clicked", lambda widget: self.changeTab( 0 ) ) self.buildButtonList() + + def blockOnPopup( self, *args ): + self.mintMenuWin.stopHiding() + return False + + def blockOnRightPress( self, widget, event ): + if event.button == 3: + self.mintMenuWin.stopHiding() + return False def focusSearchEntry( self ): # grab_focus() does select all text, @@ -951,10 +961,8 @@ class pluginclass( object ): mTree.append(propsMenuItem) mTree.show_all() + self.mintMenuWin.stopHiding() gtk.gtk_menu_popup(hash(mTree), None, None, None, None, ev.button, ev.time) - #self.mintMenuWin.grab() - mTree.connect( 'deactivate', self.onMenuPopupDeactivate) - else: mTree = Gtk.Menu() mTree.set_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK | @@ -972,9 +980,8 @@ class pluginclass( object ): removeMenuItem.connect( "activate", self.onFavoritesRemove, widget ) insertSpaceMenuItem.connect( "activate", self.onFavoritesInsertSpace, widget, insertBefore ) insertSeparatorMenuItem.connect( "activate", self.onFavoritesInsertSeparator, widget, insertBefore ) + self.mintMenuWin.stopHiding() gtk.gtk_menu_popup(hash(mTree), None, None, None, None, ev.button, ev.time) - #self.mintMenuWin.grab() - mTree.connect( 'deactivate', self.onMenuPopupDeactivate) def menuPopup( self, widget, event ): if event.button == 3: @@ -1035,11 +1042,8 @@ class pluginclass( object ): startupMenuItem.set_active( False ) startupMenuItem.connect( "toggled", self.onAddToStartup, widget ) - mTree.connect( 'deactivate', self.onMenuPopupDeactivate) + self.mintMenuWin.stopHiding() gtk.gtk_menu_popup(hash(mTree), None, None, None, None, event.button, event.time) - - def onMenuPopupDeactivate( self, widget): - self.mintMenuWin.grab() def searchPopup( self, widget=None, event=None ): menu = Gtk.Menu() @@ -1114,14 +1118,13 @@ class pluginclass( object ): menu.append(menuItem) menu.show_all() - + + self.mintMenuWin.stopHiding() gtk.gtk_menu_popup(hash(menu), None, None, None, None, event.button, event.time) #menu.attach_to_widget(self.searchButton, None) #menu.reposition() #menu.reposition() - #self.mintMenuWin.grab() - menu.connect( 'deactivate', self.onMenuPopupDeactivate) return True def pos_func(self, menu=None): diff --git a/usr/lib/linuxmint/mintMenu/pointerMonitor.py b/usr/lib/linuxmint/mintMenu/pointerMonitor.py new file mode 100644 index 0000000..1a0a071 --- /dev/null +++ b/usr/lib/linuxmint/mintMenu/pointerMonitor.py @@ -0,0 +1,84 @@ +import gi +gi.require_version("Gtk", "2.0") + +from Xlib.display import Display +from Xlib import X, error +from gi.repository import Gtk, Gdk, GObject, GLib +import threading +import ctypes +from ctypes import * + +gdk = CDLL("libgdk-x11-2.0.so.0") +gtk = CDLL("libgtk-x11-2.0.so.0") + +class PointerMonitor(GObject.GObject, threading.Thread): + __gsignals__ = { + 'activate': (GObject.SignalFlags.RUN_LAST, None, ()), + } + + def __init__(self): + GObject.GObject.__init__ (self) + threading.Thread.__init__ (self) + self.setDaemon (True) + self.display = Display() + self.root = self.display.screen().root + self.windows = [] + self.topWindows = [] + + # Receives GDK windows + def addWindowToMonitor(self, window): + xWindow = self.display.create_resource_object("window", gdk.gdk_x11_drawable_get_xid(hash(window))) + self.windows.append(xWindow) + self.topWindows.append(self.getToplevelParent(xWindow)) + + def getToplevelParent(self, window): + parent = window.query_tree().parent + if parent == self.root: + return window + else: + return self.getToplevelParent(parent) + + def grabPointer(self): + self.root.grab_button(X.AnyButton, X.AnyModifier, True, X.ButtonPressMask, X.GrabModeSync, X.GrabModeAsync, 0, 0) + self.display.flush() + + def ungrabPointer(self): + self.root.ungrab_button(X.AnyButton, X.AnyModifier) + self.display.flush() + + def idle(self): + self.emit("activate") + return False + + def activate(self): + GLib.idle_add(self.run) + + def run(self): + self.running = True + while self.running: + event = self.display.next_event() + try: + if event.type == X.ButtonPress: + # Check if pointer is inside monitored windows + for w, topW in zip(self.windows, self.topWindows): + if event.child == topW: + if topW == w: + break + else: + p = w.query_pointer() + g = w.get_geometry() + if p.win_x >= 0 and p.win_y >= 0 and p.win_x <= g.width and p.win_y <= g.height: + break + else: + # Is outside, so activate + GLib.idle_add(self.idle) + + self.display.allow_events(X.ReplayPointer, event.time) + except Exception as e: + print "Unexpected error: " + str(e) + + def stop(self): + self.running = False + self.root.ungrab_button(X.AnyButton, X.AnyModifier) + self.display.close() +