home *** CD-ROM | disk | FTP | other *** search
- #import "ShelfView.h"
- #import "IconView.h"
- #import "IconDragView.h"
- #import "compositeBackground.h"
-
- #import <appkit/appkit.h>
- #import <ansi/stdio.h>
- #import <ansi/string.h>
-
-
- #define MONSTERSHELF_FILE ".MonsterShelf"
- #define GRID_ENABLE "GridEnabled"
- #define GRID_VALUE "GridValue"
-
- #define MAX_GRID_VALUE 1024
- #define MIN_GRID_VALUE 32
- #define DEFAULT_GRID_STRING "84"
- #define DEFAULT_GRID_VALUE 84
-
- #define round(x,y) (((x)+(y-1))/(y)*(y))
-
-
- @implementation ShelfView
-
- + initialize
- {
- static NXDefaultsVector defaults = {
- {GRID_ENABLE, "NO"},
- {GRID_VALUE, DEFAULT_GRID_STRING},
- {NULL}
- };
-
- NXRegisterDefaults([NXApp appName], defaults);
-
- return self;
- }
-
-
- - initFrame:(const NXRect *) aFrame
- {
- const char *colorString;
- const char *const types[1] = {NXFilenamePboardType};
- int screenCount;
- char *backgroundString;
- NXScreen *screens;
- unsigned int i;
-
- for (i=0; i<NUM_MOUNT_SLOTS; i++)
- mountSlots[i] = nil;
-
- [super initFrame:aFrame];
- [[self window] setDelegate:self];
- [self registerForDraggedTypes:types count:1];
-
- /*
- * Determine the background color.
- */
- useBGColor = NO;
- [NXApp getScreens:&screens count:&screenCount];
-
- if (screens[0].depth == NX_TwoBitGrayDepth)
- backgroundString = "BWBackgroundColor";
- else
- backgroundString = "BackgroundColor";
-
- colorString = NXGetDefaultValue("NeXT1", backgroundString);
- if (colorString) {
- float r, g, b;
-
- sscanf(colorString, "%f %f %f", &r, &g, &b );
- bgColor = NXConvertRGBAToColor(r, g, b, NX_NOALPHA);
- useBGColor = YES;
- }
-
- /*
- * Now that we've set up our view's appearance, do the rest of the
- * initialization we need to do.
- */
- [self readShelf];
-
- return self;
- }
-
-
- - free
- {
- return [super free];
- }
-
-
- - (BOOL) acceptsFirstMouse
- {
- return YES;
- }
-
-
- - (NXColor) backgroundColor
- {
- if (useBGColor)
- return bgColor;
- else
- return NX_COLORLTGRAY;
- }
-
-
- - (BOOL) isAnyViewAt:(NXPoint) aPoint besides:aView
- {
- unsigned int i;
- unsigned int max = [[self subviews] count];
-
- for (i = 0; i < max; i++) {
- int x = aPoint.x;
- int y = aPoint.y;
- NXRect rect;
-
- if ([[self subviews] objectAt:i] == aView)
- continue;
-
- [[[self subviews] objectAt:i] getFrame:&rect];
- if ((int) rect.origin.x == x && (int) rect.origin.y == y)
- return YES;
- }
-
- return NO;
- }
-
-
- /*
- * Align all of our IconViews on the grid. Take care so that none overlap.
- */
- - (void) alignSubviews
- {
- unsigned int i;
- unsigned int max = [[self subviews] count];
- unsigned int grid = [self gridValue];
-
- if (![self gridEnabled])
- return;
-
- for (i = 0; i < max; i++) {
- id view = [[self subviews] objectAt:i];
- NXRect rect;
- NXPoint candidatePt;
- int count;
-
- if (![view isKindOf:[IconView class]])
- continue;
-
- /*
- * Make the icon the right size, and then compute the new origin.
- */
- [view getFrame:&rect];
- candidatePt.x = round((int) rect.origin.x, grid);
- candidatePt.y = round((int) rect.origin.y, grid);
-
- count = bounds.size.height / grid * bounds.size.width / grid;
- while (count-- > 0 && [self isAnyViewAt:candidatePt besides:view]) {
- candidatePt.x += grid;
- if (candidatePt.x + rect.size.width > bounds.size.width) {
- candidatePt.x = 0;
- candidatePt.y += grid;
- if (candidatePt.y + rect.size.height > bounds.size.height)
- candidatePt.y = grid;
- }
- }
-
- [view sizeTo:grid :grid];
- [view moveTo:candidatePt.x :candidatePt.y];
- }
-
- [self display];
- }
-
-
- - (BOOL) gridEnabled
- {
- const char *enabled = NXGetDefaultValue([NXApp appName], GRID_ENABLE);
- return (enabled && !strcmp(enabled, "YES"));
- }
-
-
- - (void) setGridEnabled:(BOOL) flag
- {
- (void) NXWriteDefault([NXApp appName], GRID_ENABLE, flag ? "YES" : "NO");
-
- [window disableDisplay];
-
- [[self subviews] freeObjects];
- [IconView resetCachedImages];
- [self readShelf];
-
- [[window reenableDisplay] display];
- }
-
-
- - (unsigned int) gridValue
- {
- const char *gridValue = NXGetDefaultValue([NXApp appName], GRID_VALUE);
- if (gridValue)
- return atoi(gridValue);
- else
- return DEFAULT_GRID_VALUE;
- }
-
-
- - setGridValue:(unsigned int) gridValue
- {
- char gridString[20];
-
- if (gridValue == [self gridValue])
- return self;
-
- if (gridValue < MIN_GRID_VALUE)
- gridValue = MIN_GRID_VALUE;
- else if (gridValue > MAX_GRID_VALUE)
- gridValue = MAX_GRID_VALUE;
-
- sprintf(gridString, "%d", gridValue);
- (void) NXWriteDefault([NXApp appName], GRID_VALUE, gridString);
-
- return self;
- }
-
-
- - drawSelf:(const NXRect *) rects :(int) rectCount
- {
- if (useBGColor) {
- NXSetColor(bgColor);
- NXRectFill(rects);
- }
- else
- compositeFromWorkspaceWindow(rects->origin.x, rects->origin.y,
- rects->size.width, rects->size.height);
-
- return self;
- }
-
-
- - deselectAll:sender
- {
- [subviews makeObjectsPerform:@selector(setState:) with:(id) 0];
- return NO;
- }
-
-
- - removeView:aView
- {
- NXRect viewFrame;
-
- [aView getFrame:&viewFrame];
- [aView removeFromSuperview];
- [self display:&viewFrame :1 :NO];
- return self;
- }
-
-
- - addView:aView
- {
- NXRect viewFrame;
-
- [aView getFrame:&viewFrame];
- [self addSubview:aView];
- [self display:&viewFrame :1 :NO];
- return self;
- }
-
-
- - deleteView:aView
- {
- [self removeView:aView];
- [aView free];
- [self writeShelf];
- return self;
- }
-
-
- /*
- * Return true if the point is in the area we use to get rid of views
- */
- - (BOOL) isDeadZone:(NXPoint *) aPoint
- {
- NXRect goodZone = bounds;
- NXInsetRect(&goodZone, 2, 2);
- return !NXMouseInRect(aPoint, &goodZone, NO);
- }
-
-
- - (void) createViewForPath:(const char *) path at:(NXPoint *) point
- {
- id image = [[Application workspace] getIconForFile:path];
- id newView = [IconDragView allocFromZone:[self zone]];
- NXCoord grid = [self gridValue];
- struct stat st;
- NXRect aRect;
- unsigned int i = 0;
- NXPoint viewOrigin;
-
- if (stat(path, &st) < 0)
- return;
-
- if (!point) {
- /*
- * If the caller didn't know where to put the view, stick it one of
- * our default slots.
- */
- while (i < NUM_MOUNT_SLOTS && mountSlots[i])
- ++i;
-
- if (i < NUM_MOUNT_SLOTS)
- mountSlots[i] = newView;
-
- viewOrigin.x = i * [self gridValue];
- viewOrigin.y = [self gridValue];
-
- aRect.origin = viewOrigin;
- }
- else
- aRect.origin = *point;
-
- /*
- * If the grid is on, make sure the size of the view we're about
- * to create is pegged to the grid size.
- */
- if ([self gridEnabled]) {
- aRect.size.width = grid;
- aRect.size.height = grid;
- }
-
- [newView initFrame:&aRect image:image data:path andLength:strlen(path)+1
- useSize:[self gridEnabled]];
-
- [self addSubview:newView];
- [newView getFrame:&aRect];
- [self display:&aRect :1 :NO];
- }
-
-
- static BOOL
- prefix(const char *prefix, const char *string)
- {
- while (*prefix && *string && *prefix == *string) {
- prefix++;
- string++;
- }
- return *prefix == '\0';
- }
-
-
- - (void) removeViewForPath:(const char *) fullPath
- {
- int i = [[self subviews] count];
-
- while (i > 0) {
- char *path;
- unsigned int len;
- id view = [[self subviews] objectAt:i];
- if ([view isKindOf:[IconView class]]) {
- [view getData:(void *) &path andLength:&len];
- if (prefix(fullPath, path)) {
- unsigned int j=0;
-
- while (j < NUM_MOUNT_SLOTS && mountSlots[j] != view)
- ++j;
- if (j < NUM_MOUNT_SLOTS)
- mountSlots[j] = nil;
-
- [self deleteView:view];
- }
- }
- i--;
- }
- }
-
-
- /*
- * Find the right position for the new image, based on the grid and the
- * mouse's location.
- */
- - (NXPoint) viewLocationForContext:(id <NXDraggingInfo>)dragContext
- {
- NXPoint newLoc;
- unsigned int grid = [self gridValue];
- NXPoint imagePt = [dragContext draggedImageLocation];
- NXPoint mousePt = [dragContext draggingLocation];
- NXPoint imageOffset;
-
- [draggedView getImagePoint:&imageOffset andHilitePoint:NULL];
-
- if ([self gridEnabled]) {
- NXRect rect;
- [draggedView getFrame:&rect];
-
- newLoc.x = mousePt.x - ((int) mousePt.x % grid) +
- (grid - rect.size.width) / 2;
- newLoc.y = mousePt.y - (int) mousePt.y % grid;
- }
- else {
- newLoc.x = imagePt.x - imageOffset.x;
- newLoc.y = imagePt.y - imageOffset.y;
- }
- return newLoc;
- }
-
-
- /*
- * Make a ghost image to indicate that we're really a destination.
- */
- - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
- {
- NXPoint newLoc;
- NXSize aSize, *sizePtr = NULL;
-
- if ([self gridEnabled]) {
- aSize.width = [self gridValue];
- aSize.height = aSize.width;
- sizePtr = &aSize;
- }
-
- draggedView = [[IconView allocFromZone:[self zone]]
- initFromDragContext:sender andSize:sizePtr];
-
- if ([self gridEnabled]) {
- newLoc = [self viewLocationForContext:sender];
- [draggedView moveTo:newLoc.x :newLoc.y];
-
- [draggedView setGhost:YES];
- [self addView:draggedView];
- }
-
- return NX_DragOperationAll;
- }
-
-
- /*
- * Move the dragged image, but only do it if we need to (that is, if the
- * mouse moved to a new grid cell).
- */
- - (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
- {
- NXPoint mouseLoc;
- NXPoint newLoc = [self viewLocationForContext:sender];
- NXRect aFrame;
-
- if (![self gridEnabled])
- return NX_DragOperationAll;
-
- /*
- * If the icon was dragged off the edge, hide it somewhere!
- */
- mouseLoc = [sender draggingLocation];
- if ([self isDeadZone:&mouseLoc]) {
- newLoc.x = -100;
- newLoc.y = -100;
- }
-
- [draggedView getFrame:&aFrame];
- if (aFrame.origin.x != newLoc.x || aFrame.origin.y != newLoc.y) {
- [draggedView moveTo:newLoc.x :newLoc.y];
-
- [self display:&aFrame :1 :NO]; /* erase old */
-
- aFrame.origin = newLoc;
- [self display:&aFrame :1 :NO]; /* draw new */
- }
-
- return NX_DragOperationAll;
- }
-
-
- /*
- * Get rid of the resources we used to drag the image around.
- */
- - draggingExited:(id <NXDraggingInfo>)sender
- {
- [self removeView:draggedView];
- [draggedView free];
-
- return self;
- }
-
-
- /*
- * Eat the result...
- */
- - (BOOL) prepareForDragOperation:sender
- {
- NXPoint mouseLoc;
- NXRect aFrame;
- NXPoint newLoc = [self viewLocationForContext:sender];
-
- /*
- * If the dragged item landed in the dead zone, get rid of it. If
- * the dragged item originated with us, we "accept" the image to tell
- * the source to free it.
- */
- mouseLoc = [sender draggingLocation];
- if ([self isDeadZone:&mouseLoc]) {
- [draggedView getFrame:&aFrame];
- [draggedView free];
- [self display:&aFrame :1 :NO];
- return [sender isDraggingSourceLocal];
- }
-
- /*
- * Turn the dragged IconView into an IconDragView that's actually
- * capable of being a drag destination, too.
- */
- [draggedView moveTo:newLoc.x :newLoc.y];
- [self addView:[IconDragView copyIconView:draggedView]];
- [draggedView free];
-
- return YES;
- }
-
-
- - (BOOL) performDragOperation:sender
- {
- return YES;
- }
-
-
- /*
- * Actually write the stuff way down here. It's completely at the end
- * of the operation, so a slow write won't hose the UI.
- */
- - concludeDragOperation:(id <NXDraggingInfo>)sender
- {
- [self writeShelf];
- return self;
- }
-
-
- /*
- * Be a drag source, too
- */
- - setDragView:aView onEvent:(NXEvent *) e withOffset:(NXPoint *) offset atLocation:(const NXPoint *) location
- {
- id pb = [Pasteboard newName:NXDragPboard];
- void *data;
- unsigned int length;
- NXPoint myLoc;
-
- /*
- * Initiate a drag operation. Copy stuff into the pasteboard,
- * then start dragging. To simplify matters elsewhere, we try
- * to make a local dragging operation look just like a non-local
- * one.
- */
- dragSourceView = aView;
- keepSourceOnShelf = (e->flags & NX_ALTERNATEMASK) ? YES : NO;
-
- [aView getData:&data andLength:&length];
- [pb declareTypes:&NXFilenamePboardType num:1 owner:nil];
- [pb writeType:NXFilenamePboardType data:data length:length];
-
- myLoc = *location;
- [aView convertPoint:&myLoc toView:self];
- [self dragImage:[aView image] at:&myLoc
- offset:offset event:e pasteboard:pb
- source:self slideBack:YES];
-
- return self;
- }
-
-
- - draggedImage:(NXImage *)image beganAt:(NXPoint *)screenPoint
- {
- NXRect theFrame;
-
- [dragSourceView getFrame:&theFrame];
- [self display:&theFrame :1 :NO];
- if (!keepSourceOnShelf)
- [self removeView:dragSourceView];
-
- return self;
- }
-
-
- /*
- * A drag operation, with us as the source, finished. If it was an
- * unsuccessful drag then, put the source back! If it was a successful
- * drag, and we weren't the destination, then remove the thing from the
- * shelf.
- */
- - draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint
- deposited:(BOOL)didDeposit
- {
- char *path;
- unsigned int len;
- struct stat st;
-
- /*
- * Check to see if we should keep the source dir on the shelf. We
- * do this if the keepSourceOnShelf flag is set, and if the file
- * under the icon still exists.
- */
- [dragSourceView getData:(void **) &path andLength:&len];
- if (keepSourceOnShelf && path && stat(path, &st) == 0) {
- keepSourceOnShelf = NO;
- return self;
- }
-
- /*
- * The source isn't on the screen, so either get rid of the source, or
- * put it back.
- */
- if (didDeposit)
- [self deleteView:dragSourceView];
- else
- [self addView:dragSourceView];
-
- return self;
- }
-
-
- - (NXDragOperation) draggingSourceOperationMaskForLocal:(BOOL)flag
- {
- return NX_DragOperationAll;
- }
-
-
- /*
- * Open the shelf file.
- */
- - (FILE *) openShelfFor:(char *) how
- {
- char path[MAXPATHLEN];
-
- sprintf(path, "%s/%s", NXHomeDirectory(), MONSTERSHELF_FILE);
- return fopen(path, how);
- }
-
-
- /*
- * Close it.
- */
- - closeShelf:(FILE *) file
- {
- fclose(file);
- return self;
- }
-
-
- /*
- * Read the contents of the shelf in from a file. The file's format consists
- * of lines of the form:
- *
- * x y path
- *
- * where the two numbers x,y specify the origin of the particular view on the
- * shelf, and path specifies the path to the workspace. Somewhat bogusly,
- * we assume the path starts at character 14.
- */
- - readShelf
- {
- FILE *file;
- char line[MAXPATHLEN + 30];
- char *path;
- NXPoint point;
-
- file = [self openShelfFor:"r"];
- if (file == NULL)
- return self;
-
- while (fgets(line, sizeof(line), file)) {
-
- /*
- * Parse the line in the shelf. It's too bad that we can't use
- * sscanf to parse the whole line!
- */
- sscanf(line, "%f %f", &point.x, &point.y);
-
- /* file string starts after second number, char 14 */
- if (strlen(line) > 14) {
- path = line + 14;
- if (rindex(path, '\n') != NULL)
- *rindex(path, '\n') = '\0';
- }
- else
- continue;
-
- /*
- * Make a spot for this guy...
- */
- [self createViewForPath:path at:&point];
- }
-
- [self closeShelf:file];
- if ([self gridEnabled])
- [self alignSubviews];
-
- return self;
- }
-
-
- /*
- * Write the contents of the shelf out to the shelf file.
- */
- - writeShelf
- {
- FILE *file;
- NXRect rect;
- int i;
-
- file = [self openShelfFor:"w"];
- if (file == NULL)
- return self;
-
- for (i = 0; i < [[self subviews] count]; i++) {
- id view = [[self subviews] objectAt:i];
- unsigned int length;
- char *path;
-
- if (![view isKindOf:[IconView class]] || [view isOnRemovableMedia])
- continue;
-
- [view getData:(void **) &path andLength:&length];
- [view getFrame:&rect];
-
- fprintf(file, "%6.0f %6.0f %s\n", rect.origin.x, rect.origin.y, path);
- }
-
- [self closeShelf:file];
- return self;
- }
-
- @end
-