home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / Utilities / Fiend-1.4.1-src / ShelfView.m < prev    next >
Encoding:
Text File  |  1995-11-30  |  33.5 KB  |  1,375 lines

  1. #import "Fiend.h"
  2. #import "ShelfView.h"
  3. #import "IconView.h"
  4. #import "IconDragView.h"
  5. #import "ProgressView.h"
  6. #import "Controller.h"
  7. #import "Dock.h"
  8. #import "DockMgrView.h"
  9. #import "compositeBackground.h"
  10.  
  11. #import <math.h>
  12. #import <appkit/appkit.h>
  13. #import <3Dkit/3Dkit.h>
  14. #import <ansi/stdio.h>
  15. #import <ansi/string.h>
  16.  
  17. @implementation ShelfView
  18.  
  19. - initFrame:(const NXRect *) aFrame
  20. {
  21.     int                screenCount;
  22.     char            *backgroundString;
  23.     NXScreen        *screens;
  24.     const char        *fileString;
  25.     const char        *colorString;
  26.     const char         *const types[1] = {NXFilenamePboardType};
  27.  
  28.     gridValue = atoi(NXGetDefaultValue([NXApp appName], SGRID_VALUE));
  29.  
  30.     [super initFrame:aFrame];
  31.     [[self window] setDelegate:self];
  32.     [self registerForDraggedTypes:types count:1];
  33.  
  34.     useBGColor = NO;
  35.     [NXApp getScreens:&screens count:&screenCount];
  36.  
  37.     if (screens[0].depth == NX_TwoBitGrayDepth)
  38.         backgroundString = "BWBackgroundColor";
  39.     else
  40.         backgroundString = "BackgroundColor";
  41.  
  42.     colorString = NXGetDefaultValue("NeXT1", backgroundString);
  43.     if (colorString) {
  44.         float    r, g, b;
  45.         sscanf(colorString, "%f %f %f", &r, &g, &b );
  46.         bgColor = NXConvertRGBAToColor(r, g, b, NX_NOALPHA);
  47.         useBGColor = YES;
  48.     }
  49.  
  50.     fiendSound = [Sound findSoundFor:"Fiend"];
  51.     destroySound = [Sound findSoundFor:"Destroy"];
  52.  
  53. //    [window disableFlushWindow];
  54.     [self readShelf:YES];
  55.     
  56.     tileImage = -1;
  57.     fileString = NXGetDefaultValue([NXApp appName], IMAGE_NAME);
  58.     [self setImageFileName:fileString];
  59.     [self setTileImage:!strcmp(NXGetDefaultValue([NXApp appName], TILE_IMAGE), "YES")];
  60. //    [[window reenableFlushWindow] flushWindowIfNeeded];
  61.  
  62.     return self;
  63. }
  64.  
  65.  
  66. - free
  67. {
  68.     if (theImage != nil)
  69.         [theImage free];
  70.     return [super free];
  71. }
  72.  
  73.  
  74. - (BOOL) acceptsFirstMouse
  75. {
  76.     return YES;
  77. }
  78.  
  79.  
  80. - (NXColor) backgroundColor
  81. {
  82.     if (useBGColor)
  83.         return bgColor;
  84.     else
  85.         return NX_COLORLTGRAY;
  86. }
  87.  
  88.  
  89. - (BOOL)isAnyViewAt:(NXPoint)aPoint besides:aView useSlimMargin:(BOOL)flag
  90. {
  91.     id            *testView;
  92.     int            i;
  93.     NXRect        rect;
  94.     int            count = [subviews count];
  95.     id            *viewListPtr = (count > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  96.     NXRect        interRect = {{aPoint.x, aPoint.y}, {gridValue, gridValue}};
  97.  
  98.     if (!flag)
  99.         NXInsetRect(&interRect, 0.35*gridValue, 0.35*gridValue);
  100.     for (i = 0, testView = viewListPtr;  i < count;  i++, testView++) {
  101.         if (*testView == aView)
  102.             continue;
  103.         [*testView getFrame:&rect];
  104.         if (NXIntersectsRect(&rect, &interRect))
  105.             return YES;
  106.     }
  107.  
  108.     return NO;
  109. }
  110.  
  111.  
  112. - (void)alignSubviews
  113. {
  114.     id        *view;
  115.     int        i;
  116.     int        viewCount = [subviews count];
  117.     id        *viewListPtr = (viewCount > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  118.  
  119.     [window disableFlushWindow];
  120.     for (i = 0, view = viewListPtr;  i < viewCount;  i++, view++) {
  121.         NXRect    rect;
  122.         NXPoint candidatePt;
  123.         int    count;
  124.  
  125.         if (![*view isKindOf:[IconView class]] || ![*view state])
  126.             continue;
  127.  
  128.         [*view getFrame:&rect];
  129.         candidatePt.x = gridValue * (int)(rect.origin.x/gridValue);
  130.         candidatePt.y = gridValue * (int)(rect.origin.y/gridValue);
  131.  
  132.         count = bounds.size.height * bounds.size.width / gridValue;
  133.         while (count-- > 0 && [self isAnyViewAt:candidatePt besides:*view useSlimMargin:NO]) {
  134.             candidatePt.x -= gridValue;
  135.             if (candidatePt.x < 0) {
  136.                 candidatePt.x = gridValue;
  137.                 candidatePt.y -= gridValue;
  138.                 if (candidatePt.y < gridValue)    {
  139.                     candidatePt.y = gridValue;
  140.                     candidatePt.x -= gridValue;
  141.                 }
  142.                 
  143.             }
  144.         }
  145.  
  146.         [*view moveTo:candidatePt.x :candidatePt.y];
  147.         [*view setState:NO];
  148.     }
  149.  
  150.     [self display];
  151.     [[window reenableFlushWindow] flushWindowIfNeeded];
  152.     [self writeShelf];
  153. }
  154.  
  155.  
  156. - (BOOL)tileImage
  157. {
  158.     return tileImage;
  159. }
  160.  
  161. - setTileImage:(BOOL)flag
  162. {
  163.     NXSize        imageSize;
  164.  
  165.     if (flag != tileImage)    {
  166.         tileImage = flag;
  167.         if (theImage)    {
  168.             [theImage getSize:&imageSize];
  169.             if (!tileImage && (NX_WIDTH(&frame) != imageSize.width || NX_HEIGHT(&frame) != imageSize.height))
  170.                 [theImage setSize:&frame.size];
  171.             else if (tileImage && (imageSize.height != origImageSize.height || imageSize.width != origImageSize.width))
  172.                 [theImage setSize:&origImageSize];
  173.         }
  174.         [self update];
  175.     }
  176.  
  177.     return self;
  178. }
  179.  
  180. - (char *)getImageFileName
  181. {
  182.     return imageFileName;
  183. }
  184.  
  185. - setImageFileName:(const char *)theName
  186. {
  187.     id            image;
  188.     id            repList;
  189.     int            i;
  190.     NXImageRep    *rep;
  191.  
  192.     if (!theName || !strlen(theName))    {
  193.         theImage = nil;
  194.         imageFileName[0] = '\0';
  195.         NXWriteDefault([NXApp appName], IMAGE_NAME, "");
  196.     }
  197.     else if (strcmp(imageFileName, theName))    {
  198.         image = [[NXImage allocFromZone:[self zone]] initFromFile:theName];
  199.         strcpy(imageFileName, theName);
  200. //        NXWriteDefault([NXApp appName], IMAGE_NAME, imageFileName);
  201.  
  202.         if (image != nil)    {
  203.             if ([image lockFocus])    {
  204.                 [image unlockFocus];
  205.                 if (theImage != nil)
  206.                     [theImage free];
  207.                 theImage = image;
  208.             }
  209.             else
  210.                 return nil;
  211.         }
  212.         else
  213.             return nil;
  214.  
  215.         repList = [theImage representationList];
  216.         for(i = 0; i < [repList count]; i++)    {
  217.             rep = [repList objectAt:i];
  218.             if ([rep isKindOf:[N3DRIBImageRep class]])    {
  219.                 [(N3DRIBImageRep *)rep setSurfaceType:N3D_FacetedSolids];
  220.                 break;
  221.             }
  222.         }
  223.         [theImage setScalable:YES];
  224.         [theImage getSize:&origImageSize];
  225.         if (!tileImage)
  226.             [theImage setSize:&frame.size];
  227.     }
  228.  
  229.     [self update];
  230.  
  231.     return self;
  232. }
  233.  
  234. - (BOOL) gridEnabled
  235. {
  236.     return !strcmp(NXGetDefaultValue([NXApp appName], GRID_ENABLE), "YES");
  237. }
  238.  
  239.  
  240. - (void)setGridEnabled:(BOOL) flag
  241. {
  242.     (void) NXWriteDefault([NXApp appName], GRID_ENABLE, flag ? "YES" : "NO");
  243. }
  244.  
  245.  
  246. - (int)gridValue
  247. {
  248.     return gridValue;
  249. }
  250.  
  251.  
  252. - setGridValue:(int)aValue
  253. {
  254.     if (aValue == gridValue)
  255.         return self;
  256.  
  257.     gridValue = aValue;
  258.  
  259.     if (gridValue < MIN_GRID_VALUE)
  260.         gridValue = MIN_GRID_VALUE;
  261.     else if (gridValue > MAX_GRID_VALUE)
  262.         gridValue = MAX_GRID_VALUE;
  263.  
  264. //    gridChanged = YES;
  265.     [window disableFlushWindow];
  266.     [[self subviews] freeObjects];
  267.     [IconView resetCachedShelfImages];
  268.     [[self readShelf:NO] display];
  269.     [[window reenableFlushWindow] flushWindowIfNeeded];
  270.  
  271.     return self;
  272. }
  273.  
  274. - drawSelf:(const NXRect *) rects :(int) rectCount
  275. {
  276.     int        i;
  277.  
  278.     int        j;
  279.     int        rectIndex;
  280.     int        vertCount;
  281.     int        horizCount;
  282.     NXPoint    thePoint;
  283.     NXRect    interRect;
  284.     BOOL    compositeShelf = !strcmp(NXGetDefaultValue([NXApp appName], CPST_SHLF), "YES");
  285.     NXRect    theRect = {{0.0, 0.0}, {0.0, 0.0}};
  286.  
  287.     if (theImage != nil)    {
  288.         NXSetColor(bgColor);
  289.         NXRectFill(rects);
  290.         [theImage getSize:&theRect.size];
  291.         vertCount = NX_HEIGHT(&frame)/NX_HEIGHT(&theRect);
  292.         horizCount = NX_WIDTH(&frame)/NX_WIDTH(&theRect);
  293.         rectIndex = (rectCount == 3) ? 1 : 0;
  294.         for(rectIndex = 0; rectIndex < rectCount; rectIndex++)    {
  295.             for(i = 0; i < horizCount+1; i++)    {
  296.                 for(j = 0; j < vertCount+1; j++)    {
  297.                     NXSetRect(&interRect, NX_WIDTH(&theRect)*i, NX_HEIGHT(&theRect)*j,
  298.                               NX_WIDTH(&theRect), NX_HEIGHT(&theRect));
  299.                     if (NXIntersectionRect(&rects[rectIndex], &interRect))    {
  300.                         thePoint = interRect.origin;
  301.                         NX_X(&interRect) = fmod(NX_X(&interRect), NX_WIDTH(&theRect));
  302.                         NX_Y(&interRect) = fmod(NX_Y(&interRect), NX_HEIGHT(&theRect));
  303.                         [theImage composite:NX_SOVER fromRect:&interRect toPoint:&thePoint];
  304.                     }
  305.                 }
  306.             }
  307.         }
  308.     }
  309.     else if (useBGColor && !compositeShelf) {
  310.         NXSetColor(bgColor);
  311.         NXRectFill(rects);
  312.     }
  313.     else
  314.         compositeFromWorkspaceWindow(rects->origin.x, rects->origin.y,
  315.                                      rects->size.width, rects->size.height);
  316.  
  317.     return self;
  318. }
  319.  
  320.  
  321. - deselectAll:sender
  322. {
  323.     id        *view;
  324.     int        i;
  325.     int        count = [subviews count];
  326.     id        *viewListPtr = (count > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  327.  
  328.     [window disableFlushWindow];
  329.     for(i = 0, view = viewListPtr; i < count; i++, view++)    {
  330.         if ([*view state])
  331.             [*view setState:NO];
  332.     }
  333.     [[window reenableFlushWindow] flushWindowIfNeeded];
  334.  
  335.     [[NXApp delegate] updateMenus];
  336.     return self;
  337. }
  338.  
  339.  
  340. - removeView:aView
  341. {
  342.     NXRect    viewFrame;
  343.  
  344.     [aView getFrame:&viewFrame];
  345.     [aView removeFromSuperview];
  346.     [self display:&viewFrame :1 :NO];
  347.     return self;
  348. }
  349.  
  350.  
  351. - addView:aView
  352. {
  353.     NXRect    viewFrame;
  354.  
  355.     [aView getFrame:&viewFrame];
  356.     [self addSubview:aView];
  357.     [self display:&viewFrame :1 :NO];
  358.     return self;
  359. }
  360.  
  361.  
  362. - deleteView:aView
  363. {
  364.     [self removeView:aView];
  365.     [aView free];
  366.     return self;
  367. }
  368.  
  369.  
  370. /*
  371.  *  Return true if the point is in the area we use to get rid of views
  372.  */
  373. - (BOOL) isDeadZone:(NXPoint *) aPoint
  374. {
  375.     NXRect    goodZone = bounds;
  376.     NXInsetRect(&goodZone, 2, 2);
  377.     return !NXMouseInRect(aPoint, &goodZone, NO);
  378. }
  379.  
  380.  
  381. - (void)createViewForPath:(const char *)path withImage:(NXImage *)image at:(NXPoint *)point
  382. {
  383.     id                newImage;
  384.     id                newView;
  385.     BOOL            drawThumbnail = !strcmp(NXGetDefaultValue([NXApp appName], DRAW_TNAIL), "YES");
  386.     NXRect            aRect = {{0.0, 0.0}, {gridValue, gridValue}};
  387.  
  388.     if (image == nil)
  389.         newImage = [IconView getImageForPath:path fileIcon:!drawThumbnail zone:[self zone]];
  390.     else
  391.         newImage = [image copyFromZone:[self zone]];
  392.     newView = [[IconDragView allocFromZone:[self zone]] initFrame:&aRect image:newImage
  393.                data:path andLength:strlen(path)+1 useSize:YES onDock:NO];
  394.  
  395.     aRect.origin = *point;
  396.     if (NX_MAXX(&aRect) > NX_WIDTH(&frame))
  397.         NX_X(&aRect) = fmod(point->x, NX_WIDTH(&frame)-gridValue);
  398.     if (NX_MAXY(&aRect) > NX_HEIGHT(&frame))
  399.         NX_Y(&aRect) = fmod(point->y, NX_HEIGHT(&frame)-gridValue);
  400.     [[newView setFrame:&aRect] display];
  401.  
  402.     [self addSubview:newView];
  403.     [newView getFrame:&aRect];
  404.     [self display:&aRect :1 :NO];
  405. }
  406.  
  407.  
  408. - (NXPoint)viewLocationForContext:(id <NXDraggingInfo>)dragContext
  409. {
  410.     NXPoint        newLoc;
  411.     NXPoint        imageOffset;
  412.     NXPoint        imagePt = [dragContext draggedImageLocation];
  413.     NXPoint        mousePt = [dragContext draggingLocation];
  414.     BOOL        gridEnabled = !strcmp(NXGetDefaultValue([NXApp appName], GRID_ENABLE), "YES");
  415.  
  416.     [draggedView getImagePoint:&imageOffset andHilitePoint:NULL];
  417.  
  418.     if (gridEnabled) {
  419.         NXRect    rect;
  420.         [draggedView getFrame:&rect];
  421.  
  422.         newLoc.x = mousePt.x - ((int) mousePt.x % gridValue) +
  423.             (gridValue - rect.size.width) / 2;
  424.         newLoc.y = mousePt.y - (int) mousePt.y % gridValue;
  425.     }
  426.     else {
  427.         newLoc.x = imagePt.x - imageOffset.x;
  428.         newLoc.y = imagePt.y - imageOffset.y;
  429.     }
  430.     return newLoc;
  431. }
  432.  
  433.  
  434. - createDraggedView:(id <NXDraggingInfo>)sender
  435. {
  436.     NXSize    aSize;
  437.     NXPoint    newLoc;
  438.     BOOL    gridEnabled = !strcmp(NXGetDefaultValue([NXApp appName], GRID_ENABLE), "YES");
  439.  
  440.     aSize.width = [self gridValue];
  441.     aSize.height = aSize.width;
  442.     draggedView = [[IconView allocFromZone:[self zone]]
  443.                    initFromDragContext:sender andSize:&aSize onDock:NO];
  444.  
  445.     newLoc = [self viewLocationForContext:sender];
  446.     [draggedView moveTo:newLoc.x :newLoc.y];
  447.  
  448.     [draggedView setGhost:YES];
  449.     if (gridEnabled)
  450.         [self addView:draggedView];
  451.  
  452.     return self;
  453. }
  454.  
  455. - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
  456. {
  457.     id                source = [sender draggingSource];
  458.     BOOL            localSrc = [sender isDraggingSourceLocal];
  459.     BOOL            strict = !strcmp(NXGetDefaultValue([NXApp appName], SSTRICT_COPY), "YES");
  460.     NXDragOperation    mask = [sender draggingSourceOperationMask];
  461.  
  462.     if (mask == NX_DragOperationPrivate || (localSrc && [source isKindOf:[IconView class]]) ||
  463.         (!localSrc &&
  464.          ((strict && mask != NX_DragOperationCopy) ||
  465.           (!strict && !(mask & NX_DragOperationCopy)))))    {
  466.  
  467.         draggedView = nil;
  468.         return NX_DragOperationNone;
  469.     }
  470.  
  471.     [self createDraggedView:sender];
  472.     return (mask == NX_DragOperationCopy) ? NX_DragOperationCopy :
  473.         ((localSrc)?NX_DragOperationGeneric:NX_DragOperationCopy);
  474. }
  475.  
  476.  
  477. - (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
  478. {
  479.     NXRect            aFrame;
  480.     NXPoint            newLoc;
  481.     id                source = [sender draggingSource];
  482.     BOOL            localSrc = [sender isDraggingSourceLocal];
  483.     NXDragOperation    mask = [sender draggingSourceOperationMask];
  484.     BOOL            gridEnabled = !strcmp(NXGetDefaultValue([NXApp appName], GRID_ENABLE), "YES");
  485.     BOOL            strict = !strcmp(NXGetDefaultValue([NXApp appName], SSTRICT_COPY), "YES");
  486.  
  487.     if (mask == NX_DragOperationPrivate || (localSrc && [source isKindOf:[IconView class]]) ||
  488.         (!localSrc &&
  489.          ((strict && mask != NX_DragOperationCopy) ||
  490.           (!strict && !(mask & NX_DragOperationCopy)))))    {
  491.  
  492.         if (draggedView)    {
  493.             [self removeView:draggedView];
  494.             [draggedView free];
  495.             draggedView = nil;
  496.         }
  497.         return NX_DragOperationNone;
  498.     }
  499.  
  500.     if (!draggedView)
  501.         [self createDraggedView:sender];
  502.     newLoc = [self viewLocationForContext:sender];
  503.  
  504.     if (gridEnabled)    {
  505.         [draggedView getFrame:&aFrame];
  506.         if (aFrame.origin.x != newLoc.x || aFrame.origin.y != newLoc.y) {
  507.             [draggedView moveTo:newLoc.x :newLoc.y];
  508.             [self display:&aFrame :1 :NO];        /* erase old */
  509.             aFrame.origin = newLoc;
  510.             [self display:&aFrame :1 :NO];        /* draw new */
  511.         }
  512.     }
  513.  
  514.     if (localSrc)
  515.         [source setKeepDraggedIcons:(mask == NX_DragOperationCopy)];
  516.     return (mask == NX_DragOperationCopy) ? NX_DragOperationCopy :
  517.         ((localSrc)?NX_DragOperationGeneric:NX_DragOperationCopy);
  518. }
  519.  
  520.  
  521. - (int)getHorizLimit:(char *)dragString sep:(char *)sep ctrl:(BOOL)ctrl
  522. {
  523.     char    *ptr;
  524.     int        horizLimit;
  525.  
  526.     horizLimit = 10000;
  527.     if (strlen(dragString) && !ctrl)    {
  528.         horizLimit = 0;
  529.         ptr = dragString;
  530.         while(*ptr)    {
  531.             if (*ptr == *sep)
  532.                 horizLimit++;
  533.             ptr++;
  534.         }
  535.         horizLimit = (int)sqrt((float)horizLimit);
  536.     }
  537.     return horizLimit;
  538. }
  539.  
  540. /*
  541.  *  Get rid of the resources we used to drag the image around.
  542.  */
  543. - draggingExited:(id <NXDraggingInfo>)sender
  544. {
  545.     BOOL    gridEnabled = !strcmp(NXGetDefaultValue([NXApp appName], GRID_ENABLE), "YES");
  546.  
  547.     if ([sender draggingSource] != self &&
  548.         !([sender draggingSourceOperationMask] & NX_DragOperationCopy))    {
  549.         if (draggedView)    {
  550.             [self removeView:draggedView];
  551.             [draggedView free];
  552.             draggedView = nil;
  553.         }
  554.         return self;
  555.     }
  556.  
  557.     if (gridEnabled)
  558.         [self removeView:draggedView];
  559.     [draggedView free];
  560.  
  561.     return self;
  562. }
  563.  
  564.  
  565. /*
  566.  *  Eat the result...
  567.  */
  568. - (BOOL)prepareForDragOperation:sender
  569. {
  570. //    NXLogError("shelf prepareForDragOperation");
  571.     return YES;
  572. }
  573.  
  574.  
  575. - (BOOL)performDragOperation:sender
  576. {
  577.     NXPoint    mouseLoc;
  578.     NXPoint    newLoc;
  579.  
  580.     newLoc = [self viewLocationForContext:sender];
  581.     mouseLoc = [sender draggingLocation];
  582.     if ([self isDeadZone:&mouseLoc]) {
  583.         [self removeView:draggedView];
  584.         [draggedView free];
  585.         draggedView = nil;
  586.         return NO;
  587.     }
  588.  
  589.     [draggedView moveTo:newLoc.x :newLoc.y];
  590.     return YES;
  591. }
  592.  
  593.  
  594. - selectViewsInRect:(NXRect *)theRect deselect:(BOOL)flag
  595. {
  596.     id        *theView;
  597.     int        i;
  598.     NXRect    viewRect;
  599.     NXPoint    thePoint;
  600.     BOOL    found = NO;
  601.     int        count = [subviews count];
  602.     id        *viewListPtr = (count > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  603.  
  604.     [window disableFlushWindow];
  605.     for(i = 0, theView = viewListPtr; i < count; i++, theView++)    {
  606.         if (*theView == nil || ![*theView isKindOf:[IconView class]])
  607.             continue;
  608.         [*theView getFrame:&viewRect];
  609.         thePoint.x = NX_MIDX(&viewRect);
  610.         thePoint.y = NX_MIDY(&viewRect);
  611.         if (NXIntersectsRect(&viewRect, theRect))    {
  612.             if (!flag)
  613.                 [*theView setState:![*theView state]];
  614.             else
  615.                 [*theView setState:YES];
  616.             found = YES;
  617.         }
  618.         else if (flag)
  619.             [*theView setState:NO];
  620.     }
  621.     [[window reenableFlushWindow] flushWindowIfNeeded];
  622.  
  623.     if (found)    {
  624.         [NXApp unhide:self];
  625.         [[NXApp delegate] updateMenus];
  626.     }
  627.     return self;
  628. }
  629.  
  630. - nukeViewsInRect:(NXRect *)theRect
  631. {
  632.     id        *theView;
  633.     id        *killListPtr;
  634.     int        i;
  635.     NXRect    viewRect;
  636.     NXPoint    thePoint;
  637.     int        count = [subviews count];
  638.     id        killList = [[List allocFromZone:[self zone]] init];
  639.     id        *viewListPtr = (count > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  640.     BOOL    useSound = !strcmp(NXGetDefaultValue([NXApp appName], USE_SOUND), "YES");
  641.  
  642.     [window disableFlushWindow];
  643.     for(i = 0, theView = viewListPtr; i < count; i++, theView++)    {
  644.         if (*theView == nil || ![*theView isKindOf:[IconView class]])
  645.             continue;
  646.         [*theView getFrame:&viewRect];
  647.         thePoint.x = NX_MIDX(&viewRect);
  648.         thePoint.y = NX_MIDY(&viewRect);
  649.         if (NXIntersectsRect(&viewRect, theRect))
  650.             [killList addObject:*theView];
  651.     }
  652.     count = [killList count];
  653.     killListPtr = (count > 0) ? NX_ADDRESS(killList) : (id *)NULL;
  654.     for(i = 0, theView = killListPtr; i < count; i++, theView++)
  655.         [self deleteView:*theView];
  656.     [[window reenableFlushWindow] flushWindowIfNeeded];
  657.  
  658.     [killList free];
  659.     if (count)    {
  660.         if (useSound)
  661.             [destroySound play:self];
  662.         [[NXApp delegate] updateMenus];
  663.         [self writeShelf];
  664.     }
  665.     return self;
  666. }
  667.  
  668. - deleteSelectedCells
  669. {
  670.     id        *view;
  671.     int        i;
  672.     id        *cutListPtr = (id *)NULL;
  673.     int        count = [subviews count];
  674.     id        cutList = [[List allocFromZone:[self zone]] init];
  675.     id        *viewListPtr = (count > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  676.  
  677.     [window disableFlushWindow];
  678.     for(i = 0, view = viewListPtr; i < count; i++, view++)    {
  679.         if ([*view state])
  680.             [cutList addObject:*view];
  681.     }
  682.     count = [cutList count];
  683.     cutListPtr = (count > 0) ? NX_ADDRESS(cutList) : (id *)NULL;
  684.     for(i = 0, view = cutListPtr; i < count; i++, view++)
  685.         [self deleteView:*view];
  686.  
  687.     [[window reenableFlushWindow] flushWindowIfNeeded];
  688.     [cutList free];
  689.     if (count)    {
  690.         [[NXApp delegate] updateMenus];
  691.         [self writeShelf];
  692.     }
  693.  
  694.     return self;
  695. }
  696.  
  697. - mouseDown:(NXEvent *)event
  698. {
  699.     int            selCnt;
  700.     int            savedMask;
  701.     NXPoint        p;
  702.     NXRect        theRect;
  703.     BOOL        started = NO;
  704.     NXPoint        origPt = event->location;
  705.     int            mask = NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK;
  706.     BOOL        alt = (event->flags & NX_ALTERNATEMASK) ? YES : NO;
  707.     BOOL        cmd = (event->flags & NX_COMMANDMASK) ? YES : NO;
  708.     BOOL        ctrl = (event->flags & NX_CONTROLMASK) ? YES : NO;
  709.     BOOL        shift = (event->flags & NX_SHIFTMASK) ? YES : NO;
  710.  
  711.     if (event->data.mouse.click == 2)    {
  712.         [NXApp unhide:self];
  713.         [[NXApp delegate] updateMenus];
  714.         return self;
  715.     }
  716.  
  717.     savedMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  718.     if (!shift && !ctrl && (cmd || alt) && ((selCnt = [self hasSelectedCells]) != 0))    {
  719.         NXImage     *dragImage;
  720.         Pasteboard    *pb = [Pasteboard newName:NXDragPboard];
  721.  
  722.         if (selCnt == 1)
  723.             dragImage = [IconView getImageForPath:[self selectedCellPath] fileIcon:YES zone:[self zone]];
  724.         else
  725.             dragImage = [[NXImage allocFromZone:[self zone]] initFromSection:"Sheaf"];
  726.  
  727.         dragSourceView = nil;
  728.         origPt.x -= 32.0; origPt.y -= 32.0;
  729.  
  730.         keepDraggedIcons = alt;
  731.         [self copy:self toPasteboard:pb andCut:NO];
  732.         [self dragImage:dragImage at:&origPt offset:&frame.origin
  733.                   event:event pasteboard:pb source:self slideBack:YES];
  734.         [dragImage free];
  735.  
  736.         [self deselectAll:self];
  737.         [window setEventMask:savedMask];
  738.         [[NXApp delegate] updateMenus];
  739.         return self;
  740.     }
  741.     do    {
  742.         if (!started)    {
  743.             [self lockFocus];
  744.             PSsetgray(NX_BLACK);
  745.             PSsetinstance(YES);
  746.             started = YES;
  747.         }
  748.  
  749.         p = event->location;
  750.         NX_X(&theRect) = (origPt.x < p.x) ? origPt.x : p.x;
  751.         NX_Y(&theRect) = (origPt.y < p.y) ? origPt.y : p.y;
  752.         NX_WIDTH(&theRect) = fabs(origPt.x - p.x);
  753.         NX_HEIGHT(&theRect) = fabs(origPt.y - p.y);
  754.         PSnewinstance();
  755.         NXFrameRect(&theRect);
  756.         event = [NXApp getNextEvent:mask];
  757.     } while (event && event->type != NX_MOUSEUP);
  758.  
  759.     if (started)    {
  760.         PSnewinstance();
  761.         PSsetinstance(NO);
  762.         [self unlockFocus];
  763.         PSsetgray(NX_BLACK);
  764.         if (ctrl)
  765.             [self nukeViewsInRect:&theRect];
  766.         else    {
  767.             [[NXApp delegate] deselectDock];
  768.             [self selectViewsInRect:&theRect deselect:!shift];
  769.         }
  770.     }
  771.  
  772.     [window setEventMask:savedMask];
  773.     [[NXApp delegate] updateMenus];
  774.     return self;
  775. }
  776.  
  777. - setKeepDraggedIcons:(BOOL)flag
  778. {
  779.     keepDraggedIcons = flag;
  780.     return self;
  781. }
  782.  
  783. - getNextFrame:(NXRect *)theFrame firstFrame:(NXRect *)firstFrame limit:(float)limit useSlimMargin:(BOOL)flag
  784. {
  785.     int            maxTries;
  786.     NXRect        origFrame = *theFrame;
  787.     BOOL        goingDown = TRUE;
  788.  
  789.     if (NXContainsRect(&frame, theFrame) &&
  790.         ![self isAnyViewAt:theFrame->origin besides:draggedView useSlimMargin:flag])
  791.         return self;
  792.  
  793.     theFrame->origin = firstFrame->origin;
  794.     maxTries = NX_WIDTH(&frame) * NX_HEIGHT(&frame)/(gridValue * gridValue);
  795.     while (maxTries)    {
  796.         /* scan left */
  797.         do    {
  798.             maxTries--;
  799.             NX_X(theFrame) -= NX_WIDTH(theFrame);
  800.         } while ((NX_X(theFrame) > 0.0) && ((NX_X(firstFrame) - NX_X(theFrame))/NX_WIDTH(theFrame) <= limit) &&
  801.                  ([self isAnyViewAt:theFrame->origin besides:draggedView useSlimMargin:flag]));
  802.  
  803.         /* if we're within bounds, we're done */
  804.         if (NX_X(theFrame) > 0.0  && ((NX_X(firstFrame) - NX_X(theFrame))/NX_WIDTH(theFrame) <= limit))
  805.             return self;
  806.         else    {
  807.             if (goingDown)    {
  808.                 if (NX_Y(theFrame) >= NX_HEIGHT(theFrame))
  809.                     NX_Y(theFrame) -= NX_HEIGHT(theFrame);
  810.                 else    {
  811.                     goingDown = NO;
  812.                     NX_Y(theFrame) = NX_MAXY(&origFrame);
  813.                 }
  814.             }
  815.             else    {
  816.                 if (NX_MAXY(theFrame) <= NX_HEIGHT(&frame))
  817.                     NX_Y(theFrame) += NX_HEIGHT(theFrame);
  818.                 else    {
  819.                     limit = 100.0;
  820.                     goingDown = YES;
  821.                     NX_Y(theFrame) = NX_Y(firstFrame);
  822.                 }
  823.             }
  824.             NX_X(theFrame) = NX_MAXX(firstFrame);
  825.         }
  826.     }
  827.  
  828.     return self;
  829. }
  830.  
  831.  
  832. - concludeDragOperation:(id <NXDraggingInfo>)sender
  833. {
  834.     id            oldImage;
  835.     int            len;
  836.     int            count;
  837.     int            horizLimit;
  838.     int            horizCount;
  839.     char        *ptr;
  840.     char        *path;
  841.     char        *dragString;
  842.     NXRect        newFrame;
  843.     NXRect        viewFrame;
  844.     BOOL        useSound = !strcmp(NXGetDefaultValue([NXApp appName], USE_SOUND), "YES");
  845.     BOOL        srcIsDockMgr = [[sender draggingSource] isKindOf:[DockMgrView class]];
  846.  
  847.     [draggedView getFrame:&viewFrame];
  848.     newFrame = viewFrame;
  849. //    NX_X(&newFrame) = NX_MAXX(&viewFrame);
  850.  
  851.     count = 0;
  852.     horizCount = 0;
  853.     horizLimit = 1000;
  854.     [draggedView getData:(void **)&dragString andLength:&len];
  855.     if ([sender draggingSourceOperationMask]/* & NX_DragOperationCopy*/)    {
  856.         ptr = dragString;
  857.         while(*ptr)    {
  858.             if (*ptr == '\t')
  859.                 count++;
  860.             ptr++;
  861.         }
  862.         horizLimit = (int)sqrt((float)count);
  863.     }
  864.  
  865.     [window disableFlushWindow];
  866.     path = strtok(dragString, "\t");
  867.     while(path && strlen(path))    {
  868.         oldImage = (!count && [sender isDraggingSourceLocal] && !srcIsDockMgr)? [draggedView image] : nil;
  869.         [self getNextFrame:&newFrame firstFrame:&viewFrame limit:(float)horizLimit useSlimMargin:NO];
  870.         [self createViewForPath:path withImage:oldImage at:&newFrame.origin];
  871.         NXPing();
  872.         path = strtok(NULL, "\t");
  873.     }
  874.     [[window reenableFlushWindow] flushWindowIfNeeded];
  875.  
  876.     if (useSound && (![sender isDraggingSourceLocal] ||  srcIsDockMgr))
  877.         [fiendSound play];
  878.  
  879.     if (draggedView != nil)    {
  880.         [self removeView:draggedView];
  881.         [draggedView free];
  882.     }
  883.     [[NXApp delegate] updateMenus];
  884.     [self perform:@selector(writeShelf) with:nil afterDelay:1000.0 cancelPrevious:YES];
  885.     return self;
  886. }
  887.  
  888. - setDragView:aView onEvent:(NXEvent *) e withOffset:(NXPoint *) offset
  889.    atLocation:(const NXPoint *) location
  890. {
  891.     void        *data;
  892.     int            length;
  893.     NXPoint        myLoc;
  894. //    NXSize        viewSize = {gridValue, gridValue};
  895.     id            pb = [Pasteboard newName:NXDragPboard];
  896. //    NXImage     *dragImage = [[NXImage allocFromZone:[self zone]] initSize:&viewSize];
  897.  
  898.     dragSourceView = aView;
  899.     keepDraggedIcons = (e->flags & NX_ALTERNATEMASK) ? YES : NO;
  900.  
  901.     [aView getData:&data andLength:&length];
  902.     [pb declareTypes:&NXFilenamePboardType num:1 owner:nil];
  903.     [pb writeType:NXFilenamePboardType data:data length:length];
  904.  
  905. /*
  906.     [aView allocateGState];
  907.     [aView lockFocus]; [aView unlockFocus];
  908.     if ([dragImage lockFocus])    {
  909.         PScompositerect(0.0, 0.0, gridValue, gridValue, NX_CLEAR);
  910.         PScomposite(0.0, 0.0, gridValue, gridValue, [aView gState], 0.0, 0.0, NX_COPY);
  911.         [dragImage unlockFocus];
  912.     }
  913.     [aView freeGState];
  914. */
  915.     myLoc = *location;
  916.     [aView convertPoint:&myLoc toView:self];
  917.     [self dragImage:[aView image] at:&myLoc offset:offset event:e pasteboard:pb
  918.      source:self slideBack:YES];
  919.  
  920. //    [dragImage free];
  921.     [self deselectAll:self];
  922. //    [self display];
  923.  
  924.     return self;
  925. }
  926.  
  927. - draggedImage:(NXImage *)image beganAt:(NXPoint *)screenPoint
  928. {
  929.     NXRect    theFrame;
  930.  
  931.     if (dragSourceView != nil)    {
  932.         [dragSourceView getFrame:&theFrame];
  933.         [self display:&theFrame :1 :NO];
  934.         if (!keepDraggedIcons)
  935.             [self removeView:dragSourceView];
  936.     }
  937.  
  938.     return self;
  939. }
  940.  
  941.  
  942. - draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint
  943.      deposited:(BOOL)didDeposit
  944. {
  945.     if (dragSourceView != nil)    {
  946.         if (didDeposit && !keepDraggedIcons)
  947.             [self deleteView:dragSourceView];
  948.         else
  949.             [self addView:dragSourceView];
  950.     }
  951.     else if (didDeposit && !keepDraggedIcons)
  952.         [self deleteSelectedCells];
  953.  
  954.     [[NXApp delegate] updateMenus];
  955.     return self;
  956. }
  957.  
  958.  
  959. - (NXDragOperation) draggingSourceOperationMaskForLocal:(BOOL)flag
  960. {
  961.     return NX_DragOperationAll;
  962. }
  963.  
  964.  
  965. - (const char *)selectedCellPath
  966. {
  967.     id        *view;
  968.     int        i;
  969.     int        len;
  970.     char    *path = "";
  971.     int        count = [subviews count];
  972.     id        *viewListPtr = (count > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  973.  
  974.     for(i = 0, view = viewListPtr; i < count; i++, view++)    {
  975.         if ([*view state])    {
  976.             [*view getData:(void *)&path andLength:&len];
  977.             break;
  978.         }
  979.     }
  980.     return (const char *)path;
  981. }
  982.  
  983. - (int)hasSelectedCells
  984. {
  985.     id        *view;
  986.     int        i;
  987.     int        selCnt = 0;
  988.     int        count = [subviews count];
  989.     id        *viewListPtr = (count > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  990.  
  991.     for(i = 0, view = viewListPtr; i < count; i++, view++)    {
  992.         if ([*view state])
  993.             selCnt++;
  994.     }
  995.     return selCnt;
  996. }
  997.  
  998. - copy:sender toPasteboard:pb andCut:(BOOL)cutFlag
  999. {
  1000.     id        *aView;
  1001.     id        *cutListPtr;
  1002.     int        i;
  1003.     int        junk;
  1004.     int        length;
  1005.     int        maxLength;
  1006.     char    *viewPath;
  1007.     NXRect    frameRect;
  1008.     char    *cutString;
  1009.     NXPoint    refPt = {-16000.0, -16000.0};
  1010.     int        count = [subviews count];
  1011.     id        *viewListPtr = (count > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  1012.     id        cutList = [[List allocFromZone:[self zone]] init];
  1013.     BOOL    useSound = !strcmp(NXGetDefaultValue([NXApp appName], USE_SOUND), "YES");
  1014.     NXStream    *theStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  1015.     const char    *types[] = {FIEND_PBTYPE, NXFilenamePboardType, NXAsciiPboardType};
  1016.  
  1017.     [pb declareTypes:types num:3 owner:nil];
  1018.  
  1019.     for(i = 0, aView = viewListPtr; i < count; i++, aView++)    {
  1020.         if ([*aView state])    {
  1021.             [*aView getFrame:&frameRect];
  1022.             refPt.x = (refPt.x > NX_MAXX(&frameRect)) ? refPt.x : NX_MAXX(&frameRect);
  1023.             refPt.y = (refPt.y > NX_MAXY(&frameRect)) ? refPt.y : NX_MAXY(&frameRect);
  1024.             [cutList addObject:*aView];
  1025.         }
  1026.     }
  1027.  
  1028.     NXPrintf(theStream, "SHELF\t");
  1029.     count = [cutList count];
  1030.     cutListPtr = (count > 0) ? NX_ADDRESS(cutList) : (id *)NULL;
  1031.     for(i = 0, aView = cutListPtr; i < count; i++, aView++)    {
  1032.         [*aView getFrame:&frameRect];
  1033.         [*aView getData:(void **)&viewPath andLength:&junk];
  1034.         NXPrintf(theStream,"%6d%7.2f%7.2f  %s\t", 0,
  1035.                  (NX_MAXX(&frameRect) - refPt.x)/gridValue,
  1036.                  (NX_MAXY(&frameRect) - refPt.y)/gridValue,
  1037.                  viewPath);
  1038.     }
  1039.     NXGetMemoryBuffer(theStream, &cutString, &length, &maxLength);
  1040.     [pb writeType:FIEND_PBTYPE data:cutString length:strlen(cutString)];
  1041.     NXClose(theStream);    
  1042.  
  1043.     length = 0;
  1044.     theStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  1045.     for(i = 0, aView = cutListPtr; i < count; i++, aView++)    {
  1046.         [*aView getData:(void **)&viewPath andLength:&junk];
  1047.         NXPrintf(theStream, "%s\t", viewPath);
  1048.     }
  1049.     NXGetMemoryBuffer(theStream, &cutString, &length, &maxLength);
  1050.     cutString[strlen(cutString)-1] = '\0';
  1051.     [pb writeType:NXFilenamePboardType data:cutString length:strlen(cutString)];
  1052.  
  1053.     for(i = 0; i < strlen(cutString); i++)    {
  1054.         if (cutString[i] == '\t')
  1055.             cutString[i] = '\n';
  1056.     }
  1057.     if (cutString[strlen(cutString)-1] != '\n') strcat(cutString, "\n");
  1058.     [pb writeType:NXAsciiPboardType data:cutString length:strlen(cutString)];
  1059.     NXClose(theStream);
  1060.  
  1061.     if (cutFlag)    {
  1062.         if (useSound)
  1063.             [destroySound play:self];
  1064.         [window disableFlushWindow];
  1065.         for(i = 0, aView = cutListPtr; i < count; i++, aView++)
  1066.             [self deleteView:*aView];
  1067.         [[window reenableFlushWindow] flushWindowIfNeeded];
  1068.     }
  1069.  
  1070.     [cutList free];
  1071.     [[NXApp delegate] updateMenus];
  1072.     [self perform:@selector(writeShelf) with:nil afterDelay:1000.0 cancelPrevious:YES];
  1073.  
  1074.     return self;
  1075. }
  1076.  
  1077. - showInsertCursor:(NXPoint *)locus ctrl:(BOOL *)ctrl
  1078. {
  1079.     NXEvent    *event;
  1080.  
  1081.     [NXIBeam push];
  1082.     do    {
  1083.         event = [NXApp getNextEvent:NX_MOUSEDOWNMASK|NX_MOUSEDRAGGEDMASK];
  1084.     } while (event && event->type != NX_MOUSEDOWN);
  1085.     *ctrl = (event->flags & NX_CONTROLMASK) ? YES : NO;
  1086.     [NXIBeam pop];
  1087.     *locus = event->location;
  1088.  
  1089.     return self;
  1090. }
  1091.  
  1092. - paste:sender toPasteboard:pb
  1093. {
  1094.     int        len;
  1095.     int        horizLimit;
  1096.     int        horizCount;
  1097.     int        launchFlag;
  1098.     char    *path;
  1099.     char    *dragString;
  1100.     char    tokenString[500];
  1101.     BOOL    ctrl;
  1102.     BOOL    fromDock;
  1103.     BOOL    fromShelf;
  1104.     NXRect     newFrame;
  1105.     NXCoord    x;
  1106.     NXCoord    y;
  1107.     NXPoint    locus;
  1108.     NXPoint    newPoint;
  1109.     NXPoint    maxOffset;
  1110.     const    char *type;
  1111.     char    *sep = "\t";
  1112.     NXRect    bogusFrame = {{-1000.0, -1000.0}, {gridValue, gridValue}};
  1113.     NXRect    viewFrame = {{0.0, 0.0}, {gridValue, gridValue}};
  1114.     BOOL    gridEnabled = !strcmp(NXGetDefaultValue([NXApp appName], GRID_ENABLE), "YES");
  1115.     BOOL    useSound = !strcmp(NXGetDefaultValue([NXApp appName], USE_SOUND), "YES");
  1116.     const    char *types[] = {FIEND_PBTYPE, NXFilenamePboardType};
  1117.  
  1118.     type = [pb findAvailableTypeFrom:types num:2];
  1119.     if (type && !strcmp(type, FIEND_PBTYPE))    {
  1120.         if (![pb readType:FIEND_PBTYPE data:&dragString length:&len])
  1121.             return self;
  1122.         sep = "\t";
  1123.     }        
  1124.     else if (type && !strcmp(type, NXFilenamePboard))    {
  1125.         if (![pb readType:NXFilenamePboardType data:&dragString length:&len])
  1126.             return self;
  1127.         sep = "\t";
  1128.     }
  1129. /*
  1130.     else if (!strcmp(type, NXAsciiPboard))    {
  1131.         if (![pb readType:NXAsciiPboardType data:&dragString length:&len])
  1132.             return self;
  1133.         sep = "\n";
  1134.     }
  1135. */
  1136.     else    {
  1137.         NXBeep();
  1138.         return self;
  1139.     }
  1140.  
  1141.     if (len == 0)    {
  1142.         NXBeep();
  1143.         return self;
  1144.     }
  1145.  
  1146.     if (pb != [Pasteboard new])    {
  1147.         NXRunAlertPanel([NXApp appName],
  1148.                         "Click mouse to choose site...",
  1149.                         "Ok", NULL, NULL);
  1150.     }
  1151.  
  1152.     [self showInsertCursor:&locus ctrl:&ctrl];
  1153.     if (gridEnabled)    {
  1154.         locus.x = locus.x - (int)locus.x % gridValue;
  1155.         locus.y = locus.y - (int)locus.y % gridValue;
  1156.     }
  1157.  
  1158.     viewFrame.origin = newPoint = locus;
  1159.     newFrame = viewFrame;
  1160.  
  1161.     horizCount = 0;
  1162.     horizLimit = [self getHorizLimit:dragString sep:sep ctrl:ctrl];
  1163.     [window disableFlushWindow];
  1164.     path = strtok(dragString, sep);
  1165.     fromDock = (!strcmp(path, "DOCK")) ? YES : NO;
  1166.     fromShelf = (!strcmp(path, "SHELF")) ? YES : NO;
  1167.     if (fromDock)    {
  1168.         path = strtok(NULL, sep);
  1169.         sscanf(path, "%f %f", &maxOffset.x, &maxOffset.y);
  1170.     }
  1171.     if (fromDock || fromShelf)
  1172.         path = strtok(NULL, sep);
  1173.     while(path && strlen(path))    {
  1174.         if (!strcmp(type, FIEND_PBTYPE))    {
  1175.             sscanf(path, "%d %f %f %[^\n\t]", &launchFlag, &x, &y, tokenString);
  1176.             if (fromDock)    {
  1177.                 x -= maxOffset.x;
  1178.                 y -= maxOffset.y;
  1179.             }
  1180.             NX_X(&newFrame) = locus.x + (x - 1.0) * gridValue;
  1181.             NX_Y(&newFrame) = locus.y + (y - 1.0) * gridValue;
  1182.         }
  1183.         else
  1184.             strcpy(tokenString, path);
  1185.         if (ctrl) newFrame = bogusFrame;
  1186.         [self getNextFrame:&newFrame firstFrame:&viewFrame
  1187.                      limit:(float)horizLimit useSlimMargin:NO];
  1188.         [self createViewForPath:tokenString withImage:nil at:&newFrame.origin];
  1189.         NXPing();
  1190.         path = strtok(NULL, sep);
  1191.     }
  1192.     [[window reenableFlushWindow] flushWindowIfNeeded];
  1193.     if (useSound)
  1194.         [fiendSound play:self];
  1195.  
  1196.     [pb deallocatePasteboardData:dragString length:len];
  1197.     [[NXApp delegate] updateMenus];
  1198.     [self perform:@selector(writeShelf) with:nil afterDelay:1000.0 cancelPrevious:YES];
  1199.     return self;
  1200. }
  1201.  
  1202. - paste:sender
  1203. {
  1204.     return [self paste:sender toPasteboard:[Pasteboard new]];
  1205. }
  1206.     
  1207. - (FILE *)openShelfFor:(char *)how path:(char *)path
  1208. {
  1209.     char    *path2 = "";
  1210.     FILE    *f;
  1211.  
  1212.     sprintf(path, "%s/.AppInfo/Fiend/%s.%.0f.%.0f", NXHomeDirectory(),
  1213.             FIENDSHELF_FILE, NX_WIDTH(&frame), NX_HEIGHT(&frame));
  1214.  
  1215.     if (*how == 'w')    {
  1216.         path2 = (char *)malloc(strlen(path)+2);
  1217.         sprintf(path2, "%s~", path);
  1218.         rename(path, path2);
  1219.     }
  1220.  
  1221.     f = fopen(path, how);
  1222.     if (*how == 'r')    {
  1223.         if (f == (FILE *)NULL)    {
  1224.             NXLogError("Couldn't find %s, trying %s~", path, path);
  1225.             strcat(path, "~");
  1226.             if ((f = fopen(path, how)) == (FILE *)NULL)    {
  1227.                 sprintf(path, "%s/.AppInfo/Fiend/%s", NXHomeDirectory(), FIENDSHELF_FILE);
  1228.                 NXLogError("...trying %s", path);
  1229.                 if ((f = fopen(path, how)) == (FILE *)NULL)    {
  1230.                     sprintf(path, "%s/.FiendShelf", NXHomeDirectory());
  1231.                     NXLogError("...trying %s", path);
  1232.                     if ((f = fopen(path, how)) == (FILE *)NULL)
  1233.                         NXLogError("No FiendShelf file!!");
  1234.                 }
  1235.             }
  1236.         }
  1237.         else    {
  1238.             fseek(f, 0L, SEEK_END);
  1239.             if (ftell(f) <= 5)    {
  1240.                 NXLogError("%s is empty - trying backup", path);
  1241.                 fclose(f);
  1242.                 strcat(path, "~");
  1243.                 if ((f = fopen(path, how)) != (FILE *)NULL)    {
  1244.                     fseek(f, 0L, SEEK_END);
  1245.                     if (ftell(f) <= 5)
  1246.                         NXLogError("Backup is empty - building empty shelf");
  1247.                 }
  1248.             }
  1249.             if (f != (FILE *)NULL)
  1250.                 rewind(f);
  1251.         }
  1252.         return f;
  1253.     }
  1254.     else if (f == (FILE *)NULL)    {
  1255.         NXBeep();
  1256.         NXRunAlertPanel([NXApp appName],
  1257.                         "Could not open %s to save Fiend Shelf!!",
  1258.                         NULL, NULL, NULL, path);
  1259.         rename(path2, path);
  1260.         free(path2);
  1261.         return f;
  1262.     }            
  1263.  
  1264.     if (*how == 'w') free(path2);
  1265.     return f;
  1266. }
  1267.  
  1268.  
  1269. - closeShelf:(FILE *)file
  1270. {
  1271.     fclose(file);
  1272.     return self;
  1273. }
  1274.  
  1275.  
  1276. - readShelf:(BOOL)showProgress
  1277. {
  1278.     float    i;
  1279.     float    count;
  1280.     FILE    *file;
  1281.     char    line[MAXPATHLEN + 30];
  1282.     char    *path;
  1283.     NXPoint    point;
  1284.  
  1285.     file = [self openShelfFor:"r" path:line];
  1286.     if (file == NULL)
  1287.         return self;
  1288.  
  1289.     count = 0.0;
  1290.     if (showProgress)    {
  1291.         while(fgets(line, sizeof(line), file))
  1292.             count++;
  1293.         rewind(file);
  1294.         [[NXApp delegate] setProgressViewRatio:0.0];
  1295.         if (count == 0)    {
  1296.             [self closeShelf:file];
  1297.             [[NXApp delegate] setProgressViewRatio:0.85];
  1298.             return self;
  1299.         }
  1300.     }
  1301.  
  1302.     i = 1.0;
  1303.     while (fgets(line, sizeof(line), file)) {
  1304.         if (showProgress)
  1305.             [[NXApp delegate] setProgressViewRatio:0.85*i++/count];
  1306.  
  1307.         sscanf(line, "%f %f", &point.x, &point.y);
  1308.         if (strlen(line) > 14) {
  1309.             path = line + 14;
  1310.             if (rindex(path, '\n') != NULL)
  1311.                 *rindex(path, '\n') = '\0';
  1312.         }
  1313.         else
  1314.             continue;
  1315.  
  1316.         [self createViewForPath:path withImage:nil at:&point];
  1317.     }
  1318.  
  1319.     [self closeShelf:file];
  1320.  
  1321.     return self;
  1322. }
  1323.  
  1324.  
  1325. - writeShelf
  1326. {
  1327.     id            *view;
  1328.     int            i;
  1329.     int            length;
  1330.     int            statRet;
  1331.     char        *iconPath;
  1332.     char        path[MAXPATHLEN];
  1333.     char        path2[MAXPATHLEN];
  1334.     FILE        *file;
  1335.     NXRect        rect;
  1336.     struct stat    theStat;
  1337.     int            count = [subviews count];
  1338.     id            *viewListPtr = (count > 0) ? NX_ADDRESS(subviews) : (id *)NULL;
  1339.  
  1340.     file = [self openShelfFor:"w" path:path];
  1341.     if (file == NULL)
  1342.         return self;
  1343.  
  1344.     for(i = 0, view = viewListPtr; i < count; i++, view++)    {
  1345.         if (![*view isKindOf:[IconView class]])
  1346.             continue;
  1347.  
  1348.         [*view getData:(void **)&iconPath andLength:&length];
  1349.         [*view getFrame:&rect];
  1350.  
  1351.         if (fprintf(file, "%6.0f %6.0f %s\n", rect.origin.x, rect.origin.y, iconPath) < 0)    {
  1352.             perror("Fiend 'writeShelf'");
  1353.             fclose(file);
  1354.             sprintf(path, "%s/.AppInfo/Fiend/%s.%.0f.%.0f", NXHomeDirectory(),
  1355.                     FIENDSHELF_FILE, NX_WIDTH(&frame), NX_HEIGHT(&frame));
  1356.             sprintf(path2, "%s~", path);
  1357.             fclose(file);
  1358.             unlink(path);
  1359.             rename(path, path2);
  1360.             return nil;
  1361.         }
  1362.     }
  1363.  
  1364.     [self closeShelf:file];
  1365.     sprintf(path2, "%s/.AppInfo/Fiend/FiendShelf", NXHomeDirectory());
  1366.     statRet = stat(path2, &theStat);
  1367.     if (((statRet != -1) && (theStat.st_mode & S_IFLNK)) || statRet == -1)    {
  1368.         unlink(path2);
  1369.         symlink(path, path2);
  1370.     }
  1371.     return self;
  1372. }
  1373.  
  1374. @end
  1375.