1/* GWViewerShelf.h
2 *
3 * Copyright (C) 2004-2016 Free Software Foundation, Inc.
4 *
5 * Author: Enrico Sersale <enrico@imago.ro>
6 * Date: July 2004
7 *
8 * This file is part of the GNUstep GWorkspace application
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
23 */
24
25#include <math.h>
26
27#import <AppKit/AppKit.h>
28
29#import "GWViewerShelf.h"
30#import "GWViewer.h"
31#import "GWorkspace.h"
32#import "FSNTextCell.h"
33#import "FSNBrowser.h"
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 DEF_GRID_WIDTH 90
43#define Y_MARGIN (4)
44#define EDIT_MARGIN (4)
45
46@interface GWTextField : NSTextField
47@end
48
49@implementation GWTextField
50
51- (id) initWithFrame: (NSRect)aFrame
52{
53  NSTextFieldCell *cell;
54
55  self = [super initWithFrame: aFrame];
56
57  cell =  [[[FSNTextCell alloc] init] autorelease];
58  [cell setDrawsBackground: YES];
59
60  [self setCell: cell];
61
62  return self;
63}
64
65@end
66
67
68@implementation GWViewerShelf
69
70- (void)dealloc
71{
72	[self unsetWatchers];
73  RELEASE (watchedPaths);
74  RELEASE (icons);
75  RELEASE (extInfoType);
76  if (grid != NULL)
77    {
78      NSZoneFree (NSDefaultMallocZone(), grid);
79    }
80  RELEASE (dragIcon);
81  RELEASE (focusedIconLabel);
82  RELEASE (backColor);
83  RELEASE (textColor);
84  RELEASE (disabledTextColor);
85
86  [super dealloc];
87}
88
89- (id)initWithFrame:(NSRect)frameRect
90          forViewer:(id)vwr
91{
92  self = [super initWithFrame: frameRect];
93
94  if (self) {
95    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
96    id defentry;
97
98    fsnodeRep = [FSNodeRep sharedInstance];
99
100    defentry = [defaults dictionaryForKey: @"backcolor"];
101    if (defentry) {
102      float red = [[(NSDictionary *)defentry objectForKey: @"red"] floatValue];
103      float green = [[(NSDictionary *)defentry objectForKey: @"green"] floatValue];
104      float blue = [[(NSDictionary *)defentry objectForKey: @"blue"] floatValue];
105      float alpha = [[(NSDictionary *)defentry objectForKey: @"alpha"] floatValue];
106
107      ASSIGN (backColor, [NSColor colorWithCalibratedRed: red
108                                                   green: green
109                                                    blue: blue
110                                                   alpha: alpha]);
111    } else {
112      ASSIGN (backColor, [[NSColor windowBackgroundColor] colorUsingColorSpaceName: NSDeviceRGBColorSpace]);
113    }
114
115    defentry = [defaults dictionaryForKey: @"textcolor"];
116    if (defentry) {
117      float red = [[(NSDictionary *)defentry objectForKey: @"red"] floatValue];
118      float green = [[(NSDictionary *)defentry objectForKey: @"green"] floatValue];
119      float blue = [[(NSDictionary *)defentry objectForKey: @"blue"] floatValue];
120      float alpha = [[(NSDictionary *)defentry objectForKey: @"alpha"] floatValue];
121
122      ASSIGN (textColor, [NSColor colorWithCalibratedRed: red
123                                                   green: green
124                                                    blue: blue
125                                                   alpha: alpha]);
126    } else {
127      ASSIGN (textColor, [[NSColor controlTextColor] colorUsingColorSpaceName: NSDeviceRGBColorSpace]);
128    }
129
130    ASSIGN (disabledTextColor, [textColor highlightWithLevel: NSDarkGray]);
131
132    iconSize = DEF_ICN_SIZE;
133
134    defentry = [defaults objectForKey: @"labeltxtsize"];
135    labelTextSize = defentry ? [defentry intValue] : DEF_TEXT_SIZE;
136    ASSIGN (labelFont, [NSFont systemFontOfSize: labelTextSize]);
137
138    iconPosition = DEF_ICN_POS;
139
140    defentry = [defaults objectForKey: @"fsn_info_type"];
141    infoType = defentry ? [defentry intValue] : FSNInfoNameType;
142    extInfoType = nil;
143
144    if (infoType == FSNInfoExtendedType) {
145      defentry = [defaults objectForKey: @"extended_info_type"];
146
147      if (defentry) {
148        NSArray *availableTypes = [fsnodeRep availableExtendedInfoNames];
149
150        if ([availableTypes containsObject: defentry]) {
151          ASSIGN (extInfoType, defentry);
152        }
153      }
154
155      if (extInfoType == nil) {
156        infoType = FSNInfoNameType;
157      }
158    }
159
160    defentry = [defaults objectForKey: @"shelfcellswidth"];
161    gridSize.width = defentry ? [defentry intValue] : DEF_GRID_WIDTH;
162
163		icons = [NSMutableArray new];
164
165    viewer = vwr;
166    gworkspace = [GWorkspace gworkspace];
167
168    dragIcon = nil;
169    focusedIcon = nil;
170
171    [self calculateGridSize];
172    [self makeIconsGrid];
173
174  	[self registerForDraggedTypes: [NSArray arrayWithObject: NSFilenamesPboardType]];
175
176		watchedPaths = [[NSCountedSet alloc] initWithCapacity: 1];
177
178    focusedIconLabel = [GWTextField new];
179		[focusedIconLabel setFont: [NSFont systemFontOfSize: 12]];
180		[focusedIconLabel setBezeled: NO];
181		[focusedIconLabel setAlignment: NSCenterTextAlignment];
182    [focusedIconLabel setEditable: NO];
183    [focusedIconLabel setSelectable: NO];
184    [focusedIconLabel setBackgroundColor: backColor];
185	  [focusedIconLabel setTextColor: [NSColor controlTextColor]];
186    [focusedIconLabel setFrame: NSMakeRect(0, 0, 0, 14)];
187  }
188
189  return self;
190}
191
192- (void)setContents:(NSArray *)iconsInfo
193{
194  FSNode *baseNode = [viewer baseNode];
195  int i;
196
197  for (i = 0; i < [iconsInfo count]; i++) {
198		NSDictionary *info = [iconsInfo objectAtIndex: i];
199    NSArray *paths = [info objectForKey: @"paths"];
200		int index = [[info objectForKey: @"index"] intValue];
201    NSMutableArray *icnnodes = [NSMutableArray array];
202    int j;
203
204    for (j = 0; j < [paths count]; j++) {
205      FSNode *node = [FSNode nodeWithPath: [paths objectAtIndex: j]];
206
207      if ([node isValid] && [baseNode isParentOfNode: node]) {
208        [icnnodes addObject: node];
209      }
210    }
211
212    if ([icnnodes count] && (index != -1)) {
213      if ([icnnodes count] == 1) {
214        [self addIconForNode: [icnnodes objectAtIndex: 0] atIndex: index];
215      } else {
216        [self addIconForSelection: icnnodes atIndex: index];
217      }
218    }
219  }
220
221  [self tile];
222}
223
224- (NSArray *)contentsInfo
225{
226  NSMutableArray *iconsInfo = [NSMutableArray array];
227  int i;
228
229  for (i = 0; i < [icons count]; i++) {
230		FSNIcon *icon = [icons objectAtIndex: i];
231		NSMutableDictionary *dict = [NSMutableDictionary dictionary];
232
233    if ([icon isShowingSelection]) {
234      NSArray *selection = [icon selection];
235      NSMutableArray *paths = [NSMutableArray array];
236      int j;
237
238      for (j = 0; j < [selection count]; j++) {
239        [paths addObject: [[selection objectAtIndex: j] path]];
240      }
241
242      [dict setObject: paths forKey: @"paths"];
243
244    } else {
245      [dict setObject: [NSArray arrayWithObject: [[icon node] path]]
246               forKey: @"paths"];
247    }
248
249    [dict setObject: [NSNumber numberWithInt: [icon gridIndex]]
250             forKey: @"index"];
251
252    [iconsInfo addObject: dict];
253  }
254
255  return iconsInfo;
256}
257
258- (id)addIconForNode:(FSNode *)node
259             atIndex:(int)index
260{
261  FSNIcon *icon = [[FSNIcon alloc] initForNode: node
262                                  nodeInfoType: infoType
263                                  extendedType: extInfoType
264                                      iconSize: iconSize
265                                  iconPosition: iconPosition
266                                     labelFont: labelFont
267                                     textColor: textColor
268                                     gridIndex: index
269                                     dndSource: YES
270                                     acceptDnd: YES
271                                     slideBack: NO];
272  [icons addObject: icon];
273  [self addSubview: icon];
274  RELEASE (icon);
275
276  {
277  	NSString *watched = [node parentPath];
278
279	  if ([watchedPaths containsObject: watched] == NO) {
280		  [self setWatcherForPath: watched];
281	  }
282    [watchedPaths addObject: watched];
283  }
284
285  return icon;
286}
287
288- (id)addIconForSelection:(NSArray *)selection
289                  atIndex:(int)index
290{
291  FSNIcon *icon = [self addIconForNode: [selection objectAtIndex: 0]
292                               atIndex: index];
293  [icon showSelection: selection];
294  return icon;
295}
296
297- (id)iconForNode:(FSNode *)node
298{
299	int i;
300
301	for (i = 0; i < [icons count]; i++) {
302    FSNIcon *icon = [icons objectAtIndex: i];
303
304		if ([[icon node] isEqual: node] && ([icon selection] == nil)) {
305			return icon;
306		}
307  }
308
309	return nil;
310}
311
312- (id)iconForPath:(NSString *)path
313{
314	int i;
315
316	for (i = 0; i < [icons count]; i++) {
317    FSNIcon *icon = [icons objectAtIndex: i];
318
319		if ([[[icon node] path] isEqual: path] && ([icon selection] == nil)) {
320			return icon;
321		}
322  }
323
324	return nil;
325}
326
327- (id)iconForNodesSelection:(NSArray *)selection
328{
329	int i;
330
331	for (i = 0; i < [icons count]; i++) {
332    FSNIcon *icon = [icons objectAtIndex: i];
333    NSArray *selnodes = [icon selection];
334
335    if (selnodes && [selnodes isEqual: selection]) {
336      return icon;
337    }
338  }
339
340	return nil;
341}
342
343- (id)iconForPathsSelection:(NSArray *)selection
344{
345	int i;
346
347	for (i = 0; i < [icons count]; i++) {
348    FSNIcon *icon = [icons objectAtIndex: i];
349    NSArray *selnodes = [icon selection];
350
351    if (selnodes) {
352      NSMutableArray *selpaths = [NSMutableArray array];
353      int j;
354
355      for (j = 0; j < [selnodes count]; j++) {
356        [selpaths addObject: [[selnodes objectAtIndex: j] path]];
357      }
358
359      if ([selpaths isEqual: selection]) {
360        return icon;
361      }
362    }
363  }
364
365	return nil;
366}
367
368- (void)calculateGridSize
369{
370  NSSize highlightSize = NSZeroSize;
371  NSSize labelSize = NSZeroSize;
372
373  highlightSize.width = ceil(iconSize / 3 * 4);
374  highlightSize.height = ceil(highlightSize.width * [fsnodeRep highlightHeightFactor]);
375  if ((highlightSize.height - iconSize) < 4) {
376    highlightSize.height = iconSize + 4;
377  }
378
379  labelSize.height = myrintf([fsnodeRep heightOfFont: labelFont]);
380  labelSize.width = gridSize.width;
381  gridSize.height = highlightSize.height + labelSize.height;
382}
383
384- (void)makeIconsGrid
385{
386  NSRect gridrect = [self bounds];
387  NSPoint gpnt;
388  int i;
389
390	if (grid != NULL) {
391		NSZoneFree (NSDefaultMallocZone(), grid);
392	}
393
394  colcount = (int)(gridrect.size.width / gridSize.width);
395  rowcount = (int)(gridrect.size.height / gridSize.height);
396	gridcount = colcount * rowcount;
397
398	grid = NSZoneMalloc (NSDefaultMallocZone(), sizeof(NSRect) * gridcount);
399
400  gpnt.x = 0;
401  gpnt.y = gridrect.size.height - gridSize.height - Y_MARGIN;
402
403  for (i = 0; i < gridcount; i++) {
404		if (i > 0) {
405			gpnt.x += gridSize.width;
406    }
407
408    if (gpnt.x >= (gridrect.size.width - gridSize.width)) {
409      gpnt.x = 0;
410      gpnt.y -= (gridSize.height + Y_MARGIN);
411    }
412
413    grid[i].origin = gpnt;
414    grid[i].size = gridSize;
415    grid[i] = NSIntegralRect(grid[i]);
416  }
417}
418
419- (int)firstFreeGridIndex
420{
421	int i;
422
423	for (i = 0; i < gridcount; i++) {
424    if ([self isFreeGridIndex: i]) {
425      return i;
426    }
427	}
428
429	return -1;
430}
431
432- (int)firstFreeGridIndexAfterIndex:(int)index
433{
434  int newind = index;
435
436  while (1) {
437    newind++;
438
439    if (newind >= gridcount) {
440      return [self firstFreeGridIndex];
441    }
442
443    if ([self isFreeGridIndex: newind]) {
444      return newind;
445    }
446  }
447
448	return -1;
449}
450
451- (BOOL)isFreeGridIndex:(int)index
452{
453	int i;
454
455  if ((index < 0) || (index >= gridcount)) {
456    return NO;
457  }
458
459	for (i = 0; i < [icons count]; i++) {
460		if ([[icons objectAtIndex: i] gridIndex] == index) {
461			return NO;
462		}
463  }
464
465	return YES;
466}
467
468- (id)iconWithGridIndex:(int)index
469{
470	int i;
471
472	for (i = 0; i < [icons count]; i++) {
473    FSNIcon *icon = [icons objectAtIndex: i];
474
475		if ([icon gridIndex] == index) {
476			return icon;
477		}
478  }
479
480	return nil;
481}
482
483- (int)indexOfGridRectContainingPoint:(NSPoint)p
484{
485	int i;
486
487	for (i = 0; i < gridcount; i++) {
488    if (NSPointInRect(p, grid[i])) {
489      return i;
490    }
491  }
492
493  return -1;
494}
495
496- (NSRect)iconBoundsInGridAtIndex:(int)index
497{
498  NSRect icnBounds = NSMakeRect(grid[index].origin.x, grid[index].origin.y, iconSize, iconSize);
499  NSRect hlightRect = NSZeroRect;
500
501  hlightRect.size.width = ceil(iconSize / 3 * 4);
502  hlightRect.size.height = ceil(hlightRect.size.width * [fsnodeRep highlightHeightFactor]);
503  hlightRect.origin.x = ceil((gridSize.width - hlightRect.size.width) / 2);
504  hlightRect.origin.y = floor([fsnodeRep heightOfFont: labelFont]);
505
506  icnBounds.origin.x += hlightRect.origin.x + ((hlightRect.size.width - iconSize) / 2);
507  icnBounds.origin.y += hlightRect.origin.y + ((hlightRect.size.height - iconSize) / 2);
508
509  return icnBounds;
510}
511
512- (void)tile
513{
514  NSArray *subviews = [self subviews];
515  NSUInteger i;
516
517  [self makeIconsGrid];
518
519  for (i = 0; i < [icons count]; i++) {
520    FSNIcon *icon = [icons objectAtIndex: i];
521    NSUInteger index = [icon gridIndex];
522
523    if (index < gridcount) {
524      if ([subviews containsObject: icon] == NO) {
525        [self addSubview: icon];
526      }
527      if (NSEqualRects(grid[index], [icon frame]) == NO) {
528        [icon setFrame: grid[index]];
529      }
530    } else {
531      if (focusedIcon == icon) {
532        focusedIcon = nil;
533      }
534
535      [icon removeFromSuperview];
536    }
537  }
538
539  [self updateFocusedIconLabel];
540}
541
542- (void)updateFocusedIconLabel
543{
544  if ([[self subviews] containsObject: focusedIconLabel]) {
545    NSRect rect = [focusedIconLabel frame];
546
547    [focusedIconLabel removeFromSuperview];
548    [self setNeedsDisplayInRect: rect];
549  }
550
551  if (focusedIcon) {
552    NSRect icnr = [focusedIcon frame];
553    float centerx = icnr.origin.x + (icnr.size.width / 2);
554    NSRect edrect = [self convertRect: [focusedIcon labelRect] fromView: focusedIcon];
555    int margin = [fsnodeRep labelMargin];
556    float bw = [self bounds].size.width - EDIT_MARGIN;
557    NSString *nodeDescr = [focusedIcon shownInfo];
558    float edwidth = [[focusedIconLabel font] widthOfString: nodeDescr];
559
560    edwidth += margin;
561
562    if ((centerx + (edwidth / 2)) >= bw) {
563      centerx -= (centerx + (edwidth / 2) - bw);
564    } else if ((centerx - (edwidth / 2)) < margin) {
565      centerx += fabs(centerx - (edwidth / 2)) + margin;
566    }
567
568    edrect.origin.x = centerx - (edwidth / 2);
569    edrect.size.width = edwidth;
570    edrect = NSIntegralRect(edrect);
571
572    [focusedIconLabel setFrame: edrect];
573    [focusedIconLabel setStringValue: nodeDescr];
574
575    if ([focusedIcon isLocked] == NO) {
576      [focusedIconLabel setTextColor: [NSColor controlTextColor]];
577    } else {
578      [focusedIconLabel setTextColor: [NSColor disabledControlTextColor]];
579    }
580
581    [focusedIcon setNameEdited: YES];
582
583    [self addSubview: focusedIconLabel];
584    [self setNeedsDisplayInRect: edrect];
585  }
586}
587
588- (void)setWatcherForPath:(NSString *)path
589{
590	[gworkspace addWatcherForPath: path];
591}
592
593- (void)unsetWatcherForPath:(NSString *)path
594{
595	[gworkspace removeWatcherForPath: path];
596}
597
598- (void)unsetWatchers
599{
600  NSEnumerator *enumerator = [watchedPaths objectEnumerator];
601  NSString *wpath;
602
603	while ((wpath = [enumerator nextObject])) {
604    [self unsetWatcherForPath: wpath];
605  }
606}
607
608- (NSArray *)watchedPaths
609{
610  return [watchedPaths allObjects];
611}
612
613- (void)checkIconsAfterDotsFilesChange
614{
615  int count = [icons count];
616  BOOL updated = NO;
617  int i;
618
619	for (i = 0; i < count; i++) {
620    FSNIcon *icon = [icons objectAtIndex: i];
621    int j;
622
623    if ([icon isShowingSelection] == NO) {
624      if ([[[icon node] path] rangeOfString: @"."].location != NSNotFound) {
625        [self removeRep: icon];
626        updated = YES;
627        count--;
628        i--;
629      }
630
631    } else {
632      NSArray *iconpaths = [icon pathsSelection];
633
634      for (j = 0; j < [iconpaths count]; j++) {
635        NSString *iconpath = [iconpaths objectAtIndex: j];
636
637        if ([iconpath rangeOfString: @"."].location != NSNotFound) {
638          [self removeRep: icon];
639          updated = YES;
640          count--;
641          i--;
642          break;
643        }
644      }
645    }
646	}
647
648  if (updated) {
649    [self tile];
650    [self setNeedsDisplay: YES];
651  }
652}
653
654- (void)checkIconsAfterHidingOfPaths:(NSArray *)hpaths
655{
656  int count = [icons count];
657  BOOL updated = NO;
658  int i;
659
660	for (i = 0; i < count; i++) {
661    FSNIcon *icon = [icons objectAtIndex: i];
662    int j, m;
663
664    if ([icon isShowingSelection] == NO) {
665      NSString *iconpath = [[icon node] path];
666
667	    for (m = 0; m < [hpaths count]; m++) {
668        NSString *hpath = [hpaths objectAtIndex: m];
669
670        if (isSubpathOfPath(hpath, iconpath) || [hpath isEqual: iconpath]) {
671          [self removeRep: icon];
672          updated = YES;
673          count--;
674          i--;
675          break;
676        }
677      }
678    } else {
679      NSArray *iconpaths = [icon pathsSelection];
680      BOOL removed = NO;
681
682      for (j = 0; j < [iconpaths count]; j++) {
683        NSString *iconpath = [iconpaths objectAtIndex: j];
684
685	      for (m = 0; m < [hpaths count]; m++) {
686          NSString *hpath = [hpaths objectAtIndex: m];
687
688          if (isSubpathOfPath(hpath, iconpath) || [hpath isEqual: iconpath]) {
689            [self removeRep: icon];
690            updated = YES;
691            count--;
692            i--;
693            removed = YES;
694            break;
695          }
696        }
697
698        if (removed) {
699          break;
700        }
701      }
702    }
703	}
704
705  if (updated) {
706    [self tile];
707    [self setNeedsDisplay: YES];
708  }
709}
710
711- (void)resizeWithOldSuperviewSize:(NSSize)oldFrameSize
712{
713  [self tile];
714}
715
716- (void)setFrame:(NSRect)frameRect
717{
718  [super setFrame: frameRect];
719  [self tile];
720}
721
722- (void)drawRect:(NSRect)rect
723{
724  [super drawRect: rect];
725
726	if (dragIcon) {
727		[dragIcon dissolveToPoint: dragPoint fraction: 0.3];
728	}
729}
730
731@end
732
733
734@implementation GWViewerShelf (NodeRepContainer)
735
736- (void)removeRep:(id)arep
737{
738  NSString *watched = [[arep node] parentPath];
739
740	if ([watchedPaths containsObject: watched]) {
741    [watchedPaths removeObject: watched];
742
743    if ([watchedPaths containsObject: watched] == NO) {
744      [self unsetWatcherForPath: watched];
745	  }
746  }
747
748  if ([[self subviews] containsObject: arep]) {
749    [arep removeFromSuperviewWithoutNeedingDisplay];
750  }
751
752  if (focusedIcon == arep) {
753    focusedIcon = nil;
754    [self updateFocusedIconLabel];
755  }
756
757  [icons removeObject: arep];
758}
759
760- (void)removeUndepositedRep:(id)arep
761{
762  [self removeRep: arep];
763  [self setNeedsDisplay: YES];
764}
765
766- (void)repSelected:(id)arep
767{
768  [viewer shelfDidSelectIcon: arep];
769  [arep unselect];
770}
771
772- (void)unselectOtherReps:(id)arep
773{
774  int i;
775
776  for (i = 0; i < [icons count]; i++) {
777    FSNIcon *icon = [icons objectAtIndex: i];
778
779    if (icon != arep) {
780      [icon unselect];
781    }
782  }
783}
784
785- (NSArray *)selectedPaths
786{
787  NSMutableArray *selectedPaths = [NSMutableArray array];
788  int i, j;
789
790  for (i = 0; i < [icons count]; i++) {
791    FSNIcon *icon = [icons objectAtIndex: i];
792
793    if ([icon isSelected]) {
794      NSArray *selection = [icon selection];
795
796      if (selection) {
797        for (j = 0; j < [selection count]; j++) {
798          [selectedPaths addObject: [[selection objectAtIndex: j] path]];
799        }
800      } else {
801        [selectedPaths addObject: [[icon node] path]];
802      }
803    }
804  }
805
806  return [NSArray arrayWithArray: selectedPaths];
807}
808
809- (void)openSelectionInNewViewer:(BOOL)newv
810{
811  [viewer openSelectionInNewViewer: newv];
812}
813
814- (void)nodeContentsWillChange:(NSDictionary *)info
815{
816  [self checkLockedReps];
817}
818
819- (void)nodeContentsDidChange:(NSDictionary *)info
820{
821  NSString *operation = [info objectForKey: @"operation"];
822  NSString *source = [info objectForKey: @"source"];
823  NSString *destination = [info objectForKey: @"destination"];
824  NSArray *files = [info objectForKey: @"files"];
825  int i;
826
827  if ([operation isEqual: NSWorkspaceRecycleOperation]) {
828		files = [info objectForKey: @"origfiles"];
829  }
830
831  if ([operation isEqual: @"GWorkspaceRenameOperation"]) {
832    for (i = 0; i < [icons count]; i++) {
833      FSNIcon *icon = [icons objectAtIndex: i];
834
835      if ([icon isShowingSelection] == NO) {
836        if ([[[icon node] path] isEqual: source]) {
837          [icon setNode: [FSNode nodeWithPath: destination]];
838          break;
839        }
840      }
841    }
842  }
843
844  if ([operation isEqual: @"GWorkspaceRenameOperation"]) {
845		files = [NSArray arrayWithObject: [source lastPathComponent]];
846    source = [source stringByDeletingLastPathComponent];
847  }
848
849  if ([operation isEqual: NSWorkspaceMoveOperation]
850      || [operation isEqual: NSWorkspaceDestroyOperation]
851      || [operation isEqual: @"GWorkspaceRenameOperation"]
852      || [operation isEqual: NSWorkspaceRecycleOperation]
853      || [operation isEqual: @"GWorkspaceRecycleOutOperation"]
854      || [operation isEqual: @"GWorkspaceEmptyRecyclerOperation"]) {
855    NSMutableArray *oppaths = [NSMutableArray array];
856    int count = [icons count];
857    BOOL updated = NO;
858
859    for (i = 0; i < [files count]; i++) {
860      NSString *fname = [files objectAtIndex: i];
861      NSString *fpath = [source stringByAppendingPathComponent: fname];
862      [oppaths addObject: fpath];
863    }
864
865    for (i = 0; i < count; i++) {
866      FSNIcon *icon = [icons objectAtIndex: i];
867      int j, m;
868
869      if ([icon isShowingSelection] == NO) {
870        NSString *iconpath = [[icon node] path];
871
872	      for (m = 0; m < [oppaths count]; m++) {
873          if ([iconpath hasPrefix: [oppaths objectAtIndex: m]]) {
874            [self removeRep: icon];
875            updated = YES;
876            count--;
877            i--;
878            break;
879          }
880        }
881
882      } else {
883        NSArray *iconpaths = [icon pathsSelection];
884        BOOL removed = NO;
885
886        for (j = 0; j < [iconpaths count]; j++) {
887          NSString *iconpath = [iconpaths objectAtIndex: j];
888
889	        for (m = 0; m < [oppaths count]; m++) {
890            if ([iconpath hasPrefix: [oppaths objectAtIndex: m]]) {
891              [self removeRep: icon];
892              updated = YES;
893              count--;
894              i--;
895              removed = YES;
896              break;
897            }
898          }
899
900          if (removed) {
901            break;
902          }
903        }
904      }
905    }
906
907    if (updated) {
908      [self tile];
909      [self setNeedsDisplay: YES];
910    }
911  }
912}
913
914- (void)watchedPathChanged:(NSDictionary *)info
915{
916  NSString *path = [info objectForKey: @"path"];
917  NSString *event = [info objectForKey: @"event"];
918  NSEnumerator *enumerator;
919  NSString *wpath;
920  BOOL contained = NO;
921
922  if ([event isEqual: @"GWFileCreatedInWatchedDirectory"])
923    {
924      return;
925    }
926
927  enumerator = [watchedPaths objectEnumerator];
928
929  while ((wpath = [enumerator nextObject]))
930    {
931      if (([wpath isEqual: path]) || (isSubpathOfPath(path, wpath)))
932        {
933          contained = YES;
934          break;
935        }
936    }
937
938  if (contained)
939    {
940      NSUInteger count = [icons count];
941      BOOL updated = NO;
942      FSNIcon *icon;
943      NSUInteger i;
944
945      if ([event isEqual: @"GWWatchedPathDeleted"])
946        {
947          for (i = 0; i < count; i++)
948            {
949              icon = [icons objectAtIndex: i];
950
951              if ([[icon node] isSubnodeOfPath: path])
952                {
953                  [self removeRep: icon];
954                  updated = YES;
955                  count--;
956                  i--;
957                }
958            }
959
960          if (updated)
961            {
962              [self tile];
963              [self setNeedsDisplay: YES];
964            }
965
966          return;
967        }
968
969      if ([event isEqual: @"GWFileDeletedInWatchedDirectory"])
970        {
971          NSArray *files = [info objectForKey: @"files"];
972
973          for (i = 0; i < count; i++)
974            {
975              NSUInteger j;
976
977              icon = [icons objectAtIndex: i];
978
979              if ([icon isShowingSelection] == NO)
980                {
981                  FSNode *node = [icon node];
982
983                  for (j = 0; j < [files count]; j++)
984                    {
985                      NSString *fname = [files objectAtIndex: j];
986                      NSString *fpath = [path stringByAppendingPathComponent: fname];
987
988                      if ([[node path] isEqual: fpath] || [node isSubnodeOfPath: fpath])
989                        {
990                          [self removeRep: icon];
991                          updated = YES;
992                          count--;
993                          i--;
994                          break;
995                        }
996                    }
997
998                }
999              else
1000                {
1001                  FSNode *node = [icon node];
1002                  NSArray *selection = [icon selection];
1003
1004                  for (j = 0; j < [files count]; j++)
1005                    {
1006                      NSString *fname = [files objectAtIndex: j];
1007                      NSString *fpath = [path stringByAppendingPathComponent: fname];
1008                      BOOL deleted = NO;
1009                      NSUInteger m;
1010
1011                      if (deleted)
1012                        {
1013                          break;
1014                        }
1015
1016                      if ([node isSubnodeOfPath: fpath])
1017                        {
1018                          [self removeRep: icon];
1019                          updated = YES;
1020                          count--;
1021                          i--;
1022                          break;
1023                        }
1024
1025                      for (m = 0; m < [selection count]; m++)
1026                        {
1027                          node = [selection objectAtIndex: m];
1028
1029                          if ([[node path] isEqual: fpath])
1030                            {
1031                              [self removeRep: icon];
1032                              updated = YES;
1033                              count--;
1034                              i--;
1035                              deleted = YES;
1036                              break;
1037                            }
1038                        }
1039                    }
1040                }
1041            }
1042
1043          if (updated)
1044            {
1045              [self tile];
1046              [self setNeedsDisplay: YES];
1047            }
1048        }
1049    }
1050}
1051
1052- (void)checkLockedReps
1053{
1054  NSUInteger i;
1055
1056  for (i = 0; i < [icons count]; i++)
1057    {
1058      [[icons objectAtIndex: i] checkLocked];
1059    }
1060}
1061
1062- (FSNSelectionMask)selectionMask
1063{
1064  return NSSingleSelectionMask;
1065}
1066
1067- (void)restoreLastSelection
1068{
1069  [self unselectOtherReps: nil];
1070}
1071
1072- (void)setFocusedRep:(id)arep
1073{
1074  if (arep == nil) {
1075    if (focusedIcon) {
1076      [focusedIcon setNameEdited: NO];
1077    }
1078  }
1079
1080  focusedIcon = arep;
1081  [self updateFocusedIconLabel];
1082}
1083
1084- (NSColor *)backgroundColor
1085{
1086  return backColor;
1087}
1088
1089- (NSColor *)textColor
1090{
1091  return textColor;
1092}
1093
1094- (NSColor *)disabledTextColor
1095{
1096  return disabledTextColor;
1097}
1098
1099@end
1100
1101
1102@implementation GWViewerShelf (DraggingDestination)
1103
1104- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
1105{
1106  NSPasteboard *pb = [sender draggingPasteboard];
1107  NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
1108
1109  DESTROY (dragIcon);
1110  isDragTarget = NO;
1111  dragLocalIcon = NO;
1112
1113  if ((sourceDragMask == NSDragOperationCopy)
1114      || (sourceDragMask == NSDragOperationLink))
1115    {
1116      return NSDragOperationNone;
1117    }
1118
1119  if (pb && [[pb types] containsObject: NSFilenamesPboardType])
1120    {
1121      NSArray *sourcePaths = [pb propertyListForType: NSFilenamesPboardType];
1122      NSUInteger count = [sourcePaths count];
1123      FSNode *baseNode = [viewer baseNode];
1124      NSString *basePath;
1125      NSUInteger i;
1126
1127      if (count == 0)
1128        {
1129          return NSDragOperationNone;
1130        }
1131
1132      for (i = 0; i < count; i++)
1133        {
1134          NSString *path = [sourcePaths objectAtIndex: i];
1135
1136          if ([baseNode isParentOfPath: path] == NO)
1137            {
1138              return NSDragOperationNone;
1139            }
1140        }
1141
1142      basePath = [[sourcePaths objectAtIndex: 0] stringByDeletingLastPathComponent];
1143      if ([basePath isEqual: [gworkspace trashPath]])
1144        {
1145          return NSDragOperationNone;
1146        }
1147
1148    if (count == 1) {
1149      dragLocalIcon = ([self iconForPath: [sourcePaths objectAtIndex: 0]] != nil);
1150    } else {
1151      dragLocalIcon = ([self iconForPathsSelection: sourcePaths] != nil);
1152    }
1153
1154    isDragTarget = YES;
1155    dragPoint = NSZeroPoint;
1156    DESTROY (dragIcon);
1157    insertIndex = -1;
1158
1159    return NSDragOperationAll;
1160  }
1161
1162  return NSDragOperationNone;
1163}
1164
1165- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
1166{
1167  NSDragOperation sourceDragMask;
1168  NSPoint dpoint;
1169  int index;
1170
1171	if (isDragTarget == NO) {
1172		return NSDragOperationNone;
1173	}
1174
1175  sourceDragMask = [sender draggingSourceOperationMask];
1176
1177	if ((sourceDragMask == NSDragOperationCopy)
1178												|| (sourceDragMask == NSDragOperationLink)) {
1179    if (dragIcon) {
1180      DESTROY (dragIcon);
1181      if (insertIndex != -1) {
1182        [self setNeedsDisplayInRect: grid[insertIndex]];
1183      }
1184    }
1185		return NSDragOperationNone;
1186	}
1187
1188  dpoint = [sender draggingLocation];
1189  dpoint = [self convertPoint: dpoint fromView: nil];
1190  index = [self indexOfGridRectContainingPoint: dpoint];
1191
1192  if ((index != -1) && ([self isFreeGridIndex: index])) {
1193    NSImage *img = [sender draggedImage];
1194    NSSize sz = [img size];
1195    NSRect irect = [self iconBoundsInGridAtIndex: index];
1196
1197    dragPoint.x = ceil(irect.origin.x + ((irect.size.width - sz.width) / 2));
1198    dragPoint.y = ceil(irect.origin.y + ((irect.size.height - sz.height) / 2));
1199
1200    if (dragIcon == nil) {
1201      ASSIGN (dragIcon, img);
1202    }
1203
1204    if (insertIndex != index) {
1205      [self setNeedsDisplayInRect: grid[index]];
1206
1207      if (insertIndex != -1) {
1208        [self setNeedsDisplayInRect: grid[insertIndex]];
1209      }
1210    }
1211
1212    insertIndex = index;
1213
1214  } else {
1215    DESTROY (dragIcon);
1216    if (insertIndex != -1) {
1217      [self setNeedsDisplayInRect: grid[insertIndex]];
1218    }
1219    insertIndex = -1;
1220    return NSDragOperationNone;
1221  }
1222
1223	if ((sourceDragMask == NSDragOperationCopy)
1224												|| (sourceDragMask == NSDragOperationLink)) {
1225		return NSDragOperationNone;
1226	}
1227
1228  return NSDragOperationAll;
1229}
1230
1231- (void)draggingExited:(id <NSDraggingInfo>)sender
1232{
1233  DESTROY (dragIcon);
1234  if (insertIndex != -1) {
1235    [self setNeedsDisplayInRect: grid[insertIndex]];
1236  }
1237	isDragTarget = NO;
1238}
1239
1240- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
1241{
1242  return isDragTarget;
1243}
1244
1245- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
1246{
1247  return YES;
1248}
1249
1250- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
1251{
1252	NSPasteboard *pb = [sender draggingPasteboard];
1253	NSMutableArray *sourcePaths = [pb propertyListForType: NSFilenamesPboardType];
1254  int count = [sourcePaths count];
1255  id icon;
1256
1257  DESTROY (dragIcon);
1258	isDragTarget = NO;
1259
1260  if (insertIndex != -1) {
1261    if (dragLocalIcon) {
1262      if (count == 1) {
1263        icon = [self iconForPath: [sourcePaths objectAtIndex: 0]];
1264      } else {
1265        icon = [self iconForPathsSelection: sourcePaths];
1266      }
1267
1268      if (icon) {
1269        [icon setGridIndex: insertIndex];
1270      }
1271
1272    } else {
1273      FSNode *baseNode = [viewer baseNode];
1274      NSMutableArray *icnnodes = [NSMutableArray array];
1275      int i;
1276
1277      for (i = 0; i < [sourcePaths count]; i++) {
1278        FSNode *node = [FSNode nodeWithPath: [sourcePaths objectAtIndex: i]];
1279
1280        if ([node isValid] && [baseNode isParentOfNode: node]) {
1281          [icnnodes addObject: node];
1282        }
1283      }
1284
1285      if ([icnnodes count]) {
1286        if ([icnnodes count] == 1) {
1287          [self addIconForNode: [icnnodes objectAtIndex: 0] atIndex: insertIndex];
1288        } else {
1289          [self addIconForSelection: icnnodes atIndex: insertIndex];
1290        }
1291      }
1292    }
1293  }
1294
1295  [self tile];
1296  [self setNeedsDisplay: YES];
1297}
1298
1299@end
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334