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.4 97/07/08 11:53:33 sunshine Exp $ // $Log: DirWindow.m,v $ // Revision 1.4 97/07/08 11:53:33 sunshine // v31: Worked around Cell's ClipView cache bug. // // Revision 1.3 97/06/10 05:14:32 sunshine // v30: Synchronized with ScrollDir v29.4 for OPENSTEP. // Reorganized to mirror structure of LazyScrollDir counterpart. // Now uses -setTitleAsFilename:. Fixed bug: Wasn't taking directory // sticky-bit into account when determining if file could be renamed. // No longer misleadingly stat()'s file which soft-link points at. // Ditched Message slot. Upgraded naming. Hard-links are now right-aligned. // // Revision 1.2 97/02/05 08:15:52 sunshine // v29: Resync'd with LazyScrollDir (v13). // Added 'writable' flag which prevents cmd-r from working on read-only // directories. //----------------------------------------------------------------------------- #import "DirWindow.h" #import "Defaults.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 <errno.h> #import <libc.h> // getwd() #import <string.h> #import <unistd.h> // getcwd() #import <mach/mach_init.h> // vm_page_size #import <sys/dir.h> #ifdef _POSIX_SOURCE # define GET_CURR_DIR(_BF_,_SZ_) getcwd( _BF_, _SZ_ ) # define GRP_TYPE gid_t # define MAX_GRPS NGROUPS_MAX #else # define GET_CURR_DIR(_BF_,_SZ_) getwd( _BF_ ) # define GRP_TYPE int # define MAX_GRPS NGROUPS #endif enum { ICON_SLOT, NAME_SLOT, LOCK_SLOT, SIZE_SLOT, MODIFIED_SLOT, PERMS_SLOT, OWNER_SLOT, GROUP_SLOT, HARDLINKS_SLOT, SOFTLINK_SLOT, MAX_SLOT }; 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"; typedef struct { char const* shortName; char const* longName; char softLink[ FILENAME_MAX + 1 ]; struct stat status; BOOL isDirectory; BOOL parentWritable; BOOL parentSticky; uid_t effectiveUid; } DirEntry; //----------------------------------------------------------------------------- // 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 ); } } } } //----------------------------------------------------------------------------- // dir_writable //----------------------------------------------------------------------------- static BOOL dir_writable( char const* path ) { BOOL rc = NO; struct stat st; if (stat( path, &st ) == 0) { unsigned int mode = st.st_mode; if ((mode & 0002) != 0 || // "other" ((mode & 0200) != 0 && st.st_uid == geteuid())) // "owner" rc = YES; else if ((mode & 0020) != 0) // "group" { gid_t const gid = getegid(); if (st.st_gid == gid) rc = YES; else { GRP_TYPE groups[ MAX_GRPS ]; int n = getgroups( MAX_GRPS, groups ); while (n-- > 0) if (gid == groups[n]) { rc = YES; break; } } } } return rc; } //----------------------------------------------------------------------------- // dir_sticky //----------------------------------------------------------------------------- static BOOL dir_sticky( char const* path ) { struct stat st; if (stat( path, &st ) == 0) return ((st.st_mode & 01000) != 0); return NO; } //----------------------------------------------------------------------------- // 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* i = [[Application workspace] getIconForFile:de->longName]; 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; [cell setTag:(int)i]; // Unscaled image. i = [i copy]; [i setScalable:YES]; [i setSize:&sz]; [cell setImage:i]; // Scaled image. } //----------------------------------------------------------------------------- // fmt_lock //----------------------------------------------------------------------------- static void fmt_lock( MiscTableScroll* ts, DirEntry const* de, id cell ) { BOOL const unlocked = (de->parentWritable && (!de->parentSticky || de->status.st_uid == de->effectiveUid)); BOOL const flag = (unlocked && strcmp( de->shortName, ".." ) != 0); [cell setState:flag]; [cell setEnabled:flag]; } //----------------------------------------------------------------------------- // fmt_name //----------------------------------------------------------------------------- static void fmt_name( MiscTableScroll* ts, DirEntry const* de, id cell ) { [cell setStringValue:de->shortName]; } //----------------------------------------------------------------------------- // 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 )]; [cell setTag: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 )]; [cell setTag:de->isDirectory]; } //----------------------------------------------------------------------------- // 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 ) { [cell setStringValue:de->softLink]; } //----------------------------------------------------------------------------- // 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:YES]; 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 [scroll tagAt:r:PERMS_SLOT]; } //----------------------------------------------------------------------------- // - updateButtons //----------------------------------------------------------------------------- - (void)updateButtons { BOOL const enable = [scroll numSelectedRows] == 1 && [self isDir:[scroll selectedRow]]; if (enable != [cdButton isEnabled]) [cdButton setEnabled:enable]; } //----------------------------------------------------------------------------- // - setRow:useOwner:color: //----------------------------------------------------------------------------- - (void)setRow:(int)r useOwner:(BOOL)useOwner color:(NXColor)color { int i; for (i = MAX_SLOT; i-- >= 0; ) { id cell = [scroll cellAt:r:i]; if (useOwner && [cell respondsTo:@selector(setUseOwnerBackgroundColor:)]) { [cell setUseOwnerBackgroundColor:YES]; if ([cell respondsTo:@selector(setOwnerBackgroundColor:)]) [cell setOwnerBackgroundColor:color]; } else if ([cell respondsTo:@selector(setBackgroundColor:)]) [cell setBackgroundColor:color]; } } //----------------------------------------------------------------------------- // - highlight:row: //----------------------------------------------------------------------------- - (void)highlight:(BOOL)flag row:(int)r { if (flag) [self setRow:r useOwner:NO color:NX_COLORCYAN]; else [self setRow:r useOwner:YES color:DEFAULT_COLOR]; } //----------------------------------------------------------------------------- // - highlightDirs: //----------------------------------------------------------------------------- - (void)highlightDirs:(BOOL)flag { int i; for (i = [scroll numRows]; i-- > 0; ) if ([self isDir:i]) [self highlight:flag row:i]; } //----------------------------------------------------------------------------- // - freeImages //----------------------------------------------------------------------------- - (void)freeImages { int i; for (i = [scroll numRows]; i-- > 0; ) { id cell = [scroll cellAt:i:ICON_SLOT]; id image = [cell image]; // Scaled image. [cell setImage:0]; [image free]; [(id)[cell tag] free]; // Unscaled image. } } //----------------------------------------------------------------------------- // - tableScroll:fontChangedFrom:to: //----------------------------------------------------------------------------- - (id)tableScroll:(MiscTableScroll*)ts fontChangedFrom:(Font*)oldFont to:(Font*)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 && [[ts cellAt:row:LOCK_SLOT] state] != 0); } //----------------------------------------------------------------------------- // - tableScroll:preparePasteboard:forDragOperationAt:: //----------------------------------------------------------------------------- - (void)tableScroll:(MiscTableScroll*)s preparePasteboard:(Pasteboard*)pb forDragOperationAt:(int)r :(int)c { char const* const name = [[s cellAt:r:NAME_SLOT] stringValue]; 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 (dragUnscaled ? (NXImage*)[[s cellAt:r:c] tag] : 0); } //----------------------------------------------------------------------------- // - addFile: //----------------------------------------------------------------------------- - (void)addFile:(DirEntry const*)de { int r,c; [scroll addRow]; r = [scroll numRows] - 1; for (c = 0; c < MAX_SLOT; c++) format_cell( scroll, de, [scroll cellAt:r:c], c ); if (highlightDirs && de->isDirectory) [self highlight:YES row:r]; } //----------------------------------------------------------------------------- // setupDirFlag: //----------------------------------------------------------------------------- - (void)setupDirFlag:(DirEntry*)de { unsigned int mode = de->status.st_mode; if ((mode & S_IFMT) == S_IFLNK) // soft-link? { int len = readlink( de->longName, de->softLink, FILENAME_MAX ); if (len >= 0) { struct stat st; de->softLink[ len ] = '\0'; if (stat( de->longName, &st ) == 0) mode = st.st_mode; // mode of file linked to } } de->isDirectory = ((mode & S_IFMT) == S_IFDIR); } //----------------------------------------------------------------------------- // - includeFile:length: -- exclude ".", include ".." //----------------------------------------------------------------------------- - (BOOL)includeFile:(char const*)name length:(int)len { BOOL const dotName = (name[0] == '.'); return ((len > 1 || !dotName) && // exclude "." (showHidden || !dotName || (len == 2 && name[1] == '.'))); } //----------------------------------------------------------------------------- // - fillScroll //----------------------------------------------------------------------------- - (void)fillScroll { size_t totalBytes = 0; int dirlen; DIR* dirp; char namebuff[ FILENAME_MAX + 1 ]; dirlen = strlen( path ); strcpy( namebuff, path ); if (dirlen == 0 || namebuff[ dirlen - 1 ] != '/') { namebuff[ dirlen++ ] = '/'; namebuff[ dirlen ] = '\0'; } [window disableDisplay]; [self freeImages]; [scroll empty]; if ((dirp = opendir( path )) == 0) { NXRunAlertPanel( "Can't Read", "Cannot read directory, %s\n%d:%s", "OK", 0, 0, path, errno, strerror(errno) ); } else { struct direct const* dp; uid_t const euid = geteuid(); BOOL const sticky = dir_sticky( path ); writable = dir_writable( path ); while ((dp = readdir( dirp )) != 0) { if ([self includeFile:dp->d_name length:dp->d_namlen]) { DirEntry de; memcpy( namebuff + dirlen, dp->d_name, dp->d_namlen + 1 ); de.shortName = dp->d_name; de.longName = namebuff; de.softLink[0] = '\0'; de.parentWritable = writable; de.parentSticky = sticky; de.effectiveUid = euid; if (lstat( namebuff, &de.status ) == 0) { totalBytes += de.status.st_size; [self setupDirFlag:&de]; [self addFile:&de]; } } } closedir( dirp ); } if ([scroll autoSortRows]) [scroll sortRows]; [scroll sizeToCells]; sprintf( namebuff, "%d files %lu bytes", [scroll numRows], totalBytes ); [countField setStringValue:namebuff]; [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]; } //----------------------------------------------------------------------------- // - load: //----------------------------------------------------------------------------- - (void)load:(char const*)dirname { [self setPath:dirname]; [self fillScroll]; } //----------------------------------------------------------------------------- // - 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, [[scroll cellAt:row:NAME_SLOT] stringValue] ); if ([self isDir:row]) [[self class] launchDir:buff]; else [[Application workspace] openFile:buff]; } [list free]; } return self; } //----------------------------------------------------------------------------- // - destroy: //----------------------------------------------------------------------------- - (id)destroy:(id)sender { if (writable && [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; ) { int const row = [list intAt:i]; char const* const s = [[scroll cellAt:row:NAME_SLOT] stringValue]; if (s != 0) totalLen += strlen(s) + 1; } fileList = (char*) NXZoneMalloc( z, totalLen + 1 ); totalLen = 0; for (i = [list count]; i-- > 0; ) { int row = [list intAt:i]; char const* const s = [[scroll cellAt:row:NAME_SLOT] stringValue]; 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 fillScroll]; } 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 = [[scroll cellAt:r:c] stringValue]; 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 { [scroll abortEditing]; [self fillScroll]; 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 ), "/" ), [[scroll cellAt:row:NAME_SLOT] stringValue] ); 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 fillScroll]; } 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]; [self highlightDirs:highlightDirs]; [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 ([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; } //----------------------------------------------------------------------------- // - 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; [[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 load:dirname]; [OPEN_DIRS addObject:self]; [self cascade]; [window makeKeyAndOrderFront:self]; return self; } //----------------------------------------------------------------------------- // - init //----------------------------------------------------------------------------- - (id)init { return [self initWithDir:NXHomeDirectory()]; } //----------------------------------------------------------------------------- // - free //----------------------------------------------------------------------------- - (id)free { NXZone* const z = [self zone]; [window setDelegate:0]; [window close]; [window disableDisplay]; [self freeImages]; [window free]; if (path != 0) free( path ); [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.