nkeynes@964 | 1 | /**
|
nkeynes@964 | 2 | * $Id$
|
nkeynes@964 | 3 | *
|
nkeynes@964 | 4 | * Construct and maintain the main window under cocoa.
|
nkeynes@964 | 5 | *
|
nkeynes@964 | 6 | * Copyright (c) 2008 Nathan Keynes.
|
nkeynes@964 | 7 | *
|
nkeynes@964 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@964 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@964 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@964 | 11 | * (at your option) any later version.
|
nkeynes@964 | 12 | *
|
nkeynes@964 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@964 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@964 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@964 | 16 | * GNU General Public License for more details.
|
nkeynes@964 | 17 | */
|
nkeynes@964 | 18 |
|
nkeynes@964 | 19 | #include "cocoaui/cocoaui.h"
|
nkeynes@964 | 20 | #include "lxdream.h"
|
nkeynes@964 | 21 | #include "dreamcast.h"
|
nkeynes@964 | 22 | #include "gdrom/gdrom.h"
|
nkeynes@964 | 23 | #include <ApplicationServices/ApplicationServices.h>
|
nkeynes@1028 | 24 | #include <Carbon/Carbon.h>
|
nkeynes@964 | 25 |
|
nkeynes@964 | 26 | @interface NSWindow (OSX10_5_and_later)
|
nkeynes@964 | 27 | #ifndef CGFLOAT_DEFINED
|
nkeynes@964 | 28 | # ifdef __LP64__
|
nkeynes@964 | 29 | typedef double CGFloat;
|
nkeynes@964 | 30 | # else
|
nkeynes@964 | 31 | typedef float CGFloat;
|
nkeynes@964 | 32 | # endif
|
nkeynes@964 | 33 | #endif
|
nkeynes@964 | 34 | - (void)setAutorecalculatesContentBorderThickness:(BOOL)b forEdge:(NSRectEdge)e;
|
nkeynes@964 | 35 | - (void)setContentBorderThickness:(CGFloat)b forEdge:(NSRectEdge)e;
|
nkeynes@964 | 36 | @end
|
nkeynes@964 | 37 |
|
nkeynes@964 | 38 |
|
nkeynes@964 | 39 | #if NSAppKitVersionNumber > NSAppKitVersionNumber10_4
|
nkeynes@964 | 40 |
|
nkeynes@964 | 41 | #endif
|
nkeynes@964 | 42 |
|
nkeynes@964 | 43 | #define STATUSBAR_HEIGHT 25
|
nkeynes@964 | 44 | #define STATUS_TEXT_HEIGHT 22
|
nkeynes@964 | 45 |
|
nkeynes@964 | 46 | @interface LxdreamToolbarDelegate : NSObject {
|
nkeynes@964 | 47 | NSArray *identifiers;
|
nkeynes@964 | 48 | NSArray *defaults;
|
nkeynes@964 | 49 | NSDictionary *items;
|
nkeynes@964 | 50 | }
|
nkeynes@964 | 51 | - (NSToolbarItem *) createToolbarItem: (NSString *)id label: (NSString *) label
|
nkeynes@964 | 52 | tooltip: (NSString *)tooltip
|
nkeynes@964 | 53 | icon: (NSString *)icon action: (SEL) action;
|
nkeynes@964 | 54 | @end
|
nkeynes@964 | 55 |
|
nkeynes@964 | 56 | @implementation LxdreamToolbarDelegate
|
nkeynes@964 | 57 | - (id) init
|
nkeynes@964 | 58 | {
|
nkeynes@964 | 59 | NSToolbarItem *mount = [self createToolbarItem: @"GdromMount" label: @"Open Image"
|
nkeynes@964 | 60 | tooltip: @"Mount a cdrom disc" icon: @"tb-cdrom"
|
nkeynes@964 | 61 | action: @selector(mount_action:)];
|
nkeynes@964 | 62 | NSToolbarItem *reset = [self createToolbarItem: @"Reset" label: @"Reset"
|
nkeynes@964 | 63 | tooltip: @"Reset dreamcast" icon: @"tb-reset"
|
nkeynes@964 | 64 | action: @selector(reset_action:)];
|
nkeynes@964 | 65 | NSToolbarItem *pause = [self createToolbarItem: @"Pause" label: @"Pause"
|
nkeynes@964 | 66 | tooltip: @"Pause dreamcast" icon: @"tb-pause"
|
nkeynes@964 | 67 | action: @selector(pause_action:)];
|
nkeynes@964 | 68 | NSToolbarItem *run = [self createToolbarItem: @"Run" label: @"Resume"
|
nkeynes@964 | 69 | tooltip: @"Resume" icon: @"tb-run"
|
nkeynes@964 | 70 | action: @selector(run_action:)];
|
nkeynes@964 | 71 | NSToolbarItem *load = [self createToolbarItem: @"LoadState" label: @"Load State..."
|
nkeynes@964 | 72 | tooltip: @"Load an lxdream save state" icon: @"tb-load"
|
nkeynes@964 | 73 | action: @selector(load_action:)];
|
nkeynes@964 | 74 | NSToolbarItem *save = [self createToolbarItem: @"SaveState" label: @"Save State..."
|
nkeynes@964 | 75 | tooltip: @"Create an lxdream save state" icon: @"tb-save"
|
nkeynes@964 | 76 | action: @selector(save_action:)];
|
nkeynes@964 | 77 | NSToolbarItem *prefs = [self createToolbarItem: @"Preferences" label: @"Preferences..."
|
nkeynes@964 | 78 | tooltip: @"Edit preferences" icon: @"tb-preferences"
|
nkeynes@964 | 79 | action: @selector(preferences_action:)];
|
nkeynes@964 | 80 | [pause setEnabled: NO];
|
nkeynes@964 | 81 | identifiers =
|
nkeynes@964 | 82 | [NSArray arrayWithObjects: @"GdromMount", @"Reset", @"Pause", @"Run", @"LoadState", @"SaveState", @"Preferences", nil ];
|
nkeynes@964 | 83 | defaults =
|
nkeynes@964 | 84 | [NSArray arrayWithObjects: @"GdromMount", @"Reset", @"Pause", @"Run",
|
nkeynes@964 | 85 | NSToolbarSeparatorItemIdentifier, @"LoadState", @"SaveState",
|
nkeynes@964 | 86 | NSToolbarFlexibleSpaceItemIdentifier, @"Preferences", nil ];
|
nkeynes@964 | 87 | NSArray *values = [NSArray arrayWithObjects: mount, reset, pause, run, load, save, prefs, nil ];
|
nkeynes@964 | 88 | items = [NSDictionary dictionaryWithObjects: values forKeys: identifiers];
|
nkeynes@964 | 89 | return self;
|
nkeynes@964 | 90 | }
|
nkeynes@964 | 91 |
|
nkeynes@964 | 92 | - (NSToolbarItem *) createToolbarItem: (NSString *)id label: (NSString *) label
|
nkeynes@964 | 93 | tooltip: (NSString *)tooltip icon: (NSString *)icon action: (SEL) action
|
nkeynes@964 | 94 | {
|
nkeynes@964 | 95 | NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier: id];
|
nkeynes@964 | 96 | [item setLabel: label];
|
nkeynes@964 | 97 | [item setToolTip: tooltip];
|
nkeynes@964 | 98 | [item setTarget: [NSApp delegate]];
|
nkeynes@964 | 99 | NSString *iconFile = [[NSBundle mainBundle] pathForResource:icon ofType:@"png"];
|
nkeynes@964 | 100 | NSImage *image = [[NSImage alloc] initWithContentsOfFile: iconFile];
|
nkeynes@964 | 101 | [item setImage: image];
|
nkeynes@964 | 102 | [item setAction: action];
|
nkeynes@964 | 103 | return item;
|
nkeynes@964 | 104 | }
|
nkeynes@964 | 105 |
|
nkeynes@964 | 106 | - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
|
nkeynes@964 | 107 | {
|
nkeynes@964 | 108 | return identifiers;
|
nkeynes@964 | 109 | }
|
nkeynes@964 | 110 |
|
nkeynes@964 | 111 | - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
|
nkeynes@964 | 112 | {
|
nkeynes@964 | 113 | return defaults;
|
nkeynes@964 | 114 | }
|
nkeynes@964 | 115 |
|
nkeynes@964 | 116 | - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar
|
nkeynes@964 | 117 | {
|
nkeynes@964 | 118 | return [NSArray arrayWithObjects: @"Pause", @"Run", nil];
|
nkeynes@964 | 119 | }
|
nkeynes@964 | 120 |
|
nkeynes@964 | 121 | - (NSToolbarItem *) toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier
|
nkeynes@964 | 122 | willBeInsertedIntoToolbar:(BOOL)flag
|
nkeynes@964 | 123 | {
|
nkeynes@964 | 124 | return [items objectForKey: itemIdentifier];
|
nkeynes@964 | 125 | }
|
nkeynes@964 | 126 | @end
|
nkeynes@964 | 127 |
|
nkeynes@964 | 128 | @implementation LxdreamMainWindow
|
nkeynes@964 | 129 | - (id)initWithContentRect:(NSRect)videoRect
|
nkeynes@964 | 130 | {
|
nkeynes@964 | 131 | NSRect contentRect = NSMakeRect(videoRect.origin.x,videoRect.origin.y,
|
nkeynes@964 | 132 | videoRect.size.width,videoRect.size.height+STATUSBAR_HEIGHT);
|
nkeynes@964 | 133 | if( [super initWithContentRect: contentRect
|
nkeynes@964 | 134 | styleMask: ( NSTitledWindowMask | NSClosableWindowMask |
|
nkeynes@964 | 135 | NSMiniaturizableWindowMask | NSResizableWindowMask |
|
nkeynes@964 | 136 | NSUnifiedTitleAndToolbarWindowMask )
|
nkeynes@964 | 137 | backing: NSBackingStoreBuffered defer: NO ] == nil ) {
|
nkeynes@964 | 138 | return nil;
|
nkeynes@964 | 139 | } else {
|
nkeynes@964 | 140 | useGrab = NO;
|
nkeynes@964 | 141 | isGrabbed = NO;
|
nkeynes@964 | 142 | video = (LxdreamVideoView *)video_osx_create_drawable();
|
nkeynes@964 | 143 | [video setFrameOrigin: NSMakePoint(0.0,STATUSBAR_HEIGHT)];
|
nkeynes@964 | 144 | [video setDelegate: self];
|
nkeynes@964 | 145 |
|
nkeynes@964 | 146 | status =
|
nkeynes@964 | 147 | [[NSTextField alloc] initWithFrame: NSMakeRect(0.0,0.0,videoRect.size.width,STATUS_TEXT_HEIGHT)];
|
nkeynes@964 | 148 | [status setStringValue: @"Idle"];
|
nkeynes@964 | 149 | [status setEditable: NO];
|
nkeynes@964 | 150 | [status setDrawsBackground: NO];
|
nkeynes@964 | 151 | [status setBordered: NO];
|
nkeynes@964 | 152 | [[self contentView] addSubview: video];
|
nkeynes@964 | 153 | [[self contentView] addSubview: status];
|
nkeynes@964 | 154 | [self makeFirstResponder: video];
|
nkeynes@964 | 155 |
|
nkeynes@964 | 156 | if( [self respondsToSelector:@selector(setAutorecalculatesContentBorderThickness:forEdge:)] )
|
nkeynes@964 | 157 | [self setAutorecalculatesContentBorderThickness: NO forEdge: NSMinYEdge ];
|
nkeynes@964 | 158 | if( [self respondsToSelector:@selector(setContentBorderThickness:forEdge:)] )
|
nkeynes@964 | 159 | [self setContentBorderThickness: STATUSBAR_HEIGHT forEdge: NSMinYEdge];
|
nkeynes@964 | 160 |
|
nkeynes@964 | 161 | // Share the app delegate for the purposes of keeping it in one place
|
nkeynes@964 | 162 | [self setDelegate: [NSApp delegate]];
|
nkeynes@964 | 163 | [self setContentMinSize: contentRect.size];
|
nkeynes@964 | 164 | [self setAcceptsMouseMovedEvents: YES];
|
nkeynes@964 | 165 | [self updateTitle];
|
nkeynes@964 | 166 |
|
nkeynes@964 | 167 | NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier: @"LxdreamToolbar"];
|
nkeynes@964 | 168 | [toolbar setDelegate: [[LxdreamToolbarDelegate alloc] init]];
|
nkeynes@964 | 169 | [toolbar setDisplayMode: NSToolbarDisplayModeIconOnly];
|
nkeynes@964 | 170 | [toolbar setSizeMode: NSToolbarSizeModeSmall];
|
nkeynes@964 | 171 | [toolbar setSelectedItemIdentifier: @"Pause"];
|
nkeynes@964 | 172 | [self setToolbar: toolbar];
|
nkeynes@964 | 173 | return self;
|
nkeynes@964 | 174 | }
|
nkeynes@964 | 175 | }
|
nkeynes@964 | 176 |
|
nkeynes@964 | 177 | - (void)updateTitle
|
nkeynes@964 | 178 | {
|
nkeynes@964 | 179 | NSString *title;
|
nkeynes@964 | 180 | const char *disc_title = gdrom_get_current_disc_title();
|
nkeynes@964 | 181 | if( disc_title == NULL ) {
|
nkeynes@1098 | 182 | title = [NSString stringWithFormat: @"%s - <no disc>", lxdream_package_name];
|
nkeynes@964 | 183 | } else {
|
nkeynes@964 | 184 | title = [NSString stringWithFormat: @"%s - %s", lxdream_package_name, disc_title];
|
nkeynes@964 | 185 | }
|
nkeynes@964 | 186 | [self setTitle: title];
|
nkeynes@964 | 187 | }
|
nkeynes@964 | 188 |
|
nkeynes@964 | 189 | - (void)setStatusText: (const gchar *)text
|
nkeynes@964 | 190 | {
|
nkeynes@964 | 191 | if( isGrabbed ) {
|
nkeynes@964 | 192 | gchar buf[128];
|
nkeynes@964 | 193 | snprintf( buf, sizeof(buf), "%s %s", text, _("(Press <ctrl><alt> to release grab)") );
|
nkeynes@964 | 194 | NSString *s = [NSString stringWithUTF8String: buf];
|
nkeynes@964 | 195 | [status setStringValue: s];
|
nkeynes@964 | 196 | } else {
|
nkeynes@964 | 197 | NSString *s = [NSString stringWithUTF8String: text];
|
nkeynes@964 | 198 | [status setStringValue: s];
|
nkeynes@964 | 199 | }
|
nkeynes@964 | 200 | }
|
nkeynes@964 | 201 | - (void)setRunning:(BOOL)isRunning
|
nkeynes@964 | 202 | {
|
nkeynes@964 | 203 | if( isRunning ) {
|
nkeynes@964 | 204 | [[self toolbar] setSelectedItemIdentifier: @"Run"];
|
nkeynes@964 | 205 | [self setStatusText: _("Running")];
|
nkeynes@964 | 206 | } else {
|
nkeynes@964 | 207 | [[self toolbar] setSelectedItemIdentifier: @"Pause"];
|
nkeynes@964 | 208 | [self setStatusText: _("Stopped")];
|
nkeynes@964 | 209 | }
|
nkeynes@964 | 210 | }
|
nkeynes@1028 | 211 | - (BOOL)isFullscreen
|
nkeynes@1028 | 212 | {
|
nkeynes@1028 | 213 | return isFullscreen;
|
nkeynes@1028 | 214 | }
|
nkeynes@1028 | 215 | - (void)setFullscreen:(BOOL)full
|
nkeynes@1028 | 216 | {
|
nkeynes@1028 | 217 | if( full != isFullscreen ) {
|
nkeynes@1028 | 218 | isFullscreen = full;
|
nkeynes@1028 | 219 |
|
nkeynes@1028 | 220 | if( full ) {
|
nkeynes@1028 | 221 | savedFrame = [self frame];
|
nkeynes@1028 | 222 | SetSystemUIMode( kUIModeAllHidden, 0 );
|
nkeynes@1028 | 223 | NSRect screenRect = [[NSScreen mainScreen] frame];
|
nkeynes@1028 | 224 | screenRect.size.height += STATUSBAR_HEIGHT;
|
nkeynes@1028 | 225 | screenRect.origin.y -= STATUSBAR_HEIGHT;
|
nkeynes@1028 | 226 | NSRect targetRect = [self frameRectForContentRect: screenRect];
|
nkeynes@1028 | 227 | [self setFrame: targetRect display: YES];
|
nkeynes@1028 | 228 | } else {
|
nkeynes@1028 | 229 | SetSystemUIMode( kUIModeNormal, 0 );
|
nkeynes@1028 | 230 | [self setFrame: savedFrame display: YES];
|
nkeynes@1028 | 231 | }
|
nkeynes@1028 | 232 | }
|
nkeynes@1028 | 233 | }
|
nkeynes@964 | 234 | - (BOOL)isGrabbed
|
nkeynes@964 | 235 | {
|
nkeynes@964 | 236 | return isGrabbed;
|
nkeynes@964 | 237 | }
|
nkeynes@964 | 238 | - (void)setIsGrabbed:(BOOL)grab
|
nkeynes@964 | 239 | {
|
nkeynes@964 | 240 | if( grab != isGrabbed ) {
|
nkeynes@964 | 241 | isGrabbed = grab;
|
nkeynes@964 | 242 | [self setRunning: dreamcast_is_running() ? YES : NO];
|
nkeynes@964 | 243 |
|
nkeynes@964 | 244 | if( isGrabbed ) {
|
nkeynes@964 | 245 | [NSCursor hide];
|
nkeynes@964 | 246 | CGAssociateMouseAndMouseCursorPosition(NO);
|
nkeynes@964 | 247 | } else {
|
nkeynes@964 | 248 | [NSCursor unhide];
|
nkeynes@964 | 249 | CGAssociateMouseAndMouseCursorPosition(YES);
|
nkeynes@964 | 250 | }
|
nkeynes@964 | 251 | [video setIsGrabbed: isGrabbed];
|
nkeynes@964 | 252 | }
|
nkeynes@964 | 253 | }
|
nkeynes@964 | 254 | - (void)setUseGrab:(BOOL)grab
|
nkeynes@964 | 255 | {
|
nkeynes@964 | 256 | if( grab != useGrab ) {
|
nkeynes@964 | 257 | if( !grab && isGrabbed ) {
|
nkeynes@964 | 258 | [self setIsGrabbed: NO];
|
nkeynes@964 | 259 | }
|
nkeynes@964 | 260 | useGrab = grab;
|
nkeynes@964 | 261 | }
|
nkeynes@964 | 262 | }
|
nkeynes@964 | 263 |
|
nkeynes@964 | 264 | - (id)viewRequestedGrab: (id)sender
|
nkeynes@964 | 265 | {
|
nkeynes@964 | 266 | if( useGrab ) {
|
nkeynes@964 | 267 | [self setIsGrabbed: YES];
|
nkeynes@964 | 268 | }
|
nkeynes@964 | 269 | return useGrab ? self : nil;
|
nkeynes@964 | 270 | }
|
nkeynes@964 | 271 | - (id)viewRequestedUngrab: (id)sender
|
nkeynes@964 | 272 | {
|
nkeynes@964 | 273 | [self setIsGrabbed: NO];
|
nkeynes@964 | 274 | return useGrab ? self : nil;
|
nkeynes@964 | 275 | }
|
nkeynes@1028 | 276 | - (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)aScreen
|
nkeynes@1028 | 277 | {
|
nkeynes@1028 | 278 | return frameRect;
|
nkeynes@1028 | 279 | }
|
nkeynes@964 | 280 | @end
|
nkeynes@964 | 281 |
|
nkeynes@964 | 282 | LxdreamMainWindow *cocoa_gui_create_main_window()
|
nkeynes@964 | 283 | {
|
nkeynes@964 | 284 | NSRect contentRect = {{0,0},{640,480}};
|
nkeynes@964 | 285 | return [[LxdreamMainWindow alloc] initWithContentRect: contentRect];
|
nkeynes@964 | 286 | }
|