Add a Cocoa UI for the updater on Mac

* Link the updater application with the Cocoa framework on Mac

 * Construct a UI in code and display it when running the main install process.

   Usually UIs on Mac are contained in a .nib file created with Interface Builder
   but in this case the UI is created directly in code to meet the single-binary requirement,
   although it may be possible to bundle the .nib into the binary itself.
This commit is contained in:
Robert Knight 2011-08-23 16:20:23 +01:00
parent eeeafb2c1a
commit ff21b77ec1
4 changed files with 178 additions and 5 deletions

View file

@ -29,6 +29,9 @@ set (SOURCES
if (ENABLE_GTK)
set(SOURCES ${SOURCES} UpdateDialogGtk.cpp)
endif()
if (APPLE)
set(SOURCES ${SOURCES} UpdateDialogCocoa.mm)
endif()
set (HEADERS
Dir.h
@ -43,6 +46,9 @@ set (HEADERS
if (ENABLE_GTK)
set(HEADERS ${HEADERS} UpdateDialogGtk.h)
endif()
if (APPLE)
set(SOURCES ${SOURCES} UpdateDialogCocoa.h)
endif()
add_library(updatershared
${SOURCES}
@ -73,7 +79,7 @@ add_executable(updater
if(APPLE)
set_target_properties(
updater
PROPERTIES LINK_FLAGS "-framework Security"
PROPERTIES LINK_FLAGS "-framework Security -framework Cocoa"
)
endif()

View file

@ -1,7 +1,25 @@
#pragma once
class UpdateDialogCocoa
#include "UpdateObserver.h"
class UpdateDialogPrivate;
class UpdateDialogCocoa : public UpdateObserver
{
// TODO - Update dialog using Cocoa on Mac
public:
UpdateDialogCocoa();
~UpdateDialogCocoa();
void init();
void exec();
// implements UpdateObserver
virtual void updateError(const std::string& errorMessage);
virtual bool updateRetryCancel(const std::string& message);
virtual void updateProgress(int percentage);
virtual void updateFinished();
private:
UpdateDialogPrivate* d;
};

140
src/UpdateDialogCocoa.mm Normal file
View file

@ -0,0 +1,140 @@
#include "UpdateDialogCocoa.h"
#include <Cocoa/Cocoa.h>
#include "Log.h"
#include "StringUtils.h"
@interface UpdateDialogDelegate : NSObject
{
@public UpdateDialogPrivate* dialog;
}
- (void) finishClicked;
- (void) reportUpdateError:(id)arg;
- (void) reportUpdateProgress:(id)arg;
- (void) reportUpdateFinished:(id)arg;
@end
class UpdateDialogPrivate
{
public:
UpdateDialogDelegate* delegate;
NSAutoreleasePool* pool;
NSWindow* window;
NSButton* finishButton;
NSTextField* progressLabel;
NSProgressIndicator* progressBar;
};
@implementation UpdateDialogDelegate
- (void) finishClicked
{
[NSApp stop:self];
}
- (void) reportUpdateError: (id)arg
{
NSMutableString* message;
[message appendString:@"There was a problem installing the update: "];
[message appendString:arg];
[dialog->progressLabel setTitleWithMnemonic: message];
}
- (void) reportUpdateProgress: (id)arg
{
int percentage = [arg intValue];
[dialog->progressBar setDoubleValue:(percentage/100.0)];
}
- (void) reportUpdateFinished: (id)arg
{
[dialog->progressLabel setTitleWithMnemonic:@"Updates installed. Click 'Finish' to restart the application."];
}
@end
UpdateDialogCocoa::UpdateDialogCocoa()
: d(new UpdateDialogPrivate)
{
[NSApplication sharedApplication];
d->pool = [[NSAutoreleasePool alloc] init];
}
UpdateDialogCocoa::~UpdateDialogCocoa()
{
[d->pool release];
}
void UpdateDialogCocoa::init()
{
d->delegate = [[UpdateDialogDelegate alloc] init];
d->delegate->dialog = d;
int width = 370;
int height = 100;
d->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(200, 200, width, height)
styleMask:NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask
backing:NSBackingStoreBuffered defer:NO];
[d->window setTitle:@"Mendeley Updater"];
d->finishButton = [[NSButton alloc] init];
[d->finishButton setTitle:@"Finish"];
[d->finishButton setButtonType:NSMomentaryLightButton];
[d->finishButton setBezelStyle:NSRoundedBezelStyle];
[d->finishButton setTarget:d->delegate];
[d->finishButton setAction:@selector(finishClicked)];
d->progressBar = [[NSProgressIndicator alloc] init];
[d->progressBar setIndeterminate:false];
[d->progressBar setMinValue:0.0];
[d->progressBar setMaxValue:1.0];
d->progressLabel = [[NSTextField alloc] init];
[d->progressLabel setEditable:false];
[d->progressLabel setSelectable:false];
[d->progressLabel setTitleWithMnemonic:@"Installing Updates"];
[d->progressLabel setBezeled:false];
[d->progressLabel setDrawsBackground:false];
NSView* windowContent = [d->window contentView];
[windowContent addSubview:d->progressLabel];
[windowContent addSubview:d->progressBar];
[windowContent addSubview:d->finishButton];
[d->progressLabel setFrame:NSMakeRect(10,70,width - 10,20)];
[d->progressBar setFrame:NSMakeRect(10,40,width - 20,20)];
[d->finishButton setFrame:NSMakeRect(width - 85,5,80,30)];
}
void UpdateDialogCocoa::exec()
{
[d->window makeKeyAndOrderFront:d->window];
[d->window center];
[NSApp run];
}
void UpdateDialogCocoa::updateError(const std::string& errorMessage)
{
[d->delegate performSelectorOnMainThread:@selector(reportUpdateError:)
withObject:[NSString stringWithUTF8String:errorMessage.c_str()]
waitUntilDone:false];
}
bool UpdateDialogCocoa::updateRetryCancel(const std::string& message)
{
// TODO
}
void UpdateDialogCocoa::updateProgress(int percentage)
{
[d->delegate performSelectorOnMainThread:@selector(reportUpdateProgress:)
withObject:[NSNumber numberWithInt:percentage]
waitUntilDone:false];
}
void UpdateDialogCocoa::updateFinished()
{
[d->delegate performSelectorOnMainThread:@selector(reportUpdateFinished:)
withObject:nil
waitUntilDone:false];
}

View file

@ -10,6 +10,10 @@
#include "UpdateDialogGtk.h"
#endif
#if defined(PLATFORM_MAC)
#include "UpdateDialogCocoa.h"
#endif
#include <iostream>
void runWithUi(int argc, char** argv, UpdateInstaller* installer);
@ -91,8 +95,13 @@ void runWithUi(int argc, char** argv, UpdateInstaller* installer)
#ifdef PLATFORM_MAC
void runWithUi(int argc, char** argv, UpdateInstaller* installer)
{
// TODO - Cocoa UI
installer->run();
UpdateDialogCocoa dialog;
installer->setObserver(&dialog);
dialog.init();
tthread::thread updaterThread(runUpdaterThread,installer);
dialog.exec();
updaterThread.join();
installer->restartMainApp();
}
#endif