1/* FSNIconsView.m
2 *
3 * Copyright (C) 2004-2016 Free Software Foundation, Inc.
4 *
5 * Author: Enrico Sersale <enrico@imago.ro>
6 *         Riccardo Mottola <rm@gnu.org>
7 * Date: March 2004
8 *
9 * This file is part of the GNUstep FSNode framework
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
24 */
25
26#include <math.h>
27#include <unistd.h>
28#include <sys/types.h>
29
30#import <Foundation/Foundation.h>
31#import <AppKit/AppKit.h>
32#import <GNUstepGUI/GSVersion.h>
33
34#import "FSNIconsView.h"
35#import "FSNIcon.h"
36#import "FSNFunctions.h"
37
38#define DEF_ICN_SIZE 48
39#define DEF_TEXT_SIZE 12
40#define DEF_ICN_POS NSImageAbove
41
42#define X_MARGIN (10)
43#define Y_MARGIN (12)
44
45#define EDIT_MARGIN (4)
46
47#ifndef max
48  #define max(a,b) ((a) >= (b) ? (a):(b))
49#endif
50
51#ifndef min
52  #define min(a,b) ((a) <= (b) ? (a):(b))
53#endif
54
55#define CHECK_SIZE(s) \
56if (s.width < 1) s.width = 1; \
57if (s.height < 1) s.height = 1; \
58if (s.width > maxr.size.width) s.width = maxr.size.width; \
59if (s.height > maxr.size.height) s.height = maxr.size.height
60
61#define SETRECT(o, x, y, w, h) { \
62NSRect rct = NSMakeRect(x, y, w, h); \
63if (rct.size.width < 0) rct.size.width = 0; \
64if (rct.size.height < 0) rct.size.height = 0; \
65[o setFrame: NSIntegralRect(rct)]; \
66}
67
68/* we redefine the dockstyle to read the preferences without including Dock.h" */
69typedef enum DockStyle
70{
71  DockStyleClassic = 0,
72  DockStyleModern = 1
73} DockStyle;
74
75#define SUPPORTS_XOR ((GNUSTEP_GUI_MAJOR_VERSION > 0)		\
76		      || (GNUSTEP_GUI_MAJOR_VERSION == 0	\
77			  && GNUSTEP_GUI_MINOR_VERSION > 22)	\
78		      || (GNUSTEP_GUI_MAJOR_VERSION == 0	\
79			  && GNUSTEP_GUI_MINOR_VERSION == 22	\
80			  && GNUSTEP_GUI_SUBMINOR_VERSION > 0))
81
82static void GWHighlightFrameRect(NSRect aRect)
83{
84#if SUPPORTS_XOR
85  NSFrameRectWithWidthUsingOperation(aRect, 1.0, GSCompositeHighlight);
86#endif
87}
88
89
90@implementation FSNIconsView
91
92- (void)dealloc
93{
94  RELEASE (node);
95  RELEASE (extInfoType);
96  RELEASE (icons);
97  RELEASE (labelFont);
98  RELEASE (nameEditor);
99  RELEASE (horizontalImage);
100  RELEASE (verticalImage);
101  RELEASE (lastSelection);
102  RELEASE (charBuffer);
103  RELEASE (backColor);
104  RELEASE (textColor);
105  RELEASE (disabledTextColor);
106
107  [super dealloc];
108}
109
110- (id)init
111{
112  self = [super init];
113
114  if (self) {
115    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
116    NSString *appName = [defaults stringForKey: @"DesktopApplicationName"];
117    NSString *selName = [defaults stringForKey: @"DesktopApplicationSelName"];
118    id defentry;
119
120    fsnodeRep = [FSNodeRep sharedInstance];
121
122    if (appName && selName) {
123      Class desktopAppClass = [[NSBundle mainBundle] classNamed: appName];
124      SEL sel = NSSelectorFromString(selName);
125      desktopApp = [desktopAppClass performSelector: sel];
126    }
127
128    /* we tie the transparent selection to the modern dock style */
129    transparentSelection = NO;
130    defentry = [defaults objectForKey: @"dockstyle"];
131    if ([defentry intValue] == DockStyleModern)
132      transparentSelection = YES;
133
134    ASSIGN (backColor, [NSColor windowBackgroundColor]);
135    ASSIGN (textColor, [NSColor controlTextColor]);
136    ASSIGN (disabledTextColor, [NSColor disabledControlTextColor]);
137
138    defentry = [defaults objectForKey: @"iconsize"];
139    iconSize = defentry ? [defentry intValue] : DEF_ICN_SIZE;
140
141    defentry = [defaults objectForKey: @"labeltxtsize"];
142    labelTextSize = defentry ? [defentry intValue] : DEF_TEXT_SIZE;
143    ASSIGN (labelFont, [NSFont systemFontOfSize: labelTextSize]);
144
145    defentry = [defaults objectForKey: @"iconposition"];
146    iconPosition = defentry ? [defentry intValue] : DEF_ICN_POS;
147
148    defentry = [defaults objectForKey: @"fsn_info_type"];
149    infoType = defentry ? [defentry intValue] : FSNInfoNameType;
150    extInfoType = nil;
151
152    if (infoType == FSNInfoExtendedType) {
153      defentry = [defaults objectForKey: @"extended_info_type"];
154
155      if (defentry) {
156        NSArray *availableTypes = [fsnodeRep availableExtendedInfoNames];
157
158        if ([availableTypes containsObject: defentry]) {
159          ASSIGN (extInfoType, defentry);
160        }
161      }
162
163      if (extInfoType == nil) {
164        infoType = FSNInfoNameType;
165      }
166    }
167
168    icons = [NSMutableArray new];
169
170    nameEditor = [FSNIconNameEditor new];
171    [nameEditor setDelegate: self];
172    [nameEditor setFont: labelFont];
173    [nameEditor setBezeled: NO];
174    [nameEditor setAlignment: NSCenterTextAlignment];
175    [nameEditor setBackgroundColor: backColor];
176    [nameEditor setTextColor: textColor];
177    [nameEditor setEditable: NO];
178    [nameEditor setSelectable: NO];
179    editIcon = nil;
180
181    isDragTarget = NO;
182		lastKeyPressed = 0.;
183    charBuffer = nil;
184    selectionMask = NSSingleSelectionMask;
185
186    [self calculateGridSize];
187
188    [self registerForDraggedTypes: [NSArray arrayWithObjects:
189                                                NSFilenamesPboardType,
190                                                @"GWLSFolderPboardType",
191                                                @"GWRemoteFilenamesPboardType",
192                                                nil]];
193  }
194
195  return self;
196}
197
198- (void)sortIcons
199{
200  if (infoType == FSNInfoExtendedType) {
201    [icons sortUsingFunction: (int (*)(id, id, void*))compareWithExtType
202                     context: (void *)NULL];
203  } else {
204    [icons sortUsingSelector: [fsnodeRep compareSelectorForDirectory: [node path]]];
205  }
206}
207
208- (void)calculateGridSize
209{
210  NSSize highlightSize = NSZeroSize;
211  NSSize labelSize = NSZeroSize;
212  int lblmargin = [fsnodeRep labelMargin];
213
214  highlightSize.width = ceil(iconSize / 3 * 4);
215  highlightSize.height = ceil(highlightSize.width * [fsnodeRep highlightHeightFactor]);
216  if ((highlightSize.height - iconSize) < 4) {
217    highlightSize.height = iconSize + 4;
218  }
219
220  labelSize.height = floor([fsnodeRep heightOfFont: labelFont]);
221  labelSize.width = [fsnodeRep labelWFactor] * labelTextSize;
222
223  gridSize.height = highlightSize.height;
224
225  if (infoType != FSNInfoNameType) {
226    float lbsh = (labelSize.height * 2) - 2;
227
228    if (iconPosition == NSImageAbove) {
229      gridSize.height += lbsh;
230      gridSize.width = labelSize.width;
231    } else {
232      if (lbsh > gridSize.height) {
233        gridSize.height = lbsh;
234      }
235      gridSize.width = highlightSize.width + labelSize.width + lblmargin;
236    }
237  } else {
238    if (iconPosition == NSImageAbove) {
239      gridSize.height += labelSize.height;
240      gridSize.width = labelSize.width;
241    } else {
242      gridSize.width = highlightSize.width + labelSize.width + lblmargin;
243    }
244  }
245}
246
247- (void)tile
248{
249  CREATE_AUTORELEASE_POOL (pool);
250  NSRect svr = [[self superview] frame];
251  NSRect r = [self frame];
252  NSRect maxr = [[NSScreen mainScreen] frame];
253  float px = 0 - gridSize.width;
254  float py = gridSize.height + Y_MARGIN;
255  NSSize sz;
256  NSUInteger poscount = 0;
257  NSUInteger count = [icons count];
258  NSRect *irects = NSZoneMalloc (NSDefaultMallocZone(), sizeof(NSRect) * count);
259  NSCachedImageRep *rep = nil;
260  NSArray *selection;
261  NSUInteger i;
262
263  colcount = 0;
264
265  for (i = 0; i < count; i++)
266    {
267      px += (gridSize.width + X_MARGIN);
268
269    if (px >= (svr.size.width - gridSize.width)) {
270      px = X_MARGIN;
271      py += (gridSize.height + Y_MARGIN);
272
273      if (colcount < poscount) {
274        colcount = poscount;
275      }
276      poscount = 0;
277    }
278
279		poscount++;
280
281		irects[i] = NSMakeRect(px, py, gridSize.width, gridSize.height);
282	}
283
284	py += Y_MARGIN;
285  py = (py < svr.size.height) ? svr.size.height : py;
286
287  SETRECT (self, r.origin.x, r.origin.y, svr.size.width, py);
288
289	for (i = 0; i < count; i++) {
290		FSNIcon *icon = [icons objectAtIndex: i];
291
292		irects[i].origin.y = py - irects[i].origin.y;
293    irects[i] = NSIntegralRect(irects[i]);
294
295    if (NSEqualRects(irects[i], [icon frame]) == NO) {
296      [icon setFrame: irects[i]];
297    }
298
299    [icon setGridIndex: i];
300	}
301
302  DESTROY (horizontalImage);
303  sz = NSMakeSize(svr.size.width, 2);
304  CHECK_SIZE (sz);
305  horizontalImage = [[NSImage allocWithZone: (NSZone *)[(NSObject *)self zone]]
306                               initWithSize: sz];
307
308  rep = [[NSCachedImageRep allocWithZone: (NSZone *)[(NSObject *)self zone]]
309                            initWithSize: sz
310                                   depth: [NSWindow defaultDepthLimit]
311                                separate: YES
312                                   alpha: YES];
313
314  [horizontalImage addRepresentation: rep];
315  RELEASE (rep);
316
317  DESTROY (verticalImage);
318  sz = NSMakeSize(2, py);
319  CHECK_SIZE (sz);
320  verticalImage = [[NSImage allocWithZone: (NSZone *)[(NSObject *)self zone]]
321                             initWithSize: sz];
322
323  rep = [[NSCachedImageRep allocWithZone: (NSZone *)[(NSObject *)self zone]]
324                            initWithSize: sz
325                                   depth: [NSWindow defaultDepthLimit]
326                                separate: YES
327                                   alpha: YES];
328
329  [verticalImage addRepresentation: rep];
330  RELEASE (rep);
331
332	NSZoneFree (NSDefaultMallocZone(), irects);
333
334  RELEASE (pool);
335
336  selection = [self selectedReps];
337  if ([selection count]) {
338    [self scrollIconToVisible: [selection objectAtIndex: 0]];
339  }
340
341  if ([[self subviews] containsObject: nameEditor]) {
342    [self updateNameEditor];
343  }
344}
345
346- (void)scrollIconToVisible:(FSNIcon *)icon
347{
348  NSRect irect = [icon frame];
349  float border = floor(irect.size.height * 0.2);
350
351  irect.origin.y -= border;
352  irect.size.height += border * 2;
353  [self scrollRectToVisible: irect];
354}
355
356- (NSString *)selectIconWithPrefix:(NSString *)prefix
357{
358  NSUInteger i;
359
360  for (i = 0; i < [icons count]; i++)
361    {
362      FSNIcon *icon = [icons objectAtIndex: i];
363      NSString *name = [icon shownInfo];
364
365      if ([name hasPrefix: prefix])
366	{
367	  [icon select];
368	  [self scrollIconToVisible: icon];
369
370	  return name;
371	}
372    }
373
374  return nil;
375}
376
377- (void)selectIconInPrevLine
378{
379  FSNIcon *icon;
380  NSUInteger i;
381  NSInteger pos = -1;
382
383  for (i = 0; i < [icons count]; i++)
384    {
385      icon = [icons objectAtIndex: i];
386
387      if ([icon isSelected])
388	{
389	  pos = i - colcount;
390	  break;
391	}
392    }
393
394  if (pos >= 0)
395    {
396      icon = [icons objectAtIndex: pos];
397      [icon select];
398      [self scrollIconToVisible: icon];
399    }
400}
401
402- (void)selectIconInNextLine
403{
404  FSNIcon *icon;
405  NSUInteger i;
406  NSUInteger pos = [icons count];
407
408  for (i = 0; i < [icons count]; i++)
409    {
410      icon = [icons objectAtIndex: i];
411
412      if ([icon isSelected])
413	{
414	  pos = i + colcount;
415	  break;
416	}
417    }
418
419  if (pos <= ([icons count] -1))
420    {
421      icon = [icons objectAtIndex: pos];
422      [icon select];
423      [self scrollIconToVisible: icon];
424    }
425}
426
427- (void)selectPrevIcon
428{
429  NSUInteger i;
430
431  for (i = 0; i < [icons count]; i++)
432    {
433      FSNIcon *icon = [icons objectAtIndex: i];
434
435      if ([icon isSelected])
436	{
437	  if (i > 0)
438	    {
439	      icon = [icons objectAtIndex: i - 1];
440	      [icon select];
441	      [self scrollIconToVisible: icon];
442	    }
443	  break;
444	}
445    }
446}
447
448- (void)selectNextIcon
449{
450  NSUInteger count = [icons count];
451  NSUInteger i;
452
453  for (i = 0; i < count; i++)
454    {
455      FSNIcon *icon = [icons objectAtIndex: i];
456
457      if ([icon isSelected])
458	{
459	  if (i < (count - 1))
460	    {
461	      icon = [icons objectAtIndex: i + 1];
462	      [icon select];
463	      [self scrollIconToVisible: icon];
464	    }
465	  break;
466	}
467    }
468}
469
470- (void)mouseUp:(NSEvent *)theEvent
471{
472  [self setSelectionMask: NSSingleSelectionMask];
473}
474
475- (void)mouseDown:(NSEvent *)theEvent
476{
477  if ([theEvent modifierFlags] != NSShiftKeyMask) {
478    selectionMask = NSSingleSelectionMask;
479    selectionMask |= FSNCreatingSelectionMask;
480		[self unselectOtherReps: nil];
481    selectionMask = NSSingleSelectionMask;
482
483    DESTROY (lastSelection);
484    [self selectionDidChange];
485    [self stopRepNameEditing];
486	}
487}
488
489- (void)mouseDragged:(NSEvent *)theEvent
490{
491  unsigned int eventMask = NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSPeriodicMask;
492  NSDate *future = [NSDate distantFuture];
493  NSPoint	sp;
494  NSPoint	p, pp;
495  NSRect visibleRect;
496  NSRect oldRect;
497  NSRect r;
498  NSRect selrect;
499  float x, y, w, h;
500  NSUInteger i;
501
502  pp = NSMakePoint(0,0);
503
504#define scrollPointToVisible(p) \
505{ \
506NSRect sr; \
507sr.origin = p; \
508sr.size.width = sr.size.height = 1.0; \
509[self scrollRectToVisible: sr]; \
510}
511
512#define CONVERT_CHECK \
513{ \
514NSRect br = [self bounds]; \
515pp = [self convertPoint: p fromView: nil]; \
516if (pp.x < 1) \
517pp.x = 1; \
518if (pp.x >= NSMaxX(br)) \
519pp.x = NSMaxX(br) - 1; \
520if (pp.y < 0) \
521pp.y = -1; \
522if (pp.y > NSMaxY(br)) \
523pp.y = NSMaxY(br) + 1; \
524}
525
526  p = [theEvent locationInWindow];
527  sp = [self convertPoint: p  fromView: nil];
528
529  oldRect = NSZeroRect;
530
531  [[self window] disableFlushWindow];
532
533  [NSEvent startPeriodicEventsAfterDelay: 0.02 withPeriod: 0.05];
534
535  while ([theEvent type] != NSLeftMouseUp) {
536    BOOL scrolled = NO;
537
538    CREATE_AUTORELEASE_POOL (arp);
539
540    theEvent = [NSApp nextEventMatchingMask: eventMask
541                                  untilDate: future
542                                     inMode: NSEventTrackingRunLoopMode
543                                    dequeue: YES];
544
545    if ([theEvent type] != NSPeriodic) {
546      p = [theEvent locationInWindow];
547    }
548
549    CONVERT_CHECK;
550
551    visibleRect = [self visibleRect];
552
553    if ([self mouse: pp inRect: visibleRect] == NO)
554      {
555	scrollPointToVisible(pp);
556	CONVERT_CHECK;
557
558	scrolled = YES;
559      }
560
561    x = min(sp.x, pp.x);
562    y = min(sp.y, pp.y);
563    w = max(1, max(pp.x, sp.x) - min(pp.x, sp.x));
564    h = max(1, max(pp.y, sp.y) - min(pp.y, sp.y));
565
566    r = NSMakeRect(x, y, w, h);
567
568    // Erase the previous rect
569    if (transparentSelection
570	|| !SUPPORTS_XOR
571	|| (!transparentSelection && scrolled))
572      {
573	[self setNeedsDisplayInRect: oldRect];
574	[[self window] displayIfNeeded];
575      }
576
577    // Draw the new rect
578
579    [self lockFocus];
580
581    if (transparentSelection || !SUPPORTS_XOR)
582      {
583	[[NSColor darkGrayColor] set];
584	NSFrameRect(r);
585	if (transparentSelection)
586	  {
587	    [[[NSColor darkGrayColor] colorWithAlphaComponent: 0.33] set];
588	    NSRectFillUsingOperation(r, NSCompositeSourceOver);
589	  }
590      }
591    else
592      {
593	if (!NSEqualRects(oldRect, r) && !scrolled)
594	  {
595	    GWHighlightFrameRect(oldRect);
596	    GWHighlightFrameRect(r);
597	  }
598	else if (scrolled)
599	  {
600	    GWHighlightFrameRect(r);
601	  }
602      }
603
604    [self unlockFocus];
605
606    oldRect = r;
607
608    [[self window] enableFlushWindow];
609    [[self window] flushWindow];
610    [[self window] disableFlushWindow];
611
612    DESTROY (arp);
613  }
614
615  [NSEvent stopPeriodicEvents];
616  [[self window] postEvent: theEvent atStart: NO];
617
618  // Erase the previous rect
619
620  [self setNeedsDisplayInRect: oldRect];
621  [[self window] displayIfNeeded];
622
623  [[self window] enableFlushWindow];
624  [[self window] flushWindow];
625
626  selectionMask = FSNMultipleSelectionMask;
627  selectionMask |= FSNCreatingSelectionMask;
628
629  x = min(sp.x, pp.x);
630  y = min(sp.y, pp.y);
631  w = max(1, max(pp.x, sp.x) - min(pp.x, sp.x));
632  h = max(1, max(pp.y, sp.y) - min(pp.y, sp.y));
633
634  selrect = NSMakeRect(x, y, w, h);
635
636  for (i = 0; i < [icons count]; i++) {
637    FSNIcon *icon = [icons objectAtIndex: i];
638    NSRect iconBounds = [self convertRect: [icon iconBounds] fromView: icon];
639
640    if (NSIntersectsRect(selrect, iconBounds)) {
641      [icon select];
642    }
643  }
644
645  selectionMask = NSSingleSelectionMask;
646
647  [self selectionDidChange];
648}
649
650- (void)keyDown:(NSEvent *)theEvent
651{
652  NSString *characters;
653  unichar character;
654  NSRect vRect, hiddRect;
655  NSPoint p;
656  float x, y, w, h;
657
658  characters = [theEvent characters];
659  character = 0;
660
661  if ([characters length] > 0)
662    character = [characters characterAtIndex: 0];
663
664
665  switch (character) {
666    case NSPageUpFunctionKey:
667		  vRect = [self visibleRect];
668		  p = vRect.origin;
669		  x = p.x;
670		  y = p.y + vRect.size.height;
671		  w = vRect.size.width;
672		  h = vRect.size.height;
673		  hiddRect = NSMakeRect(x, y, w, h);
674		  [self scrollRectToVisible: hiddRect];
675	    return;
676
677    case NSPageDownFunctionKey:
678		  vRect = [self visibleRect];
679		  p = vRect.origin;
680		  x = p.x;
681		  y = p.y - vRect.size.height;
682		  w = vRect.size.width;
683		  h = vRect.size.height;
684		  hiddRect = NSMakeRect(x, y, w, h);
685		  [self scrollRectToVisible: hiddRect];
686	    return;
687
688    case NSUpArrowFunctionKey:
689	    [self selectIconInPrevLine];
690      return;
691
692    case NSDownArrowFunctionKey:
693	    [self selectIconInNextLine];
694      return;
695
696    case NSLeftArrowFunctionKey:
697			{
698				if ([theEvent modifierFlags] & NSControlKeyMask) {
699	      	[super keyDown: theEvent];
700	    	} else {
701	    		[self selectPrevIcon];
702				}
703			}
704      return;
705
706    case NSRightArrowFunctionKey:
707			{
708				if ([theEvent modifierFlags] & NSControlKeyMask) {
709	      	[super keyDown: theEvent];
710	    	} else {
711	    		[self selectNextIcon];
712				}
713			}
714	  	return;
715
716		case NSCarriageReturnCharacter:
717      {
718        unsigned flags = [theEvent modifierFlags];
719        BOOL closesndr = ((flags == NSAlternateKeyMask)
720                                  || (flags == NSControlKeyMask));
721        [self openSelectionInNewViewer: closesndr];
722        return;
723      }
724
725    default:
726      break;
727  }
728
729  if (([characters length] > 0) && (character < 0xF700)) {
730		SEL icnwpSel = @selector(selectIconWithPrefix:);
731		IMP icnwp = [self methodForSelector: icnwpSel];
732
733    if (charBuffer == nil) {
734      charBuffer = [characters substringToIndex: 1];
735      RETAIN (charBuffer);
736      lastKeyPressed = 0.0;
737    } else {
738      if ([theEvent timestamp] - lastKeyPressed < 500.0) {
739        ASSIGN (charBuffer, ([charBuffer stringByAppendingString:
740				    															[characters substringToIndex: 1]]));
741      } else {
742        ASSIGN (charBuffer, ([characters substringToIndex: 1]));
743        lastKeyPressed = 0.0;
744      }
745    }
746
747    lastKeyPressed = [theEvent timestamp];
748
749    if ((*icnwp)(self, icnwpSel, charBuffer)) {
750      return;
751    }
752  }
753
754  [super keyDown: theEvent];
755}
756
757- (NSMenu *)menuForEvent:(NSEvent *)theEvent
758{
759  NSArray *selnodes;
760  NSMenu *menu;
761  NSMenuItem *menuItem;
762  NSString *firstext;
763  NSDictionary *apps;
764  NSEnumerator *app_enum;
765  id key;
766  NSUInteger i;
767
768  if ([theEvent modifierFlags] == NSControlKeyMask) {
769    return [super menuForEvent: theEvent];
770  }
771
772  selnodes = [self selectedNodes];
773
774  if ([selnodes count]) {
775    NSAutoreleasePool *pool;
776
777    firstext = [[[selnodes objectAtIndex: 0] path] pathExtension];
778
779    for (i = 0; i < [selnodes count]; i++) {
780      FSNode *snode = [selnodes objectAtIndex: i];
781      NSString *selpath = [snode path];
782      NSString *ext = [selpath pathExtension];
783
784      if ([ext isEqual: firstext] == NO) {
785        return [super menuForEvent: theEvent];
786      }
787
788      if ([snode isDirectory] == NO) {
789        if ([snode isPlain] == NO) {
790          return [super menuForEvent: theEvent];
791        }
792      } else {
793        if (([snode isPackage] == NO) || [snode isApplication]) {
794          return [super menuForEvent: theEvent];
795        }
796      }
797    }
798
799    menu = [[NSMenu alloc] initWithTitle: NSLocalizedStringFromTableInBundle(@"Open with", nil, [NSBundle bundleForClass:[self class]], @"")];
800    apps = [[NSWorkspace sharedWorkspace] infoForExtension: firstext];
801    app_enum = [[apps allKeys] objectEnumerator];
802
803    pool = [NSAutoreleasePool new];
804
805    while ((key = [app_enum nextObject])) {
806      menuItem = [NSMenuItem new];
807      key = [key stringByDeletingPathExtension];
808      [menuItem setTitle: key];
809      [menuItem setTarget: desktopApp];
810      [menuItem setAction: @selector(openSelectionWithApp:)];
811      [menuItem setRepresentedObject: key];
812      [menu addItem: menuItem];
813      RELEASE (menuItem);
814    }
815
816    RELEASE (pool);
817
818    return [menu autorelease];
819  }
820
821  return [super menuForEvent: theEvent];
822}
823
824- (void)resizeWithOldSuperviewSize:(NSSize)oldFrameSize
825{
826  [self tile];
827}
828
829- (void)viewDidMoveToSuperview
830{
831  [super viewDidMoveToSuperview];
832
833  if ([self superview]) {
834    [[self window] setBackgroundColor: backColor];
835  }
836}
837
838- (void)drawRect:(NSRect)rect
839{
840  [super drawRect: rect];
841  [backColor set];
842  NSRectFill(rect);
843}
844
845- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
846{
847  return YES;
848}
849
850- (BOOL)acceptsFirstResponder
851{
852  return YES;
853}
854
855@end
856
857
858@implementation FSNIconsView (NodeRepContainer)
859
860- (void)showContentsOfNode:(FSNode *)anode
861{
862  CREATE_AUTORELEASE_POOL(arp);
863  NSArray *subNodes = [anode subNodes];
864  NSUInteger i;
865
866  for (i = 0; i < [icons count]; i++) {
867    [[icons objectAtIndex: i] removeFromSuperview];
868  }
869  [icons removeAllObjects];
870  editIcon = nil;
871
872  ASSIGN (node, anode);
873  [self readNodeInfo];
874  [self calculateGridSize];
875
876  for (i = 0; i < [subNodes count]; i++) {
877    FSNode *subnode = [subNodes objectAtIndex: i];
878    FSNIcon *icon = [[FSNIcon alloc] initForNode: subnode
879                                    nodeInfoType: infoType
880                                    extendedType: extInfoType
881                                        iconSize: iconSize
882                                    iconPosition: iconPosition
883                                       labelFont: labelFont
884                                       textColor: textColor
885                                       gridIndex: -1
886                                       dndSource: YES
887                                       acceptDnd: YES
888                                       slideBack: YES];
889    [icons addObject: icon];
890    [self addSubview: icon];
891    RELEASE (icon);
892  }
893
894  [icons sortUsingSelector: [fsnodeRep compareSelectorForDirectory: [node path]]];
895  [self tile];
896
897  DESTROY (lastSelection);
898  [self selectionDidChange];
899  RELEASE (arp);
900}
901
902- (NSDictionary *)readNodeInfo
903{
904  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
905  NSString *prefsname = [NSString stringWithFormat: @"viewer_at_%@", [node path]];
906  NSDictionary *nodeDict = nil;
907
908  if ([node isWritable]
909          && ([[fsnodeRep volumes] containsObject: [node path]] == NO)) {
910    NSString *infoPath = [[node path] stringByAppendingPathComponent: @".gwdir"];
911
912    if ([[NSFileManager defaultManager] fileExistsAtPath: infoPath]) {
913      NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: infoPath];
914
915      if (dict) {
916        nodeDict = [NSDictionary dictionaryWithDictionary: dict];
917      }
918    }
919  }
920
921  if (nodeDict == nil) {
922    id defEntry = [defaults dictionaryForKey: prefsname];
923
924    if (defEntry) {
925      nodeDict = [NSDictionary dictionaryWithDictionary: defEntry];
926    }
927  }
928
929  if (nodeDict) {
930    id entry = [nodeDict objectForKey: @"iconsize"];
931    iconSize = entry ? [entry intValue] : iconSize;
932
933    entry = [nodeDict objectForKey: @"labeltxtsize"];
934    if (entry) {
935      labelTextSize = [entry intValue];
936      ASSIGN (labelFont, [NSFont systemFontOfSize: labelTextSize]);
937    }
938
939    entry = [nodeDict objectForKey: @"iconposition"];
940    iconPosition = entry ? [entry intValue] : iconPosition;
941
942    entry = [nodeDict objectForKey: @"fsn_info_type"];
943    infoType = entry ? [entry intValue] : infoType;
944
945    if (infoType == FSNInfoExtendedType) {
946      DESTROY (extInfoType);
947      entry = [nodeDict objectForKey: @"ext_info_type"];
948
949      if (entry) {
950        NSArray *availableTypes = [fsnodeRep availableExtendedInfoNames];
951
952        if ([availableTypes containsObject: entry]) {
953          ASSIGN (extInfoType, entry);
954        }
955      }
956
957      if (extInfoType == nil) {
958        infoType = FSNInfoNameType;
959      }
960    }
961  }
962
963  return nodeDict;
964}
965
966- (NSMutableDictionary *)updateNodeInfo:(BOOL)ondisk
967{
968  CREATE_AUTORELEASE_POOL(arp);
969  NSMutableDictionary *updatedInfo = nil;
970
971  if ([node isValid]) {
972    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
973    NSString *prefsname = [NSString stringWithFormat: @"viewer_at_%@", [node path]];
974    NSString *infoPath = [[node path] stringByAppendingPathComponent: @".gwdir"];
975    BOOL writable = ([node isWritable] && ([[fsnodeRep volumes] containsObject: [node path]] == NO));
976
977    if (writable) {
978      if ([[NSFileManager defaultManager] fileExistsAtPath: infoPath]) {
979        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: infoPath];
980
981        if (dict) {
982          updatedInfo = [dict mutableCopy];
983        }
984      }
985
986    } else {
987      NSDictionary *prefs = [defaults dictionaryForKey: prefsname];
988
989      if (prefs) {
990        updatedInfo = [prefs mutableCopy];
991      }
992    }
993
994    if (updatedInfo == nil) {
995      updatedInfo = [NSMutableDictionary new];
996    }
997
998    [updatedInfo setObject: [NSNumber numberWithInt: iconSize]
999                    forKey: @"iconsize"];
1000
1001    [updatedInfo setObject: [NSNumber numberWithInt: labelTextSize]
1002                    forKey: @"labeltxtsize"];
1003
1004    [updatedInfo setObject: [NSNumber numberWithInt: iconPosition]
1005                    forKey: @"iconposition"];
1006
1007    [updatedInfo setObject: [NSNumber numberWithInt: infoType]
1008                    forKey: @"fsn_info_type"];
1009
1010    if (infoType == FSNInfoExtendedType) {
1011      [updatedInfo setObject: extInfoType forKey: @"ext_info_type"];
1012    }
1013
1014    if (ondisk) {
1015      if (writable) {
1016        [updatedInfo writeToFile: infoPath atomically: YES];
1017      } else {
1018        [defaults setObject: updatedInfo forKey: prefsname];
1019      }
1020    }
1021  }
1022
1023  RELEASE (arp);
1024
1025  return (AUTORELEASE (updatedInfo));
1026}
1027
1028- (void)reloadContents
1029{
1030  NSArray *selection = [self selectedNodes];
1031  NSMutableArray *opennodes = [NSMutableArray array];
1032  NSUInteger i;
1033
1034  RETAIN (selection);
1035
1036  for (i = 0; i < [icons count]; i++) {
1037    FSNIcon *icon = [icons objectAtIndex: i];
1038
1039    if ([icon isOpened]) {
1040      [opennodes addObject: [icon node]];
1041    }
1042  }
1043
1044  RETAIN (opennodes);
1045
1046  [self showContentsOfNode: node];
1047
1048  selectionMask = FSNMultipleSelectionMask;
1049  selectionMask |= FSNCreatingSelectionMask;
1050
1051  for (i = 0; i < [selection count]; i++) {
1052    FSNode *nd = [selection objectAtIndex: i];
1053
1054    if ([nd isValid]) {
1055      FSNIcon *icon = [self repOfSubnode: nd];
1056
1057      if (icon) {
1058        [icon select];
1059      }
1060    }
1061  }
1062
1063  selectionMask = NSSingleSelectionMask;
1064
1065  RELEASE (selection);
1066
1067  for (i = 0; i < [opennodes count]; i++) {
1068    FSNode *nd = [opennodes objectAtIndex: i];
1069
1070    if ([nd isValid]) {
1071      FSNIcon *icon = [self repOfSubnode: nd];
1072
1073      if (icon) {
1074        [icon setOpened: YES];
1075      }
1076    }
1077  }
1078
1079  RELEASE (opennodes);
1080
1081  [self checkLockedReps];
1082  [self tile];
1083
1084  selection = [self selectedReps];
1085
1086  if ([selection count]) {
1087    [self scrollIconToVisible: [selection objectAtIndex: 0]];
1088  }
1089
1090  [self selectionDidChange];
1091}
1092
1093- (void)reloadFromNode:(FSNode *)anode
1094{
1095  if ([node isEqual: anode]) {
1096    [self reloadContents];
1097
1098  } else if ([node isSubnodeOfNode: anode]) {
1099    NSArray *components = [FSNode nodeComponentsFromNode: anode toNode: node];
1100    int i;
1101
1102    for (i = 0; i < [components count]; i++) {
1103      FSNode *component = [components objectAtIndex: i];
1104
1105      if ([component isValid] == NO) {
1106        component = [FSNode nodeWithPath: [component parentPath]];
1107        [self showContentsOfNode: component];
1108        break;
1109      }
1110    }
1111  }
1112}
1113
1114- (FSNode *)baseNode
1115{
1116  return node;
1117}
1118
1119- (FSNode *)shownNode
1120{
1121  return node;
1122}
1123
1124- (BOOL)isSingleNode
1125{
1126  return YES;
1127}
1128
1129- (BOOL)isShowingNode:(FSNode *)anode
1130{
1131  return [node isEqual: anode];
1132}
1133
1134- (BOOL)isShowingPath:(NSString *)path
1135{
1136  return [[node path] isEqual: path];
1137}
1138
1139- (void)sortTypeChangedAtPath:(NSString *)path
1140{
1141  if ((path == nil) || [[node path] isEqual: path]) {
1142    [self reloadContents];
1143  }
1144}
1145
1146- (void)nodeContentsWillChange:(NSDictionary *)info
1147{
1148  [self checkLockedReps];
1149}
1150
1151- (void)nodeContentsDidChange:(NSDictionary *)info
1152{
1153  NSString *operation = [info objectForKey: @"operation"];
1154  NSString *source = [info objectForKey: @"source"];
1155  NSString *destination = [info objectForKey: @"destination"];
1156  NSArray *files = [info objectForKey: @"files"];
1157  NSString *ndpath = [node path];
1158  NSUInteger i;
1159
1160  if ([operation isEqual: @"GWorkspaceRenameOperation"]) {
1161    files = [NSArray arrayWithObject: [source lastPathComponent]];
1162    source = [source stringByDeletingLastPathComponent];
1163  }
1164
1165  if (([ndpath isEqual: source] == NO) && ([ndpath isEqual: destination] == NO)) {
1166    [self reloadContents];
1167    return;
1168  }
1169
1170  if ([ndpath isEqual: source]) {
1171    if ([operation isEqual: NSWorkspaceMoveOperation]
1172              || [operation isEqual: NSWorkspaceDestroyOperation]
1173              || [operation isEqual: @"GWorkspaceRenameOperation"]
1174              || [operation isEqual: NSWorkspaceRecycleOperation]
1175			        || [operation isEqual: @"GWorkspaceRecycleOutOperation"]) {
1176
1177      if ([operation isEqual: NSWorkspaceRecycleOperation]) {
1178		    files = [info objectForKey: @"origfiles"];
1179      }
1180
1181      for (i = 0; i < [files count]; i++) {
1182        NSString *fname = [files objectAtIndex: i];
1183        FSNode *subnode = [FSNode nodeWithRelativePath: fname parent: node];
1184        [self removeRepOfSubnode: subnode];
1185      }
1186    }
1187  }
1188
1189  if ([operation isEqual: @"GWorkspaceRenameOperation"]) {
1190    files = [NSArray arrayWithObject: [destination lastPathComponent]];
1191    destination = [destination stringByDeletingLastPathComponent];
1192  }
1193
1194  if ([ndpath isEqual: destination]
1195          && ([operation isEqual: NSWorkspaceMoveOperation]
1196              || [operation isEqual: NSWorkspaceCopyOperation]
1197              || [operation isEqual: NSWorkspaceLinkOperation]
1198              || [operation isEqual: NSWorkspaceDuplicateOperation]
1199              || [operation isEqual: @"GWorkspaceCreateDirOperation"]
1200              || [operation isEqual: @"GWorkspaceCreateFileOperation"]
1201              || [operation isEqual: NSWorkspaceRecycleOperation]
1202              || [operation isEqual: @"GWorkspaceRenameOperation"]
1203				      || [operation isEqual: @"GWorkspaceRecycleOutOperation"])) {
1204
1205    if ([operation isEqual: NSWorkspaceRecycleOperation]) {
1206		  files = [info objectForKey: @"files"];
1207    }
1208
1209    for (i = 0; i < [files count]; i++) {
1210      NSString *fname = [files objectAtIndex: i];
1211      FSNode *subnode = [FSNode nodeWithRelativePath: fname parent: node];
1212      FSNIcon *icon = [self repOfSubnode: subnode];
1213
1214      if (icon) {
1215        [icon setNode: subnode];
1216      } else {
1217        [self addRepForSubnode: subnode];
1218      }
1219    }
1220
1221    [self sortIcons];
1222  }
1223
1224  [self checkLockedReps];
1225  [self tile];
1226  [self setNeedsDisplay: YES];
1227  [self selectionDidChange];
1228}
1229
1230- (void)watchedPathChanged:(NSDictionary *)info
1231{
1232  NSString *event = [info objectForKey: @"event"];
1233  NSArray *files = [info objectForKey: @"files"];
1234  NSString *ndpath = [node path];
1235  NSUInteger i;
1236
1237  if ([event isEqual: @"GWFileDeletedInWatchedDirectory"]) {
1238    for (i = 0; i < [files count]; i++) {
1239      NSString *fname = [files objectAtIndex: i];
1240      NSString *fpath = [ndpath stringByAppendingPathComponent: fname];
1241      [self removeRepOfSubnodePath: fpath];
1242    }
1243
1244  } else if ([event isEqual: @"GWFileCreatedInWatchedDirectory"]) {
1245    for (i = 0; i < [files count]; i++) {
1246      NSString *fname = [files objectAtIndex: i];
1247      FSNode *subnode = [FSNode nodeWithRelativePath: fname parent: node];
1248
1249      if (subnode && [subnode isValid]) {
1250        FSNIcon *icon = [self repOfSubnode: subnode];
1251
1252        if (icon) {
1253          [icon setNode: subnode];
1254        } else {
1255          [self addRepForSubnode: subnode];
1256        }
1257      }
1258    }
1259  }
1260
1261  [self sortIcons];
1262  [self tile];
1263  [self setNeedsDisplay: YES];
1264  [self selectionDidChange];
1265}
1266
1267- (void)setShowType:(FSNInfoType)type
1268{
1269  if (infoType != type) {
1270    NSUInteger i;
1271
1272    infoType = type;
1273    DESTROY (extInfoType);
1274
1275    [self calculateGridSize];
1276
1277    for (i = 0; i < [icons count]; i++) {
1278      FSNIcon *icon = [icons objectAtIndex: i];
1279
1280      [icon setNodeInfoShowType: infoType];
1281      [icon tile];
1282    }
1283
1284    [self sortIcons];
1285    [self tile];
1286  }
1287}
1288
1289- (void)setExtendedShowType:(NSString *)type
1290{
1291  if ((extInfoType == nil) || ([extInfoType isEqual: type] == NO)) {
1292    int i;
1293
1294    infoType = FSNInfoExtendedType;
1295    ASSIGN (extInfoType, type);
1296
1297    [self calculateGridSize];
1298
1299    for (i = 0; i < [icons count]; i++) {
1300      FSNIcon *icon = [icons objectAtIndex: i];
1301
1302      [icon setExtendedShowType: extInfoType];
1303      [icon tile];
1304    }
1305
1306    [self sortIcons];
1307    [self tile];
1308  }
1309}
1310
1311- (FSNInfoType)showType
1312{
1313  return infoType;
1314}
1315
1316- (void)setIconSize:(int)size
1317{
1318  NSUInteger i;
1319
1320  iconSize = size;
1321  [self calculateGridSize];
1322
1323  for (i = 0; i < [icons count]; i++) {
1324    FSNIcon *icon = [icons objectAtIndex: i];
1325    [icon setIconSize: iconSize];
1326  }
1327
1328  [self tile];
1329}
1330
1331- (int)iconSize
1332{
1333  return iconSize;
1334}
1335
1336- (void)setLabelTextSize:(int)size
1337{
1338  NSUInteger i;
1339
1340  labelTextSize = size;
1341  ASSIGN (labelFont, [NSFont systemFontOfSize: labelTextSize]);
1342  [self calculateGridSize];
1343
1344  for (i = 0; i < [icons count]; i++) {
1345    FSNIcon *icon = [icons objectAtIndex: i];
1346    [icon setFont: labelFont];
1347  }
1348
1349  [nameEditor setFont: labelFont];
1350
1351  [self tile];
1352}
1353
1354- (int)labelTextSize
1355{
1356  return labelTextSize;
1357}
1358
1359- (void)setIconPosition:(int)pos
1360{
1361  NSUInteger i;
1362
1363  iconPosition = pos;
1364  [self calculateGridSize];
1365
1366  for (i = 0; i < [icons count]; i++) {
1367    FSNIcon *icon = [icons objectAtIndex: i];
1368    [icon setIconPosition: iconPosition];
1369  }
1370
1371  [self tile];
1372}
1373
1374- (int)iconPosition
1375{
1376  return iconPosition;
1377}
1378
1379- (void)updateIcons
1380{
1381  NSUInteger i;
1382
1383  for (i = 0; i < [icons count]; i++) {
1384    FSNIcon *icon = [icons objectAtIndex: i];
1385    FSNode *inode = [icon node];
1386    [icon setNode: inode];
1387  }
1388}
1389
1390- (id)repOfSubnode:(FSNode *)anode
1391{
1392  NSUInteger i;
1393
1394  for (i = 0; i < [icons count]; i++) {
1395    FSNIcon *icon = [icons objectAtIndex: i];
1396
1397    if ([[icon node] isEqualToNode: anode]) {
1398      return icon;
1399    }
1400  }
1401
1402  return nil;
1403}
1404
1405- (id)repOfSubnodePath:(NSString *)apath
1406{
1407  NSUInteger i;
1408
1409  for (i = 0; i < [icons count]; i++) {
1410    FSNIcon *icon = [icons objectAtIndex: i];
1411
1412    if ([[[icon node] path] isEqual: apath]) {
1413      return icon;
1414    }
1415  }
1416
1417  return nil;
1418}
1419
1420- (id)addRepForSubnode:(FSNode *)anode
1421{
1422  CREATE_AUTORELEASE_POOL(arp);
1423  FSNIcon *icon = [[FSNIcon alloc] initForNode: anode
1424                                  nodeInfoType: infoType
1425                                  extendedType: extInfoType
1426                                      iconSize: iconSize
1427                                  iconPosition: iconPosition
1428                                     labelFont: labelFont
1429                                     textColor: textColor
1430                                     gridIndex: -1
1431                                     dndSource: YES
1432                                     acceptDnd: YES
1433                                     slideBack: YES];
1434  [icons addObject: icon];
1435  [self addSubview: icon];
1436  RELEASE (icon);
1437  RELEASE (arp);
1438
1439  return icon;
1440}
1441
1442- (id)addRepForSubnodePath:(NSString *)apath
1443{
1444  FSNode *subnode = [FSNode nodeWithRelativePath: apath parent: node];
1445  return [self addRepForSubnode: subnode];
1446}
1447
1448- (void)removeRepOfSubnode:(FSNode *)anode
1449{
1450  FSNIcon *icon = [self repOfSubnode: anode];
1451
1452  if (icon) {
1453    [self removeRep: icon];
1454  }
1455}
1456
1457- (void)removeRepOfSubnodePath:(NSString *)apath
1458{
1459  FSNIcon *icon = [self repOfSubnodePath: apath];
1460
1461  if (icon) {
1462    [self removeRep: icon];
1463  }
1464}
1465
1466- (void)removeRep:(id)arep
1467{
1468  if (arep == editIcon) {
1469    editIcon = nil;
1470  }
1471  [arep removeFromSuperview];
1472  [icons removeObject: arep];
1473}
1474
1475- (void)unloadFromNode:(FSNode *)anode
1476{
1477  FSNode *parent = [FSNode nodeWithPath: [anode parentPath]];
1478  [self showContentsOfNode: parent];
1479}
1480
1481- (void)repSelected:(id)arep
1482{
1483}
1484
1485- (void)unselectOtherReps:(id)arep
1486{
1487  NSUInteger i;
1488
1489  if (selectionMask & FSNMultipleSelectionMask) {
1490    return;
1491  }
1492
1493  for (i = 0; i < [icons count]; i++) {
1494    FSNIcon *icon = [icons objectAtIndex: i];
1495
1496    if (icon != arep) {
1497      [icon unselect];
1498    }
1499  }
1500}
1501
1502- (void)selectReps:(NSArray *)reps
1503{
1504  NSUInteger i;
1505
1506  selectionMask = NSSingleSelectionMask;
1507  selectionMask |= FSNCreatingSelectionMask;
1508
1509  [self unselectOtherReps: nil];
1510
1511  selectionMask = FSNMultipleSelectionMask;
1512  selectionMask |= FSNCreatingSelectionMask;
1513
1514  for (i = 0; i < [reps count]; i++) {
1515    [[reps objectAtIndex: i] select];
1516  }
1517
1518  selectionMask = NSSingleSelectionMask;
1519
1520  [self selectionDidChange];
1521}
1522
1523- (void)selectRepsOfSubnodes:(NSArray *)nodes
1524{
1525  NSUInteger i;
1526
1527  selectionMask = NSSingleSelectionMask;
1528  selectionMask |= FSNCreatingSelectionMask;
1529
1530  [self unselectOtherReps: nil];
1531
1532  selectionMask = FSNMultipleSelectionMask;
1533  selectionMask |= FSNCreatingSelectionMask;
1534
1535  for (i = 0; i < [icons count]; i++) {
1536    FSNIcon *icon = [icons objectAtIndex: i];
1537
1538    if ([nodes containsObject: [icon node]]) {
1539      [icon select];
1540    }
1541  }
1542
1543  selectionMask = NSSingleSelectionMask;
1544
1545  [self selectionDidChange];
1546}
1547
1548- (void)selectRepsOfPaths:(NSArray *)paths
1549{
1550  NSUInteger i;
1551
1552  selectionMask = NSSingleSelectionMask;
1553  selectionMask |= FSNCreatingSelectionMask;
1554
1555  [self unselectOtherReps: nil];
1556
1557  selectionMask = FSNMultipleSelectionMask;
1558  selectionMask |= FSNCreatingSelectionMask;
1559
1560  for (i = 0; i < [icons count]; i++) {
1561    FSNIcon *icon = [icons objectAtIndex: i];
1562
1563    if ([paths containsObject: [[icon node] path]]) {
1564      [icon select];
1565    }
1566  }
1567
1568  selectionMask = NSSingleSelectionMask;
1569
1570  [self selectionDidChange];
1571}
1572
1573- (void)selectAll
1574{
1575  NSUInteger i;
1576
1577  selectionMask = NSSingleSelectionMask;
1578  selectionMask |= FSNCreatingSelectionMask;
1579
1580  [self unselectOtherReps: nil];
1581
1582  selectionMask = FSNMultipleSelectionMask;
1583  selectionMask |= FSNCreatingSelectionMask;
1584
1585  for (i = 0; i < [icons count]; i++) {
1586    FSNIcon *icon = [icons objectAtIndex: i];
1587    FSNode *inode = [icon node];
1588
1589    if ([inode isReserved] == NO) {
1590      [icon select];
1591    }
1592	}
1593
1594  selectionMask = NSSingleSelectionMask;
1595
1596  [self selectionDidChange];
1597}
1598
1599- (void)scrollSelectionToVisible
1600{
1601  NSArray *selection = [self selectedReps];
1602
1603  if ([selection count]) {
1604    [self scrollIconToVisible: [selection objectAtIndex: 0]];
1605  } else {
1606    NSRect r = [self frame];
1607    [self scrollRectToVisible: NSMakeRect(0, r.size.height - 1, 1, 1)];
1608  }
1609}
1610
1611- (NSArray *)reps
1612{
1613  return icons;
1614}
1615
1616- (NSArray *)selectedReps
1617{
1618  NSMutableArray *selectedReps = [NSMutableArray array];
1619  NSUInteger i;
1620
1621  for (i = 0; i < [icons count]; i++) {
1622    FSNIcon *icon = [icons objectAtIndex: i];
1623
1624    if ([icon isSelected]) {
1625      [selectedReps addObject: icon];
1626    }
1627  }
1628
1629  return [selectedReps makeImmutableCopyOnFail: NO];
1630}
1631
1632- (NSArray *)selectedNodes
1633{
1634  NSMutableArray *selectedNodes = [NSMutableArray array];
1635  NSUInteger i;
1636
1637  for (i = 0; i < [icons count]; i++) {
1638    FSNIcon *icon = [icons objectAtIndex: i];
1639
1640    if ([icon isSelected]) {
1641      NSArray *selection = [icon selection];
1642
1643      if (selection) {
1644        [selectedNodes addObjectsFromArray: selection];
1645      } else {
1646        [selectedNodes addObject: [icon node]];
1647      }
1648    }
1649  }
1650
1651  return [selectedNodes makeImmutableCopyOnFail: NO];
1652}
1653
1654- (NSArray *)selectedPaths
1655{
1656  NSMutableArray *selectedPaths = [NSMutableArray array];
1657  NSUInteger i, j;
1658
1659  for (i = 0; i < [icons count]; i++) {
1660    FSNIcon *icon = [icons objectAtIndex: i];
1661
1662    if ([icon isSelected]) {
1663      NSArray *selection = [icon selection];
1664
1665      if (selection) {
1666        for (j = 0; j < [selection count]; j++) {
1667          [selectedPaths addObject: [[selection objectAtIndex: j] path]];
1668        }
1669      } else {
1670        [selectedPaths addObject: [[icon node] path]];
1671      }
1672    }
1673  }
1674
1675  return [selectedPaths makeImmutableCopyOnFail: NO];
1676}
1677
1678- (void)selectionDidChange
1679{
1680  if (!(selectionMask & FSNCreatingSelectionMask)) {
1681    NSArray *selection = [self selectedNodes];
1682
1683    if ([selection count] == 0) {
1684      selection = [NSArray arrayWithObject: node];
1685    }
1686
1687    if ((lastSelection == nil) || ([selection isEqual: lastSelection] == NO)) {
1688      ASSIGN (lastSelection, selection);
1689      [desktopApp selectionChanged: selection];
1690    }
1691
1692    [self updateNameEditor];
1693	}
1694}
1695
1696- (void)checkLockedReps
1697{
1698  NSUInteger i;
1699
1700  for (i = 0; i < [icons count]; i++) {
1701    [[icons objectAtIndex: i] checkLocked];
1702  }
1703}
1704
1705- (void)setSelectionMask:(FSNSelectionMask)mask
1706{
1707  selectionMask = mask;
1708}
1709
1710- (FSNSelectionMask)selectionMask
1711{
1712  return selectionMask;
1713}
1714
1715- (void)openSelectionInNewViewer:(BOOL)newv
1716{
1717  [desktopApp openSelectionInNewViewer: newv];
1718}
1719
1720- (void)restoreLastSelection
1721{
1722  if (lastSelection) {
1723    [self selectRepsOfSubnodes: lastSelection];
1724  }
1725}
1726
1727- (void)setLastShownNode:(FSNode *)anode
1728{
1729}
1730
1731- (BOOL)needsDndProxy
1732{
1733  return NO;
1734}
1735
1736- (BOOL)involvedByFileOperation:(NSDictionary *)opinfo
1737{
1738  return [node involvedByFileOperation: opinfo];
1739}
1740
1741- (BOOL)validatePasteOfFilenames:(NSArray *)names
1742                       wasCut:(BOOL)cut
1743{
1744  NSString *nodePath = [node path];
1745  NSString *prePath = [NSString stringWithString: nodePath];
1746  NSString *basePath;
1747
1748  if ([names count] == 0) {
1749		return NO;
1750  }
1751
1752  if ([node isWritable] == NO) {
1753    return NO;
1754  }
1755
1756  basePath = [[names objectAtIndex: 0] stringByDeletingLastPathComponent];
1757  if ([basePath isEqual: nodePath]) {
1758    return NO;
1759  }
1760
1761  if ([names containsObject: nodePath]) {
1762    return NO;
1763  }
1764
1765  while (1) {
1766    if ([names containsObject: prePath]) {
1767      return NO;
1768    }
1769    if ([prePath isEqual: path_separator()]) {
1770      break;
1771    }
1772    prePath = [prePath stringByDeletingLastPathComponent];
1773  }
1774
1775  return YES;
1776}
1777
1778- (void)setBackgroundColor:(NSColor *)acolor
1779{
1780  ASSIGN (backColor, acolor);
1781  [[self window] setBackgroundColor: backColor];
1782  [self setNeedsDisplay: YES];
1783}
1784
1785- (NSColor *)backgroundColor
1786{
1787  return backColor;
1788}
1789
1790- (void)setTextColor:(NSColor *)acolor
1791{
1792  NSUInteger i;
1793
1794  for (i = 0; i < [icons count]; i++) {
1795    [[icons objectAtIndex: i] setLabelTextColor: acolor];
1796  }
1797
1798  [nameEditor setTextColor: acolor];
1799
1800  ASSIGN (textColor, acolor);
1801}
1802
1803- (NSColor *)textColor
1804{
1805  return textColor;
1806}
1807
1808- (NSColor *)disabledTextColor
1809{
1810  return disabledTextColor;
1811}
1812
1813@end
1814
1815
1816@implementation FSNIconsView (DraggingDestination)
1817
1818- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1819{
1820  NSPasteboard *pb;
1821  NSDragOperation sourceDragMask;
1822  NSArray *sourcePaths;
1823  NSString *basePath;
1824  NSString *nodePath;
1825  NSString *prePath;
1826  NSUInteger count;
1827
1828	isDragTarget = NO;
1829
1830 	pb = [sender draggingPasteboard];
1831
1832  if (pb && [[pb types] containsObject: NSFilenamesPboardType]) {
1833    sourcePaths = [pb propertyListForType: NSFilenamesPboardType];
1834
1835  } else if ([[pb types] containsObject: @"GWRemoteFilenamesPboardType"]) {
1836    NSData *pbData = [pb dataForType: @"GWRemoteFilenamesPboardType"];
1837    NSDictionary *pbDict = [NSUnarchiver unarchiveObjectWithData: pbData];
1838
1839    sourcePaths = [pbDict objectForKey: @"paths"];
1840
1841  } else if ([[pb types] containsObject: @"GWLSFolderPboardType"]) {
1842    NSData *pbData = [pb dataForType: @"GWLSFolderPboardType"];
1843    NSDictionary *pbDict = [NSUnarchiver unarchiveObjectWithData: pbData];
1844
1845    sourcePaths = [pbDict objectForKey: @"paths"];
1846
1847  } else {
1848    return NSDragOperationNone;
1849  }
1850
1851	count = [sourcePaths count];
1852	if (count == 0) {
1853		return NSDragOperationNone;
1854  }
1855
1856  if ([node isWritable] == NO) {
1857    return NSDragOperationNone;
1858  }
1859
1860  nodePath = [node path];
1861
1862  basePath = [[sourcePaths objectAtIndex: 0] stringByDeletingLastPathComponent];
1863  if ([basePath isEqual: nodePath]) {
1864    return NSDragOperationNone;
1865  }
1866
1867  if ([sourcePaths containsObject: nodePath]) {
1868    return NSDragOperationNone;
1869  }
1870
1871  prePath = [NSString stringWithString: nodePath];
1872
1873  while (1) {
1874    if ([sourcePaths containsObject: prePath]) {
1875      return NSDragOperationNone;
1876    }
1877    if ([prePath isEqual: path_separator()]) {
1878      break;
1879    }
1880    prePath = [prePath stringByDeletingLastPathComponent];
1881  }
1882
1883  if ([node isDirectory] && [node isParentOfPath: basePath]) {
1884    NSArray *subNodes = [node subNodes];
1885    NSUInteger i;
1886
1887    for (i = 0; i < [subNodes count]; i++) {
1888      FSNode *nd = [subNodes objectAtIndex: i];
1889
1890      if ([nd isDirectory]) {
1891        NSUInteger j;
1892
1893        for (j = 0; j < count; j++) {
1894          NSString *fname = [[sourcePaths objectAtIndex: j] lastPathComponent];
1895
1896          if ([[nd name] isEqual: fname]) {
1897            return NSDragOperationNone;
1898          }
1899        }
1900      }
1901    }
1902  }
1903
1904  isDragTarget = YES;
1905  forceCopy = NO;
1906
1907	sourceDragMask = [sender draggingSourceOperationMask];
1908
1909	if (sourceDragMask == NSDragOperationCopy) {
1910		return NSDragOperationCopy;
1911	} else if (sourceDragMask == NSDragOperationLink) {
1912		return NSDragOperationLink;
1913	} else {
1914    if ([[NSFileManager defaultManager] isWritableFileAtPath: basePath]) {
1915      return NSDragOperationAll;
1916    } else {
1917      forceCopy = YES;
1918			return NSDragOperationCopy;
1919    }
1920	}
1921
1922  isDragTarget = NO;
1923  return NSDragOperationNone;
1924}
1925
1926- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1927{
1928  NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
1929  NSRect vr = [self visibleRect];
1930  NSRect scr = vr;
1931  int xsc = 0.0;
1932  int ysc = 0.0;
1933  int sc = 0;
1934  float margin = 4.0;
1935  NSRect ir = NSInsetRect(vr, margin, margin);
1936  NSPoint p = [sender draggingLocation];
1937  int i;
1938
1939  p = [self convertPoint: p fromView: nil];
1940
1941  if ([self mouse: p inRect: ir] == NO) {
1942    if (p.x < (NSMinX(vr) + margin)) {
1943      xsc = -gridSize.width;
1944    } else if (p.x > (NSMaxX(vr) - margin)) {
1945      xsc = gridSize.width;
1946    }
1947
1948    if (p.y < (NSMinY(vr) + margin)) {
1949      ysc = -gridSize.height;
1950    } else if (p.y > (NSMaxY(vr) - margin)) {
1951      ysc = gridSize.height;
1952    }
1953
1954    sc = (abs(xsc) >= abs(ysc)) ? xsc : ysc;
1955
1956    for (i = 0; i < (int)fabsf(sc / margin); i++) {
1957      CREATE_AUTORELEASE_POOL (pool);
1958      NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 0.01];
1959      int x = (abs(xsc) >= i) ? (xsc > 0 ? margin : -margin) : 0;
1960      int y = (abs(ysc) >= i) ? (ysc > 0 ? margin : -margin) : 0;
1961
1962      scr = NSOffsetRect(scr, x, y);
1963      [self scrollRectToVisible: scr];
1964
1965      vr = [self visibleRect];
1966      ir = NSInsetRect(vr, margin, margin);
1967
1968      p = [[self window] mouseLocationOutsideOfEventStream];
1969      p = [self convertPoint: p fromView: nil];
1970
1971      if ([self mouse: p inRect: ir]) {
1972        RELEASE (pool);
1973        break;
1974      }
1975
1976      [[NSRunLoop currentRunLoop] runUntilDate: limit];
1977      RELEASE (pool);
1978    }
1979  }
1980
1981	if (isDragTarget == NO) {
1982		return NSDragOperationNone;
1983	}
1984
1985	if (sourceDragMask == NSDragOperationCopy) {
1986		return NSDragOperationCopy;
1987	} else if (sourceDragMask == NSDragOperationLink) {
1988		return NSDragOperationLink;
1989	} else {
1990		return forceCopy ? NSDragOperationCopy : NSDragOperationAll;
1991	}
1992
1993	return NSDragOperationNone;
1994}
1995
1996- (void)draggingExited:(id <NSDraggingInfo>)sender
1997{
1998	isDragTarget = NO;
1999}
2000
2001- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
2002{
2003	return isDragTarget;
2004}
2005
2006- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
2007{
2008	return YES;
2009}
2010
2011- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
2012{
2013  NSPasteboard *pb;
2014  NSDragOperation sourceDragMask;
2015  NSArray *sourcePaths;
2016  NSString *operation, *source;
2017  NSMutableArray *files;
2018  NSMutableDictionary *opDict;
2019  NSString *trashPath;
2020  NSUInteger i;
2021
2022  isDragTarget = NO;
2023
2024  sourceDragMask = [sender draggingSourceOperationMask];
2025  pb = [sender draggingPasteboard];
2026
2027  if ([[pb types] containsObject: @"GWRemoteFilenamesPboardType"]) {
2028    NSData *pbData = [pb dataForType: @"GWRemoteFilenamesPboardType"];
2029
2030    [desktopApp concludeRemoteFilesDragOperation: pbData
2031                                     atLocalPath: [node path]];
2032    return;
2033
2034  } else if ([[pb types] containsObject: @"GWLSFolderPboardType"]) {
2035    NSData *pbData = [pb dataForType: @"GWLSFolderPboardType"];
2036
2037    [desktopApp lsfolderDragOperation: pbData
2038                      concludedAtPath: [node path]];
2039    return;
2040  }
2041
2042  sourcePaths = [pb propertyListForType: NSFilenamesPboardType];
2043
2044  if ([sourcePaths count] == 0) {
2045    return;
2046  }
2047
2048  source = [[sourcePaths objectAtIndex: 0] stringByDeletingLastPathComponent];
2049
2050  trashPath = [desktopApp trashPath];
2051
2052  if ([source isEqual: trashPath]) {
2053    operation = @"GWorkspaceRecycleOutOperation";
2054	} else {
2055		if (sourceDragMask == NSDragOperationCopy) {
2056			operation = NSWorkspaceCopyOperation;
2057		} else if (sourceDragMask == NSDragOperationLink) {
2058			operation = NSWorkspaceLinkOperation;
2059		} else {
2060      if ([[NSFileManager defaultManager] isWritableFileAtPath: source]) {
2061			  operation = NSWorkspaceMoveOperation;
2062      } else {
2063			  operation = NSWorkspaceCopyOperation;
2064      }
2065		}
2066  }
2067
2068  files = [NSMutableArray array];
2069  for(i = 0; i < [sourcePaths count]; i++) {
2070    [files addObject: [[sourcePaths objectAtIndex: i] lastPathComponent]];
2071  }
2072
2073	opDict = [NSMutableDictionary dictionary];
2074	[opDict setObject: operation forKey: @"operation"];
2075	[opDict setObject: source forKey: @"source"];
2076	[opDict setObject: [node path] forKey: @"destination"];
2077	[opDict setObject: files forKey: @"files"];
2078
2079  [desktopApp performFileOperation: opDict];
2080}
2081
2082@end
2083
2084
2085@implementation FSNIconsView (IconNameEditing)
2086
2087- (void)updateNameEditor
2088{
2089  [self stopRepNameEditing];
2090
2091  if (lastSelection && ([lastSelection count] == 1)) {
2092    editIcon = [self repOfSubnode: [lastSelection objectAtIndex: 0]];
2093  }
2094
2095  if (editIcon) {
2096    FSNode *ednode = [editIcon node];
2097    NSString *nodeDescr = [editIcon shownInfo];
2098    NSRect icnr = [editIcon frame];
2099    NSRect labr = [editIcon labelRect];
2100    int ipos = [editIcon iconPosition];
2101    int margin = [fsnodeRep labelMargin];
2102    float bw = [self bounds].size.width - EDIT_MARGIN;
2103    float edwidth = 0.0;
2104    NSRect edrect;
2105
2106    [editIcon setNameEdited: YES];
2107
2108    edwidth = [[nameEditor font] widthOfString: nodeDescr];
2109    edwidth += margin;
2110
2111    if (ipos == NSImageAbove) {
2112      float centerx = icnr.origin.x + (icnr.size.width / 2);
2113
2114      if ((centerx + (edwidth / 2)) >= bw) {
2115        centerx -= (centerx + (edwidth / 2) - bw);
2116      } else if ((centerx - (edwidth / 2)) < margin) {
2117        centerx += fabs(centerx - (edwidth / 2)) + margin;
2118      }
2119
2120      edrect = [self convertRect: labr fromView: editIcon];
2121      edrect.origin.x = centerx - (edwidth / 2);
2122      edrect.size.width = edwidth;
2123
2124    } else if (ipos == NSImageLeft) {
2125      edrect = [self convertRect: labr fromView: editIcon];
2126      edrect.size.width = edwidth;
2127
2128      if ((edrect.origin.x + edwidth) >= bw) {
2129        edrect.size.width = bw - edrect.origin.x;
2130      }
2131    }
2132    else
2133      {
2134	NSLog(@"Unexpected icon position in [FSNIconsView updateNameEditor]");
2135	return;
2136      }
2137
2138    edrect = NSIntegralRect(edrect);
2139
2140    [nameEditor setFrame: edrect];
2141
2142    if (ipos == NSImageAbove) {
2143  	  [nameEditor setAlignment: NSCenterTextAlignment];
2144    } else if (ipos == NSImageLeft) {
2145		  [nameEditor setAlignment: NSLeftTextAlignment];
2146    }
2147
2148    [nameEditor setNode: ednode
2149            stringValue: nodeDescr
2150                  index: 0];
2151
2152    [nameEditor setBackgroundColor: [NSColor selectedControlColor]];
2153
2154    if ([editIcon isLocked] == NO) {
2155      [nameEditor setTextColor: [NSColor controlTextColor]];
2156    } else {
2157      [nameEditor setTextColor: [NSColor disabledControlTextColor]];
2158    }
2159
2160    [nameEditor setEditable: NO];
2161    [nameEditor setSelectable: NO];
2162    [self addSubview: nameEditor];
2163  }
2164}
2165
2166- (void)setNameEditorForRep:(id)arep
2167{
2168}
2169
2170- (void)stopRepNameEditing
2171{
2172  NSUInteger i;
2173
2174  if ([[self subviews] containsObject: nameEditor]) {
2175    NSRect edrect = [nameEditor frame];
2176    [nameEditor abortEditing];
2177    [nameEditor setEditable: NO];
2178    [nameEditor setSelectable: NO];
2179    [nameEditor setNode: nil stringValue: @"" index: -1];
2180    [nameEditor removeFromSuperview];
2181    [self setNeedsDisplayInRect: edrect];
2182  }
2183
2184  for (i = 0; i < [icons count]; i++) {
2185    [[icons objectAtIndex: i] setNameEdited: NO];
2186  }
2187
2188  editIcon = nil;
2189}
2190
2191- (BOOL)canStartRepNameEditing
2192{
2193  return (editIcon && ([editIcon isLocked] == NO)
2194                  && ([[editIcon node] isMountPoint] == NO));
2195}
2196
2197- (void)controlTextDidChange:(NSNotification *)aNotification
2198{
2199  NSRect icnr = [editIcon frame];
2200  int ipos = [editIcon iconPosition];
2201  float edwidth = [[nameEditor font] widthOfString: [nameEditor stringValue]];
2202  int margin = [fsnodeRep labelMargin];
2203  float bw = [self bounds].size.width - EDIT_MARGIN;
2204  NSRect edrect = [nameEditor frame];
2205
2206  edwidth += margin;
2207
2208  if (ipos == NSImageAbove) {
2209    float centerx = icnr.origin.x + (icnr.size.width / 2);
2210
2211    while ((centerx + (edwidth / 2)) > bw) {
2212      centerx --;
2213      if (centerx < EDIT_MARGIN) {
2214        break;
2215      }
2216    }
2217
2218    while ((centerx - (edwidth / 2)) < EDIT_MARGIN) {
2219      centerx ++;
2220      if (centerx >= bw) {
2221        break;
2222      }
2223    }
2224
2225    edrect.origin.x = centerx - (edwidth / 2);
2226    edrect.size.width = edwidth;
2227
2228  } else if (ipos == NSImageLeft) {
2229    edrect.size.width = edwidth;
2230
2231    if ((edrect.origin.x + edwidth) >= bw) {
2232      edrect.size.width = bw - edrect.origin.x;
2233    }
2234  }
2235
2236  [self setNeedsDisplayInRect: [nameEditor frame]];
2237  [nameEditor setFrame: NSIntegralRect(edrect)];
2238}
2239
2240- (void)controlTextDidEndEditing:(NSNotification *)aNotification
2241{
2242  FSNode *ednode = [nameEditor node];
2243
2244#define CLEAREDITING \
2245  [self stopRepNameEditing]; \
2246  return
2247
2248
2249    if ([ednode isParentWritable] == NO)
2250      {
2251	showAlertNoPermission([FSNode class], [ednode parentName]);
2252	CLEAREDITING;
2253      }
2254    else if ([ednode isSubnodeOfPath: [desktopApp trashPath]])
2255      {
2256	showAlertInRecycler([FSNode class]);
2257	CLEAREDITING;
2258      }
2259    else
2260      {
2261      NSString *newname = [nameEditor stringValue];
2262      NSString *newpath = [[ednode parentPath] stringByAppendingPathComponent: newname];
2263      NSString *extension = [newpath pathExtension];
2264      NSCharacterSet *notAllowSet = [NSCharacterSet characterSetWithCharactersInString: @"/\\*:?\33"];
2265      NSRange range = [newname rangeOfCharacterFromSet: notAllowSet];
2266      NSArray *dirContents = [ednode subNodeNamesOfParent];
2267      NSMutableDictionary *opinfo = [NSMutableDictionary dictionary];
2268
2269      if (([newname length] == 0) || (range.length > 0))
2270	{
2271	  showAlertInvalidName([FSNode class]);
2272	  CLEAREDITING;
2273	}
2274
2275      if (([extension length]
2276              && ([ednode isDirectory] && ([ednode isPackage] == NO))))
2277	{
2278          if (showAlertExtensionChange([FSNode class], extension) == NSAlertDefaultReturn)
2279            {
2280              CLEAREDITING;
2281            }
2282	}
2283
2284      if ([dirContents containsObject: newname])
2285	{
2286	  if ([newname isEqual: [ednode name]])
2287	    {
2288	      CLEAREDITING;
2289	    }
2290	  else
2291	    {
2292	      showAlertNameInUse([FSNode class], newname);
2293	      CLEAREDITING;
2294	    }
2295	}
2296
2297      [opinfo setObject: @"GWorkspaceRenameOperation" forKey: @"operation"];
2298      [opinfo setObject: [ednode path] forKey: @"source"];
2299      [opinfo setObject: newpath forKey: @"destination"];
2300      [opinfo setObject: [NSArray arrayWithObject: @""] forKey: @"files"];
2301
2302      [self stopRepNameEditing];
2303      [desktopApp performFileOperation: opinfo];
2304    }
2305}
2306
2307@end
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330