Viewing file: DesktopEntry.py (15.59 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
""" Complete implementation of the XDG Desktop Entry Specification Version 1.0 http://standards.freedesktop.org/desktop-entry-spec/
Not supported: - Encoding: Legacy Mixed - Does not check exec parameters - Does not check URL's - Does not completly validate deprecated/kde items - Does not completly check categories """
from xdg.IniFile import * from xdg.BaseDirectory import * import os.path
class DesktopEntry(IniFile): "Class to parse and validate DesktopEntries"
defaultGroup = 'Desktop Entry'
def __init__(self, filename=None): self.content = dict() if filename and os.path.exists(filename): self.parse(filename) elif filename: self.new(filename)
def __str__(self): return self.getName()
def parse(self, file): IniFile.parse(self, file, ["Desktop Entry", "KDE Desktop Entry"])
# start standard keys def getType(self): return self.get('Type') """ @deprecated, use getVersionString instead """ def getVersion(self): return self.get('Version', type="numeric") def getVersionString(self): return self.get('Version') def getName(self): return self.get('Name', locale=True) def getGenericName(self): return self.get('GenericName', locale=True) def getNoDisplay(self): return self.get('NoDisplay', type="boolean") def getComment(self): return self.get('Comment', locale=True) def getIcon(self): return self.get('Icon', locale=True) def getHidden(self): return self.get('Hidden', type="boolean") def getOnlyShowIn(self): return self.get('OnlyShowIn', list=True) def getNotShowIn(self): return self.get('NotShowIn', list=True) def getTryExec(self): return self.get('TryExec') def getExec(self): return self.get('Exec') def getPath(self): return self.get('Path') def getTerminal(self): return self.get('Terminal', type="boolean") """ @deprecated, use getMimeTypes instead """ def getMimeType(self): return self.get('MimeType', list=True, type="regex") def getMimeTypes(self): return self.get('MimeType', list=True) def getCategories(self): return self.get('Categories', list=True) def getStartupNotify(self): return self.get('StartupNotify', type="boolean") def getStartupWMClass(self): return self.get('StartupWMClass') def getURL(self): return self.get('URL') # end standard keys
# start kde keys def getServiceTypes(self): return self.get('ServiceTypes', list=True) def getDocPath(self): return self.get('DocPath') def getKeywords(self): return self.get('Keywords', list=True, locale=True) def getInitialPreference(self): return self.get('InitialPreference') def getDev(self): return self.get('Dev') def getFSType(self): return self.get('FSType') def getMountPoint(self): return self.get('MountPoint') def getReadonly(self): return self.get('ReadOnly', type="boolean") def getUnmountIcon(self): return self.get('UnmountIcon', locale=True) # end kde keys
# start deprecated keys def getMiniIcon(self): return self.get('MiniIcon', locale=True) def getTerminalOptions(self): return self.get('TerminalOptions') def getDefaultApp(self): return self.get('DefaultApp') def getProtocols(self): return self.get('Protocols', list=True) def getExtensions(self): return self.get('Extensions', list=True) def getBinaryPattern(self): return self.get('BinaryPattern') def getMapNotify(self): return self.get('MapNotify') def getEncoding(self): return self.get('Encoding') def getSwallowTitle(self): return self.get('SwallowTitle', locale=True) def getSwallowExec(self): return self.get('SwallowExec') def getSortOrder(self): return self.get('SortOrder', list=True) def getFilePattern(self): return self.get('FilePattern', type="regex") def getActions(self): return self.get('Actions', list=True) # end deprecated keys
# desktop entry edit stuff def new(self, filename): if os.path.splitext(filename)[1] == ".desktop": type = "Application" elif os.path.splitext(filename)[1] == ".directory": type = "Directory" else: raise ParsingError("Unknown extension", filename)
self.content = dict() self.addGroup(self.defaultGroup) self.set("Type", type) self.filename = filename # end desktop entry edit stuff
# validation stuff def checkExtras(self): # header if self.defaultGroup == "KDE Desktop Entry": self.warnings.append('[KDE Desktop Entry]-Header is deprecated')
# file extension if self.fileExtension == ".kdelnk": self.warnings.append("File extension .kdelnk is deprecated") elif self.fileExtension != ".desktop" and self.fileExtension != ".directory": self.warnings.append('Unknown File extension')
# Type try: self.type = self.content[self.defaultGroup]["Type"] except KeyError: self.errors.append("Key 'Type' is missing")
# Name try: self.name = self.content[self.defaultGroup]["Name"] except KeyError: self.errors.append("Key 'Name' is missing")
def checkGroup(self, group): # check if group header is valid if not (group == self.defaultGroup \ or re.match("^\Desktop Action [a-zA-Z]+\$", group) \ or (re.match("^\X-", group) and group.decode("utf-8", "ignore").encode("ascii", 'ignore') == group)): self.errors.append("Invalid Group name: %s" % group) else: #OnlyShowIn and NotShowIn if self.content[group].has_key("OnlyShowIn") and self.content[group].has_key("NotShowIn"): self.errors.append("Group may either have OnlyShowIn or NotShowIn, but not both")
def checkKey(self, key, value, group): # standard keys if key == "Type": if value == "ServiceType" or value == "Service" or value == "FSDevice": self.warnings.append("Type=%s is a KDE extension" % key) elif value == "MimeType": self.warnings.append("Type=MimeType is deprecated") elif not (value == "Application" or value == "Link" or value == "Directory"): self.errors.append("Value of key 'Type' must be Application, Link or Directory, but is '%s'" % value)
if self.fileExtension == ".directory" and not value == "Directory": self.warnings.append("File extension is .directory, but Type is '%s'" % value) elif self.fileExtension == ".desktop" and value == "Directory": self.warnings.append("Files with Type=Directory should have the extension .directory")
if value == "Application": if not self.content[group].has_key("Exec"): self.warnings.append("Type=Application needs 'Exec' key") if value == "Link": if not self.content[group].has_key("URL"): self.warnings.append("Type=Application needs 'Exec' key")
elif key == "Version": self.checkValue(key, value)
elif re.match("^Name"+xdg.Locale.regex+"$", key): pass # locale string
elif re.match("^GenericName"+xdg.Locale.regex+"$", key): pass # locale string
elif key == "NoDisplay": self.checkValue(key, value, type="boolean")
elif re.match("^Comment"+xdg.Locale.regex+"$", key): pass # locale string
elif re.match("^Icon"+xdg.Locale.regex+"$", key): self.checkValue(key, value)
elif key == "Hidden": self.checkValue(key, value, type="boolean")
elif key == "OnlyShowIn": self.checkValue(key, value, list=True) self.checkOnlyShowIn(value)
elif key == "NotShowIn": self.checkValue(key, value, list=True) self.checkOnlyShowIn(value)
elif key == "TryExec": self.checkValue(key, value) self.checkType(key, "Application")
elif key == "Exec": self.checkValue(key, value) self.checkType(key, "Application")
elif key == "Path": self.checkValue(key, value) self.checkType(key, "Application")
elif key == "Terminal": self.checkValue(key, value, type="boolean") self.checkType(key, "Application")
elif key == "MimeType": self.checkValue(key, value, list=True) self.checkType(key, "Application")
elif key == "Categories": self.checkValue(key, value) self.checkType(key, "Application") self.checkCategorie(value)
elif key == "StartupNotify": self.checkValue(key, value, type="boolean") self.checkType(key, "Application")
elif key == "StartupWMClass": self.checkType(key, "Application")
elif key == "URL": self.checkValue(key, value) self.checkType(key, "URL")
# kde extensions elif key == "ServiceTypes": self.checkValue(key, value, list=True) self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "DocPath": self.checkValue(key, value) self.warnings.append("Key '%s' is a KDE extension" % key)
elif re.match("^Keywords"+xdg.Locale.regex+"$", key): self.checkValue(key, value, list=True) self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "InitialPreference": self.checkValue(key, value, type="numeric") self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "Dev": self.checkValue(key, value) self.checkType(key, "FSDevice") self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "FSType": self.checkValue(key, value) self.checkType(key, "FSDevice") self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "MountPoint": self.checkValue(key, value) self.checkType(key, "FSDevice") self.warnings.append("Key '%s' is a KDE extension" % key)
elif key == "ReadOnly": self.checkValue(key, value, type="boolean") self.checkType(key, "FSDevice") self.warnings.append("Key '%s' is a KDE extension" % key)
elif re.match("^UnmountIcon"+xdg.Locale.regex+"$", key): self.checkValue(key, value) self.checkType(key, "FSDevice") self.warnings.append("Key '%s' is a KDE extension" % key)
# deprecated keys elif key == "Encoding": self.checkValue(key, value) self.warnings.append("Key '%s' is deprecated" % key)
elif re.match("^MiniIcon"+xdg.Locale.regex+"$", key): self.checkValue(key, value) self.warnings.append("Key '%s' is deprecated" % key)
elif key == "TerminalOptions": self.checkValue(key, value) self.warnings.append("Key '%s' is deprecated" % key)
elif key == "DefaultApp": self.checkValue(key, value) self.warnings.append("Key '%s' is deprecated" % key)
elif key == "Protocols": self.checkValue(key, value, list=True) self.warnings.append("Key '%s' is deprecated" % key)
elif key == "Extensions": self.checkValue(key, value, list=True) self.warnings.append("Key '%s' is deprecated" % key)
elif key == "BinaryPattern": self.checkValue(key, value) self.warnings.append("Key '%s' is deprecated" % key)
elif key == "MapNotify": self.checkValue(key, value) self.warnings.append("Key '%s' is deprecated" % key)
elif re.match("^SwallowTitle"+xdg.Locale.regex+"$", key): self.warnings.append("Key '%s' is deprecated" % key)
elif key == "SwallowExec": self.checkValue(key, value) self.warnings.append("Key '%s' is deprecated" % key)
elif key == "FilePattern": self.checkValue(key, value, type="regex", list=True) self.warnings.append("Key '%s' is deprecated" % key)
elif key == "SortOrder": self.checkValue(key, value, list=True) self.warnings.append("Key '%s' is deprecated" % key)
elif key == "Actions": self.checkValue(key, value, list=True) self.warnings.append("Key '%s' is deprecated" % key)
# "X-" extensions elif re.match("^X-[a-zA-Z0-9-]+", key): pass
else: self.errors.append("Invalid key: %s" % key)
def checkType(self, key, type): if not self.getType() == type: self.errors.append("Key '%s' only allowed in Type=%s" % (key, type))
def checkOnlyShowIn(self, value): values = self.getList(value) valid = ["GNOME", "KDE", "ROX", "XFCE", "Old", "LXDE"] for item in values: if item not in valid and item[0:2] != "X-": self.errors.append("'%s' is not a registered OnlyShowIn value" % item);
def checkCategorie(self, value): values = self.getList(value)
main = ["AudioVideo", "Audio", "Video", "Development", "Education", "Game", "Graphics", "Network", "Office", "Settings", "System", "Utility"] hasmain = False for item in values: if item in main: hasmain = True if hasmain == False: self.errors.append("Missing main category")
additional = ["Building", "Debugger", "IDE", "GUIDesigner", "Profiling", "RevisionControl", "Translation", "Calendar", "ContactManagement", "Database", "Dictionary", "Chart", "Email", "Finance", "FlowChart", "PDA", "ProjectManagement", "Presentation", "Spreadsheet", "WordProcessor", "2DGraphics", "VectorGraphics", "3DGraphics", "RasterGraphics", "Scanning", "OCR", "Photography", "Publishing", "Viewer", "TextTools", "DesktopSettings", "HardwareSettings", "Printing", "PackageManager", "Dialup", "InstantMessaging", "Chat", "IRCClient", "FileTransfer", "HamRadio", "News", "P2P", "RemoteAccess", "Telephony", "TelephonyTools", "VideoConference", "WebBrowser", "WebDevelopment", "Midi", "Mixer", "Sequencer", "Tuner", "TV", "AudioVideoEditing", "Player", "Recorder", "DiscBurning", "ActionGame", "AdventureGame", "ArcadeGame", "BoardGame", "BlocksGame", "CardGame", "KidsGame", "LogicGame", "RolePlaying", "Simulation", "SportsGame", "StrategyGame", "Art", "Construction", "Music", "Languages", "Science", "ArtificialIntelligence", "Astronomy", "Biology", "Chemistry", "ComputerScience", "DataVisualization", "Economy", "Electricity", "Geography", "Geology", "Geoscience", "History", "ImageProcessing", "Literature", "Math", "NumericalAnalysis", "MedicalSoftware", "Physics", "Robotics", "Sports", "ParallelComputing", "Amusement", "Archiving", "Compression", "Electronics", "Emulator", "Engineering", "FileTools", "FileManager", "TerminalEmulator", "Filesystem", "Monitor", "Security", "Accessibility", "Calculator", "Clock", "TextEditor", "Documentation", "Core", "KDE", "GNOME", "GTK", "Qt", "Motif", "Java", "ConsoleOnly", "Screensaver", "TrayIcon", "Applet", "Shell"] for item in values: if item not in additional + main and item[0:2] != "X-": self.errors.append("'%s' is not a registered Category" % item);
|