1// This file is part of BOINC.
2// http://boinc.berkeley.edu
3// Copyright (C) 2016 University of California
4//
5// BOINC is free software; you can redistribute it and/or modify it
6// under the terms of the GNU Lesser General Public License
7// as published by the Free Software Foundation,
8// either version 3 of the License, or (at your option) any later version.
9//
10// BOINC is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13// See the GNU Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17
18//  BOINCGUIApp.mm
19
20#include "MacGUI.pch"
21#include "BOINCGUIApp.h"
22#include "BOINCBaseFrame.h"
23#import <Cocoa/Cocoa.h>
24
25#if !wxCHECK_VERSION(3,0,1)
26// This should be fixed after wxCocoa 3.0.0:
27// http://trac.wxwidgets.org/ticket/16156
28
29#ifndef NSEventTypeApplicationDefined
30#define NSEventTypeApplicationDefined NSApplicationDefined
31#endif
32
33// Cocoa routines which are part of CBOINCGUIApp
34// Override standard wxCocoa wxApp::CallOnInit() to allow Manager
35// to run properly when launched hidden on login via Login Item.
36bool CBOINCGUIApp::CallOnInit() {
37        NSAutoreleasePool *mypool = [[NSAutoreleasePool alloc] init];
38
39        NSEvent *event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
40                                        location:NSMakePoint(0.0, 0.0)
41                                   modifierFlags:0
42                                       timestamp:0
43                                    windowNumber:0
44                                         context:nil
45                                         subtype:0 data1:0 data2:0];
46        [NSApp postEvent:event atStart:FALSE];
47
48    bool retVal = wxApp::CallOnInit();
49
50    [mypool release];
51    return retVal;
52}
53#endif
54
55
56// Our application can get into a strange state
57// if our login item launched it hidden and the
58// first time the user "opens" it he either
59// double-clicks on our Finder icon or uses
60// command-tab.  It becomes the frontmost
61// application (with its menu in the menubar)
62// but the windows remain hidden, and it does
63// not receive an activate event, so we must
64// handle this case by polling.
65//
66// We can stop the polling after the windows
67// have been shown once, since this state only
68// occurs if the windows have never appeared.
69//
70// TODO: Can we avoid polling by using notification
71// TODO: [NSApplicationDelegate applicationDidUnhide: ] ?
72//
73void CBOINCGUIApp::CheckPartialActivation() {
74    // This code is not needed and has bad effects on OS 10.5.
75    // Initializing wasHidden this way avoids the problem
76    // because we are briefly shown at login on OS 10.5.
77    static bool wasHidden = [ NSApp isHidden ];
78
79    if (wasHidden) {
80        if (m_bAboutDialogIsOpen) return;
81
82        if (! [ NSApp isHidden ]) {
83            wasHidden = false;
84            ShowInterface();
85        }
86    }
87}
88
89
90// Returns true if file was modified since system was booted, else false
91//
92bool CBOINCGUIApp::WasFileModifiedBeforeSystemBoot(char * filePath) {
93    NSTimeInterval upTime = [[NSProcessInfo processInfo] systemUptime];
94    NSString *path = [NSString stringWithUTF8String:filePath];
95    NSError *error = nil;
96    NSDictionary * attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
97    if (attrs && !error) { // If file exists, then ...
98        NSDate *fileLastModifiedDate = [attrs fileModificationDate];
99        NSTimeInterval ageOfFile = -[fileLastModifiedDate timeIntervalSinceNow];
100        return (ageOfFile > upTime);
101    }
102
103    return false;
104}
105
106
107// HideThisApp() is called from CBOINCGUIApp::ShowApplication(bool)
108// and replaces a call of ShowHideProcess() which is deprecated
109// under OS 10.9.
110void CBOINCGUIApp::HideThisApp() {
111    [ NSApp hide:NSApp ];
112}
113
114
115/// Determines if the current process is visible.
116///
117/// @return
118///  true if the current process is visible, otherwise false.
119///
120bool CBOINCGUIApp::IsApplicationVisible() {
121    return (! [ NSApp isHidden ]);
122}
123
124
125///
126/// Shows or hides the current process.
127///
128/// @param bShow
129///   true will show the process, false will hide the process.
130///
131void CBOINCGUIApp::ShowApplication(bool bShow) {
132    if (bShow) {
133        [ NSApp activateIgnoringOtherApps:YES ];
134    } else {
135        [ NSApp hide:NSApp ];
136    }
137}
138
139
140extern bool s_bSkipExitConfirmation;
141
142// Set s_bSkipExitConfirmation to true if cancelled because of logging out or shutting down
143//
144OSErr QuitAppleEventHandler( const AppleEvent *appleEvt, AppleEvent* reply, UInt32 refcon ) {
145    DescType            senderType;
146    Size                actualSize;
147    pid_t               senderPid;
148    OSStatus            anErr;
149
150    // Refuse to quit if a modal dialog is open.
151    // Unfortunately, I know of no way to disable the Quit item in our Dock menu
152    if (wxGetApp().IsModalDialogDisplayed()) {
153        NSBeep();
154        return userCanceledErr;
155    }
156
157    anErr = AEGetAttributePtr(appleEvt, keySenderPIDAttr, typeSInt32,
158                                &senderType, &senderPid, sizeof(senderPid), &actualSize);
159    if (anErr == noErr) {
160        NSString * bundleID = [[NSRunningApplication runningApplicationWithProcessIdentifier:senderPid] bundleIdentifier];
161        // Consider a Quit command from our Dock menu as coming from this application
162        if (bundleID) {
163            if (([bundleID compare:@"com.apple.dock"] != NSOrderedSame)
164                    && ([bundleID compare:@"edu.berkeley.boinc"] != NSOrderedSame)) {
165                s_bSkipExitConfirmation = true; // Not from our app, our dock icon or our taskbar icon
166                // The following may no longer be needed under wxCocoa-3.0.0
167                wxGetApp().ExitMainLoop();  // Prevents wxMac from issuing events to closed frames
168            }
169        }
170    }
171
172    wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, wxID_EXIT);
173    wxGetApp().GetFrame()->GetEventHandler()->AddPendingEvent(evt);
174    return noErr;
175}
176