]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainWindowTitle.m
macosx: do not allow playlist item deletion for sd modules
[vlc] / modules / gui / macosx / MainWindowTitle.m
1 /*****************************************************************************
2  * MainWindowTitle.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2011-2012 Felix Paul Kühne
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #import <vlc_common.h>
25 #import "intf.h"
26 #import "MainWindowTitle.h"
27 #import "CoreInteraction.h"
28 #import "CompatibilityFixes.h"
29 #import <SystemConfiguration/SystemConfiguration.h> // for the revealInFinder clone
30
31 /*****************************************************************************
32  * VLCMainWindowTitleView
33  *
34  * this is our title bar, which can do anything a title should do
35  * it relies on the VLCWindowButtonCell to display the correct traffic light
36  * states, since we can't capture the mouse-moved events here correctly
37  *****************************************************************************/
38
39 @implementation VLCMainWindowTitleView
40 - (id)init
41 {
42     o_window_title_attributes_dict = [[NSDictionary dictionaryWithObjectsAndKeys: [NSColor whiteColor], NSForegroundColorAttributeName, [NSFont titleBarFontOfSize:12.0], NSFontAttributeName, nil] retain];
43
44     return [super init];
45 }
46
47 - (void)dealloc
48 {
49     [[NSNotificationCenter defaultCenter] removeObserver: self];
50
51     [o_red_img release];
52     [o_red_over_img release];
53     [o_red_on_img release];
54     [o_yellow_img release];
55     [o_yellow_over_img release];
56     [o_yellow_on_img release];
57     [o_green_img release];
58     [o_green_over_img release];
59     [o_green_on_img release];
60     [o_fullscreen_img release];
61     [o_fullscreen_over_img release];
62     [o_fullscreen_on_img release];
63     [o_old_fullscreen_img release];
64     [o_old_fullscreen_over_img release];
65     [o_old_fullscreen_on_img release];
66
67     [o_window_title_shadow release];
68     [o_window_title_attributes_dict release];
69
70     [super dealloc];
71 }
72
73 - (void)awakeFromNib
74 {
75     b_nativeFullscreenMode = NO;
76 #ifdef MAC_OS_X_VERSION_10_7
77     if (!OSX_SNOW_LEOPARD)
78         b_nativeFullscreenMode = var_InheritBool(VLCIntf, "macosx-nativefullscreenmode");
79 #endif
80
81     if (!b_nativeFullscreenMode || OSX_YOSEMITE) {
82         [o_fullscreen_btn setHidden: YES];
83     }
84
85     [self setAutoresizesSubviews: YES];
86     [self setImagesLeft:imageFromRes(@"topbar-dark-left") middle: imageFromRes(@"topbar-dark-center-fill") right:imageFromRes(@"topbar-dark-right")];
87
88     [self loadButtonIcons];
89     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(controlTintChanged:) name: NSControlTintDidChangeNotification object: nil];
90 }
91
92 - (void)controlTintChanged:(NSNotification *)notification
93 {
94     [self loadButtonIcons];
95
96     [o_red_btn setNeedsDisplay];
97     [o_yellow_btn setNeedsDisplay];
98     [o_green_btn setNeedsDisplay];
99 }
100
101 - (void)informModifierPressed:(BOOL)b_is_altkey;
102 {
103     BOOL b_state_changed = b_alt_pressed != b_is_altkey;
104
105     b_alt_pressed = b_is_altkey;
106
107     if (b_state_changed) {
108         [self updateGreenButton];
109     }
110 }
111
112 - (NSImage *)getButtonImage:(NSString *)o_id
113 {
114     NSString *o_name = @"";
115     if (OSX_SNOW_LEOPARD) {
116         o_name = @"snowleo-";
117     } else if (OSX_YOSEMITE) {
118         o_name = @"yosemite-";
119     } else { // OSX_LION, OSX_MOUNTAIN_LION, OSX_MAVERICKS
120         o_name = @"lion-";
121     }
122
123     o_name = [o_name stringByAppendingString:o_id];
124
125     if ([NSColor currentControlTint] != NSBlueControlTint) {
126         o_name = [o_name stringByAppendingString:@"-graphite"];
127     }
128
129     return [NSImage imageNamed:o_name];
130 }
131
132 - (void)loadButtonIcons
133 {
134     [o_red_img release];
135     [o_red_over_img release];
136     [o_red_on_img release];
137     [o_yellow_img release];
138     [o_yellow_over_img release];
139     [o_yellow_on_img release];
140     [o_green_img release];
141     [o_green_over_img release];
142     [o_green_on_img release];
143     [o_fullscreen_img release];
144     [o_fullscreen_over_img release];
145     [o_fullscreen_on_img release];
146     [o_old_fullscreen_img release];
147     [o_old_fullscreen_over_img release];
148     [o_old_fullscreen_on_img release];
149
150     o_red_img = [[self getButtonImage:@"window-close"] retain];
151     o_red_over_img = [[self getButtonImage:@"window-close-over"] retain];
152     o_red_on_img = [[self getButtonImage:@"window-close-on"] retain];
153     o_yellow_img = [[self getButtonImage:@"window-minimize"] retain];
154     o_yellow_over_img = [[self getButtonImage:@"window-minimize-over"] retain];
155     o_yellow_on_img = [[self getButtonImage:@"window-minimize-on"] retain];
156     o_green_img = [[self getButtonImage:@"window-zoom"] retain];
157     o_green_over_img = [[self getButtonImage:@"window-zoom-over"] retain];
158     o_green_on_img = [[self getButtonImage:@"window-zoom-on"] retain];
159
160     // these files are only available in the yosemite variant
161     if (OSX_YOSEMITE) {
162         o_fullscreen_img = [[self getButtonImage:@"window-fullscreen"] retain];
163         o_fullscreen_over_img = [[self getButtonImage:@"window-fullscreen-over"] retain];
164         o_fullscreen_on_img = [[self getButtonImage:@"window-fullscreen-on"] retain];
165     }
166
167     // old native fullscreen images are not available in graphite style
168     // thus they are loaded directly here
169     o_old_fullscreen_img = [[NSImage imageNamed:@"lion-window-fullscreen"] retain];
170     o_old_fullscreen_on_img = [[NSImage imageNamed:@"lion-window-fullscreen-on"] retain];
171     o_old_fullscreen_over_img = [[NSImage imageNamed:@"lion-window-fullscreen-over"] retain];
172
173     [o_red_btn setImage: o_red_img];
174     [o_red_btn setAlternateImage: o_red_on_img];
175     [[o_red_btn cell] setShowsBorderOnlyWhileMouseInside: YES];
176     [[o_red_btn cell] setTag: 0];
177     [o_yellow_btn setImage: o_yellow_img];
178     [o_yellow_btn setAlternateImage: o_yellow_on_img];
179     [[o_yellow_btn cell] setShowsBorderOnlyWhileMouseInside: YES];
180     [[o_yellow_btn cell] setTag: 1];
181
182     [self updateGreenButton];
183     [[o_green_btn cell] setShowsBorderOnlyWhileMouseInside: YES];
184     [[o_green_btn cell] setTag: 2];
185
186     [o_fullscreen_btn setImage: o_old_fullscreen_img];
187     [o_fullscreen_btn setAlternateImage: o_old_fullscreen_on_img];
188     [[o_fullscreen_btn cell] setShowsBorderOnlyWhileMouseInside: YES];
189     [[o_fullscreen_btn cell] setTag: 3];
190 }
191
192 - (void)updateGreenButton
193 {
194     // default image for old version, or if native fullscreen is
195     // disabled on yosemite, or if alt key is pressed
196     if (!OSX_YOSEMITE || !b_nativeFullscreenMode || b_alt_pressed) {
197
198         if (b_mouse_over) {
199             [o_green_btn setImage: o_green_over_img];
200             [o_green_btn setAlternateImage: o_green_on_img];
201         } else {
202             [o_green_btn setImage: o_green_img];
203             [o_green_btn setAlternateImage: o_green_on_img];
204         }
205     } else {
206
207         if (b_mouse_over) {
208             [o_green_btn setImage: o_fullscreen_over_img];
209             [o_green_btn setAlternateImage: o_fullscreen_on_img];
210         } else {
211             [o_green_btn setImage: o_fullscreen_img];
212             [o_green_btn setAlternateImage: o_fullscreen_on_img];
213         }
214     }
215 }
216
217 - (BOOL)mouseDownCanMoveWindow
218 {
219     return YES;
220 }
221
222 - (IBAction)buttonAction:(id)sender
223 {
224     if (sender == o_red_btn)
225         [[self window] performClose: sender];
226     else if (sender == o_yellow_btn)
227         [[self window] miniaturize: sender];
228     else if (sender == o_green_btn) {
229         if (OSX_YOSEMITE && b_nativeFullscreenMode && !b_alt_pressed) {
230             [[self window] toggleFullScreen:self];
231         } else {
232             [[self window] performZoom: sender];
233         }
234     } else if (sender == o_fullscreen_btn) {
235         // same action as native fs button
236         [[self window] toggleFullScreen:self];
237
238     } else
239         msg_Err(VLCIntf, "unknown button action sender");
240
241     [self setWindowButtonOver: NO];
242     [self setWindowFullscreenButtonOver: NO];
243 }
244
245 - (void)setWindowTitle:(NSString *)title
246 {
247     if (!o_window_title_shadow) {
248         o_window_title_shadow = [[NSShadow alloc] init];
249         [o_window_title_shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.5]];
250         [o_window_title_shadow setShadowOffset:NSMakeSize(0.0, -1.5)];
251         [o_window_title_shadow setShadowBlurRadius:0.5];
252         [o_window_title_shadow retain];
253     }
254
255     NSMutableAttributedString *o_attributed_title = [[NSMutableAttributedString alloc] initWithString:title attributes: o_window_title_attributes_dict];
256     NSUInteger i_titleLength = [title length];
257
258     [o_attributed_title addAttribute:NSShadowAttributeName value:o_window_title_shadow range:NSMakeRange(0, i_titleLength)];
259     [o_attributed_title setAlignment: NSCenterTextAlignment range:NSMakeRange(0, i_titleLength)];
260     [o_title_lbl setAttributedStringValue:o_attributed_title];
261     [o_attributed_title release];
262 }
263
264 - (void)setWindowButtonOver:(BOOL)b_value
265 {
266     b_mouse_over = b_value;
267     if (b_value) {
268         [o_red_btn setImage: o_red_over_img];
269         [o_yellow_btn setImage: o_yellow_over_img];
270     } else {
271         [o_red_btn setImage: o_red_img];
272         [o_yellow_btn setImage: o_yellow_img];
273     }
274
275     [self updateGreenButton];
276 }
277
278 - (void)setWindowFullscreenButtonOver:(BOOL)b_value
279 {
280     if (b_value)
281         [o_fullscreen_btn setImage: o_old_fullscreen_over_img];
282     else
283         [o_fullscreen_btn setImage: o_old_fullscreen_img];
284 }
285
286 - (void)mouseDown:(NSEvent *)event
287 {
288     NSPoint ml = [self convertPoint: [event locationInWindow] fromView: self];
289     if (([[self window] frame].size.height - ml.y) <= 22. && [event clickCount] == 2) {
290         //Get settings from "System Preferences" >  "Appearance" > "Double-click on windows title bar to minimize"
291         NSString *const MDAppleMiniaturizeOnDoubleClickKey = @"AppleMiniaturizeOnDoubleClick";
292         NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
293         [userDefaults addSuiteNamed:NSGlobalDomain];
294
295         if ([[userDefaults objectForKey:MDAppleMiniaturizeOnDoubleClickKey] boolValue])
296             [[self window] miniaturize:self];
297     }
298
299     [super mouseDown: event];
300 }
301
302 - (NSButton*)closeButton
303 {
304     return o_red_btn;
305 }
306
307 - (NSButton*)minimizeButton
308 {
309     return o_yellow_btn;
310 }
311
312 - (NSButton*)zoomButton
313 {
314     return o_green_btn;
315 }
316
317 @end
318
319 /*****************************************************************************
320  * VLCWindowButtonCell
321  *
322  * since the title bar cannot fetch these mouse events (the more top-level
323  * NSButton is unable fetch them as well), we are using a subclass of the
324  * button cell to do so. It's set in the nib for the respective objects.
325  *****************************************************************************/
326
327 @implementation VLCWindowButtonCell
328
329 - (void)mouseEntered:(NSEvent *)theEvent
330 {
331     if ([self tag] == 3)
332         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowFullscreenButtonOver: YES];
333     else
334         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowButtonOver: YES];
335 }
336
337 - (void)mouseExited:(NSEvent *)theEvent
338 {
339     if ([self tag] == 3)
340         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowFullscreenButtonOver: NO];
341     else
342         [(VLCMainWindowTitleView *)[[self controlView] superview] setWindowButtonOver: NO];
343 }
344
345 /* accessibility stuff */
346 - (NSArray*)accessibilityAttributeNames {
347     NSArray *theAttributeNames = [super accessibilityAttributeNames];
348     id theControlView = [self controlView];
349     return ([theControlView respondsToSelector: @selector(extendedAccessibilityAttributeNames:)] ? [theControlView extendedAccessibilityAttributeNames: theAttributeNames] : theAttributeNames); // ask the cell's control view (i.e., the button) for additional attribute values
350 }
351
352 - (id)accessibilityAttributeValue: (NSString*)theAttributeName {
353     id theControlView = [self controlView];
354     if ([theControlView respondsToSelector: @selector(extendedAccessibilityAttributeValue:)]) {
355         id theValue = [theControlView extendedAccessibilityAttributeValue: theAttributeName];
356         if (theValue) {
357             return theValue; // if this is an extended attribute value we added, return that -- otherwise, fall back to super's implementation
358         }
359     }
360     return [super accessibilityAttributeValue: theAttributeName];
361 }
362
363 - (BOOL)accessibilityIsAttributeSettable: (NSString*)theAttributeName {
364     id theControlView = [self controlView];
365     if ([theControlView respondsToSelector: @selector(extendedAccessibilityIsAttributeSettable:)]) {
366         NSNumber *theValue = [theControlView extendedAccessibilityIsAttributeSettable: theAttributeName];
367         if (theValue)
368             return [theValue boolValue]; // same basic strategy we use in -accessibilityAttributeValue:
369     }
370     return [super accessibilityIsAttributeSettable: theAttributeName];
371 }
372
373 @end
374
375
376 /*****************************************************************************
377  * VLCResizeControl
378  *
379  * For Leopard and Snow Leopard, we need to emulate the resize control on the
380  * bottom right of the window, since it is gone by using the borderless window
381  * mask. A proper fix would be Lion-only.
382  *****************************************************************************/
383
384 @implementation VLCResizeControl
385
386 - (void)mouseDown:(NSEvent *)theEvent {
387     BOOL keepOn = YES;
388
389     while (keepOn) {
390         theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask |
391                     NSLeftMouseDraggedMask];
392
393         switch ([theEvent type]) {
394             case NSLeftMouseDragged:
395             {
396                 NSRect windowFrame = [[self window] frame];
397                 CGFloat deltaX, deltaY, oldOriginY;
398                 deltaX = [theEvent deltaX];
399                 deltaY = [theEvent deltaY];
400                 oldOriginY = windowFrame.origin.y;
401
402                 windowFrame.origin.y = (oldOriginY + windowFrame.size.height) - (windowFrame.size.height + deltaY);
403                 windowFrame.size.width += deltaX;
404                 windowFrame.size.height += deltaY;
405
406                 NSSize winMinSize = [self window].minSize;
407                 if (windowFrame.size.width < winMinSize.width)
408                     windowFrame.size.width = winMinSize.width;
409
410                 if (windowFrame.size.height < winMinSize.height) {
411                     windowFrame.size.height = winMinSize.height;
412                     windowFrame.origin.y = oldOriginY;
413                 }
414
415                 [[self window] setFrame: windowFrame display: YES animate: NO];
416                 break;
417             }
418                 break;
419             case NSLeftMouseUp:
420                 keepOn = NO;
421                 break;
422             default:
423                 /* Ignore any other kind of event. */
424                 break;
425         }
426
427     };
428
429     return;
430 }
431
432 @end
433
434 /*****************************************************************************
435  * VLCColorView
436  *
437  * since we are using a clear window color when using the black window
438  * style, some filling is needed behind the video and some other elements
439  *****************************************************************************/
440
441 @implementation VLCColorView
442
443 - (void)drawRect:(NSRect)rect {
444     [[NSColor blackColor] setFill];
445     NSRectFill(rect);
446 }
447
448 @end
449
450 /*****************************************************************************
451  * custom window buttons to support the accessibility stuff
452  *****************************************************************************/
453
454 @implementation VLCCustomWindowButtonPrototype
455 + (Class)cellClass {
456     return [VLCWindowButtonCell class];
457 }
458
459 - (NSArray*)extendedAccessibilityAttributeNames: (NSArray*)theAttributeNames {
460     return ([theAttributeNames containsObject: NSAccessibilitySubroleAttribute] ? theAttributeNames : [theAttributeNames arrayByAddingObject: NSAccessibilitySubroleAttribute]); // run-of-the-mill button cells don't usually have a Subrole attribute, so we add that attribute
461 }
462
463 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
464     return nil;
465 }
466
467 - (NSNumber*)extendedAccessibilityIsAttributeSettable: (NSString*)theAttributeName {
468     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? [NSNumber numberWithBool:NO] : nil); // make the Subrole attribute we added non-settable
469 }
470
471 - (void)accessibilityPerformAction: (NSString*)theActionName {
472     if ([theActionName isEqualToString: NSAccessibilityPressAction]) {
473         if ([self isEnabled])
474             [self performClick: nil];
475     } else
476         [super accessibilityPerformAction: theActionName];
477 }
478
479 @end
480
481 @implementation VLCCustomWindowCloseButton
482 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
483     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityCloseButtonAttribute : nil);
484 }
485
486 @end
487
488
489 @implementation VLCCustomWindowMinimizeButton
490 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
491     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityMinimizeButtonAttribute : nil);
492 }
493
494 @end
495
496
497 @implementation VLCCustomWindowZoomButton
498 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
499     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityZoomButtonAttribute : nil);
500 }
501
502 @end
503
504
505 @implementation VLCCustomWindowFullscreenButton
506 #ifdef MAC_OS_X_VERSION_10_7
507 - (id)extendedAccessibilityAttributeValue: (NSString*)theAttributeName {
508     return ([theAttributeName isEqualToString: NSAccessibilitySubroleAttribute] ? NSAccessibilityFullScreenButtonAttribute : nil);
509 }
510 #endif
511
512 @end
513
514
515 @implementation VLCWindowTitleTextField
516
517 - (void)dealloc
518 {
519     if (contextMenu)
520         [contextMenu release];
521
522     [super dealloc];
523 }
524
525 - (void)showRightClickMenuWithEvent:(NSEvent *)o_event
526 {
527     if (contextMenu)
528         [contextMenu release];
529
530     NSURL * representedURL = [[self window] representedURL];
531     if (!representedURL)
532         return;
533
534     NSArray * pathComponents;
535     pathComponents = [representedURL pathComponents];
536
537     if (!pathComponents)
538         return;
539
540     contextMenu = [[NSMenu alloc] initWithTitle: [[NSFileManager defaultManager] displayNameAtPath: [representedURL path]]];
541
542     NSUInteger count = [pathComponents count];
543     NSImage * icon;
544     NSMenuItem * currentItem;
545     NSMutableString * currentPath;
546     NSSize iconSize = NSMakeSize(16., 16.);
547     for (NSUInteger i = count - 1; i > 0; i--) {
548         currentPath = [NSMutableString stringWithCapacity:1024];
549         for (NSUInteger y = 0; y < i; y++)
550             [currentPath appendFormat: @"/%@", [pathComponents objectAtIndex:y + 1]];
551
552         [contextMenu addItemWithTitle: [[NSFileManager defaultManager] displayNameAtPath: currentPath] action:@selector(revealInFinder:) keyEquivalent:@""];
553         currentItem = [contextMenu itemAtIndex:[contextMenu numberOfItems] - 1];
554         [currentItem setTarget: self];
555
556         icon = [[NSWorkspace sharedWorkspace] iconForFile:currentPath];
557         [icon setSize: iconSize];
558         [currentItem setImage: icon];
559     }
560
561     if ([[pathComponents objectAtIndex:1] isEqualToString:@"Volumes"]) {
562         /* we don't want to show the Volumes item, since the Cocoa does it neither */
563         currentItem = [contextMenu itemWithTitle:[[NSFileManager defaultManager] displayNameAtPath: @"/Volumes"]];
564         if (currentItem)
565             [contextMenu removeItem: currentItem];
566     } else {
567         /* we're on the boot drive, so add it since it isn't part of the components */
568         [contextMenu addItemWithTitle: [[NSFileManager defaultManager] displayNameAtPath:@"/"] action:@selector(revealInFinder:) keyEquivalent:@""];
569         currentItem = [contextMenu itemAtIndex: [contextMenu numberOfItems] - 1];
570         icon = [[NSWorkspace sharedWorkspace] iconForFile:@"/"];
571         [icon setSize: iconSize];
572         [currentItem setImage: icon];
573         [currentItem setTarget: self];
574     }
575
576     /* add the computer item */
577     [contextMenu addItemWithTitle: [(NSString*)SCDynamicStoreCopyComputerName(NULL, NULL) autorelease] action:@selector(revealInFinder:) keyEquivalent:@""];
578     currentItem = [contextMenu itemAtIndex: [contextMenu numberOfItems] - 1];
579     icon = [NSImage imageNamed: NSImageNameComputer];
580     [icon setSize: iconSize];
581     [currentItem setImage: icon];
582     [currentItem setTarget: self];
583
584     // center the context menu similar to the white interface
585     CGFloat menuWidth = [contextMenu size].width;
586     NSRect windowFrame = [[self window] frame];
587     NSPoint point;
588
589     CGFloat fullButtonWidth = 0.;
590     if([[VLCMain sharedInstance] nativeFullscreenMode])
591         fullButtonWidth = 20.;
592
593     // assumes 60 px for the window buttons
594     point.x = (windowFrame.size.width - 60. - fullButtonWidth) / 2. - menuWidth / 2. + 60. - 20.;
595     point.y = windowFrame.size.height + 1.;
596     if (point.x < 0)
597         point.x = 10;
598
599     NSEvent *fakeMouseEvent = [NSEvent mouseEventWithType:NSRightMouseDown
600                                                  location:point
601                                             modifierFlags:0
602                                                 timestamp:0
603                                              windowNumber:[[self window] windowNumber]
604                                                   context:nil
605                                               eventNumber:0
606                                                clickCount:0
607                                                  pressure:0];
608     [NSMenu popUpContextMenu: contextMenu withEvent: fakeMouseEvent forView: [self superview]];
609 }
610
611 - (IBAction)revealInFinder:(id)sender
612 {
613     NSUInteger count = [contextMenu numberOfItems];
614     NSUInteger selectedItem = [contextMenu indexOfItem: sender];
615
616     if (selectedItem == count - 1) { // the fake computer item
617         [[NSWorkspace sharedWorkspace] selectFile: @"/" inFileViewerRootedAtPath: @""];
618         return;
619     }
620
621     NSURL * representedURL = [[self window] representedURL];
622     if (! representedURL)
623         return;
624
625     if (selectedItem == 0) { // the actual file, let's save time
626         [[NSWorkspace sharedWorkspace] selectFile: [representedURL path] inFileViewerRootedAtPath: [representedURL path]];
627         return;
628     }
629
630     NSArray * pathComponents;
631     pathComponents = [representedURL pathComponents];
632     if (!pathComponents)
633         return;
634
635     NSMutableString * currentPath;
636     currentPath = [NSMutableString stringWithCapacity:1024];
637     selectedItem = count - selectedItem;
638
639     /* fix for non-startup volumes */
640     if ([[pathComponents objectAtIndex:1] isEqualToString:@"Volumes"])
641         selectedItem += 1;
642
643     for (NSUInteger y = 1; y < selectedItem; y++)
644         [currentPath appendFormat: @"/%@", [pathComponents objectAtIndex:y]];
645
646     [[NSWorkspace sharedWorkspace] selectFile: currentPath inFileViewerRootedAtPath: currentPath];
647 }
648
649 - (void)rightMouseDown:(NSEvent *)o_event
650 {
651     if ([o_event type] == NSRightMouseDown)
652         [self showRightClickMenuWithEvent:o_event];
653
654     [super mouseDown: o_event];
655 }
656
657 @end