source: trunk/macosx/InfoWindowController.m

Last change on this file was 14685, checked in by mikedld, 6 years ago

Use autolayout for torrent info window (OS X)

  • Property svn:keywords set to Date Rev Author Id
File size: 20.3 KB
Line 
1/******************************************************************************
2 * $Id: InfoWindowController.m 14685 2016-02-27 20:29:57Z mikedld $
3 *
4 * Copyright (c) 2006-2012 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#import "InfoWindowController.h"
26#import "InfoViewController.h"
27#import "InfoGeneralViewController.h"
28#import "InfoActivityViewController.h"
29#import "InfoTrackersViewController.h"
30#import "InfoPeersViewController.h"
31#import "InfoFileViewController.h"
32#import "InfoOptionsViewController.h"
33#import "InfoTabButtonCell.h"
34#import "NSApplicationAdditions.h"
35#import "NSStringAdditions.h"
36#import "Torrent.h"
37
38#define TAB_INFO_IDENT @"Info"
39#define TAB_ACTIVITY_IDENT @"Activity"
40#define TAB_TRACKER_IDENT @"Tracker"
41#define TAB_PEERS_IDENT @"Peers"
42#define TAB_FILES_IDENT @"Files"
43#define TAB_OPTIONS_IDENT @"Options"
44
45#define TAB_MIN_HEIGHT 250
46
47#define INVALID -99
48
49typedef enum
50{
51    TAB_GENERAL_TAG = 0,
52    TAB_ACTIVITY_TAG = 1,
53    TAB_TRACKERS_TAG = 2,
54    TAB_PEERS_TAG = 3,
55    TAB_FILE_TAG = 4,
56    TAB_OPTIONS_TAG = 5
57} tabTag;
58
59@interface InfoWindowController (Private)
60
61- (void) resetInfo;
62- (void) resetInfoForTorrent: (NSNotification *) notification;
63
64@end
65
66@implementation InfoWindowController
67
68- (id) init
69{
70    self = [super initWithWindowNibName: @"InfoWindow"];
71    return self;
72}
73
74- (void) awakeFromNib
75{
76    [fNoneSelectedField setStringValue: NSLocalizedString(@"No Torrents Selected", "Inspector -> selected torrents")];
77   
78    //window location and size
79    NSPanel * window = (NSPanel *)[self window];
80   
81    [window setFloatingPanel: NO];
82   
83    const CGFloat windowHeight = NSHeight([window frame]);
84    fMinWindowWidth = [window minSize].width;
85   
86    [window setFrameAutosaveName: @"InspectorWindow"];
87    [window setFrameUsingName: @"InspectorWindow"];
88   
89    NSRect windowRect = [window frame];
90    windowRect.origin.y -= windowHeight - NSHeight(windowRect);
91    windowRect.size.height = windowHeight;
92    [window setFrame: windowRect display: NO];
93   
94    [window setBecomesKeyOnlyIfNeeded: YES];
95   
96    //set tab tooltips
97    [fTabMatrix setToolTip: NSLocalizedString(@"General Info", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_GENERAL_TAG]];
98    [fTabMatrix setToolTip: NSLocalizedString(@"Activity", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_ACTIVITY_TAG]];
99    [fTabMatrix setToolTip: NSLocalizedString(@"Trackers", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_TRACKERS_TAG]];
100    [fTabMatrix setToolTip: NSLocalizedString(@"Peers", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_PEERS_TAG]];
101    [fTabMatrix setToolTip: NSLocalizedString(@"Files", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_FILE_TAG]];
102    [fTabMatrix setToolTip: NSLocalizedString(@"Options", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_OPTIONS_TAG]];
103   
104    //set selected tab
105    fCurrentTabTag = INVALID;
106    NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InspectorSelected"];
107    NSInteger tag;
108    if ([identifier isEqualToString: TAB_INFO_IDENT])
109        tag = TAB_GENERAL_TAG;
110    else if ([identifier isEqualToString: TAB_ACTIVITY_IDENT])
111        tag = TAB_ACTIVITY_TAG;
112    else if ([identifier isEqualToString: TAB_TRACKER_IDENT])
113        tag = TAB_TRACKERS_TAG;
114    else if ([identifier isEqualToString: TAB_PEERS_IDENT])
115        tag = TAB_PEERS_TAG;
116    else if ([identifier isEqualToString: TAB_FILES_IDENT])
117        tag = TAB_FILE_TAG;
118    else if ([identifier isEqualToString: TAB_OPTIONS_IDENT])
119        tag = TAB_OPTIONS_TAG;
120    else //safety
121    {
122        [[NSUserDefaults standardUserDefaults] setObject: TAB_INFO_IDENT forKey: @"InspectorSelected"];
123        tag = TAB_GENERAL_TAG;
124    }
125    [fTabMatrix selectCellWithTag: tag];
126    [self setTab: nil];
127   
128    //set blank inspector
129    [self setInfoForTorrents: [NSArray array]];
130   
131    //allow for update notifications
132    NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];
133    [nc addObserver: self selector: @selector(resetInfoForTorrent:) name: @"ResetInspector" object: nil];
134    [nc addObserver: self selector: @selector(updateInfoStats) name: @"UpdateStats" object: nil];
135    [nc addObserver: self selector: @selector(updateOptions) name: @"UpdateOptions" object: nil];
136}
137
138- (void) dealloc
139{
140    [[NSNotificationCenter defaultCenter] removeObserver: self];
141   
142    if ([fViewController respondsToSelector: @selector(saveViewSize)])
143        [fViewController saveViewSize];
144   
145    [fGeneralViewController release];
146    [fActivityViewController release];
147    [fTrackersViewController release];
148    [fPeersViewController release];
149    [fFileViewController release];
150    [fOptionsViewController release];
151   
152    [fTorrents release];
153   
154    [super dealloc];
155}
156
157- (void) setInfoForTorrents: (NSArray *) torrents
158{
159    if (fTorrents && [fTorrents isEqualToArray: torrents])
160        return;
161   
162    [fTorrents release];
163    fTorrents = [torrents retain];
164   
165    [self resetInfo];
166}
167
168- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame
169{
170    NSRect windowRect = [window frame];
171    windowRect.size.width = [window minSize].width;
172    return windowRect;
173}
174
175- (void) windowWillClose: (NSNotification *) notification
176{
177    if (fCurrentTabTag == TAB_FILE_TAG && ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]))
178        [[QLPreviewPanel sharedPreviewPanel] reloadData];
179}
180
181- (void) setTab: (id) sender
182{
183    const NSInteger oldTabTag = fCurrentTabTag;
184    fCurrentTabTag = [fTabMatrix selectedTag];
185    if (fCurrentTabTag == oldTabTag)
186        return;
187   
188    //take care of old view
189    CGFloat oldHeight = 0;
190    if (oldTabTag != INVALID)
191    {
192        //deselect old tab item
193        [(InfoTabButtonCell *)[fTabMatrix cellWithTag: oldTabTag] setSelectedTab: NO];
194       
195        if ([fViewController respondsToSelector: @selector(saveViewSize)])
196            [fViewController saveViewSize];
197       
198        if ([fViewController respondsToSelector: @selector(clearView)])
199            [fViewController clearView];
200       
201        NSView * oldView = [fViewController view];
202        oldHeight = NSHeight([oldView frame]);
203       
204        //remove old view
205        [oldView removeFromSuperview];
206    }
207   
208    //set new tab item
209    NSString * identifier;
210    switch (fCurrentTabTag)
211    {
212        case TAB_GENERAL_TAG:
213            if (!fGeneralViewController)
214            {
215                fGeneralViewController = [[InfoGeneralViewController alloc] init];
216                [fGeneralViewController setInfoForTorrents: fTorrents];
217            }
218           
219            fViewController = fGeneralViewController;
220            identifier = TAB_INFO_IDENT;
221            break;
222        case TAB_ACTIVITY_TAG:
223            if (!fActivityViewController)
224            {
225                fActivityViewController = [[InfoActivityViewController alloc] init];
226                [fActivityViewController setInfoForTorrents: fTorrents];
227            }
228           
229            fViewController = fActivityViewController;
230            identifier = TAB_ACTIVITY_IDENT;
231            break;
232        case TAB_TRACKERS_TAG:
233            if (!fTrackersViewController)
234            {
235                fTrackersViewController = [[InfoTrackersViewController alloc] init];
236                [fTrackersViewController setInfoForTorrents: fTorrents];
237            }
238           
239            fViewController = fTrackersViewController;
240            identifier = TAB_TRACKER_IDENT;
241            break;
242        case TAB_PEERS_TAG:
243            if (!fPeersViewController)
244            {
245                fPeersViewController = [[InfoPeersViewController alloc] init];
246                [fPeersViewController setInfoForTorrents: fTorrents];
247            }
248           
249            fViewController = fPeersViewController;
250            identifier = TAB_PEERS_IDENT;
251            break;
252        case TAB_FILE_TAG:
253            if (!fFileViewController)
254            {
255                fFileViewController = [[InfoFileViewController alloc] init];
256                [fFileViewController setInfoForTorrents: fTorrents];
257            }
258           
259            fViewController = fFileViewController;
260            identifier = TAB_FILES_IDENT;
261            break;
262        case TAB_OPTIONS_TAG:
263            if (!fOptionsViewController)
264            {
265                fOptionsViewController = [[InfoOptionsViewController alloc] init];
266                [fOptionsViewController setInfoForTorrents: fTorrents];
267            }
268           
269            fViewController = fOptionsViewController;
270            identifier = TAB_OPTIONS_IDENT;
271            break;
272        default:
273            NSAssert1(NO, @"Unknown info tab selected: %ld", fCurrentTabTag);
274            return;
275    }
276   
277    [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"];
278   
279    NSWindow * window = [self window];
280   
281    [window setTitle: [NSString stringWithFormat: @"%@ - %@", [fViewController title],
282                        NSLocalizedString(@"Torrent Inspector", "Inspector -> title")]];
283   
284    //selected tab item
285    [(InfoTabButtonCell *)[fTabMatrix selectedCell] setSelectedTab: YES];
286   
287    NSView * view = [fViewController view];
288   
289    [fViewController updateInfo];
290   
291    NSRect windowRect = [window frame], viewRect = [view frame];
292   
293    const CGFloat difference = NSHeight(viewRect) - oldHeight;
294    windowRect.origin.y -= difference;
295    windowRect.size.height += difference;
296   
297    const CGFloat minWindowWidth = MAX(fMinWindowWidth, [view fittingSize].width);
298    windowRect.size.width = MAX(NSWidth(windowRect), minWindowWidth);
299   
300    if ([fViewController respondsToSelector: @selector(saveViewSize)]) //a little bit hacky, but avoids requiring an extra method
301    {
302        if ([window screen])
303        {
304            const CGFloat screenHeight = NSHeight([[window screen] visibleFrame]);
305            if (NSHeight(windowRect) > screenHeight)
306            {
307                const CGFloat difference = screenHeight - NSHeight(windowRect);
308                windowRect.origin.y -= difference;
309                windowRect.size.height += difference;
310               
311                viewRect.size.height += difference;
312            }
313        }
314       
315        [window setMinSize: NSMakeSize(minWindowWidth, NSHeight(windowRect) - NSHeight(viewRect) + TAB_MIN_HEIGHT)];
316        [window setMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)];
317    }
318    else
319    {
320        [window setMinSize: NSMakeSize(minWindowWidth, NSHeight(windowRect))];
321        [window setMaxSize: NSMakeSize(FLT_MAX, NSHeight(windowRect))];
322    }
323   
324    viewRect.size.width = NSWidth(windowRect);
325    [view setFrame: viewRect];
326   
327    [window setFrame: windowRect display: YES animate: oldTabTag != INVALID];
328    [[window contentView] addSubview: view];
329   
330    [[window contentView] addConstraints: [NSLayoutConstraint constraintsWithVisualFormat: @"H:|-0-[view]-0-|"
331                                                                                  options: 0
332                                                                                  metrics: nil
333                                                                                    views: @{ @"view": view }]];
334    [[window contentView] addConstraints: [NSLayoutConstraint constraintsWithVisualFormat: @"V:[tabs]-0-[view]-0-|"
335                                                                                  options: 0
336                                                                                  metrics: nil
337                                                                                    views: @{ @"tabs": fTabMatrix, @"view": view }]];
338   
339    if ((fCurrentTabTag == TAB_FILE_TAG || oldTabTag == TAB_FILE_TAG)
340        && ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]))
341        [[QLPreviewPanel sharedPreviewPanel] reloadData];
342}
343
344- (void) setNextTab
345{
346    NSInteger tag = [fTabMatrix selectedTag]+1;
347    if (tag >= [fTabMatrix numberOfColumns])
348        tag = 0;
349   
350    [fTabMatrix selectCellWithTag: tag];
351    [self setTab: nil];
352}
353
354- (void) setPreviousTab
355{
356    NSInteger tag = [fTabMatrix selectedTag]-1;
357    if (tag < 0)
358        tag = [fTabMatrix numberOfColumns]-1;
359   
360    [fTabMatrix selectCellWithTag: tag];
361    [self setTab: nil];
362}
363
364- (void) swipeWithEvent: (NSEvent *) event
365{
366    if ([event deltaX] < 0.0)
367        [self setNextTab];
368    else if ([event deltaX] > 0.0)
369        [self setPreviousTab];
370}
371
372- (void) updateInfoStats
373{
374    [fViewController updateInfo];
375}
376
377- (void) updateOptions
378{
379    [fOptionsViewController updateOptions];
380}
381
382- (NSArray *) quickLookURLs
383{
384    return [fFileViewController quickLookURLs];
385}
386
387- (BOOL) canQuickLook
388{
389    if (fCurrentTabTag != TAB_FILE_TAG || ![[self window] isVisible])
390        return NO;
391   
392    return [fFileViewController canQuickLook];
393}
394
395- (NSRect) quickLookSourceFrameForPreviewItem: (id <QLPreviewItem>) item
396{
397    return [fFileViewController quickLookSourceFrameForPreviewItem: item];
398}
399
400@end
401
402@implementation InfoWindowController (Private)
403
404- (void) resetInfo
405{
406    const NSUInteger numberSelected = [fTorrents count];
407    if (numberSelected != 1)
408    {
409        if (numberSelected > 0)
410        {
411            [fImageView setImage: [NSImage imageNamed: NSImageNameMultipleDocuments]];
412           
413            [fNameField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ Torrents Selected",
414                                            "Inspector -> selected torrents"),
415                                            [NSString formattedUInteger: numberSelected]]];
416            [fNameField setHidden: NO];
417       
418            uint64_t size = 0;
419            NSUInteger fileCount = 0, magnetCount = 0;
420            for (Torrent * torrent in fTorrents)
421            {
422                size += [torrent size];
423                fileCount += [torrent fileCount];
424                if ([torrent isMagnet])
425                    ++magnetCount;
426            }
427           
428            NSMutableArray * fileStrings = [NSMutableArray arrayWithCapacity: 2];
429            if (fileCount > 0)
430            {
431                NSString * fileString;
432                if (fileCount == 1)
433                    fileString = NSLocalizedString(@"1 file", "Inspector -> selected torrents");
434                else
435                    fileString = [NSString stringWithFormat: NSLocalizedString(@"%@ files", "Inspector -> selected torrents"),
436                                    [NSString formattedUInteger: fileCount]];
437                [fileStrings addObject: fileString];
438            }
439            if (magnetCount > 0)
440            {
441                NSString * magnetString;
442                if (magnetCount == 1)
443                    magnetString = NSLocalizedString(@"1 magnetized transfer", "Inspector -> selected torrents");
444                else
445                    magnetString = [NSString stringWithFormat: NSLocalizedString(@"%@ magnetized transfers",
446                                    "Inspector -> selected torrents"), [NSString formattedUInteger: magnetCount]];
447                [fileStrings addObject: magnetString];
448            }
449           
450            NSString * fileString = [fileStrings componentsJoinedByString: @" + "];
451           
452            if (magnetCount < numberSelected)
453            {
454                [fBasicInfoField setStringValue: [NSString stringWithFormat: @"%@, %@", fileString,
455                    [NSString stringWithFormat: NSLocalizedString(@"%@ total", "Inspector -> selected torrents"),
456                        [NSString stringForFileSize: size]]]];
457               
458                NSString * byteString;
459                if ([NSApp isOnMountainLionOrBetter])
460                {
461                    NSByteCountFormatter * formatter = [[NSByteCountFormatterMtLion alloc] init];
462                    [formatter setAllowedUnits: NSByteCountFormatterUseBytes];
463                    byteString = [formatter stringFromByteCount: size];
464                    [formatter release];
465                }
466                else
467                    byteString = [NSString stringWithFormat: NSLocalizedString(@"%@ bytes", "Inspector -> selected torrents"), [NSString formattedUInteger: size]];
468                [fBasicInfoField setToolTip: byteString];
469            }
470            else
471            {
472                [fBasicInfoField setStringValue: fileString];
473                [fBasicInfoField setToolTip: nil];
474            }
475            [fBasicInfoField setHidden: NO];
476           
477            [fNoneSelectedField setHidden: YES];
478        }
479        else
480        {
481            [fImageView setImage: [NSImage imageNamed: NSImageNameApplicationIcon]];
482            [fNoneSelectedField setHidden: NO];
483           
484            [fNameField setHidden: YES];
485            [fBasicInfoField setHidden: YES];
486        }
487       
488        [fNameField setToolTip: nil];
489    }
490    else
491    {
492        Torrent * torrent = [fTorrents objectAtIndex: 0];
493       
494        [fImageView setImage: [torrent icon]];
495       
496        NSString * name = [torrent name];
497        [fNameField setStringValue: name];
498        [fNameField setToolTip: name];
499        [fNameField setHidden: NO];
500       
501        if (![torrent isMagnet])
502        {
503            NSString * basicString = [NSString stringForFileSize: [torrent size]];
504            if ([torrent isFolder])
505            {
506                NSString * fileString;
507                const NSUInteger fileCount = [torrent fileCount];
508                if (fileCount == 1)
509                    fileString = NSLocalizedString(@"1 file", "Inspector -> selected torrents");
510                else
511                    fileString= [NSString stringWithFormat: NSLocalizedString(@"%@ files", "Inspector -> selected torrents"),
512                                    [NSString formattedUInteger: fileCount]];
513                basicString = [NSString stringWithFormat: @"%@, %@", fileString, basicString];
514            }
515            [fBasicInfoField setStringValue: basicString];
516           
517            NSString * byteString;
518            if ([NSApp isOnMountainLionOrBetter])
519            {
520                NSByteCountFormatter * formatter = [[NSByteCountFormatterMtLion alloc] init];
521                [formatter setAllowedUnits: NSByteCountFormatterUseBytes];
522                byteString = [formatter stringFromByteCount: [torrent size]];
523                [formatter release];
524            }
525            else
526                byteString = [NSString stringWithFormat: NSLocalizedString(@"%@ bytes", "Inspector -> selected torrents"), [NSString formattedUInteger: [torrent size]]];
527            [fBasicInfoField setToolTip: byteString];
528        }
529        else
530        {
531            [fBasicInfoField setStringValue: NSLocalizedString(@"Magnetized transfer", "Inspector -> selected torrents")];
532            [fBasicInfoField setToolTip: nil];
533        }
534        [fBasicInfoField setHidden: NO];
535       
536        [fNoneSelectedField setHidden: YES];
537    }
538   
539    [fGeneralViewController setInfoForTorrents: fTorrents];
540    [fActivityViewController setInfoForTorrents: fTorrents];
541    [fTrackersViewController setInfoForTorrents: fTorrents];
542    [fPeersViewController setInfoForTorrents: fTorrents];
543    [fFileViewController setInfoForTorrents: fTorrents];
544    [fOptionsViewController setInfoForTorrents: fTorrents];
545   
546    [fViewController updateInfo];
547}
548
549- (void) resetInfoForTorrent: (NSNotification *) notification
550{
551    Torrent * torrent = [[notification userInfo] objectForKey: @"Torrent"];
552    if (fTorrents && (!torrent || [fTorrents containsObject: torrent]))
553        [self resetInfo];
554}
555
556@end
Note: See TracBrowser for help on using the repository browser.