Newer
Older
twander / twander.py
#!/usr/bin/env python
# twander - Wander around the file system
# Copyright (c) 2002 TundraWare Inc.  All Rights Reserved.


PROGNAME = "twander"
RCSID = "$Id: twander.py,v 1.76 2002/11/23 10:07:27 tundra Exp $"
VERSION = RCSID.split()[2]


#----------------------------------------------------------#
#                     Imports                              #
#----------------------------------------------------------#

import getopt
import os
from socket import getfqdn
import sys
import time

from Tkinter import *
from tkMessageBox import showerror, showwarning
from tkSimpleDialog import askstring

# Imports conditional on OS

# Set OS type - this allows us to trigger OS-specific code
# where needed.

OSNAME = os.name


if OSNAME == 'posix':
    import grp
    import pwd
    


#----------------------------------------------------------#
#          Variables User Might Change                     #
#----------------------------------------------------------#

#####
# Defaults
#####

#####
# Key Assignments
#####

# General Program Commands

KEYPRESS  = '<KeyPress>'                 # Any keypress (for commands)
QUITPROG  = '<Escape>'                   # Quit the program
READCONF  = '<Control-r>'                # Re-read the configuration file
REFRESH   = '<Control-l>'                # Refresh screen
TOGDETAIL = '<Control-t>'                # Toggle detail view

# Directory Navigation

CHANGEDIR = '<Control-x>'                # Enter a new path
DIRHOME   = '<Control-h>'                # Goto $HOME
DIRBACK   = '<Control-b>'                # Goto previous directory
DIRSTART  = '<Control-s>'                # Goto starting directory
DIRUP     = '<BackSpace>'                # Go up one directory level

# Selection Keys

SELNEXT   = '<Control-n>'                # Select next item
SELPREV   = '<Control-p>'                # Select previous item
SELEND    = '<Control-e>'                # Select bottom item
SELTOP    = '<Control-a>'                # Select top item
SELKEY    = '<Control-space>'            # Select item w/keyboard
SELMOUSE  = '<Double-ButtonRelease-1>'   # Select item w/mouse


#####
# Default Directories & Files
#####

# Default startup directory
STARTDIR = "." + os.sep

HOME = os.getenv("HOME") or STARTDIR


#####
# Program Constants
#####

HEIGHT   = 25
WIDTH    = 90

#####
# Colors
#####

BCOLOR  = "black"
FCOLOR  = "green"


#####
# Fonts
#####

FNAME = "Courier"
FSZ   = 12
FWT   = "bold"


#------------------- Nothing Below Here Should Need Changing ------------------#


#----------------------------------------------------------#
#              Constants & Literals                        #
#----------------------------------------------------------#



#####
# Booleans
#####

# Don't need to define TRUE & FALSE - they are defined in the Tkinter module


#####
# Defaults
#####


DETAILVIEW = TRUE             # File details on
WARN       = TRUE             # Warnings on






#####
# Constants
#####

# General constants

KB            = 1024      # 1 KB constant
MB            = KB * KB   # 1 MB constant
GB            = MB * KB   # 1 GB constant
HOSTNAME      = getfqdn() # Full name of this host
REFRESHINT    = 5000      # Interval (ms) for automatic refresh


# Stat-related constants (in octal)

ST_PIPE       = 0010000   # FIFO
ST_CHAR       = 0020000   # Character Special
ST_DIR        = 0040000   # Directory
ST_BLOCK      = 0060000   # Block Special
ST_REG        = 0100000   # Regular File
ST_SYMLINK    = 0120000   # Symbolic Link
ST_SOCKET     = 0140000   # Socket

ST_PERMIT     = ["---", "--x", "-w-", "-wx",
                 "r--", "r-x", "rw-", "rwx"]
                 

#####
# General Literals
#####

PSEP       = os.sep           # Character separating path components


#####
# Configuration File Related Literals
#####

