From 3436dd09eec8373351de17a413930b8e2c52a244 Mon Sep 17 00:00:00 2001 From: Maxim Stewart Date: Sun, 29 Mar 2020 19:07:06 -0500 Subject: [PATCH] added layout structure --- bin/pytop-0-0-1-x64.deb | Bin 28968 -> 29068 bytes src/Pytop/resources/PyTop.glade | 87 ++- src/Pytop/signal_classes/GridSignals.py | 5 +- src/Pytop/signal_classes/TaskbarSignals.py | 19 +- src/Pytop/widgets/Icon.py | 5 +- src/Pytop/widgets/icon_manager/__init__.py | 3 + src/Pytop/widgets/icon_manager/easybuttons.py | 613 ++++++++++++++++++ src/Pytop/widgets/icon_manager/execute.py | 74 +++ src/Pytop/widgets/icon_manager/filemonitor.py | 136 ++++ 9 files changed, 921 insertions(+), 21 deletions(-) create mode 100644 src/Pytop/widgets/icon_manager/__init__.py create mode 100644 src/Pytop/widgets/icon_manager/easybuttons.py create mode 100644 src/Pytop/widgets/icon_manager/execute.py create mode 100644 src/Pytop/widgets/icon_manager/filemonitor.py diff --git a/bin/pytop-0-0-1-x64.deb b/bin/pytop-0-0-1-x64.deb index 4823e22ebaa863e58b23338a41c30ba40357eefc..fe7e71631d621747ad07d3778ea1cfad40b9b3d0 100644 GIT binary patch delta 27081 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 #)=O!QhgxAnU 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 + + + True @@ -45,6 +48,22 @@ 1 + + + True + False + start + + + + + + False + True + end + 0 + + False @@ -53,23 +72,66 @@ - + True - True - in + False - + True - False + True + in - + True - True - 6 - multiple + False + + + True + True + 6 + multiple + + + + True + True + 0 + + + + + 180 + 64 + True + True + never + in + 225 + + + True + False + + + True + False + vertical + + + + + + + + + + False + True + 1 + @@ -94,10 +156,9 @@ True False - + True False - start @@ -113,7 +174,7 @@ - + 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()