source: trunk/macosx/Controller.m @ 16

Last change on this file since 16 was 16, checked in by root, 16 years ago

Update 2005-12-17

File size: 30.6 KB
Line 
1/******************************************************************************
2 * Copyright (c) 2005 Eric Petit
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *****************************************************************************/
22
23#include <IOKit/IOMessage.h>
24
25#include "NameCell.h"
26#include "ProgressCell.h"
27#include "Utils.h"
28#include "TorrentTableView.h"
29
30#define TOOLBAR_OPEN        @"Toolbar Open"
31#define TOOLBAR_REMOVE      @"Toolbar Remove"
32#define TOOLBAR_PREFS       @"Toolbar Preferences"
33#define TOOLBAR_INFO        @"Toolbar Info"
34#define TOOLBAR_PAUSE_ALL   @"Toolbar Pause All"
35#define TOOLBAR_RESUME_ALL  @"Toolbar Resume All"
36
37#define CONTEXT_PAUSE           1
38#define CONTEXT_REMOVE          2
39#define CONTEXT_REMOVE_TORRENT  3
40#define CONTEXT_REMOVE_DATA     4
41#define CONTEXT_REMOVE_BOTH     5
42#define CONTEXT_INFO            6                                             
43
44static void sleepCallBack( void * controller, io_service_t y,
45        natural_t messageType, void * messageArgument )
46{
47    Controller * c = controller;
48    [c sleepCallBack: messageType argument: messageArgument];
49}
50
51@implementation Controller
52
53- (void) awakeFromNib
54{
55    fHandle = tr_init();
56
57    [fPrefsController setHandle: fHandle];
58
59    [fWindow setContentMinSize: NSMakeSize( 400, 120 )];
60
61    /* Check or uncheck menu item in respect to current preferences */
62    [fAdvancedBarItem setState: [[NSUserDefaults standardUserDefaults]
63        boolForKey:@"UseAdvancedBar"] ? NSOnState : NSOffState];
64   
65    fToolbar = [[NSToolbar alloc] initWithIdentifier: @"Transmission Toolbar"];
66    [fToolbar setDelegate: self];
67    [fToolbar setAllowsUserCustomization: YES];
68    [fToolbar setAutosavesConfiguration: YES];
69    [fWindow  setToolbar:  fToolbar];
70    [fWindow  setDelegate: self];
71
72    NSTableColumn * tableColumn;
73    NameCell      * nameCell;
74    ProgressCell  * progressCell;
75
76    nameCell     = [[NameCell     alloc] init];
77    progressCell = [[ProgressCell alloc] init];
78    tableColumn  = [fTableView tableColumnWithIdentifier: @"Name"];
79    [tableColumn setDataCell: nameCell];
80    [tableColumn setMinWidth: 10.0];
81    [tableColumn setMaxWidth: 3000.0];
82
83    tableColumn  = [fTableView tableColumnWithIdentifier: @"Progress"];
84    [tableColumn setDataCell: progressCell];
85    [tableColumn setMinWidth: 134.0];
86    [tableColumn setMaxWidth: 134.0];
87
88    [fTableView setAutosaveTableColumns: YES];
89    [fTableView sizeToFit];
90
91    [fTableView registerForDraggedTypes: [NSArray arrayWithObjects:
92        NSFilenamesPboardType, NULL]];
93
94    IONotificationPortRef  notify;
95    io_object_t            anIterator;
96
97    /* Register for sleep notifications */
98    fRootPort = IORegisterForSystemPower( self, &notify, sleepCallBack,
99                                          &anIterator);
100    if( !fRootPort )
101    {
102        printf( "Could not IORegisterForSystemPower\n" );
103    }
104    else
105    {
106        CFRunLoopAddSource( CFRunLoopGetCurrent(),
107                            IONotificationPortGetRunLoopSource( notify ),
108                            kCFRunLoopCommonModes );
109    }
110
111    NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
112
113    NSArray * history = [defaults arrayForKey: @"History"];
114    if( history )
115    {
116        unsigned i;
117        NSDictionary * dic;
118        NSString * torrentPath, * downloadFolder, * paused;
119
120        for( i = 0; i < [history count]; i++ )
121        {
122            dic = [history objectAtIndex: i];
123
124            torrentPath    = [dic objectForKey: @"TorrentPath"];
125            downloadFolder = [dic objectForKey: @"DownloadFolder"];
126            paused         = [dic objectForKey: @"Paused"];
127
128            if( !torrentPath || !downloadFolder || !paused )
129            {
130                continue;
131            }
132
133            if( tr_torrentInit( fHandle, [torrentPath UTF8String] ) )
134            {
135                continue;
136            }
137
138            tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
139                                    [downloadFolder UTF8String] );
140
141            if( [paused isEqualToString: @"NO"] )
142            {
143                tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
144            }
145        }
146    }
147   
148    /*  Register with the growl system */
149    [self growlRegister: self];
150   
151    /* Update the interface every 500 ms */
152    fCount = 0;
153    fStat  = NULL;
154    fTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5 target: self
155        selector: @selector( updateUI: ) userInfo: NULL repeats: YES];
156    [[NSRunLoop currentRunLoop] addTimer: fTimer
157        forMode: NSEventTrackingRunLoopMode];
158}
159
160- (void) windowDidResize: (NSNotification *) n
161{
162    [fTableView sizeToFit];
163}
164
165- (BOOL) windowShouldClose: (id) sender
166{
167    [fWindow orderOut: NULL];
168    return NO;
169}
170
171- (BOOL) applicationShouldHandleReopen: (NSApplication *) app
172    hasVisibleWindows: (BOOL) flag
173{
174    [self showMainWindow: NULL];
175    return NO;
176}
177
178- (NSApplicationTerminateReply) applicationShouldTerminate:
179    (NSApplication *) app
180{
181    NSMutableArray * history = [NSMutableArray
182        arrayWithCapacity: TR_MAX_TORRENT_COUNT];
183    int i;
184
185    /* Stop updating the interface */
186    [fTimer invalidate];
187
188    /* Save history and stop running torrents */
189    for( i = 0; i < fCount; i++ )
190    {
191        [history addObject: [NSDictionary dictionaryWithObjectsAndKeys:
192            [NSString stringWithUTF8String: fStat[i].info.torrent],
193            @"TorrentPath",
194            [NSString stringWithUTF8String: tr_torrentGetFolder( fHandle, i )],
195            @"DownloadFolder",
196            ( fStat[i].status & ( TR_STATUS_CHECK | TR_STATUS_DOWNLOAD |
197                TR_STATUS_SEED ) ) ? @"NO" : @"YES",
198            @"Paused",
199            NULL]];
200
201        if( fStat[i].status & ( TR_STATUS_CHECK |
202                TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
203        {
204            tr_torrentStop( fHandle, i );
205        }
206    }
207
208    /* Wait for torrents to stop (5 seconds timeout) */
209    NSDate * start = [NSDate date];
210    while( fCount > 0 )
211    {
212        while( [[NSDate date] timeIntervalSinceDate: start] < 5 )
213        {
214            fCount = tr_torrentStat( fHandle, &fStat );
215            if( fStat[0].status & TR_STATUS_PAUSE )
216            {
217                break;
218            }
219            usleep( 500000 );
220        }
221        tr_torrentClose( fHandle, 0 );
222        fCount = tr_torrentStat( fHandle, &fStat );
223    }
224
225    tr_close( fHandle );
226
227    [[NSUserDefaults standardUserDefaults]
228        setObject: history forKey: @"History"];
229
230    return NSTerminateNow;
231}
232
233- (void) folderChoiceClosed: (NSOpenPanel *) s returnCode: (int) code
234    contextInfo: (void *) info
235{
236    if( code != NSOKButton )
237    {
238        tr_torrentClose( fHandle, tr_torrentCount( fHandle ) - 1 );
239        [NSApp stopModal];
240        return;
241    }
242
243    tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
244                         [[[s filenames] objectAtIndex: 0] UTF8String] );
245    tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
246    [NSApp stopModal];
247}
248
249
250- (void) application: (NSApplication *) sender
251         openFiles: (NSArray *) filenames
252{
253    unsigned i;
254    NSUserDefaults * defaults;
255    NSString * downloadChoice, * downloadFolder, * torrentPath;
256
257    defaults       = [NSUserDefaults standardUserDefaults];
258    downloadChoice = [defaults stringForKey: @"DownloadChoice"];
259    downloadFolder = [defaults stringForKey: @"DownloadFolder"];
260
261    for( i = 0; i < [filenames count]; i++ )
262    {
263        torrentPath = [filenames objectAtIndex: i];
264
265        if( tr_torrentInit( fHandle, [torrentPath UTF8String] ) )
266        {
267            continue;
268        }
269
270        if( [downloadChoice isEqualToString: @"Constant"] )
271        {
272            tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
273                                 [downloadFolder UTF8String] );
274            tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
275            continue;
276        }
277
278        if( [downloadChoice isEqualToString: @"Torrent"] )
279        {
280            tr_torrentSetFolder( fHandle, tr_torrentCount( fHandle ) - 1,
281                [[torrentPath stringByDeletingLastPathComponent] UTF8String] );
282            tr_torrentStart( fHandle, tr_torrentCount( fHandle ) - 1 );
283            continue;
284        }
285
286        NSOpenPanel * panel;
287        NSString    * message;
288
289        panel   = [NSOpenPanel openPanel];
290        message = [NSString stringWithFormat:
291            @"Select the download folder for %@",
292            [torrentPath lastPathComponent]];
293       
294        [panel setPrompt:                  @"Select"];
295        [panel setMessage:                   message];
296        [panel setAllowsMultipleSelection:        NO];
297        [panel setCanChooseFiles:                 NO];
298        [panel setCanChooseDirectories:          YES];
299
300        [panel beginSheetForDirectory: NULL file: NULL types: NULL
301            modalForWindow: fWindow modalDelegate: self didEndSelector:
302            @selector( folderChoiceClosed:returnCode:contextInfo: )
303            contextInfo: NULL];
304        [NSApp runModalForWindow: panel];
305    }
306
307    [self updateUI: NULL];
308}
309
310- (void) advancedChanged: (id) sender
311{
312    NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
313    if( [fAdvancedBarItem state] == NSOnState )
314    {
315        [fAdvancedBarItem setState: NSOffState];
316        [defaults setObject:@"NO" forKey:@"UseAdvancedBar"];
317    }
318    else
319    {
320        [fAdvancedBarItem setState: NSOnState];
321        [defaults setObject:@"YES" forKey:@"UseAdvancedBar"];
322    }
323    [fTableView display];
324}
325
326/* called on by applescript */
327- (void) open: (NSArray *) files
328{
329    fFilenames = [files retain];
330    [self performSelectorOnMainThread: @selector(cantFindAName:)
331                withObject: NULL waitUntilDone: NO];
332}
333
334- (void) openShowSheet: (id) sender
335{
336    NSOpenPanel * panel;
337    NSArray     * fileTypes;
338
339    panel     = [NSOpenPanel openPanel];
340    fileTypes = [NSArray arrayWithObject: @"torrent"];
341   
342    [panel setAllowsMultipleSelection: YES];
343    [panel setCanChooseFiles:          YES];
344    [panel setCanChooseDirectories:    NO];
345
346    [panel beginSheetForDirectory: NULL file: NULL types: fileTypes
347        modalForWindow: fWindow modalDelegate: self didEndSelector:
348        @selector( openSheetClosed:returnCode:contextInfo: )
349        contextInfo: NULL];
350}
351
352- (void) cantFindAName: (id) sender
353{
354    [self application: NSApp openFiles: fFilenames];
355    [fFilenames release];
356}
357
358- (void) openSheetClosed: (NSOpenPanel *) s returnCode: (int) code
359    contextInfo: (void *) info
360{
361    if( code != NSOKButton )
362    {
363        return;
364    }
365
366    fFilenames = [[s filenames] retain];
367
368    [self performSelectorOnMainThread: @selector(cantFindAName:)
369                withObject: NULL waitUntilDone: NO];
370}
371
372- (void) resumeTorrent: (id) sender
373{
374    [self resumeTorrentWithIndex: [fTableView selectedRow]];
375}
376
377- (void) resumeAllTorrents: (id) sender
378{
379    int i;
380    for ( i = 0; i < fCount; i++)
381    {
382        if ( fStat[i].status & ( TR_STATUS_STOPPING
383        | TR_STATUS_PAUSE | TR_STATUS_STOPPED ) )
384        {
385            [self resumeTorrentWithIndex: i];
386        }
387    }
388}
389
390- (void) resumeTorrentWithIndex: (int) idx
391{
392    tr_torrentStart( fHandle, idx );
393    [self updateUI: NULL];
394}
395
396- (void) stopTorrent: (id) sender
397{
398    [self stopTorrentWithIndex: [fTableView selectedRow]];
399}
400
401- (void) stopAllTorrents: (id) sender
402{
403    int i;
404    for ( i = 0; i < fCount; i++)
405    {
406        if ( fStat[i].status & ( TR_STATUS_CHECK
407        | TR_STATUS_DOWNLOAD | TR_STATUS_SEED) )
408        {
409            [self stopTorrentWithIndex: i];
410        }
411    }
412}
413
414- (void) stopTorrentWithIndex: (int) idx
415{
416    tr_torrentStop( fHandle, idx );
417    [self updateUI: NULL];
418}
419
420
421- (void) removeTorrentWithIndex: (int) idx
422                  deleteTorrent: (BOOL) deleteTorrent
423                     deleteData: (BOOL) deleteData
424{
425    if( deleteData )
426    {
427        [self finderTrash: [NSString stringWithFormat: @"%@/%@",
428            [NSString stringWithUTF8String: fStat[idx].folder],
429            [NSString stringWithUTF8String: fStat[idx].info.name]]];
430    }
431    if( deleteTorrent )
432    {
433        [self finderTrash: [NSString stringWithUTF8String:
434            fStat[idx].info.torrent]];
435    }
436   
437    tr_torrentClose( fHandle, idx );
438    [self updateUI: NULL];
439}
440
441- (void) removeTorrent: (id) sender
442{
443    [self removeTorrentWithIndex: [fTableView selectedRow] deleteTorrent: NO deleteData: NO ];
444}
445
446- (void) removeTorrentDeleteFile: (id) sender
447{
448    [self removeTorrentWithIndex: [fTableView selectedRow] deleteTorrent: YES deleteData: NO];
449}
450
451- (void) removeTorrentDeleteData: (id) sender
452{
453    [self removeTorrentWithIndex: [fTableView selectedRow] deleteTorrent: NO deleteData: YES];
454}
455
456- (void) removeTorrentDeleteBoth: (id) sender
457{
458    [self removeTorrentWithIndex: [fTableView selectedRow] deleteTorrent: YES deleteData: YES];
459}
460
461- (void) showInfo: (id) sender
462{
463    if( [fInfoPanel isVisible] )
464    {
465        [fInfoPanel close];
466    }
467    else
468    {
469        [fInfoPanel orderFront: sender];
470    }
471}
472
473- (void) updateUI: (NSTimer *) t
474{
475    float dl, ul;
476    int row, i;
477
478    /* Update the NSTableView */
479    if( fStat )
480    {
481        free( fStat );
482    }
483    fCount = tr_torrentStat( fHandle, &fStat );
484    [fTableView updateUI: fStat];
485
486    /* Update the global DL/UL rates */
487    tr_torrentRates( fHandle, &dl, &ul );
488    [fTotalDLField setStringValue: [NSString stringWithFormat:
489        @"Total DL: %.2f KB/s", dl]];
490    [fTotalULField setStringValue: [NSString stringWithFormat:
491        @"Total UL: %.2f KB/s", ul]];
492
493    /* Update DL/UL totals in the Info panel */
494    row = [fTableView selectedRow];
495    if( row > -1 )
496    {
497        [fInfoDownloaded setStringValue:
498            stringForFileSize( fStat[row].downloaded )];
499        [fInfoUploaded setStringValue:
500            stringForFileSize( fStat[row].uploaded )];
501    }
502   
503    /* check if torrents have recently ended. */
504    for (i = 0; i < fCount; i++)
505    {
506        if( !tr_getFinished( fHandle, i ) )
507        {
508            continue;
509        }
510        [self notifyGrowl: [NSString stringWithUTF8String:
511            fStat[i].info.name]];
512        tr_setFinished( fHandle, i, 0 );
513    }
514}
515
516
517- (NSMenu *) menuForIndex: (int) idx
518{
519    if ( idx < 0 || idx >= fCount )
520    {
521        return nil;
522    }
523   
524    int status = fStat[idx].status;
525   
526    NSMenuItem *pauseItem =  [fContextMenu itemWithTag: CONTEXT_PAUSE];
527    NSMenuItem *removeItem = [fContextMenu itemAtIndex: 1];
528    NSMenuItem *infoItem =   [fContextMenu itemAtIndex: 2];
529   
530    [pauseItem setTarget: self];
531   
532    if ( status & TR_STATUS_CHECK ||
533         status & TR_STATUS_DOWNLOAD ||
534         status & TR_STATUS_SEED )
535    {
536        /* we can stop */
537        [removeItem setEnabled: NO];
538        [pauseItem setTitle: @"Pause"];
539        [pauseItem setAction: @selector(stopTorrent:)];
540        [pauseItem setEnabled: YES];
541    } else {
542        /* we are stopped */
543        [removeItem setEnabled: YES];
544        [pauseItem setTitle: @"Resume"];
545        [pauseItem setAction: @selector(resumeTorrent:)];
546        /* don't allow resuming if we aren't in PAUSE */
547        if ( !(status & TR_STATUS_PAUSE) )
548            [pauseItem setEnabled: NO];
549    }
550   
551    if( [fInfoPanel isVisible] )
552    {
553        [infoItem setTitle: @"Hide Info"];
554    } else {
555        [infoItem setTitle: @"Show Info"];
556    }
557   
558    return fContextMenu;
559}
560
561- (int) numberOfRowsInTableView: (NSTableView *) t
562{
563    return fCount;
564}
565
566- (id) tableView: (NSTableView *) t objectValueForTableColumn:
567    (NSTableColumn *) tableColumn row: (int) rowIndex
568{
569    return NULL;
570}
571
572- (void) tableView: (NSTableView *) t willDisplayCell: (id) cell
573    forTableColumn: (NSTableColumn *) tableColumn row: (int) rowIndex
574{
575    BOOL w;
576
577    w = [fWindow isKeyWindow] && rowIndex == [fTableView selectedRow];
578    if( [[tableColumn identifier] isEqualToString: @"Name"] )
579    {
580        [(NameCell *) cell setStat: &fStat[rowIndex] whiteText: w];
581    }
582    else if( [[tableColumn identifier] isEqualToString: @"Progress"] )
583    {
584        [(ProgressCell *) cell setStat: &fStat[rowIndex] whiteText: w];
585    }
586}
587
588- (BOOL) tableView: (NSTableView *) t acceptDrop:
589    (id <NSDraggingInfo>) info row: (int) row dropOperation:
590    (NSTableViewDropOperation) operation
591{
592    NSPasteboard * pasteboard;
593
594    pasteboard = [info draggingPasteboard];
595    if( ![[pasteboard types] containsObject: NSFilenamesPboardType] )
596    {
597        return NO;
598    }
599
600    [self application: NSApp openFiles:
601        [pasteboard propertyListForType: NSFilenamesPboardType]];
602
603    return YES;
604}
605
606- (NSDragOperation) tableView: (NSTableView *) t validateDrop:
607    (id <NSDraggingInfo>) info proposedRow: (int) row
608    proposedDropOperation: (NSTableViewDropOperation) operation
609{
610    return NSDragOperationGeneric;
611}
612
613- (void) tableViewSelectionDidChange: (NSNotification *) n
614{
615    int row = [fTableView selectedRow];
616
617    if( row < 0 )
618    {
619        [fInfoTitle      setStringValue: @"No torrent selected"];
620        [fInfoTracker    setStringValue: @""];
621        [fInfoAnnounce   setStringValue: @""];
622        [fInfoSize       setStringValue: @""];
623        [fInfoPieces     setStringValue: @""];
624        [fInfoPieceSize  setStringValue: @""];
625        [fInfoFolder     setStringValue: @""];
626        [fInfoDownloaded setStringValue: @""];
627        [fInfoUploaded   setStringValue: @""];
628        [fInfoSeeders    setStringValue: @""];
629        [fInfoLeechers   setStringValue: @""];
630        return;
631    }
632
633    /* Update info window */
634    [fInfoTitle setStringValue: [NSString stringWithUTF8String:
635        fStat[row].info.name]];
636    [fInfoTracker setStringValue: [NSString stringWithFormat:
637        @"%s:%d", fStat[row].info.trackerAddress, fStat[row].info.trackerPort]];
638    [fInfoAnnounce setStringValue: [NSString stringWithCString:
639        fStat[row].info.trackerAnnounce]];
640    [fInfoSize setStringValue:
641        stringForFileSize( fStat[row].info.totalSize )];
642    [fInfoPieces setStringValue: [NSString stringWithFormat: @"%d",
643        fStat[row].info.pieceCount]];
644    [fInfoPieceSize setStringValue:
645        stringForFileSize( fStat[row].info.pieceSize )];
646    [fInfoFolder setStringValue: [[NSString stringWithUTF8String:
647        tr_torrentGetFolder( fHandle, row )] lastPathComponent]];
648       
649    if ( fStat[row].seeders == -1 ) {
650        [fInfoSeeders setStringValue: [NSString stringWithUTF8String: "?"]];
651    } else {
652        [fInfoSeeders setStringValue: [NSString stringWithFormat: @"%d",
653            fStat[row].seeders]];
654    }
655    if ( fStat[row].leechers == -1 ) {
656        [fInfoLeechers setStringValue: [NSString stringWithUTF8String: "?"]];
657    } else {
658        [fInfoLeechers setStringValue: [NSString stringWithFormat: @"%d",
659            fStat[row].leechers]];
660    }
661}
662
663- (NSToolbarItem *) toolbar: (NSToolbar *) t itemForItemIdentifier:
664    (NSString *) ident willBeInsertedIntoToolbar: (BOOL) flag
665{
666    NSToolbarItem * item;
667    item = [[NSToolbarItem alloc] initWithItemIdentifier: ident];
668
669    if( [ident isEqualToString: TOOLBAR_OPEN] )
670    {
671        [item setLabel: @"Open"];
672        [item setPaletteLabel: [item label]];
673        [item setToolTip: @"Open a torrent"];
674        [item setImage: [NSImage imageNamed: @"Open.png"]];
675        [item setTarget: self];
676        [item setAction: @selector( openShowSheet: )];
677    }
678    else if( [ident isEqualToString: TOOLBAR_REMOVE] )
679    {
680        [item setLabel: @"Remove"];
681        [item setPaletteLabel: [item label]];
682        [item setToolTip: @"Remove torrent from list"];
683        [item setImage: [NSImage imageNamed: @"Remove.png"]];
684        [item setTarget: self];
685        [item setAction: @selector( removeTorrent: )];
686    }
687    else if( [ident isEqualToString: TOOLBAR_PREFS] )
688    {
689        [item setLabel: @"Preferences"];
690        [item setPaletteLabel: [item label]];
691        [item setToolTip: @"Show the Preferences panel"];
692        [item setImage: [NSImage imageNamed: @"Preferences.png"]];
693        [item setTarget: fPrefsController];
694        [item setAction: @selector( show: )];
695    }
696    else if( [ident isEqualToString: TOOLBAR_INFO] )
697    {
698        [item setLabel: @"Info"];
699        [item setPaletteLabel: [item label]];
700        [item setToolTip: @"Information"];
701        [item setImage: [NSImage imageNamed: @"Info.png"]];
702        [item setTarget: self];
703        [item setAction: @selector( showInfo: )];
704    }
705    else if( [ident isEqualToString: TOOLBAR_RESUME_ALL] )
706    {
707        [item setLabel: @"Resume All"];
708        [item setPaletteLabel: [item label]];
709        [item setToolTip: @"Resume all torrents"];
710        [item setImage: [NSImage imageNamed: @"Resume.png"]];
711        [item setTarget: self];
712        [item setAction: @selector( resumeAllTorrents: )];
713    }
714    else if( [ident isEqualToString: TOOLBAR_PAUSE_ALL] )
715    {
716        [item setLabel: @"Pause All"];
717        [item setPaletteLabel: [item label]];
718        [item setToolTip: @"Pause all torrents"];
719        [item setImage: [NSImage imageNamed: @"Stop.png"]];
720        [item setTarget: self];
721        [item setAction: @selector( stopAllTorrents: )];
722    }
723    else
724    {
725        [item release];
726        return NULL;
727    }
728
729    return item;
730}
731
732- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) t
733{
734    return [NSArray arrayWithObjects:
735            TOOLBAR_OPEN, TOOLBAR_REMOVE,
736            /* TOOLBAR_RESUME_ALL, TOOLBAR_PAUSE_ALL, */
737            TOOLBAR_PREFS, TOOLBAR_INFO,
738            NSToolbarSeparatorItemIdentifier,
739            NSToolbarSpaceItemIdentifier,
740            NSToolbarFlexibleSpaceItemIdentifier,
741            NSToolbarCustomizeToolbarItemIdentifier,
742            NULL];
743}
744
745- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) t
746{
747    return [NSArray arrayWithObjects:
748            TOOLBAR_OPEN, TOOLBAR_REMOVE,
749            /* NSToolbarSeparatorItemIdentifier,
750            TOOLBAR_RESUME_ALL, TOOLBAR_PAUSE_ALL, */
751            NSToolbarFlexibleSpaceItemIdentifier,
752            TOOLBAR_PREFS, TOOLBAR_INFO,
753            NULL];
754}
755
756- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem
757{
758    //check remove item
759    if ([toolbarItem action] == @selector(removeTorrent:))
760    {
761        int row = [fTableView selectedRow];
762        return ( row >= 0 ) && ( fStat[row].status &
763                ( TR_STATUS_STOPPING | TR_STATUS_PAUSE ) );
764    }
765   
766    return true;
767}
768
769- (void) runCustomizationPalette: (id) sender
770{
771    [fToolbar runCustomizationPalette:sender];
772}
773
774- (void) showHideToolbar: (id) sender
775{
776    [fWindow toggleToolbarShown:sender];
777}
778
779- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
780{
781    //enable customize toolbar item
782    if ([menuItem action] == @selector(showHideToolbar:))
783        [menuItem setTitle: [fToolbar isVisible] ? @"Hide Toolbar" : @"Show Toolbar"];
784       
785    if ([fToolbar customizationPaletteIsRunning])
786        return false;
787       
788    //enable show info
789    if ([menuItem action] == @selector(showInfo:))
790    {
791        [menuItem setTitle: [fInfoPanel isVisible] ? @"Hide Info" : @"Show Info"];
792        return true;
793    }
794       
795    int row = [fTableView selectedRow];
796       
797    //enable remove items
798    if ([menuItem action] == @selector(removeTorrent:) || [menuItem action] == @selector(removeTorrentDeleteFile:)
799        || [menuItem action] == @selector(removeTorrentDeleteData:) || [menuItem action] == @selector(removeTorrentDeleteBoth:)) {
800        /* Can we remove it ? */
801        return ( row >= 0 ) && ( fStat[row].status &
802                ( TR_STATUS_STOPPING | TR_STATUS_PAUSE ) );
803    }
804   
805    //enable reveal in finder item
806    if ([menuItem action] == @selector(revealFromMenu:))
807        return row >= 0;
808       
809    //enable and change pause and remove item
810    if ([menuItem action] == @selector(resumeTorrent:) || [menuItem action] == @selector(stopTorrent:))
811    {
812        if (row >= 0 && fStat[row].status & TR_STATUS_PAUSE)
813        {
814            [menuItem setTitle: @"Resume"];
815            [menuItem setAction: @selector( resumeTorrent: )];
816        }
817        else
818        {
819            [menuItem setTitle: @"Pause"];
820            [menuItem setAction: @selector( stopTorrent: )];
821        }
822        return row >= 0;
823    }
824   
825    return true;
826}
827
828- (void) sleepCallBack: (natural_t) messageType argument:
829                          (void *) messageArgument
830{
831    int i;
832
833    switch( messageType )
834    {
835        case kIOMessageSystemWillSleep:
836            /* Close all connections before going to sleep and remember
837               we should resume when we wake up */
838            for( i = 0; i < fCount; i++ )
839            {
840                if( fStat[i].status & ( TR_STATUS_CHECK |
841                        TR_STATUS_DOWNLOAD | TR_STATUS_SEED ) )
842                {
843                    tr_torrentStop( fHandle, i );
844                    fResumeOnWake[i] = 1;
845                }
846                else
847                {
848                    fResumeOnWake[i] = 0;
849                }
850            }
851
852            /* TODO: wait a few seconds to let the torrents
853               stop properly */
854           
855            IOAllowPowerChange( fRootPort, (long) messageArgument );
856            break;
857
858        case kIOMessageCanSystemSleep:
859            /* Do not prevent idle sleep */
860            /* TODO: prevent it unless there are all paused? */
861            IOAllowPowerChange( fRootPort, (long) messageArgument );
862            break;
863
864        case kIOMessageSystemHasPoweredOn:
865            /* Resume download after we wake up */
866            for( i = 0; i < fCount; i++ )
867            {
868                if( fResumeOnWake[i] )
869                {
870                    tr_torrentStart( fHandle, i );
871                }
872            }
873            break;
874    }
875}
876
877- (NSRect) windowWillUseStandardFrame: (NSWindow *) w
878    defaultFrame: (NSRect) defaultFrame
879{
880    NSRect rectWin, rectView;
881    float  foo;
882
883    rectWin  = [fWindow frame];
884    rectView = [[fWindow contentView] frame];
885    foo      = 47.0 + MAX( 1, tr_torrentCount( fHandle ) ) * 62.0 -
886                  rectView.size.height;
887
888    rectWin.size.height += foo;
889    rectWin.origin.y    -= foo;
890
891    return rectWin;
892}
893
894- (void) showMainWindow: (id) sender
895{
896    [fWindow makeKeyAndOrderFront: NULL];
897}
898
899- (void) linkHomepage: (id) sender
900{
901    [[NSWorkspace sharedWorkspace] openURL: [NSURL
902        URLWithString:@"http://transmission.m0k.org/"]];
903}
904
905- (void) linkForums: (id) sender
906{
907    [[NSWorkspace sharedWorkspace] openURL: [NSURL
908        URLWithString:@"http://transmission.m0k.org/forum/"]];
909}
910
911- (BOOL) hasGrowl
912{
913    NSFileManager * manager = [NSFileManager defaultManager];
914    NSString      * helper  = @"/Library/PreferencePanes/Growl.prefPane/"
915                               "Contents/Resources/GrowlHelperApp.app";
916
917    if( [manager fileExistsAtPath: helper] )
918    {
919        /* Growl was installed for all users */
920        return YES;
921    }
922    if( [manager fileExistsAtPath: [[NSString stringWithFormat: @"~%@",
923            helper] stringByExpandingTildeInPath]] )
924    {
925        /* Growl was installed for this user only */
926        return YES;
927    }
928
929    return NO;
930}
931
932- (void) notifyGrowl: (NSString * ) file
933{
934    NSString * growlScript;
935    NSAppleScript * appleScript;
936    NSDictionary * error;
937
938    if( ![self hasGrowl] )
939    {
940        return;
941    }
942   
943    growlScript = [NSString stringWithFormat:
944        @"tell application \"System Events\"\n"
945         "  if exists application process \"GrowlHelperApp\" then\n"
946         "    tell application \"GrowlHelperApp\"\n "
947         "      notify with name \"Download Complete\""
948         "        title \"Download Complete\""
949         "        description \"%@\""
950         "        application name \"Transmission\"\n"
951         "    end tell\n"
952         "  end if\n"
953         "end tell", file];
954    appleScript = [[NSAppleScript alloc] initWithSource: growlScript];
955    if( ![appleScript executeAndReturnError: &error] )
956    {
957        printf( "Growl notify failed\n" );
958    }
959    [appleScript release];
960}
961
962- (void) growlRegister: (id) sender
963{   
964    NSString * growlScript;
965    NSAppleScript * appleScript;
966    NSDictionary * error;
967
968    if( ![self hasGrowl] )
969    {
970        return;
971    }
972   
973    growlScript = [NSString stringWithFormat:
974        @"tell application \"System Events\"\n"
975         "  if exists application process \"GrowlHelperApp\" then\n"
976         "    tell application \"GrowlHelperApp\"\n"
977         "      register as application \"Transmission\" "
978         "        all notifications {\"Download Complete\"}"
979         "        default notifications {\"Download Complete\"}"
980         "        icon of application \"Transmission\"\n"
981         "    end tell\n"
982         "  end if\n"
983         "end tell"];
984    appleScript = [[NSAppleScript alloc] initWithSource: growlScript];
985    if( ![appleScript executeAndReturnError: &error] )
986    {
987        printf( "Growl registration failed\n" );
988    }
989    [appleScript release];
990}
991
992
993- (void) revealFromMenu: (id) sender
994{
995    int row;
996
997    row = [fTableView selectedRow];
998    if( row < 0 )
999    {
1000        return;
1001    }
1002    [self finderReveal: [NSString stringWithFormat: @"%@/%@",
1003        [NSString stringWithUTF8String: fStat[row].folder],
1004        [NSString stringWithUTF8String: fStat[row].info.name]]];
1005}
1006
1007- (void) finderReveal: (NSString *) path
1008{
1009    NSString * string;
1010    NSAppleScript * appleScript;
1011    NSDictionary * error;
1012   
1013    string = [NSString stringWithFormat:
1014        @"tell application \"Finder\"\n"
1015         "  activate\n"
1016         "  reveal (POSIX file \"%@\")\n"
1017         "end tell", path];
1018
1019    appleScript = [[NSAppleScript alloc] initWithSource: string];
1020    if( ![appleScript executeAndReturnError: &error] )
1021    {
1022        printf( "finderReveal failed\n" );
1023    }
1024    [appleScript release];
1025}
1026
1027- (void) finderTrash: (NSString *) path
1028{
1029    NSString * string;
1030    NSAppleScript * appleScript;
1031    NSDictionary * error;
1032
1033    string = [NSString stringWithFormat:
1034        @"tell application \"Finder\"\n"
1035         "  move (POSIX file \"%@\") to trash\n"
1036         "end tell", path];
1037
1038    appleScript = [[NSAppleScript alloc] initWithSource: string];
1039    if( ![appleScript executeAndReturnError: &error] )
1040    {
1041        printf( "finderTrash failed\n" );
1042    }
1043    [appleScript release];
1044}
1045
1046@end
Note: See TracBrowser for help on using the repository browser.