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