CONF       = ""               # Config file user selected with -c option
CMDKEY     = r'&'             # Command key delimiter
COMMENT    = r"#"             # Comment character
DIRNAME    = r'[DIRECTORY]'   # Substitution field in config files
FILE       = r'[FILE]'        # Substitution field in config files
FILES      = r'[FILES]'       # Ditto
ENVIRO     = r'$'             # Introduces environment variables


#----------------------------------------------------------#
#            Prompts, & Application Strings                #
#----------------------------------------------------------#


#####
# Error, Information, & Warning  Messages
#####

# Errors

eBADROOT    = " %s Is Not A Directory"
eDIRRD      = "Cannot Open Directory : %s  ---  Check Permissions."
eDUPKEY     = "Duplicate Key In Configuration File Found In Entry: \'%s\'"
eERROR      = "ERROR"
eINITDIRBAD = "Cannot Open Starting Directory : %s - Check Permissions - ABORTING!."
eNOENV      = "Configuration File References Undefined Environment Variable: %s"
eOPEN       = "Cannot Open File: %s"
eTOOMANY    = "You Can Only Specify One Starting Directory."

# Prompts

pCHPATH   = "Change Path"
pENPATH   = "Enter New Path Desired:"

# Warnings

wCMDKEY   = "Configuration File Entry For: \'%s\' Has No Command Key Defined."
wCONFOPEN = "Cannot Open Configuration File:\n%s\n\n%s"
wNOCMDS   = "Running With No Commands Defined!"
wSYMBACK  = " Symbolic Link %s Points Back To Own Directory"
wWARN     = "WARNING"


#####
# Usage Information
#####

uTable = [PROGNAME + " " + VERSION + " - Copyright 2002, TundraWare Inc., All Rights Reserved\n",
          "usage:  " + PROGNAME + " [-bcfhnsvwxy] [startdir] where,\n",
          "          startdir  name of directory in which to begin (default: current dir)",
          "          -b color  background color (default: black)",
          "          -c file   name of configuration file (default: $HOME/." + PROGNAME +
                     " or PROGDIR/." + PROGNAME + ")",
          "          -f color  foreground color (default: green)",
          "          -h        print this help information",
          "          -n name   name of font to use (default: courier)",
          "          -q        quiet mode - no warnings (default: warnings on)",
          "          -s size   size of font to use (default: 12)",
          "          -v        print detailed version information",
          "          -w wght   weight/style of font to use (default: bold)",
          "          -x width  window width (default: 60)",
          "          -y height window height (default: 25)",
          ]


#---------------------------Code Begins Here----------------------------------#


#----------------------------------------------------------#
#             General Support Functions                    #
#----------------------------------------------------------#


#####
# Print An Error Message
#####

def ErrMsg(emsg):

    showerror(PROGNAME + " " + VERSION + "    " + eERROR, emsg)

# End of 'ErrMsg()'


#####
# Print An Information Message
#####

def InfoMsg(emsg):

    showinfo(PROGNAME + " " + VERSION + "    " + iINFO, emsg)

# End of 'ErrMsg()'


#####
# Print A Warning Message
#####

def WrnMsg(wmsg):
    if WARN:
        showwarning(PROGNAME + " " + VERSION + "    " + wWARN, wmsg)

# End of 'WrnMsg()'


#####
# Parse & Process The Configuraton File
# This is called once at program start time
# and again any time someone hits the READCONF key
# while the program is running.
#####

