1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 07:40:23 +02:00

Adding wxMac accessibility. Should resolve:

http://bugzilla.audacityteam.org/show_bug.cgi?id=139
This commit is contained in:
lllucius 2012-03-21 03:09:06 +00:00
parent 6b21ebb1a7
commit 5f1ef8f8fa
12 changed files with 4485 additions and 72 deletions

View File

@ -55,6 +55,7 @@ static ControlID kButtonID = { kCustom, kButton };
struct CustomData {
FileDialog *me;
NavDialogRef context;
WindowRef window;
Rect bounds;
ControlRef userpane;
@ -122,63 +123,11 @@ static void HandleCustomMouseDown(NavCBRecPtr callBackParms, CustomData *data)
GlobalToLocal(&where);
ControlRef control = FindControlUnderMouse(where, callBackParms->window, NULL);
if (control != NULL)
{
ControlID cid;
GetControlID(control, &cid);
HandleControlClick(control, where, evt->modifiers, (ControlActionUPP)-1L);
if (cid.signature == kCustom)
{
switch (cid.id)
{
case kChoice:
{
MenuRef menu;
UInt32 v;
menu = GetControlPopupMenuRef(control);
v = GetControl32BitValue(control) - 1;
const size_t numFilters = data->extensions.GetCount();
if (v < numFilters)
{
data->currentfilter = v;
if (data->saveMode)
{
int i = data->currentfilter;
wxString extension = data->extensions[i].AfterLast('.') ;
extension.MakeLower() ;
wxString sfilename ;
wxMacCFStringHolder cfString(NavDialogGetSaveFileName(callBackParms->context), false);
sfilename = cfString.AsString() ;
#if 0
int pos = sfilename.Find('.', true) ;
if (pos != wxNOT_FOUND)
{
sfilename = sfilename.Left(pos + 1) + extension;
cfString.Assign(sfilename, wxFONTENCODING_DEFAULT);
NavDialogSetSaveFileName(callBackParms->context, cfString);
}
#endif
}
NavCustomControl(callBackParms->context, kNavCtlBrowserRedraw, NULL);
}
break;
}
case kButton:
{
data->me->ClickButton(GetControl32BitValue(data->choice) - 1);
break;
}
}
}
}
}
static void HandleNormalEvents(NavCBRecPtr callBackParms, CustomData *data)
@ -196,6 +145,7 @@ static void HandleNormalEvents(NavCBRecPtr callBackParms, CustomData *data)
static const EventTypeSpec namedAttrsList[] =
{
{ kEventClassAccessibility , kEventAccessibleGetNamedAttribute },
{ kEventClassControl , kEventControlHit },
};
// ----------------------------------------------------------------------------
@ -208,7 +158,8 @@ static OSStatus SetElement(wxMacCarbonEvent & event, HIObjectRef objectRef,
AXUIElementRef elem;
elem = AXUIElementCreateWithHIObjectAndIdentifier(objectRef, id);
if (elem) {
if (elem)
{
result = event.SetParameter(parm,
typeCFTypeRef,
sizeof(elem),
@ -225,6 +176,53 @@ static pascal OSStatus HandlePaneEvents(EventHandlerCallRef handlerRef, EventRef
CustomData *dt = (CustomData *) data;
OSStatus result = eventNotHandledErr;
switch (event.GetClass())
{
case kEventClassControl:
{
ControlRef control;
ControlID cid;
control = event.GetParameter<ControlRef>(kEventParamDirectObject, typeControlRef);
if (control == NULL)
{
break;
}
GetControlID(control, &cid);
if (cid.signature != kCustom)
{
break;
}
switch (cid.id)
{
case kChoice:
{
MenuRef menu = GetControlPopupMenuRef(control);
UInt32 v = GetControl32BitValue(control) - 1;
const size_t numFilters = dt->extensions.GetCount();
if (v < (UInt32) dt->extensions.GetCount())
{
dt->currentfilter = v;
NavCustomControl(dt->context, kNavCtlBrowserRedraw, NULL);
}
}
break;
case kButton:
{
dt->me->ClickButton(GetControl32BitValue(dt->choice) - 1);
}
break;
}
}
break;
case kEventClassAccessibility:
{
switch (event.GetKind())
{
case kEventAccessibleGetNamedAttribute:
@ -239,7 +237,8 @@ static pascal OSStatus HandlePaneEvents(EventHandlerCallRef handlerRef, EventRef
if (false)
{
}
else if (CFStringCompare(attr, kAXRoleAttribute, 0) == kCFCompareEqualTo) {
else if (CFStringCompare(attr, kAXRoleAttribute, 0) == kCFCompareEqualTo)
{
CFStringRef role = kAXGroupRole;
result = event.SetParameter(kEventParamAccessibleAttributeValue,
@ -249,12 +248,14 @@ static pascal OSStatus HandlePaneEvents(EventHandlerCallRef handlerRef, EventRef
require_noerr(result, ParameterError);
}
else if (CFStringCompare(attr, kAXRoleDescriptionAttribute, 0) == kCFCompareEqualTo) {
else if (CFStringCompare(attr, kAXRoleDescriptionAttribute, 0) == kCFCompareEqualTo)
{
CFStringRef role = kAXGroupRole;
CFStringRef desc;
desc = HICopyAccessibilityRoleDescription(role, NULL);
if (desc) {
if (desc)
{
result = event.SetParameter(kEventParamAccessibleAttributeValue,
typeCFStringRef,
sizeof(desc),
@ -265,39 +266,49 @@ static pascal OSStatus HandlePaneEvents(EventHandlerCallRef handlerRef, EventRef
require_noerr(result, ParameterError);
}
}
else if (CFStringCompare(attr, kAXParentAttribute, 0) == kCFCompareEqualTo) {
else if (CFStringCompare(attr, kAXParentAttribute, 0) == kCFCompareEqualTo)
{
HIViewRef viewRef = HIViewGetSuperview(dt->userpane);
if (viewRef) {
if (viewRef)
{
result = SetElement(event, (HIObjectRef) viewRef, 0,
kEventParamAccessibleAttributeValue);
require_noerr(result, ParameterError);
}
}
else if (CFStringCompare(attr, kAXWindowAttribute, 0) == kCFCompareEqualTo) {
else if (CFStringCompare(attr, kAXWindowAttribute, 0) == kCFCompareEqualTo)
{
WindowRef winRef = HIViewGetWindow((HIViewRef) dt->userpane);
if (winRef) {
if (winRef)
{
result = SetElement(event, (HIObjectRef) winRef, 0,
kEventParamAccessibleAttributeValue);
require_noerr(result, ParameterError);
}
}
else if (CFStringCompare(attr, kAXTopLevelUIElementAttribute, 0) == kCFCompareEqualTo) {
if (dt->window) {
else if (CFStringCompare(attr, kAXTopLevelUIElementAttribute, 0) == kCFCompareEqualTo)
{
if (dt->window)
{
result = SetElement(event, (HIObjectRef) dt->window, 0,
kEventParamAccessibleAttributeValue);
require_noerr(result, ParameterError);
}
}
else {
else
{
result = eventNotHandledErr;
}
}
break;
}
}
break;
}
ParameterError:
return result;
@ -307,6 +318,8 @@ DEFINE_ONE_SHOT_HANDLER_GETTER(HandlePaneEvents);
static void HandleStartEvent(NavCBRecPtr callBackParms, CustomData *data)
{
data->context = callBackParms->context;
CreateUserPaneControl(callBackParms->window, &data->bounds, kControlSupportsEmbedding, &data->userpane);
InstallControlEventHandler(data->userpane,

View File

@ -67,16 +67,22 @@ STEP 3: Patch, compile and install wxMac
-----------------------------------------
* Apply "wxMac_bug_2_10.5.8_PPC.patch" in the
"mac/wxMac_additions" folder in the Audacity sources.
"mac/wxMac_additions" folder in the Audacity sources.
This has to be applied until we drop support for OS X 10.5.
* Apply "wxMac-2.8.12-wxaccessible.patch" in the
"mac/wxMac_additions" folder in the Audacity sources.
This will add accessibility support (VoiceOver) to wxMac.
It is not absolutely required, but to be compatible with
released versions of Audacity, it should be applied.
* Apply any other wxMac patches in the "mac" directory that
are relevant to the version of wxMac you are compiling.
cd wxMac-2.8.12
mkdir macbuild
cd macbuild
../configure --disable-shared
../configure --disable-shared --enable-unicode --enable-accessibility
make
sudo make install

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,11 @@
BEGIN_EVENT_TABLE(MixerTrackSlider, ASlider)
EVT_MOUSE_EVENTS(MixerTrackSlider::OnMouseEvent)
EVT_SET_FOCUS(MixerTrackSlider::OnFocus)
EVT_KILL_FOCUS(MixerTrackSlider::OnFocus)
EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, MixerTrackSlider::OnCaptureKey)
END_EVENT_TABLE()
MixerTrackSlider::MixerTrackSlider(wxWindow * parent,
@ -73,6 +78,39 @@ void MixerTrackSlider::OnMouseEvent(wxMouseEvent &event)
}
}
void MixerTrackSlider::OnFocus(wxFocusEvent &event)
{
wxCommandEvent e(EVT_CAPTURE_KEYBOARD);
if (event.GetEventType() == wxEVT_KILL_FOCUS) {
e.SetEventType(EVT_RELEASE_KEYBOARD);
}
e.SetEventObject(this);
GetParent()->GetEventHandler()->ProcessEvent(e);
Refresh(false);
event.Skip();
}
void MixerTrackSlider::OnCaptureKey(wxCommandEvent &event)
{
wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject();
int keyCode = kevent->GetKeyCode();
// Pass LEFT/RIGHT/UP/DOWN/PAGEUP/PAGEDOWN through for input/output sliders
if (keyCode == WXK_LEFT || keyCode == WXK_RIGHT ||
keyCode == WXK_UP || keyCode == WXK_DOWN ||
keyCode == WXK_PAGEUP || keyCode == WXK_PAGEDOWN) {
return;
}
event.Skip();
return;
}
// class MixerTrackCluster
@ -136,6 +174,8 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
#endif
mRightTrack = pRightTrack;
SetName(mLeftTrack->GetName());
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
// Not sure why, but sizers weren't getting offset vertically,
@ -182,6 +222,8 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
_("Gain"),
ctrlPos, ctrlSize, DB_SLIDER, true,
true, 0.0, wxVERTICAL);
mSlider_Gain->SetName(_("Gain"));
this->UpdateGain();
@ -201,6 +243,7 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
ctrlPos, ctrlSize,
wxBU_AUTODRAW, wxDefaultValidator,
_("Musical Instrument"));
mBitmapButton_MusicalInstrument->SetName(_("Musical Instrumnet"));
// pan slider
@ -218,6 +261,7 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
/* i18n-hint: Title of the Pan slider, used to move the sound left or right */
_("Pan"),
ctrlPos, ctrlSize, PAN_SLIDER, true);
mSlider_Pan->SetName(_("Pan"));
this->UpdatePan();
@ -230,6 +274,7 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
*(mMixerBoard->mImageMuteUp), *(mMixerBoard->mImageMuteOver),
*(mMixerBoard->mImageMuteDown), *(mMixerBoard->mImageMuteDisabled),
true); // toggle button
mToggleButton_Mute->SetName(_("Mute"));
mToggleButton_Mute->SetAlternateImages(
*(mMixerBoard->mImageMuteUp), *(mMixerBoard->mImageMuteOver),
*(mMixerBoard->mImageMuteDown), *(mMixerBoard->mImageMuteDisabled));
@ -242,6 +287,7 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
*(mMixerBoard->mImageSoloUp), *(mMixerBoard->mImageSoloOver),
*(mMixerBoard->mImageSoloDown), *(mMixerBoard->mImageSoloDisabled),
true); // toggle button
mToggleButton_Solo->SetName(_("Solo"));
this->UpdateSolo();
bool bSoloNone = mProject->IsSoloNone();
mToggleButton_Solo->Show(!bSoloNone);
@ -263,6 +309,7 @@ MixerTrackCluster::MixerTrackCluster(wxWindow* parent,
false, // bool isInput
ctrlPos, ctrlSize, // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
Meter::MixerTrackCluster); // Style style = HorizontalStereo,
mMeter->SetName(_("Signal Level Meter"));
#ifdef EXPERIMENTAL_MIDI_OUT
} else {
mMeter = NULL;
@ -395,6 +442,7 @@ void MixerTrackCluster::UpdateName()
#else
const wxString newName = mLeftTrack->GetName();
#endif
SetName(newName);
mStaticText_TrackName->SetLabel(newName);
#if wxUSE_TOOLTIPS
mStaticText_TrackName->SetToolTip(newName);

View File

@ -49,6 +49,9 @@ public:
void OnMouseEvent(wxMouseEvent & event);
void OnFocus(wxFocusEvent &event);
void OnCaptureKey(wxCommandEvent& event);
protected:
bool mIsPan;
@ -108,6 +111,7 @@ private:
void OnButton_Solo(wxCommandEvent& event);
//v void OnSliderScroll_Gain(wxScrollEvent& event);
public:
#ifdef EXPERIMENTAL_MIDI_OUT
// mTrack is redundant, but simplifies code that operates on either

View File

@ -104,7 +104,9 @@ void TimerRecordDialog::OnTimer(wxTimerEvent& event)
wxDateTime dateTime_UNow = wxDateTime::UNow();
if (m_DateTime_Start < dateTime_UNow) {
m_DateTime_Start = dateTime_UNow;
if (m_DateTime_Start.GetDateOnly() < dateTime_UNow.GetDateOnly()) {
m_pDatePickerCtrl_Start->SetValue(m_DateTime_Start);
}
m_pTimeTextCtrl_Start->SetTimeValue(wxDateTime_to_AudacityTime(m_DateTime_Start));
this->UpdateEnd(); // Keep Duration constant and update End for changed Start.
}
@ -165,6 +167,9 @@ void TimerRecordDialog::OnDatePicker_End(wxDateEvent& event)
// need to implement it for the TimeTextCtrls.
if (m_DateTime_End < m_DateTime_Start) {
m_DateTime_End = m_DateTime_Start;
m_pDatePickerCtrl_End->SetValue(m_DateTime_End);
m_pDatePickerCtrl_End->SetRange(m_DateTime_End, wxInvalidDateTime);
m_pDatePickerCtrl_End->Refresh();
m_pTimeTextCtrl_End->SetTimeValue(wxDateTime_to_AudacityTime(m_DateTime_End));
}
@ -424,9 +429,11 @@ void TimerRecordDialog::UpdateEnd()
{
//v Use remaining disk -> record time calcs from AudacityProject::OnTimer to set range?
m_DateTime_End = m_DateTime_Start + m_TimeSpan_Duration;
if (m_pDatePickerCtrl_End->GetValue().GetDateOnly() < m_DateTime_End.GetDateOnly()) {
m_pDatePickerCtrl_End->SetValue(m_DateTime_End);
m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
m_pDatePickerCtrl_End->Refresh();
}
m_pTimeTextCtrl_End->SetTimeValue(wxDateTime_to_AudacityTime(m_DateTime_End));
}

View File

@ -6105,6 +6105,9 @@ void TrackPanel::OnToggle()
mTracks->Select( t, !t->GetSelected() );
EnsureVisible( t );
mAx->Updated();
return;
}

View File

@ -60,15 +60,15 @@ Track *TrackPanelAx::GetFocus()
// Changes focus to a specified track
void TrackPanelAx::SetFocus( Track *track )
{
#if wxUSE_ACCESSIBILITY
if( mFocusedTrack != NULL )
{
#if wxUSE_ACCESSIBILITY
NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE,
mTrackPanel,
wxOBJID_CLIENT,
TrackNum( mFocusedTrack ) );
#endif
}
#endif
if( track == NULL )
{
@ -280,6 +280,7 @@ wxAccStatus TrackPanelAx::GetLocation( wxRect& rect, int elementId )
// Gets the name of the specified object.
wxAccStatus TrackPanelAx::GetName( int childId, wxString* name )
{
#if defined(__WXMSW__)
if( childId == wxACC_SELF )
{
*name = _( "TrackView" );
@ -319,11 +320,17 @@ wxAccStatus TrackPanelAx::GetName( int childId, wxString* name )
}
return wxACC_OK;
#endif
#if defined(__WXMAC__)
return wxACC_NOT_IMPLEMENTED;
#endif
}
// Returns a role constant.
wxAccStatus TrackPanelAx::GetRole( int childId, wxAccRole* role )
{
#if defined(__WXMSW__)
if( childId == wxACC_SELF )
{
*role = wxROLE_SYSTEM_TABLE;
@ -332,6 +339,18 @@ wxAccStatus TrackPanelAx::GetRole( int childId, wxAccRole* role )
{
*role = wxROLE_SYSTEM_ROW;
}
#endif
#if defined(__WXMAC__)
if( childId == wxACC_SELF )
{
*role = wxROLE_SYSTEM_PANE;
}
else
{
*role = wxROLE_SYSTEM_STATICTEXT;
}
#endif
return wxACC_OK;
}
@ -379,7 +398,49 @@ wxAccStatus TrackPanelAx::GetState( int childId, long* state )
// or child.
wxAccStatus TrackPanelAx::GetValue( int childId, wxString* strValue )
{
return wxACC_NOT_SUPPORTED;
#if defined(__WXMSW__)
return wxACC_NOT_IMPLEMENTED;
#endif
#if defined(__WXMAC__)
if( childId == wxACC_SELF )
{
*strValue = _( "TrackView" );
}
else
{
Track *t = FindTrack( childId );
if( t == NULL )
{
return wxACC_FAIL;
}
else
{
*strValue = t->GetName();
if( *strValue == t->GetDefaultName() )
{
strValue->Printf(_("Track %d"), TrackNum( t ) );
}
// LLL: Remove these during "refactor"
if( t->GetMute() )
{
strValue->Append( _( " Mute On" ) );
}
if( t->GetSolo() )
{
strValue->Append( _( " Solo On" ) );
}
if( t->GetSelected() )
{
strValue->Append( _( " Select On" ) );
}
}
}
return wxACC_OK;
#endif
}
// Gets the window with the keyboard focus.
@ -388,6 +449,7 @@ wxAccStatus TrackPanelAx::GetValue( int childId, wxString* strValue )
// If this object has the focus, child should be 'this'.
wxAccStatus TrackPanelAx::GetFocus( int *childId, wxAccessible **child )
{
#if defined(__WXMSW__)
if( *childId == wxACC_SELF )
{
if( mFocusedTrack )
@ -401,7 +463,27 @@ wxAccStatus TrackPanelAx::GetFocus( int *childId, wxAccessible **child )
}
return wxACC_OK;
#endif
#if defined(__WXMAC__)
if( GetWindow() == wxWindow::FindFocus() )
{
if( mFocusedTrack )
{
*childId = TrackNum( mFocusedTrack );
}
else
{
*childId = wxACC_SELF;
}
return wxACC_OK;
}
return wxACC_NOT_IMPLEMENTED;
#endif
}
#endif // wxUSE_ACCESSIBILITY
// Indentation settings for Vim and Emacs.

View File

@ -65,6 +65,10 @@ private:
none = 99,
};
#if defined(__WXMAC__) && wxUSE_ACCESSIBILITY
virtual bool AcceptsFocus() const {return false;}
#endif
void OnPaint(wxPaintEvent& evt);
void OnMouseCaptureChanged(wxMouseCaptureChangedEvent &evt);
void OnMouseCaptureLost(wxMouseCaptureLostEvent &evt);

View File

@ -60,6 +60,7 @@ Grabber::Grabber(wxWindow * parent, wxWindowID id)
mPressed = false;
SetLabel(_("Grabber"));
SetName(_("Grabber"));
}
//

View File

@ -765,7 +765,13 @@ wxAccStatus GridAx::GetParent(wxAccessible **parent)
wxAccStatus GridAx::GetRole(int childId, wxAccRole *role)
{
if (childId == wxACC_SELF) {
#if defined(__WXMSW__)
*role = wxROLE_SYSTEM_TABLE;
#endif
#if defined(__WXMAC__)
*role = wxROLE_SYSTEM_GROUPING;
#endif
}
else {
*role = wxROLE_SYSTEM_TEXT;
@ -791,18 +797,38 @@ wxAccStatus GridAx::GetSelections(wxVariant *selections)
wxAccStatus GridAx::GetState(int childId, long *state)
{
int flag = wxACC_STATE_SYSTEM_FOCUSABLE |
wxACC_STATE_SYSTEM_SELECTABLE |
wxACC_STATE_SYSTEM_FOCUSED |
wxACC_STATE_SYSTEM_SELECTED;
wxACC_STATE_SYSTEM_SELECTABLE;
int col;
int row;
if (GetRowCol(childId, row, col)) {
if (!GetRowCol(childId, row, col)) {
*state = 0;
return wxACC_FAIL;
}
#if defined(__WXMSW__)
flag |= wxACC_STATE_SYSTEM_FOCUSED |
wxACC_STATE_SYSTEM_SELECTED;
if (mGrid->IsReadOnly(row, col)) {
flag = wxACC_STATE_SYSTEM_UNAVAILABLE;
}
#endif
#if defined(__WXMAC__)
if (mGrid->IsInSelection(row, col)) {
flag |= wxACC_STATE_SYSTEM_SELECTED;
}
if (mGrid->GetGridCursorRow() == row && mGrid->GetGridCursorCol() == col) {
flag |= wxACC_STATE_SYSTEM_FOCUSED;
}
if (mGrid->IsReadOnly(row, col)) {
flag |= wxACC_STATE_SYSTEM_UNAVAILABLE;
}
#endif
*state = flag;
return wxACC_OK;
@ -814,8 +840,34 @@ wxAccStatus GridAx::GetValue(int childId, wxString *strValue)
{
strValue->Clear();
#if defined(__WXMSW__)
return wxACC_OK;
#endif
#if defined(__WXMAC__)
return GetName(childId, strValue);
#endif
}
#if defined(__WXMAC__)
// Selects the object or child.
wxAccStatus GridAx::Select(int childId, wxAccSelectionFlags selectFlags)
{
int row;
int col;
if (GetRowCol(childId, row, col)) {
if (selectFlags & wxACC_SEL_TAKESELECTION) {
mGrid->SetGridCursor(row, col);
}
mGrid->SelectBlock(row, col, row, col, selectFlags & wxACC_SEL_ADDSELECTION);
}
return wxACC_OK;
}
#endif
// Gets the window with the keyboard focus.
// If childId is 0 and child is NULL, no object in

View File

@ -293,6 +293,11 @@ class GridAx
// or child.
virtual wxAccStatus GetValue(int childId, wxString* strValue);
#if defined(__WXMAC__)
// Selects the object or child.
virtual wxAccStatus Select(int childId, wxAccSelectionFlags selectFlags);
#endif
Grid *mGrid;
int mLastId;