1/********************************************************************** 2 3 Audacity: A Digital Audio Editor 4 5 VSTControlOSX.mm 6 7 Leland Lucius 8 9 Several ideas and code snippets taken from HairerSoft's HSVSTView class: 10 11 http://www.hairersoft.com/Downloads/HSVSTView.zip 12 13 Created by Martin on 02/06/2007. 14 Copyright 2010 by HairerSoft. 15 16 You are most welcome to use this code in your own (open source or not) 17 project. Use at your own risk of course, etc. Please acknowledge at an 18 appropriate location (manual or about box for example). 19 20 Bug reports most welcome: Martin@HairerSoft.com 21 22**********************************************************************/ 23 24 25#include "VSTControlOSX.h" 26 27#include <memory> 28 29@interface VSTView : NSView 30{ 31} 32@end 33 34@implementation VSTView 35 36+ (void)initialize 37{ 38 static BOOL initialized = NO; 39 if (!initialized) 40 { 41 initialized = YES; 42 wxOSXCocoaClassAddWXMethods(self); 43 } 44} 45@end 46 47VSTControlImpl::VSTControlImpl(wxWindowMac *peer, NSView *view) 48: wxWidgetCocoaImpl(peer, view) 49{ 50} 51 52VSTControlImpl::~VSTControlImpl() 53{ 54} 55 56VSTControl::VSTControl() 57: VSTControlBase() 58{ 59 mVSTView = nil; 60 mView = nil; 61 62#if !defined(_LP64) 63 mHIView = NULL; 64 mWindowRef = NULL; 65#endif 66} 67 68VSTControl::~VSTControl() 69{ 70 Close(); 71} 72 73void VSTControl::Close() 74{ 75#if !defined(_LP64) 76 if (mWindowRef) 77 { 78 mLink->callDispatcher(effEditClose, 0, 0, mWindowRef, 0.0); 79 mWindowRef = 0; 80 } 81#endif 82} 83 84bool VSTControl::Create(wxWindow *parent, VSTEffectLink *link) 85{ 86 DontCreatePeer(); 87 88 if (!VSTControlBase::Create(parent, link)) 89 { 90 return false; 91 } 92 93 mVSTView = [VSTView alloc]; 94 if (!mVSTView) 95 { 96 return false; 97 } 98 [mVSTView init]; 99 [mVSTView retain]; 100 101 // wxWidgets takes ownership so safenew 102 SetPeer(safenew VSTControlImpl(this, mVSTView)); 103 104 CreateCocoa(); 105 106#if !defined(_LP64) 107 if (!mView) 108 { 109 CreateCarbon(); 110 } 111#endif 112 113 if (!mView 114#if !defined(_LP64) 115 && !mHIView 116#endif 117 ) 118 { 119 return false; 120 } 121 122 // Must get the size again since SetPeer() could cause it to change 123 SetInitialSize(GetMinSize()); 124 125 MacPostControlCreate(wxDefaultPosition, wxDefaultSize); 126 127 return true; 128} 129 130void VSTControl::CreateCocoa() 131{ 132 if ((mLink->callDispatcher(effCanDo, 0, 0, (void *) "hasCockosViewAsConfig", 0.0) & 0xffff0000) != 0xbeef0000) 133 { 134 return; 135 } 136 137 VstRect *rect; 138 139 // Some effects like to have us get their rect before opening them. 140 mLink->callDispatcher(effEditGetRect, 0, 0, &rect, 0.0); 141 142 // Ask the effect to add its GUI 143 mLink->callDispatcher(effEditOpen, 0, 0, mVSTView, 0.0); 144 145 // Get the subview it created 146 mView = [[mVSTView subviews] objectAtIndex:0]; 147 if (mView == NULL) 148 { 149 // Doesn't seem the effect created the subview. This can 150 // happen when an effect uses the content view directly. 151 // As of this time, we will not try to support those and 152 // just fall back to the textual interface. 153 return; 154 } 155 156 // Get the final bounds of the effect GUI 157 mLink->callDispatcher(effEditGetRect, 0, 0, &rect, 0.0); 158 159 NSRect frame = { 160 { 0, 0 }, 161 { (CGFloat) rect->right - rect->left, (CGFloat) rect->bottom - rect->top } 162 }; 163 164 [mView setFrame:frame]; 165 166 [mVSTView addSubview:mView]; 167 168 SetMinSize(wxSize(frame.size.width, frame.size.height)); 169 170 return; 171} 172 173#if !defined(_LP64) 174 175void VSTControl::CreateCarbon() 176{ 177 OSStatus result; 178 179 Bind(wxEVT_SIZE, &VSTControl::OnSize, this); 180 181 VstRect *rect; 182 183 // Some effects like to have us get their rect before opening them. 184 mLink->callDispatcher(effEditGetRect, 0, 0, &rect, 0.0); 185 186 // Suggest a dummy size 187 Rect bounds = { 0, 0, 0, 0 }; 188 189 // And create the window 190 result = CreateNewWindow(kOverlayWindowClass, 191 kWindowStandardHandlerAttribute | 192 kWindowCompositingAttribute | 193 kWindowOpaqueForEventsAttribute, 194 &bounds, 195 &mWindowRef); 196 if (result != noErr) 197 { 198 return; 199 } 200 201 // Get the root control 202 ControlRef root = HIViewGetRoot(mWindowRef); 203 204 // Find the content view within our window 205 HIViewRef content; 206 result = HIViewFindByID(root, kHIViewWindowContentID, &content); 207 if (result != noErr) 208 { 209 DisposeWindow(mWindowRef); 210 mWindowRef = NULL; 211 212 return; 213 } 214 215 // Some effects (iZotope Vinyl) seem to need an existing subview 216 // of the content view. So just use a "dummy" scrollview. 217 result = HIScrollViewCreate(kHIScrollViewOptionsVertScroll, &mHIView); 218 219 // Don't want to see the scroll bars 220 HIScrollViewSetScrollBarAutoHide(mHIView, true); 221 222 // Add it as a subview of the content view 223 HIViewAddSubview(content, mHIView); 224 225 // Ask the effect to add its GUI 226 mLink->callDispatcher(effEditOpen, 0, 0, mWindowRef, 0.0); 227 228 // Get the subview it created 229 HIViewRef subview = HIViewGetFirstSubview(content); 230 if (subview) 231 { 232 // The scrollview was used, so leave it. 233 if (subview == mHIView) 234 { 235 subview = HIViewGetFirstSubview(mHIView); 236 } 237 // The effect didn't use our scrollview, so dispose of it. 238 else 239 { 240 HIViewRemoveFromSuperview(mHIView); 241 CFRelease(mHIView); 242 mHIView = subview; 243 } 244 } 245 246 // Doesn't seem the effect created a subview. This can 247 // happen when an effect uses the content view directly. 248 // As of this time, we will not try to support those and 249 // just fall back to the textual interface. 250 if (subview == NULL) 251 { 252 mLink->callDispatcher(effEditClose, 0, 0, mWindowRef, 0.0); 253 DisposeWindow(mWindowRef); 254 mWindowRef = NULL; 255 mHIView = NULL; 256 257 return; 258 } 259 260 // Get the final bounds of the effect GUI 261 mLink->callDispatcher(effEditGetRect, 0, 0, &rect, 0.0); 262 263 // Set the size of the scrollview to match 264 HIRect r = { 265 { 0, 0 }, 266 { (CGFloat) rect->right - rect->left, (CGFloat) rect->bottom - rect->top } 267 }; 268 269 // One effect, mutagene lipredemuco, doesn't return a valid rect so 270 // try to detect it and use the created view dimensions instead. 271 if (rect->left < 0 || rect->top < 0 || rect->right <= 0 || rect->bottom <= 0) 272 { 273 HIViewGetFrame(subview, &r); 274 } 275 276 // Make sure container is the same size as the effect GUI 277 HIViewSetFrame(mHIView, &r); 278 HIViewPlaceInSuperviewAt(mHIView, 0, 0); 279 280 // Establish the minimum size 281 SetMinSize(wxSize(r.size.width, r.size.height)); 282 283 NSWindow *parent = [mVSTView window]; 284 NSWindow *host = [[[NSWindow alloc] initWithWindowRef:mWindowRef] autorelease]; 285 [parent addChildWindow:host ordered:NSWindowAbove]; 286 287 ShowWindow(mWindowRef); 288} 289 290void VSTControl::OnSize(wxSizeEvent & evt) 291{ 292 evt.Skip(); 293 294 wxRect rect = GetScreenRect(); 295 296 MoveWindow(mWindowRef, rect.x, rect.y, true); 297 SizeWindow(mWindowRef, rect.width, rect.height, true); 298} 299 300#endif 301