| """File selection dialog classes. | 
 |  | 
 | Classes: | 
 |  | 
 | - FileDialog | 
 | - LoadFileDialog | 
 | - SaveFileDialog | 
 |  | 
 | """ | 
 |  | 
 | from Tkinter import * | 
 | from Dialog import Dialog | 
 |  | 
 | ANCHOR = 'anchor' | 
 |  | 
 | import os | 
 | import fnmatch | 
 |  | 
 |  | 
 | dialogstates = {} | 
 |  | 
 |  | 
 | class FileDialog: | 
 |  | 
 |     """Standard file selection dialog -- no checks on selected file. | 
 |  | 
 |     Usage: | 
 |  | 
 |         d = FileDialog(master) | 
 |         file = d.go(dir_or_file, pattern, default, key) | 
 |         if file is None: ...canceled... | 
 | 	else: ...open file... | 
 |  | 
 |     All arguments to go() are optional. | 
 |  | 
 |     The 'key' argument specifies a key in the global dictionary | 
 |     'dialogstates', which keeps track of the values for the directory | 
 |     and pattern arguments, overriding the values passed in (it does | 
 |     not keep track of the default argument!).  If no key is specified, | 
 |     the dialog keeps no memory of previous state.  Note that memory is | 
 |     kept even when the dialog is cancelled.  (All this emulates the | 
 |     behavior of the Macintosh file selection dialogs.) | 
 |  | 
 |     """ | 
 |  | 
 |     title = "File Selection Dialog" | 
 |  | 
 |     def __init__(self, master, title=None): | 
 | 	if title is None: title = self.title | 
 | 	self.master = master | 
 | 	self.directory = None | 
 |  | 
 | 	self.top = Toplevel(master) | 
 | 	self.top.title(title) | 
 | 	self.top.iconname(title) | 
 |  | 
 | 	self.botframe = Frame(self.top) | 
 | 	self.botframe.pack(side=BOTTOM, fill=X) | 
 |  | 
 | 	self.selection = Entry(self.top) | 
 | 	self.selection.pack(side=BOTTOM, fill=X) | 
 | 	self.selection.bind('<Return>', self.ok_event) | 
 |  | 
 | 	self.filter = Entry(self.top) | 
 | 	self.filter.pack(side=TOP, fill=X) | 
 | 	self.filter.bind('<Return>', self.filter_command) | 
 |  | 
 | 	self.midframe = Frame(self.top) | 
 | 	self.midframe.pack(expand=YES, fill=BOTH) | 
 |  | 
 | 	self.filesbar = Scrollbar(self.midframe) | 
 | 	self.filesbar.pack(side=RIGHT, fill=Y) | 
 | 	self.files = Listbox(self.midframe, exportselection=0, | 
 | 			     yscrollcommand=(self.filesbar, 'set')) | 
 | 	self.files.pack(side=RIGHT, expand=YES, fill=BOTH) | 
 | 	self.files.bind('<ButtonRelease-1>', self.files_select_event) | 
 | 	self.files.bind('<Double-ButtonRelease-1>', self.files_double_event) | 
 | 	self.filesbar.config(command=(self.files, 'yview')) | 
 |  | 
 | 	self.dirsbar = Scrollbar(self.midframe) | 
 | 	self.dirsbar.pack(side=LEFT, fill=Y) | 
 | 	self.dirs = Listbox(self.midframe, exportselection=0, | 
 | 			    yscrollcommand=(self.dirsbar, 'set')) | 
 | 	self.dirs.pack(side=LEFT, expand=YES, fill=BOTH) | 
 | 	self.dirsbar.config(command=(self.dirs, 'yview')) | 
 | 	self.dirs.bind('<ButtonRelease-1>', self.dirs_select_event) | 
 | 	self.dirs.bind('<Double-ButtonRelease-1>', self.dirs_double_event) | 
 |  | 
 | 	self.ok_button = Button(self.botframe, | 
 | 				 text="OK", | 
 | 				 command=self.ok_command) | 
 | 	self.ok_button.pack(side=LEFT) | 
 | 	self.filter_button = Button(self.botframe, | 
 | 				    text="Filter", | 
 | 				    command=self.filter_command) | 
 | 	self.filter_button.pack(side=LEFT, expand=YES) | 
 | 	self.cancel_button = Button(self.botframe, | 
 | 				    text="Cancel", | 
 | 				    command=self.cancel_command) | 
 | 	self.cancel_button.pack(side=RIGHT) | 
 |  | 
 | 	self.top.protocol('WM_DELETE_WINDOW', self.cancel_command) | 
 | 	# XXX Are the following okay for a general audience? | 
 | 	self.top.bind('<Alt-w>', self.cancel_command) | 
 | 	self.top.bind('<Alt-W>', self.cancel_command) | 
 |  | 
 |     def go(self, dir_or_file=os.curdir, pattern="*", default="", key=None): | 
 | 	if key and dialogstates.has_key(key): | 
 | 	    self.directory, pattern = dialogstates[key] | 
 | 	else: | 
 | 	    dir_or_file = os.path.expanduser(dir_or_file) | 
 | 	    if os.path.isdir(dir_or_file): | 
 | 		self.directory = dir_or_file | 
 | 	    else: | 
 | 		self.directory, default = os.path.split(dir_or_file) | 
 | 	self.set_filter(self.directory, pattern) | 
 | 	self.set_selection(default) | 
 | 	self.filter_command() | 
 | 	self.selection.focus_set() | 
 | 	self.top.grab_set() | 
 | 	self.how = None | 
 | 	self.master.mainloop()		# Exited by self.quit(how) | 
 | 	if key: dialogstates[key] = self.get_filter() | 
 | 	self.top.destroy() | 
 | 	return self.how | 
 |  | 
 |     def quit(self, how=None): | 
 | 	self.how = how | 
 | 	self.master.quit()		# Exit mainloop() | 
 |  | 
 |     def dirs_double_event(self, event): | 
 | 	self.filter_command() | 
 |  | 
 |     def dirs_select_event(self, event): | 
 | 	dir, pat = self.get_filter() | 
 | 	subdir = self.dirs.get(ANCHOR) | 
 | 	dir = os.path.normpath(os.path.join(self.directory, subdir)) | 
 | 	self.set_filter(dir, pat) | 
 |  | 
 |     def files_double_event(self, event): | 
 | 	self.ok_command() | 
 |  | 
 |     def files_select_event(self, event): | 
 | 	file = self.files.get(ANCHOR) | 
 | 	self.set_selection(file) | 
 |  | 
 |     def ok_event(self, event): | 
 | 	self.ok_command() | 
 |  | 
 |     def ok_command(self): | 
 | 	self.quit(self.get_selection()) | 
 |  | 
 |     def filter_command(self, event=None): | 
 | 	dir, pat = self.get_filter() | 
 | 	try: | 
 | 	    names = os.listdir(dir) | 
 | 	except os.error: | 
 | 	    self.master.bell() | 
 | 	    return | 
 | 	self.directory = dir | 
 | 	self.set_filter(dir, pat) | 
 | 	names.sort() | 
 | 	subdirs = [os.pardir] | 
 | 	matchingfiles = [] | 
 | 	for name in names: | 
 | 	    fullname = os.path.join(dir, name) | 
 | 	    if os.path.isdir(fullname): | 
 | 		subdirs.append(name) | 
 | 	    elif fnmatch.fnmatch(name, pat): | 
 | 		matchingfiles.append(name) | 
 | 	self.dirs.delete(0, END) | 
 | 	for name in subdirs: | 
 | 	    self.dirs.insert(END, name) | 
 | 	self.files.delete(0, END) | 
 | 	for name in matchingfiles: | 
 | 	    self.files.insert(END, name) | 
 | 	head, tail = os.path.split(self.get_selection()) | 
 | 	if tail == os.curdir: tail = '' | 
 | 	self.set_selection(tail) | 
 |  | 
 |     def get_filter(self): | 
 | 	filter = self.filter.get() | 
 | 	filter = os.path.expanduser(filter) | 
 | 	if filter[-1:] == os.sep or os.path.isdir(filter): | 
 | 	    filter = os.path.join(filter, "*") | 
 | 	return os.path.split(filter) | 
 |  | 
 |     def get_selection(self): | 
 | 	file = self.selection.get() | 
 | 	file = os.path.expanduser(file) | 
 | 	return file | 
 |  | 
 |     def cancel_command(self, event=None): | 
 | 	self.quit() | 
 |  | 
 |     def set_filter(self, dir, pat): | 
 | 	if not os.path.isabs(dir): | 
 | 	    try: | 
 | 		pwd = os.getcwd() | 
 | 	    except os.error: | 
 | 		pwd = None | 
 | 	    if pwd: | 
 | 		dir = os.path.join(pwd, dir) | 
 | 		dir = os.path.normpath(dir) | 
 | 	self.filter.delete(0, END) | 
 | 	self.filter.insert(END, os.path.join(dir or os.curdir, pat or "*")) | 
 |  | 
 |     def set_selection(self, file): | 
 | 	self.selection.delete(0, END) | 
 | 	self.selection.insert(END, os.path.join(self.directory, file)) | 
 |  | 
 |  | 
 | class LoadFileDialog(FileDialog): | 
 |  | 
 |     """File selection dialog which checks that the file exists.""" | 
 |  | 
 |     title = "Load File Selection Dialog" | 
 |  | 
 |     def ok_command(self): | 
 | 	file = self.get_selection() | 
 | 	if not os.path.isfile(file): | 
 | 	    self.master.bell() | 
 | 	else: | 
 | 	    self.quit(file) | 
 |  | 
 |  | 
 | class SaveFileDialog(FileDialog): | 
 |  | 
 |     """File selection dialog which checks that the file may be created.""" | 
 |  | 
 |     title = "Save File Selection Dialog" | 
 |  | 
 |     def ok_command(self): | 
 | 	file = self.get_selection() | 
 | 	if os.path.exists(file): | 
 | 	    if os.path.isdir(file): | 
 | 		self.master.bell() | 
 | 		return | 
 | 	    d = Dialog(self.top, | 
 | 		       title="Overwrite Existing File Question", | 
 | 		       text="Overwrite existing file %s?" % `file`, | 
 | 		       bitmap='questhead', | 
 | 		       default=1, | 
 | 		       strings=("Yes", "Cancel")) | 
 | 	    if d.num != 0: | 
 | 		return | 
 | 	else: | 
 | 	    head, tail = os.path.split(file) | 
 | 	    if not os.path.isdir(head): | 
 | 		self.master.bell() | 
 | 		return | 
 | 	self.quit(file) | 
 |  | 
 |  | 
 | def test(): | 
 |     """Simple test program.""" | 
 |     root = Tk() | 
 |     root.withdraw() | 
 |     fd = LoadFileDialog(root) | 
 |     loadfile = fd.go(key="test") | 
 |     fd = SaveFileDialog(root) | 
 |     savefile = fd.go(key="test") | 
 |     print loadfile, savefile | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     test() |