ftp.nice.ch/pub/next/tools/dock/LaunchPad.I.bs.tar.gz#/LaunchPad.I.bs/Source/Controller.m

This is Controller.m in view mode; [Download] [Up]

// Controller.m

/*
 * Copyright 1991 RightBrain Software.  All rights reserved.
 *
 * No part of this code may be reproduced in any form, compiled
 * or source code, nor used for any purpose without the express
 * written permission of RightBrain Software.
 * 
 * Entered into the public domain 12/15/93 by RightBrain Software.
 *
 */
#import <libc.h>
#import <strings.h>
#import <time.h>
#import <math.h>
#import <sys/types.h>
#import <sys/stat.h>
#import <streams/streams.h>
#import <objc/objc-load.h>
#import <objc/NXStringTable.h>
#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
#import <defaults/defaults.h>
#import <appkit/publicWraps.h>
#import <appkit/TextField.h>
#import <appkit/View.h>
#import <appkit/Window.h>
#import <appkit/Panel.h>
#import <appkit/Listener.h>
#import <appkit/Speaker.h>
#import <appkit/Application.h>
#import <appkit/ScrollView.h>
#import <appkit/OpenPanel.h>
#import <appkit/SavePanel.h>
#import <appkit/Button.h>
#import <appkit/Text.h>
#import <appkit/ButtonCell.h>
#import <appkit/Font.h>
#import <objc/List.h>

#import "MyPanel.h"
#import "InfoPanel.h"
#import "timebomb.h"

#define NO_VIEWER 0
#define TIFF_VIEWER 1
#define TEXT_VIEWER 2
#define EPS_VIEWER 3

#define ICON_DELTA 54.0

#import "IconView.h"
#import "Controller.h"
#import "CopyIcon.h"

id	theClass;

@implementation Controller


