diff --git a/bin/pytop-0-0-1-x64.deb b/bin/pytop-0-0-1-x64.deb
index 4823e22..fe7e716 100644
Binary files a/bin/pytop-0-0-1-x64.deb and b/bin/pytop-0-0-1-x64.deb differ
diff --git a/src/Pytop/resources/PyTop.glade b/src/Pytop/resources/PyTop.glade
index e1124db..f416ce2 100644
--- a/src/Pytop/resources/PyTop.glade
+++ b/src/Pytop/resources/PyTop.glade
@@ -30,6 +30,9 @@
+
+
+
+
+
+
+ False
+ True
+ end
+ 0
+
+
False
@@ -53,23 +72,66 @@
-
-
+
True
False
start
@@ -124,7 +185,7 @@
False
True
- 2
+ 1
diff --git a/src/Pytop/signal_classes/GridSignals.py b/src/Pytop/signal_classes/GridSignals.py
index 1062118..1ed18ad 100644
--- a/src/Pytop/signal_classes/GridSignals.py
+++ b/src/Pytop/signal_classes/GridSignals.py
@@ -27,7 +27,6 @@ class GridSignals:
# Add filter to allow only folders to be selected
selectDirDialog.add_filter(filefilter)
selectDirDialog.set_filename(self.currentPath)
- print(selectDirDialog.get_filename())
self.setNewDirectory(selectDirDialog)
@@ -35,9 +34,7 @@ class GridSignals:
newPath = widget.get_filename()
self.gridClss = Grid(self.gridObj, self.settings)
self.gridClss.setNewDirectory(newPath)
-
- if not "~/Desktop/" in newPath:
- self.settings.saveSettings(newPath)
+ self.settings.saveSettings(newPath)
# File control events
diff --git a/src/Pytop/signal_classes/TaskbarSignals.py b/src/Pytop/signal_classes/TaskbarSignals.py
index 6fca296..7dd3c8a 100644
--- a/src/Pytop/signal_classes/TaskbarSignals.py
+++ b/src/Pytop/signal_classes/TaskbarSignals.py
@@ -19,16 +19,29 @@ class TaskbarSignals:
def __init__(self, settings):
self.settings = settings
self.builder = self.settings.returnBuilder()
+ self.orientation = 1 # 0 = horizontal, 1 = vertical
self.setPagerWidget()
self.setTasklistWidget()
def setPagerWidget(self):
pager = wnck.Pager()
- self.builder.get_object('taskBarWorkspaces').add(pager)
+
+ pager.set_orientation(self.orientation)
+ if self.orientation == 0:
+ self.builder.get_object('taskBarWorkspacesHor').add(pager)
+ else:
+ self.builder.get_object('taskBarWorkspacesVer').add(pager)
+
def setTasklistWidget(self):
barBtns = wnck.Tasklist()
+ barBtns.set_scroll_enabled(False)
barBtns.set_button_relief(2) # 0 = normal relief, 2 = no relief
- barBtns.set_grouping(1) # 0 = mever group, 1 auto group, 2 = always group
- self.builder.get_object('taskBarButtons').add(barBtns)
+ barBtns.set_grouping(1) # 0 = mever group, 1 auto group, 2 = always group
+
+ barBtns.set_orientation(self.orientation)
+ if self.orientation == 0:
+ self.builder.get_object('taskBarButtonsHor').add(barBtns)
+ else:
+ self.builder.get_object('taskBarButtonsVer').add(barBtns)
diff --git a/src/Pytop/widgets/Icon.py b/src/Pytop/widgets/Icon.py
index e443d42..7b97926 100644
--- a/src/Pytop/widgets/Icon.py
+++ b/src/Pytop/widgets/Icon.py
@@ -12,6 +12,8 @@ import os, subprocess, hashlib, threading
from os.path import isdir, isfile, join
# Application imports
+from .icon_manager import easybuttons
+
def threaded(fn):
@@ -91,7 +93,6 @@ class Icon:
try:
xdgObj = DesktopEntry(fullPath)
icon = xdgObj.getIcon()
- iconsDirs = "/usr/share/icons"
altIconPath = ""
if "steam" in icon:
@@ -128,6 +129,8 @@ class Icon:
elif os.path.exists(icon):
return self.createScaledImage(icon, self.systemIconImageWH)
else:
+ # return easybuttons.IconManager().getIcon(icon, 64)
+ iconsDirs = "/usr/share/icons"
for (dirpath, dirnames, filenames) in os.walk(iconsDirs):
for file in filenames:
appNM = "application-x-" + icon
diff --git a/src/Pytop/widgets/icon_manager/__init__.py b/src/Pytop/widgets/icon_manager/__init__.py
new file mode 100644
index 0000000..c797f4b
--- /dev/null
+++ b/src/Pytop/widgets/icon_manager/__init__.py
@@ -0,0 +1,3 @@
+from . import easybuttons
+from . import execute
+from . import filemonitor
diff --git a/src/Pytop/widgets/icon_manager/easybuttons.py b/src/Pytop/widgets/icon_manager/easybuttons.py
new file mode 100644
index 0000000..9b215dc
--- /dev/null
+++ b/src/Pytop/widgets/icon_manager/easybuttons.py
@@ -0,0 +1,613 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2007-2014 Clement Lefebvre
+# Copyright (C) 2015 Martin Wimpress
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import os
+import re
+import shutil
+import xdg.DesktopEntry
+import xdg.Menu
+import gi
+
+gi.require_version('Gtk', '3.0')
+
+from .execute import *
+from .filemonitor import monitor as filemonitor
+from gi.repository import Gtk, Gdk, GLib
+from gi.repository import Pango
+from gi.repository import GObject
+
+class IconManager(GObject.GObject):
+
+ __gsignals__ = {
+ "changed" : (GObject.SignalFlags.RUN_LAST, None, () )
+ }
+
+ def __init__( self ):
+ GObject.GObject.__init__( self )
+ self.icons = { }
+ self.count = 0
+
+ # Some apps don't put a default icon in the default theme folder, so we will search all themes
+ def createTheme( d ):
+ theme = Gtk.IconTheme()
+ theme.set_custom_theme( d )
+ return theme
+
+ # This takes to much time and there are only a very few applications that use icons from different themes
+ #self.themes = map( createTheme, [ d for d in os.listdir( "/usr/share/icons" ) if os.path.isdir( os.path.join( "/usr/share/icons", d ) ) ] )
+
+ self.defaultTheme = Gtk.IconTheme.get_default()
+
+ # Setup and clean up the temp icon dir
+ configDir = GLib.get_user_config_dir()
+ self.iconDir = os.path.join(configDir, "mate-menu")
+ if not os.path.exists(self.iconDir):
+ os.makedirs(self.iconDir)
+ # Skip over files and dirs belonging to the applications plugin
+ contents = frozenset(os.listdir(self.iconDir)) - frozenset(('applications', 'applications.list'))
+ for fn in contents:
+ if os.path.isfile(os.path.join(self.iconDir, fn)):
+ print("Removing file : " + os.path.join(self.iconDir, fn))
+ os.remove(os.path.join(self.iconDir, fn))
+ else:
+ print(os.path.join(self.iconDir, fn) + " is not a file, skipping delete.")
+
+ self.defaultTheme.append_search_path(self.iconDir)
+
+ # Themes with the same content as the default them aren't needed
+ #self.themes = [ theme for theme in self.themes if theme.list_icons() != defaultTheme.list_icons() ]
+
+ self.themes = [ self.defaultTheme ]
+
+ # Listen for changes in the themes
+ for theme in self.themes:
+ theme.connect("changed", self.themeChanged )
+
+
+ def getIcon( self, iconName, iconSize ):
+
+ if not iconName:
+ return None
+
+ try:
+ iconFileName = ""
+ realIconName = ""
+ needTempFile = False
+ #[ iconWidth, iconHeight ] = self.getIconSize( iconSize )
+ if iconSize <= 0:
+ return None
+
+ elif os.path.isabs( iconName ):
+ iconFileName = iconName
+ needTempFile = True
+ else:
+ if iconName[-4:] in [".png", ".xpm", ".svg", ".gif"]:
+ realIconName = iconName[:-4]
+ else:
+ realIconName = iconName
+
+ if iconFileName and needTempFile and os.path.exists( iconFileName ):
+ tmpIconName = iconFileName.replace("/", "-")
+ realIconName = tmpIconName[:-4]
+ if not os.path.exists(os.path.join(self.iconDir, tmpIconName)):
+ shutil.copyfile(iconFileName, os.path.join(self.iconDir, tmpIconName))
+ self.defaultTheme.append_search_path(self.iconDir)
+
+ image = Gtk.Image()
+ icon_found = False
+ for theme in self.themes:
+ if theme.lookup_icon( realIconName, 0, Gtk.IconLookupFlags.FORCE_REGULAR ):
+ icon_found = True
+ break
+
+ if icon_found:
+ image.set_from_icon_name(realIconName, Gtk.IconSize.DND)
+ image.set_pixel_size(iconSize)
+ else:
+ image = None
+
+ return image
+ except Exception as e:
+ print("Exception " + e.__class__.__name__ + ": " + e.message)
+ return None
+
+ def themeChanged( self, theme ):
+ self.emit( "changed" )
+
+GObject.type_register(IconManager)
+
+class easyButton( Gtk.Button ):
+
+ def __init__( self, iconName, iconSize, labels = None, buttonWidth = -1, buttonHeight = -1 ):
+ GObject.GObject.__init__( self )
+ self.connections = [ ]
+ self.iconName = iconName
+ self.iconSize = iconSize
+ self.showIcon = True
+
+ self.set_relief( Gtk.ReliefStyle.NONE )
+ self.set_size_request( buttonWidth, buttonHeight )
+
+ HBox1 = Gtk.Box( orientation=Gtk.Orientation.HORIZONTAL )
+ HBox1.set_valign(Gtk.Align.CENTER)
+ HBox1.set_hexpand(True)
+ self.labelBox = Gtk.Box( orientation=Gtk.Orientation.VERTICAL, spacing=2 )
+
+
+ self.buttonImage = Gtk.Image()
+ icon = self.getIcon( self.iconSize )
+ if icon:
+ self.buttonImage = icon
+ else:
+ #[ iW, iH ] = iconManager.getIconSize( self.iconSize )
+ self.buttonImage.set_size_request( self.iconSize, self.iconSize )
+ self.image_box = Gtk.Box( orientation=Gtk.Orientation.HORIZONTAL )
+ self.image_box.pack_start(self.buttonImage, False, False, 5)
+ self.image_box.show_all()
+ HBox1.pack_start( self.image_box, False, False, 0 )
+
+ if labels:
+ for label in labels:
+ if isinstance( label, str ):
+ self.addLabel( label )
+ elif isinstance( label, list ):
+ self.addLabel( label[0], label[1] )
+
+ self.labelBox.show()
+ HBox1.pack_start( self.labelBox , True, True, 0)
+ HBox1.show()
+ self.add( HBox1 )
+
+ self.set_events(Gdk.EventMask.POINTER_MOTION_MASK)
+ self.connectSelf( "motion-notify-event", self.onMotion )
+ self.connectSelf( "enter-notify-event", self.onEnter )
+ self.connectSelf( "focus-in-event", self.onFocusIn )
+ self.connectSelf( "focus-out-event", self.onFocusOut )
+ self.connectSelf( "destroy", self.onDestroy )
+ self.connect( "released", self.onRelease )
+ # Reload icons when the theme changed
+ self.themeChangedHandlerId = iconManager.connect("changed", self.themeChanged )
+
+ def connectSelf( self, event, callback ):
+ self.connections.append( self.connect( event, callback ) )
+
+ def onMotion( self, widget, event ):
+ # Only grab if mouse is actually hovering
+ if self.mouse_entered:
+ self.grab_focus()
+ self.mouse_entered = False
+
+ def onEnter( self, widget, event ):
+ # Prevent false "enter" notifications by determining
+ # whether the mouse is actually hovering on the button.
+ self.mouse_entered = True
+
+ def onFocusIn( self, widget, event ):
+ self.set_state_flags( Gtk.StateFlags.PRELIGHT, False )
+
+ def onFocusOut( self, widget, event ):
+ self.unset_state_flags( Gtk.StateFlags.PRELIGHT )
+
+ def onRelease( self, widget ):
+ widget.get_style_context().set_state( Gtk.StateFlags.NORMAL )
+
+ def onDestroy( self, widget ):
+ self.buttonImage.clear()
+ iconManager.disconnect( self.themeChangedHandlerId )
+ for connection in self.connections:
+ self.disconnect( connection )
+ del self.connections
+
+
+ def addLabel( self, text, styles = None ):
+ label = Gtk.Label()
+ if "" in text or "= (3, 16):
+ label.set_xalign(0.0)
+ label.set_yalign(1.0)
+ else:
+ label.set_alignment( 0.0, 1.0 )
+ label.set_max_width_chars(0)
+ label.show()
+ self.labelBox.pack_start( label , True, True, 0)
+
+
+ def getIcon ( self, iconSize ):
+ if not self.iconName:
+ return None
+
+ icon = iconManager.getIcon( self.iconName, iconSize )
+ if icon is None:
+ icon = iconManager.getIcon( "gtk-missing-image", iconSize )
+
+ return icon
+
+ def setIcon ( self, iconName ):
+ self.iconName = iconName
+ self.iconChanged()
+
+ # IconTheme changed, setup new button icons
+ def themeChanged( self, theme ):
+ self.iconChanged()
+
+ def iconChanged( self ):
+ icon = self.getIcon( self.iconSize )
+ self.buttonImage.destroy()
+ if icon:
+ self.buttonImage = icon
+ self.image_box.pack_start(self.buttonImage, False, False, 5)
+ self.image_box.show_all()
+ else:
+ #[iW, iH ] = iconManager.getIconSize( self.iconSize )
+ self.buttonImage.set_size_request( self.iconSize, self.iconSize )
+
+ def setIconSize( self, size ):
+ self.iconSize = size
+ icon = self.getIcon( self.iconSize )
+ self.buttonImage.destroy()
+ if icon:
+ self.buttonImage = icon
+ self.image_box.pack_start(self.buttonImage, False, False, 5)
+ self.image_box.show_all()
+ elif self.iconSize:
+ #[ iW, iH ] = iconManager.getIconSize( self.iconSize )
+ self.buttonImage.set_size_request( self.iconSize, self.iconSize )
+
+class ApplicationLauncher( easyButton ):
+
+ def __init__( self, desktopFile, iconSize):
+
+ if isinstance( desktopFile, xdg.Menu.MenuEntry ):
+ desktopItem = desktopFile.DesktopEntry
+ desktopFile = desktopItem.filename
+ self.appDirs = desktop.desktopFile.AppDirs
+ elif isinstance( desktopFile, xdg.Menu.DesktopEntry ):
+ desktopItem = desktopFile
+ desktopFile = desktopItem.filename
+ self.appDirs = [ os.path.dirname( desktopItem.filename ) ]
+ else:
+ desktopItem = xdg.DesktopEntry.DesktopEntry( desktopFile )
+ self.appDirs = [ os.path.dirname( desktopFile ) ]
+
+ self.desktopFile = desktopFile
+ self.startupMonitorId = 0
+ self.relevance = 0
+
+ self.loadDesktopEntry( desktopItem )
+
+ self.desktopEntryMonitors = []
+
+ base = os.path.basename( self.desktopFile )
+ for dir in self.appDirs:
+ self.desktopEntryMonitors.append( filemonitor.addMonitor( os.path.join(dir, base) , self.desktopEntryFileChangedCallback ) )
+
+ easyButton.__init__( self, self.appIconName, iconSize )
+ self.setupLabels()
+
+ # Drag and Drop
+ self.connectSelf( "drag-data-get", self.dragDataGet )
+
+ targets = ( Gtk.TargetEntry.new( "text/plain", 0, 100 ), Gtk.TargetEntry.new( "text/uri-list", 0, 101 ) )
+ self.drag_source_set( Gdk.ModifierType.BUTTON1_MASK, targets, Gdk.DragAction.COPY )
+
+ icon = self.getIcon( Gtk.IconSize.DND )
+ if icon:
+ iconName, s = icon.get_icon_name()
+ self.drag_source_set_icon_name( iconName )
+
+ self.connectSelf( "focus-in-event", self.onFocusIn )
+ self.connectSelf( "focus-out-event", self.onFocusOut )
+ self.connectSelf( "clicked", self.execute )
+
+
+
+ def loadDesktopEntry( self, desktopItem ):
+ try:
+ self.appName = desktopItem.getName()
+ self.appGenericName = desktopItem.getGenericName()
+ self.appComment = desktopItem.getComment()
+ self.appExec = desktopItem.getExec().replace('\\\\', '\\')
+ self.appIconName = desktopItem.getIcon()
+ self.appCategories = desktopItem.getCategories()
+ self.appMateDocPath = desktopItem.get( "X-MATE-DocPath" ) or ""
+ self.useTerminal = desktopItem.getTerminal()
+ self.appPath = desktopItem.getPath()
+ self.appName = self.appName.strip()
+ self.appGenericName = self.appGenericName.strip()
+ self.appComment = self.appComment.strip()
+
+ configPath = os.environ.get( "XDG_CONFIG_HOME",
+ os.path.join( os.environ["HOME"], ".config" ) )
+ basename = os.path.basename( self.desktopFile )
+ self.startupFilePath = os.path.join( configPath, "autostart", basename )
+ if self.startupMonitorId:
+ filemonitor.removeMonitor( self.startupMonitorId )
+ if os.path.exists (self.startupFilePath):
+ self.startupMonitorId = filemonitor.addMonitor( self.startupFilePath, self.startupFileChanged )
+
+ except Exception as e:
+ print(e)
+ self.appName = ""
+ self.appGenericName = ""
+ self.appComment = ""
+ self.appExec = ""
+ self.appIconName = ""
+ self.appCategories = ""
+ self.appDocPath = ""
+ self.startupMonitorId = 0
+
+
+ def onFocusIn( self, widget, event ):
+ super(ApplicationLauncher, self).onFocusIn( widget, event )
+ self.set_relief( Gtk.ReliefStyle.HALF )
+
+ def onFocusOut( self, widget, event ):
+ super(ApplicationLauncher, self).onFocusOut( widget, event )
+ self.set_relief( Gtk.ReliefStyle.NONE )
+
+ def setupLabels( self ):
+ self.addLabel( self.appName )
+
+ def filterText( self, text ):
+ keywords = text.lower().split()
+ self.relevance = 0
+ appName = self.appName.lower()
+ appGenericName = self.appGenericName.lower()
+ appComment = self.appComment.lower()
+ appExec = self.appExec.lower()
+ for keyword in keywords:
+ keyw = keyword
+
+ # Hide if the term does not match
+ if keyw != "" and appName.find( keyw ) == -1 and appGenericName.find( keyw ) == -1 and appComment.find( keyw ) == -1 and appExec.find( keyw ) == -1:
+ self.hide()
+ return False
+
+ # Give better ranking to the actual app name
+ if appName == keyw:
+ self.relevance += 32
+ elif appName.find( keyw ) == 0:
+ self.relevance += 16
+ elif appName.find( keyw ) != -1:
+ self.relevance += 8
+
+ if appExec.find( keyw ) != -1:
+ self.relevance += 4
+ if appComment.find( keyw ) != -1:
+ self.relevance += 2
+ if appGenericName.find( keyw ) != -1:
+ self.relevance += 1
+
+ self.show()
+ return True
+
+ def getTooltip( self ):
+ tooltip = self.appName
+ if self.appComment != "" and self.appComment != self.appName:
+ tooltip = tooltip + "\n" + self.appComment
+
+ return tooltip
+
+ def dragDataGet( self, widget, context, selection, targetType, eventTime ):
+ if targetType == 100: # text/plain
+ selection.set_text( "'" + self.desktopFile + "'", -1 )
+ elif targetType == 101: # text/uri-list
+ if self.desktopFile[0:7] == "file://":
+ selection.set_uris( [ self.desktopFile ] )
+ else:
+ selection.set_uris( [ "file://" + self.desktopFile ] )
+
+ def execute( self, *args ):
+
+ def pathExists(file):
+ if os.path.exists(file):
+ return True
+ for path in os.environ["PATH"].split(os.pathsep):
+ if os.path.exists(os.path.join(path, file)):
+ return True
+
+ if self.appExec:
+ if self.useTerminal:
+ if pathExists("mate-terminal"):
+ cmd = "mate-terminal -e \"" + self.appExec + "\""
+ elif pathExists("x-terminal-emulator"):
+ cmd = "x-terminal-emulator -e \"" + self.appExec + "\""
+ else:
+ cmd = "xterm -e \"" + self.appExec + "\""
+ Execute(cmd, self.appPath)
+ else:
+ Execute(self.appExec, self.appPath)
+
+ # IconTheme changed, setup new icons for button and drag 'n drop
+ def iconChanged( self ):
+ easyButton.iconChanged( self )
+
+ icon = self.getIcon( Gtk.IconSize.DND )
+ if icon:
+ iconName, size = icon.get_icon_name()
+ self.drag_source_set_icon_name( iconName )
+
+ def startupFileChanged( self, *args ):
+ self.inStartup = os.path.exists( self.startupFilePath )
+
+ def removeFromStartup( self ):
+ if os.path.exists( self.startupFilePath ):
+ os.remove( self.startupFilePath )
+
+ def addToFavourites( self ):
+ configPath = os.environ.get( "XDG_CONFIG_HOME",
+ os.path.join( os.environ["HOME"], ".config" ) )
+ favouritesDir = os.path.join( configPath, "mate-menu", "applications" );
+ if not os.path.exists( favouritesDir ):
+ os.makedirs( favouritesDir )
+
+ shutil.copyfile( self.desktopFile, self.favouritesFilePath )
+
+ def removeFromFavourites( self ):
+ if os.path.exists( self.favouritesFilePath ):
+ os.remove( self.favouritesFilePath )
+
+ def isInStartup( self ):
+ #return self.inStartup
+ return os.path.exists( self.startupFilePath )
+
+ def onDestroy( self, widget ):
+ easyButton.onDestroy( self, widget )
+ if self.startupMonitorId:
+ filemonitor.removeMonitor( self.startupMonitorId )
+ for id in self.desktopEntryMonitors:
+ filemonitor.removeMonitor( id )
+
+ def desktopEntryFileChangedCallback (self):
+ GLib.timeout_add(200, self.onDesktopEntryFileChanged)
+
+ def onDesktopEntryFileChanged( self ):
+ exists = False
+ base = os.path.basename( self.desktopFile )
+ for dir in self.appDirs:
+ if os.path.exists( os.path.join( dir, base ) ):
+ # print(os.path.join( dir, base ), self.desktopFile)
+ self.loadDesktopEntry( xdg.DesktopEntry.DesktopEntry( os.path.join( dir, base ) ) )
+ for child in self.labelBox:
+ child.destroy()
+
+ self.iconName = self.appIconName
+
+ self.setupLabels()
+ self.iconChanged()
+ exists = True
+ break
+
+ if not exists:
+ # FIXME: What to do in this case?
+ self.destroy()
+ return False
+
+class MenuApplicationLauncher( ApplicationLauncher ):
+
+ def __init__( self, desktopFile, iconSize, category, showComment, highlight=False ):
+
+ self.showComment = showComment
+ self.appCategory = category
+ self.highlight = highlight
+
+ ApplicationLauncher.__init__( self, desktopFile, iconSize )
+
+
+ def filterCategory( self, category ):
+ if self.appCategory == category or category == "":
+ self.show()
+ else:
+ self.hide()
+
+ def setupLabels( self ):
+ appName = self.appName
+ appComment = self.appComment
+ if self.highlight:
+ try:
+ #color = self.labelBox.get_style_context().get_color( Gtk.StateFlags.SELECTED ).to_string()
+ #if len(color) > 0 and color[0] == "#":
+ #appName = "%s" % (color, appName);
+ #appComment = "%s" % (color, appComment);
+ #appName = "%s" % (appName);
+ #appComment = "%s" % (appComment);
+ #else:
+ #appName = "%s" % (appName);
+ #appComment = "%s" % (appComment);
+ appName = "%s" % (appName);
+ appComment = "%s" % (appComment);
+ except Exception as detail:
+ print(detail)
+ pass
+
+ if self.showComment and self.appComment != "":
+ if self.iconSize <= 2:
+ self.addLabel( '%s' % appName)
+ self.addLabel( '%s' % appComment)
+ else:
+ self.addLabel( appName )
+ self.addLabel( '%s' % appComment)
+ else:
+ self.addLabel( appName )
+
+ def execute( self, *args ):
+ self.highlight = False
+ for child in self.labelBox:
+ child.destroy()
+ self.setupLabels()
+ return super(MenuApplicationLauncher, self).execute(*args)
+
+ def setShowComment( self, showComment ):
+ self.showComment = showComment
+ for child in self.labelBox:
+ child.destroy()
+ self.setupLabels()
+
+class FavApplicationLauncher( ApplicationLauncher ):
+
+ def __init__( self, desktopFile, iconSize, swapGeneric = False ):
+
+ self.swapGeneric = swapGeneric
+
+ ApplicationLauncher.__init__( self, desktopFile, iconSize )
+
+ def setupLabels( self ):
+ if self.appGenericName:
+ if self.swapGeneric:
+ self.addLabel( '%s' % self.appName )
+ self.addLabel( self.appGenericName )
+ else:
+ self.addLabel( '%s' % self.appGenericName )
+ self.addLabel( self.appName )
+ else:
+ self.addLabel( '%s' % self.appName )
+ if self.appComment != "":
+ self.addLabel( self.appComment )
+ else:
+ self.addLabel ( "" )
+
+ def setSwapGeneric( self, swapGeneric ):
+ self.swapGeneric = swapGeneric
+ for child in self.labelBox:
+ child.destroy()
+
+ self.setupLabels()
+
+
+class CategoryButton( easyButton ):
+
+ def __init__( self, iconName, iconSize, labels , f ):
+ easyButton.__init__( self, iconName, iconSize, labels )
+ self.filter = f
+ self.set_focus_on_click(False)
+
+
+iconManager = IconManager()
diff --git a/src/Pytop/widgets/icon_manager/execute.py b/src/Pytop/widgets/icon_manager/execute.py
new file mode 100644
index 0000000..d39c900
--- /dev/null
+++ b/src/Pytop/widgets/icon_manager/execute.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2007-2014 Clement Lefebvre
+# Copyright (C) 2015 Martin Wimpress
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import os
+import shlex
+import subprocess
+
+def RemoveArgs(Execline):
+ NewExecline = []
+ Specials=["\"%c\"", "%f","%F","%u","%U","%d","%D","%n","%N","%i","%c","%k","%v","%m","%M", "-caption", "/bin/sh", "sh", "-c", "STARTED_FROM_MENU=yes"]
+ for elem in Execline:
+ elem = elem.replace("'","")
+ elem = elem.replace("\"", "")
+ if elem not in Specials:
+ print(elem)
+ NewExecline.append(elem)
+ return NewExecline
+
+# Actually execute the command
+def ExecuteCommand(cmd , commandCwd=None):
+ if not commandCwd:
+ cwd = os.path.expanduser( "~" );
+ else:
+ tmpCwd = os.path.expanduser( commandCwd );
+ if (os.path.exists(tmpCwd)):
+ cwd = tmpCwd
+
+ if isinstance( cmd, str ):
+ if (cmd.find("/home/") >= 0) or (cmd.find("xdg-su") >= 0) or (cmd.find("\"") >= 0):
+ print("running manually...")
+ try:
+ os.chdir(cwd)
+ subprocess.Popen(shlex.split(cmd))
+ return True
+ except Exception as detail:
+ print(detail)
+ return False
+ cmd = cmd.split()
+ cmd = RemoveArgs(cmd)
+
+ try:
+ os.chdir( cwd )
+ string = ' '.join(cmd)
+ subprocess.Popen(shlex.split(string))
+ return True
+ except Exception as detail:
+ print(detail)
+ return False
+
+# Execute cmd using the double fork method
+def Execute(cmd, commandCwd=None):
+ child_pid = os.fork()
+ if child_pid == 0:
+ ExecuteCommand(cmd, commandCwd)
+ os._exit(0)
+ else:
+ os.wait()
diff --git a/src/Pytop/widgets/icon_manager/filemonitor.py b/src/Pytop/widgets/icon_manager/filemonitor.py
new file mode 100644
index 0000000..6ac5919
--- /dev/null
+++ b/src/Pytop/widgets/icon_manager/filemonitor.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2007-2014 Clement Lefebvre
+# Copyright (C) 2015 Martin Wimpress
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import os
+import os.path
+import threading
+import time
+from gi.repository import GLib
+
+try:
+ import pyinotify
+ hasInotify = True
+except ImportError:
+ hasInotify = False
+
+if hasInotify:
+ class FileMonitor(object):
+ def __init__( self ):
+ self.monitorId = 0
+ self.wm = pyinotify.WatchManager()
+ self.wdds = {}
+ self.callbacks = {}
+ self.notifier = pyinotify.ThreadedNotifier(self.wm, self.fileChanged)
+ self.notifier.setDaemon( True )
+ self.notifier.start()
+
+
+ def addMonitor( self, filename, callback, args = None ):
+ try:
+ mask = pyinotify.IN_DELETE | pyinotify.IN_CREATE | pyinotify.IN_MODIFY
+ mId = self.wm.add_watch( filename, mask, rec = True)[filename]
+ if mId >= 0:
+ self.callbacks[mId] = ( callback, args )
+ except Exception as detail:
+ mId = 0
+ return mId
+
+ def removeMonitor( self, monitorId ):
+ if monitorId in self.callbacks:
+ self.wm.rm_watch( monitorId )
+ del self.callbacks[monitorId]
+
+ def fileChanged(self, event ):
+ if event.wd in self.callbacks:
+ callback = self.callbacks[event.wd]
+ if callback[1]:
+ GLib.idle_add( callback[0], callback[1] )
+ else:
+ GLib.idle_add( callback[0] )
+else:
+
+ class _MonitoredFile( object ):
+ def __init__( self, filename, callback, monitorId, args ):
+ self.filename = filename
+ self.callback = callback
+ self.monitorId = monitorId
+ self.args = args
+ self.exists = os.path.exists( self.filename )
+ if self.exists:
+ self.mtime = os.stat( filename ).st_mtime
+ else:
+ self.mtime = 0
+
+ def hasChanged( self ):
+ if os.path.exists( self.filename ):
+ if not self.exists:
+ self.exists = True
+ self.mtime = os.stat( self.filename ).st_mtime
+ return True
+ else:
+ mtime = os.stat( self.filename ).st_mtime
+ if mtime != self.mtime:
+ self.mtime = mtime
+ return True
+ else:
+ if self.exists:
+ self.exists = False
+ return True
+
+ return False
+
+ class MonitorThread(threading.Thread):
+ def __init__(self, monitor):
+ threading.Thread.__init__ ( self )
+ self.monitor = monitor
+
+ def run(self):
+ while(1):
+ self.monitor.checkFiles()
+ time.sleep(1)
+
+ class FileMonitor(object):
+ def __init__( self ):
+ self.monitorId = 0
+ self.monitoredFiles = []
+ self.monitorThread = MonitorThread( self )
+ self.monitorThread.setDaemon( True )
+ self.monitorThread.start()
+
+ def addMonitor( self, filename, callback, args = None ):
+ self.monitorId += 1
+ self.monitoredFiles.append( _MonitoredFile( filename, callback, self.monitorId, args ) )
+ return self.monitorId
+
+ def removeMonitor( self, monitorId ):
+ for monitored in self.monitoredFiles:
+ if monitorId == monitored.monitorId:
+ self.monitoredFiles.remove( monitored )
+ break
+
+ def checkFiles( self ):
+ for monitored in self.monitoredFiles:
+ if monitored.hasChanged():
+ if monitored.args:
+ GLib.idle_add( monitored.callback, monitored.args )
+ else:
+ GLib.idle_add( monitored.callback )
+
+monitor = FileMonitor()