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