File: programming/cocoa/Filie.zip/Filie/Filie/UKFolderController.m


/* =============================================================================
    PROJECT:    Filie
    FILE:       UKFolderController.m
    
    COPYRIGHT:  (c) 2004 by M. Uli Kusterer, all rights reserved.
    
    AUTHORS:    M. Uli Kusterer - UK
    
    LICENSES:   GNU GPL
    
    REVISIONS:
        2004-04-15  UK  Created.
   ========================================================================== */
 
// -----------------------------------------------------------------------------
//  Headers:
// -----------------------------------------------------------------------------
 
#import "UKFolderController.h"
#import "UKDirectoryEnumerator.h"
#import "UKFSItemController.h"
#import "UKDistributedView.h"
#import "UKFinderIconCell.h"
#import "NSImage+NiceScaling.h"
#import "NSFileManager+NameForTempFile.h"
#import "UKFSItem.h"
#if USE_KQUEUE
#import "UKKQueue.h"
#else
#import "UKFNSubscribeFileWatcher.h"
#endif
#import "UKThreadActionQueue.h"
#import "UKPushbackMessenger.h"
#import "UKFolderMetaStorage.h"
#import "UKFolderDataSource.h"
#include <unistd.h>
 
 
// -----------------------------------------------------------------------------
//  Constants:
// -----------------------------------------------------------------------------
 
// List of int array-entries for the various item sizes.
//  Each item's index corresponds to the tag of one menu item.
#define ICON_SIZES      16, 32, 48, 64, 128, 256
// -----------------------------------------------------------------------------
//  Factory methods according to UKFSItemController protocol:
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  initWithPath:
//      Crate a controller for the item at the specified path. * DEPRECATED *
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  initWithURL:
//      Crate a controller for the item at the specified URL.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
#if USE_KQUEUE
		kqueue = [[UKKQueue alloc] init];
        #else
