1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   Grid.cpp
6 
7   Leland Lucius
8 
9 *******************************************************************//**
10 
11 \class Grid
12 \brief Supplies an accessible grid based on wxGrid.
13 
14 *//*******************************************************************/
15 
16 
17 #include "Grid.h"
18 
19 #include <wx/setup.h> // for wxUSE_* macros
20 
21 #include <wx/defs.h>
22 #include <wx/choice.h>
23 #include <wx/clipbrd.h>
24 #include <wx/dc.h>
25 #include <wx/grid.h>
26 #include <wx/intl.h>
27 #include <wx/settings.h>
28 #include <wx/toplevel.h>
29 
30 #include "SelectedRegion.h"
31 
32 #if wxUSE_ACCESSIBILITY
33 #include "WindowAccessible.h"
34 
35 /**********************************************************************//**
36 \class GridAx
37 \brief wxAccessible object providing grid information for Grid.
38 **************************************************************************/
39 class GridAx final : public WindowAccessible
40 {
41 
42  public:
43 
44    GridAx(Grid *grid);
45 
46    void SetCurrentCell(int row, int col);
47    void TableUpdated();
48    bool GetRowCol(int childId, int & row, int & col);
49 
50    // Retrieves the address of an IDispatch interface for the specified child.
51    // All objects must support this property.
52    wxAccStatus GetChild(int childId, wxAccessible **child) override;
53 
54    // Gets the number of children.
55    wxAccStatus GetChildCount(int *childCount) override;
56 
57    // Gets the default action for this object (0) or > 0 (the action for a child).
58    // Return wxACC_OK even if there is no action. actionName is the action, or the empty
59    // string if there is no action.
60    // The retrieved string describes the action that is performed on an object,
61    // not what the object does as a result. For example, a toolbar button that prints
62    // a document has a default action of "Press" rather than "Prints the current document."
63    wxAccStatus GetDefaultAction(int childId, wxString *actionName) override;
64 
65    // Returns the description for this object or a child.
66    wxAccStatus GetDescription(int childId, wxString *description) override;
67 
68    // Gets the window with the keyboard focus.
69    // If childId is 0 and child is NULL, no object in
70    // this subhierarchy has the focus.
71    // If this object has the focus, child should be 'this'.
72    wxAccStatus GetFocus(int *childId, wxAccessible **child) override;
73 
74    // Returns help text for this object or a child, similar to tooltip text.
75    wxAccStatus GetHelpText(int childId, wxString *helpText) override;
76 
77    // Returns the keyboard shortcut for this object or child.
78    // Return e.g. ALT+K
79    wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut) override;
80 
81    // Returns the rectangle for this object (id = 0) or a child element (id > 0).
82    // rect is in screen coordinates.
83    wxAccStatus GetLocation(wxRect & rect, int elementId) override;
84 
85    // Gets the name of the specified object.
86    wxAccStatus GetName(int childId, wxString *name) override;
87 
88    // Gets the parent, or NULL.
89    wxAccStatus GetParent(wxAccessible **parent) override;
90 
91    // Returns a role constant.
92    wxAccStatus GetRole(int childId, wxAccRole *role) override;
93 
94    // Gets a variant representing the selected children
95    // of this object.
96    // Acceptable values:
97    // - a null variant (IsNull() returns TRUE)
98    // - a list variant (GetType() == wxT("list"))
99    // - an integer representing the selected child element,
100    //   or 0 if this object is selected (GetType() == wxT("long"))
101    // - a "void*" pointer to a wxAccessible child object
102    wxAccStatus GetSelections(wxVariant *selections) override;
103 
104    // Returns a state constant.
105    wxAccStatus GetState(int childId, long* state) override;
106 
107    // Returns a localized string representing the value for the object
108    // or child.
109    wxAccStatus GetValue(int childId, wxString* strValue) override;
110 
111 #if defined(__WXMAC__)
112    // Selects the object or child.
113    wxAccStatus Select(int childId, wxAccSelectionFlags selectFlags) override;
114 #endif
115 
116    Grid *mGrid;
117    int mLastId;
118 
119 };
120 #endif
121 
NumericEditor(NumericConverter::Type type,const NumericFormatSymbol & format,double rate)122 NumericEditor::NumericEditor
123    (NumericConverter::Type type, const NumericFormatSymbol &format, double rate)
124 {
125    mType = type;
126    mFormat = format;
127    mRate = rate;
128    mOld = 0.0;
129 }
130 
~NumericEditor()131 NumericEditor::~NumericEditor()
132 {
133 }
134 
Create(wxWindow * parent,wxWindowID id,wxEvtHandler * handler)135 void NumericEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *handler)
136 {
137    wxASSERT(parent); // to justify safenew
138    auto control = safenew NumericTextCtrl(
139       parent, wxID_ANY,
140       mType,
141       mFormat,
142       mOld,
143       mRate,
144       NumericTextCtrl::Options{}
145          .AutoPos(true)
146          .InvalidValue(mType == NumericTextCtrl::FREQUENCY,
147                        SelectedRegion::UndefinedFrequency)
148    );
149    m_control = control;
150 
151    wxGridCellEditor::Create(parent, id, handler);
152 }
153 
SetSize(const wxRect & rect)154 void NumericEditor::SetSize(const wxRect &rect)
155 {
156    wxSize size = m_control->GetSize();
157 
158    // Always center...looks bad otherwise
159    int x = rect.x + ((rect.width / 2) - (size.x / 2)) + 1;
160    int y = rect.y + ((rect.height / 2) - (size.y / 2)) + 1;
161 
162    m_control->Move(x, y);
163 }
164 
BeginEdit(int row,int col,wxGrid * grid)165 void NumericEditor::BeginEdit(int row, int col, wxGrid *grid)
166 {
167    wxGridTableBase *table = grid->GetTable();
168 
169    mOldString = table->GetValue(row, col);
170    mOldString.ToDouble(&mOld);
171 
172    auto control = GetNumericTextControl();
173    control->SetValue(mOld);
174    control->EnableMenu();
175 
176    control->SetFocus();
177 }
178 
179 
EndEdit(int WXUNUSED (row),int WXUNUSED (col),const wxGrid * WXUNUSED (grid),const wxString & WXUNUSED (oldval),wxString * newval)180 bool NumericEditor::EndEdit(int WXUNUSED(row), int WXUNUSED(col), const wxGrid *WXUNUSED(grid), const wxString &WXUNUSED(oldval), wxString *newval)
181 {
182    double newtime = GetNumericTextControl()->GetValue();
183    bool changed = newtime != mOld;
184 
185    if (changed) {
186       mValueAsString = wxString::Format(wxT("%g"), newtime);
187       *newval = mValueAsString;
188    }
189 
190    return changed;
191 }
192 
ApplyEdit(int row,int col,wxGrid * grid)193 void NumericEditor::ApplyEdit(int row, int col, wxGrid *grid)
194 {
195    grid->GetTable()->SetValue(row, col, mValueAsString);
196 }
197 
Reset()198 void NumericEditor::Reset()
199 {
200    GetNumericTextControl()->SetValue(mOld);
201 }
202 
IsAcceptedKey(wxKeyEvent & event)203 bool NumericEditor::IsAcceptedKey(wxKeyEvent &event)
204 {
205    if (wxGridCellEditor::IsAcceptedKey(event)) {
206       if (event.GetKeyCode() == WXK_RETURN) {
207          return true;
208       }
209    }
210 
211    return false;
212 }
213 
214 // Clone is required by wxwidgets; implemented via copy constructor
Clone() const215 wxGridCellEditor *NumericEditor::Clone() const
216 {
217    return safenew NumericEditor{ mType, mFormat, mRate };
218 }
219 
GetValue() const220 wxString NumericEditor::GetValue() const
221 {
222    return wxString::Format(wxT("%g"), GetNumericTextControl()->GetValue());
223 }
224 
GetFormat() const225 NumericFormatSymbol NumericEditor::GetFormat() const
226 {
227    return mFormat;
228 }
229 
GetRate() const230 double NumericEditor::GetRate() const
231 {
232    return mRate;
233 }
234 
SetFormat(const NumericFormatSymbol & format)235 void NumericEditor::SetFormat(const NumericFormatSymbol &format)
236 {
237    mFormat = format;
238 }
239 
SetRate(double rate)240 void NumericEditor::SetRate(double rate)
241 {
242    mRate = rate;
243 }
244 
~NumericRenderer()245 NumericRenderer::~NumericRenderer()
246 {
247 }
248 
Draw(wxGrid & grid,wxGridCellAttr & attr,wxDC & dc,const wxRect & rect,int row,int col,bool isSelected)249 void NumericRenderer::Draw(wxGrid &grid,
250                         wxGridCellAttr &attr,
251                         wxDC &dc,
252                         const wxRect &rect,
253                         int row,
254                         int col,
255                         bool isSelected)
256 {
257    wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
258 
259    wxGridTableBase *table = grid.GetTable();
260    NumericEditor *ne =
261       static_cast<NumericEditor *>(grid.GetCellEditor(row, col));
262    wxString tstr;
263 
264    if (ne) {
265       double value;
266 
267       table->GetValue(row, col).ToDouble(&value);
268 
269       NumericTextCtrl tt(&grid, wxID_ANY,
270                       mType,
271                       ne->GetFormat(),
272                       value,
273                       ne->GetRate(),
274                       NumericTextCtrl::Options{}.AutoPos(true),
275                       wxPoint(10000, 10000));  // create offscreen
276       tstr = tt.GetString();
277 
278       ne->DecRef();
279    }
280 
281    dc.SetBackgroundMode(wxTRANSPARENT);
282 
283    if (grid.IsEnabled())
284    {
285       if (isSelected)
286       {
287          dc.SetTextBackground(grid.GetSelectionBackground());
288          dc.SetTextForeground(grid.GetSelectionForeground());
289       }
290       else
291       {
292          dc.SetTextBackground(attr.GetBackgroundColour());
293          dc.SetTextForeground(attr.GetTextColour());
294       }
295    }
296    else
297    {
298       dc.SetTextBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
299       dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
300    }
301 
302    dc.SetFont(attr.GetFont());
303 
304    int hAlign, vAlign;
305 
306    attr.GetAlignment(&hAlign, &vAlign);
307 
308    grid.DrawTextRectangle(dc, tstr, rect, hAlign, vAlign);
309 }
310 
GetBestSize(wxGrid & grid,wxGridCellAttr & WXUNUSED (attr),wxDC & WXUNUSED (dc),int row,int col)311 wxSize NumericRenderer::GetBestSize(wxGrid &grid,
312                                  wxGridCellAttr & WXUNUSED(attr),
313                                  wxDC & WXUNUSED(dc),
314                                  int row,
315                                  int col)
316 {
317    wxGridTableBase *table = grid.GetTable();
318    NumericEditor *ne =
319       static_cast<NumericEditor *>(grid.GetCellEditor(row, col));
320    wxSize sz;
321 
322    if (ne) {
323       double value;
324       table->GetValue(row, col).ToDouble(&value);
325       NumericTextCtrl tt(&grid, wxID_ANY,
326                       mType,
327                       ne->GetFormat(),
328                       value,
329                       ne->GetRate(),
330                       NumericTextCtrl::Options{}.AutoPos(true),
331                       wxPoint(10000, 10000));  // create offscreen
332       sz = tt.GetSize();
333 
334       ne->DecRef();
335    }
336 
337    return sz;
338 }
339 
340 // Clone is required by wxwidgets; implemented via copy constructor
Clone() const341 wxGridCellRenderer *NumericRenderer::Clone() const
342 {
343    return safenew NumericRenderer{ mType };
344 }
345 
ChoiceEditor(size_t count,const wxString choices[])346 ChoiceEditor::ChoiceEditor(size_t count, const wxString choices[])
347 {
348    if (count) {
349       mChoices.reserve(count);
350       for (size_t n = 0; n < count; n++) {
351          mChoices.push_back(choices[n]);
352       }
353    }
354 }
355 
ChoiceEditor(const wxArrayString & choices)356 ChoiceEditor::ChoiceEditor(const wxArrayString &choices)
357 {
358    mChoices = choices;
359 }
360 
~ChoiceEditor()361 ChoiceEditor::~ChoiceEditor()
362 {
363    if (m_control)
364       mHandler.DisconnectEvent(m_control);
365 }
366 
367 // Clone is required by wxwidgets; implemented via copy constructor
Clone() const368 wxGridCellEditor *ChoiceEditor::Clone() const
369 {
370    return safenew ChoiceEditor(mChoices);
371 }
372 
Create(wxWindow * parent,wxWindowID id,wxEvtHandler * evtHandler)373 void ChoiceEditor::Create(wxWindow* parent, wxWindowID id, wxEvtHandler* evtHandler)
374 {
375    m_control = safenew wxChoice(parent,
376                             id,
377                             wxDefaultPosition,
378                             wxDefaultSize,
379                             mChoices);
380 
381    wxGridCellEditor::Create(parent, id, evtHandler);
382    mHandler.ConnectEvent(m_control);
383 }
384 
SetSize(const wxRect & rect)385 void ChoiceEditor::SetSize(const wxRect &rect)
386 {
387    wxSize size = m_control->GetSize();
388 
389    // Always center...looks bad otherwise
390    int x = rect.x + ((rect.width / 2) - (size.x / 2)) + 1;
391    int y = rect.y + ((rect.height / 2) - (size.y / 2)) + 1;
392 
393    m_control->Move(x, y);
394 }
395 
BeginEdit(int row,int col,wxGrid * grid)396 void ChoiceEditor::BeginEdit(int row, int col, wxGrid* grid)
397 {
398    if (!m_control)
399       return;
400 
401    mOld = grid->GetTable()->GetValue(row, col);
402 
403    Choice()->Clear();
404    Choice()->Append(mChoices);
405    Choice()->SetSelection( make_iterator_range( mChoices ).index( mOld ) );
406    Choice()->SetFocus();
407 }
408 
EndEdit(int row,int col,wxGrid * grid)409 bool ChoiceEditor::EndEdit(int row, int col, wxGrid *grid)
410 {
411     wxString newvalue;
412     bool changed = EndEdit(row, col, grid, mOld, &newvalue);
413     if (changed) {
414         ApplyEdit(row, col, grid);
415     }
416     return changed;
417 }
418 
EndEdit(int WXUNUSED (row),int WXUNUSED (col),const wxGrid * WXUNUSED (grid),const wxString & WXUNUSED (oldval),wxString * newval)419 bool ChoiceEditor::EndEdit(int WXUNUSED(row), int WXUNUSED(col),
420                            const wxGrid* WXUNUSED(grid),
421                            const wxString &WXUNUSED(oldval), wxString *newval)
422 {
423    int sel = Choice()->GetSelection();
424 
425    // This can happen if the wxChoice control is displayed and the list of choices get changed
426    if ((sel < 0) || (sel >= (int)(mChoices.size())))
427    {
428       return false;
429    }
430 
431    wxString val = mChoices[sel];
432    bool changed = val != mOld;
433 
434    if (changed)
435    {
436       mValueAsString = val;
437       *newval = val;
438    }
439 
440    return changed;
441 }
442 
ApplyEdit(int row,int col,wxGrid * grid)443 void ChoiceEditor::ApplyEdit(int row, int col, wxGrid *grid)
444 {
445    grid->GetTable()->SetValue(row, col, mValueAsString);
446 }
447 
Reset()448 void ChoiceEditor::Reset()
449 {
450    Choice()->SetSelection( make_iterator_range( mChoices ).index( mOld ) );
451 }
452 
SetChoices(const wxArrayString & choices)453 void ChoiceEditor::SetChoices(const wxArrayString &choices)
454 {
455    mChoices = choices;
456 }
457 
GetValue() const458 wxString ChoiceEditor::GetValue() const
459 {
460    return mChoices[Choice()->GetSelection()];
461 }
462 
463 ///
464 ///
465 ///
466 
BEGIN_EVENT_TABLE(Grid,wxGrid)467 BEGIN_EVENT_TABLE(Grid, wxGrid)
468    EVT_SET_FOCUS(Grid::OnSetFocus)
469    EVT_KEY_DOWN(Grid::OnKeyDown)
470    EVT_GRID_SELECT_CELL(Grid::OnSelectCell)
471    EVT_GRID_EDITOR_SHOWN(Grid::OnEditorShown)
472 END_EVENT_TABLE()
473 
474 Grid::Grid(wxWindow *parent,
475            wxWindowID id,
476            const wxPoint& pos,
477            const wxSize& size,
478            long style,
479            const wxString& name)
480 : wxGrid(parent, id, pos, size, style | wxWANTS_CHARS, name)
481 {
482 #if wxUSE_ACCESSIBILITY
483    GetGridWindow()->SetAccessible(mAx = safenew GridAx(this));
484 #endif
485 
486    // RegisterDataType takes ownership of renderer and editor
487 
488    RegisterDataType(GRID_VALUE_TIME,
489                     safenew NumericRenderer{ NumericConverter::TIME },
490                     safenew NumericEditor
491                       { NumericTextCtrl::TIME,
492                         NumericConverter::SecondsFormat(), 44100.0 });
493 
494    RegisterDataType(GRID_VALUE_FREQUENCY,
495                     safenew NumericRenderer{ NumericConverter::FREQUENCY },
496                     safenew NumericEditor
497                     { NumericTextCtrl::FREQUENCY,
498                       NumericConverter::HertzFormat(), 44100.0 });
499 
500    RegisterDataType(GRID_VALUE_CHOICE,
501                     safenew wxGridCellStringRenderer,
502                     safenew ChoiceEditor);
503 
504    // Bug #2803:
505    // Ensure selection doesn't show up.
506    SetSelectionForeground(GetDefaultCellTextColour());
507    SetSelectionBackground(GetDefaultCellBackgroundColour());
508 }
509 
~Grid()510 Grid::~Grid()
511 {
512 #if wxUSE_ACCESSIBILITY
513    int cnt = mChildren.size();
514    while (cnt--) {
515       // PRL: I found this loop destroying right-to-left.
516       // Is the sequence of destruction important?
517       mChildren.pop_back();
518    }
519 #endif
520 }
521 
OnSetFocus(wxFocusEvent & event)522 void Grid::OnSetFocus(wxFocusEvent &event)
523 {
524    event.Skip();
525 
526 #if wxUSE_ACCESSIBILITY
527    mAx->SetCurrentCell(GetGridCursorRow(), GetGridCursorCol());
528 #endif
529 }
530 
OnSelectCell(wxGridEvent & event)531 void Grid::OnSelectCell(wxGridEvent &event)
532 {
533    event.Skip();
534 
535    MakeCellVisible(event.GetRow(), event.GetCol());
536 
537 #if wxUSE_ACCESSIBILITY
538    mAx->SetCurrentCell(event.GetRow(), event.GetCol());
539 #endif
540 }
541 
OnEditorShown(wxGridEvent & event)542 void Grid::OnEditorShown(wxGridEvent &event)
543 {
544    event.Skip();
545 
546    // Bug #2803 (comment 7):
547    // Select row whenever an editor is displayed
548    SelectRow(GetGridCursorRow());
549 }
550 
OnKeyDown(wxKeyEvent & event)551 void Grid::OnKeyDown(wxKeyEvent &event)
552 {
553    auto keyCode = event.GetKeyCode();
554    int crow = GetGridCursorRow();
555    int ccol = GetGridCursorCol();
556 
557    if (event.CmdDown() && crow != wxGridNoCellCoords.GetRow() && ccol != wxGridNoCellCoords.GetCol())
558    {
559       wxClipboardLocker cb;
560 
561       switch (keyCode)
562       {
563          case 'C': // Copy
564          {
565             wxTextDataObject *data = safenew wxTextDataObject(GetCellValue(crow, ccol));
566             wxClipboard::Get()->SetData(data);
567             return;
568          }
569          break;
570 
571          case 'X': // Cut
572          {
573             wxTextDataObject *data = safenew wxTextDataObject(GetCellValue(crow, ccol));
574             wxClipboard::Get()->SetData(data);
575             SetCellValue(crow, ccol, "" );
576             return;
577          }
578          break;
579 
580          case 'V': // Paste
581          {
582             if (wxClipboard::Get()->IsSupported(wxDF_UNICODETEXT))
583             {
584                wxTextDataObject data;
585                if (wxClipboard::Get()->GetData(data))
586                {
587                   SetCellValue(crow, ccol, data.GetText());
588                   return;
589                }
590             }
591          }
592          break;
593       }
594    }
595 
596    switch (keyCode)
597    {
598       case WXK_LEFT:
599       case WXK_RIGHT:
600       {
601          int rows = GetNumberRows();
602          int cols = GetNumberCols();
603 
604          const bool has_cells = rows > 0 && cols > 0;
605 
606          if (has_cells) {
607             int crow = GetGridCursorRow();
608             int ccol = GetGridCursorCol();
609 
610             const bool has_no_selection = crow == wxGridNoCellCoords.GetRow() || ccol == wxGridNoCellCoords.GetCol();
611 
612             if (has_no_selection) {
613                SetGridCursor(0, 0);
614             }
615             else if (event.GetKeyCode() == WXK_LEFT) {
616                if (crow == 0 && ccol == 0) {
617                   // do nothing
618                }
619                else if (ccol == 0) {
620                   SetGridCursor(crow - 1, cols - 1);
621                }
622                else {
623                   SetGridCursor(crow, ccol - 1);
624                }
625             }
626             else {
627                if (crow == rows - 1 && ccol == cols - 1) {
628                   // do nothing
629                }
630                else if (ccol == cols - 1) {
631                   SetGridCursor(crow + 1, 0);
632                }
633                else {
634                   SetGridCursor(crow, ccol + 1);
635                }
636             }
637          }
638 
639 #if wxUSE_ACCESSIBILITY
640          // Make sure the NEW cell is made available to the screen reader
641          mAx->SetCurrentCell(GetGridCursorRow(), GetGridCursorCol());
642 #endif
643       }
644       break;
645 
646       case WXK_TAB:
647       {
648          if (event.ControlDown()) {
649             int flags = wxNavigationKeyEvent::FromTab |
650                         ( event.ShiftDown() ?
651                           wxNavigationKeyEvent::IsBackward :
652                           wxNavigationKeyEvent::IsForward );
653             Navigate(flags);
654             return;
655          }
656 
657          int rows = GetNumberRows();
658          int cols = GetNumberCols();
659          int crow = GetGridCursorRow();
660          int ccol = GetGridCursorCol();
661 
662          const auto is_empty = rows <= 0 || cols <= 0;
663          const auto has_no_selection = crow == wxGridNoCellCoords.GetRow() || ccol == wxGridNoCellCoords.GetCol();
664 
665          if (event.ShiftDown()) {
666             if (is_empty) {
667                Navigate(wxNavigationKeyEvent::FromTab | wxNavigationKeyEvent::IsBackward);
668                return;
669             }
670 
671             if (crow == 0 && ccol == 0) {
672                Navigate(wxNavigationKeyEvent::FromTab | wxNavigationKeyEvent::IsBackward);
673                return;
674             }
675 
676             if (has_no_selection) {
677                SetGridCursor(rows -1, cols - 1);
678             }
679             else if (ccol == 0) {
680                SetGridCursor(crow - 1, cols - 1);
681             }
682             else {
683                SetGridCursor(crow, ccol - 1);
684             }
685          }
686          else {
687             if (is_empty) {
688                Navigate(wxNavigationKeyEvent::FromTab | wxNavigationKeyEvent::IsForward);
689                return;
690             }
691 
692             if (crow == rows - 1 && ccol == cols - 1) {
693                Navigate(wxNavigationKeyEvent::FromTab | wxNavigationKeyEvent::IsForward);
694                return;
695             }
696 
697             if (has_no_selection) {
698                SetGridCursor(0, 0);
699             }
700             else if (ccol == cols - 1) {
701                SetGridCursor(crow + 1, 0);
702             }
703             else {
704                SetGridCursor(crow, ccol + 1);
705             }
706          }
707 
708          MakeCellVisible(GetGridCursorRow(), GetGridCursorCol());
709 
710 #if wxUSE_ACCESSIBILITY
711          // Make sure the NEW cell is made available to the screen reader
712          mAx->SetCurrentCell(GetGridCursorRow(), GetGridCursorCol());
713 #endif
714       }
715       break;
716 
717       case WXK_RETURN:
718       case WXK_NUMPAD_ENTER:
719       {
720          if (!IsCellEditControlShown()) {
721             wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
722             wxWindow *def = tlw->GetDefaultItem();
723             if (def && def->IsEnabled()) {
724                wxCommandEvent cevent(wxEVT_COMMAND_BUTTON_CLICKED,
725                                      def->GetId());
726                cevent.SetEventObject( def );
727                GetParent()->GetEventHandler()->ProcessEvent(cevent);
728             }
729          }
730          else {
731             wxGrid::OnKeyDown(event);
732 
733             // This looks strange, but what it does is selects the cell when
734             // enter is pressed after editing.  Without it, Jaws and Window-Eyes
735             // do not speak the NEW cell contents (the one below the edited one).
736             SetGridCursor(GetGridCursorRow(), GetGridCursorCol());
737          }
738          break;
739       }
740 
741       default:
742          wxGrid::OnKeyDown(event);
743       break;
744    }
745 }
746 
747 #if wxUSE_ACCESSIBILITY
ClearGrid()748 void Grid::ClearGrid()
749 {
750    wxGrid::ClearGrid();
751 
752    mAx->TableUpdated();
753 
754    return;
755 }
756 
InsertRows(int pos,int numRows,bool updateLabels)757 bool Grid::InsertRows(int pos, int numRows, bool updateLabels)
758 {
759    bool res = wxGrid::InsertRows(pos, numRows, updateLabels);
760 
761    mAx->TableUpdated();
762 
763    return res;
764 }
765 
AppendRows(int numRows,bool updateLabels)766 bool Grid::AppendRows(int numRows, bool updateLabels)
767 {
768    bool res = wxGrid::AppendRows(numRows, updateLabels);
769 
770    mAx->TableUpdated();
771 
772    return res;
773 }
774 
DeleteRows(int pos,int numRows,bool updateLabels)775 bool Grid::DeleteRows(int pos, int numRows, bool updateLabels)
776 {
777    bool res = wxGrid::DeleteRows(pos, numRows, updateLabels);
778 
779    mAx->TableUpdated();
780 
781    return res;
782 }
783 
InsertCols(int pos,int numCols,bool updateLabels)784 bool Grid::InsertCols(int pos, int numCols, bool updateLabels)
785 {
786    bool res = wxGrid::InsertCols(pos, numCols, updateLabels);
787 
788    mAx->TableUpdated();
789 
790    return res;
791 }
792 
AppendCols(int numCols,bool updateLabels)793 bool Grid::AppendCols(int numCols, bool updateLabels)
794 {
795    bool res = wxGrid::AppendCols(numCols, updateLabels);
796 
797    mAx->TableUpdated();
798 
799    return res;
800 }
801 
DeleteCols(int pos,int numCols,bool updateLabels)802 bool Grid::DeleteCols(int pos, int numCols, bool updateLabels)
803 {
804    bool res = wxGrid::DeleteCols(pos, numCols, updateLabels);
805 
806    mAx->TableUpdated();
807 
808    return res;
809 }
810 
GridAx(Grid * grid)811 GridAx::GridAx(Grid *grid)
812 : WindowAccessible(grid->GetGridWindow())
813 {
814    mGrid = grid;
815    mLastId = -1;
816 }
817 
TableUpdated()818 void GridAx::TableUpdated()
819 {
820    NotifyEvent(wxACC_EVENT_OBJECT_REORDER,
821                mGrid->GetGridWindow(),
822                wxOBJID_CLIENT,
823                0);
824 }
825 
SetCurrentCell(int row,int col)826 void GridAx::SetCurrentCell(int row, int col)
827 {
828    int id = (((row * mGrid->GetNumberCols()) + col) + 1);
829 
830    if (mLastId != -1) {
831       NotifyEvent(wxACC_EVENT_OBJECT_SELECTIONREMOVE,
832                mGrid->GetGridWindow(),
833                wxOBJID_CLIENT,
834                mLastId);
835    }
836 
837    if (mGrid == wxWindow::FindFocus()) {
838       NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
839                   mGrid->GetGridWindow(),
840                   wxOBJID_CLIENT,
841                   id);
842    }
843 
844    NotifyEvent(wxACC_EVENT_OBJECT_SELECTION,
845                mGrid->GetGridWindow(),
846                wxOBJID_CLIENT,
847                id);
848 
849    mLastId = id;
850 }
851 
GetRowCol(int childId,int & row,int & col)852 bool GridAx::GetRowCol(int childId, int & row, int & col)
853 {
854    if (childId == wxACC_SELF) {
855       return false;
856    }
857 
858    int cols = mGrid->GetNumberCols();
859    int id = childId - 1;
860 
861    row = id / cols;
862    col = id % cols;
863 
864    return true;
865 }
866 
867 // Retrieves the address of an IDispatch interface for the specified child.
868 // All objects must support this property.
GetChild(int childId,wxAccessible ** child)869 wxAccStatus GridAx::GetChild(int childId, wxAccessible** child)
870 {
871    if (childId == wxACC_SELF) {
872       *child = this;
873    }
874    else {
875       *child = NULL;
876    }
877 
878    return wxACC_OK;
879 }
880 
881 // Gets the number of children.
GetChildCount(int * childCount)882 wxAccStatus GridAx::GetChildCount(int *childCount)
883 {
884    *childCount = mGrid->GetNumberRows() * mGrid->GetNumberCols();
885 
886    return wxACC_OK;
887 }
888 
889 // Gets the default action for this object (0) or > 0 (the action for a child).
890 // Return wxACC_OK even if there is no action. actionName is the action, or the empty
891 // string if there is no action.
892 // The retrieved string describes the action that is performed on an object,
893 // not what the object does as a result. For example, a toolbar button that prints
894 // a document has a default action of "Press" rather than "Prints the current document."
GetDefaultAction(int WXUNUSED (childId),wxString * actionName)895 wxAccStatus GridAx::GetDefaultAction(int WXUNUSED(childId), wxString *actionName)
896 {
897    actionName->clear();
898 
899    return wxACC_OK;
900 }
901 
902 // Returns the description for this object or a child.
GetDescription(int WXUNUSED (childId),wxString * description)903 wxAccStatus GridAx::GetDescription(int WXUNUSED(childId), wxString *description)
904 {
905    description->clear();
906 
907    return wxACC_OK;
908 }
909 
910 // Returns help text for this object or a child, similar to tooltip text.
GetHelpText(int WXUNUSED (childId),wxString * helpText)911 wxAccStatus GridAx::GetHelpText(int WXUNUSED(childId), wxString *helpText)
912 {
913    helpText->clear();
914 
915    return wxACC_OK;
916 }
917 
918 // Returns the keyboard shortcut for this object or child.
919 // Return e.g. ALT+K
GetKeyboardShortcut(int WXUNUSED (childId),wxString * shortcut)920 wxAccStatus GridAx::GetKeyboardShortcut(int WXUNUSED(childId), wxString *shortcut)
921 {
922    shortcut->clear();
923 
924    return wxACC_OK;
925 }
926 
927 // Returns the rectangle for this object (id = 0) or a child element (id > 0).
928 // rect is in screen coordinates.
GetLocation(wxRect & rect,int elementId)929 wxAccStatus GridAx::GetLocation(wxRect & rect, int elementId)
930 {
931    wxRect r;
932    int row;
933    int col;
934 
935    if (GetRowCol(elementId, row, col)) {
936       rect = mGrid->CellToRect(row, col);
937       rect.SetPosition(mGrid->CalcScrolledPosition(rect.GetPosition()));
938       rect.SetPosition(mGrid->GetGridWindow()->ClientToScreen(rect.GetPosition()));
939    }
940    else {
941       rect = mGrid->GetRect();
942       rect.SetPosition(mGrid->GetParent()->ClientToScreen(rect.GetPosition()));
943    }
944 
945    return wxACC_OK;
946 }
947 
948 // Gets the name of the specified object.
GetName(int childId,wxString * name)949 wxAccStatus GridAx::GetName(int childId, wxString *name)
950 {
951    int row;
952    int col;
953 
954    if (GetRowCol(childId, row, col)) {
955       wxString n = mGrid->GetColLabelValue(col);
956       wxString v = mGrid->GetCellValue(row, col);
957       if (v.empty()) {
958          v = _("Empty");
959       }
960 
961       // Hack to provide a more intelligible response
962       NumericEditor *dt =
963          static_cast<NumericEditor *>(mGrid->GetDefaultEditorForType(GRID_VALUE_TIME));
964       NumericEditor *df =
965          static_cast<NumericEditor *>(mGrid->GetDefaultEditorForType(GRID_VALUE_FREQUENCY));
966       NumericEditor *c =
967          static_cast<NumericEditor *>(mGrid->GetCellEditor(row, col));
968 
969       if (c && dt && df && ( c == dt || c == df)) {
970          double value;
971          v.ToDouble(&value);
972          NumericConverter converter(c == dt ? NumericConverter::TIME : NumericConverter::FREQUENCY,
973                         c->GetFormat(),
974                         value,
975                         c->GetRate() );
976 
977          v = converter.GetString();
978       }
979 
980       if (c)
981          c->DecRef();
982       if (dt)
983          dt->DecRef();
984       if (df)
985          df->DecRef();
986 
987       *name = n + wxT(" ") + v;
988    }
989 
990    return wxACC_OK;
991 }
992 
GetParent(wxAccessible ** WXUNUSED (parent))993 wxAccStatus GridAx::GetParent(wxAccessible ** WXUNUSED(parent))
994 {
995    return wxACC_NOT_IMPLEMENTED;
996 }
997 
998 // Returns a role constant.
GetRole(int childId,wxAccRole * role)999 wxAccStatus GridAx::GetRole(int childId, wxAccRole *role)
1000 {
1001    if (childId == wxACC_SELF) {
1002 #if defined(__WXMSW__)
1003       *role = wxROLE_SYSTEM_TABLE;
1004 #endif
1005 
1006 #if defined(__WXMAC__)
1007       *role = wxROLE_SYSTEM_GROUPING;
1008 #endif
1009    }
1010    else {
1011       *role = wxROLE_SYSTEM_TEXT;
1012    }
1013 
1014    return wxACC_OK;
1015 }
1016 
1017 // Gets a variant representing the selected children
1018 // of this object.
1019 // Acceptable values:
1020 // - a null variant (IsNull() returns TRUE)
1021 // - a list variant (GetType() == wxT("list"))
1022 // - an integer representing the selected child element,
1023 //   or 0 if this object is selected (GetType() == wxT("long"))
1024 // - a "void*" pointer to a wxAccessible child object
GetSelections(wxVariant * WXUNUSED (selections))1025 wxAccStatus GridAx::GetSelections(wxVariant * WXUNUSED(selections))
1026 {
1027    return wxACC_NOT_IMPLEMENTED;
1028 }
1029 
1030 // Returns a state constant.
GetState(int childId,long * state)1031 wxAccStatus GridAx::GetState(int childId, long *state)
1032 {
1033    int flag = wxACC_STATE_SYSTEM_FOCUSABLE |
1034               wxACC_STATE_SYSTEM_SELECTABLE;
1035    int col;
1036    int row;
1037 
1038    if (!GetRowCol(childId, row, col)) {
1039       *state = 0;
1040       return wxACC_FAIL;
1041    }
1042 
1043 #if defined(__WXMSW__)
1044    flag |= wxACC_STATE_SYSTEM_FOCUSED |
1045            wxACC_STATE_SYSTEM_SELECTED;
1046 
1047       if (mGrid->IsReadOnly(row, col)) {
1048          // It would be more logical to also include the state
1049          // wxACC_STATE_SYSTEM_FOCUSABLE, but this causes Window-Eyes to
1050          // no longer read the cell as disabled
1051          flag = wxACC_STATE_SYSTEM_UNAVAILABLE | wxACC_STATE_SYSTEM_FOCUSED;
1052       }
1053 #endif
1054 
1055 #if defined(__WXMAC__)
1056    if (mGrid->IsInSelection(row, col)) {
1057       flag |= wxACC_STATE_SYSTEM_SELECTED;
1058    }
1059 
1060    if (mGrid->GetGridCursorRow() == row && mGrid->GetGridCursorCol() == col) {
1061        flag |= wxACC_STATE_SYSTEM_FOCUSED;
1062    }
1063 
1064    if (mGrid->IsReadOnly(row, col)) {
1065       flag |= wxACC_STATE_SYSTEM_UNAVAILABLE;
1066    }
1067 #endif
1068 
1069    *state = flag;
1070 
1071    return wxACC_OK;
1072 }
1073 
1074 // Returns a localized string representing the value for the object
1075 // or child.
1076 #if defined(__WXMAC__)
GetValue(int childId,wxString * strValue)1077 wxAccStatus GridAx::GetValue(int childId, wxString *strValue)
1078 #else
1079 wxAccStatus GridAx::GetValue(int WXUNUSED(childId), wxString *strValue)
1080 #endif
1081 {
1082    strValue->clear();
1083 
1084 #if defined(__WXMSW__)
1085    return wxACC_OK;
1086 #endif
1087 
1088 #if defined(__WXMAC__)
1089    return GetName(childId, strValue);
1090 #endif
1091 }
1092 
1093 #if defined(__WXMAC__)
1094 // Selects the object or child.
Select(int childId,wxAccSelectionFlags selectFlags)1095 wxAccStatus GridAx::Select(int childId, wxAccSelectionFlags selectFlags)
1096 {
1097    int row;
1098    int col;
1099 
1100    if (GetRowCol(childId, row, col)) {
1101 
1102       if (selectFlags & wxACC_SEL_TAKESELECTION) {
1103          mGrid->SetGridCursor(row, col);
1104       }
1105 
1106       mGrid->SelectBlock(row, col, row, col, selectFlags & wxACC_SEL_ADDSELECTION);
1107    }
1108 
1109    return wxACC_OK;
1110 }
1111 #endif
1112 
1113 // Gets the window with the keyboard focus.
1114 // If childId is 0 and child is NULL, no object in
1115 // this subhierarchy has the focus.
1116 // If this object has the focus, child should be 'this'.
GetFocus(int * childId,wxAccessible ** child)1117 wxAccStatus GridAx::GetFocus(int * childId, wxAccessible **child)
1118 {
1119    if (mGrid == wxWindow::FindFocus()) {
1120       if (mGrid->GetNumberRows() * mGrid->GetNumberCols() == 0) {
1121          *child = this;
1122       }
1123       else {
1124          *childId = mGrid->GetGridCursorRow()*mGrid->GetNumberCols() +
1125             mGrid->GetGridCursorCol() + 1;
1126       }
1127    }
1128 
1129    return wxACC_OK;
1130 }
1131 
1132 #endif // wxUSE_ACCESSIBILITY
1133