def ParseConfFile(event):
    global CONF, UI

    # If user specified a config file, try that
    # Otherwise use HOME == either $HOME or ./

    if not CONF:
        CONF = os.path.join(HOME, "." + PROGNAME)
    try:
        cf = open(CONF)
    except:
        WrnMsg(wCONFOPEN % (CONF, wNOCMDS))
        UI.rcfile = {}
        return

    # Successful open of config file - Begin processing it
    
    # Cleanout any old 
    UI.rcfile = {}

    # Process and massage the configuration file
    for line in cf.read().splitlines():

        # Lex for comment token and discard until EOL
        # A line beginning with the comment token is thus
        # turned into a blank line, which is discarded.

        idx = line.find(COMMENT)
        if idx > -1:             # found a comment character
            line = line[:idx]

        # Anything which gets through the next conditional
        # must be a non-blank line - i.e., Configuration information
        # we care about, so process it and save for future use.

        if line != "":
            fields = line.split()
            for x in range(1,len(fields)):

                # Process environment variables
                if fields[x][0] == ENVIRO:
                    envval = os.getenv(fields[x][1:])
                    if not envval:       # Environment variable not defined
                        ErrMsg(eNOENV % fields[x])
                        sys.exit(1)
                    else:
                        fields[x] = envval

            # Get command key value and store in dictionary

            keypos = fields[0].find(CMDKEY) # Look for key delimiter

            # No delimiter or delimiter at end of string
            if (keypos < 0) or (CMDKEY == fields[0][-1]):
                WrnMsg(wCMDKEY % fields[0])
                key = fields[0]             # Use whole word as index

            # Found legit delimiter, so use it as
            # dictionary index - mapped to lower case so
            # command keys are case insensitive.
            else:
                key = fields[0][keypos+1].lower()

            if key in UI.rcfile:               # This is a Python 2.2 or later idiom
                ErrMsg(eDUPKEY % fields[0]) # Duplicate key found
                cf.close()
                sys.exit(1)
                
            # Save command name and command using key as index
            UI.rcfile[key] = ["".join(fields[0].split(CMDKEY)),
                           " ".join(fields[1:])
                          ]

    cf.close()

# End of 'ParseConfFile()'


#####
# Print Usage Information
#####

def Usage():
    ustring =""
    
    for x in uTable:
        print x
# End of 'Usage()'


#----------------------------------------------------------#
#                    GUI Definition                        #
#----------------------------------------------------------#



#####
# Enacapsulate the UI in a class
#####


class twanderUI:

    def __init__(self, root):

        # Setup the visual elements

        self.hSB = Scrollbar(root, orient=HORIZONTAL)
        self.vSB = Scrollbar(root, orient=VERTICAL)
        self.DirList = Listbox(root,
                               foreground = FCOLOR,
                               background  = BCOLOR,
                               font=(FNAME, FSZ, FWT),
                               selectmode=EXTENDED,
                               exportselection=0,
                               xscrollcommand=self.hSB.set,
                               yscrollcommand=self.vSB.set,
                               height = HEIGHT,
                               width = WIDTH,
                               )

        # Make them visible by packing
        
        self.hSB.config(command=self.DirList.xview)
        self.hSB.pack(side=BOTTOM, fill=X)
        self.vSB.config(command=self.DirList.yview)
        self.vSB.pack(side=RIGHT, fill=Y)
        self.DirList.pack(side=LEFT, fill=BOTH, expand=1)

        #####
        # Bind the relevant event handlers
        #####
        
        # General Program Commands

        # Bind handler for individual keystrokes
        self.DirList.bind(KEYPRESS, KeystrokeHandler)

        # Bind handler for "Quit Program"
        self.DirList.bind(QUITPROG, KeyQuitProg)

        # Bind handler of "Read Config File"
        self.DirList.bind(READCONF, ParseConfFile)

        # Bind handler of "Refresh Screen" 
        self.DirList.bind(REFRESH, RefreshDirList)

        # Bind handler of "Toggle Detail" 
        self.DirList.bind(TOGDETAIL, KeyToggleDetail)


        # Directory Navigation

        # Bind handler for "Change Directory"
        self.DirList.bind(CHANGEDIR, ChangeDir)

        # Bind handler for "Home Dir"
        self.DirList.bind(DIRHOME, KeyHomeDir)

        # Bind handler for "Previous Dir"
        self.DirList.bind(DIRBACK, KeyBackDir)

        # Bind handler for "Starting Dir"
        self.DirList.bind(DIRSTART, KeyStartDir)

        # Bind handler for "Up Dir"
        self.DirList.bind(DIRUP, KeyUpDir)


        # Selection Keys

        # Bind handler for "Next Item"
        self.DirList.bind(SELNEXT, KeySelNext)

        # Bind handler for "Previous Item"
        self.DirList.bind(SELPREV, KeySelPrev)

        # Bind handler for "First Item"
        self.DirList.bind(SELTOP, KeySelTop)

        # Bind handler for "Last Item"
        self.DirList.bind(SELEND, KeySelEnd)

        # Bind handler for "Item Select"
        self.DirList.bind(SELKEY, DirListHandler)

        # Bind handler for "Mouse Select"
        self.DirList.bind(SELMOUSE, DirListHandler)

        # Give the listbox focus so it gets keystrokes
        self.DirList.focus()

        # End if method 'twanderUI.__init__()'


    #####
    # Support periodic polling to make sure widget stays
    # in sync with reality of current directory.
    #####

    def poll(self):

        RefreshDirList()
        self.DirList.after(REFRESHINT, self.poll)

    # End of method 'twanderUI.poll()'


    #####
    # Return tuple of all selected items
    #####

    def AllSelection(self):
        sellist = []

        for entry in self.DirList.curselection():
            sellist.append(self.DirList.get(entry)[UI.NameIndex:])

        return sellist

    # End of method 'twanderUI.AllSelection()'
        

    #####
    # Return name of currently selected item
    #####

    def LastInSelection(self):
        index = self.DirList.curselection()
        if index:
            return self.DirList.get(index[-1])[UI.NameIndex:]
        else:
            return ""

    # End of method 'twanderUI.LastInSelection()'


    #####
    # Update title bar with most current information
    #####

    def UpdateTitle(self, mainwin):

        mainwin.title(PROGNAME + " " + VERSION +
                      "       " + HOSTNAME + ":    "+
                      UI.CurrentDir + "        Total: " +
                      str(UI.DirList.size()))

    # End of method 'twanderUI.UpdateTitle()'

