from Tkinter import *
from listbox import *
from rhtkinter import *
from buttonbar import *
from rhutil import *
from fnmatch import fnmatch
import os
import rhdialog
import regsub

class FileSelectList(RHFrame):
    def nearest(self, y):
	return self.listbox.nearest(y)

    def get(self, index):
	return self.listbox.get(index)

    def curselection(self):
	return self.listbox.curselection()

    def bind(self, pattern, action):
	self.listbox.bind(pattern, action)

    def insert(self, where, item):
	self.listbox.insert(where, item)

    def delete(self, first, last=None):
	self.listbox.delete(first, last)
   
    def createWidgets(self):
	self.frame = RHFrame(self)
	self.listbox = RHListbox(self.frame, { 'height' : '7' } )
	self.title = Label(self.frame, { 'text' : self.title })

	self.vscroll = Scrollbar(self.frame, { 'command' : self.listbox.yview })
	self.listbox['yscrollcommand'] = self.vscroll.set;

	self.hscroll = Scrollbar(self.frame, { 'orient' : 'horizontal',
				'command' : self.listbox.xview } )
	self.listbox['xscrollcommand'] = self.hscroll.set;
	
	self.title.pack({ 'side' : 'top', 'anchor' : 'w' })
	self.hscroll.pack({ 'side' : 'bottom', 'fill' : 'x' })
	self.vscroll.pack({ 'side' : 'right', 'fill' : 'y' })
	self.listbox.pack()

	self.frame.pack({ 'expand' : '1', 'fill' : 'both' })

    def __init__(self, master, title):
	RHFrame.__init__(self, master)
	self.titleLabel = None
	self.title = title
	self.createWidgets()

class FileSelectWin(RHFrame):

    def createWidgets(self):

	self.filterFrame = RHFrame(self)

	self.filterLabel = Label(self.filterFrame, { 'text' : 'Filter' })
	self.filter = RHEntry(self.filterFrame, 
				{ 'textvar': self.currentFilter })
	self.filter.bind("<Return>", self.updateFileLists)

	self.hiddenCheck = Checkbutton(self.filterFrame)
	self.hiddenCheck['text'] = 'Show hidden files'
	self.hiddenCheck['variable'] = self.showHidden;
	self.hiddenCheck.bind("<Button-1>", self.hiddenCheckToggle)

	self.filter.pack({ 'fill' : 'x', 'side' : 'bottom' })
	self.filterLabel.pack({ 'anchor' : 'w', 'side' : 'left' })
	self.hiddenCheck.pack({ 'side' : 'right', 'anchor' : 'e' })

	self.pathFrame = RHFrame(self)

	self.pathLabel = Label(self.pathFrame, { 'text' : 'File name' })
	self.path = RHEntry(self.pathFrame, { 'textvar' : self.currentPath })
	self.path.bind("<Return>", self.done)
	self.pathLabel.pack({ 'anchor' : 'w' })
	self.path.pack({ 'fill' : 'x' })

	self.listFrame = RHFrame(self)

	self.dirList = FileSelectList(self.listFrame, 'Directories')
	self.dirList.bind("<Button-1>", self.directorySelected)
	self.dirList.bind("<Double-1>", self.updateFileLists)
	self.fileList = FileSelectList(self.listFrame, 'Files')
	self.fileList.bind("<Button-1>", self.fileSelected)
	self.fileList.bind("<Double-1>", self.done)
	
	self.dirList.pack({ 'side' : 'left', 'padx' : '4' })
	self.fileList.pack({ 'side' : 'left', 'padx' : '4' })

	self.Buttons = ButtonBar(self)
	self.Buttons.addButton("Ok", self.done, 1)
	self.Buttons.addButton("Filter", self.updateFileLists, 1)
	self.Buttons.addButton("Cancel", self.quit, 1)

	self.filterFrame.pack({ 'side' : 'top', 'fill' : 'x', 'padx' : '4' })
	self.listFrame.pack({ 'side' : 'top' })
	self.pathFrame.pack({ 'side' : 'top', 'fill' : 'x', 'padx' : '4' })
	self.Buttons.pack({ 'fill' : 'x' })

	self.pack({ 'expand' : '1', 'fill' : 'both' })

    def hiddenCheckToggle(self, event):
	# this is an ugly hack to get around not having nice access to
	# tk's bindtags mechanism, which is what should really be used here
	oldstate = self.showHidden.get()
	self.showHidden.set(not oldstate)
	self.update()
	self.updateFileLists()
	self.showHidden.set(oldstate)

    def directorySelected(self, event):
        selection = self.dirList.nearest(event.y)
	cdir = self.dirList.get(selection)
	self.currentFilter.set(self.directory + "/" + cdir +"/"+self.currfilter)
	self.currentFilter.set(os.path.join(self.directory,
		os.path.join(cdir, self.currfilter)))

    def fileSelected(self, event):
        selection = self.fileList.nearest(event.y)
	file = self.fileList.get(selection)
	self.currentPath.set(os.path.join(self.directory, file))

    def updateFileLists(self, event = None):
	path = os.path.expanduser(self.currentFilter.get())
	oldpath = ''
	while (oldpath != path):
	    oldpath = path
	    path = regsub.gsub("[^/][^/]*/\.\./", "", path)
	    path = regsub.gsub("//", "/", path)
	    path = regsub.gsub("/\./", "/", path)

	if (os.path.isdir(path)):
	    filter = '*';
	else:
	    (path, filter) = os.path.split(path)

	self.directory = path
	self.currfilter = filter

	if (self.lastpath == path):
	    files = self.lastfiles
	else:
	    if (os.path.isdir(path)):
		files = os.listdir(path)
		files.sort()
		self.lastpath = path
		self.lastfiles = files
	    else:
		rhdialog.error(path + ' is not a directory.')
		return

	self.dirList.delete(0, 'end')
	self.fileList.delete(0, 'end')

	if (not self.showHidden.get()):
	    self.dirList.insert('end', '..')

	for n in files:
	    if ((self.showHidden.get()) or (n[0] != '.')):
		if (os.path.isdir(os.path.join(path, n))):
		    self.dirList.insert('end', n)
		elif (fnmatch(n, filter)):
		    self.fileList.insert('end', n)

	self.currentFilter.set(os.path.join(path, filter))
	self.currentPath.set('')

    def done(self, e = None):
	if (e and e.widget == self.fileList):
	    selection = self.fileList.nearest(event.y)
	    self.file = self.fileList.get(selection)
	    self.quit()
	else:
	    file = self.currentPath.get()
	    if (len(file) == 0):
		rhdialog.error("You must choose a file.")
	    elif (os.path.isfile(file)):
		self.file = file
		self.quit()
	    else:
		rhdialog.error(file + " is not a valid filename.")

    def getResult(self):
	return self.file

    def __init__(self, deffilter, defpath, showhidden, Master = None):
	if (not Master):
	    Master = Toplevel()
	
	RHFrame.__init__(self, Master)

	if (not defpath):
	    defpath = os.getcwd()

	self.currentPath = StringVar()
	self.currentPath.set(defpath)
	self.currentFilter = StringVar()
	self.currentFilter.set(os.path.join(defpath, deffilter))
	self.showHidden = BooleanVar()
	self.showHidden.set(showhidden)

	self.directory = defpath
	self.currfilter = deffilter
	self.lastpath = None
	self.file = None

	self.createWidgets()
	self.updateFileLists()

def selectFileDialog(self, deffilter = '*', defpath = None, showhidden = 0):
    win = FileSelectWin(deffilter, defpath, showhidden)
    win.wait_window(win)
    return win.getResult()
