fix carbon GUI focus/re-exposure
[ardour.git] / gtk2_ardour / au_pluginui.mm
1 #undef  Marker
2 #define Marker FuckYouAppleAndYourLackOfNameSpaces
3
4 #include <gtkmm/button.h>
5 #include <gdk/gdkquartz.h>
6
7 #include "pbd/convert.h"
8 #include "pbd/error.h"
9
10 #include "ardour/audio_unit.h"
11 #include "ardour/debug.h"
12 #include "ardour/plugin_insert.h"
13
14 #undef check // stupid gtk, stupid apple
15
16 #include <gtkmm2ext/utils.h>
17
18 #include "au_pluginui.h"
19 #include "gui_thread.h"
20
21 #include "appleutility/CAAudioUnit.h"
22 #include "appleutility/CAComponent.h"
23
24 #import <AudioUnit/AUCocoaUIView.h>
25 #import <CoreAudioKit/AUGenericView.h>
26
27 #undef Marker
28
29 #include "keyboard.h"
30 #include "utils.h"
31 #include "public_editor.h"
32 #include "i18n.h"
33
34 using namespace ARDOUR;
35 using namespace Gtk;
36 using namespace Gtkmm2ext;
37 using namespace std;
38 using namespace PBD;
39
40 vector<string> AUPluginUI::automation_mode_strings;
41
42 static const gchar* _automation_mode_strings[] = {
43         X_("Manual"),
44         X_("Play"),
45         X_("Write"),
46         X_("Touch"),
47         0
48 };
49
50 static void
51 dump_view_tree (NSView* view, int depth)
52 {
53         NSArray* subviews = [view subviews];
54         unsigned long cnt = [subviews count];
55
56         for (int d = 0; d < depth; d++) {
57                 cerr << '\t';
58         }
59         NSRect frame = [view frame];
60         cerr << " view @ " <<  frame.origin.x << ", " << frame.origin.y
61              << ' ' << frame.size.width << " x " << frame.size.height
62              << endl;
63         
64         for (unsigned long i = 0; i < cnt; ++i) {
65                 NSView* subview = [subviews objectAtIndex:i];
66                 dump_view_tree (subview, depth+1);
67         }
68 }
69
70 @implementation NotificationObject
71
72 - (NotificationObject*) initWithPluginUI: (AUPluginUI*) apluginui andCocoaParent: (NSWindow*) cp andTopLevelParent: (NSWindow*) tlp
73 {
74         self = [ super init ];
75
76         if (self) {
77                 plugin_ui = apluginui; 
78                 top_level_parent = tlp;
79                 
80                 if (cp) {
81                         cocoa_parent = cp;
82                         
83                         [[NSNotificationCenter defaultCenter] addObserver:self
84                          selector:@selector(cocoaParentActivationHandler:)
85                          name:NSWindowDidBecomeMainNotification
86                          object:NULL];
87                         
88                         [[NSNotificationCenter defaultCenter] addObserver:self
89                          selector:@selector(cocoaParentBecameKeyHandler:)
90                          name:NSWindowDidBecomeKeyNotification
91                          object:NULL];
92                 }
93         }
94
95         return self;
96 }
97                 
98 - (void)cocoaParentActivationHandler:(NSNotification *)notification
99 {
100         NSWindow* notification_window = (NSWindow *)[notification object];
101
102         if (top_level_parent == notification_window || cocoa_parent == notification_window) {
103                 if ([notification_window isMainWindow]) {
104                         plugin_ui->activate();
105                 } else {
106                         plugin_ui->deactivate();
107                 }
108         } 
109 }
110
111 - (void)cocoaParentBecameKeyHandler:(NSNotification *)notification
112 {
113         NSWindow* notification_window = (NSWindow *)[notification object];
114
115         if (top_level_parent == notification_window || cocoa_parent == notification_window) {
116                 if ([notification_window isKeyWindow]) {
117                         plugin_ui->activate();
118                 } else {
119                         plugin_ui->deactivate();
120                 }
121         } 
122 }
123
124 - (void)auViewResized:(NSNotification *)notification
125 {
126         (void) notification; // stop complaints about unusued argument
127         plugin_ui->cocoa_view_resized();
128 }
129
130 @end
131
132 AUPluginUI::AUPluginUI (boost::shared_ptr<PluginInsert> insert)
133         : PlugUIBase (insert)
134         , automation_mode_label (_("Automation"))
135         , preset_label (_("Presets"))
136         
137 {
138         if (automation_mode_strings.empty()) {
139                 automation_mode_strings = I18N (_automation_mode_strings);
140         }
141         
142         set_popdown_strings (automation_mode_selector, automation_mode_strings);
143         automation_mode_selector.set_active_text (automation_mode_strings.front());
144
145         if ((au = boost::dynamic_pointer_cast<AUPlugin> (insert->plugin())) == 0) {
146                 error << _("unknown type of editor-supplying plugin (note: no AudioUnit support in this version of ardour)") << endmsg;
147                 throw failed_constructor ();
148         }
149
150         /* stuff some stuff into the top of the window */
151
152         HBox* smaller_hbox = manage (new HBox);
153
154         smaller_hbox->set_spacing (6);
155         smaller_hbox->pack_start (preset_label, false, false, 4);
156         smaller_hbox->pack_start (_preset_combo, false, false);
157         smaller_hbox->pack_start (save_button, false, false);
158 #if 0
159         /* one day these might be useful with an AU plugin, but not yet */
160         smaller_hbox->pack_start (automation_mode_label, false, false);
161         smaller_hbox->pack_start (automation_mode_selector, false, false);
162 #endif
163         smaller_hbox->pack_start (bypass_button, false, true);
164
165         VBox* v1_box = manage (new VBox);
166         VBox* v2_box = manage (new VBox);
167
168         v1_box->pack_start (*smaller_hbox, false, true);
169         v2_box->pack_start (focus_button, false, true);
170
171         top_box.set_homogeneous (false);
172         top_box.set_spacing (6);
173         top_box.set_border_width (6);
174
175         top_box.pack_end (*v2_box, false, false);
176         top_box.pack_end (*v1_box, false, false);
177
178         set_spacing (6);
179         pack_start (top_box, false, false);
180         pack_start (low_box, false, false);
181
182         preset_label.show ();
183         _preset_combo.show ();
184         automation_mode_label.show ();
185         automation_mode_selector.show ();
186         bypass_button.show ();
187         top_box.show ();
188         low_box.show ();
189
190         cocoa_parent = 0;
191         cocoa_window = 0;
192
193 #ifdef WITH_CARBON
194         _activating_from_app = false;
195         _notify = 0;
196         au_view = 0;
197         editView = 0;
198         carbon_window = 0;
199 #endif
200
201         /* prefer cocoa, fall back to cocoa, but use carbon if its there */
202
203         if (test_cocoa_view_support()) {
204                 create_cocoa_view ();
205 #ifdef WITH_CARBON
206         } else if (test_carbon_view_support()) {
207                 create_carbon_view ();
208 #endif
209         } else {
210                 create_cocoa_view ();
211         }
212
213         low_box.add_events(Gdk::VISIBILITY_NOTIFY_MASK);
214
215         low_box.signal_realize().connect (mem_fun (this, &AUPluginUI::lower_box_realized));
216         low_box.signal_visibility_notify_event ().connect (mem_fun (this, &AUPluginUI::lower_box_visibility_notify));
217 }
218
219 AUPluginUI::~AUPluginUI ()
220 {
221         if (_notify) {
222                 [[NSNotificationCenter defaultCenter] removeObserver:_notify];
223         }
224
225         if (cocoa_parent) {
226                 NSWindow* win = get_nswindow();
227                 [win removeChildWindow:cocoa_parent];
228         } 
229
230 #ifdef WITH_CARBON
231         if (carbon_window) {
232                 /* not parented, just overlaid on top of our window */
233                 DisposeWindow (carbon_window);
234         }
235 #endif
236
237         if (editView) {
238                 CloseComponent (editView);
239         }
240
241         if (au_view) {
242                 /* remove whatever we packed into low_box so that GTK doesn't
243                    mess with it.
244                 */
245
246                 [au_view removeFromSuperview];
247         }
248 }
249
250 bool
251 AUPluginUI::test_carbon_view_support ()
252 {
253 #ifdef WITH_CARBON
254         bool ret = false;
255         
256         carbon_descriptor.componentType = kAudioUnitCarbonViewComponentType;
257         carbon_descriptor.componentSubType = 'gnrc';
258         carbon_descriptor.componentManufacturer = 'appl';
259         carbon_descriptor.componentFlags = 0;
260         carbon_descriptor.componentFlagsMask = 0;
261         
262         OSStatus err;
263
264         // ask the AU for its first editor component
265         UInt32 propertySize;
266         err = AudioUnitGetPropertyInfo(*au->get_au(), kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, &propertySize, NULL);
267         if (!err) {
268                 int nEditors = propertySize / sizeof(ComponentDescription);
269                 ComponentDescription *editors = new ComponentDescription[nEditors];
270                 err = AudioUnitGetProperty(*au->get_au(), kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, editors, &propertySize);
271                 if (!err) {
272                         // just pick the first one for now
273                         carbon_descriptor = editors[0];
274                         ret = true;
275                 }
276                 delete[] editors;
277         }
278
279         return ret;
280 #else
281         return false;
282 #endif
283 }
284         
285 bool
286 AUPluginUI::test_cocoa_view_support ()
287 {
288         UInt32 dataSize   = 0;
289         Boolean isWritable = 0;
290         OSStatus err = AudioUnitGetPropertyInfo(*au->get_au(),
291                                                 kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
292                                                 0, &dataSize, &isWritable);
293         
294         return dataSize > 0 && err == noErr;
295 }
296
297 bool
298 AUPluginUI::plugin_class_valid (Class pluginClass)
299 {
300         if([pluginClass conformsToProtocol: @protocol(AUCocoaUIBase)]) {
301                 if([pluginClass instancesRespondToSelector: @selector(interfaceVersion)] &&
302                    [pluginClass instancesRespondToSelector: @selector(uiViewForAudioUnit:withSize:)]) {
303                                 return true;
304                 }
305         }
306         return false;
307 }
308
309 int
310 AUPluginUI::create_cocoa_view ()
311 {
312         bool wasAbleToLoadCustomView = false;
313         AudioUnitCocoaViewInfo* cocoaViewInfo = NULL;
314         UInt32               numberOfClasses = 0;
315         UInt32     dataSize;
316         Boolean    isWritable;
317         NSString*           factoryClassName = 0;
318         NSURL*              CocoaViewBundlePath = NULL;
319
320         OSStatus result = AudioUnitGetPropertyInfo (*au->get_au(),
321                                                     kAudioUnitProperty_CocoaUI,
322                                                     kAudioUnitScope_Global, 
323                                                     0,
324                                                     &dataSize,
325                                                     &isWritable );
326
327         numberOfClasses = (dataSize - sizeof(CFURLRef)) / sizeof(CFStringRef);
328
329         // Does view have custom Cocoa UI?
330         
331         if ((result == noErr) && (numberOfClasses > 0) ) {
332
333                 DEBUG_TRACE(DEBUG::AudioUnits,
334                             string_compose ( "based on %1, there are %2 cocoa UI classes\n", dataSize, numberOfClasses));
335
336                 cocoaViewInfo = (AudioUnitCocoaViewInfo *)malloc(dataSize);
337
338                 if(AudioUnitGetProperty(*au->get_au(),
339                                         kAudioUnitProperty_CocoaUI,
340                                         kAudioUnitScope_Global,
341                                         0,
342                                         cocoaViewInfo,
343                                         &dataSize) == noErr) {
344
345                         CocoaViewBundlePath     = (NSURL *)cocoaViewInfo->mCocoaAUViewBundleLocation;
346                                 
347                         // we only take the first view in this example.
348                         factoryClassName        = (NSString *)cocoaViewInfo->mCocoaAUViewClass[0];
349                         
350                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("the factory name is %1 bundle is %2\n",
351                                                                         [factoryClassName UTF8String], CocoaViewBundlePath));
352                         
353                 } else {
354
355                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("No cocoaUI property cocoaViewInfo = %1\n", cocoaViewInfo));
356
357                         if (cocoaViewInfo != NULL) {
358                                 free (cocoaViewInfo);
359                                 cocoaViewInfo = NULL;
360                         }
361                 }
362         }
363
364         // [A] Show custom UI if view has it
365
366         if (CocoaViewBundlePath && factoryClassName) {
367                 NSBundle *viewBundle    = [NSBundle bundleWithPath:[CocoaViewBundlePath path]];
368
369                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("tried to create bundle, result = %1\n", viewBundle));
370
371                 if (viewBundle == NULL) {
372                         error << _("AUPluginUI: error loading AU view's bundle") << endmsg;
373                         return -1;
374                 } else {
375                         Class factoryClass = [viewBundle classNamed:factoryClassName];
376                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("tried to create factory class, result = %1\n", factoryClass));
377                         if (!factoryClass) {
378                                 error << _("AUPluginUI: error getting AU view's factory class from bundle") << endmsg;
379                                 return -1;
380                         }
381                         
382                         // make sure 'factoryClass' implements the AUCocoaUIBase protocol
383                         if (!plugin_class_valid (factoryClass)) {
384                                 error << _("AUPluginUI: U view's factory class does not properly implement the AUCocoaUIBase protocol") << endmsg;
385                                 return -1;
386                         }
387                         // make a factory
388                         id factory = [[[factoryClass alloc] init] autorelease];
389                         if (factory == NULL) {
390                                 error << _("AUPluginUI: Could not create an instance of the AU view factory") << endmsg;
391                                 return -1;
392                         }
393
394                         DEBUG_TRACE (DEBUG::AudioUnits, "got a factory instance\n");
395
396                         // make a view
397                         au_view = [factory uiViewForAudioUnit:*au->get_au() withSize:NSZeroSize];
398
399                         DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("view created @ %1\n", au_view));
400                         
401                         // cleanup
402                         [CocoaViewBundlePath release];
403                         if (cocoaViewInfo) {
404                                 UInt32 i;
405                                 for (i = 0; i < numberOfClasses; i++)
406                                         CFRelease(cocoaViewInfo->mCocoaAUViewClass[i]);
407                                 
408                                 free (cocoaViewInfo);
409                         }
410                         wasAbleToLoadCustomView = true;
411                 }
412         }
413
414         if (!wasAbleToLoadCustomView) {
415                 // load generic Cocoa view
416                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("Loading generic view using %1 -> %2\n", au,
417                                                                 au->get_au()));
418                 au_view = [[AUGenericView alloc] initWithAudioUnit:*au->get_au()];
419                 DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("view created @ %1\n", au_view));
420                 [(AUGenericView *)au_view setShowsExpertParameters:1];
421         }
422
423         // Get the initial size of the new AU View's frame 
424         
425         NSRect rect = [au_view frame];
426         prefheight = rect.size.height;
427         prefwidth = rect.size.width;
428         low_box.set_size_request (rect.size.width, rect.size.height);
429
430         return 0;
431 }
432
433 void
434 AUPluginUI::cocoa_view_resized ()
435 {
436         NSWindow* window = get_nswindow ();
437         NSRect windowFrame= [window frame];
438         NSRect new_frame = [au_view frame];
439
440         float dy = last_au_frame.size.height - new_frame.size.height;
441         float dx = last_au_frame.size.width - new_frame.size.width;
442
443         windowFrame.origin.y    += dy;
444         windowFrame.origin.x    += dx;
445         windowFrame.size.height -= dy;
446         windowFrame.size.width  -= dx;
447         
448         [[NSNotificationCenter defaultCenter] removeObserver:_notify
449          name:NSViewFrameDidChangeNotification 
450          object:au_view];
451
452         NSUInteger old_auto_resize = [au_view autoresizingMask];
453
454         [au_view setAutoresizingMask:NSViewNotSizable];
455         [window setFrame:windowFrame display:1];
456
457         /* Some stupid AU Views change the origin of the original AU View
458            when they are resized (I'm looking at you AUSampler). If the origin
459            has been moved, move it back.
460         */
461
462         if (last_au_frame.origin.x != new_frame.origin.x ||
463             last_au_frame.origin.y != new_frame.origin.y) {
464                 new_frame.origin = last_au_frame.origin;
465                 [au_view setFrame:new_frame];
466                 /* also be sure to redraw the topbox because this can
467                    also go wrong.
468                  */
469                 top_box.queue_draw ();
470         }
471
472         [au_view setAutoresizingMask:old_auto_resize];
473
474         [[NSNotificationCenter defaultCenter] addObserver:_notify
475          selector:@selector(auViewResized:) name:NSViewFrameDidChangeNotification
476          object:au_view];
477
478         last_au_frame = new_frame;
479 }
480
481 int
482 AUPluginUI::create_carbon_view ()
483 {
484 #ifdef WITH_CARBON
485         OSStatus err;
486         ControlRef root_control;
487
488         Component editComponent = FindNextComponent(NULL, &carbon_descriptor);
489         
490         OpenAComponent(editComponent, &editView);
491         if (!editView) {
492                 error << _("AU Carbon view: cannot open AU Component") << endmsg;
493                 return -1;
494         }
495         
496         Rect r = { 100, 100, 100, 100 };
497         WindowAttributes attr = WindowAttributes (kWindowStandardHandlerAttribute |
498                                                   kWindowCompositingAttribute|
499                                                   kWindowNoShadowAttribute|
500                                                   kWindowNoTitleBarAttribute);
501
502         if ((err = CreateNewWindow(kUtilityWindowClass, attr, &r, &carbon_window)) != noErr) {
503                 error << string_compose (_("AUPluginUI: cannot create carbon window (err: %1)"), err) << endmsg;
504                 CloseComponent (editView);
505                 return -1;
506         }
507         
508         if ((err = GetRootControl(carbon_window, &root_control)) != noErr) {
509                 error << string_compose (_("AUPlugin: cannot get root control of carbon window (err: %1)"), err) << endmsg;
510                 DisposeWindow (carbon_window);
511                 CloseComponent (editView);
512                 return -1;
513         }
514
515         ControlRef viewPane;
516         Float32Point location  = { 0.0, 0.0 };
517         Float32Point size = { 0.0, 0.0 } ;
518
519         if ((err = AudioUnitCarbonViewCreate (editView, *au->get_au(), carbon_window, root_control, &location, &size, &viewPane)) != noErr) {
520                 error << string_compose (_("AUPluginUI: cannot create carbon plugin view (err: %1)"), err) << endmsg;
521                 DisposeWindow (carbon_window);
522                 CloseComponent (editView);
523                 return -1;
524         }
525
526         // resize window
527
528         Rect bounds;
529         GetControlBounds(viewPane, &bounds);
530         size.x = bounds.right-bounds.left;
531         size.y = bounds.bottom-bounds.top;
532
533         prefwidth = (int) (size.x + 0.5);
534         prefheight = (int) (size.y + 0.5);
535
536         SizeWindow (carbon_window, prefwidth, prefheight,  true);
537         low_box.set_size_request (prefwidth, prefheight);
538
539         return 0;
540 #else
541         error << _("AU Carbon GUI is not supported.") << endmsg;
542         return -1;
543 #endif
544 }
545
546 NSWindow*
547 AUPluginUI::get_nswindow ()
548 {
549         Gtk::Container* toplevel = get_toplevel();
550
551         if (!toplevel || !toplevel->is_toplevel()) {
552                 error << _("AUPluginUI: no top level window!") << endmsg;
553                 return 0;
554         }
555
556         NSWindow* true_parent = gdk_quartz_window_get_nswindow (toplevel->get_window()->gobj());
557
558         if (!true_parent) {
559                 error << _("AUPluginUI: no top level window!") << endmsg;
560                 return 0;
561         }
562
563         return true_parent;
564 }
565
566 void
567 AUPluginUI::activate ()
568 {
569 #ifdef WITH_CARBON
570         ActivateWindow (carbon_window, TRUE);
571 #endif
572 }
573
574 void
575 AUPluginUI::deactivate ()
576 {
577 #ifdef WITH_CARBON
578         ActivateWindow (carbon_window, FALSE);
579 #endif
580 }
581
582 int
583 AUPluginUI::parent_carbon_window ()
584 {
585 #ifdef WITH_CARBON
586         NSWindow* win = get_nswindow ();
587         Rect windowStructureBoundsRect;
588
589         if (!win) {
590                 return -1;
591         }
592
593         Gtk::Container* toplevel = get_toplevel();
594
595         if (!toplevel || !toplevel->is_toplevel()) {
596                 error << _("AUPluginUI: no top level window!") << endmsg;
597                 return -1;
598         }
599         
600         /* figure out where the cocoa parent window is in carbon-coordinate space, which
601            differs from both cocoa-coordinate space and GTK-coordinate space
602         */
603
604         GetWindowBounds((WindowRef) [win windowRef], kWindowStructureRgn, &windowStructureBoundsRect);
605
606         /* compute how tall the title bar is, because we have to offset the position of the carbon window
607            by that much.
608         */
609
610         NSRect content_frame = [NSWindow contentRectForFrameRect:[win frame] styleMask:[win styleMask]];
611         NSRect wm_frame = [NSWindow frameRectForContentRect:content_frame styleMask:[win styleMask]];
612
613         int titlebar_height = wm_frame.size.height - content_frame.size.height;
614
615         int packing_extra = 6; // this is the total vertical packing in our top level window
616
617         /* move into position, based on parent window position */
618         MoveWindow (carbon_window, 
619                     windowStructureBoundsRect.left, 
620                     windowStructureBoundsRect.top + titlebar_height + top_box.get_height() + packing_extra, 
621                     false);
622         ShowWindow (carbon_window);
623
624         // create the cocoa window for the carbon one and make it visible
625         cocoa_parent = [[NSWindow alloc] initWithWindowRef: carbon_window];
626
627         SetWindowActivationScope (carbon_window, kWindowActivationScopeNone);
628
629         _notify = [ [NotificationObject alloc] initWithPluginUI:this andCocoaParent:cocoa_parent andTopLevelParent:win ]; 
630
631         [win addChildWindow:cocoa_parent ordered:NSWindowAbove];
632         [win setAutodisplay:1]; // turn of GTK stuff for this window
633
634         return 0;
635 #else
636         return -1;
637 #endif
638 }       
639
640 int
641 AUPluginUI::parent_cocoa_window ()
642 {
643         NSWindow* win = get_nswindow ();
644
645         if (!win) {
646                 return -1;
647         }
648
649         [win setAutodisplay:1]; // turn of GTK stuff for this window
650
651         Gtk::Container* toplevel = get_toplevel();
652
653         if (!toplevel || !toplevel->is_toplevel()) {
654                 error << _("AUPluginUI: no top level window!") << endmsg;
655                 return -1;
656         }
657
658         NSView* view = gdk_quartz_window_get_nsview (get_toplevel()->get_window()->gobj());
659         GtkRequisition a = top_box.size_request ();
660
661         /* move the au_view down so that it doesn't overlap the top_box contents */
662
663         const int spacing = 6;  // main vbox spacing
664         const int pad     = 4;  // box pad
665
666         NSPoint origin = { spacing + pad, static_cast<CGFloat> (a.height) + (2 * spacing) + pad };
667
668         [au_view setFrameOrigin:origin];
669         [view addSubview:au_view positioned:NSWindowBelow relativeTo:NULL];
670
671         last_au_frame = [au_view frame];
672
673         // watch for size changes of the view
674
675         _notify = [ [NotificationObject alloc] initWithPluginUI:this andCocoaParent:NULL andTopLevelParent:win ];
676
677         [[NSNotificationCenter defaultCenter] addObserver:_notify
678          selector:@selector(auViewResized:) name:NSViewFrameDidChangeNotification
679          object:au_view];
680
681         return 0;
682 }
683
684 void
685 AUPluginUI::grab_focus()
686 {
687         if (au_view) {
688                 [au_view becomeFirstResponder];
689         }
690 }
691 void
692 AUPluginUI::forward_key_event (GdkEventKey* ev)
693 {
694         NSEvent* nsevent = gdk_quartz_event_get_nsevent ((GdkEvent*)ev);
695
696         if (au_view && nsevent) {
697
698                 /* filter on nsevent type here because GDK massages FlagsChanged
699                    messages into GDK_KEY_{PRESS,RELEASE} but Cocoa won't
700                    handle a FlagsChanged message as a keyDown or keyUp
701                 */
702
703                 if ([nsevent type] == NSKeyDown) {
704                         [[[au_view window] firstResponder] keyDown:nsevent];
705                 } else if ([nsevent type] == NSKeyUp) {
706                         [[[au_view window] firstResponder] keyUp:nsevent];
707                 } else if ([nsevent type] == NSFlagsChanged) {
708                         [[[au_view window] firstResponder] flagsChanged:nsevent];
709                 }
710         }
711 }
712
713 void
714 AUPluginUI::on_realize ()
715 {
716         VBox::on_realize ();
717
718         /* our windows should not have that resize indicator */
719
720         NSWindow* win = get_nswindow ();
721         if (win) {
722                 [win setShowsResizeIndicator:0];
723         }
724 }
725
726 void
727 AUPluginUI::lower_box_realized ()
728 {
729         if (au_view) {
730                 parent_cocoa_window ();
731         } else if (carbon_window) {
732                 parent_carbon_window ();
733         }
734 }
735
736 bool
737 AUPluginUI::lower_box_visibility_notify (GdkEventVisibility* ev)
738 {
739 #ifdef WITH_CARBON
740         if (carbon_window  && ev->state != GDK_VISIBILITY_UNOBSCURED) {
741                 ShowWindow (carbon_window);
742                 ActivateWindow (carbon_window, TRUE);
743                 return true;
744         }
745 #endif
746         return false;
747 }
748
749 void
750 AUPluginUI::on_window_hide ()
751 {
752 #ifdef WITH_CARBON
753         if (carbon_window) {
754                 HideWindow (carbon_window);
755                 ActivateWindow (carbon_window, FALSE);
756         }
757 #endif
758         hide_all ();
759         
760 #if 0
761         NSArray* wins = [NSApp windows];
762         for (uint32_t i = 0; i < [wins count]; i++) {
763                 id win = [wins objectAtIndex:i];
764         }
765 #endif
766 }
767
768 bool
769 AUPluginUI::on_window_show (const string& /*title*/)
770 {
771         /* this is idempotent so just call it every time we show the window */
772
773         gtk_widget_realize (GTK_WIDGET(low_box.gobj()));
774
775         show_all ();
776
777 #ifdef WITH_CARBON
778         if (carbon_window) {
779                 ShowWindow (carbon_window);
780                 ActivateWindow (carbon_window, TRUE);
781         }
782 #endif
783
784         return true;
785 }
786
787 bool
788 AUPluginUI::start_updating (GdkEventAny*)
789 {
790         return false;
791 }
792
793 bool
794 AUPluginUI::stop_updating (GdkEventAny*)
795 {
796         return false;
797 }
798
799 PlugUIBase*
800 create_au_gui (boost::shared_ptr<PluginInsert> plugin_insert, VBox** box)
801 {
802         AUPluginUI* aup = new AUPluginUI (plugin_insert);
803         (*box) = aup;
804         return aup;
805 }
806
807