This is ShelfView.m in view mode; [Download] [Up]
#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 BACKGROUNDTILE "BackgroundTile" #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}, {BACKGROUNDTILE, "YES"}, {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", (int)gridValue); (void) NXWriteDefault([NXApp appName], GRID_VALUE, gridString); return self; } - (BOOL) useBackgroundTile { const char *useIt = NXGetDefaultValue([NXApp appName], BACKGROUNDTILE); return (useIt && !strcmp(useIt, "YES")); } - (void) setBackgroundEnabled:(BOOL) flag { (void) NXWriteDefault([NXApp appName], BACKGROUNDTILE, flag ? "YES" : "NO"); [window disableDisplay]; [[self subviews] freeObjects]; [IconView resetCachedImages]; [self readShelf]; [[window reenableDisplay] display]; } - 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] useTile:[self useBackgroundTile]]; [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 andTilePoint:NULL 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 isOn:[self useBackgroundTile]]; 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
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.