# End of class definition, 'twanderUI'


#----------------------------------------------------------#
#                   Handler Functions                      #
#----------------------------------------------------------#


#--------------- General Program Commands -----------------#


#####
# Event Handler: Individual Keystrokes
#####

def KeystrokeHandler(event):

    # If the key pressed is a command key,
    # get its associated string and
    # execute the command.

    cmd = UI.rcfile.get(event.char.lower(), ["",""])[1]

    # cmd == null means no matching command key -  do nothing
    # Otherwise, replace config tokens with actual file/dir names
    if cmd:

        # Replace runtime-determined tokens

        cmd = cmd.replace(FILE, UI.LastInSelection())

        fstring = ""
        for selected in UI.AllSelection():
            fstring += selected + " "
            
        cmd = cmd.replace(FILES, fstring)
        cmd = cmd.replace(DIRNAME, UI.CurrentDir)

        # Actually execute the command
        os.system(cmd)
    
# end of 'KeystrokeHandler()'
    

#####
# Event Handler: Program Quit
#####

def KeyQuitProg(event):
    sys.exit()

# End of 'KeyQuitProg()'


#####
# Event Handler: Toggle Detail View
#####

def KeyToggleDetail(event):
    global DETAILVIEW
    
    DETAILVIEW = not DETAILVIEW
    RefreshDirList(event)

# End of 'KeyToggleDetail()'


#------------------- Directory Navigation -----------------#


#####
# Event Handler: Change Directory/Path
####

def ChangeDir(event):

    newpath = askstring(pCHPATH, pENPATH)
    if newpath:
        LoadDirList(newpath)
        KeySelTop(event)
    UI.DirList.focus()

# End of 'ChangeDir()'


#####
# Event Handler: Goto $HOME
#####

def KeyHomeDir(event):

    if HOME:
        LoadDirList(HOME)
    else:
        LoadDirList(STARTDIR)

# End of 'KeyHomeDir()'


#####
# Event Handler: Move To Previous Directory
#####

def KeyBackDir(event):

    # Move to last directory visited, if any - inhibit this from
    # being placed on the directory traversal stack
    if UI.LastDir:
        LoadDirList(UI.LastDir.pop(), save=FALSE)

    # No previous directory
    else:
        pass

