This is DirWindow.m in view mode; [Download] [Up]
//============================================================================= // // Copyright (C) 1995-1997 by Paul S. McCarthy and Eric Sunshine. // Written by Paul S. McCarthy and Eric Sunshine. // All Rights Reserved. // // This notice may not be removed from this source code. // // This object is included in the MiscKit by permission from the authors // and its use is governed by the MiscKit license, found in the file // "License.rtf" in the MiscKit distribution. Please refer to that file // for a list of all applicable permissions and restrictions. // //============================================================================= //----------------------------------------------------------------------------- // DirWindow.m // // Manages window which displays directory listing. // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // $Id: DirWindow.m,v 1.3 97/07/08 11:55:29 sunshine Exp $ // $Log: DirWindow.m,v $ // Revision 1.3 97/07/08 11:55:29 sunshine // v16: Worked around Cell's ClipView cache bug. // // Revision 1.2 97/06/10 05:05:24 sunshine // v15: Synchronized with ScrollDir v30. Now uses -setTitleAsFilename:. // Ditched Message slot. Upgraded naming. Hard-links are now right-aligned. // No longer unnecessarily sets the tag on the date and permission cells. // // Revision 1.1 97/02/05 08:24:10 sunshine // v13: Synchronized with ScrollDir (v29). Added cell editing, image // dragging, filename locking, notion of writable vs. readonly directory. //----------------------------------------------------------------------------- #import "DirWindow.h" #import "Defaults.h" #import "DirArray.h" #import "NameCache.h" #import <misckit/MiscExporter.h> #import <misckit/MiscIntList.h> #import <misckit/MiscTableScroll.h> #import <misckit/MiscTableCell.h> #import <appkit/Application.h> #import <appkit/Button.h> #import <appkit/ButtonCell.h> #import <appkit/NXImage.h> #import <appkit/Panel.h> #import <appkit/Text.h> #import <appkit/TextField.h> #import <appkit/workspaceRequest.h> #import <objc/NXBundle.h> #import <assert.h> #import <ctype.h> #import <errno.h> #import <libc.h> // getwd() #import <string.h> #import <unistd.h> // getcwd() #import <mach/mach_init.h> // vm_page_size #ifdef _POSIX_SOURCE # define GET_CURR_DIR(_BF_,_SZ_) getcwd( _BF_, _SZ_ ) #else # define GET_CURR_DIR(_BF_,_SZ_) getwd( _BF_ ) #endif static int const CASCADE_MAX = 10; static int CASCADE_COUNTER = 0; static NXCoord CASCADE_ORIGIN_X; static NXCoord CASCADE_ORIGIN_Y; static NXCoord CASCADE_DELTA_X; static NXCoord CASCADE_DELTA_Y; static BOOL DEFAULT_AUTO_SORT; static BOOL DEFAULT_SHOW_HIDDEN; static BOOL DEFAULT_HIGHLIGHT_DIRS; static BOOL DEFAULT_DRAG_UNSCALED; static NXColor DEFAULT_COLOR; static NXSize DEFAULT_WIN_SIZE; static Font* DEFAULT_FONT; static List* OPEN_DIRS = 0; static NXImage* LOCKED_ICON = 0; static NXImage* UNLOCKED_ICON = 0; static OwnerCache* OWNERS = 0; static GroupCache* GROUPS = 0; static char const COLOR_DEF[] = "DirColor"; static char const SIZE_DEF[] = "DirSize"; static char const FONT_DEF[] = "DirFont"; static char const SORT_DEF[] = "AutoSort"; static char const HIDDEN_DEF[] = "ShowHidden"; static char const HLIGHT_DEF[] = "HighlightDirs"; static char const UNSCALED_DEF[] = "DragUnscaled"; static char const COL_SIZES_DEF[] = "ColSizes"; static char const COL_ORDER_DEF[] = "ColOrder"; static char const LOCKED_ICON_S[] = "Lock.secure"; static char const UNLOCKED_ICON_S[] = "Lock.insecure"; //----------------------------------------------------------------------------- // NOTE: USE_PRIVATE_ZONE // When set to '1', this program will place each window in a separate // NXZone and destroy the zone when the window is closed. However, // the NEXTSTEP 3.3 Cell class caches ClipViews which it uses for editing // scrollable cells. Unfortunately the caching code is buggy. It // blindly allocates ClipViews from transient zones such as ours then // caches and tries to re-use the ClipViews even after the originating // zones are defunct. Hence the program crashes. Verification of this // bug was obtained via disassembly of relevant portions of the AppKit. // The OPENSTEP AppKit does not have this problem. Disassembly of the // relevant OPENSTEP AppKit code reveals that the NSClipViews are // allocated from a zone which is guranteed to stay around for the life // of the application, NSDefaultMallocZone(). //----------------------------------------------------------------------------- #define USE_PRIVATE_ZONE 0 #if USE_PRIVATE_ZONE # define EMPLOY_ZONE() NXCreateZone( vm_page_size, vm_page_size, YES ) # define RETIRE_ZONE(Z) NXDestroyZone(Z) #else # define EMPLOY_ZONE() NXDefaultMallocZone() # define RETIRE_ZONE(Z) (void)(Z) #endif //----------------------------------------------------------------------------- // normalize_path //----------------------------------------------------------------------------- static void normalize_path( char* buff, size_t sz ) { char save_dir[ FILENAME_MAX + 1 ]; char tmp[ FILENAME_MAX + 1 ]; if (buff != 0 && sz > 0) { if (GET_CURR_DIR( save_dir, sizeof(save_dir) ) != 0) { if (chdir( buff ) == 0) { if (GET_CURR_DIR( tmp, sizeof(tmp) ) != 0) { int const tmp_len = strlen( tmp ); if (tmp_len < sz) strcpy( buff, tmp ); else { strncpy( buff, tmp, sz ); buff[ sz - 1 ] = '\0'; } } chdir( save_dir ); } } } } //----------------------------------------------------------------------------- // str_int //----------------------------------------------------------------------------- static char const* str_int( int n ) { static char buff[ 32 ]; sprintf( buff, "%d", n ); return buff; } //----------------------------------------------------------------------------- // str_date //----------------------------------------------------------------------------- static char const* str_date( time_t t ) { #define BUFF_SIZE 64 static char buff[ BUFF_SIZE ]; strftime( buff, BUFF_SIZE, "%m/%d/%y %H:%M", localtime(&t) ); return buff; } //----------------------------------------------------------------------------- // str_perms //----------------------------------------------------------------------------- static char const* str_perms( unsigned int mode ) { static char buff[ 16 ]; int ftype; int c; char* s = buff; ftype = (mode & S_IFMT); switch (ftype) { case S_IFDIR: c = 'd'; break; case S_IFCHR: c = 'c'; break; case S_IFBLK: c = 'b'; break; case S_IFREG: c = '-'; break; case S_IFLNK: c = 'l'; break; case S_IFSOCK: c = 's'; break; default: c = '-'; break; } *s++ = c; *s++ = (mode & 0400) ? 'r' : '-'; *s++ = (mode & 0200) ? 'w' : '-'; *s++ = (mode & S_ISUID) ? 's' : (mode & 0100) ? 'x' : '-'; *s++ = (mode & 0040) ? 'r' : '-'; *s++ = (mode & 0020) ? 'w' : '-'; *s++ = (mode & S_ISGID) ? 's' : (mode & 0010) ? 'x' : '-'; *s++ = (mode & 0004) ? 'r' : '-'; *s++ = (mode & 0002) ? 'w' : '-'; *s++ = (mode & S_ISVTX) ? 't' : (mode & 0001) ? 'x' : '-'; *s = '\0'; return buff; } //----------------------------------------------------------------------------- // fmt_icon //----------------------------------------------------------------------------- static void fmt_icon( MiscTableScroll* ts, DirEntry const* de, id cell ) { NXCoord h,w,s; NXSize sz; NXImage* img; w = [ts colSize:ICON_SLOT]; if (w == 0) w = 18; h = [ts uniformSizeRows]; if (h == 0) h = 18; s = (w < h ? w : h) - 1.0; sz.width = s; sz.height = s; img = [de->dir getImage:de scaled:YES]; [img setSize:&sz]; [cell setImage:img]; } //----------------------------------------------------------------------------- // fmt_lock //----------------------------------------------------------------------------- static void fmt_lock( MiscTableScroll* ts, DirEntry const* de, id cell ) { [cell setState:!de->isLocked]; [cell setEnabled:de->canToggleLock]; } //----------------------------------------------------------------------------- // fmt_name //----------------------------------------------------------------------------- static void fmt_name( MiscTableScroll* ts, DirEntry const* de, id cell ) { [cell setStringValueNoCopy:de->name]; } //----------------------------------------------------------------------------- // fmt_size //----------------------------------------------------------------------------- static void fmt_size( MiscTableScroll* ts, DirEntry const* de, id cell ) { [cell setIntValue:de->status.st_size]; } //----------------------------------------------------------------------------- // fmt_modified //----------------------------------------------------------------------------- static void fmt_modified( MiscTableScroll* ts, DirEntry const* de, id cell ) { [cell setStringValue:str_date( de->status.st_mtime )]; } //----------------------------------------------------------------------------- // fmt_perms //----------------------------------------------------------------------------- static void fmt_perms( MiscTableScroll* ts, DirEntry const* de, id cell ) { [cell setStringValue:str_perms( de->status.st_mode )]; } //----------------------------------------------------------------------------- // fmt_owner //----------------------------------------------------------------------------- static void fmt_owner( MiscTableScroll* ts, DirEntry const* de, id cell ) { [cell setStringValueNoCopy:[OWNERS lookup:de->status.st_uid]]; } //----------------------------------------------------------------------------- // fmt_group //----------------------------------------------------------------------------- static void fmt_group( MiscTableScroll* ts, DirEntry const* de, id cell ) { [cell setStringValueNoCopy:[GROUPS lookup:de->status.st_gid]]; } //----------------------------------------------------------------------------- // fmt_hardlinks //----------------------------------------------------------------------------- static void fmt_hardlinks( MiscTableScroll* ts, DirEntry const* de, id cell ) { [cell setIntValue:de->status.st_nlink]; } //----------------------------------------------------------------------------- // fmt_softlink //----------------------------------------------------------------------------- static void fmt_softlink( MiscTableScroll* ts, DirEntry const* de, id cell ) { char const* s = de->softLink; [cell setStringValue:(s != 0 ? s : "")]; } //----------------------------------------------------------------------------- // FORMAT_FUNC //----------------------------------------------------------------------------- typedef void (*FormatFunc)( MiscTableScroll*, DirEntry const*, id ); static FormatFunc FORMAT_FUNC[ MAX_SLOT ] = { fmt_icon, // ICON_SLOT, fmt_name, // NAME_SLOT, fmt_lock, // LOCK_SLOT, fmt_size, // SIZE_SLOT, fmt_modified, // MODIFIED_SLOT, fmt_perms, // PERMS_SLOT, fmt_owner, // OWNER_SLOT, fmt_group, // GROUP_SLOT, fmt_hardlinks, // HARDLINKS_SLOT, fmt_softlink, // SOFTLINK_SLOT, }; //----------------------------------------------------------------------------- // format_cell //----------------------------------------------------------------------------- static inline void format_cell( MiscTableScroll* ts, DirEntry const* de, id cell, unsigned int col ) { FORMAT_FUNC[ col ]( ts, de, cell ); } //============================================================================= // IMPLEMENTATION //============================================================================= @implementation DirWindow //----------------------------------------------------------------------------- // + initCascader //----------------------------------------------------------------------------- + (void)initCascader { NXSize s; [NXApp getScreenSize:&s]; CASCADE_ORIGIN_X = s.width / 4; CASCADE_ORIGIN_Y = s.height - 40; CASCADE_DELTA_X = 20; CASCADE_DELTA_Y = 20; } //----------------------------------------------------------------------------- // + initialize //----------------------------------------------------------------------------- + (id)initialize { if (self == [DirWindow class]) { [self initCascader]; OPEN_DIRS = [[List alloc] init]; LOCKED_ICON = [NXImage findImageNamed:LOCKED_ICON_S]; UNLOCKED_ICON = [NXImage findImageNamed:UNLOCKED_ICON_S]; OWNERS = [OwnerCache commonInstance]; GROUPS = [GroupCache commonInstance]; DEFAULT_COLOR = [Defaults getColor:COLOR_DEF fallback:NX_COLORLTGRAY]; DEFAULT_AUTO_SORT = [Defaults getBool:SORT_DEF fallback:NO]; DEFAULT_SHOW_HIDDEN = [Defaults getBool:HIDDEN_DEF fallback:NO]; DEFAULT_HIGHLIGHT_DIRS = [Defaults getBool:HLIGHT_DEF fallback:NO]; DEFAULT_DRAG_UNSCALED = [Defaults getBool:UNSCALED_DEF fallback:YES]; } return self; } //----------------------------------------------------------------------------- // - cascade //----------------------------------------------------------------------------- - (void)cascade { NXCoord top,left; left = CASCADE_ORIGIN_X + (CASCADE_DELTA_X * CASCADE_COUNTER); top = CASCADE_ORIGIN_Y - (CASCADE_DELTA_Y * CASCADE_COUNTER); [window moveTopLeftTo:left:top]; if (++CASCADE_COUNTER >= CASCADE_MAX) CASCADE_COUNTER = 0; } //----------------------------------------------------------------------------- // - isDir: //----------------------------------------------------------------------------- - (BOOL)isDir:(int)r { return [dirArray isDirAt:r]; } //----------------------------------------------------------------------------- // - updateButtons //----------------------------------------------------------------------------- - (void)updateButtons { BOOL const enable = [scroll numSelectedRows] == 1 && [self isDir:[scroll selectedRow]]; if (enable != [cdButton isEnabled]) [cdButton setEnabled:enable]; } //----------------------------------------------------------------------------- // - tableScroll:cellAt:: //----------------------------------------------------------------------------- - (id)tableScroll:(MiscTableScroll*)ts cellAt:(int)row :(int)col { id cell = lazyRow[col]; DirEntry const* const de = [dirArray entryAt:row]; format_cell( ts, de, cell, col ); if ([cell respondsTo:@selector(setBackgroundColor:)]) { if (highlightDirs && [dirArray isDir:de]) [cell setBackgroundColor:NX_COLORCYAN]; else [cell setBackgroundColor:[ts backgroundColor]]; } return cell; } //----------------------------------------------------------------------------- // - tableScroll:stringValueAt:: //----------------------------------------------------------------------------- - (char const*)tableScroll:(MiscTableScroll*)ts stringValueAt:(int)r :(int)c { char const* rc = 0; if (r < [dirArray count]) { DirEntry const* const de = [dirArray entryAt:r]; switch (c) { case SIZE_SLOT: rc = str_int(de->status.st_size); break; case MODIFIED_SLOT: rc = str_date(de->status.st_mtime); break; case PERMS_SLOT: rc = str_perms(de->status.st_mode); break; case OWNER_SLOT: rc = [OWNERS lookup:de->status.st_uid]; break; case GROUP_SLOT: rc = [GROUPS lookup:de->status.st_gid]; break; case HARDLINKS_SLOT:rc = str_int(de->status.st_nlink); break; case NAME_SLOT: rc = de->name; break; case SOFTLINK_SLOT: rc = de->softLink; break; case ICON_SLOT: case LOCK_SLOT: default: rc = ""; break; } } return (rc != 0 ? rc : ""); } //----------------------------------------------------------------------------- // -intValueAt:: //----------------------------------------------------------------------------- - (int)intValueAt:(int)r :(int)c { int rc = 0; if (r < [dirArray count]) { DirEntry const* const de = [dirArray entryAt:r]; switch (c) { case LOCK_SLOT: rc = de->isLocked; break; case SIZE_SLOT: rc = de->status.st_size; break; case MODIFIED_SLOT: rc = de->status.st_mtime; break; case PERMS_SLOT: rc = de->status.st_mode; break; case OWNER_SLOT: rc = de->status.st_uid; break; case GROUP_SLOT: rc = de->status.st_gid; break; case HARDLINKS_SLOT: rc = de->status.st_nlink; break; case ICON_SLOT: case NAME_SLOT: case SOFTLINK_SLOT: default: break; } } return rc; } //----------------------------------------------------------------------------- // - tableScroll:intValueAt:: //----------------------------------------------------------------------------- - (int)tableScroll:(MiscTableScroll*)ts intValueAt:(int)r :(int)c { return [self intValueAt:r:c]; } //----------------------------------------------------------------------------- // - tableScroll:tagAt:: //----------------------------------------------------------------------------- - (int)tableScroll:(MiscTableScroll*)ts tagAt:(int)r :(int)c { return [self intValueAt:r:c]; } //----------------------------------------------------------------------------- // - tableScroll:stateAt:: //----------------------------------------------------------------------------- - (int)tableScroll:(MiscTableScroll*)ts stateAt:(int)r :(int)c { return [self intValueAt:r:c]; } //----------------------------------------------------------------------------- // - tableScroll:fontChangedFrom:to: //----------------------------------------------------------------------------- - (id)tableScroll:(MiscTableScroll*)ts fontChangedFrom:(Font*)oldFont to:(Font*)newFont { int col; for (col = 0; col < MAX_SLOT; col++) [lazyRow[col] setFont:newFont]; DEFAULT_FONT = newFont; [Defaults set:FONT_DEF font:DEFAULT_FONT]; return self; } //----------------------------------------------------------------------------- // - tableScroll:border:slotResized: //----------------------------------------------------------------------------- - (id)tableScroll:(MiscTableScroll*)ts border:(MiscBorderType)b slotResized:(int)n { char* s; assert( b == MISC_COL_BORDER ); s = [ts colSizesAsString:0 size:0 canExpand:YES]; [Defaults set:COL_SIZES_DEF str:s]; free(s); return self; } //----------------------------------------------------------------------------- // saveSlotOrder:border: //----------------------------------------------------------------------------- - (void)saveSlotOrder:(MiscTableScroll*)ts border:(MiscBorderType)b { if (b == MISC_COL_BORDER) { char* s = [ts colOrderAsString:0 size:0 canExpand:YES]; [Defaults set:COL_ORDER_DEF str:s]; free(s); } } //----------------------------------------------------------------------------- // - tableScroll:border:slotDraggedFrom:to: //----------------------------------------------------------------------------- - (id)tableScroll:(MiscTableScroll*)ts border:(MiscBorderType)b slotDraggedFrom:(int)fromPos to:(int)toPos { [self saveSlotOrder:ts border:b]; return self; } //----------------------------------------------------------------------------- // - tableScroll:border:slotSortReversed: //----------------------------------------------------------------------------- - (id)tableScroll:(MiscTableScroll*)ts border:(MiscBorderType)b slotSortReversed:(int)n { [self saveSlotOrder:ts border:b]; return self; } //----------------------------------------------------------------------------- // - tableScroll:canEdit:at:: //----------------------------------------------------------------------------- - (BOOL)tableScroll:(MiscTableScroll*)ts canEdit:(NXEvent const*)ev at:(int)row :(int)col { return ((ev == 0 || ev->data.mouse.click == 2) && col == NAME_SLOT && ![dirArray isLockedAt:row]); } //----------------------------------------------------------------------------- // - tableScroll:setStringValue:at:: //----------------------------------------------------------------------------- - (BOOL)tableScroll:(MiscTableScroll*)ts setStringValue:(char const*)s at:(int)row :(int)col { while (*s != '\0' && isspace(*s)) s++; [dirArray setName:s at:row]; return YES; } //----------------------------------------------------------------------------- // - tableScroll:preparePasteboard:forDragOperationAt:: //----------------------------------------------------------------------------- - (void)tableScroll:(MiscTableScroll*)s preparePasteboard:(Pasteboard*)pb forDragOperationAt:(int)r :(int)c { char const* const name = [dirArray entryAt:r]->name; char buff[ FILENAME_MAX * 2 + 1 ]; strcat( strcat( strcpy( buff, path ), "/" ), name ); [pb declareTypes:&NXFilenamePboardType num:1 owner:0]; [pb writeType:NXFilenamePboardType data:buff length:strlen(buff)]; } //----------------------------------------------------------------------------- // - tableScroll:allowDragOperationAt:: //----------------------------------------------------------------------------- - (BOOL)tableScroll:(MiscTableScroll*)s allowDragOperationAt:(int)r :(int)c { return (c == ICON_SLOT); } //----------------------------------------------------------------------------- // - tableScroll:imageForDragOperationAt:: //----------------------------------------------------------------------------- - (NXImage*)tableScroll:(MiscTableScroll*)s imageForDragOperationAt:(int)r :(int)c { return [dirArray getImageAt:r scaled:!dragUnscaled]; } //----------------------------------------------------------------------------- // - fillScroll //----------------------------------------------------------------------------- - (void)fillScroll { char buff[ 128 ]; [window disableDisplay]; [scroll renewRows:[dirArray count]]; if ([scroll autoSortRows]) [scroll sortRows]; sprintf( buff, "%d files %lu bytes", [dirArray count], [dirArray totalBytes] ); [countField setStringValue:buff]; [self updateButtons]; [[window reenableDisplay] display]; } //----------------------------------------------------------------------------- // - setPath: //----------------------------------------------------------------------------- - (void)setPath:(char const*)dirname { NXZone* const z = [self zone]; int dirlen; if (path != 0) NXZoneFree( z, path ); if (dirname == 0) dirname = NXHomeDirectory(); if (dirname == 0) dirname = "/"; dirlen = strlen( dirname ) + 1; path = (char*) NXZoneMalloc( z, dirlen ); memcpy( path, dirname, dirlen ); [window setTitleAsFilename:path]; } //----------------------------------------------------------------------------- // - loadDirectory: //----------------------------------------------------------------------------- - (void)loadDirectory:(char const*)dirname { int rc = [dirArray loadPath:dirname showHidden:showHidden]; if (rc != 0) NXRunAlertPanel( "Can't Open", "Cannot open directory, %s\n%d:%s", "OK", 0, 0, path, rc, strerror(rc) ); } //----------------------------------------------------------------------------- // - reload //----------------------------------------------------------------------------- - (void)reload { [scroll abortEditing]; [self loadDirectory:path]; [self fillScroll]; } //----------------------------------------------------------------------------- // - load: //----------------------------------------------------------------------------- - (void)load:(char const*)dirname { [self setPath:dirname]; [self reload]; } //----------------------------------------------------------------------------- // - save: //----------------------------------------------------------------------------- - (id)save:(id)sender { [[MiscExporter commonInstance] exportTableScroll:scroll]; return self; } //----------------------------------------------------------------------------- // - print: //----------------------------------------------------------------------------- - (id)print:(id)sender { [scroll printPSCode:self]; return self; } //----------------------------------------------------------------------------- // - open: //----------------------------------------------------------------------------- - (id)open:(id)sender { if ([scroll hasRowSelection]) { int i; int len; char buff[ FILENAME_MAX + 1 ]; MiscIntList* list = [[MiscIntList allocFromZone:[self zone]] init]; strcpy( buff, path ); len = strlen( buff ); if (len == 0 || buff[len - 1] != '/') { buff[ len++ ] = '/'; buff[ len ] = '\0'; } [scroll selectedRows:list]; for (i = [list count]; i-- > 0; ) { int row = [list intAt:i]; strcpy( buff + len, [dirArray entryAt:row]->name ); if ([self isDir:row]) [[self class] launchDir:buff]; else [[Application workspace] openFile:buff]; } [list free]; } return self; } //----------------------------------------------------------------------------- // - destroy: //----------------------------------------------------------------------------- - (id)destroy:(id)sender { if ([dirArray dirWritable] && [scroll hasRowSelection]) { size_t totalLen = 0; char* fileList; int i; NXZone* const z = [self zone]; MiscIntList* list = [[MiscIntList allocFromZone:z] init]; [scroll selectedRows:list]; for (i = [list count]; i-- > 0; ) { DirEntry const* de = [dirArray entryAt:[list intAt:i]]; if (de->name != 0) totalLen += strlen( de->name ) + 1; } fileList = (char*) NXZoneMalloc( z, totalLen + 1 ); totalLen = 0; for (i = [list count]; i-- > 0; ) { DirEntry const* de = [dirArray entryAt:[list intAt:i]]; char const* const s = de->name; if (s != 0) { int const len = strlen(s); strcpy( fileList + totalLen, s ); totalLen += len; fileList[ totalLen++ ] = '\t'; } } fileList[ totalLen ] = '\0'; [[Application workspace] performFileOperation:WSM_DESTROY_OPERATION source:path destination:"" files:fileList options:""]; NXZoneFree( z, fileList ); [list free]; [self reload]; } return self; } //----------------------------------------------------------------------------- // - rename:to:msg:sys: //----------------------------------------------------------------------------- - (BOOL)rename:(char const*)oldName to:(char const*)newName msg:(char const**)msg sys:(char const**)sys { BOOL ok = NO; char oldPath[ MAXPATHLEN ]; char newPath[ MAXPATHLEN ]; struct stat st; int pathLen; int rc; pathLen = strlen( path ); assert( pathLen > 0 ); memcpy( oldPath, path, pathLen ); memcpy( newPath, path, pathLen ); if (oldPath[ pathLen - 1 ] != '/') { oldPath[ pathLen ] = '/'; newPath[ pathLen ] = '/'; pathLen++; } strcpy( oldPath + pathLen, oldName ); strcpy( newPath + pathLen, newName ); rc = stat( newPath, &st ); if (rc == 0) *msg = "Filename in use."; else if (errno != ENOENT) { *msg = "stat(): "; *sys = strerror( errno ); } else // (rc != 0 && errno == ENOENT) { rc = rename( oldPath, newPath ); if (rc == 0) ok = YES; else { *msg = "rename(): "; *sys = strerror( errno ); } } return ok; } //----------------------------------------------------------------------------- // - textWillEnd: //----------------------------------------------------------------------------- - (BOOL)textWillEnd:(id)sender { BOOL reject = YES; int r,c,len; char const* errMsg; char const* sysErr; char const* oldName; char* newName; r = [scroll clickedRow]; c = [scroll clickedCol]; assert( c == NAME_SLOT ); oldName = [dirArray entryAt:r]->name; if (oldName == 0) oldName = ""; len = [sender textLength]; if (len == 0) errMsg = "Filename cannot be the empty string."; else if (len + 1 + strlen(path) >= MAXPATHLEN) errMsg = "Filename too long."; else { newName = (char*) malloc( len + 1 ); assert( newName != 0 ); [sender getSubstring:newName start:0 length:len + 1]; if (strcmp( newName, oldName ) == 0) reject = NO; // Unchanged, silently ignore. else if (strcmp( newName, ".." ) == 0 || strcmp( newName, "." ) == 0 || strchr( newName, '/' ) != 0) errMsg = "Illegal filename."; else reject = ![self rename:oldName to:newName msg:&errMsg sys:&sysErr]; free( newName ); } if (reject) { if (sysErr == 0) sysErr = ""; NXRunAlertPanel( "Error", "%s%s", "OK", 0, 0, errMsg, sysErr ); [sender setText:oldName]; } return reject; } //----------------------------------------------------------------------------- // - refreshPressed: //----------------------------------------------------------------------------- - (id)refreshPressed:(id)sender { [self reload]; return self; } //----------------------------------------------------------------------------- // - cdPressed: //----------------------------------------------------------------------------- - (id)cdPressed:(id)sender { [scroll abortEditing]; if ([scroll numSelectedRows] == 1) { MiscCoord_P const row = [scroll selectedRow]; if ([self isDir:row]) { char buff[ FILENAME_MAX * 2 + 1 ]; strcat( strcat( strcpy( buff, path ), "/" ), [dirArray entryAt:row]->name ); normalize_path( buff, sizeof(buff) ); [self load:buff]; } } return self; } //----------------------------------------------------------------------------- // - autoSortClick: //----------------------------------------------------------------------------- - (id)autoSortClick:(id)sender { BOOL const switchState = [autoSortSwitch state]; [scroll abortEditing]; if (autoSort != switchState) { DEFAULT_AUTO_SORT = autoSort = switchState; [Defaults set:SORT_DEF bool:DEFAULT_AUTO_SORT]; [scroll setAutoSortRows:switchState]; if (switchState) [scroll sortRows]; } return self; } //----------------------------------------------------------------------------- // - hiddenFilesClick: //----------------------------------------------------------------------------- - (id)hiddenFilesClick:(id)sender { BOOL const switchState = [hiddenFilesSwitch state]; [scroll abortEditing]; if (showHidden != switchState) { DEFAULT_SHOW_HIDDEN = showHidden = switchState; [Defaults set:HIDDEN_DEF bool:DEFAULT_SHOW_HIDDEN]; [self reload]; } return self; } //----------------------------------------------------------------------------- // - highlightClick: //----------------------------------------------------------------------------- - (id)highlightClick:(id)sender { BOOL const switchState = [highlightSwitch state]; [scroll abortEditing]; if (highlightDirs != switchState) { DEFAULT_HIGHLIGHT_DIRS = highlightDirs = switchState; [Defaults set:HLIGHT_DEF bool:DEFAULT_HIGHLIGHT_DIRS]; [scroll display]; } return self; } //----------------------------------------------------------------------------- // - dragUnscaledClick: //----------------------------------------------------------------------------- - (id)dragUnscaledClick:(id)sender { BOOL const switchState = [dragUnscaledSwitch state]; if (dragUnscaled != switchState) { DEFAULT_DRAG_UNSCALED = dragUnscaled = switchState; [Defaults set:UNSCALED_DEF bool:DEFAULT_DRAG_UNSCALED]; } return self; } //----------------------------------------------------------------------------- // - lockClick: //----------------------------------------------------------------------------- - (id)lockClick:(id)sender { int const row = [sender clickedRow]; if ([dirArray canToggleLockAt:row]) { [dirArray setLocked:![dirArray isLockedAt:row] at:row]; if ([sender autoSortRows]) [sender sortRow:row]; } return self; } //----------------------------------------------------------------------------- // - didClick: //----------------------------------------------------------------------------- - (id)didClick:(id)sender { [self updateButtons]; return self; } //----------------------------------------------------------------------------- // - didDoubleClick: //----------------------------------------------------------------------------- - (id)didDoubleClick:(id)sender { [self open:sender]; return self; } //----------------------------------------------------------------------------- // - makeKeyAndOrderFront: //----------------------------------------------------------------------------- - (id)makeKeyAndOrderFront:(id)sender { [window makeKeyAndOrderFront:sender]; return self; } //----------------------------------------------------------------------------- // - windowWillClose: //----------------------------------------------------------------------------- - (id)windowWillClose:(id)sender { [scroll abortEditing]; [OPEN_DIRS removeObject:self]; [NXApp delayedFree:self]; return self; } //----------------------------------------------------------------------------- // - windowDidResize: //----------------------------------------------------------------------------- - (id)windowDidResize:(id)sender { NXRect r; [sender getFrame:&r]; if (r.size.width != DEFAULT_WIN_SIZE.width || r.size.height != DEFAULT_WIN_SIZE.height) { DEFAULT_WIN_SIZE = r.size; [Defaults set:SIZE_DEF size:DEFAULT_WIN_SIZE]; } return self; } //----------------------------------------------------------------------------- // - setDefaultColor: //----------------------------------------------------------------------------- - (void)setDefaultColor:(NXColor)c { DEFAULT_COLOR = c; [Defaults set:COLOR_DEF color:c]; } //----------------------------------------------------------------------------- // - setColors: //----------------------------------------------------------------------------- - (void)setColors:(NXColor)c { [window disableDisplay]; [window setBackgroundColor:c]; [scroll setColor:c]; [window reenableDisplay]; } //----------------------------------------------------------------------------- // - draggingEntered: //----------------------------------------------------------------------------- - (NXDragOperation)draggingEntered:(id<NXDraggingInfo>)sender { return ([sender draggingSourceOperationMask] & NX_DragOperationGeneric); } //----------------------------------------------------------------------------- // - performDragOperation: //----------------------------------------------------------------------------- - (BOOL)performDragOperation:(id<NXDraggingInfo>)sender { [self setDefaultColor: NXReadColorFromPasteboard( [sender draggingPasteboard] )]; [self setColors:DEFAULT_COLOR]; [window display]; return YES; } //----------------------------------------------------------------------------- // - initLazyRow //----------------------------------------------------------------------------- - (void)initLazyRow { Font* font = [scroll font]; NXZone* const z = [self zone]; int i; for (i = 0; i < MAX_SLOT; i++) { id cell = [[scroll colCellPrototype:i] copyFromZone:z]; if ([cell respondsTo:@selector(setFont:)]) [cell setFont:font]; lazyRow[i] = cell; } } //----------------------------------------------------------------------------- // - initDefaults //----------------------------------------------------------------------------- - (void)initDefaults { static BOOL initialized = NO; if (!initialized) { NXRect r; [window getFrame:&r]; DEFAULT_WIN_SIZE = r.size; DEFAULT_FONT = [Defaults getFont:FONT_DEF fallback:[scroll font]]; initialized = YES; } } //----------------------------------------------------------------------------- // - loadDefaults //----------------------------------------------------------------------------- - (void)loadDefaults { NXRect r; char const* s; [window getFrame:&r]; r.size = [Defaults getSize:SIZE_DEF fallback:DEFAULT_WIN_SIZE]; [window placeWindow:&r]; autoSort = DEFAULT_AUTO_SORT; showHidden = DEFAULT_SHOW_HIDDEN; highlightDirs = DEFAULT_HIGHLIGHT_DIRS; dragUnscaled = DEFAULT_DRAG_UNSCALED; [autoSortSwitch setState:autoSort]; [hiddenFilesSwitch setState:showHidden]; [highlightSwitch setState:highlightDirs]; [dragUnscaledSwitch setState:dragUnscaled]; [scroll setAutoSortRows:autoSort]; [scroll setFont:DEFAULT_FONT]; [self setColors:DEFAULT_COLOR]; s = [Defaults getStr:COL_SIZES_DEF fallback:0]; if (s) [scroll setColSizesFromString:s]; s = [Defaults getStr:COL_ORDER_DEF fallback:0]; if (s) [scroll setColOrderFromString:s]; } //----------------------------------------------------------------------------- // - initLockSlot //----------------------------------------------------------------------------- - (void)initLockSlot { id proto = [scroll colCellPrototype:LOCK_SLOT]; [proto setType:NX_SWITCH]; [proto setIconPosition:NX_ICONONLY]; [proto setTarget:self]; [proto setAction:@selector(lockClick:)]; [proto setImage:LOCKED_ICON]; [proto setAltImage:UNLOCKED_ICON]; } //----------------------------------------------------------------------------- // - initNameSlot //----------------------------------------------------------------------------- - (void)initNameSlot { id proto = [scroll colCellPrototype:NAME_SLOT]; [proto setEditable:YES]; [proto setScrollable:YES]; } //----------------------------------------------------------------------------- // - initSlots //----------------------------------------------------------------------------- - (void)initSlots { [self initLockSlot]; [self initNameSlot]; [[scroll colCellPrototype:SIZE_SLOT] setAlignment:NX_RIGHTALIGNED]; [[scroll colCellPrototype:HARDLINKS_SLOT] setAlignment:NX_RIGHTALIGNED]; } //----------------------------------------------------------------------------- // - initWithDir: //----------------------------------------------------------------------------- - (id)initWithDir:(char const*)dirname { char buff[ FILENAME_MAX + 1 ]; NXZone* const z = [self zone]; [super init]; path = 0; dirArray = [[DirArray allocFromZone:z] init]; [[NXBundle bundleForClass:[self class]] getPath:buff forResource:[[self class] name] ofType:"nib"]; [NXApp loadNibFile:buff owner:self withNames:NO fromZone:z]; [window registerForDraggedTypes:&NXColorPboardType count:1]; [self initSlots]; [self initDefaults]; [self loadDefaults]; [self initLazyRow]; [self load:dirname]; [OPEN_DIRS addObject:self]; [self cascade]; [window makeKeyAndOrderFront:self]; return self; } //----------------------------------------------------------------------------- // - init //----------------------------------------------------------------------------- - (id)init { return [self initWithDir:NXHomeDirectory()]; } //----------------------------------------------------------------------------- // - free //----------------------------------------------------------------------------- - (id)free { int i; NXZone* const z = [self zone]; [window setDelegate:0]; [window close]; [window free]; if (path != 0) free( path ); [dirArray free]; for (i = 0; i < MAX_SLOT; i++) [lazyRow[i] free]; [super free]; RETIRE_ZONE(z); return 0; } //----------------------------------------------------------------------------- // - path //----------------------------------------------------------------------------- - (char const*)path { return path; } //----------------------------------------------------------------------------- // + findDir: //----------------------------------------------------------------------------- + (DirWindow*)findDir:(char const*)normalizedPath { if (normalizedPath != 0) { unsigned int i; unsigned int const lim = [OPEN_DIRS count]; for (i = 0; i < lim; i++) { DirWindow* p = (DirWindow*) [OPEN_DIRS objectAt:i]; char const* s = [p path]; if (s != 0 && strcmp( s, normalizedPath ) == 0) return p; } } return 0; } //----------------------------------------------------------------------------- // + launchDir: //----------------------------------------------------------------------------- + (id)launchDir:(char const*)dirname { DirWindow* p = 0; char buff[ FILENAME_MAX + 1 ]; if (dirname == 0) dirname = NXHomeDirectory(); if (dirname == 0) dirname = "/"; strncpy( buff, dirname, sizeof(buff) ); buff[ sizeof(buff) - 1 ] = '\0'; normalize_path( buff, sizeof(buff) ); if ((p = [self findDir:buff]) != 0) [p makeKeyAndOrderFront:self]; else p = [[self allocFromZone:EMPLOY_ZONE()] initWithDir:buff]; return p; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.