#endif
".htaccess"// distant past +60 seconds so this appears newer than the distantPast of a new meta storage.
// -----------------------------------------------------------------------------
//  dealloc:
//      Destructor.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  newDataSourceForURL:
//      Method that creates the data source for this controller. Called by
//      finishCreation. Provided for subclasses which may wish to get their
//      file list from another data source.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  newMetaStorageForURL:
//      Method that creates the metadata storage for this controller. Called by
//      finishCreation. Provided for subclasses which may wish to get their
//      metadata from another storage.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  recalcCellSize:
//      Recalculate and change the cell size of our icon view based on the
//      iconSize instance variable.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  finishCreation:
//      Instead of awakeFromNib, because awake gets called when UKNibOwner loads
//      our NIB, but at that time the object hasn't been fully constructed yet.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// Make sure we get spatial metaphor right and position our window where user left it:
"snapToGrid""showIconPreviews""iconSize""filterString""keepArrangedMode"// Now finally kick off loading of our files:
// -----------------------------------------------------------------------------
//  loadItemIcon:
//      Add the specified UKFSItem to our queue of objects to get a loadItemIcon
//      message.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  startProgress:
//      Start the progress indicator spinning.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  updateProgress:
//      Display a new status message.
//
//  REVISIONS:
//      2005-02-21  UK  Created.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  startProgress:recordTiming:
//      Start the progress indicator spinning. Can be called from another
//      thread. Calls to this can be nested. Only the outermost call's
//      statusText is displayed, all sub-tasks aren't shown.
//
//      NOTE: recordTiming: is only for debugging purposes. It'll be turned off
//      once we release.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
"%d items - %@"// -----------------------------------------------------------------------------
//  stopProgress:
//      Balance a call to startProgress: or startProgress:recordTiming: once you
//      are done.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
"%d items - took %f seconds"// -----------------------------------------------------------------------------
//  UKDistributedView delegate methods that start/stop the progress indicator
//  when the view is busy:
// -----------------------------------------------------------------------------
"Cacheing items..."// -----------------------------------------------------------------------------
//  itemForFile:
//      Return the UKFSItem for the specified file name (not path). Returns NIL
//      If there is no such file.
//
//      FIX ME! Right now this tries to pick up its search where it last left
//      off, which is an optimization, but it still performs a linear search.
//      Since this is called during loading, we may want to optimize this a lot
//      more, especially for sequential access of our file list.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// Try twice in case we need to wrap around.
// Wrap around index:
// -----------------------------------------------------------------------------
//  loadFolderContents:
//      Called in a new thread whenever the folder contents need to be updated.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// newItems functions as flag to avoid re-entrancy.
// Tell FSItem data source to list files.
"%@: We have a current store.",folderPath);
        [pool release];
    }
}
 
 
// -----------------------------------------------------------------------------
//  dataSourceWillReload:
//      Called by the FSItem data source before it starts telling us about the
//      files it has available.
//
//      This makes a new array into which the new list of files is loaded. That
//      way, the user can still work with the old list until the update has
//      finished. However, since old items are shared between the two sources,
//      the user will already get part of the update while working with the old
//      list.
//
//      *this runs in another thread*
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
"Loading files..."// Was empty till now? First load!
// Let user watch while it's loading.
    }
}
 
 
// -----------------------------------------------------------------------------
//  listItem:withAttributes:source:
//      Called by the FSItem data source for each file it offers us. We ignore
//      invisible files if the user wants us to. This works on a list that is
//      not displayed in a window, but carries over old FSItems.
//
//      *this runs in another thread*
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// We're at file system root? Hide all items named in the .hidden file:
"/""/.hidden""\n""Network"] retain];  // We also hide "Network", because we display that in a special location.
'.'// Begins with dot and we're not supposed to show dotted files? hide!
// Hidden, but user added it to the "always show" list?
// Showing, but user (or .hidden file) added it to "always hide" list?
// All these checks still mean we should show 'em?
// Item we already know?
// Remember for later. We can't pick a new position yet, because we don't know what spaces are occupied before everything's been loaded.
        }
    }
}
 
 
// -----------------------------------------------------------------------------
//  dataSourceWillRecache:
//      Called by the FSItem data source occasionally when it hits a short pause
//      in listing files, e.g. when it's replenishing its cache. If this is the
//      first time we're listing items in our window, this lets the user watch
//      and causes a redraw of the icon view.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// Let user watch if this is first time we're loading.
// -----------------------------------------------------------------------------
//  dataSourceDidReload:
//      Called by the FSItem data source once it's finished telling us about its
//      list of files. We know swap the old list with the new, updated list and
//      quickly pick positions for all newly-arrived items.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
"Positioning new items..."];
    
    // Add all new items in one fell swoop:
// Caches the last free position so we don't have to skip all those occupied slots again.
"Positioning item %d of %d"// Picks a free position that lies on the grid.
// Re-sort if needed:
"Cleaning up..."];
 
    // Let user see new stuff:
// Clean up:
"Bored."// newItems doubles as a flag for finding out whether we're busy reloading. Clear flag!
// Swap in updated list.
// -----------------------------------------------------------------------------
//  fsDataSource:
//      Return our FSItem data source.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  closeController:
//      Close this controller and its window.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// This sends us windowWillClose which takes care of the rest.
}
 
 
// -----------------------------------------------------------------------------
//  shouldCloseController:
//      If we return NO here, we can interrupt a quit, etc.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  fileStorePath:
//      Return the path to use for our metadata store. * DEPRECATED *
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
"filieStorePath called!"".Filie_Store"];
}
 
 
// -----------------------------------------------------------------------------
//  selectController:
//      Activate this controller by bringing its window to the front.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  displayName:
//      Return the display name for this controller, to be used in GUI lists etc.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  searchString:
//      Return the string we're filtering by.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  setSearchString:
//      Specify the string to filter by. Called by our filter field.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  itemNeedsDisplay:
//      Find the specified item and make the distributed view update it.
//
//      FIX ME! We could probably optimize this by just taking the item's
//      position directly and adding a method to UKDistributedView for updating
//      an item at a specific coordinate.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  Table view delegate methods (for use in list view one day):
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  distributedView:positionForCell:atItemIndex:
//      Delegate method called by UKDistributedView to display each item.
//      Sets up the cell and returns the item's position.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
/* may be nil if the view only wants the item position. */// -----------------------------------------------------------------------------
//  distributedView:setPosition:forItemIndex:
//      Delegate method called by UKDistributedView when an item has been moved
//      through dragging it.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  distributedView:setObjectValue:forItemIndex:
//      Delegate method called by UKDistributedView when an item has been
//      inline-edited. This is where we rename the item.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// Give kqueue thread a chance to ignore update notification.
// -----------------------------------------------------------------------------
//  distributedView:cellDoubleClickedAtItemIndex:
//      Delegate method called by UKDistributedView when an item has been
//      double-clicked. This is where we open all selected items.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  distributedView:toolTipForItemAtIndex:
//      Delegate method called by UKDistributedView when it needs to know what
//      tool tip ("help tag") to display for an item. This is where we display
//      the full, un-shortened name.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  distributedView:itemIndexForString:options:
//      Delegate method called by UKDistributedView when it needs to know what
//      item to select during type-ahead selection. Also called by the filter
//      field to select the first matching item.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// Visible? Good enough.
// Not visible? Keep looking for another, possibly visible item so we avoid scrolling stuff away the user wants.
// -----------------------------------------------------------------------------
//  distributedView:writeItems:toPasteboard:
//      Delegate method called by UKDistributedView when a drag has been
//      started. UKDV automatically adds the item positions to the drag and
//      generates a nice drag image.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  distributedView:validateDrop:proposedItem:
//      Delegate method called by UKDistributedView when a drag has entered
//      this view. It will propose to have the drop occur on the item that
//      the mouse is over, or -1 to mean inside the view itself.
//
//      This returns NSDragOperationNone to say it doesn't accept the drag,
//      otherwise says how it will accept the drag.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// We can only drop into folders.
            *row = -1;                  // Drop in container if it's on top of a file.
