source: trunk/macosx/Controller.m @ 19

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

Update 2005-12-21

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