source: trunk/macosx/Controller.m @ 15

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

Update 2005-12-14

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