/****************************** appDidInit */
- appDidInit:sender
{
    unsigned int	windowNum;
    const char		*defaultValue;
    id			speaker;
    FILE		*fd, *tmp;
//    int			response;
    NXRect onScreen =  {{874.0,310.0},{172.0,301.0}};
    NXRect offScreen = {{1300.0,-1200.0},{72.0,72.0}};
    
    [NXApp loadNibSection: "InfoPanel.nib" owner: self withNames:YES];
    #if TIME_BOMB_VERSION
	[infoPanel timeBombCheck];
    #endif
    [infoPanel setupVersionText];
    [infoPanel checkRegistration:self];

//    defaults = [Defaults new];
    
  // open panel and promote panel to front
    [NXApp loadNibSection:"Panel.nib" owner:self withNames:NO];
    [dragPanel orderOut:self];
    [dragPanel placeWindow:&offScreen];
    [dragPanel makeKeyAndOrderFront:self];
    [dragPanel orderOut:self];
    [dragPanel placeWindow:&onScreen];
    [dragPanel setDelegate:self];

  // register our app icon window with the workspace
    NXConvertWinNumToGlobal([dragPanel windowNum], &windowNum);
    myWindowNum = windowNum;
    speaker = [NXApp appSpeaker];
    [speaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
    [speaker registerWindow:windowNum
    	     toPort:[[NXApp appListener] listenPort]];
    
    iconList = [[List alloc] initCount:100];
    [[scroller docView] setAutoresizeSubviews:YES];
    iconCount = 0;
    selected = NULL;

    sprintf ( listFileName, "/bin/mkdirs %s/.RightBrain", NXHomeDirectory() );
    system ( listFileName );
    sprintf ( listFileName, "%s/.RightBrain/launchList", NXHomeDirectory() );

    fd = fopen ( listFileName, "r" );
    if ( !fd ) {
	/***
	response = NXRunAlertPanel ( "No File",
	    "Can't find %s",
	    "Continue", "Quit", NULL, listFileName
	);
	if ( response != NX_ALERTDEFAULT ) {
	    [NXApp terminate:self];
	    exit(1);
	}
	***/
	sprintf ( buff, "cp %s %s.bak", listFileName, listFileName );
	system ( buff );
	fd = fopen ( listFileName, "w" );
	if ( fd ) {
	    fprintf ( fd, "#window: 874.00 310.00 172.00 301.00\n" );

	    // check for LockScreen and install if you find it
	    sprintf ( buff,
		"%s/Apps/LockScreen.app/LockScreen", NXHomeDirectory
	    );
	    tmp = fopen ( buff, "r" );
	    if ( tmp ) {
		sprintf ( buff,
		    "X%s/Apps/LockScreen.app\n", NXHomeDirectory
		);
		fprintf ( fd, buff );
		fclose ( tmp );
	    } else {
		tmp = fopen ( "/LocalApps/LockScreen.app/LockScreen", "r" );
		if ( tmp ) {
		    fprintf ( fd, "X/LocalApps/LockScreen.app\n" );
		    fclose ( tmp );
		}
	    }
	    // check for Rulers and install if you find it
	    sprintf ( buff,
		"%s/Apps/Rulers", NXHomeDirectory
	    );
	    tmp = fopen ( buff, "r" );
	    if ( tmp ) {
		sprintf ( buff,
		    "X%s/Apps/Rulers\n", NXHomeDirectory
		);
		fprintf ( fd, buff );
		fclose ( tmp );
	    } else {
		tmp = fopen ( "/LocalApps/Rulers", "r" );
		if ( tmp ) {
		    fprintf ( fd, "X/LocalApps/Rulers\n" );
		    fclose ( tmp );
		}
	    }
	    // check for Portfolio and install if you find it
	    sprintf ( buff,
		"%s/Apps/Portfolio", NXHomeDirectory
	    );
	    tmp = fopen ( buff, "r" );
	    if ( tmp ) {
		sprintf ( buff,
		    "X%s/Apps/Portfolio\n", NXHomeDirectory
		);
		fprintf ( fd, buff );
		fclose ( tmp );
	    } else {
		tmp = fopen ( "/LocalApps/Portfolio", "r" );
		if ( tmp ) {
		    fprintf ( fd, "X/LocalApps/Portfolio\n" );
		    fclose ( tmp );
		}
	    }
	    // check for TypeView and install if you find it
	    sprintf ( buff,
		"%s/Apps/TypeView.app/TypeView", NXHomeDirectory
	    );
	    tmp = fopen ( buff, "r" );
	    if ( tmp ) {
		sprintf ( buff,
		    "X%s/Apps/TypeView.app\n", NXHomeDirectory
		);
		fprintf ( fd, buff );
		fclose ( tmp );
	    } else {
		tmp = fopen ( "/LocalApps/TypeView.app/TypeView", "r" );
		if ( tmp ) {
		    fprintf ( fd, "X/LocalApps/TypeView.app\n" );
		    fclose ( tmp );
		}
	    }
	    // check for PasteUp and install if you find it
	    sprintf ( buff,
		"%s/Apps/PasteUp.app/PasteUp", NXHomeDirectory
	    );
	    tmp = fopen ( buff, "r" );
	    if ( tmp ) {
		sprintf ( buff,
		    "X%s/Apps/PasteUp.app\n", NXHomeDirectory
		);
		fprintf ( fd, buff );
		fclose ( tmp );
	    } else {
		tmp = fopen ( "/LocalApps/PasteUp.app/PasteUp", "r" );
		if ( tmp ) {
		    fprintf ( fd, "X/LocalApps/PasteUp.app\n" );
		    fclose ( tmp );
		}
	    }
	    fclose ( fd );
	}
    } else {
	fclose ( fd );
    }
    [self readFileList:listFileName];
    [dragPanel makeKeyAndOrderFront:self];
    // [dragPanel setFloatingPanel:YES];
    // PSsetwindowlevel(MAXINT-1, [dragPanel windowNum]);
    
    // auto-launch files as appropriate:
    defaultValue = NXGetDefaultValue("LaunchPad", "NXAutoLaunch");
    if ( defaultValue ) {
	if (*defaultValue == 'Y') {
	    [NXApp deactivateSelf];
	    [self launchAll:self];
	    /***
	    if ( iconCount ) {
		[NXApp hide:self];
	    }
	    ***/
	}
    }
    [self setFrom:NULL];

    return self;
}

- appDidUnhide:sender;
{
    [self displayDock:self];
    return self;
}

- appDidBecomeActive:sender;
{
    [self displayDock:self];
    return self;
}


- new:sender
{
    id document;

    document = [[MyPanel alloc] init];
    [NXApp loadNibSection:"Panel.nib" owner:self withNames:NO];
    [document makeKeyAndOrderFront:self];
    return self;
}


- (char *)listFileName;
{
    return listFileName;
}


- defaults;
{
    return defaults;
}


- (float) widestName {
    return widestName;
}


- gimmeScroller;
{
    return scroller;
}


- stringTable;
{
    return stringTable;
}

- launchAll:sender;
{
    int response, idx;
    id file;
    
    if ( sender != self ) {
	response = NXRunAlertPanel (
	    [stringTable valueForStringKey:"launch"],
	    "%s",
	    [stringTable valueForStringKey:"launchAll"],
	    [stringTable valueForStringKey:"launchChecked"],
	    [stringTable valueForStringKey:"cancel"],
	    [stringTable valueForStringKey:"reallyLaunch"]
	);
	if ( response == NX_ALERTDEFAULT ) {
	    for ( idx=0; idx < [iconList count]; idx++ ) {
		file = [iconList objectAt:idx];
		[file openSelf:sender];
	    }
	} else if ( response == NX_ALERTALTERNATE ) {
	    for ( idx=0; idx < [iconList count]; idx++ ) {
		file = [iconList objectAt:idx];
		[file openSelfIfAutoLaunch:sender];
	    }
	} else {
	    return self;
	}
    } else {
	for ( idx=0; idx < [iconList count]; idx++ ) {
	    file = [iconList objectAt:idx];
	    [file openSelfIfAutoLaunch:sender];
	}
    }
    return self;
}


#define OPEN 1
#define SAVE 2
- (const char *)getFile: (int)openOrClose;
{
    const char *types[2];
    const char *mydirectory;
    char inputfile[FILENAME_MAX];
    const char *const *files;
    int result;
    id openpanel;

    if ( openOrClose == SAVE ) {
	openpanel = [OpenPanel new];
    } else {
	openpanel = [SavePanel new];
    }
    if ( !mydirectory ) mydirectory = NXHomeDirectory();
    inputfile[0] = '\0';
    types[0] = "drag";
    types[1] = NULL;
    if ( openOrClose == SAVE ) {
	[openpanel setRequiredFileType: ".drag"];
	result = [openpanel runModalForDirectory:mydirectory
		    file:inputfile types:types];
    } else {
	result = [openpanel runModalForDirectory:mydirectory
		    file:inputfile types:types];
    }
    if ( result ) {
	files = [openpanel filenames];
	sprintf ( inputfile, "%s/%s", [openpanel directory], *files );
	return inputfile;
    }
    return NULL;
}


- selected;
{
    selected = iconView;
    return selected;
}

- setSelected:which;
{
    selected = which;
    iconView = which;
    return self;
}

- fromWhich;
{
    return fromWhich;
}

- setFrom:which;
{
    fromWhich = which;
    return self;
}


- cut:sender;
{
    int response;
    const char *fname, *tab;
    
    if ( iconView && [iconView filename] && strlen([iconView filename]) ) {
	fname = [iconView filename];
	tab = index ( fname, '\t' );
	if ( tab ) {
	    fname = [stringTable valueForStringKey:"fileCollection"];
	} else {
	    fname = (const char *)(rindex([iconView filename], '/') + 1);
	}
	response = NXRunAlertPanel (
	    [stringTable valueForStringKey:"Cut"],
	    "%s %s ?",
	    [stringTable valueForStringKey:"OK"],
	    [stringTable valueForStringKey:"cancel"], NULL,
	    [stringTable valueForStringKey:"remove"],
	    fname
	);
	if ( response != NX_ALERTDEFAULT ) return self;
	
	[self removeFileFromScroller:[iconView filename]];
    } else {
	NXBeep();
    }
    return self;
}


- clearList:sender;
{
    int idx;
    const char *filename;
    
    for ( idx=0; idx < [iconList count]; idx++ ) {
	filename = [[iconList objectAt:idx] filename];
	if ( filename && strlen(filename) ) {
	    [self removeFileFromScroller:filename];
	}
    }
    return self;
}


- openList:sender;
{
    const char *filename;
    
    filename = [self getFile:OPEN];
    if ( filename ) {
	[self clearList:self];
	[self readFileList:filename];
    }
    return self;
}


- addList:sender;
{
    const char *filename;
    
    filename = [self getFile:OPEN];
    if ( filename ) {
	[self readFileList:filename];
    }
    return self;
}


- newList:sender;
{
    int result;
    result = NXRunAlertPanel (
	[stringTable valueForStringKey:"alert"],
	"%s",
	[stringTable valueForStringKey:"OK"],
	[stringTable valueForStringKey:"cancel"], NULL,
	[stringTable valueForStringKey:"reallyToss"]
    );
    if ( result == NX_ALERTDEFAULT ) {
	[self clearList:self];
    }
    return self;
}


- saveList:sender;
{
    const char *filename;
    
    filename = [self getFile:SAVE];
    if ( filename ) {
	[self writeFileList:filename];
    }
    return self;
}


- saveAsList:sender;
{
    const char *filename;
    
    filename = [self getFile:SAVE];
    if ( filename ) {
	[self writeFileList:filename];
    }
    return self;
}


- writeFileList: (const char *)fname;
{
    FILE *fd;
    int idx;
    const char *filename;
    NXRect winRect;

    [dragPanel getPosition:&winRect];
    if ( winRect.size.width <= 0.0 ) {
	return self;
    }
    sprintf ( buff, "cp %s %s.bak", fname, fname );
    system ( buff );
    fd = fopen ( fname, "w" );
    if ( fd ) {
	fprintf ( fd,
	    "#window: %3.2f %3.2f %3.2f %3.2f\n",
	    winRect.origin.x, winRect.origin.y,
	    winRect.size.width, winRect.size.height
	);
	for ( idx=0; idx < [iconList count]; idx++ ) {
	    filename = [[iconList objectAt:idx] filename];
	    if ( [[iconList objectAt:idx] autoLaunch] ) {
		fprintf ( fd, "X" );
	    }
	    if ( filename && strlen(filename) ) {
		fprintf ( fd, "%s\n", filename );
	    }
	}
	fclose ( fd );
    } else {
	sprintf ( buff, "cp %s.bak %s", fname, fname );
	fprintf ( stderr, "couldn't open; restoring %s\n", fname );
	system ( buff );
    }
    return self;
}


- readFileList: (const char *)fname;
{
    FILE *fd;
    char *bytes, dummy[30], *tabChar;
    int morefiles, multiple;
    float f1, f2, f3, f4;
    NXRect pos;

    fd = fopen ( fname, "r" );
    if ( fd ) {
	bytes = fgets ( buff, 1024, fd );
	if ( buff[0] != '#' ) {
	    if ( buff[0] == '/' ) {
		buff[strlen(buff)-1] = '\0';
		tabChar = index ( buff, '\t' );
		multiple = (tabChar != NULL);
		[self addFileToScroller:buff:multiple:0];
		filePath = (char *)[[iconList lastObject] filename];
		[self setFileIconFor:[iconList lastObject]:filePath];
		[dragPanel makeKeyAndOrderFront:self];
	    }
	} else {
	    sscanf ( buff, "%s %f %f %f %f", dummy, &f1, &f2, &f3, &f4 );
	    if ( (f3 > 20.0) && (f4 > 20.0) ) {
		pos.origin.x = f1;
		pos.origin.y = f2;
		pos.size.width = f3;
		pos.size.height = f4;
		[dragPanel setPosition:&pos];
	    }
	    [dragPanel makeKeyAndOrderFront:self];
	}
	morefiles = YES;
	while ( morefiles ) {
	    bytes = fgets ( buff, 1024, fd );
	    if ( bytes ) {
		if ( strncmp(buff,"window",6) ) {
		    if ( buff[0] == '\n' ) {
			continue;
		    }
		    if ( (buff[0] != '/') && (buff[0] != 'X') ) {
			continue;
		    }
		    buff[strlen(buff)-1] = '\0';
		    tabChar = index ( buff, '\t' );
		    multiple = (tabChar != NULL);
		    [self addFileToScroller:buff:multiple:0];
		    filePath = (char *)[[iconList lastObject] filename];
		    [self setFileIconFor:[iconList lastObject]:filePath];
		}
	    } else {
		morefiles = NO;
	    }
	}
	fclose ( fd );
	filePath = NULL; // (char *)[[iconList lastObject] filename];
	iconView = [iconList lastObject];
	selected = iconView;
    }
    return self;
}


- moveIconInScroller: theIcon by:(float)dX : (float)dY;
{
    [theIcon moveBy: dX : dY];
    [[theIcon myCheckBox] moveBy: dX : dY];
    [[theIcon myWideBox] moveBy: dX : dY];
    [[theIcon myLabelText] moveBy: dX : dY];
    [[theIcon myDirText] moveBy: dX : dY];
    return self;
}


- slideIconInScroller: theIcon toLocation:(float)toY;
{
    int idx, ix, newPosition, oldPosition;
    // const char	*nameFromList;
    id		movingIcon;
    NXPoint	releasePoint;
    
    oldPosition = 0;
    for ( idx=0; idx < [iconList count]; idx++ ) {
	if ( [iconList objectAt:idx] == theIcon ) {
	    oldPosition = idx;
	    break;
	}
    }
    releasePoint.x = 24.0;  releasePoint.y = toY;
    // [dragPanel convertScreenToBase:&releasePoint];
    [[dragPanel contentView] convertPoint:&releasePoint
	toView:[scroller docView]
    ];
    newPosition = (int)(releasePoint.y / ICON_DELTA);
    if ( newPosition >= [iconList count] ) {
	newPosition = [iconList count] - 1;
    }
    if ( newPosition < 0) newPosition = 0;
    if ( newPosition != oldPosition ) {
	[self moveIconInScroller: iconView
	    by:0.0:(ICON_DELTA*(newPosition-oldPosition))
	];
	// remove old icon:
	for ( ix=idx+1; ix < [iconList count]; ix++ ) {
	    movingIcon = [iconList objectAt:ix];
	    [self moveIconInScroller: movingIcon by:0.0:ICON_DELTA * -1];
	}
	[iconList removeObjectAt:idx];
	// add new one:
	for ( ix=newPosition; ix < [iconList count]; ix++ ) {
	    movingIcon = [iconList objectAt:ix];
	    [self moveIconInScroller: movingIcon by:0.0:ICON_DELTA];
	}
	[iconList insertObject: iconView at:newPosition];
	[[scroller docView] display];
    }
    [self writeFileList:listFileName];
    return self;
}


- removeFileFromScroller: (const char *)fname;
{
    id tmp, newSelected;
    int idx, location;
    const char *filename;

    location = 0;
    if ( fname == NULL ) {
	// remove the current object
	filename = [iconView filename];
	location = [iconList indexOf:iconView];
    } else {
	for ( idx=0; idx < [iconList count]; idx++ ) {
	    tmp = [iconList objectAt:idx];
	    filename = [tmp filename];
	    if ( !strcmp ( fname, filename ) ) {
		iconView = tmp;
		location = idx;
		break;
	    }
	}
    }
    [iconList removeObject:iconView];
    iconCount--;
    [iconView removeFromSuperview];
    [[[iconView myCheckBox] removeFromSuperview] free];
    [[[iconView myWideBox]  removeFromSuperview] free];
    [[[iconView myLabelText] removeFromSuperview] free];
    [[[iconView myDirText] removeFromSuperview] free];
    for ( idx=location; idx < [iconList count]; idx++ ) {
	tmp = [iconList objectAt:idx];
	[self moveIconInScroller: tmp by:0.0:ICON_DELTA * -1];
    }
    [[scroller docView] sizeBy: 0 : -1 * ICON_DELTA];
    [[scroller docView] display];
    [dragPanel display];
    NXPing();
    [iconView free];
    if ( location >= [iconList count] ) {
	newSelected = [iconList lastObject];
    } else {
	newSelected = [iconList objectAt:location];
    }
    [[NXApp delegate] setSelected:newSelected];
    [newSelected display];
    [self writeFileList:listFileName];
    return self;
}


- addFileToScroller: (char *)fname : (int)multiple : (int)winNum
{
    NXRect iconRect = {{0.0, 0.0}, {54.0, 54.0}};
    NXRect textRect = {{0.0, 0.0}, {1000.0, 16.0}};
    NXRect checkRect = {{0.0, 0.0}, {15.0, 15.0}};
    NXRect scrollRect;
    id	   checkBox, wideBox, Xview, labelText, dirText;
    char  *ptr;
    int    isAuto;
    float  width;

    if ( !fname ) return self;
    if ( !strlen(fname) ) return self;
    
    ptr = fname;
    if ( fname[0] == 'X' ) {
	isAuto = YES;
	ptr++;
    } else {
	isAuto = NO;
    }

    iconView = [[IconView new] initFrame:&iconRect];
    [iconView setFilename:ptr multipleSelected:multiple];
    [iconList addObject:iconView];
    iconCount++;
    Xview = [scroller docView];
    [Xview addSubview:iconView];
    [iconView moveBy: 0 : ICON_DELTA*(iconCount-1)];
    [Xview sizeBy: 0 : ICON_DELTA];
    NXSetRect(&scrollRect, 0.0, ICON_DELTA*(iconCount-1), 54.0, 54.0);
    [Xview scrollRectToVisible:&scrollRect];

    // add a check box
    checkBox = [[Button new] initFrame: &checkRect
	icon:"NXswitch"
	tag:0
	target:iconView
	action:@selector(toggleCheckBox:)
	key:(unsigned short)0
	enabled:YES
    ];
    [checkBox setBordered:YES];
    [checkBox setAltIcon:"NXswitchH"];
    [checkBox setType:NX_TOGGLE];
    if ( isAuto ) {
	[checkBox setState: 1];
	[iconView toggleCheckBox:self];
    }
    [Xview addSubview:checkBox];
    [iconView setMyCheckBox:checkBox];
    [checkBox moveBy: ICON_DELTA + 2 : ICON_DELTA*iconCount - 17.0];

    // add a resize box
    wideBox = [[Button new] initFrame: &checkRect
	icon:"makeWideIcon"
	tag:-1
	target:self
	action:@selector(toggleFromButton:)
	key:(unsigned short)0
	enabled:YES
    ];
    [wideBox setBordered:NO];
    [wideBox setAltIcon:"makeWideIcon"];
    [wideBox setType:NX_TOGGLE];
    [Xview addSubview:wideBox];
    [iconView setMyWideBox:wideBox];
    [wideBox moveBy: ICON_DELTA + 2 : ICON_DELTA*(iconCount-1) + 2.0];

    // add the file name
    labelText = [[Text new]
	initFrame:&textRect
	text:(const char *)[iconView appname]
	alignment:NX_LEFTALIGNED
    ];
    [labelText setBackgroundGray:NX_LTGRAY];
    [labelText setEditable:NO];
    [labelText setSelectable:NO];
    [labelText setOpaque:YES];
    width = [[labelText font] getWidthOf:[iconView appname]];
    if ( width > widestName ) {
	widestName = width;
    }
    [Xview addSubview:labelText];
    [labelText moveBy: ICON_DELTA + 20 : ICON_DELTA*iconCount - 17.0];
    [iconView setMyLabelText:labelText];

    // add the directory name
    sprintf ( buff, "(in %s)", [iconView dirname] );
    dirText = [[Text new]
	initFrame:&textRect
	text:(const char *)[iconView dirname]
	alignment:NX_LEFTALIGNED
    ];
    [dirText setText:buff];
    [dirText setBackgroundGray:NX_LTGRAY];
    [dirText setEditable:NO];
    [dirText setSelectable:NO];
    [dirText setOpaque:YES];
    // add 9 to the width because directory name is shifted right 9 points
    width = [[dirText font] getWidthOf:[iconView dirname]] + 15;
    if ( width > widestName ) {
	widestName = width;
    }
    [Xview addSubview:dirText];
    [dirText moveBy: ICON_DELTA + 30 : ICON_DELTA*iconCount - 32.0];
    [iconView setMyDirText:dirText];
    
    [Xview display];

    return self;
}


- setFileIconFor : theView : (char *)fname;
{
    id		speaker;
    char	*application, *fileType;
    int		anIlk, flag;
    char	*tiff;
    int		okFlag, length;
    FILE	*fd;
    int		rfd;
    char	tmpFile [ 128 ];

  /* ask the Workspace for information about the file */
    if ( fname && strlen(fname) && theView ) {
	speaker = [NXApp appSpeaker];
	[speaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
	[speaker getFileInfoFor: fname
		    app:&application
		    type:&fileType
		    ilk:&anIlk
		    ok:&flag];
	if ( flag ) {
	    [theView setAppOrFile:anIlk];
	} else {
	    [theView setAppOrFile:MISSING_FILE];
	}
	[speaker getFileIconFor: fname
	    TIFF: &tiff
	    TIFFLength: &length
	    ok: &okFlag
	];
	if ( okFlag ) {
	    strcpy ( tmpFile, "/tmp/launchTMP123456.tiff" );
	    NXGetTempFilename(tmpFile, 14);
	    fd = fopen ( tmpFile, "w" );
	    if ( fd ) {
		rfd = fileno ( fd );
		write ( rfd, tiff, length );
		close ( rfd );
		[theView takeIconFromTIFF:tmpFile];
		[theView display];
		unlink ( tmpFile );
	    }
	} else {
	    /*
	     * to get the "?" icon, create a symbolic link then
	     * delete the thing that the link points to, and then
	     * ask for the icon for the link.  What a hack.
	     */
	    fopen ( "/tmp/bogus", "w" );
	    system ( "ln -s /tmp/bogus /tmp/totally" );
	    unlink ( "/tmp/bogus" );
	    [speaker getFileIconFor: "/tmp/totally"
		TIFF: &tiff
		TIFFLength: &length
		ok: &okFlag
	    ];
	    if ( okFlag ) {
		strcpy ( tmpFile, "/tmp/launchTMP123456.tiff" );
		NXGetTempFilename(tmpFile, 14);
		fd = fopen ( tmpFile, "w" );
		if ( fd ) {
		    rfd = fileno ( fd );
		    write ( rfd, tiff, length );
		    close ( rfd );
		    [theView takeIconFromTIFF:tmpFile];
		    // [theView display];
		    unlink ( tmpFile );
		}
	    }
	    unlink ( "/tmp/totally" );
	    [theView setAppOrFile:MISSING_FILE];
	}
    }
    return self;
}


- (int)iconEntered:(int)windowNum at:(double)x :(double)y
    iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
    iconWidth:(double)iconWidth iconHeight:(double)iconHeight
    pathList:(char *)pathList
{
    char	*stringPosition;
    const char	*nameFromList;
    int		length, files=1;
    int		idx;
    
  /* save the file's path for later */
    length = strlen(pathList);
    if (filePathLength <= length) {
	if (filePath) {
	    free(filePath);
	}
	filePath = (char *)malloc(length + 1);
	filePathLength = length;
    }
    strcpy(filePath, pathList);
    stringPosition = filePath;
    
  /* the number of tabs + 1 equals the number of files dragged in */
    files = 0;
    while (stringPosition = index(stringPosition, '\t')) {
      files++;
      stringPosition++;
    }
    if ( files ) {
	multipleFiles = YES;
    } else {
	multipleFiles = NO;
    }
    alreadyHaveIt  = NO;
    nameEntering = filePath;
    if ( nameEntering ) {
	for ( idx=0; idx < [iconList count]; idx++ ) {
	    nameFromList = [[iconList objectAt:idx] filename];
	    if ( !strcmp(nameFromList,nameEntering) ) {
		alreadyHaveIt = YES;
		iconView = [iconList objectAt:idx];
		break;
	    }
	}
	if ( !alreadyHaveIt ) {
	    [self addFileToScroller:
		(char *)nameEntering:multipleFiles:iconWindowNum
	    ];
	    [iconView takeIconFromWindow:iconWindowNum
    			:(float)iconX		:(float)iconY
			:(float)iconWidth	:(float)iconHeight
	    ];
	    [[scroller docView] display];
	    // alreadyHaveIt = YES;
	    // NXDrawGrayBezel([[scroller docView] bounds], NULL);
	}
    }
    return 0;
}


- (int)iconExitedAt:(double)x :(double)y
{
    int idx;
    const char	*nameFromList;
    
    // if ( multipleFiles ) return 0;
    if ( !alreadyHaveIt ) {
	iconView = [iconList lastObject];
	[self removeFileFromScroller:[iconView filename]]; 
	[[scroller docView] display];
	iconView = [iconList lastObject];
    } else {
	for ( idx=0; idx < [iconList count]; idx++ ) {
	    nameFromList = [[iconList objectAt:idx] filename];
	    if ( !strcmp(nameFromList,nameEntering) ) {
		iconView = [iconList objectAt:idx];
		break;
	    }
	}
	/***
	[self perform:@selector(cut:)
	    with:self
	    afterDelay:1
	    cancelPrevious:YES
	];
	***/
	// [self cut:self];
    }
    return 0;
}


- updateIcon:sender;
{
    [self setFileIconFor:
	[iconList lastObject] :
	(char *)[[iconList lastObject] filename]
    ];
    return self;
}


- (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
{
    int idx;
    const char	*nameFromList;
    
    if ( alreadyHaveIt && (fromWhich!=NULL) ) {
	for ( idx=0; idx < [iconList count]; idx++ ) {
	    nameFromList = [[iconList objectAt:idx] filename];
	    if ( !strcmp(nameFromList,nameEntering) ) {
		iconView = [iconList objectAt:idx];
		break;
	    }
	}
	[self slideIconInScroller: iconView toLocation:y];
    } else {
	// add to the list of permanently stored files
	[[scroller docView] display];
	[self perform:@selector(updateIcon:)
		with:self
		afterDelay:1
		cancelPrevious:NO
	];
    }
    [self setSelected:iconView];
    [self writeFileList:listFileName];
    [self setFrom:NULL];

  /* accept the icon */
    *flag = 1;

    return 0;
}



/* instance methods */

- init
{
    char	appDirectory[1024], *lastSlash;

  /*
   * get our full path;  if it contains no slashes, we're being run from the
   * command line, so our current working directory's ok;  why are we worried
   * about the cwd?  well, we have to access our .o and .nib files and we use
   * relative pathnames to do so;  if our path contains a slash, use it to
   * figure out the directory we're executing from (should be from within a
   * file package)
   */
    strcpy(appDirectory, NXArgv[0]);
    lastSlash = rindex(appDirectory, '/');
    if (lastSlash) {
	*lastSlash = '\0';
      /* set our current working directory to be within the file package */
	chdir(appDirectory);
    }
    
    drawingView = emptyBox;
    
    return self;
}
    
- getFileInfo:sender
{
    id		speaker, last;
    char	*application, *fileType;
    int		anIlk, flag;
    char	*tiff;
    int		okFlag, length;
    FILE	*fd;
    int		rfd;
    char	tmpFile [ 128 ];



  /* ask the Workspace for information about the file */
    if ( filePath && strlen(filePath) ) {
	speaker = [NXApp appSpeaker];
	[speaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
	[speaker getFileInfoFor: filePath
		    app:&application
		    type:&fileType
		    ilk:&anIlk
		    ok:&flag];
    
	last = [iconList lastObject];
	if ( !last ) return self;
	[last setAppOrFile : anIlk];
	if ( !([last hasIcon]) ) {
	    [speaker getFileIconFor: filePath
		TIFF: &tiff
		TIFFLength: &length
		ok: &okFlag
	    ];
	    if ( okFlag ) {
		strcpy ( tmpFile, "/tmp/launchTMP123456.tiff" );
		NXGetTempFilename(tmpFile, 14);
		fd = fopen ( tmpFile, "w" );
		if ( fd ) {
		    rfd = fileno ( fd );
		    write ( rfd, tiff, length );
		    close ( rfd );
		    [last takeIconFromTIFF:tmpFile];
		    unlink ( tmpFile );
		}
	    }
	}
    }
    return self;
}

/****************************** infoPanel */
- infoPanel {
    if (!infoPanel) {
	[NXApp loadNibSection: "InfoPanel.nib" owner: self withNames:YES];
	// infoPanel = [InfoPanel new];
    }
    return infoPanel;
}

/****************************** displayInfoPanel */
- displayInfoPanel:sender
{
    if (!infoPanel) {
        [NXApp loadNibSection:"InfoPanel.nib" owner:self withNames:YES];
	// infoPanel = [InfoPanel new];
    }
    [infoPanel makeKeyAndOrderFront:self];
    
    return self;
}

/****************************** displayHelpPanel */
- displayHelpPanel:sender
{
    if (!helpPanel) {
        [NXApp loadNibSection:"HelpPanel.nib" owner:self withNames:NO];
    }
    [helpPanel makeKeyAndOrderFront:NULL];
    
    return self;
}

/****************************** displayPrefPanel */
- displayPrefPanel:sender
{
    if (!prefPanel) {
	[NXApp loadNibSection: "PrefPanel.nib" owner: self];
    }
    [prefPanel makeKeyAndOrderFront:NULL];
    
    return self;
}

/****************************** displayDock */
- displayDock:sender
{
    if ( dragPanel ) {
	[dragPanel makeKeyAndOrderFront:self];
    }
    return self;
}

- toggleFromButton:sender;
{
    [self toggleTextVisible:self];
    return self;
}


/****************************** toggleTextVisible */
- toggleTextVisible:sender
{
    NXSize screenSize;
    NXRect newPos;
    float maxX, deltaX;
    int weirdPosition;
    
    weirdPosition = NO;
    if ( textVisible ) {
	textVisible = NO;
	if ( oldPos.origin.x && oldPos.size.width ) {
	    [dragPanel placeWindow: &oldPos];
	} else {
	    // [dragPanel placeWindow: &onScreen];
	}
	[toggleTextMenu setTitle:
	    [stringTable valueForStringKey:"toggleShow"]
	];
    } else {
	textVisible = YES;
	[dragPanel getFrame: &oldPos];
	newPos.origin.x = oldPos.origin.x;
	newPos.origin.y = oldPos.origin.y;
	newPos.size.height = oldPos.size.height;
	newPos.size.width = oldPos.size.width;
	deltaX = widestName+ICON_DELTA+24+48 - oldPos.size.width;
	newPos.size.width += deltaX;
	[NXApp getScreenSize: &screenSize];
	maxX = newPos.origin.x + newPos.size.width;
	if ( maxX > screenSize.width - 66) {
	    if ( maxX  - deltaX > screenSize.width - 66 ) {
		newPos.origin.x = screenSize.width - 66 - newPos.size.width;
	    } else {
		newPos.origin.x -= deltaX;
	    }
	    weirdPosition = YES;
	}
	if ( newPos.origin.x <= 0.0 ) {
	    newPos.origin.x = 0.0;
	    weirdPosition = YES;
	}
	[dragPanel placeWindow: &newPos];
	[toggleTextMenu setTitle:
	    [stringTable valueForStringKey:"toggleHide"]
	];
	/***
	if ( (sender == self) && weirdPosition ) {
	    [self perform:@selector(toggleTextVisible:)
		with:self
		afterDelay:600
		cancelPrevious:YES
	    ];
	}
	***/
    }
    return self;
}

/****************************** windowDidMove */
- windowDidMove:sender;
{
    NXRect tmpPos;
    if ( textVisible ) {
	[dragPanel getFrame: &tmpPos];
	oldPos.origin.x = tmpPos.origin.x;
	oldPos.origin.y = tmpPos.origin.y;
    }
    [dragPanel rememberPosition:self];
    return self;
}

/****************************** windowDidResize */
- windowDidResize:sender;
{
    [dragPanel rememberPosition:self];
    textVisible = NO;
    [toggleTextMenu setTitle:
	[stringTable valueForStringKey:"toggleShow"]
    ];
    return self;
}



@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.