1/* RecyclerIcon.m
2 *
3 * Copyright (C) 2004-2014 Free Software Foundation, Inc.
4 *
5 * Author: Enrico Sersale <enrico@imago.ro>
6 *         Riccardo Mottola <rm@gnu.org>
7 *
8 * Date: June 2004
9 *
10 * This file is part of the GNUstep Recycler application
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
25 */
26
27
28#import <Foundation/Foundation.h>
29#import <AppKit/AppKit.h>
30#import <GNUstepBase/GNUstep.h>
31
32#import "Recycler.h"
33#import "RecyclerIcon.h"
34
35
36#define ISIZE 48
37
38static id <DesktopApplication> desktopApp = nil;
39
40@implementation RecyclerIcon
41
42- (void)dealloc
43{
44  RELEASE (trashFullIcon);
45  [super dealloc];
46}
47
48+ (void)initialize
49{
50  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
51  NSString *appname = [defaults stringForKey: @"DesktopApplicationName"];
52  NSString *selname = [defaults stringForKey: @"DesktopApplicationSelName"];
53
54  if (appname && selname) {
55		Class desktopAppClass = [[NSBundle mainBundle] principalClass];
56    SEL sel = NSSelectorFromString(selname);
57
58    desktopApp = [desktopAppClass performSelector: sel];
59  }
60}
61
62- (id)initWithRecyclerNode:(FSNode *)anode
63{
64  self = [super initForNode: anode
65               nodeInfoType: FSNInfoNameType
66               extendedType: nil
67                   iconSize: ISIZE
68               iconPosition: NSImageOnly
69                  labelFont: [NSFont systemFontOfSize: 12]
70                  textColor: [NSColor controlTextColor]
71                  gridIndex: 0
72                  dndSource: NO
73                  acceptDnd: YES
74                  slideBack: NO];
75
76  if (self) {
77    NSArray *subNodes = [node subNodes];
78    int count = [subNodes count];
79    int i;
80
81    ASSIGN (icon, [fsnodeRep trashIconOfSize: icnBounds.size.width]);
82    ASSIGN (trashFullIcon, [fsnodeRep trashFullIconOfSize: icnBounds.size.width]);
83
84    for (i = 0; i < [subNodes count]; i++) {
85      if ([[subNodes objectAtIndex: i] isReserved]) {
86        count--;
87      }
88    }
89
90    trashFull = (count != 0);
91
92    [self registerForDraggedTypes: [NSArray arrayWithObject: NSFilenamesPboardType]];
93
94    ws = [NSWorkspace sharedWorkspace];
95  }
96
97  return self;
98}
99
100- (void)setTrashFull:(BOOL)value
101{
102  trashFull = value;
103  [self setNeedsDisplay: YES];
104}
105
106- (void)mouseDown:(NSEvent *)theEvent
107{
108  if ([theEvent clickCount] == 1) {
109    if ([(Recycler *)desktopApp isDocked] == NO) {
110      NSWindow *win = [self window];
111      NSPoint	lastLocation = [theEvent locationInWindow];
112      NSPoint	location;
113      NSDate *theDistantFuture = [NSDate distantFuture];
114      BOOL done = NO;
115      unsigned eventMask = NSLeftMouseDownMask | NSLeftMouseUpMask
116	                | NSPeriodicMask | NSOtherMouseUpMask | NSRightMouseUpMask;
117
118      [NSEvent startPeriodicEventsAfterDelay: 0.02 withPeriod: 0.02];
119
120      while (done == NO) {
121        theEvent = [NSApp nextEventMatchingMask: eventMask
122					                            untilDate: theDistantFuture
123					                               inMode: NSEventTrackingRunLoopMode
124					                              dequeue: YES];
125
126        switch ([theEvent type]) {
127          case NSRightMouseUp:
128          case NSOtherMouseUp:
129          case NSLeftMouseUp:
130		        done = YES;
131		        break;
132
133          case NSPeriodic:
134		        location = [win mouseLocationOutsideOfEventStream];
135
136            if (NSEqualPoints(location, lastLocation) == NO) {
137		          NSPoint	origin = [win frame].origin;
138		          origin.x += (location.x - lastLocation.x);
139		          origin.y += (location.y - lastLocation.y);
140		          [win setFrameOrigin: origin];
141		        }
142		        break;
143
144          default:
145		        break;
146        }
147      }
148
149      [NSEvent stopPeriodicEvents];
150    }
151    else
152      [[self nextResponder] tryToPerform:_cmd with:theEvent];
153  } else {
154    id <workspaceAppProtocol> workspaceApp = [desktopApp workspaceApplication];
155
156    if (workspaceApp) {
157      NSString *path = [node path];
158      [workspaceApp selectFile: path inFileViewerRootedAtPath: path];
159    }
160  }
161}
162
163- (void)drawRect:(NSRect)rect
164{
165  if (trashFull) {
166    [trashFullIcon compositeToPoint: icnPoint operation: NSCompositeSourceOver];
167  } else {
168    [icon compositeToPoint: icnPoint operation: NSCompositeSourceOver];
169  }
170}
171
172@end
173
174
175@implementation RecyclerIcon (DraggingDestination)
176
177- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
178{
179  NSPasteboard *pb = [sender draggingPasteboard];
180
181  if ([[pb types] containsObject: NSFilenamesPboardType]) {
182    isDragTarget = YES;
183    return NSDragOperationAll;
184  }
185
186  isDragTarget = NO;
187  return NSDragOperationNone;
188}
189
190- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
191{
192  NSPasteboard *pb = [sender draggingPasteboard];
193
194  if ([[pb types] containsObject: NSFilenamesPboardType]) {
195    isDragTarget = YES;
196    [self select];
197    return NSDragOperationAll;
198  }
199
200  isDragTarget = NO;
201  return NSDragOperationNone;
202}
203
204- (void)draggingExited:(id <NSDraggingInfo>)sender
205{
206  isDragTarget = NO;
207}
208
209- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
210{
211  return isDragTarget;
212}
213
214- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
215{
216  return isDragTarget;
217}
218
219// FIXME: this code is now very similar to what is in DockIcon, it should be generalized
220
221- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
222{
223  NSPasteboard *pb = [sender draggingPasteboard];
224
225  [self unselect];
226  isDragTarget = NO;
227
228  if ([[pb types] containsObject: NSFilenamesPboardType])
229    {
230      NSArray *sourcePaths = [pb propertyListForType: NSFilenamesPboardType];
231      NSString *source = [[sourcePaths objectAtIndex: 0] stringByDeletingLastPathComponent];
232      NSArray *vpaths = [ws mountedLocalVolumePaths];
233      NSMutableArray *files = [NSMutableArray array];
234      NSMutableArray *umountPaths = [NSMutableArray array];
235      NSUInteger i;
236
237
238      for (i = 0; i < [sourcePaths count]; i++)
239        {
240          NSString *srcpath = [sourcePaths objectAtIndex: i];
241
242          if ([vpaths containsObject: srcpath])
243            {
244              [umountPaths addObject: srcpath];
245            }
246          else
247            {
248              [files addObject: [srcpath lastPathComponent]];
249            }
250        }
251
252
253      for (i = 0; i < [umountPaths count]; i++)
254        {
255          NSString *umpath = [umountPaths objectAtIndex: i];
256
257          if (![ws unmountAndEjectDeviceAtPath: umpath])
258            {
259              NSString *err = NSLocalizedString(@"Error", @"");
260              NSString *msg = NSLocalizedString(@"You are not allowed to umount\n", @"");
261              NSString *buttstr = NSLocalizedString(@"Continue", @"");
262              NSRunAlertPanel(err, [NSString stringWithFormat: @"%@ \"%@\"!\n", msg, umpath], buttstr, nil, nil);
263            }
264        }
265
266
267      if ([files count])
268        {
269          if ([[NSFileManager defaultManager] isWritableFileAtPath: source] == NO)
270            {
271              NSString *err = NSLocalizedString(@"Error", @"");
272              NSString *msg = NSLocalizedString(@"You do not have write permission\nfor", @"");
273              NSString *buttstr = NSLocalizedString(@"Continue", @"");
274              NSRunAlertPanel(err, [NSString stringWithFormat: @"%@ \"%@\"!\n", msg, source], buttstr, nil, nil);
275              return;
276            }
277
278          [desktopApp performFileOperation: NSWorkspaceRecycleOperation
279                                    source: source
280                               destination: [node path]
281                                     files: files];
282        }
283    }
284}
285
286@end
287
288