1/*   SDLMain.m - main entry point for our Cocoa-ized SDL app
2Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
3Non-NIB-Code & other changes: Max Horn <max@quendi.de>
4
5Feel free to customize this file to suit your needs
6*/
7
8#import "SDL.h"
9#import "SDLMain.h"
10#import <sys/param.h> /* for MAXPATHLEN */
11#import <unistd.h>
12
13/* Use this flag to determine whether we use SDLMain.nib or not */
14#define		SDL_USE_NIB_FILE	1
15
16
17static int    gArgc;
18static char  **gArgv;
19static BOOL   gFinderLaunch;
20
21#if SDL_USE_NIB_FILE
22/* A helper category for NSString */
23@interface NSString (ReplaceSubString)
24- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
25@end
26#else
27/* An internal Apple class used to setup Apple menus */
28@interface NSAppleMenuController:NSObject {}
29- (void)controlMenu:(NSMenu *)aMenu;
30@end
31#endif
32
33@interface SDLApplication : NSApplication
34@end
35
36@implementation SDLApplication
37/* Invoked from the Quit menu item */
38- (void)terminate:(id)sender
39{
40    /* Post a SDL_QUIT event */
41    SDL_Event event;
42    event.type = SDL_QUIT;
43    SDL_PushEvent(&event);
44}
45@end
46
47
48/* The main class of the application, the application's delegate */
49@implementation SDLMain
50
51- (IBAction)prefsMenu:(id)sender
52{
53    printf ("prefs menu\n");
54}
55
56- (IBAction)newGame:(id)sender
57{
58    printf ("new game\n");
59
60    NSRunAlertPanel (@"Get ready to blow up some... stuff!",
61							@"Click OK to begin total carnage. Click Cancel to prevent total carnage.", 	        		@"OK", @"Cancel", nil);
62}
63
64- (IBAction)openGame:(id)sender
65{
66    NSString *path = nil;
67    NSOpenPanel *openPanel = [ NSOpenPanel openPanel ];
68
69    if ( [ openPanel runModalForDirectory:nil
70												 file:@"SavedGame" types:nil ] ) {
71
72        path = [ [ openPanel filenames ] objectAtIndex:0 ];
73    }
74
75    printf ("open game: %s\n", [ path cString ]);
76}
77
78- (IBAction)saveGame:(id)sender
79{
80    NSString *path = nil;
81    NSSavePanel *savePanel = [ NSSavePanel savePanel ];
82
83    if ( [ savePanel runModalForDirectory:nil
84												 file:@"SaveGameFile" ] ) {
85
86        path = [ savePanel filename ];
87    }
88
89    printf ("save game: %s\n", [ path cString ]);
90}
91
92- (IBAction)saveGameAs:(id)sender
93{
94    printf ("save game as\n");
95}
96
97- (IBAction)help:(id)sender
98{
99    NSRunAlertPanel (@"Oh help, where have ye gone?",
100							@"Sorry, there is no help available.\n\nThis message brought to you by We Don't Document, Inc.\n\n", @"Rats", @"Good, I never read it anyway", nil);
101}
102
103
104/* Set the working directory to the .app's parent directory */
105- (void) setupWorkingDirectory:(BOOL)shouldChdir
106{
107    char parentdir[MAXPATHLEN];
108    char *c;
109
110    strncpy ( parentdir, gArgv[0], sizeof(parentdir) );
111    c = (char*) parentdir;
112
113    while (*c != '\0')     /* go to end */
114			  c++;
115
116			  while (*c != '/')      /* back up to parent */
117				c--;
118
119				*c++ = '\0';             /* cut off last part (binary name) */
120
121				if (shouldChdir)
122				{
123					 assert ( chdir (parentdir) == 0 );   /* chdir to the binary app's parent */
124					 assert ( chdir ("../../../") == 0 ); /* chdir to the .app's parent */
125				}
126}
127
128#if SDL_USE_NIB_FILE
129
130/* Fix menu to contain the real app name instead of "SDL App" */
131- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
132{
133    NSRange aRange;
134    NSEnumerator *enumerator;
135    NSMenuItem *menuItem;
136
137    aRange = [[aMenu title] rangeOfString:@"SDL App"];
138    if (aRange.length != 0)
139        [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
140
141    enumerator = [[aMenu itemArray] objectEnumerator];
142    while ((menuItem = [enumerator nextObject]))
143    {
144        aRange = [[menuItem title] rangeOfString:@"SDL App"];
145        if (aRange.length != 0)
146            [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
147        if ([menuItem hasSubmenu])
148            [self fixMenu:[menuItem submenu] withAppName:appName];
149    }
150    [ aMenu sizeToFit ];
151}
152
153#else
154
155void setupAppleMenu(void)
156{
157    /* warning: this code is very odd */
158    NSAppleMenuController *appleMenuController;
159    NSMenu *appleMenu;
160    NSMenuItem *appleMenuItem;
161
162    appleMenuController = [[NSAppleMenuController alloc] init];
163    appleMenu = [[NSMenu alloc] initWithTitle:@""];
164    appleMenuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
165
166    [appleMenuItem setSubmenu:appleMenu];
167
168    /* yes, we do need to add it and then remove it --
169		  if you don't add it, it doesn't get displayed
170		  if you don't remove it, you have an extra, titleless item in the menubar
171		  when you remove it, it appears to stick around
172		  very, very odd */
173    [[NSApp mainMenu] addItem:appleMenuItem];
174    [appleMenuController controlMenu:appleMenu];
175    [[NSApp mainMenu] removeItem:appleMenuItem];
176    [appleMenu release];
177    [appleMenuItem release];
178}
179
180/* Create a window menu */
181void setupWindowMenu(void)
182{
183    NSMenu		*windowMenu;
184    NSMenuItem	*windowMenuItem;
185    NSMenuItem	*menuItem;
186
187
188    windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
189
190    /* "Minimize" item */
191    menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
192    [windowMenu addItem:menuItem];
193    [menuItem release];
194
195    /* Put menu into the menubar */
196    windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
197    [windowMenuItem setSubmenu:windowMenu];
198    [[NSApp mainMenu] addItem:windowMenuItem];
199
200    /* Tell the application object that this is now the window menu */
201    [NSApp setWindowsMenu:windowMenu];
202
203    /* Finally give up our references to the objects */
204    [windowMenu release];
205    [windowMenuItem release];
206}
207
208/* Replacement for NSApplicationMain */
209void CustomApplicationMain (argc, argv)
210{
211    NSAutoreleasePool	*pool = [[NSAutoreleasePool alloc] init];
212    SDLMain				*sdlMain;
213
214    /* Ensure the application object is initialised */
215    [SDLApplication sharedApplication];
216
217    /* Set up the menubar */
218    [NSApp setMainMenu:[[NSMenu alloc] init]];
219    setupAppleMenu();
220    setupWindowMenu();
221
222    /* Create SDLMain and make it the app delegate */
223    sdlMain = [[SDLMain alloc] init];
224    [NSApp setDelegate:sdlMain];
225
226    /* Start the main event loop */
227    [NSApp run];
228
229    [sdlMain release];
230    [pool release];
231}
232
233#endif
234
235/* Called when the internal event loop has just started running */
236- (void) applicationDidFinishLaunching: (NSNotification *) note
237{
238    int status;
239
240    /* Set the working directory to the .app's parent directory */
241    [self setupWorkingDirectory:gFinderLaunch];
242
243#if SDL_USE_NIB_FILE
244    /* Set the main menu to contain the real app name instead of "SDL App" */
245    [self fixMenu:[NSApp mainMenu] withAppName:[[NSProcessInfo processInfo] processName]];
246#endif
247
248    /* Hand off to main application code */
249    status = SDL_main (gArgc, gArgv);
250
251    /* We're done, thank you for playing */
252    exit(status);
253}
254@end
255
256
257@implementation NSString (ReplaceSubString)
258
259- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
260{
261    unsigned int bufferSize;
262    unsigned int selfLen = [self length];
263    unsigned int aStringLen = [aString length];
264    unichar *buffer;
265    NSRange localRange;
266    NSString *result;
267
268    bufferSize = selfLen + aStringLen - aRange.length;
269    buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
270
271    /* Get first part into buffer */
272    localRange.location = 0;
273    localRange.length = aRange.location;
274    [self getCharacters:buffer range:localRange];
275
276    /* Get middle part into buffer */
277    localRange.location = 0;
278    localRange.length = aStringLen;
279    [aString getCharacters:(buffer+aRange.location) range:localRange];
280
281    /* Get last part into buffer */
282    localRange.location = aRange.location + aRange.length;
283    localRange.length = selfLen - localRange.location;
284    [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
285
286    /* Build output string */
287    result = [NSString stringWithCharacters:buffer length:bufferSize];
288
289    NSDeallocateMemoryPages(buffer, bufferSize);
290
291    return result;
292}
293
294@end
295
296
297
298#ifdef main
299#  undef main
300#endif
301
302
303/* Main entry point to executable - should *not* be SDL_main! */
304int main (int argc, char **argv)
305{
306
307    /* Copy the arguments into a global variable */
308    int i;
309
310    /* This is passed if we are launched by double-clicking */
311    if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
312        gArgc = 1;
313		  gFinderLaunch = YES;
314    } else {
315        gArgc = argc;
316		  gFinderLaunch = NO;
317    }
318    gArgv = (char**) malloc (sizeof(*gArgv) * (gArgc+1));
319    assert (gArgv != NULL);
320    for (i = 0; i < gArgc; i++)
321        gArgv[i] = argv[i];
322    gArgv[i] = NULL;
323
324#if SDL_USE_NIB_FILE
325    [SDLApplication poseAsClass:[NSApplication class]];
326    NSApplicationMain (argc, argv);
327#else
328    CustomApplicationMain (argc, argv);
329#endif
330    return 0;
331}
332