source: trunk/macosx/FileOutlineController.m @ 12418

Last change on this file since 12418 was 12418, checked in by livings124, 11 years ago

#4232 Extend tooltip support to disabled files in Inspector->Files

  • Property svn:keywords set to Date Rev Author Id
File size: 17.7 KB
Line 
1/******************************************************************************
2 * $Id: FileOutlineController.m 12418 2011-05-09 02:13:39Z livings124 $
3 *
4 * Copyright (c) 2008-2011 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#import "FileOutlineController.h"
26#import "Torrent.h"
27#import "FileOutlineView.h"
28#import "FilePriorityCell.h"
29#import "FileListNode.h"
30#import "NSApplicationAdditions.h"
31#import <Quartz/Quartz.h>
32
33#define ROW_SMALL_HEIGHT 18.0
34
35typedef enum
36{
37    FILE_CHECK_TAG,
38    FILE_UNCHECK_TAG
39} fileCheckMenuTag;
40
41typedef enum
42{
43    FILE_PRIORITY_HIGH_TAG,
44    FILE_PRIORITY_NORMAL_TAG,
45    FILE_PRIORITY_LOW_TAG
46} filePriorityMenuTag;
47
48@interface FileOutlineController (Private)
49
50- (NSMenu *) menu;
51
52@end
53
54@implementation FileOutlineController
55
56- (void) awakeFromNib
57{
58    [fOutline setDoubleAction: @selector(revealFile:)];
59    [fOutline setTarget: self];
60   
61    //set table header tool tips
62    [[fOutline tableColumnWithIdentifier: @"Check"] setHeaderToolTip: NSLocalizedString(@"Download",
63                                                                        "file table -> header tool tip")];
64    [[fOutline tableColumnWithIdentifier: @"Priority"] setHeaderToolTip: NSLocalizedString(@"Priority",
65                                                                        "file table -> header tool tip")];
66   
67    [fOutline setMenu: [self menu]];
68   
69    [self setTorrent: nil];
70}
71
72- (void) dealloc
73{
74    [fFileList release];
75    [fFilterText release];
76    [super dealloc];
77}
78
79- (FileOutlineView *) outlineView
80{
81    return fOutline;
82}
83
84- (void) setTorrent: (Torrent *) torrent
85{
86    fTorrent = torrent;
87    [fOutline setTorrent: fTorrent];
88   
89    [fFileList release];
90    fFileList = [[fTorrent fileList] retain];
91   
92    [fFilterText release];
93    fFilterText = nil;
94   
95    [fOutline deselectAll: nil];
96    [fOutline reloadData];
97}
98
99- (void) setFilterText: (NSString *) text
100{
101    if ([text isEqualToString: @""])
102        text = nil;
103   
104    if ((!text && !fFilterText) || (text && fFilterText && [text isEqualToString: fFilterText]))
105        return;
106   
107    [fFilterText release];
108    fFilterText = [text retain];
109   
110    [fFileList release];
111    if (!fFilterText)
112        fFileList = [[fTorrent fileList] retain];
113    else
114    {
115        NSMutableArray * list = [NSMutableArray arrayWithCapacity: [fTorrent fileCount]];
116       
117        for (FileListNode * node in [fTorrent flatFileList])
118            if ([[node name] rangeOfString: fFilterText options: NSCaseInsensitiveSearch].location != NSNotFound)
119                [list addObject: node];
120       
121        fFileList = [[NSArray alloc] initWithArray: list];
122    }
123   
124    [fOutline reloadData];
125}
126
127- (void) reloadData
128{
129    [fTorrent updateFileStat];
130    [fOutline reloadData];
131}
132
133- (void) outlineViewSelectionDidChange: (NSNotification *) notification
134{
135    if ([NSApp isOnSnowLeopardOrBetter] && [QLPreviewPanelSL sharedPreviewPanelExists]
136        && [[QLPreviewPanelSL sharedPreviewPanel] isVisible])
137        [[QLPreviewPanelSL sharedPreviewPanel] reloadData];
138}
139
140- (NSInteger) outlineView: (NSOutlineView *) outlineView numberOfChildrenOfItem: (id) item
141{
142    if (!item)
143        return fFileList ? [fFileList count] : 0;
144    else
145    {
146        FileListNode * node = (FileListNode *)item;
147        return [node isFolder] ? [[node children] count] : 0;
148    }
149}
150
151- (BOOL) outlineView: (NSOutlineView *) outlineView isItemExpandable: (id) item
152{
153    return [(FileListNode *)item isFolder];
154}
155
156- (id) outlineView: (NSOutlineView *) outlineView child: (NSInteger) index ofItem: (id) item
157{
158    return [(item ? [(FileListNode *)item children] : fFileList) objectAtIndex: index];
159}
160
161- (id) outlineView: (NSOutlineView *) outlineView objectValueForTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
162{
163    if ([[tableColumn identifier] isEqualToString: @"Check"])
164        return [NSNumber numberWithInteger: [fTorrent checkForFiles: [(FileListNode *)item indexes]]];
165    else
166        return item;
167}
168
169- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell
170            forTableColumn: (NSTableColumn *) tableColumn item: (id) item
171{
172    NSString * identifier = [tableColumn identifier];
173    if ([identifier isEqualToString: @"Check"])
174        [cell setEnabled: [fTorrent canChangeDownloadCheckForFiles: [(FileListNode *)item indexes]]];
175    else if ([identifier isEqualToString: @"Priority"])
176    {
177        [cell setRepresentedObject: item];
178       
179        NSInteger hoveredRow = [fOutline hoveredRow];
180        [(FilePriorityCell *)cell setHovered: hoveredRow != -1 && hoveredRow == [fOutline rowForItem: item]];
181    }
182    else;
183}
184
185- (void) outlineView: (NSOutlineView *) outlineView setObjectValue: (id) object
186        forTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
187{
188    NSString * identifier = [tableColumn identifier];
189    if ([identifier isEqualToString: @"Check"])
190    {
191        NSIndexSet * indexSet;
192        if (([NSApp isOnSnowLeopardOrBetter] ? [NSEvent modifierFlags] : [[NSApp currentEvent] modifierFlags]) & NSAlternateKeyMask)
193            indexSet = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fTorrent fileCount])];
194        else
195            indexSet = [(FileListNode *)item indexes];
196       
197        [fTorrent setFileCheckState: [object intValue] != NSOffState ? NSOnState : NSOffState forIndexes: indexSet];
198        [fOutline reloadData];
199       
200        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
201    }
202}
203
204- (NSString *) outlineView: (NSOutlineView *) outlineView typeSelectStringForTableColumn: (NSTableColumn *) tableColumn item: (id) item
205{
206    return [(FileListNode *)item name];
207}
208
209- (NSString *) outlineView: (NSOutlineView *) outlineView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
210        tableColumn: (NSTableColumn *) tableColumn item: (id) item mouseLocation: (NSPoint) mouseLocation
211{
212    NSString * ident = [tableColumn identifier];
213    if ([ident isEqualToString: @"Name"])
214    {
215        NSString * path = [fTorrent fileLocation: item];
216        if (!path)
217            path = [[item path] stringByAppendingPathComponent: [item name]];
218        return path;
219    }
220    else if ([ident isEqualToString: @"Check"])
221    {
222        switch ([cell state])
223        {
224            case NSOffState:
225                return NSLocalizedString(@"Don't Download", "files tab -> tooltip");
226            case NSOnState:
227                return NSLocalizedString(@"Download", "files tab -> tooltip");
228            case NSMixedState:
229                return NSLocalizedString(@"Download Some", "files tab -> tooltip");
230        }
231    }
232    else if ([ident isEqualToString: @"Priority"])
233    {
234        NSSet * priorities = [fTorrent filePrioritiesForIndexes: [(FileListNode *)item indexes]];
235        switch ([priorities count])
236        {
237            case 0:
238                return NSLocalizedString(@"Priority Not Available", "files tab -> tooltip");
239            case 1:
240                switch ([[priorities anyObject] intValue])
241                {
242                    case TR_PRI_LOW:
243                        return NSLocalizedString(@"Low Priority", "files tab -> tooltip");
244                    case TR_PRI_HIGH:
245                        return NSLocalizedString(@"High Priority", "files tab -> tooltip");
246                    case TR_PRI_NORMAL:
247                        return NSLocalizedString(@"Normal Priority", "files tab -> tooltip");
248                }
249                break;
250            default:
251                return NSLocalizedString(@"Multiple Priorities", "files tab -> tooltip");
252        }
253    }
254    else;
255   
256    return nil;
257}
258
259- (CGFloat) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
260{
261    if ([(FileListNode *)item isFolder])
262        return ROW_SMALL_HEIGHT;
263    else
264        return [outlineView rowHeight];
265}
266
267- (void) setCheck: (id) sender
268{
269    NSInteger state = [sender tag] == FILE_UNCHECK_TAG ? NSOffState : NSOnState;
270   
271    NSIndexSet * indexSet = [fOutline selectedRowIndexes];
272    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
273    for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
274        [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
275   
276    [fTorrent setFileCheckState: state forIndexes: itemIndexes];
277    [fOutline reloadData];
278}
279
280- (void) setOnlySelectedCheck: (id) sender
281{
282    NSIndexSet * indexSet = [fOutline selectedRowIndexes];
283    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
284    for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
285        [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
286   
287    [fTorrent setFileCheckState: NSOnState forIndexes: itemIndexes];
288   
289    NSMutableIndexSet * remainingItemIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fTorrent fileCount])];
290    [remainingItemIndexes removeIndexes: itemIndexes];
291    [fTorrent setFileCheckState: NSOffState forIndexes: remainingItemIndexes];
292   
293    [fOutline reloadData];
294}
295
296- (void) setPriority: (id) sender
297{
298    tr_priority_t priority;
299    switch ([sender tag])
300    {
301        case FILE_PRIORITY_HIGH_TAG:
302            priority = TR_PRI_HIGH;
303            break;
304        case FILE_PRIORITY_NORMAL_TAG:
305            priority = TR_PRI_NORMAL;
306            break;
307        case FILE_PRIORITY_LOW_TAG:
308            priority = TR_PRI_LOW;
309    }
310   
311    NSIndexSet * indexSet = [fOutline selectedRowIndexes];
312    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
313    for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
314        [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
315   
316    [fTorrent setFilePriority: priority forIndexes: itemIndexes];
317    [fOutline reloadData];
318}
319
320- (void) revealFile: (id) sender
321{
322    NSIndexSet * indexes = [fOutline selectedRowIndexes];
323    if ([NSApp isOnSnowLeopardOrBetter])
324    {
325        NSMutableArray * paths = [NSMutableArray arrayWithCapacity: [indexes count]];
326        for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
327        {
328            NSString * path = [fTorrent fileLocation: [fOutline itemAtRow: i]];
329            if (path)
330                [paths addObject: [NSURL fileURLWithPath: path]];
331        }
332       
333        if ([paths count])
334            [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs: paths];
335    }
336    else
337    {
338        for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
339        {
340            NSString * path = [fTorrent fileLocation: [fOutline itemAtRow: i]];
341            if (path)
342                [[NSWorkspace sharedWorkspace] selectFile: path inFileViewerRootedAtPath: nil];
343        }
344    }
345}
346
347#warning make real view controller (Leopard-only) so that Command-R will work
348- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
349{
350    if (!fTorrent)
351        return NO;
352   
353    SEL action = [menuItem action];
354   
355    if (action == @selector(revealFile:))
356    {
357        NSIndexSet * indexSet = [fOutline selectedRowIndexes];
358        for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
359            if ([fTorrent fileLocation: [fOutline itemAtRow: i]] != nil)
360                return YES;
361        return NO;
362    }
363   
364    if (action == @selector(setCheck:))
365    {
366        if ([fOutline numberOfSelectedRows] == 0)
367            return NO;
368       
369        NSIndexSet * indexSet = [fOutline selectedRowIndexes];
370        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
371        for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
372            [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
373       
374        NSInteger state = ([menuItem tag] == FILE_CHECK_TAG) ? NSOnState : NSOffState;
375        return [fTorrent checkForFiles: itemIndexes] != state && [fTorrent canChangeDownloadCheckForFiles: itemIndexes];
376    }
377   
378    if (action == @selector(setOnlySelectedCheck:))
379    {
380        if ([fOutline numberOfSelectedRows] == 0)
381            return NO;
382       
383        NSIndexSet * indexSet = [fOutline selectedRowIndexes];
384        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
385        for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
386            [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
387       
388        return [fTorrent canChangeDownloadCheckForFiles: itemIndexes];
389    }
390   
391    if (action == @selector(setPriority:))
392    {
393        if ([fOutline numberOfSelectedRows] == 0)
394        {
395            [menuItem setState: NSOffState];
396            return NO;
397        }
398       
399        //determine which priorities are checked
400        NSIndexSet * indexSet = [fOutline selectedRowIndexes];
401        tr_priority_t priority;
402        switch ([menuItem tag])
403        {
404            case FILE_PRIORITY_HIGH_TAG:
405                priority = TR_PRI_HIGH;
406                break;
407            case FILE_PRIORITY_NORMAL_TAG:
408                priority = TR_PRI_NORMAL;
409                break;
410            case FILE_PRIORITY_LOW_TAG:
411                priority = TR_PRI_LOW;
412                break;
413        }
414       
415        BOOL current = NO, canChange = NO;
416        for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
417        {
418            NSIndexSet * fileIndexSet = [[fOutline itemAtRow: i] indexes];
419            if (![fTorrent canChangeDownloadCheckForFiles: fileIndexSet])
420                continue;
421           
422            canChange = YES;
423            if ([fTorrent hasFilePriority: priority forIndexes: fileIndexSet])
424            {
425                current = YES;
426                break;
427            }
428        }
429       
430        [menuItem setState: current ? NSOnState : NSOffState];
431        return canChange;
432    }
433   
434    return YES;
435}
436
437@end
438
439@implementation FileOutlineController (Private)
440
441- (NSMenu *) menu
442{
443    NSMenu * menu = [[NSMenu alloc] initWithTitle: @"File Outline Menu"];
444   
445    //check and uncheck
446    NSMenuItem * item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Check Selected", "File Outline -> Menu")
447                            action: @selector(setCheck:) keyEquivalent: @""];
448    [item setTarget: self];
449    [item setTag: FILE_CHECK_TAG];
450    [menu addItem: item];
451    [item release];
452   
453    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Uncheck Selected", "File Outline -> Menu")
454            action: @selector(setCheck:) keyEquivalent: @""];
455    [item setTarget: self];
456    [item setTag: FILE_UNCHECK_TAG];
457    [menu addItem: item];
458    [item release];
459   
460    //only check selected
461    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Only Check Selected", "File Outline -> Menu")
462            action: @selector(setOnlySelectedCheck:) keyEquivalent: @""];
463    [item setTarget: self];
464    [menu addItem: item];
465    [item release];
466   
467    [menu addItem: [NSMenuItem separatorItem]];
468   
469    //priority
470    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Priority", "File Outline -> Menu") action: NULL keyEquivalent: @""];
471    NSMenu * priorityMenu = [[NSMenu alloc] initWithTitle: @"File Priority Menu"];
472    [item setSubmenu: priorityMenu];
473    [menu addItem: item];
474    [item release];
475   
476    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"High", "File Outline -> Priority Menu")
477            action: @selector(setPriority:) keyEquivalent: @""];
478    [item setTarget: self];
479    [item setTag: FILE_PRIORITY_HIGH_TAG];
480    [item setImage: [NSImage imageNamed: @"PriorityHigh.png"]];
481    [priorityMenu addItem: item];
482    [item release];
483   
484    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Normal", "File Outline -> Priority Menu")
485            action: @selector(setPriority:) keyEquivalent: @""];
486    [item setTarget: self];
487    [item setTag: FILE_PRIORITY_NORMAL_TAG];
488    [item setImage: [NSImage imageNamed: @"PriorityNormal.png"]];
489    [priorityMenu addItem: item];
490    [item release];
491   
492    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Low", "File Outline -> Priority Menu")
493            action: @selector(setPriority:) keyEquivalent: @""];
494    [item setTarget: self];
495    [item setTag: FILE_PRIORITY_LOW_TAG];
496    [item setImage: [NSImage imageNamed: @"PriorityLow.png"]];
497    [priorityMenu addItem: item];
498    [item release];
499   
500    [priorityMenu release];
501   
502    [menu addItem: [NSMenuItem separatorItem]];
503   
504    //reveal in finder
505    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Show in Finder", "File Outline -> Menu")
506            action: @selector(revealFile:) keyEquivalent: @""];
507    [item setTarget: self];
508    [menu addItem: item];
509    [item release];
510   
511    return [menu autorelease];
512}
513
514@end
Note: See TracBrowser for help on using the repository browser.