#!/usr/bin/env python import gi gi.require_version("Gtk", "2.0") from gi.repository import Gtk, Pango, Gdk, Gio, GLib import os #import mateconf import fnmatch import time import string import gettext import threading import commands import subprocess import filecmp import ctypes import capi from ctypes import * from easybuttons import * from execute import Execute from easygsettings import EasyGSettings from easyfiles import * gtk = CDLL("libgtk-x11-2.0.so.0") from filemonitor import monitor as filemonitor #import xdg.Menu import matemenu from user import home # i18n gettext.install("mintmenu", "/usr/share/linuxmint/locale") class PackageDescriptor(): def __init__(self, name, summary, description): self.name = name self.summary = summary self.description = description def print_timing(func): def wrapper(*arg): t1 = time.time() res = func(*arg) t2 = time.time() print '%s took %0.3f ms' % (func.func_name, (t2-t1)*1000.0) return res return wrapper # Evil patching #def xdgParsePatched(filename=None): # # conver to absolute path # if filename and not os.path.isabs(filename): # filename = xdg.Menu.__getFileName(filename) # # # use default if no filename given # if not filename: # filename = xdg.Menu.__getFileName("applications.menu") # # if not filename: # raise xdg.Menu.ParsingError(_("File not found"), "/etc/xdg/menus/applications.menu") # # # check if it is a .menu file # if not os.path.splitext(filename)[1] == ".menu": # raise xdg.Menu.ParsingError(_("Not a .menu file"), filename) # # # create xml parser # try: # doc = xdg.Menu.xml.dom.minidom.parse(filename) # except xdg.Menu.xml.parsers.expat.ExpatError: # raise xdg.Menu.ParsingError(_("Not a valid .menu file"), filename) # # # parse menufile # xdg.Menu.tmp["Root"] = "" # xdg.Menu.tmp["mergeFiles"] = [] # xdg.Menu.tmp["DirectoryDirs"] = [] # xdg.Menu.tmp["cache"] = xdg.Menu.MenuEntryCache() # # xdg.Menu.__parse(doc, filename, xdg.Menu.tmp["Root"]) # xdg.Menu.__parsemove(xdg.Menu.tmp["Root"]) # xdg.Menu.__postparse(xdg.Menu.tmp["Root"]) # # xdg.Menu.tmp["Root"].Doc = doc # xdg.Menu.tmp["Root"].Filename = filename # # # generate the menu # xdg.Menu.__genmenuNotOnlyAllocated(xdg.Menu.tmp["Root"]) # xdg.Menu.__genmenuOnlyAllocated(xdg.Menu.tmp["Root"]) # # # and finally sort # xdg.Menu.sort(xdg.Menu.tmp["Root"]) # xdg.Menu.tmp["Root"].Files = xdg.Menu.tmp["mergeFiles"] + [ xdg.Menu.tmp["Root"].Filename ] # return xdg.Menu.tmp["Root"] # #xdg.Menu.parse = xdgParsePatched # Helper function for retrieving the user's location for storing new or modified menu items def get_user_item_path(): item_dir = None if os.environ.has_key('XDG_DATA_HOME'): item_dir = os.path.join(os.environ['XDG_DATA_HOME'], 'applications') else: item_dir = os.path.join(os.environ['HOME'], '.local', 'share', 'applications') if not os.path.isdir(item_dir): os.makedirs(item_dir) return item_dir def get_system_item_paths(): item_dir = None if os.environ.has_key('XDG_DATA_DIRS'): item_dirs = os.environ['XDG_DATA_DIRS'].split(":") else: item_dirs = [os.path.join('usr', 'share')] return item_dirs def rel_path(target, base=os.curdir): if not os.path.exists(target): raise OSError, 'Target does not exist: '+target if not os.path.isdir(base): raise OSError, 'Base is not a directory or does not exist: '+base base_list = (os.path.abspath(base)).split(os.sep) target_list = (os.path.abspath(target)).split(os.sep) for i in range(min(len(base_list), len(target_list))): if base_list[i] <> target_list[i]: break else: i += 1 rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:] return os.path.join(*rel_list) class Menu: def __init__( self, MenuToLookup ): self.tree = matemenu.lookup_tree( MenuToLookup ) self.directory = self.tree.get_root_directory() def getMenus( self, parent=None ): if parent == None: #gives top-level "Applications" item yield self.tree.root else: for menu in parent.get_contents(): if menu.get_type() == matemenu.TYPE_DIRECTORY and self.__isVisible( menu ): yield menu def getItems( self, menu ): for item in menu.get_contents(): if item.get_type() == matemenu.TYPE_ENTRY and item.get_desktop_file_id()[-19:] != '-usercustom.desktop' and self.__isVisible( item ): yield item def __isVisible( self, item ): if item.get_type() == matemenu.TYPE_ENTRY: return not ( item.get_is_excluded() or item.get_is_nodisplay() ) if item.get_type() == matemenu.TYPE_DIRECTORY and len( item.get_contents() ): return True class SuggestionButton ( Gtk.Button ): def __init__( self, iconName, iconSize, label ): Gtk.Button.__init__( self ) self.iconName = iconName self.set_relief( Gtk.ReliefStyle.NONE ) self.set_size_request( -1, -1 ) Align1 = Gtk.Alignment() Align1.set( 0, 0.5, 1.0, 0 ) HBox1 = Gtk.HBox() labelBox = Gtk.VBox( False, 2 ) self.image = Gtk.Image() self.image.set_from_stock( self.iconName, iconSize ) self.image.show() HBox1.pack_start( self.image, False, False, 5 ) self.label = Gtk.Label() self.label.set_ellipsize( Pango.EllipsizeMode.END ) self.label.set_alignment( 0.0, 1.0 ) self.label.show() labelBox.pack_start( self.label, True, True, 2 ) labelBox.show() HBox1.pack_start( labelBox, True, True, 2 ) HBox1.show() Align1.add( HBox1 ) Align1.show() self.add( Align1 ) self.show() def set_image(self, path): self.image.set_from_file(path) def set_text( self, text): self.label.set_markup(text) def set_icon_size (self, size): self.image.set_from_stock( self.iconName, size ) class TargetEntry(Structure): _fields_ = [("target", c_char_p), ("flags", c_int), ("info", c_int)] class pluginclass( object ): TARGET_TYPE_TEXT = 80 array2 = TargetEntry * 2 toButton = array2( ("text/uri-list", 0, TARGET_TYPE_TEXT), ("text/uri-list", 0, TARGET_TYPE_TEXT) ) TARGET_TYPE_FAV = 81 array = TargetEntry * 3 toFav = array( ( "FAVORITES", Gtk.TargetFlags.SAME_APP, 81 ), ( "text/plain", 0, 100 ), ( "text/uri-list", 0, 101 ) ) array1 = TargetEntry * 2 fromFav = array1( ("FAVORITES", Gtk.TargetFlags.SAME_APP, 81), ("FAVORITES", Gtk.TargetFlags.SAME_APP, 81) ) @print_timing def __init__( self, mintMenuWin, toggleButton, de ): self.mintMenuWin = mintMenuWin self.mainMenus = [ ] self.toggleButton = toggleButton self.de = de self.builder = Gtk.Builder() # The Glade file for the plugin self.builder.add_from_file (os.path.join( os.path.dirname( __file__ ), "applications.glade" )) # Read GLADE file self.searchEntry =self.builder.get_object( "searchEntry" ) self.searchButton =self.builder.get_object( "searchButton" ) self.showAllAppsButton =self.builder.get_object( "showAllAppsButton" ) self.showFavoritesButton =self.builder.get_object( "showFavoritesButton" ) self.applicationsBox =self.builder.get_object( "applicationsBox" ) self.categoriesBox =self.builder.get_object( "categoriesBox" ) self.favoritesBox =self.builder.get_object( "favoritesBox" ) self.applicationsScrolledWindow =self.builder.get_object( "applicationsScrolledWindow" ) #i18n self.builder.get_object("searchLabel").set_text("" + _("Search:") + "") self.builder.get_object("searchLabel").set_use_markup(True) self.builder.get_object("label6").set_text(_("Favorites")) self.builder.get_object("label3").set_text(_("Favorites")) 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.numApps = 0 # These properties are NECESSARY to maintain consistency # Set 'window' property for the plugin (Must be the root widget) self.window =self.builder.get_object( "mainWindow" ) # Set 'heading' property for plugin self.heading = ""#_("Applications") # This should be the first item added to the window in glade 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" ) ] # Unset all timers self.filterTimer = None self.menuChangedTimer = None # Hookup for text input self.content_holder.connect( "key-press-event", self.keyPress ) self.favoritesBox.connect( "drag-data-received", self.ReceiveCallback ) gtk.gtk_drag_dest_set ( hash(self.favoritesBox), Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, self.toButton, 2, Gdk.DragAction.COPY ) self.showFavoritesButton.connect( "drag-data-received", self.ReceiveCallback ) gtk.gtk_drag_dest_set ( hash(self.showFavoritesButton), Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, self.toButton, 2, Gdk.DragAction.COPY ) # self.searchButton.connect( "button_release_event", self.SearchWithButton ) try: # GSettings stuff self.settings = EasyGSettings( "com.linuxmint.mintmenu.plugins.applications" ) self.GetGSettingsEntries() self.settings.notifyAdd( "icon-size", self.changeIconSize ) self.settings.notifyAdd( "favicon-size", self.changeFavIconSize ) self.settings.notifyAdd( "height", self.changePluginSize ) self.settings.notifyAdd( "width", self.changePluginSize ) self.settings.notifyAdd( "categories-mouse-over", self.changeCategoriesMouseOver ) self.settings.notifyAdd( "swap-generic-name", self.changeSwapGenericName ) self.settings.notifyAdd( "show-category-icons", self.changeShowCategoryIcons ) self.settings.notifyAdd( "show-application-comments", self.changeShowApplicationComments ) self.settings.notifyAdd( "use-apt", self.switchAPTUsage) self.settings.notifyAdd( "fav-cols", self.changeFavCols ) self.settings.notifyAdd( "remember-filter", self.changeRememberFilter) self.settings.bindGSettingsEntryToVar( "int", "category-hover-delay", self, "categoryhoverdelay" ) self.settings.bindGSettingsEntryToVar( "bool", "do-not-filter", self, "donotfilterapps" ) self.settings.bindGSettingsEntryToVar( "string", "search-command", self, "searchtool" ) self.settings.bindGSettingsEntryToVar( "int", "default-tab", self, "defaultTab" ) except Exception, detail: print detail self.currentFavCol = 0 self.favorites = [] self.content_holder.set_size_request( self.width, self.height ) self.categoriesBox.set_size_request( self.width / 3, -1 ) self.applicationsBox.set_size_request( self.width / 2, -1 ) self.buildingButtonList = False self.stopBuildingButtonList = False self.categoryList = [] self.applicationList = [] #self.menuFileMonitors = [] #dirty ugly hack, to get favorites drag origin position self.drag_origin = None self.rebuildLock = False self.activeFilter = (1, "", self.searchEntry) self.adminMenu = None for mainitems in [ "mate-applications.menu", "mate-settings.menu" ]: mymenu = Menu( mainitems ) mymenu.tree.add_monitor( self.menuChanged, None ) #for f in mymenu.directory.Files: # self.menuFileMonitors.append( filemonitor.addMonitor(f, self.onMenuChanged, mymenu.directory.Filename ) ) #for f in mymenu.directory.AppDirs: # self.menuFileMonitors.append( filemonitor.addMonitor(f, self.onMenuChanged, mymenu.directory.Filename ) ) self.refresh_apt_cache() self.suggestions = [] self.current_suggestion = None self.panel = "top" self.panel_position = -1 self.builder.get_object("searchButton").connect( "button-release-event", self.searchPopup ) def refresh_apt_cache(self): if self.useAPT: os.system("mkdir -p %s/.linuxmint/mintMenu/" % home) os.system("/usr/lib/linuxmint/mintMenu/plugins/get_apt_cache.py > %s/.linuxmint/mintMenu/apt.cache &" % home) def get_panel(self): panelsettings = Gio.Settings.new("org.mate.panel") applet_list = panelsettings.get_strv("object-id-list") for applet in applet_list: object_schema = Gio.Settings.new_with_path("org.mate.panel.object", "/org/mate/panel/objects/%s/" % (applet)) keys = object_schema.list_keys() if "applet-iid" in keys: iid = object_schema.get_string("applet-iid") if iid is not None and iid.find("MintMenu") != -1: self.panel = object_schema.get_string("toplevel-id") self.panel_position = object_schema.get_int("position") + 1 def apturl_install(self, widget, pkg_name): if os.path.exists("/usr/bin/apturl"): os.system("/usr/bin/apturl apt://%s &" % pkg_name) else: os.system("xdg-open apt://" + pkg_name + " &") self.mintMenuWin.hide() def __del__( self ): print u"Applications plugin deleted" def wake (self) : pass def destroy( self ): self.content_holder.destroy() self.searchEntry.destroy() self.searchButton.destroy() self.showAllAppsButton.destroy() self.showFavoritesButton.destroy() self.applicationsBox.destroy() self.categoriesBox.destroy() self.favoritesBox.destroy() self.settings.notifyRemoveAll() #for mId in self.menuFileMonitors: # filemonitor.removeMonitor( mId ) def changePluginSize( self, settings, key, args ): if key == "width": self.width = settings.get_int(key) self.categoriesBox.set_size_request( self.width / 3, -1 ) self.applicationsBox.set_size_request( self.width / 2, -1 ) elif key == "height": self.heigth = settings.get_int(key) self.content_holder.set_size_request( self.width, self.height ) def changeSwapGenericName( self, settings, key, args ): self.swapgeneric = settings.get_boolean(key) for child in self.favoritesBox: if isinstance( child, FavApplicationLauncher): child.setSwapGeneric( self.swapgeneric ) def changeShowCategoryIcons( self, settings, key, args ): self.showcategoryicons = settings.get_boolean(key) if self.showcategoryicons: categoryIconSize = self.iconSize else: categoryIconSize = 0 for child in self.categoriesBox: child.setIconSize( categoryIconSize ) def changeIconSize( self, settings, key, args ): self.iconSize = settings.get_int(key) if self.showcategoryicons: categoryIconSize = self.iconSize else: categoryIconSize = 0 for child in self.categoriesBox: child.setIconSize( categoryIconSize ) for child in self.applicationsBox: try: child.setIconSize( self.iconSize ) except: pass def changeFavIconSize( self, settings, key, args ): self.faviconsize = settings.get_int(key) for child in self.favoritesBox: if isinstance( child, FavApplicationLauncher): child.setIconSize( self.faviconsize ) def switchAPTUsage( self, settings, key, args ): self.useAPT = settings.get_boolean(key) self.refresh_apt_cache() def changeRememberFilter( self, settings, key, args): self.rememberFilter = settings.get_boolean(key) def changeShowApplicationComments( self, settings, key, args ): self.showapplicationcomments = settings.get_boolean(key) for child in self.applicationsBox: child.setShowComment( self.showapplicationcomments ) def changeCategoriesMouseOver( self, settings, key, args ): self.categories_mouse_over = settings.get_boolean(key) for child in self.categoriesBox: if self.categories_mouse_over and not child.mouseOverHandlerIds: startId = child.connect( "enter", self.StartFilter, child.filter ) stopId = child.connect( "leave", self.StopFilter ) child.mouseOverHandlerIds = ( startId, stopId ) elif not self.categories_mouse_over and child.mouseOverHandlerIds: child.disconnect( child.mouseOverHandlerIds[0] ) child.disconnect( child.mouseOverHandlerIds[1] ) child.mouseOverHandlerIds = None def changeFavCols(self, settings, key, args): self.favCols = settings.get_int(key) for fav in self.favorites: self.favoritesBox.remove( fav ) self.favoritesPositionOnGrid( fav ) def RegenPlugin( self, *args, **kargs ): self.refresh_apt_cache() # save old config - this is necessary because the app will notified when it sets the default values and you don't want the to reload itself several times oldcategories_mouse_over = self.categories_mouse_over oldtotalrecent = self.totalrecent oldsticky = self.sticky oldminimized = self.minimized oldicon = self.icon oldhideseparator = self.hideseparator oldshowapplicationcomments = self.showapplicationcomments self.GetGSettingsEntries() # if the config hasn't changed return if oldcategories_mouse_over == self.categories_mouse_over and oldiconsize == self.iconSize and oldfaviconsize == self.faviconsize and oldtotalrecent == self.totalrecent and oldswapgeneric == self.swapgeneric and oldshowcategoryicons == self.showcategoryicons and oldcategoryhoverdelay == self.categoryhoverdelay and oldsticky == self.sticky and oldminimized == self.minimized and oldicon == self.icon and oldhideseparator == self.hideseparator and oldshowapplicationcomments == self.showapplicationcomments: return self.Todos() self.buildFavorites() self.RebuildPlugin() def GetGSettingsEntries( self ): self.categories_mouse_over = self.settings.get( "bool", "categories-mouse-over") self.width = self.settings.get( "int", "width") self.height = self.settings.get( "int", "height") self.donotfilterapps = self.settings.get( "bool", "do-not-filter") self.iconSize = self.settings.get( "int", "icon-size") self.faviconsize = self.settings.get( "int", "favicon-size") self.favCols = self.settings.get( "int", "fav-cols") self.swapgeneric = self.settings.get( "bool", "swap-generic-name") self.showcategoryicons = self.settings.get( "bool", "show-category-icons") self.categoryhoverdelay = self.settings.get( "int", "category-hover-delay") self.showapplicationcomments = self.settings.get( "bool", "show-application-comments") self.useAPT = self.settings.get( "bool", "use-apt") self.rememberFilter = self.settings.get( "bool", "remember-filter") self.lastActiveTab = self.settings.get( "int", "last-active-tab") self.defaultTab = self.settings.get( "int", "default-tab") # Allow plugin to be minimized to the left plugin pane self.sticky = self.settings.get( "bool", "sticky") self.minimized = self.settings.get( "bool", "minimized") # Search tool self.searchtool = self.settings.get( "string", "search-command") if self.searchtool == "beagle-search SEARCH_STRING": self.searchtool = "mate-search-tool --named \"%s\" --start" self.settings.set( "string", "search-command", "mate-search-tool --named \"%s\" --start" ) # Plugin icon self.icon = self.settings.get( "string", "icon" ) # Hide vertical dotted separator self.hideseparator = self.settings.get( "bool", "hide-separator") def SetHidden( self, state ): if state == True: self.settings.set( "bool", "minimized", True ) else: self.settings.set( "bool", "minimized", False ) def RebuildPlugin(self): self.content_holder.set_size_request( self.width, self.height ) def checkMintMenuFolder( self ): if os.path.exists( os.path.join( os.path.expanduser( "~" ), ".linuxmint", "mintMenu", "applications" ) ): return True try: os.makedirs( os.path.join( os.path.expanduser( "~" ), ".linuxmint", "mintMenu", "applications" ) ) return True except: pass return False def onShowMenu( self ): if len( self.favorites ): if self.defaultTab == -1: self.changeTab( self.lastActiveTab) else: self.changeTab( (self.defaultTab - 1) * -1 ) else: self.changeTab( 1 ) self.searchEntry.select_region( 0, -1 ) if self.rememberFilter and self.searchEntry.get_text().strip() != "": self.Filter(self.activeFilter[2], self.activeFilter[1]) def onHideMenu( self ): self.settings.set( "int", "last-active-tab", self.lastActiveTab ) def changeTab( self, tabNum ): notebook = self.builder.get_object( "notebook2" ) if tabNum == 0: notebook.set_current_page( 0 ) elif tabNum == 1: notebook.set_current_page( 1 ) self.lastActiveTab = tabNum self.focusSearchEntry() def Todos( self ): 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 focusSearchEntry( self ): # grab_focus() does select all text, # restoring the original selection is somehow broken, so just select the end # of the existing text, that's the most likely candidate anyhow self.searchEntry.grab_focus() if self.rememberFilter: gtk.gtk_editable_set_position(hash(self.searchEntry), -1) else: self.searchEntry.set_text("") def buildButtonList( self ): if self.buildingButtonList: self.stopBuildingButtonList = True GLib.timeout_add( 100, self.buildButtonList ) return self.stopBuildingButtonList = False self.updateBoxes(False) def categoryBtnFocus( self, widget, event, category ): self.scrollItemIntoView( widget ) self.StartFilter( widget, category ) def StartFilter( self, widget, category ): # if there is a timer for a different category running stop it if self.filterTimer: GLib.source_remove( self.filterTimer ) self.filterTimer = GLib.timeout_add( self.categoryhoverdelay, self.Filter, widget, category ) def StopFilter( self, widget ): if self.filterTimer: GLib.source_remove( self.filterTimer ) self.filterTimer = None def add_search_suggestions(self, text): text = "%s" % text suggestionButton = SuggestionButton(Gtk.STOCK_ADD, self.iconSize, "") suggestionButton.connect("clicked", self.search_google) suggestionButton.set_text(_("Search Google for %s") % text) suggestionButton.set_image("/usr/lib/linuxmint/mintMenu/search_engines/google.ico") self.applicationsBox.add(suggestionButton) self.suggestions.append(suggestionButton) suggestionButton = SuggestionButton(Gtk.STOCK_ADD, self.iconSize, "") suggestionButton.connect("clicked", self.search_wikipedia) suggestionButton.set_text(_("Search Wikipedia for %s") % text) suggestionButton.set_image("/usr/lib/linuxmint/mintMenu/search_engines/wikipedia.ico") self.applicationsBox.add(suggestionButton) self.suggestions.append(suggestionButton) separator = Gtk.EventBox() separator.add(Gtk.HSeparator()) 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) suggestionButton = SuggestionButton(Gtk.STOCK_ADD, self.iconSize, "") suggestionButton.connect("clicked", self.search_dictionary) suggestionButton.set_text(_("Lookup %s in Dictionary") % text) suggestionButton.set_image("/usr/lib/linuxmint/mintMenu/search_engines/dictionary.png") self.applicationsBox.add(suggestionButton) self.suggestions.append(suggestionButton) suggestionButton = SuggestionButton(Gtk.STOCK_FIND, self.iconSize, "") suggestionButton.connect("clicked", self.Search) suggestionButton.set_text(_("Search Computer for %s") % text) self.applicationsBox.add(suggestionButton) self.suggestions.append(suggestionButton) #self.last_separator = gtk.EventBox() #self.last_separator.add(gtk.HSeparator()) #self.last_separator.set_size_request(-1, 20) #self.last_separator.type = "separator" #self.mintMenuWin.SetPaneColors( [ self.last_separator ] ) #self.last_separator.show_all() #self.applicationsBox.add(self.last_separator) #self.suggestions.append(self.last_separator) def add_apt_filter_results(self, keyword): try: # Wait to see if the keyword has changed.. before doing anything current_keyword = keyword current_keyword = self.searchEntry.get_text() if keyword != current_keyword: return found_packages = [] found_in_name = [] found_elsewhere = [] keywords = keyword.split(" ") command = "cat %(home)s/.linuxmint/mintMenu/apt.cache" % {'home':home} for word in keywords: command = "%(command)s | grep %(word)s" % {'command':command, 'word':word} pkgs = commands.getoutput(command) pkgs = pkgs.split("\n") num_pkg_found = 0 for pkg in pkgs: values = string.split(pkg, "###") if len(values) == 4: status = values[0] if (status == "ERROR"): print "Could not refresh APT cache" elif (status == "CACHE"): name = values[1] summary = values[2] description = values[3].replace("~~~", "\n") package = PackageDescriptor(name, summary, description) #See if all keywords are in the name (so we put these results at the top of the list) some_found = False some_not_found = False for word in keywords: if word in package.name: some_found = True else: some_not_found = True if some_found and not some_not_found: found_in_name.append(package) else: found_elsewhere.append(package) num_pkg_found+=1 else: print "Invalid status code: " + status found_packages.extend(found_in_name) found_packages.extend(found_elsewhere) if keyword == self.searchEntry.get_text() and len(found_packages) > 0: last_separator = Gtk.EventBox() 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.show_all() self.applicationsBox.add(last_separator) self.suggestions.append(last_separator) #Reduce the number of results to 10 max... it takes a HUGE amount of time to add the GTK box in the menu otherwise.. if len(found_packages) > 10: found_packages = found_packages[:10] for pkg in found_packages: name = pkg.name for word in keywords: if word != "": name = name.replace(word, "%s" % word); suggestionButton = SuggestionButton(Gtk.STOCK_ADD, self.iconSize, "") suggestionButton.connect("clicked", self.apturl_install, pkg.name) suggestionButton.set_text(_("Install package '%s'") % name) suggestionButton.set_tooltip_text("%s\n\n%s\n\n%s" % (pkg.name, pkg.summary, pkg.description)) suggestionButton.set_icon_size(self.iconSize) self.applicationsBox.add(suggestionButton) self.suggestions.append(suggestionButton) #if cache != self.current_results: # self.current_results.append(pkg) #if len(found_packages) == 0: # gtk.gdk.threads_enter() # try: # self.applicationsBox.remove(self.last_separator) # self.suggestions.remove(self.last_separator) # finally: # gtk.gdk.threads_leave() except Exception, detail: print detail def add_apt_filter_results_sync(self, cache, keyword): try: found_packages = [] keywords = keyword.split(" ") if cache is not None: for pkg in cache: some_found = False some_not_found = False for word in keywords: if word in pkg.name: some_found = True else: some_not_found = True if some_found and not some_not_found: found_packages.append(pkg) if len(found_packages) > 0: last_separator = Gtk.EventBox() 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.show_all() self.applicationsBox.add(last_separator) self.suggestions.append(last_separator) for pkg in found_packages: name = pkg.name for word in keywords: if word != "": name = name.replace(word, "%s" % word); suggestionButton = SuggestionButton(Gtk.STOCK_ADD, self.iconSize, "") suggestionButton.connect("clicked", self.apturl_install, pkg.name) suggestionButton.set_text(_("Install package '%s'") % name) suggestionButton.set_tooltip_text("%s\n\n%s\n\n%s" % (pkg.name, pkg.summary.capitalize(), pkg.description)) suggestionButton.set_icon_size(self.iconSize) self.applicationsBox.add(suggestionButton) self.suggestions.append(suggestionButton) #if len(found_packages) == 0: # self.applicationsBox.remove(self.last_separator) # self.suggestions.remove(self.last_separator) except Exception, detail: print detail def Filter( self, widget, category = None ): self.filterTimer = None for suggestion in self.suggestions: self.applicationsBox.remove(suggestion) self.suggestions = [] if widget == self.searchEntry: if self.donotfilterapps: widget.set_text( "" ) else: if self.lastActiveTab != 1: self.changeTab( 1 ) text = widget.get_text() showns = False # Are any app shown? shownList = [] for i in self.applicationsBox.get_children(): shown = i.filterText( text ) if (shown): dupe = False for item in shownList: if i.desktopFile == item.desktopFile: dupe = True if dupe: i.hide() else: shownList.append(i) showns = True if (not showns and os.path.exists("/usr/lib/linuxmint/mintInstall/icon.svg")): if len(text) >= 3: if self.current_suggestion is not None and self.current_suggestion in text: # We're restricting our search... self.add_search_suggestions(text) #if (len(self.current_results) > 0): #self.add_apt_filter_results_sync(self.current_results, text) #else: GLib.timeout_add (300, self.add_apt_filter_results, text) else: self.current_results = [] self.add_search_suggestions(text) GLib.timeout_add (300, self.add_apt_filter_results, text) self.current_suggestion = text else: self.current_suggestion = None self.current_results = [] else: self.current_suggestion = None self.current_results = [] for i in self.categoriesBox.get_children(): i.released() i.set_relief( Gtk.ReliefStyle.NONE ) allButton = self.categoriesBox.get_children()[0]; allButton.set_relief( Gtk.ReliefStyle.HALF ) self.activeFilter = (0, text, widget) else: #print "CATFILTER" self.activeFilter = (1, category, widget) if category == "": listedDesktopFiles = [] for i in self.applicationsBox.get_children(): if not i.desktop_file_path in listedDesktopFiles: listedDesktopFiles.append( i.desktop_file_path ) i.show_all() else: i.hide() else: for i in self.applicationsBox.get_children(): i.filterCategory( category ) for i in self.categoriesBox.get_children(): 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 ) # Forward all text to the search box def keyPress( self, widget, event ): if event.string.strip() != "" or event.keyval == Gdk.KEY_BackSpace: self.searchEntry.event( event ) return True if event.keyval == Gdk.KEY_Down and self.searchEntry.is_focus(): self.applicationsBox.get_children()[0].grab_focus() return False def favPopup( self, widget, ev ): if ev.button == 3: if ev.y > widget.get_allocation().height / 2: insertBefore = False else: insertBefore = True if widget.type == "location": mTree = Gtk.Menu() mTree.set_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK) #i18n desktopMenuItem = Gtk.MenuItem(_("Add to desktop")) panelMenuItem = Gtk.MenuItem(_("Add to panel")) separator1 = Gtk.SeparatorMenuItem() insertSpaceMenuItem = Gtk.MenuItem(_("Insert space")) insertSeparatorMenuItem = Gtk.MenuItem(_("Insert separator")) separator2 = Gtk.SeparatorMenuItem() startupMenuItem = Gtk.CheckMenuItem(_("Launch when I log in")) separator3 = Gtk.SeparatorMenuItem() launchMenuItem = Gtk.MenuItem(_("Launch")) removeFromFavMenuItem = Gtk.MenuItem(_("Remove from favorites")) separator4 = Gtk.SeparatorMenuItem() propsMenuItem = Gtk.MenuItem(_("Edit properties")) desktopMenuItem.connect("activate", self.add_to_desktop, widget) panelMenuItem.connect("activate", self.add_to_panel, widget) insertSpaceMenuItem.connect( "activate", self.onFavoritesInsertSpace, widget, insertBefore ) insertSeparatorMenuItem.connect( "activate", self.onFavoritesInsertSeparator, widget, insertBefore ) if widget.isInStartup(): startupMenuItem.set_active( True ) startupMenuItem.connect( "toggled", self.onRemoveFromStartup, widget ) else: startupMenuItem.set_active( False ) startupMenuItem.connect( "toggled", self.onAddToStartup, widget ) launchMenuItem.connect( "activate", self.onLaunchApp, widget) removeFromFavMenuItem.connect( "activate", self.onFavoritesRemove, widget ) propsMenuItem.connect( "activate", self.onPropsApp, widget) if self.de == "mate": mTree.append(desktopMenuItem) mTree.append(panelMenuItem) mTree.append(separator1) mTree.append(insertSpaceMenuItem) mTree.append(insertSeparatorMenuItem) mTree.append(separator2) mTree.append(startupMenuItem) mTree.append(separator3) mTree.append(launchMenuItem) mTree.append(removeFromFavMenuItem) mTree.append(separator4) mTree.append(propsMenuItem) mTree.show_all() gtk.gtk_menu_popup(hash(mTree), None, None, None, None, ev.button, ev.time) self.mintMenuWin.grab() else: mTree = Gtk.Menu() mTree.set_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.POINTER_MOTION_HINT_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK) #i18n removeMenuItem = Gtk.MenuItem(_("Remove")) insertSpaceMenuItem = Gtk.MenuItem(_("Insert space")) insertSeparatorMenuItem = Gtk.MenuItem(_("Insert separator")) mTree.append(removeMenuItem) mTree.append(insertSpaceMenuItem) mTree.append(insertSeparatorMenuItem) mTree.show_all() removeMenuItem.connect( "activate", self.onFavoritesRemove, widget ) insertSpaceMenuItem.connect( "activate", self.onFavoritesInsertSpace, widget, insertBefore ) insertSeparatorMenuItem.connect( "activate", self.onFavoritesInsertSeparator, widget, insertBefore ) gtk.gtk_menu_popup(hash(mTree), None, None, None, None, ev.button, ev.time) self.mintMenuWin.grab() def menuPopup( self, widget, event ): if event.button == 3: mTree = Gtk.Menu() #i18n desktopMenuItem = Gtk.MenuItem(_("Add to desktop")) panelMenuItem = Gtk.MenuItem(_("Add to panel")) separator1 = Gtk.SeparatorMenuItem() favoriteMenuItem = Gtk.CheckMenuItem(_("Show in my favorites")) startupMenuItem = Gtk.CheckMenuItem(_("Launch when I log in")) separator2 = Gtk.SeparatorMenuItem() launchMenuItem = Gtk.MenuItem(_("Launch")) uninstallMenuItem = Gtk.MenuItem(_("Uninstall")) deleteMenuItem = Gtk.MenuItem(_("Delete from menu")) separator3 = Gtk.SeparatorMenuItem() propsMenuItem = Gtk.MenuItem(_("Edit properties")) if self.de == "mate": mTree.append(desktopMenuItem) mTree.append(panelMenuItem) mTree.append(separator1) mTree.append(favoriteMenuItem) mTree.append(startupMenuItem) mTree.append(separator2) mTree.append(launchMenuItem) mTree.append(uninstallMenuItem) if home in widget.desktopFile: mTree.append(deleteMenuItem) deleteMenuItem.connect("activate", self.delete_from_menu, widget) mTree.append(separator3) mTree.append(propsMenuItem) mTree.show_all() desktopMenuItem.connect("activate", self.add_to_desktop, widget) panelMenuItem.connect("activate", self.add_to_panel, widget) launchMenuItem.connect( "activate", self.onLaunchApp, widget ) propsMenuItem.connect( "activate", self.onPropsApp, widget) uninstallMenuItem.connect ( "activate", self.onUninstallApp, widget ) if self.isLocationInFavorites( widget.desktopFile ): favoriteMenuItem.set_active( True ) favoriteMenuItem.connect( "toggled", self.onRemoveFromFavorites, widget ) else: favoriteMenuItem.set_active( False ) favoriteMenuItem.connect( "toggled", self.onAddToFavorites, widget ) if widget.isInStartup(): startupMenuItem.set_active( True ) startupMenuItem.connect( "toggled", self.onRemoveFromStartup, widget ) else: startupMenuItem.set_active( False ) startupMenuItem.connect( "toggled", self.onAddToStartup, widget ) mTree.connect( 'deactivate', self.onMenuPopupDeactivate) gtk.gtk_menu_popup(hash(mTree), None, None, None, None, 0, 0) def onMenuPopupDeactivate( self, widget): self.mintMenuWin.grab() def searchPopup( self, widget=None, event=None ): menu = Gtk.Menu() menuItem = Gtk.ImageMenuItem(_("Search Google")) img = Gtk.Image() img.set_from_file('/usr/lib/linuxmint/mintMenu/search_engines/google.ico') menuItem.set_image(img) menuItem.connect("activate", self.search_google) menu.append(menuItem) menuItem = Gtk.ImageMenuItem(_("Search Wikipedia")) img = Gtk.Image() img.set_from_file('/usr/lib/linuxmint/mintMenu/search_engines/wikipedia.ico') menuItem.set_image(img) menuItem.connect("activate", self.search_wikipedia) menu.append(menuItem) menuItem = Gtk.SeparatorMenuItem() menu.append(menuItem) menuItem = Gtk.ImageMenuItem(_("Lookup Dictionary")) img = Gtk.Image() img.set_from_file('/usr/lib/linuxmint/mintMenu/search_engines/dictionary.png') menuItem.set_image(img) menuItem.connect("activate", self.search_dictionary) menu.append(menuItem) menuItem = Gtk.ImageMenuItem(_("Search Computer")) img = Gtk.Image() img.set_from_stock(Gtk.STOCK_FIND, self.iconSize) menuItem.set_image(img) menuItem.connect("activate", self.Search) menu.append(menuItem) menuItem = Gtk.SeparatorMenuItem() menu.append(menuItem) menuItem = Gtk.ImageMenuItem(_("Find Software")) img = Gtk.Image() img.set_from_file('/usr/lib/linuxmint/mintMenu/search_engines/software.png') menuItem.set_image(img) menuItem.connect("activate", self.search_mint_software) menu.append(menuItem) menuItem = Gtk.ImageMenuItem(_("Find Tutorials")) img = Gtk.Image() img.set_from_file('/usr/lib/linuxmint/mintMenu/search_engines/tutorials.png') menuItem.set_image(img) menuItem.connect("activate", self.search_mint_tutorials) menu.append(menuItem) menuItem = Gtk.ImageMenuItem(_("Find Hardware")) img = Gtk.Image() img.set_from_file('/usr/lib/linuxmint/mintMenu/search_engines/hardware.png') menuItem.set_image(img) menuItem.connect("activate", self.search_mint_hardware) menu.append(menuItem) menuItem =Gtk.ImageMenuItem(_("Find Ideas")) img = Gtk.Image() img.set_from_file('/usr/lib/linuxmint/mintMenu/search_engines/ideas.png') menuItem.set_image(img) menuItem.connect("activate", self.search_mint_ideas) menu.append(menuItem) menuItem = Gtk.ImageMenuItem(_("Find Users")) img = Gtk.Image() img.set_from_file('/usr/lib/linuxmint/mintMenu/search_engines/users.png') menuItem.set_image(img) menuItem.connect("activate", self.search_mint_users) menu.append(menuItem) menu.show_all() gtk.gtk_menu_popup(hash(menu), None, None, None, None, 3, 0) #menu.attach_to_widget(self.searchButton, None) #menu.reposition() #menu.reposition() self.mintMenuWin.grab() self.focusSearchEntry() return True def pos_func(self, menu=None): rect = self.searchButton.get_allocation() x = rect.x + rect.width y = rect.y + rect.height return (x, y, False) def search_google(self, widget): text = self.searchEntry.get_text() text = text.replace(" ", "+") os.system("xdg-open \"http://www.google.com/cse?cx=002683415331144861350%3Atsq8didf9x0&ie=utf-8&sa=Search&q=" + text + "\" &") self.mintMenuWin.hide() def search_wikipedia(self, widget): text = self.searchEntry.get_text() text = text.replace(" ", "+") os.system("xdg-open \"http://en.wikipedia.org/wiki/Special:Search?search=" + text + "\" &") self.mintMenuWin.hide() def search_dictionary(self, widget): text = self.searchEntry.get_text() os.system("mate-dictionary \"" + text + "\" &") self.mintMenuWin.hide() def search_mint_tutorials(self, widget): text = self.searchEntry.get_text() text = text.replace(" ", "%20") os.system("xdg-open \"http://community.linuxmint.com/index.php/tutorial/search/0/" + text + "\" &") self.mintMenuWin.hide() def search_mint_ideas(self, widget): text = self.searchEntry.get_text() text = text.replace(" ", "%20") os.system("xdg-open \"http://community.linuxmint.com/index.php/idea/search/0/" + text + "\" &") self.mintMenuWin.hide() def search_mint_users(self, widget): text = self.searchEntry.get_text() text = text.replace(" ", "%20") os.system("xdg-open \"http://community.linuxmint.com/index.php/user/search/0/" + text + "\" &") self.mintMenuWin.hide() def search_mint_hardware(self, widget): text = self.searchEntry.get_text() text = text.replace(" ", "%20") os.system("xdg-open \"http://community.linuxmint.com/index.php/hardware/search/0/" + text + "\" &") self.mintMenuWin.hide() def search_mint_software(self, widget): text = self.searchEntry.get_text() text = text.replace(" ", "%20") os.system("xdg-open \"http://community.linuxmint.com/index.php/software/search/0/" + text + "\" &") self.mintMenuWin.hide() def add_to_desktop(self, widget, desktopEntry): try: # Determine where the Desktop folder is (could be localized) import sys, commands sys.path.append('/usr/lib/linuxmint/common') from configobj import ConfigObj config = ConfigObj(home + "/.config/user-dirs.dirs") desktopDir = home + "/Desktop" tmpdesktopDir = config['XDG_DESKTOP_DIR'] tmpdesktopDir = commands.getoutput("echo " + tmpdesktopDir) if os.path.exists(tmpdesktopDir): desktopDir = tmpdesktopDir # Copy the desktop file to the desktop os.system("cp \"%s\" \"%s/\"" % (desktopEntry.desktopFile, desktopDir)) os.system("chmod a+rx %s/*.desktop" % (desktopDir)) except Exception, detail: print detail def add_to_panel(self, widget, desktopEntry): self.get_panel() i = 0 panel_schema = Gio.Settings.new("org.mate.panel") applet_list = panel_schema.get_strv("object-id-list") while True: test_obj = "object_%d" % (i) if test_obj in applet_list: i += 1 else: break path = "/org/mate/panel/objects/%s/" % (test_obj) new_schema = Gio.Settings.new_with_path("org.mate.panel.object", path) new_schema.set_string("launcher-location", desktopEntry.desktopFile) new_schema.set_string("object-type", "launcher") new_schema.set_string("toplevel-id", self.panel) new_schema.set_int("position", self.panel_position) applet_list.append(test_obj) panel_schema.set_strv("object-id-list", applet_list) def delete_from_menu(self, widget, desktopEntry): try: os.system("rm \"%s\" &" % desktopEntry.desktopFile) except Exception, detail: print detail def onLaunchApp( self, menu, widget ): widget.execute() self.mintMenuWin.hide() def onPropsApp( self, menu, widget ): newFileFlag = False sysPaths = get_system_item_paths() for path in sysPaths: path += "applications" relPath = os.path.relpath(widget.desktopFile, path) if widget.desktopFile == os.path.join(path, relPath): filePath = os.path.join(get_user_item_path(), relPath) (head,tail) = os.path.split(filePath) if not os.path.isdir(head): os.makedirs(head) if not os.path.isfile(filePath): data = open(widget.desktopFile).read() open(filePath, 'w').write(data) newFileFlag = True break else: filePath = widget.desktopFile self.mintMenuWin.hide() Gdk.flush() editProcess = subprocess.Popen(["/usr/bin/mate-desktop-item-edit", filePath]) subprocess.Popen.communicate(editProcess) if newFileFlag: if filecmp.cmp(widget.desktopFile, filePath): os.remove(filePath) else: favoriteChange = 0 for favorite in self.favorites: if favorite.type == "location": if favorite.desktopFile == widget.desktopFile: favorite.desktopFile = filePath favoriteChange = 1 if favoriteChange == 1: self.favoritesSave() self.buildFavorites() else: self.buildFavorites() def onUninstallApp( self, menu, widget ): widget.uninstall() self.mintMenuWin.hide() def onFavoritesInsertSpace( self, menu, widget, insertBefore ): if insertBefore: self.favoritesAdd( self.favoritesBuildSpace(), widget.position ) else: self.favoritesAdd( self.favoritesBuildSpace(), widget.position + 1 ) def onFavoritesInsertSeparator( self, menu, widget, insertBefore ): if insertBefore: self.favoritesAdd( self.favoritesBuildSeparator(), widget.position ) else: self.favoritesAdd( self.favoritesBuildSeparator(), widget.position + 1 ) def onFavoritesRemove( self, menu, widget ): self.favoritesRemove( widget.position ) def onAddToStartup( self, menu, widget ): widget.addToStartup() def onRemoveFromStartup( self, menu, widget ): widget.removeFromStartup() def onAddToFavorites( self, menu, widget ): self.favoritesAdd( self.favoritesBuildLauncher( widget.desktopFile ) ) def onRemoveFromFavorites( self, menu, widget ): self.favoritesRemoveLocation( widget.desktopFile ) def ReceiveCallback( self, widget, context, x, y, selection, targetType, time ): if targetType == self.TARGET_TYPE_TEXT: for uri in selection.get_uris(): self.favoritesAdd( self.favoritesBuildLauncher( uri ) ) def Search( self, widget ): text = self.searchEntry.get_text().strip() if text != "": self.mintMenuWin.hide() fullstring = self.searchtool.replace( "%s", text ) os.system(fullstring + " &") def SearchWithButton( self, widget, event ): self.Search( widget ) def do_plugin( self ): self.Todos() self.buildFavorites() # Scroll button into view def scrollItemIntoView( self, widget, event = None ): viewport = widget.parent while not isinstance( viewport, Gtk.Viewport ): if not viewport.parent: return viewport = viewport.parent aloc = widget.get_allocation() viewport.get_vadjustment().clamp_page(aloc.y, aloc.y + aloc.height) def favoritesBuildSpace( self ): space = Gtk.EventBox() space.set_size_request( -1, 20 ) space.set_visible_window(False) space.connect( "button_release_event", self.favPopup ) space.type = "space" self.mintMenuWin.SetPaneColors( [ space ] ) space.show() return space def favoritesBuildSeparator( self ): separator = Gtk.HSeparator() separator.set_size_request( -1, 20 ) separator.type = "separator" self.mintMenuWin.SetPaneColors( [ separator ] ) separator.show_all() box = Gtk.EventBox() box.type = "separator" box.add(separator) box.set_visible_window(False) box.connect( "button_release_event", self.favPopup ) box.show_all() return box def favoritesBuildLauncher( self, location ): try: ButtonIcon = None # For Folders and Network Shares location = string.join( location.split( "%20" ) ) if location.startswith( "file" ): ButtonIcon = "mate-fs-directory" if location.startswith( "smb" ) or location.startswith( "ssh" ) or location.startswith( "network" ): ButtonIcon = "mate-fs-network" #For Special locations if location == "x-nautilus-desktop:///computer": location = "/usr/share/applications/nautilus-computer.desktop" elif location == "x-nautilus-desktop:///home": location = "/usr/share/applications/nautilus-home.desktop" elif location == "x-nautilus-desktop:///network": location = "/usr/share/applications/network-scheme.desktop" elif location.startswith( "x-nautilus-desktop:///" ): location = "/usr/share/applications/nautilus-computer.desktop" if location.startswith( "file://" ): location = location[7:] # Don't add a location twice for fav in self.favorites: if fav.type == "location" and fav.desktopFile == location: return None favButton = FavApplicationLauncher( location, self.faviconsize, self.swapgeneric ) if favButton.appExec: favButton.show() favButton.connect( "popup-menu", self.favPopup ) favButton.connect( "button_release_event", self.favPopup ) favButton.connect( "focus-in-event", self.scrollItemIntoView ) favButton.connect( "clicked", lambda w: self.mintMenuWin.hide() ) self.mintMenuWin.setTooltip( favButton, favButton.getTooltip() ) favButton.type = "location" return favButton except Exception, e: print u"File in favorites not found: '" + location + "'", e return None def buildFavorites( self ): try: from user import home if (not os.path.exists(home + "/.linuxmint/mintMenu/applications.list")): os.system("mkdir -p " + home + "/.linuxmint/mintMenu/applications") os.system("cp /usr/lib/linuxmint/mintMenu/applications.list " + home + "/.linuxmint/mintMenu/applications.list") applicationsFile = open ( os.path.join( os.path.expanduser( "~" ), ".linuxmint", "mintMenu", "applications.list" ), "r" ) applicationsList = applicationsFile.readlines() self.favorites = [] for child in self.favoritesBox: child.destroy() position = 0 for app in applicationsList : app = app.strip() if app[0:9] == "location:": favButton = self.favoritesBuildLauncher( app[9:] ) elif app == "space": favButton = self.favoritesBuildSpace() elif app == "separator": favButton = self.favoritesBuildSeparator() else: if ( app.endswith( ".desktop" ) ): favButton = self.favoritesBuildLauncher( app ) else: favButton = None if favButton: favButton.position = position self.favorites.append( favButton ) self.favoritesPositionOnGrid( favButton ) favButton.connect( "drag-data-received", self.onFavButtonDragReorder ) gtk.gtk_drag_dest_set( hash(favButton), Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, self.fromFav, 2, Gdk.DragAction.COPY ) favButton.connect( "drag-data-get", self.onFavButtonDragReorderGet ) gtk.gtk_drag_source_set( hash(favButton), Gdk.ModifierType.BUTTON1_MASK, self.toFav, 3, Gdk.DragAction.COPY ) position += 1 self.favoritesSave() except Exception, e: print e def favoritesGetNumRows( self ): rows = 0 col = 0 for fav in self.favorites: if ( fav.type == "separator" or fav.type == "space" ) and col != 0: rows += 1 col = 0 col += 1 if fav.type == "separator" or fav.type == "space": rows += 1 col = 0 if col >= self.favCols: rows += 1 col = 0 return rows def favoritesPositionOnGrid( self, favorite ): row = 0 col = 0 for fav in self.favorites: if ( fav.type == "separator" or fav.type == "space" ) and col != 0: row += 1 col = 0 if fav.position == favorite.position: break col += 1 if fav.type == "separator" or fav.type == "space": row += 1 col = 0 if col >= self.favCols: row += 1 col = 0 if favorite.type == "separator" or favorite.type == "space": self.favoritesBox.attach( favorite, col, col + self.favCols, row, row + 1, yoptions = 0 ) else: self.favoritesBox.attach( favorite, col, col + 1, row, row + 1, yoptions = 0 ) def favoritesReorder( self, oldposition, newposition ): if oldposition == newposition: return tmp = self.favorites[ oldposition ] if newposition > oldposition: if ( self.favorites[ newposition - 1 ].type == "space" or self.favorites[ newposition - 1 ].type == "separator" ) and self.favCols > 1: newposition = newposition - 1 for i in range( oldposition, newposition ): self.favorites[ i ] = self.favorites[ i + 1 ] self.favorites[ i ].position = i elif newposition < oldposition: for i in range( 0, oldposition - newposition ): self.favorites[ oldposition - i ] = self.favorites[ oldposition - i - 1 ] self.favorites[ oldposition - i ] .position = oldposition - i self.favorites[ newposition ] = tmp self.favorites[ newposition ].position = newposition for fav in self.favorites: self.favoritesBox.remove( fav ) self.favoritesPositionOnGrid( fav ) self.favoritesSave() self.favoritesBox.resize( self.favoritesGetNumRows(), self.favCols ) def favoritesAdd( self, favButton, position = -1 ): if favButton: favButton.position = len( self.favorites ) self.favorites.append( favButton ) self.favoritesPositionOnGrid( favButton ) favButton.connect( "drag-data-received", self.onFavButtonDragReorder ) gtk.gtk_drag_dest_set( hash(favButton), Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, self.toFav, 3, Gdk.DragAction.COPY ) favButton.connect( "drag-data-get", self.onFavButtonDragReorderGet ) gtk.gtk_drag_source_set ( hash(favButton), Gdk.ModifierType.BUTTON1_MASK, self.toFav, 3, Gdk.DragAction.COPY ) if position >= 0: self.favoritesReorder( favButton.position, position ) self.favoritesSave() def favoritesRemove( self, position ): tmp = self.favorites[ position ] self.favorites.remove( self.favorites[ position ] ) tmp.destroy() for i in range( position, len( self.favorites ) ): self.favorites[ i ].position = i self.favoritesBox.remove( self.favorites[ i ] ) self.favoritesPositionOnGrid( self.favorites[ i ] ) self.favoritesSave() self.favoritesBox.resize( self.favoritesGetNumRows(), self.favCols ) def favoritesRemoveLocation( self, location ): for fav in self.favorites: if fav.type == "location" and fav.desktopFile == location: self.favoritesRemove( fav.position ) def favoritesSave( self ): try: self.checkMintMenuFolder() appListFile = open( os.path.join( os.path.expanduser( "~"), ".linuxmint", "mintMenu", "applications.list" ) , "w" ) for favorite in self.favorites: if favorite.type == "location": appListFile.write( "location:" + favorite.desktopFile + "\n" ) else: appListFile.write( favorite.type + "\n" ) appListFile.close( ) except Exception, e: msgDlg = Gtk.MessageDialog( None, gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, _("Couldn't save favorites. Check if you have write access to ~/.linuxmint/mintMenu")+"\n(" + e.__str__() + ")" ) msgDlg.run(); msgDlg.destroy(); def isLocationInFavorites( self, location ): for fav in self.favorites: if fav.type == "location" and fav.desktopFile == location: return True return False def onFavButtonDragReorderGet( self, widget, context, selection, targetType, eventTime ): if targetType == self.TARGET_TYPE_FAV: self.drag_origin = widget.position selection.set( selection.target, 8, str(widget.position)) def onFavButtonDragReorder( self, widget, context, x, y, selection, targetType, time ): if targetType == self.TARGET_TYPE_FAV: #self.favoritesReorder( int(selection.data), widget.position ) self.favoritesReorder( self.drag_origin, widget.position ) def menuChanged( self, x, y ): # wait some miliseconds because there a multiple events send at the same time and we don't want to rebuild the menu for each if self.menuChangedTimer: GLib.source_remove( self.menuChangedTimer ) self.menuChangedTimer = GLib.timeout_add( 100, self.updateBoxes, True ) def updateBoxes( self, menu_has_changed ): # FIXME: This is really bad! if self.rebuildLock: return self.rebuildLock = True self.menuChangedTimer = None self.loadMenuFiles() # Find added and removed categories than update the category list newCategoryList = self.buildCategoryList() addedCategories = [] removedCategories = [] # TODO: optimize this!!! if not self.categoryList: addedCategories = newCategoryList else: for item in newCategoryList: found = False for item2 in self.categoryList: pass if item["name"] == item2["name"] and item["icon"] == item2["icon"] and item["tooltip"] == item2["tooltip"] and item["index"] == item2["index"]: found = True break if not found: addedCategories.append(item) key = 0 for item in self.categoryList: found = False for item2 in newCategoryList: if item["name"] == item2["name"] and item["icon"] == item2["icon"] and item["tooltip"] == item2["tooltip"] and item["index"] == item2["index"]: found = True break if not found: removedCategories.append( key ) else: key += 1 if self.showcategoryicons == True: categoryIconSize = self.iconSize else: categoryIconSize = 0 for key in removedCategories: self.categoryList[key]["button"].destroy() del self.categoryList[key] if addedCategories: sortedCategoryList = [] for item in self.categoryList: self.categoriesBox.remove( item["button"] ) sortedCategoryList.append( ( str(item["index"]) + item["name"], item["button"] ) ) # Create new category buttons and add the to the list for item in addedCategories: item["button"] = CategoryButton( item["icon"], categoryIconSize, [ item["name"] ], item["filter"] ) self.mintMenuWin.setTooltip( item["button"], item["tooltip"] ) if self.categories_mouse_over: startId = item["button"].connect( "enter", self.StartFilter, item["filter"] ) stopId = item["button"].connect( "leave", self.StopFilter ) item["button"].mouseOverHandlerIds = ( startId, stopId ) else: item["button"].mouseOverHandlerIds = None item["button"].connect( "clicked", self.Filter, item["filter"] ) item["button"].connect( "focus-in-event", self.categoryBtnFocus, item["filter"] ) item["button"].show() self.categoryList.append( item ) sortedCategoryList.append( ( str(item["index"]) + item["name"], item["button"] ) ) sortedCategoryList.sort() for item in sortedCategoryList: self.categoriesBox.pack_start( item[1], False, False, 0 ) # Find added and removed applications add update the application list newApplicationList = self.buildApplicationList() addedApplications = [] removedApplications = [] # TODO: optimize this!!! if not self.applicationList: addedApplications = newApplicationList else: for item in newApplicationList: found = False for item2 in self.applicationList: if item["entry"].get_desktop_file_path() == item2["entry"].get_desktop_file_path(): found = True break if not found: addedApplications.append(item) key = 0 for item in self.applicationList: found = False for item2 in newApplicationList: if item["entry"].get_desktop_file_path() == item2["entry"].get_desktop_file_path(): found = True break if not found: removedApplications.append(key) else: # don't increment the key if this item is going to be removed # because when it is removed the index of all later items is # going to be decreased key += 1 for key in removedApplications: self.applicationList[key]["button"].destroy() del self.applicationList[key] if addedApplications: sortedApplicationList = [] for item in self.applicationList: self.applicationsBox.remove( item["button"] ) sortedApplicationList.append( ( item["button"].appName, item["button"] ) ) for item in addedApplications: item["button"] = MenuApplicationLauncher( item["entry"].get_desktop_file_path(), self.iconSize, item["category"], self.showapplicationcomments, highlight=(True and menu_has_changed) ) if item["button"].appExec: self.mintMenuWin.setTooltip( item["button"], item["button"].getTooltip() ) item["button"].connect( "button-press-event", self.menuPopup ) item["button"].connect( "focus-in-event", self.scrollItemIntoView ) item["button"].connect( "clicked", lambda w: self.mintMenuWin.hide() ) if self.activeFilter[0] == 0: item["button"].filterText( self.activeFilter[1] ) else: item["button"].filterCategory( self.activeFilter[1] ) item["button"].desktop_file_path = item["entry"].get_desktop_file_path() sortedApplicationList.append( ( item["button"].appName.upper(), item["button"] ) ) self.applicationList.append( item ) else: item["button"].destroy() sortedApplicationList.sort() for item in sortedApplicationList: self.applicationsBox.pack_start( item[1], False, False, 0 ) self.rebuildLock = False # Reload the menufiles from the filesystem def loadMenuFiles( self ): self.menuFiles = [] for mainitems in [ "mate-applications.menu", "mate-settings.menu" ]: self.menuFiles.append( Menu( mainitems) ) # Build a list of all categories in the menu ( [ { "name", "icon", tooltip" } ] def buildCategoryList( self ): newCategoryList = [ { "name": _("All"), "icon": "stock_select-all", "tooltip": _("Show all applications"), "filter":"", "index": 0 } ] num = 1 for menu in self.menuFiles: for child in menu.directory.get_contents(): if child.get_type() == matemenu.TYPE_DIRECTORY: icon = str(child.icon) #if (icon == "preferences-system"): # self.adminMenu = child.name #if (icon != "applications-system" and icon != "applications-other"): newCategoryList.append( { "name": child.name, "icon": child.icon, "tooltip": child.name, "filter": child.name, "index": num } ) num += 1 return newCategoryList # Build a list containing the DesktopEntry object and the category of each application in the menu def buildApplicationList( self ): newApplicationsList = [] def find_applications_recursively(app_list, directory, catName): for item in directory.get_contents(): if item.get_type() == matemenu.TYPE_ENTRY: print "=======>>> " + str(item.name) + " = " + str(catName) app_list.append( { "entry": item, "category": catName } ) elif item.get_type() == matemenu.TYPE_DIRECTORY: find_applications_recursively(app_list, item, catName) for menu in self.menuFiles: directory = menu.directory for entry in directory.get_contents(): if entry.get_type() == matemenu.TYPE_DIRECTORY and len(entry.get_contents()): #Entry is a top-level category #catName = entry.name #icon = str(entry.icon) #if (icon == "applications-system" or icon == "applications-other"): # catName = self.adminMenu for item in entry.get_contents(): if item.get_type() == matemenu.TYPE_DIRECTORY: find_applications_recursively(newApplicationsList, item, entry.name) elif item.get_type() == matemenu.TYPE_ENTRY: newApplicationsList.append( { "entry": item, "category": entry.name } ) #elif entry.get_type() == matemenu.TYPE_ENTRY: # if not (entry.get_is_excluded() or entry.get_is_nodisplay()): # print "=======>>> " + item.name + " = top level" # newApplicationsList.append( { "entry": item, "category": "" } ) return newApplicationsList