mirror of
https://github.com/glest/glest-source.git
synced 2025-02-24 19:52:25 +01:00
823 lines
33 KiB
Objective-C
823 lines
33 KiB
Objective-C
/**
|
|
* MojoSetup; a portable, flexible installation application.
|
|
*
|
|
* Please see the file LICENSE.txt in the source's root directory.
|
|
*
|
|
* This file written by Ryan C. Gordon.
|
|
Copyright (c) 2006-2010 Ryan C. Gordon and others.
|
|
|
|
This software is provided 'as-is', without any express or implied warranty.
|
|
In no event will the authors be held liable for any damages arising from
|
|
the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software in a
|
|
product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
|
|
2. Altered source versions must be plainly marked as such, and must not be
|
|
misrepresented as being the original software.
|
|
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
Ryan C. Gordon <icculus@icculus.org>
|
|
|
|
*/
|
|
|
|
#if !SUPPORT_GUI_COCOA
|
|
#error Something is wrong in the build system.
|
|
#endif
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
#undef true
|
|
#undef false
|
|
|
|
#define BUILDING_EXTERNAL_PLUGIN 1
|
|
#include "gui.h"
|
|
|
|
MOJOGUI_PLUGIN(cocoa)
|
|
|
|
#if !GUI_STATIC_LINK_COCOA
|
|
CREATE_MOJOGUI_ENTRY_POINT(cocoa)
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
CLICK_BACK=-1,
|
|
CLICK_CANCEL,
|
|
CLICK_NEXT,
|
|
CLICK_NONE
|
|
} ClickValue;
|
|
|
|
// This nasty hack is because we appear to need to be under
|
|
// -[NSApp run] when calling things like NSRunAlertPanel().
|
|
// So we push a custom event, call -[NSApp run], catch it, do
|
|
// the panel, then call -[NSApp stop]. Yuck.
|
|
typedef enum
|
|
{
|
|
CUSTOMEVENT_BASEVALUE=3234,
|
|
CUSTOMEVENT_RUNQUEUE,
|
|
CUSTOMEVENT_MSGBOX,
|
|
CUSTOMEVENT_PROMPTYN,
|
|
CUSTOMEVENT_PROMPTYNAN,
|
|
CUSTOMEVENT_INSERTMEDIA,
|
|
} CustomEvent;
|
|
|
|
|
|
static NSAutoreleasePool *GAutoreleasePool = nil;
|
|
|
|
@interface MojoSetupController : NSView
|
|
{
|
|
IBOutlet NSButton *BackButton;
|
|
IBOutlet NSButton *CancelButton;
|
|
IBOutlet NSComboBox *DestinationCombo;
|
|
IBOutlet NSTextField *FinalText;
|
|
IBOutlet NSWindow *MainWindow;
|
|
IBOutlet NSButton *NextButton;
|
|
IBOutlet NSProgressIndicator *ProgressBar;
|
|
IBOutlet NSTextField *ProgressComponentLabel;
|
|
IBOutlet NSTextField *ProgressItemLabel;
|
|
IBOutlet NSTextView *ReadmeText;
|
|
IBOutlet NSTabView *TabView;
|
|
IBOutlet NSTextField *TitleLabel;
|
|
IBOutlet NSMenuItem *QuitMenuItem;
|
|
IBOutlet NSMenuItem *AboutMenuItem;
|
|
IBOutlet NSMenuItem *HideMenuItem;
|
|
IBOutlet NSMenuItem *WindowMenuItem;
|
|
IBOutlet NSMenuItem *HideOthersMenuItem;
|
|
IBOutlet NSMenuItem *ShowAllMenuItem;
|
|
IBOutlet NSMenuItem *ServicesMenuItem;
|
|
IBOutlet NSMenuItem *MinimizeMenuItem;
|
|
IBOutlet NSMenuItem *ZoomMenuItem;
|
|
IBOutlet NSMenuItem *BringAllToFrontMenuItem;
|
|
IBOutlet NSView *OptionsView;
|
|
ClickValue clickValue;
|
|
boolean canForward;
|
|
boolean needToBreakEventLoop;
|
|
boolean finalPage;
|
|
MojoGuiYNAN answerYNAN;
|
|
MojoGuiSetupOptions *mojoOpts;
|
|
}
|
|
- (void)awakeFromNib;
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
|
|
- (void)prepareWidgets:(const char*)winTitle;
|
|
- (void)unprepareWidgets;
|
|
- (void)fireCustomEvent:(CustomEvent)eventType data1:(NSInteger)data1 data2:(NSInteger)data2 atStart:(BOOL)atStart;
|
|
- (void)doCustomEvent:(NSEvent *)event;
|
|
- (void)doMsgBox:(const char *)title text:(const char *)text;
|
|
- (void)doPromptYN:(const char *)title text:(const char *)text;
|
|
- (void)doPromptYNAN:(const char *)title text:(const char *)text;
|
|
- (void)doInsertMedia:(const char *)medianame;
|
|
- (MojoGuiYNAN)getAnswerYNAN;
|
|
- (IBAction)backClicked:(NSButton *)sender;
|
|
- (IBAction)cancelClicked:(NSButton *)sender;
|
|
- (IBAction)nextClicked:(NSButton *)sender;
|
|
- (IBAction)browseClicked:(NSButton *)sender;
|
|
- (IBAction)menuQuit:(NSMenuItem *)sender;
|
|
- (int)doPage:(NSString *)pageId title:(const char *)_title canBack:(boolean)canBack canFwd:(boolean)canFwd canCancel:(boolean)canCancel canFwdAtStart:(boolean)canFwdAtStart shouldBlock:(BOOL)shouldBlock;
|
|
- (int)doReadme:(const char *)title text:(NSString *)text canBack:(boolean)canBack canFwd:(boolean)canFwd;
|
|
- (void)setOptionTreeSensitivity:(MojoGuiSetupOptions *)opts enabled:(boolean)val;
|
|
- (void)optionToggled:(id)toggle;
|
|
- (NSView *)newOptionLevel:(NSView *)box;
|
|
- (void)buildOptions:(MojoGuiSetupOptions *)opts view:(NSView *)box sensitive:(boolean)sensitive;
|
|
- (int)doOptions:(MojoGuiSetupOptions *)opts canBack:(boolean)canBack canFwd:(boolean)canFwd;
|
|
- (char *)doDestination:(const char **)recommends recnum:(int)recnum command:(int *)command canBack:(boolean)canBack canFwd:(boolean)canFwd;
|
|
- (int)doProductKey:(const char *)desc fmt:(const char *)fmt buf:(char *)buf buflen:(const int)buflen canBack:(boolean)canBack canFwd:(boolean)canFwd;
|
|
- (int)doProgress:(const char *)type component:(const char *)component percent:(int)percent item:(const char *)item canCancel:(boolean)canCancel;
|
|
- (void)doFinal:(const char *)msg;
|
|
@end // interface MojoSetupController
|
|
|
|
@implementation MojoSetupController
|
|
- (void)awakeFromNib
|
|
{
|
|
clickValue = CLICK_NONE;
|
|
canForward = false;
|
|
answerYNAN = MOJOGUI_NO;
|
|
needToBreakEventLoop = false;
|
|
finalPage = false;
|
|
mojoOpts = nil;
|
|
} // awakeFromNib
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
|
{
|
|
printf("didfinishlaunching\n");
|
|
[NSApp stop:self]; // break out of NSApp::run()
|
|
} // applicationDidFinishLaunching
|
|
|
|
- (void)prepareWidgets:(const char*)winTitle
|
|
{
|
|
#if 1
|
|
[BackButton setTitle:[NSString stringWithUTF8String:_("Back")]];
|
|
[NextButton setTitle:[NSString stringWithUTF8String:_("Next")]];
|
|
[CancelButton setTitle:[NSString stringWithUTF8String:_("Cancel")]];
|
|
#else
|
|
// !!! FIXME: there's probably a better way to do this.
|
|
// Set the correct localization for the buttons, then resize them so
|
|
// the new text fits perfectly. After that, we need to reposition
|
|
// them so they don't look scattered.
|
|
NSRect frameBack = [BackButton frame];
|
|
NSRect frameNext = [NextButton frame];
|
|
NSRect frameCancel = [CancelButton frame];
|
|
const float startX = frameCancel.origin.x + frameCancel.size.width;
|
|
const float spacing = (frameBack.origin.x + frameBack.size.width) - frameNext.origin.x;
|
|
[BackButton setTitle:[NSString stringWithUTF8String:_("Back")]];
|
|
[NextButton setTitle:[NSString stringWithUTF8String:_("Next")]];
|
|
[CancelButton setTitle:[NSString stringWithUTF8String:_("Cancel")]];
|
|
[BackButton sizeToFit];
|
|
[NextButton sizeToFit];
|
|
[CancelButton sizeToFit];
|
|
frameBack = [BackButton frame];
|
|
frameNext = [NextButton frame];
|
|
frameCancel = [CancelButton frame];
|
|
frameCancel.origin.x = startX - frameCancel.size.width;
|
|
frameNext.origin.x = (frameCancel.origin.x - frameNext.size.width) - spacing;
|
|
frameBack.origin.x = (frameNext.origin.x - frameBack.size.width) - spacing;
|
|
[CancelButton setFrame:frameCancel];
|
|
[CancelButton setNeedsDisplay:YES];
|
|
[NextButton setFrame:frameNext];
|
|
[NextButton setNeedsDisplay:YES];
|
|
[BackButton setFrame:frameBack];
|
|
[BackButton setNeedsDisplay:YES];
|
|
#endif
|
|
|
|
[ProgressBar setUsesThreadedAnimation:YES]; // we don't pump fast enough.
|
|
[ProgressBar startAnimation:self];
|
|
|
|
[WindowMenuItem setTitle:[NSString stringWithUTF8String:_("Window")]];
|
|
[HideOthersMenuItem setTitle:[NSString stringWithUTF8String:_("Hide Others")]];
|
|
[ShowAllMenuItem setTitle:[NSString stringWithUTF8String:_("Show All")]];
|
|
[ServicesMenuItem setTitle:[NSString stringWithUTF8String:_("Services")]];
|
|
[MinimizeMenuItem setTitle:[NSString stringWithUTF8String:_("Minimize")]];
|
|
[ZoomMenuItem setTitle:[NSString stringWithUTF8String:_("Zoom")]];
|
|
[BringAllToFrontMenuItem setTitle:[NSString stringWithUTF8String:_("Bring All to Front")]];
|
|
|
|
NSString *appName;
|
|
appName = (NSString *) [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
|
|
if (appName == nil)
|
|
appName = [[NSProcessInfo processInfo] processName];
|
|
|
|
const char *utf8AppName = [appName UTF8String];
|
|
char *text;
|
|
|
|
text = format(_("About %0"), utf8AppName);
|
|
[AboutMenuItem setTitle:[NSString stringWithUTF8String:text]];
|
|
free(text);
|
|
|
|
text = format(_("Hide %0"), utf8AppName);
|
|
[HideMenuItem setTitle:[NSString stringWithUTF8String:text]];
|
|
free(text);
|
|
|
|
text = format(_("Quit %0"), utf8AppName);
|
|
[QuitMenuItem setTitle:[NSString stringWithUTF8String:text]];
|
|
free(text);
|
|
|
|
[MainWindow setTitle:[NSString stringWithUTF8String:winTitle]];
|
|
[MainWindow center];
|
|
[MainWindow makeKeyAndOrderFront:self];
|
|
} // prepareWidgets
|
|
|
|
- (void)unprepareWidgets
|
|
{
|
|
[MainWindow orderOut:self];
|
|
} // unprepareWidgets
|
|
|
|
- (void)fireCustomEvent:(CustomEvent)eventType data1:(NSInteger)data1 data2:(NSInteger)data2 atStart:(BOOL)atStart
|
|
{
|
|
NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:(short)eventType data1:data1 data2:data2];
|
|
[NSApp postEvent:event atStart:atStart];
|
|
[NSApp run]; // event handler _must_ call -[NSApp stop], or you block here forever.
|
|
} // fireCustomEvent
|
|
|
|
- (void)doCustomEvent:(NSEvent*)event
|
|
{
|
|
printf("custom event!\n");
|
|
switch ((CustomEvent) [event subtype])
|
|
{
|
|
case CUSTOMEVENT_RUNQUEUE:
|
|
if ([NSApp modalWindow] != nil)
|
|
{
|
|
// If we're in a modal thing, so don't break the event loop.
|
|
// Just make a note to break it later.
|
|
needToBreakEventLoop = true;
|
|
return;
|
|
} // if
|
|
break; // we just need the -[NSApp stop] call.
|
|
case CUSTOMEVENT_MSGBOX:
|
|
[self doMsgBox:(const char *)[event data1] text:(const char *)[event data2]];
|
|
break;
|
|
case CUSTOMEVENT_PROMPTYN:
|
|
[self doPromptYN:(const char *)[event data1] text:(const char *)[event data2]];
|
|
break;
|
|
case CUSTOMEVENT_PROMPTYNAN:
|
|
[self doPromptYNAN:(const char *)[event data1] text:(const char *)[event data2]];
|
|
break;
|
|
case CUSTOMEVENT_INSERTMEDIA:
|
|
[self doInsertMedia:(const char *)[event data1]];
|
|
break;
|
|
default:
|
|
return; // let it go without breaking the event loop.
|
|
} // switch
|
|
|
|
[NSApp stop:self]; // break the event loop.
|
|
} // doCustomEvent
|
|
|
|
- (void)doMsgBox:(const char *)title text:(const char *)text
|
|
{
|
|
NSString *titlestr = [NSString stringWithUTF8String:title];
|
|
NSString *textstr = [NSString stringWithUTF8String:text];
|
|
NSString *okstr = [NSString stringWithUTF8String:_("OK")];
|
|
NSRunInformationalAlertPanel(titlestr, textstr, okstr, nil, nil);
|
|
if (needToBreakEventLoop)
|
|
{
|
|
needToBreakEventLoop = false;
|
|
[self fireCustomEvent:CUSTOMEVENT_RUNQUEUE data1:0 data2:0 atStart:NO];
|
|
} // if
|
|
} // doMsgBox
|
|
|
|
- (void)doPromptYN:(const char *)title text:(const char *)text
|
|
{
|
|
NSString *titlestr = [NSString stringWithUTF8String:title];
|
|
NSString *textstr = [NSString stringWithUTF8String:text];
|
|
NSString *yesstr = [NSString stringWithUTF8String:_("Yes")];
|
|
NSString *nostr = [NSString stringWithUTF8String:_("No")];
|
|
const NSInteger rc = NSRunAlertPanel(titlestr, textstr, yesstr, nostr, nil);
|
|
answerYNAN = ((rc == NSAlertDefaultReturn) ? MOJOGUI_YES : MOJOGUI_NO);
|
|
if (needToBreakEventLoop)
|
|
{
|
|
needToBreakEventLoop = false;
|
|
[self fireCustomEvent:CUSTOMEVENT_RUNQUEUE data1:0 data2:0 atStart:NO];
|
|
} // if
|
|
} // doPromptYN
|
|
|
|
- (void)doPromptYNAN:(const char *)title text:(const char *)text
|
|
{
|
|
// !!! FIXME
|
|
[self doPromptYN:title text:text];
|
|
} // doPromptYN
|
|
|
|
- (void)doInsertMedia:(const char *)medianame
|
|
{
|
|
NSString *title = [NSString stringWithUTF8String:_("Media change")];
|
|
char *fmt = xstrdup(_("Please insert '%0'"));
|
|
char *_text = format(fmt, medianame);
|
|
NSString *text = [NSString stringWithUTF8String:_text];
|
|
free(_text);
|
|
free(fmt);
|
|
NSString *okstr = [NSString stringWithUTF8String:_("OK")];
|
|
NSString *cancelstr = [NSString stringWithUTF8String:_("Cancel")];
|
|
const NSInteger rc = NSRunAlertPanel(title, text, okstr, cancelstr, nil);
|
|
answerYNAN = ((rc == NSAlertDefaultReturn) ? MOJOGUI_YES : MOJOGUI_NO);
|
|
if (needToBreakEventLoop)
|
|
{
|
|
needToBreakEventLoop = false;
|
|
[self fireCustomEvent:CUSTOMEVENT_RUNQUEUE data1:0 data2:0 atStart:NO];
|
|
} // if
|
|
} // doInsertMedia
|
|
|
|
- (MojoGuiYNAN)getAnswerYNAN
|
|
{
|
|
return answerYNAN;
|
|
} // getAnswerYNAN
|
|
|
|
- (IBAction)backClicked:(NSButton *)sender
|
|
{
|
|
clickValue = CLICK_BACK;
|
|
[NSApp stop:self];
|
|
} // backClicked
|
|
|
|
- (IBAction)cancelClicked:(NSButton *)sender
|
|
{
|
|
char *title = xstrdup(_("Cancel installation"));
|
|
char *text = xstrdup(_("Are you sure you want to cancel installation?"));
|
|
[self doPromptYN:title text:text];
|
|
free(title);
|
|
free(text);
|
|
if (answerYNAN == MOJOGUI_YES)
|
|
{
|
|
clickValue = CLICK_CANCEL;
|
|
[NSApp stop:self];
|
|
} // if
|
|
} // cancelClicked
|
|
|
|
- (IBAction)nextClicked:(NSButton *)sender
|
|
{
|
|
clickValue = CLICK_NEXT;
|
|
[NSApp stop:self];
|
|
} // nextClicked
|
|
|
|
- (IBAction)browseClicked:(NSButton *)sender
|
|
{
|
|
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
|
[panel setTitle:[NSString stringWithUTF8String:_("Destination")]];
|
|
[panel setAllowsMultipleSelection:NO];
|
|
[panel setCanCreateDirectories:YES];
|
|
[panel setCanChooseDirectories:YES];
|
|
[panel setCanChooseFiles:NO];
|
|
if ([panel runModal] == NSOKButton)
|
|
[DestinationCombo setStringValue:[panel filename]];
|
|
} // browseClicked
|
|
|
|
- (IBAction)menuQuit:(NSMenuItem *)sender
|
|
{
|
|
if (finalPage) // make this work like you clicked "finished".
|
|
[self nextClicked:nil];
|
|
else if ([CancelButton isEnabled]) // make this work like you clicked "cancel".
|
|
[self cancelClicked:nil];
|
|
} // menuQuit
|
|
|
|
- (int)doPage:(NSString *)pageId title:(const char *)_title canBack:(boolean)canBack canFwd:(boolean)canFwd canCancel:(boolean)canCancel canFwdAtStart:(boolean)canFwdAtStart shouldBlock:(BOOL)shouldBlock
|
|
{
|
|
[TitleLabel setStringValue:[NSString stringWithUTF8String:_title]];
|
|
clickValue = CLICK_NONE;
|
|
canForward = canFwd;
|
|
[BackButton setEnabled:canBack ? YES : NO];
|
|
[NextButton setEnabled:canFwdAtStart ? YES : NO];
|
|
[CancelButton setEnabled:canCancel ? YES : NO];
|
|
[TabView selectTabViewItemWithIdentifier:pageId];
|
|
if (shouldBlock == NO)
|
|
[self fireCustomEvent:CUSTOMEVENT_RUNQUEUE data1:0 data2:0 atStart:NO];
|
|
else
|
|
{
|
|
[NSApp run];
|
|
assert(clickValue < CLICK_NONE);
|
|
} // else
|
|
return (int) clickValue;
|
|
} // doPage
|
|
|
|
- (int)doReadme:(const char *)title text:(NSString *)text canBack:(boolean)canBack canFwd:(boolean)canFwd
|
|
{
|
|
NSRange range = {0, 1}; // reset scrolling to start of text.
|
|
[ReadmeText setString:text];
|
|
[ReadmeText scrollRangeToVisible:range];
|
|
return [self doPage:@"Readme" title:title canBack:canBack canFwd:canFwd canCancel:true canFwdAtStart:canFwd shouldBlock:YES];
|
|
} // doReadme
|
|
|
|
- (void)setOptionTreeSensitivity:(MojoGuiSetupOptions *)opts enabled:(boolean)val
|
|
{
|
|
if (opts != nil)
|
|
{
|
|
[((id) opts->guiopaque) setEnabled:(val ? YES : NO)];
|
|
[self setOptionTreeSensitivity:opts->next_sibling enabled:val];
|
|
[self setOptionTreeSensitivity:opts->child enabled:(val && opts->value)];
|
|
} // if
|
|
} // setOptionTreeSensitivity
|
|
|
|
- (MojoGuiSetupOptions *)findMojoOption:(id)obj opt:(MojoGuiSetupOptions *)opt
|
|
{
|
|
// !!! FIXME: this is not ideal. How can we attach this pointer to
|
|
// !!! FIXME: the objects themselves so we don't have to walk a tree
|
|
// !!! FIXME: to find it on each action? The objects are controls
|
|
// !!! FIXME: and cells (distinct classes), and I don't control the
|
|
// !!! FIXME: creation of all of them (radio buttons).
|
|
// !!! FIXME: Alternately, let's just hold a hashtable to map
|
|
// !!! FIXME: objects to options without walking this tree.
|
|
if (opt == nil)
|
|
return nil;
|
|
|
|
MojoGuiSetupOptions *i;
|
|
for (i = opt; i != nil; i = i->next_sibling)
|
|
{
|
|
if (i->guiopaque == ((void *) obj))
|
|
return i;
|
|
MojoGuiSetupOptions *rc = [self findMojoOption:obj opt:i->child];
|
|
if (rc != nil)
|
|
return rc;
|
|
} // for
|
|
|
|
return [self findMojoOption:obj opt:opt->child];
|
|
} // findMojoOption
|
|
|
|
- (void)optionToggled:(id)toggle
|
|
{
|
|
MojoGuiSetupOptions *opts = [self findMojoOption:toggle opt:mojoOpts];
|
|
assert(opts != nil);
|
|
// !!! FIXME: cast is wrong. use a selector?
|
|
const boolean enabled = ([((NSControl*)toggle) isEnabled] == YES);
|
|
opts->value = enabled;
|
|
[self setOptionTreeSensitivity:opts->child enabled:enabled];
|
|
} // optionToggled
|
|
|
|
- (NSView *)newOptionLevel:(NSView *)box
|
|
{
|
|
NSRect frame = NSMakeRect(10, 10, 10, 10);
|
|
NSView *widget = [[NSView alloc] initWithFrame:frame];
|
|
[box addSubview:widget positioned:NSWindowBelow relativeTo:nil];
|
|
[widget release]; // (box) owns it now.
|
|
return widget;
|
|
} // newOptionLevel
|
|
|
|
// !!! FIXME: most of this mess is cut, pasted, and Cocoaized from the
|
|
// !!! FIXME: GTK+ GUI. Can we abstract this in the high level and just
|
|
// !!! FIXME: implement the target-specific bits in the plugins?
|
|
- (void)buildOptions:(MojoGuiSetupOptions *)opts view:(NSView *)box sensitive:(boolean)sensitive
|
|
{
|
|
NSRect frame = NSMakeRect(10, 10, 10, 10);
|
|
if (opts != nil)
|
|
{
|
|
if (opts->is_group_parent)
|
|
{
|
|
MojoGuiSetupOptions *kids = opts->child;
|
|
NSView *childbox = nil;
|
|
//GtkWidget *alignment = gtk_alignment_new(0.0, 0.5, 0, 0);
|
|
//gtk_widget_show(alignment);
|
|
|
|
// !!! FIXME: disable line wrap?
|
|
// !!! FIXME: resize on superview resize?
|
|
NSTextField *widget = [[NSTextField alloc] initWithFrame:frame];
|
|
[widget setStringValue:[NSString stringWithUTF8String:opts->description]];
|
|
[widget setEnabled:(sensitive ? YES : NO)];
|
|
[widget setEditable:NO];
|
|
[widget setSelectable:NO];
|
|
[widget setBordered:NO];
|
|
[widget setBezeled:NO];
|
|
[widget setAlignment:NSLeftTextAlignment];
|
|
[widget sizeToFit];
|
|
if (opts->tooltip != nil)
|
|
[widget setToolTip:[NSString stringWithUTF8String:opts->tooltip]];
|
|
[box addSubview:widget positioned:NSWindowBelow relativeTo:nil];
|
|
[widget release]; // (box) owns it now.
|
|
//!!! FIXME[box sizeToFit];
|
|
|
|
childbox = [self newOptionLevel:box];
|
|
NSButtonCell *prototype = [[NSButtonCell alloc] init];
|
|
[prototype setButtonType:NSRadioButton];
|
|
[prototype setAllowsMixedState:NO];
|
|
NSMatrix *matrix = [[NSMatrix alloc] initWithFrame:frame mode:NSRadioModeMatrix prototype:(NSCell *)prototype numberOfRows:0 numberOfColumns:1];
|
|
int row = 0;
|
|
while (kids)
|
|
{
|
|
[matrix addRow];
|
|
NSButtonCell *cell = (NSButtonCell *) [matrix cellAtRow:row column:0];
|
|
kids->guiopaque = cell;
|
|
[cell setTitle:[NSString stringWithUTF8String:kids->description]];
|
|
[matrix setState:(kids->value ? NSOnState : NSOffState) atRow:row column:0];
|
|
[cell setEnabled:(kids->value ? YES : NO)];
|
|
[cell setTarget:self];
|
|
[cell setAction:@selector(optionToggled:)];
|
|
|
|
if (kids->tooltip != nil)
|
|
[matrix setToolTip:[NSString stringWithUTF8String:kids->tooltip] forCell:cell];
|
|
|
|
if (kids->child != nil)
|
|
[self buildOptions:kids->child view:[self newOptionLevel:childbox] sensitive:sensitive];
|
|
|
|
kids = kids->next_sibling;
|
|
row++;
|
|
} // while
|
|
|
|
[matrix sizeToCells];
|
|
[childbox addSubview:matrix positioned:NSWindowBelow relativeTo:nil];
|
|
[matrix release]; // childbox owns it now.
|
|
//!!! FIXME: [childbox sizeToFit];
|
|
//!!! FIXME: [[childbox superview] sizeToFit];
|
|
} // if
|
|
|
|
else
|
|
{
|
|
NSButton *widget = [[NSButton alloc] initWithFrame:frame];
|
|
opts->guiopaque = widget;
|
|
[widget setAllowsMixedState:NO];
|
|
[widget setTitle:[NSString stringWithUTF8String:opts->description]];
|
|
[widget setState:(opts->value ? NSOnState : NSOffState)];
|
|
[widget setEnabled:(sensitive ? YES : NO)];
|
|
[widget setTarget:self];
|
|
[widget setAction:@selector(optionToggled:)];
|
|
[box addSubview:widget positioned:NSWindowBelow relativeTo:nil];
|
|
[widget release]; // (box) owns it now.
|
|
//!!!FIXME:[box sizeToFit];
|
|
|
|
if (opts->tooltip != nil)
|
|
[widget setToolTip:[NSString stringWithUTF8String:opts->tooltip]];
|
|
|
|
if (opts->child != nil)
|
|
[self buildOptions:opts->child view:[self newOptionLevel:box] sensitive:((sensitive) && (opts->value))];
|
|
} // else
|
|
|
|
[self buildOptions:opts->next_sibling view:box sensitive:sensitive];
|
|
} // if
|
|
|
|
//!!! FIXME:[box sizeToFit];
|
|
} // buildOptions
|
|
|
|
- (int)doOptions:(MojoGuiSetupOptions *)opts canBack:(boolean)canBack canFwd:(boolean)canFwd
|
|
{
|
|
// add all the option widgets to the page's view.
|
|
[self buildOptions:opts view:OptionsView sensitive:true];
|
|
|
|
// run the page.
|
|
mojoOpts = opts;
|
|
int retval = [self doPage:@"Options" title:_("Options") canBack:canBack canFwd:canFwd canCancel:true canFwdAtStart:canFwd shouldBlock:YES];
|
|
mojoOpts = nil;
|
|
|
|
// we're done, so nuke everything from the view.
|
|
NSArray *array = [[OptionsView subviews] copy];
|
|
NSEnumerator *enumerator = [array objectEnumerator];
|
|
NSView *obj;
|
|
while ((obj = (NSView *) [enumerator nextObject]) != nil)
|
|
[obj removeFromSuperviewWithoutNeedingDisplay];
|
|
[OptionsView setNeedsDisplay:YES];
|
|
[enumerator release];
|
|
[array release];
|
|
|
|
return retval;
|
|
} // doOptions
|
|
|
|
- (char *)doDestination:(const char **)recommends recnum:(int)recnum command:(int *)command canBack:(boolean)canBack canFwd:(boolean)canFwd
|
|
{
|
|
const boolean fwdAtStart = ( (recnum > 0) && (*(recommends[0])) );
|
|
int i;
|
|
|
|
[DestinationCombo removeAllItems];
|
|
for (i = 0; i < recnum; i++)
|
|
[DestinationCombo addItemWithObjectValue:[NSString stringWithUTF8String:recommends[i]]];
|
|
|
|
if (recnum > 0)
|
|
[DestinationCombo setStringValue:[NSString stringWithUTF8String:recommends[0]]];
|
|
else
|
|
[DestinationCombo setStringValue:@""];
|
|
|
|
*command = [self doPage:@"Destination" title:_("Destination") canBack:canBack canFwd:canFwd canCancel:true canFwdAtStart:fwdAtStart shouldBlock:YES];
|
|
char *retval = xstrdup([[DestinationCombo stringValue] UTF8String]);
|
|
[DestinationCombo removeAllItems];
|
|
[DestinationCombo setStringValue:@""];
|
|
return retval;
|
|
} // doDestination
|
|
|
|
- (int)doProductKey:(const char *)desc fmt:(const char *)fmt buf:(char *)buf buflen:(const int)buflen canBack:(boolean)canBack canFwd:(boolean)canFwd
|
|
{
|
|
// !!! FIXME: write me!
|
|
return [self doPage:@"ProductKey" title:desc canBack:canBack canFwd:canFwd canCancel:true canFwdAtStart:canFwd shouldBlock:YES];
|
|
} // doProductKey
|
|
|
|
- (int)doProgress:(const char *)type component:(const char *)component percent:(int)percent item:(const char *)item canCancel:(boolean)canCancel
|
|
{
|
|
const BOOL indeterminate = (percent < 0) ? YES : NO;
|
|
[ProgressComponentLabel setStringValue:[NSString stringWithUTF8String:component]];
|
|
[ProgressItemLabel setStringValue:[NSString stringWithUTF8String:item]];
|
|
[ProgressBar setIndeterminate:indeterminate];
|
|
if (!indeterminate)
|
|
[ProgressBar setDoubleValue:(double)percent];
|
|
return [self doPage:@"Progress" title:type canBack:false canFwd:false canCancel:canCancel canFwdAtStart:false shouldBlock:NO];
|
|
} // doProgress
|
|
|
|
- (void)doFinal:(const char *)msg
|
|
{
|
|
finalPage = true;
|
|
[FinalText setStringValue:[NSString stringWithUTF8String:msg]];
|
|
[NextButton setTitle:[NSString stringWithUTF8String:_("Finish")]];
|
|
[self doPage:@"Final" title:_("Finish") canBack:false canFwd:true canCancel:false canFwdAtStart:true shouldBlock:YES];
|
|
} // doFinal
|
|
@end // implementation MojoSetupController
|
|
|
|
// Override [NSApplication sendEvent], so we can catch custom events.
|
|
@interface MojoSetupApplication : NSApplication
|
|
{
|
|
}
|
|
- (void)sendEvent:(NSEvent *)event;
|
|
@end // interface MojoSetupApplication
|
|
|
|
@implementation MojoSetupApplication
|
|
- (void)sendEvent:(NSEvent *)event
|
|
{
|
|
if ([event type] == NSApplicationDefined)
|
|
[((MojoSetupController *)[self delegate]) doCustomEvent:event];
|
|
[super sendEvent:event];
|
|
} // sendEvent
|
|
@end // implementation MojoSetupApplication
|
|
|
|
|
|
static uint8 MojoGui_cocoa_priority(boolean istty)
|
|
{
|
|
// obviously this is the thing you want on Mac OS X.
|
|
return MOJOGUI_PRIORITY_TRY_FIRST;
|
|
} // MojoGui_cocoa_priority
|
|
|
|
|
|
static boolean MojoGui_cocoa_init(void)
|
|
{
|
|
// This lets a stdio app become a GUI app. Otherwise, you won't get
|
|
// GUI events from the system and other things will fail to work.
|
|
// Putting the app in an application bundle does the same thing.
|
|
// TransformProcessType() is a 10.3+ API. SetFrontProcess() is 10.0+.
|
|
if (TransformProcessType != NULL) // check it as a weak symbol.
|
|
{
|
|
ProcessSerialNumber psn = { 0, kCurrentProcess };
|
|
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
|
|
SetFrontProcess(&psn);
|
|
} // if
|
|
|
|
GAutoreleasePool = [[NSAutoreleasePool alloc] init];
|
|
|
|
// !!! FIXME: make sure we have access to the desktop...may be ssh'd in
|
|
// !!! FIXME: as another user that doesn't have the Finder loaded or
|
|
// !!! FIXME: something.
|
|
|
|
// For NSApp to be our subclass, instead of default NSApplication.
|
|
[MojoSetupApplication sharedApplication];
|
|
if ([NSBundle loadNibNamed:@"MojoSetup" owner:NSApp] == NO)
|
|
return false;
|
|
|
|
// Force NSApp initialization stuff. MojoSetupController is set, in the
|
|
// .nib, to be NSApp's delegate. Its applicationDidFinishLaunching calls
|
|
// [NSApp stop] to break event loop right away so we can continue.
|
|
[NSApp run];
|
|
|
|
return true; // always succeeds.
|
|
} // MojoGui_cocoa_init
|
|
|
|
|
|
static void MojoGui_cocoa_deinit(void)
|
|
{
|
|
[GAutoreleasePool release];
|
|
GAutoreleasePool = nil;
|
|
// !!! FIXME: destroy nib and NSApp?
|
|
} // MojoGui_cocoa_deinit
|
|
|
|
|
|
static void MojoGui_cocoa_msgbox(const char *title, const char *text)
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
[[NSApp delegate] fireCustomEvent:CUSTOMEVENT_MSGBOX data1:(NSInteger)title data2:(NSInteger)text atStart:YES];
|
|
[pool release];
|
|
} // MojoGui_cocoa_msgbox
|
|
|
|
|
|
static boolean MojoGui_cocoa_promptyn(const char *title, const char *text,
|
|
boolean defval)
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
[[NSApp delegate] fireCustomEvent:CUSTOMEVENT_PROMPTYN data1:(NSInteger)title data2:(NSInteger)text atStart:YES];
|
|
const MojoGuiYNAN ynan = [[NSApp delegate] getAnswerYNAN];
|
|
[pool release];
|
|
assert((ynan == MOJOGUI_YES) || (ynan == MOJOGUI_NO));
|
|
return (ynan == MOJOGUI_YES);
|
|
} // MojoGui_cocoa_promptyn
|
|
|
|
|
|
static MojoGuiYNAN MojoGui_cocoa_promptynan(const char *title,
|
|
const char *text,
|
|
boolean defval)
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
[[NSApp delegate] fireCustomEvent:CUSTOMEVENT_PROMPTYNAN data1:(NSInteger)title data2:(NSInteger)text atStart:YES];
|
|
const MojoGuiYNAN retval = [[NSApp delegate] getAnswerYNAN];
|
|
[pool release];
|
|
return retval;
|
|
} // MojoGui_cocoa_promptynan
|
|
|
|
|
|
static boolean MojoGui_cocoa_start(const char *title,
|
|
const MojoGuiSplash *splash)
|
|
{
|
|
printf("start\n");
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
// !!! FIXME: deal with (splash).
|
|
[[NSApp delegate] prepareWidgets:title];
|
|
[pool release];
|
|
return true;
|
|
} // MojoGui_cocoa_start
|
|
|
|
|
|
static void MojoGui_cocoa_stop(void)
|
|
{
|
|
printf("stop\n");
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
[[NSApp delegate] unprepareWidgets];
|
|
[pool release];
|
|
} // MojoGui_cocoa_stop
|
|
|
|
|
|
static int MojoGui_cocoa_readme(const char *name, const uint8 *data,
|
|
size_t len, boolean can_back,
|
|
boolean can_fwd)
|
|
{
|
|
printf("readme\n");
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
NSString *str = [[[NSString alloc] initWithBytes:data length:len encoding:NSUTF8StringEncoding] autorelease];
|
|
const int retval = [[NSApp delegate] doReadme:name text:str canBack:can_back canFwd:can_fwd];
|
|
[pool release];
|
|
return retval;
|
|
} // MojoGui_cocoa_readme
|
|
|
|
|
|
static int MojoGui_cocoa_options(MojoGuiSetupOptions *opts,
|
|
boolean can_back, boolean can_fwd)
|
|
{
|
|
printf("options\n");
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
const int retval = [[NSApp delegate] doOptions:opts canBack:can_back canFwd:can_fwd];
|
|
[pool release];
|
|
return retval;
|
|
} // MojoGui_cocoa_options
|
|
|
|
|
|
static char *MojoGui_cocoa_destination(const char **recommends, int recnum,
|
|
int *command, boolean can_back,
|
|
boolean can_fwd)
|
|
{
|
|
printf("destination\n");
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
char *retval = [[NSApp delegate] doDestination:recommends recnum:recnum command:command canBack:can_back canFwd:can_fwd];
|
|
[pool release];
|
|
return retval;
|
|
} // MojoGui_cocoa_destination
|
|
|
|
|
|
static int MojoGui_cocoa_productkey(const char *desc, const char *fmt,
|
|
char *buf, const int buflen,
|
|
boolean can_back, boolean can_fwd)
|
|
{
|
|
printf("productkey\n");
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
const int retval = [[NSApp delegate] doProductKey:desc fmt:fmt buf:buf buflen:buflen canBack:can_back canFwd:can_fwd];
|
|
[pool release];
|
|
return retval;
|
|
} // MojoGui_cocoa_productkey
|
|
|
|
|
|
static boolean MojoGui_cocoa_insertmedia(const char *medianame)
|
|
{
|
|
printf("insertmedia\n");
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
[[NSApp delegate] fireCustomEvent:CUSTOMEVENT_INSERTMEDIA data1:(NSInteger)medianame data2:0 atStart:YES];
|
|
const MojoGuiYNAN ynan = [[NSApp delegate] getAnswerYNAN];
|
|
assert((ynan == MOJOGUI_YES) || (ynan == MOJOGUI_NO));
|
|
[pool release];
|
|
return (ynan == MOJOGUI_YES);
|
|
} // MojoGui_cocoa_insertmedia
|
|
|
|
|
|
static void MojoGui_cocoa_progressitem(void)
|
|
{
|
|
printf("progressitem\n");
|
|
// no-op in this UI target.
|
|
} // MojoGui_cocoa_progressitem
|
|
|
|
|
|
static int MojoGui_cocoa_progress(const char *type, const char *component,
|
|
int percent, const char *item,
|
|
boolean can_cancel)
|
|
{
|
|
printf("progress\n");
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
const int retval = [[NSApp delegate] doProgress:type component:component percent:percent item:item canCancel:can_cancel];
|
|
[pool release];
|
|
return retval;
|
|
} // MojoGui_cocoa_progress
|
|
|
|
|
|
static void MojoGui_cocoa_final(const char *msg)
|
|
{
|
|
printf("final\n");
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
[[NSApp delegate] doFinal:msg];
|
|
[pool release];
|
|
} // MojoGui_cocoa_final
|
|
|
|
// end of gui_cocoa.m ...
|
|
|