Enable save/load of palettes (use extension .spal)
[synfig.git] / synfig-osx / launcher / X11Controller.m
1 /* X11Controller.m -- connect the IB ui, also the NSApp delegate
2    $Id: X11Controller.m,v 1.36 2003/07/24 17:52:29 jharper Exp $
3
4    Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
5
6    Permission is hereby granted, free of charge, to any person
7    obtaining a copy of this software and associated documentation files
8    (the "Software"), to deal in the Software without restriction,
9    including without limitation the rights to use, copy, modify, merge,
10    publish, distribute, sublicense, and/or sell copies of the Software,
11    and to permit persons to whom the Software is furnished to do so,
12    subject to the following conditions:
13
14    The above copyright notice and this permission notice shall be
15    included in all copies or substantial portions of the Software.
16
17    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20    NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
21    HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24    DEALINGS IN THE SOFTWARE.
25
26    Except as contained in this notice, the name(s) of the above
27    copyright holders shall not be used in advertising or otherwise to
28    promote the sale, use or other dealings in this Software without
29    prior written authorization. */
30 #define __DARWIN__
31
32 #import "X11Controller.h"
33 #import "X11Application.h"
34 #import <Carbon/Carbon.h>
35
36
37 /* ouch! */
38 #define BOOL X_BOOL
39 # include "Xproto.h"
40 #define WindowPtr X_WindowPtr
41 #define Cursor X_Cursor
42 # include "quartz.h"
43 # define _APPLEWM_SERVER_
44 # include "applewm.h"
45 # include "X.h"
46 #undef Cursor
47 #undef WindowPtr
48 #undef BOOL
49
50 #include <stdio.h>
51 #include <unistd.h>
52
53 #define TRACE() fprintf (stderr, "%s\n", __FUNCTION__)
54
55 @implementation X11Controller
56
57 - (void) awakeFromNib
58 {
59     X11Application *xapp = NSApp;
60     NSArray *array;
61
62     /* Point X11Application at ourself. */
63     [xapp set_controller:self];
64 #if 0
65     array = [xapp prefs_get_array:@PREFS_APPSMENU];
66     if (array != nil)
67     {
68         int count;
69
70         /* convert from [TITLE1 COMMAND1 TITLE2 COMMAND2 ...]
71            to [[TITLE1 COMMAND1] [TITLE2 COMMAND2] ...] format. */
72
73         count = [array count];
74         if (count > 0
75             && ![[array objectAtIndex:0] isKindOfClass:[NSArray class]])
76         {
77             int i;
78             NSMutableArray *copy, *sub;
79
80             copy = [NSMutableArray arrayWithCapacity:(count / 2)];
81
82             for (i = 0; i < count / 2; i++)
83             {
84                 sub = [[NSMutableArray alloc] initWithCapacity:3];
85                 [sub addObject:[array objectAtIndex:i*2]];
86                 [sub addObject:[array objectAtIndex:i*2+1]];
87                 [sub addObject:@""];
88                 [copy addObject:sub];
89                 [sub release];
90             }
91
92             array = copy;
93         }
94
95         [self set_apps_menu:array];
96     }
97         #endif
98 }
99
100 - (void) item_selected:sender
101 {
102     [NSApp activateIgnoringOtherApps:YES];
103
104     QuartzMessageMainThread (kXquartzControllerNotify, 2,
105                              AppleWMWindowMenuItem, [sender tag]);
106 }
107
108 - (void) remove_window_menu
109 {
110     NSMenu *menu;
111     int first, count, i;
112
113     /* Work backwards so we don't mess up the indices */
114     menu = [window_separator menu];
115     first = [menu indexOfItem:window_separator] + 1;
116     count = [menu numberOfItems];
117     for (i = count - 1; i >= first; i--)
118         [menu removeItemAtIndex:i];
119
120     menu = [dock_window_separator menu];
121     count = [menu indexOfItem:dock_window_separator];
122     for (i = 0; i < count; i++)
123         [dock_menu removeItemAtIndex:0];
124 }
125
126 - (void) install_window_menu:(NSArray *)list
127 {
128     NSMenu *menu;
129     NSMenuItem *item;
130     int first, count, i;
131
132     menu = [window_separator menu];
133     first = [menu indexOfItem:window_separator] + 1;
134     count = [list count];
135
136     for (i = 0; i < count; i++)
137     {
138         NSString *name, *shortcut;
139
140         name = [[list objectAtIndex:i] objectAtIndex:0];
141         shortcut = [[list objectAtIndex:i] objectAtIndex:1];
142
143         item = (NSMenuItem *) [menu addItemWithTitle:name action:@selector
144                                (item_selected:) keyEquivalent:shortcut];
145         [item setTarget:self];
146         [item setTag:i];
147         [item setEnabled:YES];
148
149         item = (NSMenuItem *) [dock_menu insertItemWithTitle:name
150                                action:@selector
151                                (item_selected:) keyEquivalent:shortcut
152                                atIndex:i];
153         [item setTarget:self];
154         [item setTag:i];
155         [item setEnabled:YES];
156     }
157
158     if (checked_window_item >= 0 && checked_window_item < count)
159     {
160         item = (NSMenuItem *) [menu itemAtIndex:first + checked_window_item];
161         [item setState:NSOnState];
162         item = (NSMenuItem *) [dock_menu itemAtIndex:checked_window_item];
163         [item setState:NSOnState];
164     }
165 }
166
167 - (void) remove_apps_menu
168 {
169     NSMenu *menu;
170     NSMenuItem *item;
171     int i;
172
173     if (apps == nil || apps_separator == nil)
174         return;
175
176     menu = [apps_separator menu];
177
178     if (menu != nil)
179     {
180         for (i = [menu numberOfItems] - 1; i >= 0; i--)
181         {
182             item = (NSMenuItem *) [menu itemAtIndex:i];
183             if ([item tag] != 0)
184                 [menu removeItemAtIndex:i];
185         }
186     }
187
188     if (dock_apps_menu != nil)
189     {
190         for (i = [dock_apps_menu numberOfItems] - 1; i >= 0; i--)
191         {
192             item = (NSMenuItem *) [dock_apps_menu itemAtIndex:i];
193             if ([item tag] != 0)
194                 [dock_apps_menu removeItemAtIndex:i];
195         }
196     }
197
198     [apps release];
199     apps = nil;
200 }
201
202 - (void) prepend_apps_item:(NSArray *)list index:(int)i menu:(NSMenu *)menu
203 {
204     NSString *title, *shortcut = @"";
205     NSArray *group;
206     NSMenuItem *item;
207
208     group = [list objectAtIndex:i];
209     title = [group objectAtIndex:0];
210     if ([group count] >= 3)
211         shortcut = [group objectAtIndex:2];
212
213     if ([title length] != 0)
214     {
215         item = (NSMenuItem *) [menu insertItemWithTitle:title
216                                action:@selector (app_selected:)
217                                keyEquivalent:shortcut atIndex:0];
218         [item setTarget:self];
219         [item setEnabled:YES];
220     }
221     else
222     {
223         item = (NSMenuItem *) [NSMenuItem separatorItem];
224         [menu insertItem:item atIndex:0];
225     }
226
227     [item setTag:i+1];                  /* can't be zero, so add one */
228 }
229
230 - (void) install_apps_menu:(NSArray *)list
231 {
232     NSMenu *menu;
233     int i, count;
234
235     count = [list count];
236
237     if (count == 0 || apps_separator == nil)
238         return;
239
240     menu = [apps_separator menu];
241
242     for (i = count - 1; i >= 0; i--)
243     {
244         if (menu != nil)
245             [self prepend_apps_item:list index:i menu:menu];
246         if (dock_apps_menu != nil)
247             [self prepend_apps_item:list index:i menu:dock_apps_menu];
248     }
249
250     apps = [list retain];
251 }
252
253 - (void) set_window_menu:(NSArray *)list
254 {
255     [self remove_window_menu];
256     [self install_window_menu:list];
257
258     QuartzMessageMainThread (kXquartzControllerNotify, 1,
259                              AppleWMWindowMenuNotify);
260 }
261
262 - (void) set_window_menu_check:(NSNumber *)nn
263 {
264     NSMenu *menu;
265     NSMenuItem *item;
266     int first, count;
267     int n = [nn intValue];
268
269     menu = [window_separator menu];
270     first = [menu indexOfItem:window_separator] + 1;
271     count = [menu numberOfItems] - first;
272
273     if (checked_window_item >= 0 && checked_window_item < count)
274     {
275         item = (NSMenuItem *) [menu itemAtIndex:first + checked_window_item];
276         [item setState:NSOffState];
277         item = (NSMenuItem *) [dock_menu itemAtIndex:checked_window_item];
278         [item setState:NSOffState];
279     }
280     if (n >= 0 && n < count)
281     {
282         item = (NSMenuItem *) [menu itemAtIndex:first + n];
283         [item setState:NSOnState];
284         item = (NSMenuItem *) [dock_menu itemAtIndex:n];
285         [item setState:NSOnState];
286     }
287     checked_window_item = n;
288 }
289
290 - (void) set_apps_menu:(NSArray *)list
291 {
292     [self remove_apps_menu];
293     [self install_apps_menu:list];
294 }
295
296 - (void) launch_client:(NSString *)command
297 {
298     QuartzRunClient ([command cString]);
299 }
300
301 - (void) app_selected:sender
302 {
303     int tag;
304     NSString *item;
305
306     tag = [sender tag] - 1;
307     if (apps == nil || tag < 0 || tag >= [apps count])
308         return;
309
310     item = [[apps objectAtIndex:tag] objectAtIndex:1];
311
312     [self launch_client:item];
313 }
314
315 - (IBAction) apps_table_show:sender
316 {
317     NSArray *columns;
318
319     if (table_apps == nil)
320     {
321         table_apps = [[NSMutableArray alloc] initWithCapacity:1];
322
323         if (apps != nil)
324             [table_apps addObjectsFromArray:apps];
325     }
326
327     columns = [apps_table tableColumns];
328     [[columns objectAtIndex:0] setIdentifier:@"0"];
329     [[columns objectAtIndex:1] setIdentifier:@"1"];
330     [[columns objectAtIndex:2] setIdentifier:@"2"];
331
332     [apps_table setDataSource:self];
333     [apps_table selectRow:0 byExtendingSelection:NO];
334
335     [[apps_table window] makeKeyAndOrderFront:sender];
336 }
337
338 - (IBAction) apps_table_cancel:sender
339 {
340     [[apps_table window] orderOut:sender];
341
342     [table_apps release];
343     table_apps = nil;
344 }
345
346 - (IBAction) apps_table_done:sender
347 {
348     [apps_table deselectAll:sender];    /* flush edits? */
349
350     [self remove_apps_menu];
351     [self install_apps_menu:table_apps];
352
353     [NSApp prefs_set_array:@PREFS_APPSMENU value:table_apps];
354     [NSApp prefs_synchronize];
355
356     [[apps_table window] orderOut:sender];
357
358     [table_apps release];
359     table_apps = nil;
360 }
361
362 - (IBAction) apps_table_new:sender
363 {
364     NSMutableArray *item;
365
366     int row = [apps_table selectedRow], i;
367
368     if (row < 0)
369         row = 0;
370     else
371         row = row + 1;
372
373     i = row;
374     if (i > [table_apps count])
375         return;                         /* avoid exceptions */
376
377     item = [[NSMutableArray alloc] initWithCapacity:3];
378     [item addObject:@""];
379     [item addObject:@""];
380     [item addObject:@""];
381
382     [table_apps insertObject:item atIndex:i];
383     [item release];
384
385     [apps_table noteNumberOfRowsChanged];
386     [apps_table selectRow:row byExtendingSelection:NO];
387 }
388
389 - (IBAction) apps_table_duplicate:sender
390 {
391     int row = [apps_table selectedRow], i;
392     NSObject *a;
393
394     if (row < 0)
395     {
396         [self apps_table_new:sender];
397         return;
398     }
399
400     i = row;
401     if (i > [table_apps count] - 1)
402         return;                         /* avoid exceptions */
403
404     a = [table_apps objectAtIndex:i];
405     [table_apps insertObject:[a copy] atIndex:i];
406
407     [apps_table noteNumberOfRowsChanged];
408     [apps_table selectRow:row+1 byExtendingSelection:NO];
409 }
410
411 - (IBAction) apps_table_delete:sender
412 {
413     int row = [apps_table selectedRow];
414
415     if (row >= 0)
416     {
417         int i = row;
418
419         if (i > [table_apps count] - 1)
420             return;                     /* avoid exceptions */
421
422         [table_apps removeObjectAtIndex:i];
423     }
424
425     [apps_table noteNumberOfRowsChanged];
426 }
427
428 - (int) numberOfRowsInTableView:(NSTableView *)tableView
429 {
430     if (table_apps == nil)
431         return 0;
432
433     return [table_apps count];
434 }
435
436 - (id) tableView:(NSTableView *)tableView
437        objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
438 {
439     NSArray *item;
440     int col;
441
442     if (table_apps == nil)
443         return nil;
444
445     col = [[tableColumn identifier] intValue];
446
447     item = [table_apps objectAtIndex:row];
448     if ([item count] > col)
449         return [item objectAtIndex:col];
450     else
451         return @"";
452 }
453
454 - (void) tableView:(NSTableView *)tableView setObjectValue:(id)object
455          forTableColumn:(NSTableColumn *)tableColumn row:(int)row
456 {
457     NSMutableArray *item;
458     int col;
459
460     if (table_apps == nil)
461         return;
462
463     col = [[tableColumn identifier] intValue];
464
465     item = [table_apps objectAtIndex:row];
466     [item replaceObjectAtIndex:col withObject:object];
467 }
468
469 - (void) hide_window:sender
470 {
471     if ([X11App x_active])
472         QuartzMessageMainThread (kXquartzControllerNotify, 1, AppleWMHideWindow);
473     else
474         NSBeep ();                      /* FIXME: something here */
475 }
476
477 - (IBAction)bring_to_front:sender
478 {
479     QuartzMessageMainThread (kXquartzControllerNotify, 1,
480                              AppleWMBringAllToFront);
481 }
482
483 - (IBAction)close_window:sender
484 {
485     if ([X11App x_active])
486         QuartzMessageMainThread (kXquartzControllerNotify, 1, AppleWMCloseWindow);
487     else
488         [[NSApp keyWindow] performClose:sender];
489 }
490
491 - (IBAction)minimize_window:sender
492 {
493     if ([X11App x_active])
494         QuartzMessageMainThread (kXquartzControllerNotify, 1, AppleWMMinimizeWindow);
495     else
496         [[NSApp keyWindow] performMiniaturize:sender];
497 }
498
499 - (IBAction)zoom_window:sender
500 {
501     if ([X11App x_active])
502         QuartzMessageMainThread (kXquartzControllerNotify, 1, AppleWMZoomWindow);
503     else
504         [[NSApp keyWindow] performZoom:sender];
505 }
506
507 - (IBAction) next_window:sender
508 {
509     QuartzMessageMainThread (kXquartzControllerNotify, 1, AppleWMNextWindow);
510 }
511
512 - (IBAction) previous_window:sender
513 {
514     QuartzMessageMainThread (kXquartzControllerNotify,
515                              1, AppleWMPreviousWindow);
516 }
517
518 - (IBAction) enable_fullscreen_changed:sender
519 {
520     int value = ![enable_fullscreen intValue];
521
522     QuartzMessageMainThread (kXquartzSetRootless, 1, value);
523
524     [NSApp prefs_set_boolean:@PREFS_ROOTLESS value:value];
525     [NSApp prefs_synchronize];
526 }
527
528 - (IBAction) toggle_fullscreen:sender
529 {
530     QuartzMessageMainThread (kXquartzToggleFullscreen, 0);
531 }
532
533 - (void) set_can_quit:(BOOL)state
534 {
535     can_quit = state;
536 }
537
538 - (IBAction)prefs_changed:sender
539 {
540     darwinFakeButtons = [fake_buttons intValue];
541     quartzUseSysBeep = [use_sysbeep intValue];
542     X11EnableKeyEquivalents = [enable_keyequivs intValue];
543     darwinSyncKeymap = [sync_keymap intValue];
544
545     /* after adding prefs here, also add to [X11Application read_defaults]
546        and below */
547
548     [NSApp prefs_set_boolean:@PREFS_FAKEBUTTONS value:darwinFakeButtons];
549     [NSApp prefs_set_boolean:@PREFS_SYSBEEP value:quartzUseSysBeep];
550     [NSApp prefs_set_boolean:@PREFS_KEYEQUIVS value:X11EnableKeyEquivalents];
551     [NSApp prefs_set_boolean:@PREFS_SYNC_KEYMAP value:darwinSyncKeymap];
552     [NSApp prefs_set_boolean:@PREFS_NO_AUTH value:![enable_auth intValue]];
553     [NSApp prefs_set_boolean:@PREFS_NO_TCP value:![enable_tcp intValue]];
554     [NSApp prefs_set_integer:@PREFS_DEPTH value:[depth selectedTag]];
555
556     [NSApp prefs_synchronize];
557 }
558
559 - (IBAction) prefs_show:sender
560 {
561     [fake_buttons setIntValue:darwinFakeButtons];
562     [use_sysbeep setIntValue:quartzUseSysBeep];
563     [enable_keyequivs setIntValue:X11EnableKeyEquivalents];
564     [sync_keymap setIntValue:darwinSyncKeymap];
565     [sync_keymap setEnabled:darwinKeymapFile == NULL];
566
567     [enable_auth setIntValue:![NSApp prefs_get_boolean:@PREFS_NO_AUTH default:NO]];
568     [enable_tcp setIntValue:![NSApp prefs_get_boolean:@PREFS_NO_TCP default:NO]];
569     [depth selectItemAtIndex:[depth indexOfItemWithTag:[NSApp prefs_get_integer:@PREFS_DEPTH default:-1]]];
570
571     [enable_fullscreen setIntValue:!quartzEnableRootless];
572
573     [prefs_panel makeKeyAndOrderFront:sender];
574 }
575
576 - (IBAction) quit:sender
577 {
578     QuartzMessageMainThread (kXdarwinQuit, 0);
579 }
580
581 - (IBAction) x11_help:sender
582 {
583     AHLookupAnchor (CFSTR ("Mac Help"), CFSTR ("mchlp2276"));
584 }
585
586 - (BOOL) validateMenuItem:(NSMenuItem *)item
587 {
588     NSMenu *menu = [item menu];
589
590     if (item == toggle_fullscreen_item)
591     {
592         return !quartzEnableRootless;
593     }
594     else if (menu == [window_separator menu] || menu == dock_menu
595              || (menu == [x11_about_item menu] && [item tag] == 42))
596     {
597         return (AppleWMSelectedEvents () & AppleWMControllerNotifyMask) != 0;
598     }
599     else
600     {
601         return TRUE;
602     }
603 }
604
605 - (void) applicationDidHide:(NSNotification *)notify
606 {
607     QuartzMessageMainThread (kXquartzControllerNotify, 1, AppleWMHideAll);
608 }
609
610 - (void) applicationDidUnhide:(NSNotification *)notify
611 {
612     QuartzMessageMainThread (kXquartzControllerNotify, 1, AppleWMShowAll);
613 }
614
615 - (NSApplicationTerminateReply) applicationShouldTerminate:sender
616 {
617     NSString *msg;
618
619     if (can_quit || [X11App prefs_get_boolean:@PREFS_NO_QUIT_ALERT default:NO])
620     {
621         return NSTerminateNow;
622     }
623
624     /* Make sure we're frontmost. */
625     [NSApp activateIgnoringOtherApps:YES];
626
627     msg = NSLocalizedString (@"Are you sure you want to quit X11?\n\n\
628 If you quit X11, any X11 applications you are running will stop immediately \
629 and you will lose any changes you have not saved.", @"");
630
631     /* FIXME: safe to run the alert in here? Or should we return Later
632        and then run the alert on a timer? It seems to work here, so.. */
633
634     return (NSRunAlertPanel (nil, msg, NSLocalizedString (@"Quit", @""),
635                              NSLocalizedString (@"Cancel", @""), nil)
636             == NSAlertDefaultReturn) ? NSTerminateNow : NSTerminateCancel;
637 }
638
639 - (void) applicationWillTerminate:(NSNotification *)aNotification
640 {
641     [X11App prefs_synchronize];
642
643     /* shutdown the X server, it will exit () for us. */
644     QuartzMessageMainThread (kXdarwinQuit, 0);
645
646     /* In case it doesn't, exit anyway after a while. */
647     while (sleep (10) != 0) ;
648     exit (1);
649 }
650
651 - (void) server_ready
652 {
653     x_list *node;
654
655     finished_launching = YES;
656
657     for (node = pending_apps; node != NULL; node = node->next)
658     {
659         NSString *filename = node->data;
660         QuartzRunClient ([filename UTF8String]);
661         [filename release];
662     }
663
664     x_list_free (pending_apps);
665     pending_apps = NULL;
666 }
667
668 - (BOOL) application:(NSApplication *)app openFile:(NSString *)filename
669 {
670 #if 0
671     const char *name = [filename UTF8String];
672
673     if (finished_launching)
674         QuartzRunClient (name);
675     else if (name[0] != ':')            /* ignore display names */
676         pending_apps = x_list_prepend (pending_apps, [filename retain]);
677
678     /* FIXME: report failures. */
679     return YES;
680 #endif
681         return NO;
682 }
683
684 @end
685
686 void X11ControllerMain (int argc, const char *argv[],
687                         void (*server_thread) (void *), void *server_arg)
688 {
689     X11ApplicationMain (argc, argv, server_thread, server_arg);
690 }