// Attempt to drop an item on itself? User is probably moving it just a little.
                *row = -1;      // Make the target the container.
// -----------------------------------------------------------------------------
//  itemForPath:
//      Return the item at the specified path in this controller, or NIL if this
//      controller doesn't contain the specified item.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  distributedView:acceptDrop:onItem:
//      The user has dropped something on our icon view. Actually drop the
//      items now and add them to our file list (i.e. move/copy the files).
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// Loop over the files dropped:
// Determine the position this item was dropped at:
// It wasn't an item in here that was just moved?
// Copy/Link/Move the object over and add an item for it:
// Place the (possibly new) item at its new position:
// Update the icon view.
// We're accepting this drop.
}
 
 
// -----------------------------------------------------------------------------
//  distributedView:dragEndedWithOperation:
//      The user has dropped something from our icon view somewhere else.
//      If it was dropped on the trash, we re-route this to mean "delete".
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// Just pretend somebody had chosen the "clear" menu item. Message will be handed on to delegate.
}
 
 
// -----------------------------------------------------------------------------
//  delete:
//      Someone pressed the "delete" key or dropped some items on the trash.
//      Move them to the trash using the appropriate NSWorkspace method.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
""// -----------------------------------------------------------------------------
//  distributedView:draggingSourceOperationMaskForLocal:
//      The user is trying to drag something out of this view. Happily say yes.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  createNewFolder:
//      The "new folder" menu item.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
"New Folder",@""// -----------------------------------------------------------------------------
//  changeItemSize:
//      Menu item action for the entries in the "icon size" popup menu.
//      This determines what size to switch to based on the menu item's tag and
//      then "scales" the item positions so it behaves as if the view was just
//      zoomed.
//
//      FIX ME! This could potentially cause a loss of precision. We may want
//      to instead keep the positions for 128x128 icon sizes in the metadata
//      store and multiply them by a factor for the smaller sizes upon display.
//      That way, changing the icon size will *never* modify item positions.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// Now "scale" item positions:
// -----------------------------------------------------------------------------
//  changeShowIconPreview:
//      Button action for the "show icon preview" button. Toggles the option
//      and causes a repaint.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  UKRearrangeStringCompareFunc:
//      Function used for sorting items by name. Case-insensitive. For items
//      that come up the same, this uses the file's actual name as a secondary
//      criterion.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
""""// -----------------------------------------------------------------------------
//  UKRearrangeCompareFunc:
//      Function used for sorting items. For items that come up the same, this
//      uses the display name as a secondary criterion by calling
//      UKRearrangeStringCompareFunc, which in turn uses the actual name as a
//      final fallback that should ensure a stable sort order.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
"""."""".""displayName"// -----------------------------------------------------------------------------
//  rearrangeItemsBy:
//      Menu item action for sorting items in the window. Uses the tag of the
//      menu item to determine the actual criterion. Calls rearrangeItemsByTag:
//      to do the actual work.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  keepArrangedBy:
//      Menu item action for keeping items sorted in the window. Uses the tag of
//      the menu item to determine the actual criterion. Changes the
//      keepArrangedMode accordingly.
//      Calls rearrangeItemsByTag: to do the actual work.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  rearrangeItemsByTag:
//      Main sorting bottleneck. This actually repositions the items according
//      to the specified sort criterion.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
"displayName", @"attributes.NSFileModificationDate",
                                @"attributes.NSFileCreationDate", @"attributes.NSFileSize",
                                @"attributes.NSFileType", @"attributes.UKLabelNumber"// -----------------------------------------------------------------------------
//  showInfoPanel:
//      Show info panels for the selected items.
//
//      TODO: Write a variation of this that creates a single info panel for all
//      selected items and hook that up to Command-Shift-I or so.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  validateMenuItem:
//      Make sure all menu and popup menu items are correctly enabled and
//      checked.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  openDocument:
//      Action for the "open..." menu item.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  openSelectedFiles:
//      Open the selected items. This simply fakes a double-click on one of the
//      selected items, which implicitly opens all others as well, after all.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  saveFileStore:
//      Save our metadata to the store. This saves item positions, controller
//      dimensions etc.
//
//  REVISIONS:
//      2005-02-20  UK  Fixed sidebar saving.
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
/*if( [[self storeLastModified] compare: [self lastModified]] == NSOrderedSame )
    {
        NSLog( @"saveFileStore - No changes, skipping save of item positions/icons." );
        [pool release];
        return;
    }*/"Saving item positions..."// Save controller-wide prefs:
"snapToGrid""iconSize""showIconPreviews"// Sidebar may not have search field, and we don't want to error because of that.
"filterString""keepArrangedMode"// Save file icon positions to file store:
// Save changed file store:
// Make sure this save doesn't cause an unnecessary reload of the view.
// -----------------------------------------------------------------------------
//  retain:
//      Used during debugging. Can probably be deleted.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  windowWillClose:
//      User closed our window. Save and perform suicide.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// Registry disposes of us.
}
 
 
// -----------------------------------------------------------------------------
//  performKeepArranged:
//      This is called whenever items can change and will enforce the sort order
//      of the "keep arranged" submenu, if the user requested sorting.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  windowDidResize:
//      Our window has resized. Enforce keep arranged.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  windowDidMove:
//      Our window has been moved. Mark as dirty.
//
//  REVISIONS:
//      2005-02-23  UK  Created.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  path:
//      Return the folder path for which this controller is responsible.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  isEqual:
//      Compare this item to another. For use with collection classes like
//      NSArray, but possibly broken.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  hash:
//      hash for comparing this item to another. For use with collection classes
//      like NSArray, but possibly broken.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  watcher:receivedNotification:forPath:
//      Our kqueue that watches for file change notifications has noticed a
//      change. Reload our folder's contents.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  windowWillUseStandardFrame:defaultFrame:
//      Implement smarter zooming of our window.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  itemIsVisible:
//      Return whether one of our FSItems is actually visible. Used to decide
//      which items to load first.
//
//      TODO: Could probably be optimized to get the position directly from the
//      item and then call a method on UKDistributedView that returns whether
//      an item is visible based on its position.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  showIconPreview:
//      Return whether we want our icons to contain previews. Called by our
//      FSItems when they load their final icon.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  iconSizeForItem:
//      Return what size we want our icons to be. Called by our FSItems when
//      they load their final icons.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  controlTextDidChange:
//      Sent by our filter search field when it is modified. Triggers a new
//      search.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//  description:
//      Makes our item show up a tad prettier in NSLog() statements.
//
//  REVISIONS:
//      2004-12-22  UK  Documented.
// -----------------------------------------------------------------------------
"UKFolderController { path = \"%@\" }"

This code uses the PclZip Zip File reading code, which is subject to the GNU LGPL. It also uses the GeSHi syntax highlighter, subject to the GPL. Ask if you want this for your own web site, it's free.