2 File: AUCarbonViewControl.cpp
3 Abstract: AUCarbonViewControl.h
6 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
7 Inc. ("Apple") in consideration of your agreement to the following
8 terms, and your use, installation, modification or redistribution of
9 this Apple software constitutes acceptance of these terms. If you do
10 not agree with these terms, please do not use, install, modify or
11 redistribute this Apple software.
13 In consideration of your agreement to abide by the following terms, and
14 subject to these terms, Apple grants you a personal, non-exclusive
15 license, under Apple's copyrights in this original Apple software (the
16 "Apple Software"), to use, reproduce, modify and redistribute the Apple
17 Software, with or without modifications, in source and/or binary forms;
18 provided that if you redistribute the Apple Software in its entirety and
19 without modifications, you must retain this notice and the following
20 text and disclaimers in all such redistributions of the Apple Software.
21 Neither the name, trademarks, service marks or logos of Apple Inc. may
22 be used to endorse or promote products derived from the Apple Software
23 without specific prior written permission from Apple. Except as
24 expressly stated in this notice, no other rights or licenses, express or
25 implied, are granted by Apple herein, including but not limited to any
26 patent rights that may be infringed by your derivative works or by other
27 works in which the Apple Software may be incorporated.
29 The Apple Software is provided by Apple on an "AS IS" basis. APPLE
30 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
35 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42 POSSIBILITY OF SUCH DAMAGE.
44 Copyright (C) 2014 Apple Inc. All Rights Reserved.
47 #include "AUCarbonViewControl.h"
48 #include "AUCarbonViewBase.h"
49 #include "AUViewLocalizedStringKeys.h"
51 AUCarbonViewControl::AUCarbonViewControl(AUCarbonViewBase *ownerView, AUParameterListenerRef listener, ControlType type, const CAAUParameter ¶m, ControlRef control) :
52 mOwnerView(ownerView),
57 mInControlInitialization(0)
60 SetControlReference(control, SRefCon(this));
64 AUCarbonViewControl::~AUCarbonViewControl()
66 AUListenerRemoveParameter(mListener, this, &mParam);
69 AUCarbonViewControl* AUCarbonViewControl::mLastControl = NULL;
71 void AUCarbonViewControl::Bind()
74 mInControlInitialization = 1; // true
75 AUListenerAddParameter(mListener, this, &mParam);
76 // will cause an almost-immediate callback
78 EventTypeSpec events[] = {
79 { kEventClassControl, kEventControlValueFieldChanged } // N.B. OS X only
82 WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events);
84 if (mType == kTypeContinuous || mType == kTypeText || mType == kTypeDiscrete) {
85 EventTypeSpec events[] = {
86 { kEventClassControl, kEventControlHit },
87 { kEventClassControl, kEventControlClick },
88 { kEventClassControl, kEventControlTrack }
90 WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events);
93 if (mType == kTypeText) {
94 EventTypeSpec events[] = {
95 { kEventClassControl, kEventControlSetFocusPart }
97 WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events);
98 ControlKeyFilterUPP proc = mParam.ValuesHaveStrings() ? StdKeyFilterCallback : NumericKeyFilterCallback;
99 // this will fail for a static text field
100 SetControlData(mControl, 0, kControlEditTextKeyFilterTag, sizeof(proc), &proc);
104 mInControlInitialization = 0; // false
108 void AUCarbonViewControl::ParameterToControl(Float32 paramValue)
111 ++mInControlInitialization;
113 case kTypeContinuous:
114 SetValueFract(AUParameterValueToLinear(paramValue, &mParam));
118 long value = long(paramValue);
120 // special case [1] -- menu parameters
121 if (mParam.HasNamedParams()) {
122 // if we're dealing with menus they behave differently!
123 // becaue setting min and max doesn't work correctly for the control value
124 // first menu item always reports a control value of 1
125 ControlKind ctrlKind;
126 if (GetControlKind(mControl, &ctrlKind) == noErr) {
127 if ((ctrlKind.kind == kControlKindPopupArrow)
128 || (ctrlKind.kind == kControlKindPopupButton))
130 value = value - long(mParam.ParamInfo().minValue) + 1;
135 // special case [2] -- Write-only boolean parameters
136 AudioUnitParameterInfo AUPI = mParam.ParamInfo();
138 bool isWriteOnlyBoolParameter = ( (AUPI.unit == kAudioUnitParameterUnit_Boolean) &&
139 (AUPI.flags & kAudioUnitParameterFlag_IsWritable) &&
140 !(AUPI.flags & kAudioUnitParameterFlag_IsReadable) );
141 if (!isWriteOnlyBoolParameter) {
148 CFStringRef cfstr = mParam.GetStringFromValueCopy(¶mValue);
150 if ( !(mParam.ParamInfo().flags & kAudioUnitParameterFlag_IsWritable) //READ ONLY PARAMS
151 && (mParam.ParamInfo().flags & kAudioUnitParameterFlag_IsReadable))
153 if (mParam.GetParamTag()) {
154 CFMutableStringRef str = CFStringCreateMutableCopy(NULL, 256, cfstr);
156 CFStringAppend (str, CFSTR(" "));
157 CFStringAppend (str, mParam.GetParamTag());
166 --mInControlInitialization;
170 void AUCarbonViewControl::ControlToParameter()
173 if (mInControlInitialization)
177 case kTypeContinuous:
179 double controlValue = GetValueFract();
180 Float32 paramValue = AUParameterValueFromLinear(controlValue, &mParam);
181 mParam.SetValue(mListener, this, paramValue);
186 long value = GetValue();
188 // special case [1] -- Menus
189 if (mParam.HasNamedParams()) {
190 // if we're dealing with menus they behave differently!
191 // becaue setting min and max doesn't work correctly for the control value
192 // first menu item always reports a control value of 1
193 ControlKind ctrlKind;
194 if (GetControlKind(mControl, &ctrlKind) == noErr) {
195 if ((ctrlKind.kind == kControlKindPopupArrow)
196 || (ctrlKind.kind == kControlKindPopupButton))
198 value = value + long(mParam.ParamInfo().minValue) - 1;
203 // special case [2] -- Write-only boolean parameters
204 AudioUnitParameterInfo AUPI = mParam.ParamInfo();
206 bool isWriteOnlyBoolParameter = ( (AUPI.unit == kAudioUnitParameterUnit_Boolean) &&
207 (AUPI.flags & kAudioUnitParameterFlag_IsWritable) &&
208 !(AUPI.flags & kAudioUnitParameterFlag_IsReadable) );
209 if (isWriteOnlyBoolParameter) {
213 mParam.SetValue (mListener, this, value);
218 Float32 val = mParam.GetValueFromString (GetTextValue());
219 mParam.SetValue(mListener, this, (mParam.IsIndexedParam() ? (int)val : val));
220 if (mParam.ValuesHaveStrings())
221 ParameterToControl(val); //make sure we display the correct text (from the AU)
228 void AUCarbonViewControl::SetValueFract(double value)
231 SInt32 minimum = GetControl32BitMinimum(mControl);
232 SInt32 maximum = GetControl32BitMaximum(mControl);
233 SInt32 cval = SInt32(value * (maximum - minimum) + minimum + 0.5);
234 SetControl32BitValue(mControl, cval);
235 // printf("set: value=%lf, min=%ld, max=%ld, ctl value=%ld\n", value, minimum, maximum, cval);
239 double AUCarbonViewControl::GetValueFract()
242 SInt32 minimum = GetControl32BitMinimum(mControl);
243 SInt32 maximum = GetControl32BitMaximum(mControl);
244 SInt32 cval = GetControl32BitValue(mControl);
245 double result = double(cval - minimum) / double(maximum - minimum);
246 // printf("get: min=%ld, max=%ld, value=%ld, result=%f\n", minimum, maximum, cval, result);
253 void AUCarbonViewControl::SetTextValue(CFStringRef cfstr)
256 verify_noerr(SetControlData(mControl, 0, kControlEditTextCFStringTag, sizeof(CFStringRef), &cfstr));
260 CFStringRef AUCarbonViewControl::GetTextValue()
264 verify_noerr(GetControlData(mControl, 0, kControlEditTextCFStringTag, sizeof(CFStringRef), &cfstr, NULL));
271 void AUCarbonViewControl::SetValue(long value)
274 SetControl32BitValue(mControl, value);
278 long AUCarbonViewControl::GetValue()
281 return GetControl32BitValue(mControl);
287 /* Notes on event handling
289 Button (Click and release on button)
290 kEventControlClick received
291 kEventControlTrack received
292 kEventControlValueFieldChanged received
293 kEventControlHit received
295 Button (Click and release outside of button bounds)
296 kEventControlClick received
297 kEventControlTrack received
299 Slider (Click, drag, and release)
300 kEventControlClick received
301 kEventControlTrack received
302 kEventControlValueFieldChanged received
303 kEventControlValueFieldChanged received
304 kEventControlHit received
306 Slider (Click, release without changing value)
307 kEventControlClick received
308 kEventControlTrack received
310 bool AUCarbonViewControl::HandleEvent(EventHandlerCallRef inHandlerRef, EventRef event)
312 UInt32 eclass = GetEventClass(event);
313 UInt32 ekind = GetEventKind(event);
318 case kEventClassControl:
320 AudioUnitParameterInfo AUPI = mParam.ParamInfo();
322 bool isWriteOnlyBoolParameter = ( (AUPI.unit == kAudioUnitParameterUnit_Boolean) &&
323 (AUPI.flags & kAudioUnitParameterFlag_IsWritable) &&
324 !(AUPI.flags & kAudioUnitParameterFlag_IsReadable) );
327 case kEventControlSetFocusPart: // tab
328 handled = !handled; // fall through to next case
330 case kEventControlValueFieldChanged:
331 GetEventParameter(event, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &control);
332 verify(control == mControl);
333 ControlToParameter();
335 case kEventControlClick:
336 if (isWriteOnlyBoolParameter) {
337 GetEventParameter(event, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &control);
338 verify(control == mControl);
339 ControlToParameter();
340 } else if (mLastControl != this) {
341 if (mLastControl != NULL) {
342 mLastControl->Update(false);
346 mOwnerView->TellListener(mParam, kAudioUnitCarbonViewEvent_MouseDownInControl, NULL);
347 break; // don't return true, continue normal processing
348 case kEventControlHit:
349 if (mLastControl != this) {
350 if (mLastControl != NULL)
351 mLastControl->Update(false);
354 mOwnerView->TellListener(mParam, kAudioUnitCarbonViewEvent_MouseUpInControl, NULL);
355 break; // don't return true, continue normal processing
356 case kEventControlTrack:
357 if (mLastControl != this) {
358 if (mLastControl != NULL)
359 mLastControl->Update(false);
363 CallNextEventHandler(inHandlerRef, event);
364 ControlToParameter(); // new code
365 mOwnerView->TellListener(mParam, kAudioUnitCarbonViewEvent_MouseUpInControl, NULL);
367 // break; // don't return true, continue normal processing
369 return handled; // don't return true, continue normal processing
376 pascal void AUCarbonViewControl::SliderTrackProc(ControlRef theControl, ControlPartCode partCode)
378 // this doesn't need to actually do anything
379 // AUCarbonViewControl *This = (AUCarbonViewControl *)GetControlReference(theControl);
382 pascal ControlKeyFilterResult AUCarbonViewControl::StdKeyFilterCallback(ControlRef theControl,
383 SInt16 *keyCode, SInt16 *charCode,
384 EventModifiers *modifiers)
387 SInt16 c = *charCode;
388 if (c >= ' ' || c == '\b' || c == 0x7F || (c >= 0x1c && c <= 0x1f) || c == '\t')
389 return kControlKeyFilterPassKey;
390 if (c == '\r' || c == 3) { // return or Enter
391 AUCarbonViewControl *This = (AUCarbonViewControl *)GetControlReference(theControl);
392 ControlEditTextSelectionRec sel = { 0, 32767 };
393 SetControlData(This->mControl, 0, kControlEditTextSelectionTag, sizeof(sel), &sel);
394 This->ControlToParameter();
397 return kControlKeyFilterBlockKey;
400 pascal ControlKeyFilterResult AUCarbonViewControl::NumericKeyFilterCallback(ControlRef theControl,
401 SInt16 *keyCode, SInt16 *charCode,
402 EventModifiers *modifiers)
405 SInt16 c = *charCode;
406 if (isdigit(c) || c == '+' || c == '-' || c == '.' || c == '\b' || c == 0x7F || (c >= 0x1c && c <= 0x1f)
408 return kControlKeyFilterPassKey;
409 if (c == '\r' || c == 3) { // return or Enter
410 AUCarbonViewControl *This = (AUCarbonViewControl *)GetControlReference(theControl);
411 ControlEditTextSelectionRec sel = { 0, 32767 };
412 SetControlData(This->mControl, 0, kControlEditTextSelectionTag, sizeof(sel), &sel);
413 This->ControlToParameter();
416 return kControlKeyFilterBlockKey;
419 Boolean AUCarbonViewControl::SizeControlToFit(ControlRef inControl, SInt16 *outWidth, SInt16 *outHeight)
422 if (inControl == 0) return false;
424 Boolean bValue = false;
425 // this only works on text controls -- returns an error for other controls, but doesn't do anything,
426 // so the error is irrelevant
427 SetControlData(inControl, kControlEntireControl, 'stim' /* kControlStaticTextIsMultilineTag */, sizeof(Boolean), &bValue);
429 SInt16 baseLineOffset;
431 OSErr err = GetBestControlRect(inControl, &bestRect, &baseLineOffset);
432 if (err != noErr) return false;
434 int width = (bestRect.right - bestRect.left) + 1;
435 int height = (bestRect.bottom - bestRect.top) + 1;
438 GetControlBounds (inControl, &boundsRect);
441 newRect.top = boundsRect.top;
442 newRect.bottom = newRect.top + height;
443 newRect.left = boundsRect.left;
444 newRect.right = newRect.left + width;
446 SetControlBounds (inControl, &newRect);
457 #pragma mark ___AUPropertyControl
458 bool AUPropertyControl::HandleEvent(EventHandlerCallRef inHandlerRef, EventRef event)
460 UInt32 eclass = GetEventClass(event);
461 UInt32 ekind = GetEventKind(event);
463 case kEventClassControl:
465 case kEventControlValueFieldChanged:
466 HandleControlChange();
467 return true; // handled
474 void AUPropertyControl::RegisterEvents ()
477 EventTypeSpec events[] = {
478 { kEventClassControl, kEventControlValueFieldChanged } // N.B. OS X only
481 WantEventTypes(GetControlEventTarget(mControl), GetEventTypeCount(events), events);
485 void AUPropertyControl::EmbedControl (ControlRef theControl)
487 mView->EmbedControl (theControl);
490 WindowRef AUPropertyControl::GetCarbonWindow()
492 return mView->GetCarbonWindow();
495 #pragma mark ___AUVPreset
497 static CFStringRef kStringFactoryPreset = kAUViewLocalizedStringKey_FactoryPreset;
498 static bool sAUVPresetLocalized = false;
501 AUVPresets::AUVPresets (AUCarbonViewBase* inParentView,
502 CFArrayRef& inPresets,
506 ControlFontStyleRec & inFontStyle)
507 : AUPropertyControl (inParentView),
508 mPresets (inPresets),
514 // ok we now have an array of factory presets
515 // get their strings and display them
517 r.top = inLocation.v; r.bottom = r.top;
518 r.left = inLocation.h; r.right = r.left;
520 // localize as necessary
521 if (!sAUVPresetLocalized) {
522 CFBundleRef mainBundle = CFBundleGetBundleWithIdentifier(kLocalizedStringBundle_AUView);
524 kStringFactoryPreset = CFCopyLocalizedStringFromTableInBundle(
525 kAUViewLocalizedStringKey_FactoryPreset, kLocalizedStringTable_AUView,
526 mainBundle, CFSTR("FactoryPreset title string"));
527 sAUVPresetLocalized = true;
531 // create localized title string
532 CFMutableStringRef factoryPresetsTitle = CFStringCreateMutable(NULL, 0);
533 CFStringAppend(factoryPresetsTitle, kStringFactoryPreset);
534 CFStringAppend(factoryPresetsTitle, kAUViewUnlocalizedString_TitleSeparator);
536 ControlRef theControl;
537 verify_noerr(CreateStaticTextControl(mView->GetCarbonWindow(), &r, factoryPresetsTitle, &inFontStyle, &theControl));
539 AUCarbonViewControl::SizeControlToFit(theControl, &width, &mHeight);
540 CFRelease(factoryPresetsTitle);
541 EmbedControl(theControl);
544 r.left += width + 10;
548 verify_noerr(CreatePopupButtonControl ( mView->GetCarbonWindow(), &r, NULL,
549 -12345, // DON'T GET MENU FROM RESOURCE mMenuID,!!!
550 FALSE, // variableWidth,
552 0, // titleJustification,
557 verify_noerr(CreateNewMenu(1, 0, &menuRef));
559 int numPresets = CFArrayGetCount(mPresets);
561 for (int i = 0; i < numPresets; ++i)
563 AUPreset* preset = (AUPreset*) CFArrayGetValueAtIndex (mPresets, i);
564 verify_noerr(AppendMenuItemTextWithCFString (menuRef, preset->presetName, 0, 0, 0));
567 verify_noerr(SetControlData(mControl, 0, kControlPopupButtonMenuRefTag, sizeof(menuRef), &menuRef));
568 verify_noerr (SetControlFontStyle (mControl, &inFontStyle));
570 SetControl32BitMaximum (mControl, numPresets);
575 AUCarbonViewControl::SizeControlToFit(mControl, &width, &height);
577 if (height > mHeight) mHeight = height;
578 if (mHeight < 0) mHeight = 0;
580 // find which menu item is the Default preset
581 UInt32 propertySize = sizeof(AUPreset);
582 AUPreset defaultPreset;
583 OSStatus result = AudioUnitGetProperty (mView->GetEditAudioUnit(),
584 kAudioUnitProperty_PresentPreset,
585 kAudioUnitScope_Global,
590 mPropertyID = kAudioUnitProperty_PresentPreset;
593 if (result != noErr) { // if the PresentPreset property is not implemented, fall back to the CurrentPreset property
594 OSStatus result = AudioUnitGetProperty (mView->GetEditAudioUnit(),
595 kAudioUnitProperty_CurrentPreset,
596 kAudioUnitScope_Global,
600 mPropertyID = kAudioUnitProperty_CurrentPreset;
602 CFRetain (defaultPreset.presetName);
606 EmbedControl (mControl);
608 HandlePropertyChange(defaultPreset);
614 void AUVPresets::AddInterest (AUEventListenerRef inListener,
618 e.mEventType = kAudioUnitEvent_PropertyChange;
619 e.mArgument.mProperty.mAudioUnit = mView->GetEditAudioUnit();
620 e.mArgument.mProperty.mPropertyID = mPropertyID;
621 e.mArgument.mProperty.mScope = kAudioUnitScope_Global;
622 e.mArgument.mProperty.mElement = 0;
624 AUEventListenerAddEventType(inListener, inObject, &e);
627 void AUVPresets::RemoveInterest (AUEventListenerRef inListener,
631 e.mEventType = kAudioUnitEvent_PropertyChange;
632 e.mArgument.mProperty.mAudioUnit = mView->GetEditAudioUnit();
633 e.mArgument.mProperty.mPropertyID = mPropertyID;
634 e.mArgument.mProperty.mScope = kAudioUnitScope_Global;
635 e.mArgument.mProperty.mElement = 0;
637 AUEventListenerRemoveEventType(inListener, inObject, &e);
640 void AUVPresets::HandleControlChange ()
643 SInt32 i = GetControl32BitValue(mControl);
646 AUPreset* preset = (AUPreset*) CFArrayGetValueAtIndex (mPresets, i-1);
648 verify_noerr(AudioUnitSetProperty (mView->GetEditAudioUnit(),
649 mPropertyID, // either currentPreset or PresentPreset depending on which is supported
650 kAudioUnitScope_Global,
655 // when we change a preset we can't expect the AU to update its state
656 // as it isn't meant to know that its being viewed!
657 // so we broadcast a notification to all listeners that all parameters on this AU have changed
658 AudioUnitParameter changedUnit;
659 changedUnit.mAudioUnit = mView->GetEditAudioUnit();
660 changedUnit.mParameterID = kAUParameterListener_AnyParameter;
661 verify_noerr (AUParameterListenerNotify (NULL, NULL, &changedUnit) );
666 void AUVPresets::HandlePropertyChange(AUPreset &preset)
669 // check to see if the preset is in our menu
670 int numPresets = CFArrayGetCount(mPresets);
671 if (preset.presetNumber < 0) {
672 SetControl32BitValue (mControl, 0); //controls are one-based
674 for (SInt32 i = 0; i < numPresets; ++i) {
675 AUPreset* currPreset = (AUPreset*) CFArrayGetValueAtIndex (mPresets, i);
676 if (preset.presetNumber == currPreset->presetNumber) {
677 SetControl32BitValue (mControl, ++i); //controls are one-based
683 if (preset.presetName)
684 CFRelease (preset.presetName);
688 bool AUVPresets::HandlePropertyChange (const AudioUnitProperty &inProp)
690 if (inProp.mPropertyID == mPropertyID)
692 UInt32 theSize = sizeof(AUPreset);
693 AUPreset currentPreset;
695 OSStatus result = AudioUnitGetProperty(inProp.mAudioUnit,
698 inProp.mElement, ¤tPreset, &theSize);
700 if (result == noErr) {
702 if (inProp.mPropertyID == kAudioUnitProperty_CurrentPreset && currentPreset.presetName)
703 CFRetain (currentPreset.presetName);
705 HandlePropertyChange(currentPreset);