################################################################################ # # This file is part of Gato (Graph Animation Toolbox) # # file: GatoFile.py # author: Achim Gaedke (achim.gaedke@zpr.uni-koeln.de) # # Copyright (C) 1998-2005, Alexander Schliep, Winfried Hochstaettler and # Copyright 1998-2001 ZAIK/ZPR, Universitaet zu Koeln # # Contact: schliep@molgen.mpg.de, wh@zpr.uni-koeln.de # Information: http://gato.sf.net # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # This file is version $Revision: 1.7 $ # from $Date: 2005/02/22 11:12:51 $ # last change by $Author: schliep $. # ################################################################################ import os import os.path import shutil import sys import string import re import Tkinter import tkSimpleDialog import tkFileDialog import traceback try: import _winreg except ImportError: # we are not on a windows system pass gatoMimeType="application/gato" gatoFileExtension="gato" gatoDescription="gato graph/algorithm tool" class ConfigurationException(Exception): """ to report configuration errors """ def __init__(self,message): Exception.__init__(self) self.message=message def __repr__(self): if hasattr(self,"message"): return "%s: %s"%(self.__class__.__name__,self.message) else: return self.__class__.__name__ class configureOS: """ system configuration for gato file type support """ def __init__(self,DialogMaster=None): """ creates configurator object """ # find script location self.myExecutable=os.path.abspath(sys.argv[0]) self.DialogMaster=DialogMaster def check(self): """ check, if already configured, returns true, if this executable is installed """ raise ConfigurationException("base class method should not be called") def askUserInstall(self): """ ask user, if she/he likes configuration """ raise ConfigurationException("base class method should not be called") def askUserUninstall(self): """ ask user, if she/he likes uninstallation """ raise ConfigurationException("base class method should not be called") def configureSystem(self): """ do system configuration """ raise ConfigurationException("base class method should not be called") def unconfigureSystem(self): """ do system unconfiguration """ raise ConfigurationException("base class method should not be called") def runInstall(self): if not self.check() and self.askUserInstall(): self.configureSystem() def runUninstall(self): if self.check() and self.askUserUninstall(): self.unconfigureSystem() def installBinary(self,path): """ installs this binary to another place """ # test, if it is our place... if not os.path.exists(path): os.makedirs(path) newLocation=os.path.join(path,os.path.basename(self.myExecutable)) if os.path.exists(newLocation): os.remove(newLocation) shutil.copy2(self.myExecutable,newLocation) self.myExecutable=os.path.abspath(newLocation) class configureUnsupported(configureOS): """ some tips and exit """ check=configureSystem=askUserInstall=lambda x:None def runInstall(self): """ """ print "unsupported operating system %s"%sys.platform class configureUNIX(configureOS): """ linux configuration expands mailcap file """ def __init__(self,DialogMaster=None): """ find script location, configuration file location... """ configureOS.__init__(self,DialogMaster) # find mailcap file if not os.environ.has_key("HOME"): raise ConfigurationException("could not determine home directory") self.mailcapFile=os.path.join(os.environ["HOME"],".mailcap") self.mime_typesFile=os.path.join(os.environ["HOME"],".mime.types") def check(self): """ """ self.cache_check_mailcap=self.check_mailcap() self.cache_check_mime_types=self.check_mime_types() return self.cache_check_mailcap and self.cache_check_mime_types def check_mailcap(self): """ """ if not os.access(self.mailcapFile,os.R_OK): return 0 mailcap=file(self.mailcapFile,"r") while 1: line=mailcap.readline() # line end if not line: break # skip comments if line[0]=="#": continue line=line.strip() while line[-1]=="\\": line=line[:-1]+mailcap.readline().rstrip() entries=line.split(";") (mimeType,viewCommand)=map(string.strip,entries[:2]) if (mimeType==gatoMimeType and os.path.exists(viewCommand.split(" ")[0]) and os.path.samefile(viewCommand.split(" ")[0],self.myExecutable)): return 1 return 0 def check_mime_types(self): """ """ if not os.access(self.mime_typesFile,os.R_OK): return 0 mime_types=file(self.mime_typesFile,"r") while 1: line=mime_types.readline() # line end if not line: break # skip comments if line[0]=="#": continue #catenate lines line=line.strip() while line[-1]=="\\": newLine=mime_types.readline() if newLine=="": break line=line[:-1]+newLine.rstrip() mime_dict={} # format: key=value or key="value" kv_pair=re.compile("(\w+)=((?:\"[^\"]*\")|(?:[^\"\s]*))") kv_pairs=kv_pair.findall(line) for kv in kv_pairs: (key,value)=kv if value[0]=="\"": value=value[1:-1] mime_dict[key]=value if mime_dict.get("type")=="application/gato" and mime_dict.get("exts")=="gato": return 1 return 0 class askUserInstallDialog(tkSimpleDialog.Dialog): """ dialog for system file manipulation of linux systems """ def __init__(self,master,title=None): if not title: title="System Configuration" tkSimpleDialog.Dialog.__init__(self,master,title) def body(self, master): """ """ row=0 # install prefix question Tkinter.Label(master, text="Install Gato to another place?").grid(row=row,column=0) self.installQ = Tkinter.IntVar() self.installQ.set(1) Tkinter.Radiobutton(master, text="Yes", variable=self.installQ, value=1, command=self.enablePathEntry).grid(row=row,column=1) Tkinter.Radiobutton(master, text="No", variable=self.installQ, value=2, command=self.disablePathEntry).grid(row=row,column=2) # install location row+=1 self.installP=Tkinter.Entry(master) self.installP.insert(0,os.path.expanduser("~/bin")) self.installP.rowLocation=row self.installP.colLocation=0 self.installP.grid(row=row,column=0,sticky=Tkinter.EW) self.searchP=Tkinter.Button(master,text="search...", command=self.askInstallPrefix, pady=0) self.searchP.grid(row=row,column=1,columnspan=2) # mime type reg? row+=1 Tkinter.Label(master, text="add gato mime type").grid(row=row,column=0) self.mimeQ = Tkinter.IntVar() self.mimeQ.set(1) Tkinter.Radiobutton(master, text="Yes", variable=self.mimeQ, value=1).grid(row=row,column=1) Tkinter.Radiobutton(master, text="No", variable=self.mimeQ, value=2).grid(row=row,column=2) # mime type reg? row+=1 Tkinter.Label(master, text="add gato to .mailcap").grid(row=row,column=0) self.mailcapQ = Tkinter.IntVar() self.mailcapQ.set(1) Tkinter.Radiobutton(master, text="Yes", variable=self.mailcapQ, value=1).grid(row=row,column=1) Tkinter.Radiobutton(master, text="No", variable=self.mailcapQ, value=2).grid(row=row,column=2) def enablePathEntry(self): self.installP.grid(row=self.installP.rowLocation,column=self.installP.colLocation,sticky=Tkinter.EW) self.searchP["state"]=Tkinter.NORMAL def disablePathEntry(self): self.installP.grid_forget() self.searchP["state"]=Tkinter.DISABLED def askInstallPrefix(self): """ command for search button """ initDir=self.installP.get() newPrefix="" if os.path.exists(initDir): newPrefix=tkFileDialog.askdirectory(parent=self,initialdir=initDir,mustexist=1) else: newPrefix=tkFileDialog.askdirectory(parent=self,mustexist=1) if newPrefix: self.installP.delete(0, Tkinter.END) self.installP.insert(0, newPrefix) def apply(self): """ return result as dictionary """ self.result={} if (hasattr(self,"installP") and hasattr(self,"installQ") and (self.installQ.get()==1)): self.result["installTo"]=self.installP.get() else: self.result["installTo"]=None if hasattr(self,"mimeQ"): self.result["mimeQ"]=self.mimeQ.get()==1 if hasattr(self,"mailcapQ"): self.result["mailcapQ"]=self.mailcapQ.get()==1 def askUserInstall(self): """ """ self.askedUser=self.askUserInstallDialog(self.DialogMaster).result return self.askedUser class askUserUninstallDialog(tkSimpleDialog.Dialog): """ dialog for system file manipulation of linux systems """ def __init__(self,master,title=None): if not title: title="System Configuration" tkSimpleDialog.Dialog.__init__(self,master,title) def body(self, master): """ Are you sure...? Dialog... """ row=0 # install prefix question Tkinter.Label(master, text="Install Gato from\n%s ?"%self.myExecutable ).grid(row=row,column=0) def apply(self): """ return result as dictionary """ self.result={} def askUserUninstall(self): self.askedUser=self.askUserUnnstallDialog(self.DialogMaster).result return self.askedUser def configureSystem(self): """ """ if self.askedUser.get("installTo",0): self.installBinary(self.askedUser["installTo"]) if not self.cache_check_mailcap and self.askedUser.get("mailcapQ",0): self.configure_mailcap() if not self.cache_check_mime_types and self.askedUser.get("mimeQ",0): self.configure_mime_types() def configure_mailcap(self): """ append my mime type to .mailcap """ savedLines="" # if this file exists if os.access(self.mailcapFile,os.R_OK): mailcap=file(self.mailcapFile,"r") while 1: line=mailcap.readline() savedLine=line[:] # line end if not line: break # skip comments if line[0]=="#": savedLines+=line continue line=line.strip() continuedLines=[] while line[-1]=="\\": continuedLines.append(savedLine) savedLine=mailcap.readline() line=line[:-1]+savedLine.rstrip() entries=line.split(";") (mimeType,viewCommand)=map(string.strip,entries[:2]) # skip my old entry if mimeType==gatoMimeType: continue savedLines+=string.join(continuedLines,'')+savedLine mailcap.close() # open for write access mailcap=file(self.mailcapFile,"w") mailcap.write(savedLines) mailcap.write("%s;%s \"%%s\"\n"%(gatoMimeType,self.myExecutable)) mailcap.close() def configure_mime_types(self): """ append my information to .mime.types """ savedLines="" # if this file exists if os.access(self.mime_typesFile,os.R_OK): mime_types=file(self.mime_typesFile,"r") while 1: line=mime_types.readline() savedLine=line[:] # line end if not line: break # skip comments if line[0]=="#": savedLines+=line continue line=line.strip() continuedLines=[] while line[-1]=="\\": continuedLines.append(savedLine) savedLine=mime_types.readline() line=line[:-1]+savedLine.rstrip() entries=line.split(";") mime_dict={} # format: key=value or key="value" kv_pair=re.compile("(\w+)=((?:\"[^\"]*\")|(?:[^\"\s]*))") kv_pairs=kv_pair.findall(line) for kv in kv_pairs: (key,value)=kv if value[0]=="\"": value=value[1:-1] mime_dict[key]=value if (mime_dict.get("type")==gatoMimeType and mime_dict.get("exts")==gatoFileExtension): continue savedLines+=string.join(continuedLines,'')+savedLine mime_types.close() else: # fake Netscape MIME file savedLines+="#--Netscape Communications Corporation MIME Information\n" # open for write access mime_types=file(self.mime_typesFile,"w") mime_types.write(savedLines) mime_types.write("# inserted by gato SystemConfiguration Module\n") mime_types.write("type=%s \\\ndesc=\"%s\" \\\nexts=\"%s\"\n"% (gatoMimeType,gatoDescription,gatoFileExtension)) mime_types.close() class configureLinux(configureUNIX): """ """ def __init__(self, DialogMaster=None): if sys.platform[:5]!='linux': raise ConfigurationException("tried to instantiate %s on %s"% (self.__class__.__name__,sys.platform)) configureUNIX.__init__(self,DialogMaster) class configureSUNOS(configureUNIX): """ """ def __init__(self, DialogMaster=None): if sys.platform[:5]!='sunos': raise ConfigurationException("tried to instantiate %s on %s"% (self.__class__.__name__,sys.platform)) configureUNIX.__init__(self,DialogMaster) class configureWindows(configureOS): """ Configuration module for windows contaminates the windows registry with our extension, program and mime type """ def __init__(self,DialogMaster=None): """ find script location... """ configureOS.__init__(self,DialogMaster) def check(self): """ """ self.ClassesSection=self.findWritableClassesSection() return 0 class askUserInstallDialog(tkSimpleDialog.Dialog): """ """ def __init__(self,master,title=None): if not title: title="System Configuration" tkSimpleDialog.Dialog.__init__(self,master,title) def body(self, master): """ dialog body """ row=0 # install question Tkinter.Label(master, text="Install Gato.exe to another place?").grid(row=row,column=0) self.installQ = Tkinter.IntVar() self.installQ.set(1) Tkinter.Radiobutton(master, text="Yes", variable=self.installQ, value=1, command=self.enablePathEntry).grid(row=row,column=1) Tkinter.Radiobutton(master, text="No", variable=self.installQ, value=2, command=self.disablePathEntry).grid(row=row,column=2) # install location row+=1 self.installP=Tkinter.Entry(master) self.installP.insert(0,"C:\\Gato\\") self.installP.rowLocation=row self.installP.colLocation=0 self.installP.grid(row=row,column=0,sticky=Tkinter.EW) self.searchP=Tkinter.Button(master,text="search...",command=self.askInstallPrefix, pady=0) self.searchP.grid(row=row,column=1,columnspan=2) # extension question row+=1 self.extensionQ = Tkinter.IntVar() self.extensionQ.set(1) Tkinter.Label(master, text="Create bindings to .%s file extensions:"%gatoFileExtension).grid(row=row) Tkinter.Radiobutton(master, text="Yes", variable=self.extensionQ, value=1).grid(row=row,column=1) Tkinter.Radiobutton(master, text="No", variable=self.extensionQ, value=2).grid(row=row,column=2) # MIME type question row+=1 self.mimeQ = Tkinter.IntVar() self.mimeQ.set(1) Tkinter.Label(master, text="Register MIME type %s:"%gatoMimeType).grid(row=row) Tkinter.Radiobutton(master, text="Yes", variable=self.mimeQ, value=1).grid(row=row,column=1) Tkinter.Radiobutton(master, text="No", variable=self.mimeQ, value=2).grid(row=row,column=2) def apply(self): """ return result as dictionary """ self.result={} if (hasattr(self,"installP") and hasattr(self,"installQ") and (self.installQ.get()==1)): self.result["installTo"]=self.installP.get() else: self.result["installTo"]=None if hasattr(self,"mimeQ"): self.result["mimeQ"]=self.mimeQ.get()==1 if hasattr(self,"extensionsQ"): self.result["extensionsQ"]=self.extensionsQ.get()==1 def enablePathEntry(self): self.installP.grid(row=self.installP.rowLocation,column=self.installP.colLocation,sticky=Tkinter.EW) self.searchP["state"]=Tkinter.NORMAL def disablePathEntry(self): self.installP.grid_forget() self.searchP["state"]=Tkinter.DISABLED def askInstallPrefix(self): """ command for search button """ initDir=self.installP.get() newPrefix="" if os.path.exists(initDir): newPrefix=tkFileDialog.askdirectory(parent=self,initialdir=initDir,mustexist=1) else: newPrefix=tkFileDialog.askdirectory(parent=self,mustexist=1) if newPrefix: self.installP.delete(0, Tkinter.END) self.installP.insert(0, newPrefix) def askUserInstall(self): """ """ self.askedUser=self.askUserInstallDialog(self.DialogMaster).result return self.askedUser def configureSystem(self): """ do the system configuration """ if self.askedUser.get("installTo",0): self.installBinary(self.askedUser["installTo"]) if self.askedUser.get("extensionQ",0) or self.askedUser.get("mimeQ",0): self.insertGatoEntries(self.ClassesSection) def printGatoEntries(self): """ start reading... """ reader=_winreg.ConnectRegistry(None,_winreg.HKEY_CLASSES_ROOT) # get Gato.File section GatoFileHandle=None try: GatoFileHandle=_winreg.OpenKey(reader,"Gato.File") except WindowsError: print "Could not find Gato.File section" else: print "found Gato.File section:" self.printSubRegistry(GatoFileHandle) # get Gato FileExtension Section GatoExtensionHandle=None try: GatoExtensionHandle=_winreg.OpenKey(reader,"."+gatoFileExtension) except WindowsError: print "could not find the file extension .%s"%gatoFileExtension else: print "found gato's extension" self.printSubRegistry(GatoExtensionHandle) # get Gato mime Type section GatoMimeHandleGatoExt=None try: GatoMimeHandle=_winreg.OpenKey(reader,"MIME") GatoMimeHandleDatabase=_winreg.OpenKey(GatoMimeHandle, "Database") GatoMimeHandleContentType=_winreg.OpenKey(GatoMimeHandleDatabase, "Content Type") GatoMimeHandleGatoExt=_winreg.OpenKey(GatoMimeHandleContentType, gatoMimeType) except WindowsError: print "could not find mime type: %s"%gatoMimeType else: print "found %s mime type"%gatoMimeType self.printSubRegistry(GatoMimeHandleGatoExt) def printSubRegistry(self,key,indent=""): """ print all information of a subkey """ subkeyNo,valueNo,lastMod=_winreg.QueryInfoKey(key) for i in range(valueNo): print indent,_winreg.EnumValue(key, i) for i in range(subkeyNo): subkeyName=_winreg.EnumKey(key,i) subkey=_winreg.OpenKey(key,subkeyName) print indent,subkeyName self.printSubRegistry(subkey,indent+" ") def findWritableClassesSection(self): # first try in HKEY_CLASSES_ROOT try: writer=_winreg.ConnectRegistry(None,_winreg.HKEY_CLASSES_ROOT) GatoFileTestHandle=_winreg.CreateKey(writer,"Gato.File.Test") _winreg.DeleteKey(writer,"Gato.File.Test") return writer except WindowsError: # print "could not access HKEY_CLASSES_ROOT/Gato.File" # self.traceback.print_exc() pass # next try... try: writer=_winreg.ConnectRegistry(None,_winreg.HKEY_CURRENT_USER) SoftwareSection=_winreg.OpenKey(writer,"Software") ClassesSection=_winreg.OpenKey(SoftwareSection,"Classes",0,_winreg.KEY_SET_VALUE) GatoFileTestHandle=_winreg.CreateKey(ClassesSection,"Gato.File.Test") _winreg.DeleteKey(ClassesSection,"Gato.File.Test") return ClassesSection except WindowsError: # print "could not access HKEY_CURRENT_USER/Software/Classes Section" # self.traceback.print_exc() return None def insertGatoEntries(self,ClassesSection): """ updates registry database """ # update Gato.File section try: GatoFileHandle=_winreg.CreateKey(ClassesSection,"Gato.File") _winreg.SetValueEx(GatoFileHandle,"",0,_winreg.REG_SZ,"Gato.File") except WindowsError: print "Could not create/update the Gato.File section" self.traceback.print_exc() # update Gato.File's subsections try: GatoShellHandle=_winreg.CreateKey(GatoFileHandle,"shell") GatoOpenHandle=_winreg.CreateKey(GatoShellHandle,"open") GatoOpenCommandHandle=_winreg.CreateKey(GatoOpenHandle,"command") _winreg.SetValueEx(GatoOpenCommandHandle,"",0,_winreg.REG_SZ,self.myExecutable+' "%1"') except WindowsError: print "could not install open command for gato" self.traceback.print_exc() # update .gato section try: GatoExtensionHandle=_winreg.CreateKey(ClassesSection,"."+gatoFileExtension) _winreg.SetValueEx(GatoExtensionHandle,"",0,_winreg.REG_SZ,"Gato.File") _winreg.SetValueEx(GatoExtensionHandle,"Content Type",0,_winreg.REG_SZ,gatoMimeType) except WindowsError: print "could not create/update FileExtension section" self.traceback.print_exc() # access MIME Database section MimeContentTypeSection=None try: MimeSection=_winreg.CreateKey(ClassesSection,"MIME") MimeDatabaseSection=_winreg.CreateKey(MimeSection,"Database") MimeContentTypeSection=_winreg.CreateKey(MimeDatabaseSection,"Content Type") except WindowsError: print "could not access MIME Content Type database" self.traceback.print_exc() return # update gato's mime type try: GatoMimeContentTypeHandle=_winreg.CreateKey(MimeContentTypeSection,gatoMimeType) _winreg.SetValueEx(GatoMimeContentTypeHandle,"Extension",0, _winreg.REG_SZ,"."+gatoFileExtension) except WindowsError: print "could not update the gato MIME section" self.traceback.print_exc() class GatoInstaller: """ A instance of this class reflects the installation capabilities and status. It should be instantiated only once. """ instanceCounter=0 def __init__(self,disabled=0,menu=None,index=Tkinter.END): """ gets system configurator and checks state """ # assure singleton if GatoInstaller.instanceCounter!=0: raise ConfigurationException("class GatoInstaller should be instantiated once.") GatoInstaller.instanceCounter+=1 self.disabled=disabled self.menu=menu self.index=index # set if no menu item should appear if self.disabled: return self.SysConfig=self.getConfigurator() self.state=self.SysConfig.check() def __del__(self): """ clean up and decrement """ GatoInstaller.instanceCounter-=1 def enable(self): self.disabled=0 self.SysConfig=getConfigurator() self.state=self.SysConfig.check() if self.menu is not None: self.insertMenuEntry() def disable(self): self.disabled=1 self.removeMenuEntry() def addMenuEntry(self,menu,index=Tkinter.END): self.menu=menu self.index=menu.index(index) # make absolute position if self.disabled: return self.insertMenuEntry() def insertMenuEntry(self): if self.menu is None: return self.menu.insert_command(self.index) self.configureMenuEntry() def removeMenuEntry(self): if self.menu is None: return self.menu.delete(self.index) def configureMenuEntry(self): if self.menu is None: return if self.state: self.menu.entryconfig(self.index, command=self.uninstallCommand, label='Uninstall Gato' ) else: self.menu.entryconfig(self.index, command=self.installCommand, label='Install Gato...') def installCommand(self): self.SysConfig.runInstall() self.state=self.SysConfig.check() self.configureMenuEntry() def uninstallCommand(self): print "uninstall" self.state=self.SysConfig.check() self.configureMenuEntry() def getConfigurator(self): if sys.platform[:5]=="linux": return configureLinux() elif sys.platform=="win32": return configureWindows() elif sys.platform[:5]=="sunos": return configureSUNOS() else: return configureUnsupported() if __name__=="__main__": i=GatoInstaller()