diff --git a/include/wx/dataview.h b/include/wx/dataview.h index 08a54eb40d..e74764cf52 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -54,6 +54,9 @@ class WXDLLIMPEXP_FWD_ADV wxDataViewCtrl; class WXDLLIMPEXP_FWD_ADV wxDataViewColumn; class WXDLLIMPEXP_FWD_ADV wxDataViewRenderer; class WXDLLIMPEXP_FWD_ADV wxDataViewModelNotifier; +#if wxUSE_ACCESSIBILITY +class WXDLLIMPEXP_FWD_ADV wxDataViewCtrlAccessible; +#endif // wxUSE_ACCESSIBILITY extern WXDLLIMPEXP_DATA_ADV(const char) wxDataViewCtrlNameStr[]; @@ -1301,9 +1304,17 @@ public: //----------------------------------------------------------------------------- +#if wxUSE_ACCESSIBILITY +class WXDLLIMPEXP_FWD_ADV wxDataViewTreeCtrlAccessible; +#endif // wxUSE_ACCESSIBILITY + class WXDLLIMPEXP_ADV wxDataViewTreeCtrl: public wxDataViewCtrl, public wxWithImages { +#if wxUSE_ACCESSIBILITY + friend class wxDataViewTreeCtrlAccessible; +#endif // wxUSE_ACCESSIBILITY + public: wxDataViewTreeCtrl() { } wxDataViewTreeCtrl(wxWindow *parent, @@ -1401,6 +1412,21 @@ private: #define wxEVT_COMMAND_DATAVIEW_ITEM_DROP_POSSIBLE wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE #define wxEVT_COMMAND_DATAVIEW_ITEM_DROP wxEVT_DATAVIEW_ITEM_DROP +#if wxUSE_ACCESSIBILITY +//----------------------------------------------------------------------------- +// wxDataViewTreeCtrlAccessible +//----------------------------------------------------------------------------- + +class WXDLLIMPEXP_ADV wxDataViewTreeCtrlAccessible: public wxDataViewCtrlAccessible +{ +public: + wxDataViewTreeCtrlAccessible(wxDataViewTreeCtrl* win); + virtual ~wxDataViewTreeCtrlAccessible() {}; + + virtual wxAccStatus GetName(int childId, wxString* name) wxOVERRIDE; +}; +#endif // wxUSE_ACCESSIBILITY + #endif // wxUSE_DATAVIEWCTRL #endif diff --git a/include/wx/generic/dataview.h b/include/wx/generic/dataview.h index 694ee8ffe6..80d7cba62d 100644 --- a/include/wx/generic/dataview.h +++ b/include/wx/generic/dataview.h @@ -17,9 +17,15 @@ #include "wx/scrolwin.h" #include "wx/icon.h" #include "wx/vector.h" +#if wxUSE_ACCESSIBILITY + #include "wx/access.h" +#endif // wxUSE_ACCESSIBILITY class WXDLLIMPEXP_FWD_ADV wxDataViewMainWindow; class WXDLLIMPEXP_FWD_ADV wxDataViewHeaderWindow; +#if wxUSE_ACCESSIBILITY +class WXDLLIMPEXP_FWD_ADV wxDataViewCtrlAccessible; +#endif // wxUSE_ACCESSIBILITY // --------------------------------------------------------- // wxDataViewColumn @@ -172,6 +178,9 @@ class WXDLLIMPEXP_ADV wxDataViewCtrl : public wxDataViewCtrlBase, friend class wxDataViewHeaderWindow; friend class wxDataViewHeaderWindowMSW; friend class wxDataViewColumn; +#if wxUSE_ACCESSIBILITY + friend class wxDataViewCtrlAccessible; +#endif // wxUSE_ACCESSIBILITY public: wxDataViewCtrl() : wxScrollHelper(this) @@ -376,5 +385,58 @@ private: wxDECLARE_EVENT_TABLE(); }; +#if wxUSE_ACCESSIBILITY +//----------------------------------------------------------------------------- +// wxDataViewCtrlAccessible +//----------------------------------------------------------------------------- + +class WXDLLIMPEXP_ADV wxDataViewCtrlAccessible: public wxWindowAccessible +{ +public: + wxDataViewCtrlAccessible(wxDataViewCtrl* win); + virtual ~wxDataViewCtrlAccessible() {}; + + virtual wxAccStatus HitTest(const wxPoint& pt, int* childId, + wxAccessible** childObject) wxOVERRIDE; + + virtual wxAccStatus GetLocation(wxRect& rect, int elementId) wxOVERRIDE; + + virtual wxAccStatus Navigate(wxNavDir navDir, int fromId, + int* toId, wxAccessible** toObject) wxOVERRIDE; + + virtual wxAccStatus GetName(int childId, wxString* name) wxOVERRIDE; + + virtual wxAccStatus GetChildCount(int* childCount) wxOVERRIDE; + + virtual wxAccStatus GetChild(int childId, wxAccessible** child) wxOVERRIDE; + + // wxWindowAccessible::GetParent() implementation is enough. + // virtual wxAccStatus GetParent(wxAccessible** parent) wxOVERRIDE; + + virtual wxAccStatus DoDefaultAction(int childId) wxOVERRIDE; + + virtual wxAccStatus GetDefaultAction(int childId, wxString* actionName) wxOVERRIDE; + + virtual wxAccStatus GetDescription(int childId, wxString* description) wxOVERRIDE; + + virtual wxAccStatus GetHelpText(int childId, wxString* helpText) wxOVERRIDE; + + virtual wxAccStatus GetKeyboardShortcut(int childId, wxString* shortcut) wxOVERRIDE; + + virtual wxAccStatus GetRole(int childId, wxAccRole* role) wxOVERRIDE; + + virtual wxAccStatus GetState(int childId, long* state) wxOVERRIDE; + + virtual wxAccStatus GetValue(int childId, wxString* strValue) wxOVERRIDE; + + virtual wxAccStatus Select(int childId, wxAccSelectionFlags selectFlags) wxOVERRIDE; + + virtual wxAccStatus GetFocus(int* childId, wxAccessible** child) wxOVERRIDE; + +#if wxUSE_VARIANT + virtual wxAccStatus GetSelections(wxVariant* selections) wxOVERRIDE; +#endif // wxUSE_VARIANT +}; +#endif // wxUSE_ACCESSIBILITY #endif // __GENERICDATAVIEWCTRLH__ diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index f8dcfee354..2e6c8287b0 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -31,6 +31,9 @@ #include "wx/choice.h" #include "wx/imaglist.h" #include "wx/renderer.h" +#if wxUSE_ACCESSIBILITY + #include "wx/access.h" +#endif // wxUSE_ACCESSIBILITY const char wxDataViewCtrlNameStr[] = "dataviewCtrl"; @@ -2567,6 +2570,10 @@ bool wxDataViewTreeCtrl::Create( wxWindow *parent, wxWindowID id, 0 // not resizable ); +#if wxUSE_ACCESSIBILITY + SetAccessible(new wxDataViewTreeCtrlAccessible(this)); +#endif // wxUSE_ACCESSIBILITY + return true; } @@ -2736,5 +2743,50 @@ void wxDataViewTreeCtrl::OnSize( wxSizeEvent &event ) event.Skip( true ); } +#if wxUSE_ACCESSIBILITY +//----------------------------------------------------------------------------- +// wxDataViewTreeCtrlAccessible +//----------------------------------------------------------------------------- + +wxDataViewTreeCtrlAccessible::wxDataViewTreeCtrlAccessible(wxDataViewTreeCtrl* win) + : wxDataViewCtrlAccessible(win) +{ +} + +// Gets the name of the specified object. +wxAccStatus wxDataViewTreeCtrlAccessible::GetName(int childId, wxString* name) +{ + wxDataViewTreeCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewTreeCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + if ( childId == wxACC_SELF ) + { + *name = dvCtrl->GetName(); + } + else + { + wxDataViewItem item = dvCtrl->GetItemByRow(childId-1); + if ( !item.IsOk() ) + { + return wxACC_NOT_IMPLEMENTED; + } + + wxString itemName = dvCtrl->GetItemText(item); + if ( itemName.empty() ) + { + // Return row number if not textual column found. + // Rows are numbered from 1. + *name = _("Row") + wxString::Format(wxS(" %i"), childId); + } + else + { + *name = itemName; + } + } + + return wxACC_OK; +} +#endif // wxUSE_ACCESSIBILITY + #endif // wxUSE_DATAVIEWCTRL diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 9c9a87fb17..9501b60a71 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -241,6 +241,12 @@ public: wxDataViewHeaderWindow(wxDataViewCtrl *parent) : wxHeaderCtrl(parent) { +#if wxUSE_ACCESSIBILITY + // Under MSW wxHeadrCtrl is a native control + // so we just need to pass all requests + // to the accessibility framework. + SetAccessible(new wxAccessible(this)); +#endif // wxUSE_ACCESSIBILITY } wxDataViewCtrl *GetOwner() const @@ -824,6 +830,10 @@ public: // specified item in the given column. void StartEditing(const wxDataViewItem& item, const wxDataViewColumn* col); void FinishEditing(); + bool HasEditableColumn(const wxDataViewItem& item) const + { + return FindColumnForEditing(item, wxDATAVIEW_CELL_EDITABLE) != NULL; + } int GetSortColumn() const { return m_sortColumn; } bool IsAscendingSort() const { return m_sortAscending; } @@ -843,7 +853,7 @@ private: wxDataViewTreeNode * FindNode( const wxDataViewItem & item ); - wxDataViewColumn *FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode); + wxDataViewColumn *FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode) const; bool IsCellEditableInMode(const wxDataViewItem& item, const wxDataViewColumn *col, wxDataViewCellMode mode) const; @@ -3575,7 +3585,7 @@ void wxDataViewMainWindow::DestroyTree() } wxDataViewColumn* -wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode) +wxDataViewMainWindow::FindColumnForEditing(const wxDataViewItem& item, wxDataViewCellMode mode) const { // Edit the current column editable in 'mode'. If no column is focused // (typically because the user has full row selected), try to find the @@ -4659,6 +4669,10 @@ bool wxDataViewCtrl::Create(wxWindow *parent, m_clientArea = new wxDataViewMainWindow( this, wxID_ANY ); +#if wxUSE_ACCESSIBILITY + SetAccessible(new wxDataViewCtrlAccessible(this)); +#endif // wxUSE_ACCESSIBILITY + // We use the cursor keys for moving the selection, not scrolling, so call // this method to ensure wxScrollHelperEvtHandler doesn't catch all // keyboard events forwarded to us from wxListMainWindow. @@ -5141,14 +5155,14 @@ wxDataViewColumn *wxDataViewCtrl::GetSortingColumn() const { if ( m_sortingColumnIdxs.empty() ) return NULL; - + return GetColumn(m_sortingColumnIdxs.front()); } wxVector wxDataViewCtrl::GetSortingColumns() const { wxVector out; - + for ( wxVector::const_iterator it = m_sortingColumnIdxs.begin(), end = m_sortingColumnIdxs.end(); it != end; @@ -5484,4 +5498,789 @@ void wxDataViewCtrl::DoEnableSystemTheme(bool enable, wxWindow* window) #endif // !wxUSE_GENERICDATAVIEWCTRL +#if wxUSE_ACCESSIBILITY +//----------------------------------------------------------------------------- +// wxDataViewCtrlAccessible +//----------------------------------------------------------------------------- + +wxDataViewCtrlAccessible::wxDataViewCtrlAccessible(wxDataViewCtrl* win) + : wxWindowAccessible(win) +{ +} + +// Can return either a child object, or an integer +// representing the child element, starting from 1. +wxAccStatus wxDataViewCtrlAccessible::HitTest(const wxPoint& pt, + int* childId, wxAccessible** childObject) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + wxDataViewItem item; + wxDataViewColumn* col; + const wxPoint posCtrl = dvCtrl->ScreenToClient(pt); + dvCtrl->HitTest(posCtrl, item, col); + if ( item.IsOk() ) + { + *childId = dvCtrl->GetRowByItem(item)+1; + *childObject = NULL; + } + else + { + if( ((wxWindow*)dvCtrl)->HitTest(posCtrl) == wxHT_WINDOW_INSIDE ) + { + // First check if provided point belongs to the header + // because header control handles accesibility requestes on its own. + wxHeaderCtrl* dvHdr = dvCtrl->GenericGetHeader(); + if ( dvHdr ) + { + const wxPoint posHdr = dvHdr->ScreenToClient(pt); + if ( dvHdr->HitTest(posHdr) == wxHT_WINDOW_INSIDE ) + { + *childId = wxACC_SELF; + *childObject = dvHdr->GetOrCreateAccessible(); + return wxACC_OK; + } + } + + *childId = wxACC_SELF; + *childObject = this; + } + else + { + *childId = wxACC_SELF; + *childObject = NULL; + } + } + + return wxACC_OK; +} + +// Returns the rectangle for this object (id = 0) or a child element (id > 0). +wxAccStatus wxDataViewCtrlAccessible::GetLocation(wxRect& rect, int elementId) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + if ( elementId == wxACC_SELF ) + { + // Header accesibility requestes are handled separately + // so header is excluded from effective client area + // and hence only main window area is reported. + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + rect = dvWnd->GetScreenRect(); + } + else + { + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + wxDataViewItem item = dvWnd->GetItemByRow(elementId-1); + if ( !item.IsOk() ) + { + return wxACC_NOT_IMPLEMENTED; + } + + rect = dvWnd->GetItemRect(item, NULL); + // Indentation and expander column should be included here and therefore + // reported row width should by the same as the width of the client area. + rect.width += rect.x; + rect.x = 0; + wxPoint posScreen = dvWnd->ClientToScreen(rect.GetPosition()); + rect.SetPosition(posScreen); + } + + return wxACC_OK; +} + +// Navigates from fromId to toId/toObject. +wxAccStatus wxDataViewCtrlAccessible::Navigate(wxNavDir navDir, int fromId, + int* toId, wxAccessible** toObject) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + + const int numRows = (int)dvWnd->GetRowCount(); + + if ( fromId == wxACC_SELF ) + { + switch ( navDir ) + { + case wxNAVDIR_FIRSTCHILD: + if ( numRows > 0 ) + { + *toId = 1; + *toObject = NULL; + return wxACC_OK; + } + return wxACC_FALSE; + case wxNAVDIR_LASTCHILD: + if ( numRows > 0 ) + { + *toId = numRows; + *toObject = NULL; + return wxACC_OK; + } + return wxACC_FALSE; + case wxNAVDIR_DOWN: + wxFALLTHROUGH; + case wxNAVDIR_NEXT: + wxFALLTHROUGH; + case wxNAVDIR_UP: + wxFALLTHROUGH; + case wxNAVDIR_PREVIOUS: + wxFALLTHROUGH; + case wxNAVDIR_LEFT: + wxFALLTHROUGH; + case wxNAVDIR_RIGHT: + // Standard wxWindow navigation is applicable here. + return wxWindowAccessible::Navigate(navDir, fromId, toId, toObject); + } + } + else + { + switch ( navDir ) + { + case wxNAVDIR_FIRSTCHILD: + return wxACC_FALSE; + case wxNAVDIR_LASTCHILD: + return wxACC_FALSE; + case wxNAVDIR_LEFT: + return wxACC_FALSE; + case wxNAVDIR_RIGHT: + return wxACC_FALSE; + case wxNAVDIR_DOWN: + wxFALLTHROUGH; + case wxNAVDIR_NEXT: + if ( fromId < numRows ) + { + *toId = fromId + 1; + *toObject = NULL; + return wxACC_OK; + } + return wxACC_FALSE; + case wxNAVDIR_PREVIOUS: + wxFALLTHROUGH; + case wxNAVDIR_UP: + if ( fromId > 1 ) + { + *toId = fromId - 1; + *toObject = NULL; + return wxACC_OK; + } + return wxACC_FALSE; + } + } + + // Let the framework handle the other cases. + return wxACC_NOT_IMPLEMENTED; +} + +// Gets the name of the specified object. +wxAccStatus wxDataViewCtrlAccessible::GetName(int childId, wxString* name) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + if ( childId == wxACC_SELF ) + { + *name = dvCtrl->GetName(); + } + else + { + wxDataViewItem item = dvCtrl->GetItemByRow(childId-1); + if ( !item.IsOk() ) + { + return wxACC_NOT_IMPLEMENTED; + } + + // Name is the value in the first textual column + // plus the name of this column: + // Column1: Value1 + wxString itemName; + + wxDataViewModel* model = dvCtrl->GetModel(); + const unsigned int numCols = dvCtrl->GetColumnCount(); + for ( unsigned int col = 0; col < numCols; col++ ) + { + wxDataViewColumn *dvCol = dvCtrl->GetColumnAt(col); + if ( dvCol->IsHidden() ) + continue; // skip it + + wxVariant value; + model->GetValue(value, item, dvCol->GetModelColumn()); + if ( !value.IsNull() && !value.IsType(wxS("bool")) ) + { + wxString vs = value.MakeString(); + if ( !vs.empty() ) + { + wxString colName = dvCol->GetTitle(); + // If column has no label then present its index. + if ( colName.empty() ) + { + // Columns are numbered from 1. + colName = _("Column") + wxString::Format(wxS(" %u"), col+1); + } + itemName = colName + wxS(": ") + vs; + break; + } + } + } + + if ( itemName.empty() ) + { + // Return row number if not textual column found. + // Rows are numbered from 1. + *name = _("Row") + wxString::Format(wxS(" %i"), childId); + } + else + { + *name = itemName; + } + } + + return wxACC_OK; +} + +// Gets the number of children. +wxAccStatus wxDataViewCtrlAccessible::GetChildCount(int* childCount) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + + *childCount = (int)dvWnd->GetRowCount(); + return wxACC_OK; +} + +// Gets the specified child (starting from 1). +// If *child is NULL and return value is wxACC_OK, +// this means that the child is a simple element and +// not an accessible object. +wxAccStatus wxDataViewCtrlAccessible::GetChild(int childId, wxAccessible** child) +{ + *child = (childId == wxACC_SELF) ? this : NULL; + return wxACC_OK; +} + +// Performs the default action. childId is 0 (the action for this object) +// or > 0 (the action for a child). +// Return wxACC_NOT_SUPPORTED if there is no default action for this +// window (e.g. an edit control). +wxAccStatus wxDataViewCtrlAccessible::DoDefaultAction(int childId) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + if ( childId != wxACC_SELF ) + { + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + if ( !dvWnd->IsList() ) + { + wxDataViewTreeNode* node = dvWnd->GetTreeNodeByRow(childId-1); + if ( node ) + { + if ( node->HasChildren() ) + { + // Expand or collapse the node. + node->ToggleOpen(); + return wxACC_OK; + } + } + } + } + + return wxACC_NOT_SUPPORTED; +} + +// Gets the default action for this object (0) or > 0 (the action for a child). +// Return wxACC_OK even if there is no action. actionName is the action, or the empty +// string if there is no action. +// The retrieved string describes the action that is performed on an object, +// not what the object does as a result. For example, a toolbar button that prints +// a document has a default action of "Press" rather than "Prints the current document." +wxAccStatus wxDataViewCtrlAccessible::GetDefaultAction(int childId, wxString* actionName) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + wxString action; + if ( childId != wxACC_SELF ) + { + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + if ( !dvWnd->IsList() ) + { + wxDataViewTreeNode* node = dvWnd->GetTreeNodeByRow(childId-1); + if ( node ) + { + if ( node->HasChildren() ) + { + if ( node->IsOpen() ) + action = _("Collapse"); + else + action = _("Expand"); + } + } + } + } + + *actionName = action; + return wxACC_OK; +} + +// Returns the description for this object or a child. +wxAccStatus wxDataViewCtrlAccessible::GetDescription(int childId, wxString* description) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + if ( childId == wxACC_SELF ) + { + *description = wxString::Format(_("This %s has %u columns"), + dvCtrl->GetName().c_str(), + dvCtrl->GetColumnCount()); + } + else + { + wxDataViewItem item = dvCtrl->GetItemByRow(childId-1); + if ( !item.IsOk() ) + { + return wxACC_NOT_IMPLEMENTED; + } + + // Description is concatenation of the contents of items in all columns: + // Column1: Value1, Column2: Value2, ... + // First textual item should be skipped because it is returned + // as a Name property. + wxString itemDesc; + + bool firstTextSkipped = false; + wxDataViewModel* model = dvCtrl->GetModel(); + const unsigned int numCols = dvCtrl->GetColumnCount(); + for ( unsigned int col = 0; col < numCols; col++ ) + { + if ( model->IsContainer(item) && !model->HasContainerColumns(item) ) + continue; // skip it + + wxDataViewColumn *dvCol = dvCtrl->GetColumnAt(col); + if ( dvCol->IsHidden() ) + continue; // skip it + + wxString valStr; + wxVariant value; + model->GetValue(value, item, dvCol->GetModelColumn()); + if ( value.IsNull() ) + { + valStr = _("null"); + } + else if ( value.IsType(wxS("bool")) ) + { + valStr = value.GetBool() ? _("yes") : _("no"); + } + else + { + // First textual item is returned as Name property + // so it needs to be skipped for Description. + valStr = value.MakeString(); + if ( !valStr.empty() && !firstTextSkipped ) + { + firstTextSkipped = true; + valStr.Empty(); + } + } + + if ( !valStr.empty() ) + { + wxString colName = dvCol->GetTitle(); + // If column has no label then present its index. + if ( colName.empty() ) + { + // Columns are numbered from 1. + colName = _("Column") + wxString::Format(wxS(" %u"), col+1); + } + + if ( !itemDesc.empty() ) + itemDesc.Append(wxS(", ")); + itemDesc.Append(colName); + itemDesc.Append(wxS(": ")); + itemDesc.Append(valStr); + } + } + + *description = itemDesc; + } + + return wxACC_OK; +} + +// Returns help text for this object or a child, similar to tooltip text. +wxAccStatus wxDataViewCtrlAccessible::GetHelpText(int childId, wxString* helpText) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + if ( childId == wxACC_SELF ) + { + *helpText = dvCtrl->GetHelpText(); + } + else + { + wxDataViewItem item = dvCtrl->GetItemByRow(childId-1); + if ( item.IsOk() ) + { + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + wxRect rect = dvWnd->GetItemRect(item, NULL); + *helpText = dvWnd->GetHelpTextAtPoint(rect.GetPosition(), wxHelpEvent::Origin_Keyboard); + } + else + { + *helpText = wxEmptyString; + } + } + return wxACC_OK; +} + +// Returns the keyboard shortcut for this object or child. +// Return e.g. ALT+K +wxAccStatus wxDataViewCtrlAccessible::GetKeyboardShortcut(int childId, wxString* shortcut) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + if ( childId != wxACC_SELF ) + { + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + if ( !dvWnd->IsList() ) + { + wxDataViewTreeNode* node = dvWnd->GetTreeNodeByRow(childId-1); + if ( node ) + { + if ( node->HasChildren() ) + { + if ( node->IsOpen() ) + *shortcut = _("Left"); + else + *shortcut = _("Right"); + + return wxACC_OK; + } + } + } + } + + return wxACC_FALSE; +} + +// Returns a role constant. +wxAccStatus wxDataViewCtrlAccessible::GetRole(int childId, wxAccRole* role) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + + if ( childId == wxACC_SELF ) + *role = dvWnd->IsList() ? wxROLE_SYSTEM_LIST : wxROLE_SYSTEM_OUTLINE; + else + *role = dvWnd->IsList() ? wxROLE_SYSTEM_LISTITEM : wxROLE_SYSTEM_OUTLINEITEM; + + return wxACC_OK; +} + +// Returns a state constant. +wxAccStatus wxDataViewCtrlAccessible::GetState(int childId, long* state) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + + long st = 0; + if ( childId == wxACC_SELF ) + { + if( dvWnd->IsFocusable() ) + st |= wxACC_STATE_SYSTEM_FOCUSABLE; + + if ( dvWnd->HasFocus() ) + st |= wxACC_STATE_SYSTEM_FOCUSED; + } + else + { + if( dvWnd->IsFocusable() ) + st |= wxACC_STATE_SYSTEM_FOCUSABLE | wxACC_STATE_SYSTEM_SELECTABLE; + if ( !dvWnd->IsSingleSel() ) + st |= wxACC_STATE_SYSTEM_MULTISELECTABLE | wxACC_STATE_SYSTEM_EXTSELECTABLE; + + if ( dvWnd->GetCurrentRow() == (unsigned int)childId-1 ) + st |= wxACC_STATE_SYSTEM_FOCUSED; + if ( dvWnd->IsRowSelected(childId-1) ) + st |= wxACC_STATE_SYSTEM_SELECTED; + + if ( !dvWnd->IsList() ) + { + wxDataViewTreeNode* node = dvWnd->GetTreeNodeByRow(childId-1); + if ( node ) + { + if ( node->HasChildren() ) + { + if ( node->IsOpen() ) + st |= wxACC_STATE_SYSTEM_EXPANDED; + else + st |= wxACC_STATE_SYSTEM_COLLAPSED; + } + } + } + + wxDataViewItem item = dvWnd->GetItemByRow(childId-1); + if ( item.IsOk() ) + { + if ( !dvWnd->HasEditableColumn(item) ) + { + st |= wxACC_STATE_SYSTEM_READONLY; + } + } + } + *state = st; + return wxACC_OK; +} + +// Returns a localized string representing the value for the object +// or child. +wxAccStatus wxDataViewCtrlAccessible::GetValue(int childId, wxString* strValue) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + wxString val; + + if ( childId != wxACC_SELF ) + { + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + if ( !dvWnd->IsList() ) + { + // In the tree view each item within the control has a zero-based value + // that represents its level within the hierarchy and this value + // is returned as a Value property. + wxDataViewTreeNode *node = dvWnd->GetTreeNodeByRow(childId-1); + if ( node ) + { + val = wxString::Format(wxS("%i"), node->GetIndentLevel()); + } + } + } + + *strValue = val; + return wxACC_OK; +} + +// Selects the object or child. +wxAccStatus wxDataViewCtrlAccessible::Select(int childId, wxAccSelectionFlags selectFlags) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + + if ( childId == wxACC_SELF ) + { + if ( selectFlags == wxACC_SEL_TAKEFOCUS ) + { + dvWnd->SetFocus(); + } + else if ( selectFlags != wxACC_SEL_NONE ) + { + wxFAIL_MSG( wxS("Invalid selection flag") ); + return wxACC_INVALID_ARG; + } + } + else + { + // These flags are not allowed in the single-selection mode: + if ( dvWnd->IsSingleSel() && + selectFlags & (wxACC_SEL_EXTENDSELECTION | wxACC_SEL_ADDSELECTION | wxACC_SEL_REMOVESELECTION) ) + { + wxFAIL_MSG( wxS("Invalid selection flag") ); + return wxACC_INVALID_ARG; + } + + const int row = childId-1; + + if ( selectFlags == wxACC_SEL_TAKEFOCUS ) + { + dvWnd->ChangeCurrentRow(row); + } + else if ( selectFlags & wxACC_SEL_TAKESELECTION ) + { + // This flag must not be combined with the following flags: + if ( selectFlags & (wxACC_SEL_EXTENDSELECTION | wxACC_SEL_ADDSELECTION | wxACC_SEL_REMOVESELECTION) ) + { + wxFAIL_MSG( wxS("Invalid selection flag") ); + return wxACC_INVALID_ARG; + } + + dvWnd->UnselectAllRows(); + dvWnd->SelectRow(row, true); + if ( selectFlags & wxACC_SEL_TAKEFOCUS || dvWnd->IsSingleSel() ) + { + dvWnd->ChangeCurrentRow(row); + } + } + else if ( selectFlags & wxACC_SEL_EXTENDSELECTION ) + { + // This flag must not be combined with the following flag: + if ( selectFlags & wxACC_SEL_TAKESELECTION ) + { + wxFAIL_MSG( wxS("Invalid selection flag") ); + return wxACC_INVALID_ARG; + } + // These flags cannot be set together: + if ( (selectFlags & (wxACC_SEL_ADDSELECTION | wxACC_SEL_REMOVESELECTION)) + == (wxACC_SEL_ADDSELECTION | wxACC_SEL_REMOVESELECTION) ) + { + wxFAIL_MSG( wxS("Invalid selection flag") ); + return wxACC_INVALID_ARG; + } + + // We have to have a focused object as a selection anchor. + unsigned int focusedRow = dvWnd->GetCurrentRow(); + if ( focusedRow == (unsigned int)-1 ) + { + wxFAIL_MSG( wxS("No selection anchor") ); + return wxACC_INVALID_ARG; + } + + bool doSelect; + if ( selectFlags & wxACC_SEL_ADDSELECTION ) + doSelect = true; + else if ( selectFlags & wxACC_SEL_REMOVESELECTION ) + doSelect = false; + else + // If the anchor object is selected, the selection is extended. + // If the anchor object is not selected, all objects are unselected. + doSelect = dvWnd->IsRowSelected(focusedRow); + + if ( doSelect ) + { + dvWnd->SelectRows(focusedRow, row); + } + else + { + for( int r = focusedRow; r <= row; r++ ) + dvWnd->SelectRow(r, false); + } + + if ( selectFlags & wxACC_SEL_TAKEFOCUS ) + { + dvWnd->ChangeCurrentRow(row); + } + } + else if ( selectFlags & wxACC_SEL_ADDSELECTION ) + { + // This flag must not be combined with the following flags: + if ( selectFlags & (wxACC_SEL_TAKESELECTION | wxACC_SEL_REMOVESELECTION) ) + { + wxFAIL_MSG( wxS("Invalid selection flag") ); + return wxACC_INVALID_ARG; + } + + // Combination with wxACC_SEL_EXTENDSELECTION is already handled + // (see wxACC_SEL_EXTENDSELECTION block). + dvWnd->SelectRow(row, true); + if ( selectFlags & wxACC_SEL_TAKEFOCUS ) + { + dvWnd->ChangeCurrentRow(row); + } + } + else if ( selectFlags & wxACC_SEL_REMOVESELECTION ) + { + // This flag must not be combined with the following flags: + if ( selectFlags & (wxACC_SEL_TAKESELECTION | wxACC_SEL_ADDSELECTION) ) + { + wxFAIL_MSG( wxS("Invalid selection flag") ); + return wxACC_INVALID_ARG; + } + + // Combination with wxACC_SEL_EXTENDSELECTION is already handled + // (see wxACC_SEL_EXTENDSELECTION block). + dvWnd->SelectRow(row, false); + if ( selectFlags & wxACC_SEL_TAKEFOCUS ) + { + dvWnd->ChangeCurrentRow(row); + } + } + } + + return wxACC_OK; +} + +// Gets the window with the keyboard focus. +// If childId is 0 and child is NULL, no object in +// this subhierarchy has the focus. +// If this object has the focus, child should be 'this'. +wxAccStatus wxDataViewCtrlAccessible::GetFocus(int* childId, wxAccessible** child) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + wxDataViewMainWindow* dvWnd = wxDynamicCast(dvCtrl->GetMainWindow(), wxDataViewMainWindow); + + const unsigned int row = dvWnd->GetCurrentRow(); + if ( row != (unsigned int)childId-1 ) + { + *childId = row+1; + *child = NULL; + } + else if ( dvWnd->HasFocus() ) + { + *childId = wxACC_SELF; + *child = this; + } + else + { + *childId = 0; + *child = NULL; + } + + return wxACC_OK; +} + +#if wxUSE_VARIANT +// Gets a variant representing the selected children +// of this object. +// Acceptable values: +// - a null variant (IsNull() returns true) +// - a "void*" pointer to a wxAccessible child object +// - an integer representing the selected child element, +// or 0 if this object is selected (GetType() == wxT("long")) +// - a list variant (GetType() == wxT("list")) +wxAccStatus wxDataViewCtrlAccessible::GetSelections(wxVariant* selections) +{ + wxDataViewCtrl* dvCtrl = wxDynamicCast(GetWindow(), wxDataViewCtrl); + wxCHECK( dvCtrl, wxACC_FAIL ); + + wxDataViewItemArray sel; + dvCtrl->GetSelections(sel); + if ( sel.IsEmpty() ) + { + selections->MakeNull(); + } + else + { + wxVariantList tempList; + wxVariant v(tempList); + + for( size_t i = 0; i < sel.GetCount(); i++ ) + { + int row = dvCtrl->GetRowByItem(sel[i]); + v.Append(wxVariant((long)row+1)); + } + + // Don't return the list if one child is selected. + if ( v.GetCount() == 1 ) + *selections = wxVariant(v[0].GetLong()); + else + *selections = v; + } + + return wxACC_OK; +} +#endif // wxUSE_VARIANT + +#endif // wxUSE_ACCESSIBILITY + #endif // wxUSE_DATAVIEWCTRL