# End of 'KeyBackDir()'


#####
# Event Handler: Go Back to Initial Directory
#####

def KeyStartDir(event):
    global STARTDIR
    
    LoadDirList(STARTDIR)

# End of 'KeyStartDir()'


#####
# Event Handler: Move up one directory
#####

def KeyUpDir(event):

    # Move up one directory level unless we're already at the root
    if UI.CurrentDir != os.path.abspath(PSEP):
        LoadDirList(UI.CurrentDir + "..")

# End of 'KeyUpDir()'


#---------------------- Selection Keys ---------------------#


#####
# Event Handler: Select Next Item
#####

def KeySelNext(event):
    next = UI.DirList.index(ACTIVE) + 1
    SetSelection((str(next),), next)

# End of 'KeySelNext()'


#####
# Event Handler: Select Previous Item
#####

def KeySelPrev(event):
    prev = UI.DirList.index(ACTIVE) - 1
    SetSelection((str(prev),), prev)

# End of 'KeySelPrev()'


#####
# Event Handler: Select Last Item
#####

def KeySelEnd(event):

    # Get current number of items in listbox
    sz = UI.DirList.size()

    # And setup to last item accordingly
    SetSelection((str(sz),), sz)

# End of 'KeySelEnd()'


#####
# Event Handler: Select First Item
#####

def KeySelTop(event):
    SetSelection(('0',),0)

# End of 'KeySelTop()'


#####
# Process Current Selection
#####

def DirListHandler(event):
    SAVE = TRUE

    # Get current selection.  If none, just return, otherwise process
    selected =  UI.LastInSelection()
    if not selected:
        return
    
    # If selection is a directory, move there and list contents.
    
    if os.path.isdir(os.path.join(UI.CurrentDir, selected)):

        # If we're on Unix, don't follow symlinks pointing back to themselves

        if OSNAME == 'posix' and os.path.samefile(UI.CurrentDir, UI.CurrentDir + selected):
            WrnMsg(wSYMBACK % (UI.CurrentDir + selected))
            return

        # We don't push this selection on the stack if
        # we are at root directory and user presses '..'

        if (selected == '..') and (UI.CurrentDir == os.path.abspath(PSEP)):
            SAVE = FALSE

        # Build full path name
        selected = os.path.join(os.path.abspath(UI.CurrentDir), selected)

        # Convert ending ".." into canonical path
        if selected.endswith(".."):
            selected = PSEP.join(selected.split(PSEP)[:-2])
        
        # Need to end the directory string with a path
        # separator character so that subsequent navigation
        # will work when we hit the root directory of the file
        # system.  In the case of Unix, this means that
        # if we ended up at the root directory, we'll just
        # get "/".  In the case of Win32, we will get
        # "DRIVE:/".

        selected += PSEP

        # Load UI with new directory
        LoadDirList(selected, save=SAVE)

        # And always force selection of first item there.
        # This guarantees a selection in the new
        # directory context, so subsequent commands
        # won't try to operate on an item selected in a
        # previous directory
        
        KeySelTop(event)

    # File selected with a double-click
    # If we're running Win32, use OS association to try and run it.

    elif OSNAME == 'nt':
        os.startfile(os.path.join(os.path.abspath(UI.CurrentDir), selected))

    # Have to update the window title because selection changed
    UI.UpdateTitle(UIroot)

# End of 'DirListHandler()'


#####
# Load UI With Selected Directory
#####

