1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* vim:set ts=2 sw=2 sts=2 et cindent: */
3/* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7#import <Cocoa/Cocoa.h>
8#include <stdio.h>
9#include <unistd.h>
10#include "mozilla/Sprintf.h"
11#include "progressui.h"
12#include "readstrings.h"
13#include "updatererrors.h"
14
15#define TIMER_INTERVAL 0.2
16
17static float sProgressVal;  // between 0 and 100
18static BOOL sQuit = NO;
19static BOOL sIndeterminate = NO;
20static StringTable sLabels;
21static const char* sUpdatePath;
22
23@interface UpdaterUI : NSObject {
24  IBOutlet NSProgressIndicator* progressBar;
25  IBOutlet NSTextField* progressTextField;
26}
27@end
28
29@implementation UpdaterUI
30
31- (void)awakeFromNib {
32  NSWindow* w = [progressBar window];
33
34  [w setTitle:[NSString stringWithUTF8String:sLabels.title.get()]];
35  [progressTextField setStringValue:[NSString stringWithUTF8String:sLabels.info.get()]];
36
37  NSRect origTextFrame = [progressTextField frame];
38  [progressTextField sizeToFit];
39
40  int widthAdjust = progressTextField.frame.size.width - origTextFrame.size.width;
41
42  if (widthAdjust > 0) {
43    NSRect f;
44    f.size.width = w.frame.size.width + widthAdjust;
45    f.size.height = w.frame.size.height;
46    [w setFrame:f display:YES];
47  }
48
49  [w center];
50
51  [progressBar setIndeterminate:sIndeterminate];
52  [progressBar setDoubleValue:0.0];
53
54  [[NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL
55                                    target:self
56                                  selector:@selector(updateProgressUI:)
57                                  userInfo:nil
58                                   repeats:YES] retain];
59
60  // Make sure we are on top initially
61  [NSApp activateIgnoringOtherApps:YES];
62}
63
64// called when the timer goes off
65- (void)updateProgressUI:(NSTimer*)aTimer {
66  if (sQuit) {
67    [aTimer invalidate];
68    [aTimer release];
69
70    // It seems to be necessary to activate and hide ourselves before we stop,
71    // otherwise the "run" method will not return until the user focuses some
72    // other app.  The activate step is necessary if we are not the active app.
73    // This is a big hack, but it seems to do the trick.
74    [NSApp activateIgnoringOtherApps:YES];
75    [NSApp hide:self];
76    [NSApp stop:self];
77  }
78
79  float progress = sProgressVal;
80
81  [progressBar setDoubleValue:(double)progress];
82}
83
84// leave this as returning a BOOL instead of NSApplicationTerminateReply
85// for backward compatibility
86- (BOOL)applicationShouldTerminate:(NSApplication*)sender {
87  return sQuit;
88}
89
90@end
91
92int InitProgressUI(int* pargc, char*** pargv) {
93  sUpdatePath = (*pargv)[1];
94
95  return 0;
96}
97
98int ShowProgressUI(bool indeterminate) {
99  if (!sUpdatePath) {
100    // InitProgressUI was never called.
101    return -1;
102  }
103
104  // Only show the Progress UI if the process is taking a significant amount of
105  // time where a significant amount of time is defined as .5 seconds after
106  // ShowProgressUI is called sProgress is less than 70.
107  usleep(500000);
108
109  if (sQuit || sProgressVal > 70.0f) {
110    return 0;
111  }
112
113  char path[PATH_MAX];
114  SprintfLiteral(path, "%s/updater.ini", sUpdatePath);
115  if (ReadStrings(path, &sLabels) != OK) {
116    return -1;
117  }
118
119  sIndeterminate = indeterminate;
120  [NSApplication sharedApplication];
121  [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp topLevelObjects:nil];
122  [NSApp run];
123
124  return 0;
125}
126
127// Called on a background thread
128void QuitProgressUI() { sQuit = YES; }
129
130// Called on a background thread
131void UpdateProgressUI(float progress) {
132  sProgressVal = progress;  // 32-bit writes are atomic
133}
134