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