source: trunk/macosx/FileOutlineController.m @ 10116

Last change on this file since 10116 was 10116, checked in by livings124, 12 years ago

add some temporary asserts to track down #2805

  • Property svn:keywords set to Date Rev Author Id
File size: 18.0 KB
Line 
1/******************************************************************************
2 * $Id: FileOutlineController.m 10116 2010-02-06 17:12:16Z livings124 $
3 *
4 * Copyright (c) 2008-2010 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    {
165        NSIndexSet * indexSet = [(FileListNode *)item indexes];
166        NSAssert(indexSet != nil, @"indexSet must not be nil - temp");
167        return [NSNumber numberWithInteger: [fTorrent checkForFiles: [(FileListNode *)item indexes]]];
168    }
169    else
170        return item;
171}
172
173- (void) outlineView: (NSOutlineView *) outlineView willDisplayCell: (id) cell
174            forTableColumn: (NSTableColumn *) tableColumn item: (id) item
175{
176    NSString * identifier = [tableColumn identifier];
177    if ([identifier isEqualToString: @"Check"])
178    {
179        NSIndexSet * indexSet = [(FileListNode *)item indexes];
180        NSAssert(indexSet != nil, @"indexSet must not be nil - temp");
181        [cell setEnabled: [fTorrent canChangeDownloadCheckForFiles: [(FileListNode *)item indexes]]];
182    }
183    else if ([identifier isEqualToString: @"Priority"])
184    {
185        [cell setRepresentedObject: item];
186       
187        NSInteger hoveredRow = [fOutline hoveredRow];
188        [(FilePriorityCell *)cell setHovered: hoveredRow != -1 && hoveredRow == [fOutline rowForItem: item]];
189    }
190    else;
191}
192
193- (void) outlineView: (NSOutlineView *) outlineView setObjectValue: (id) object
194        forTableColumn: (NSTableColumn *) tableColumn byItem: (id) item
195{
196    NSString * identifier = [tableColumn identifier];
197    if ([identifier isEqualToString: @"Check"])
198    {
199        NSIndexSet * indexSet;
200        if (([NSApp isOnSnowLeopardOrBetter] ? [NSEvent modifierFlags] : [[NSApp currentEvent] modifierFlags]) & NSAlternateKeyMask)
201            indexSet = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fTorrent fileCount])];
202        else
203            indexSet = [(FileListNode *)item indexes];
204       
205        [fTorrent setFileCheckState: [object intValue] != NSOffState ? NSOnState : NSOffState forIndexes: indexSet];
206        [fOutline reloadData];
207       
208        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
209    }
210}
211
212- (NSString *) outlineView: (NSOutlineView *) outlineView typeSelectStringForTableColumn: (NSTableColumn *) tableColumn item: (id) item
213{
214    return [(FileListNode *)item name];
215}
216
217- (NSString *) outlineView: (NSOutlineView *) outlineView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
218        tableColumn: (NSTableColumn *) tableColumn item: (id) item mouseLocation: (NSPoint) mouseLocation
219{
220    NSString * ident = [tableColumn identifier];
221    if ([ident isEqualToString: @"Name"])
222        return [fTorrent fileLocation: item];
223    else if ([ident isEqualToString: @"Check"])
224    {
225        switch ([cell state])
226        {
227            case NSOffState:
228                return NSLocalizedString(@"Don't Download", "files tab -> tooltip");
229            case NSOnState:
230                return NSLocalizedString(@"Download", "files tab -> tooltip");
231            case NSMixedState:
232                return NSLocalizedString(@"Download Some", "files tab -> tooltip");
233        }
234    }
235    else if ([ident isEqualToString: @"Priority"])
236    {
237        NSSet * priorities = [fTorrent filePrioritiesForIndexes: [(FileListNode *)item indexes]];
238        switch ([priorities count])
239        {
240            case 0:
241                return NSLocalizedString(@"Priority Not Available", "files tab -> tooltip");
242            case 1:
243                switch ([[priorities anyObject] intValue])
244                {
245                    case TR_PRI_LOW:
246                        return NSLocalizedString(@"Low Priority", "files tab -> tooltip");
247                    case TR_PRI_HIGH:
248                        return NSLocalizedString(@"High Priority", "files tab -> tooltip");
249                    case TR_PRI_NORMAL:
250                        return NSLocalizedString(@"Normal Priority", "files tab -> tooltip");
251                }
252                break;
253            default:
254                return NSLocalizedString(@"Multiple Priorities", "files tab -> tooltip");
255        }
256    }
257    else;
258   
259    return nil;
260}
261
262- (CGFloat) outlineView: (NSOutlineView *) outlineView heightOfRowByItem: (id) item
263{
264    if ([(FileListNode *)item isFolder])
265        return ROW_SMALL_HEIGHT;
266    else
267        return [outlineView rowHeight];
268}
269
270- (void) setCheck: (id) sender
271{
272    NSInteger state = [sender tag] == FILE_UNCHECK_TAG ? NSOffState : NSOnState;
273   
274    NSIndexSet * indexSet = [fOutline selectedRowIndexes];
275    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
276    for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
277        [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
278   
279    [fTorrent setFileCheckState: state forIndexes: itemIndexes];
280    [fOutline reloadData];
281}
282
283- (void) setOnlySelectedCheck: (id) sender
284{
285    NSIndexSet * indexSet = [fOutline selectedRowIndexes];
286    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
287    for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
288        [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
289   
290    [fTorrent setFileCheckState: NSOnState forIndexes: itemIndexes];
291   
292    NSMutableIndexSet * remainingItemIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fTorrent fileCount])];
293    [remainingItemIndexes removeIndexes: itemIndexes];
294    [fTorrent setFileCheckState: NSOffState forIndexes: remainingItemIndexes];
295   
296    [fOutline reloadData];
297}
298
299- (void) setPriority: (id) sender
300{
301    tr_priority_t priority;
302    switch ([sender tag])
303    {
304        case FILE_PRIORITY_HIGH_TAG:
305            priority = TR_PRI_HIGH;
306            break;
307        case FILE_PRIORITY_NORMAL_TAG:
308            priority = TR_PRI_NORMAL;
309            break;
310        case FILE_PRIORITY_LOW_TAG:
311            priority = TR_PRI_LOW;
312    }
313   
314    NSIndexSet * indexSet = [fOutline selectedRowIndexes];
315    NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
316    for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
317        [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
318   
319    [fTorrent setFilePriority: priority forIndexes: itemIndexes];
320    [fOutline reloadData];
321}
322
323- (void) revealFile: (id) sender
324{
325    NSIndexSet * indexes = [fOutline selectedRowIndexes];
326    if ([NSApp isOnSnowLeopardOrBetter])
327    {
328        NSMutableArray * paths = [NSMutableArray arrayWithCapacity: [indexes count]];
329        for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
330        {
331            NSString * path = [fTorrent fileLocation: [fOutline itemAtRow: i]];
332            if (path)
333                [paths addObject: [NSURL fileURLWithPath: path]];
334        }
335       
336        if ([paths count])
337            [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs: paths];
338    }
339    else
340    {
341        for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
342        {
343            NSString * path = [fTorrent fileLocation: [fOutline itemAtRow: i]];
344            if (path)
345                [[NSWorkspace sharedWorkspace] selectFile: path inFileViewerRootedAtPath: nil];
346        }
347    }
348}
349
350#warning make real view controller (Leopard-only) so that Command-R will work
351- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
352{
353    if (!fTorrent)
354        return NO;
355   
356    SEL action = [menuItem action];
357   
358    if (action == @selector(revealFile:))
359    {
360        NSIndexSet * indexSet = [fOutline selectedRowIndexes];
361        for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
362            if ([fTorrent fileLocation: [fFileList objectAtIndex: i]] != nil)
363                return YES;
364        return NO;
365    }
366   
367    if (action == @selector(setCheck:))
368    {
369        if ([fOutline numberOfSelectedRows] == 0)
370            return NO;
371       
372        NSIndexSet * indexSet = [fOutline selectedRowIndexes];
373        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
374        for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
375            [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
376       
377        NSInteger state = ([menuItem tag] == FILE_CHECK_TAG) ? NSOnState : NSOffState;
378        NSAssert(itemIndexes != nil, @"indexSet must not be nil - temp");
379        return [fTorrent checkForFiles: itemIndexes] != state && [fTorrent canChangeDownloadCheckForFiles: itemIndexes];
380    }
381   
382    if (action == @selector(setOnlySelectedCheck:))
383    {
384        if ([fOutline numberOfSelectedRows] == 0)
385            return NO;
386       
387        NSIndexSet * indexSet = [fOutline selectedRowIndexes];
388        NSMutableIndexSet * itemIndexes = [NSMutableIndexSet indexSet];
389        for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
390            [itemIndexes addIndexes: [[fOutline itemAtRow: i] indexes]];
391       
392        NSAssert(itemIndexes != nil, @"indexSet must not be nil - temp");
393        return [fTorrent canChangeDownloadCheckForFiles: itemIndexes];
394    }
395   
396    if (action == @selector(setPriority:))
397    {
398        if ([fOutline numberOfSelectedRows] == 0)
399        {
400            [menuItem setState: NSOffState];
401            return NO;
402        }
403       
404        //determine which priorities are checked
405        NSIndexSet * indexSet = [fOutline selectedRowIndexes];
406        tr_priority_t priority;
407        switch ([menuItem tag])
408        {
409            case FILE_PRIORITY_HIGH_TAG:
410                priority = TR_PRI_HIGH;
411                break;
412            case FILE_PRIORITY_NORMAL_TAG:
413                priority = TR_PRI_NORMAL;
414                break;
415            case FILE_PRIORITY_LOW_TAG:
416                priority = TR_PRI_LOW;
417                break;
418        }
419       
420        BOOL current = NO, canChange = NO;
421        for (NSInteger i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
422        {
423            NSIndexSet * fileIndexSet = [[fOutline itemAtRow: i] indexes];
424            NSAssert(fileIndexSet != nil, @"indexSet must not be nil - temp");
425            if (![fTorrent canChangeDownloadCheckForFiles: fileIndexSet])
426                continue;
427           
428            canChange = YES;
429            if ([fTorrent hasFilePriority: priority forIndexes: fileIndexSet])
430            {
431                current = YES;
432                break;
433            }
434        }
435       
436        [menuItem setState: current ? NSOnState : NSOffState];
437        return canChange;
438    }
439   
440    return YES;
441}
442
443@end
444
445@implementation FileOutlineController (Private)
446
447- (NSMenu *) menu
448{
449    NSMenu * menu = [[NSMenu alloc] initWithTitle: @"File Outline Menu"];
450   
451    //check and uncheck
452    NSMenuItem * item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Check Selected", "File Outline -> Menu")
453                            action: @selector(setCheck:) keyEquivalent: @""];
454    [item setTarget: self];
455    [item setTag: FILE_CHECK_TAG];
456    [menu addItem: item];
457    [item release];
458   
459    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Uncheck Selected", "File Outline -> Menu")
460            action: @selector(setCheck:) keyEquivalent: @""];
461    [item setTarget: self];
462    [item setTag: FILE_UNCHECK_TAG];
463    [menu addItem: item];
464    [item release];
465   
466    //only check selected
467    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Only Check Selected", "File Outline -> Menu")
468            action: @selector(setOnlySelectedCheck:) keyEquivalent: @""];
469    [item setTarget: self];
470    [menu addItem: item];
471    [item release];
472   
473    [menu addItem: [NSMenuItem separatorItem]];
474   
475    //priority
476    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Priority", "File Outline -> Menu") action: NULL keyEquivalent: @""];
477    NSMenu * priorityMenu = [[NSMenu alloc] initWithTitle: @"File Priority Menu"];
478    [item setSubmenu: priorityMenu];
479    [menu addItem: item];
480    [item release];
481   
482    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"High", "File Outline -> Priority Menu")
483            action: @selector(setPriority:) keyEquivalent: @""];
484    [item setTarget: self];
485    [item setTag: FILE_PRIORITY_HIGH_TAG];
486    [item setImage: [NSImage imageNamed: @"PriorityHigh.png"]];
487    [priorityMenu addItem: item];
488    [item release];
489   
490    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Normal", "File Outline -> Priority Menu")
491            action: @selector(setPriority:) keyEquivalent: @""];
492    [item setTarget: self];
493    [item setTag: FILE_PRIORITY_NORMAL_TAG];
494    [item setImage: [NSImage imageNamed: @"PriorityNormal.png"]];
495    [priorityMenu addItem: item];
496    [item release];
497   
498    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Low", "File Outline -> Priority Menu")
499            action: @selector(setPriority:) keyEquivalent: @""];
500    [item setTarget: self];
501    [item setTag: FILE_PRIORITY_LOW_TAG];
502    [item setImage: [NSImage imageNamed: @"PriorityLow.png"]];
503    [priorityMenu addItem: item];
504    [item release];
505   
506    [priorityMenu release];
507   
508    [menu addItem: [NSMenuItem separatorItem]];
509   
510    //reveal in finder
511    item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString(@"Show in Finder", "File Outline -> Menu")
512            action: @selector(revealFile:) keyEquivalent: @""];
513    [item setTarget: self];
514    [menu addItem: item];
515    [item release];
516   
517    return [menu autorelease];
518}
519
520@end
Note: See TracBrowser for help on using the repository browser.