def LoadDirList(newdir, save=TRUE):

    # Get path into canonical form
    newdir = os.path.abspath(newdir)

    # Make sure newdir properly terminated
    if newdir[-1] != PSEP:
        newdir += PSEP

    # Check right now to see if we can read
    # the directory.  If not, at least we
    # haven't screwed up the widget's current
    # contents or program state.

    try:
        contents = BuildDirList(newdir)
    except:
        # If CurrentDir set, we're still there: error w/ recovery
        if UI.CurrentDir:
            ErrMsg(eDIRRD % newdir)
            return

        # If not, we failed on the initial directory: error & abort
        else:
            ErrMsg(eINITDIRBAD % newdir)
            sys.exit(1)

    # Push last directory visited onto the visited stack

    # Do not do this if we've been told not to OR if
    # what we're about to save is the same as the top
    # of the stack OR if the current directory is ""
    
    # If there is anything on the stack, see if last element
    # matches what we're about to put there.
    
    if UI.LastDir and UI.LastDir[-1] == UI.CurrentDir:
        save = FALSE

    if save and UI.CurrentDir:
        UI.LastDir.append(UI.CurrentDir)

    # And select new directory to visit
    UI.CurrentDir = newdir

    # Clear out the old contents
    UI.DirList.delete(0,END)

    # Load new directory contents into UI
    for x in contents:
        UI.DirList.insert(END, x)

    # And update the title to reflect changes
    UI.UpdateTitle(UIroot)

    # Indicate that we've just entered a new directory
    # so RefreshDirList does the right thing

    UI.NewDirEntered = TRUE
 
# End of 'LoadDirList():


#####
# Return Ordered List Of Directories & Files For Current Root
#####

def BuildDirList(currentdir):
    global UI
    
    dList, fList = [], []
    
    # Walk the directory separate subdirs and files
    for file in os.listdir(currentdir):
        if os.path.isdir(os.path.join(currentdir, file)):
            dList.append(file + PSEP)
        else:
            fList.append(file)

    dList.sort()

    # Entry to move up one directory is always first,
    # no matter what the sort.  This is necessary because
    # OSs like Win32 like to use '$' in file names  which
    # sorts before "."

    dList.insert(0, ".." + PSEP)

    fList.sort()

    # For both choices below, we have to set the UI.NameIndex
    # value.  This tells other handlers where in a given
    # selection the actual name of the file can be found.
    # This is necessary because we may be selecting a from
    # a detailed view and need to know where in that view
    # the file name lives.  It is not good enoch to just
    # split() the string and use the [-1] entry because
    # we may be dealing with a file which has spaces in its
    # name.


    # If user has not requested detailed display, we're done
    if not DETAILVIEW:
        UI.NameIndex = 0
        return dList + fList

    # Detailed display requested, do the work

    all     = dList + fList
    detlist = []
    for index in range(len(all)):
        try:
            stinfo =  os.stat(os.path.join(currentdir, all[index]))
            detlist.append("")

            # Mode - 1st get into octal string
            mode = stinfo[0]
            modestr =  str("%o" % mode)

            # Set the permission bits

            mode = ""
            for x in [-3, -2, -1]:
                mode +=  ST_PERMIT[int(modestr[x])]

            detlist[index] += str(mode) + (11 - len(str(mode))) * " "

            # Number of links to entry
            detlist[index] += str(stinfo[3]) + ( 5 - len(str(stinfo[3]))) * " "

            # Get 1st 8 characters of owner and group names on unix
            if OSNAME == 'posix':
                owner = pwd.getpwuid(stinfo[4])[0][:8]
                group = grp.getgrgid(stinfo[5])[0][:8]

            # Default names for all other OSs
            else:
                owner = OSNAME + 'user'
                group = OSNAME + 'group'

            # Add them to the detail
            
            detlist[index] += owner + (12 - len(owner)) * " "
            detlist[index] += group + (12 - len(group)) * " "

            # Length
            # Files under 1 MB show actual length
            # Files < 1 MB < 1 GB shown in KB
            # Files 1 GB or greater, shown in MB

            flen = stinfo[6]
            if flen >= GB:
                flen = str(flen/MB) + "m"
            elif flen >= MB:
                flen = str(flen/KB) + "k"
            else:
                flen = str(flen)
                
            detlist[index] += flen + (11 - len(flen)) * " "

            # Ctime
            ftime = " ".join(time.ctime(stinfo[9]).split()[1:])
            detlist[index] += ftime + (21 - len(ftime)) * " "

            # Set the index into the file name
            UI.NameIndex = len(detlist[index])

            # File name
            detlist[index] += all[index]

        except:
            detlist[index] = all[index]
            
    return detlist

# End of  'BuildDirList()'


#####
# Refresh contents of directory listing to stay in sync with reality
#####

def RefreshDirList(*args):

    # Get current selection and active

    sellist  = UI.DirList.curselection()

    active = UI.DirList.index(ACTIVE)

    # Save current scroll positions

    xs = UI.hSB.get()
    ys = UI.vSB.get()

    # Get new directory listing
    newlist = BuildDirList(UI.CurrentDir)

    # Clean out old listbox contents
    UI.DirList.delete(0,END)

    # Save the new directory information
    UI.DirList.insert(0, *newlist)

    # First refresh after a new directory
    # entered needs to force selection and
    # active to 0.  This works around some
    # strange Tkinter behavior.

    if UI.NewDirEntered:
        KeySelTop(args)
        UI.NewDirEntered = FALSE

    # Otherwise restore selections
    else:
        SetSelection(sellist, active)

    # Restore scroll positions

    UI.DirList.xview(MOVETO, xs[0])
    UI.DirList.yview(MOVETO, ys[0])
    

# End of 'RefreshDirList()


#####
# Set a particular selection, w/bounds checking
#####

def SetSelection(selection, active):

    # Clear all current selection(s)
    UI.DirList.selection_clear(0, END)

    # Get current maximum index
    maxindex =  UI.DirList.size() - 1

    # And bounds check/adjust

    if active > maxindex:
        active = maxindex

    # Set desired selected items, if any

    if selection:
        for entry in selection:
            UI.DirList.select_set(entry)
        UI.DirList.see(selection[-1])

    # Now set the active entry
    UI.DirList.activate(active)


# End of 'SetSelection()'


#----------------------------------------------------------#
#                  Program Entry Point                     #
#----------------------------------------------------------#

# Command line processing

try:
    opts, args = getopt.getopt(sys.argv[1:], '-b:c:f:hn:qs:vw:x:y:')
except getopt.GetoptError:
    Usage()
    sys.exit(1)

# Parse command line

for opt, val in opts:
    if opt == "-b":
        BCOLOR = val
    if opt == "-c":
        CONF = val
    if opt == "-f":
        FCOLOR = val
    if opt == "-h":
        Usage()
        sys.exit(0)
    if opt == "-n":
        FNAME = val
    if opt == "-q":
        WARN = FALSE
    if opt == "-s":
        FSZ = val
    if opt == "-v":
        print RCSID
        sys.exit(0)
    if opt == "-w":
        FWT = val
    if opt == "-x":
        WIDTH = val
    if opt == "-y":
        HEIGHT = val


# Create an instance of the UI
UIroot = Tk()
UI = twanderUI(UIroot)

# Make the Tk window the topmost in the Z stack.
# 'Gotta do this or Win32 will not return input
# focus to our program after a startup warning
# display.

UIroot.tkraise()

#####
# Setup global UI variables
#####

# Figure out where to start
# Program can only have 0 or 1 arguments
# Make sure any startdir argument is legit

if len(args) > 1:
    ErrMsg(eTOOMANY)
    sys.exit(1)

if len(args) == 1:
    STARTDIR = args[0]
    if not os.path.isdir(STARTDIR):
        ErrMsg(eBADROOT % STARTDIR)
        sys.exit(1)

# Get starting directory into canonical form
STARTDIR = os.path.abspath(STARTDIR)


# Parse the and store configuration file, if any
ParseConfFile(None)

# Initialize directory stack
UI.LastDir    = []

# And current location
UI.CurrentDir = ""

# And the flag indicating new directory selected
UI.NewDirEntered = FALSE

# Initialize the UI directory listing
LoadDirList(STARTDIR)
KeySelTop(None)

# And start the periodic polling of the widget
UI.poll()

# Run the program interface
UIroot.mainloop()