Audacity  3.0.3
KeyView.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  KeyView.cpp
6 
7 *******************************************************************//*********************************************************************/
13 
14 
15 #include "KeyView.h"
16 
17 #include <wx/setup.h> // for wxUSE_* macros
18 #include <wx/defs.h>
19 #include <wx/settings.h>
20 #include <wx/vlbox.h>
21 
22 #include "AColor.h"
23 #include "../ShuttleGui.h"
24 #include "../commands/CommandManager.h"
25 
26 #include <wx/dc.h>
27 #include <wx/menu.h>
28 
29 #if wxUSE_ACCESSIBILITY
30 #include "WindowAccessible.h"
31 
32 // ----------------------------------------------------------------------------
33 // KeyViewAx
34 //
35 // wxAccessible object providing information for KeyView.
36 // ----------------------------------------------------------------------------
37 
38 class KeyViewAx final : public WindowAccessible
39 {
40 public:
41 
42  KeyViewAx(KeyView *view);
43 
44  void SetCurrentLine(int row);
45  void ListUpdated();
46  bool LineToId(int line, int & childId);
47  bool IdToLine(int childId, int & line);
48 
49  // Can return either a child object, or an integer
50  // representing the child element, starting from 1.
51  wxAccStatus HitTest(const wxPoint & pt, int *childId, wxAccessible **childObject) override;
52 
53  // Retrieves the address of an IDispatch interface for the specified child.
54  // All objects must support this property.
55  wxAccStatus GetChild(int childId, wxAccessible **child) override;
56 
57  // Gets the number of children.
58  wxAccStatus GetChildCount(int *childCount) override;
59 
60  // Gets the default action for this object (0) or > 0 (the action for a child).
61  // Return wxACC_OK even if there is no action. actionName is the action, or the empty
62  // string if there is no action.
63  // The retrieved string describes the action that is performed on an object,
64  // not what the object does as a result. For example, a toolbar button that prints
65  // a document has a default action of "Press" rather than "Prints the current document."
66  wxAccStatus GetDefaultAction(int childId, wxString *actionName) override;
67 
68  // Returns the description for this object or a child.
69  wxAccStatus GetDescription(int childId, wxString *description) override;
70 
71  // Gets the window with the keyboard focus.
72  // If childId is 0 and child is NULL, no object in
73  // this subhierarchy has the focus.
74  // If this object has the focus, child should be 'this'.
75  wxAccStatus GetFocus(int *childId, wxAccessible **child) override;
76 
77  // Returns help text for this object or a child, similar to tooltip text.
78  wxAccStatus GetHelpText(int childId, wxString *helpText) override;
79 
80  // Returns the keyboard shortcut for this object or child.
81  // Return e.g. ALT+K
82  wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut) override;
83 
84  // Returns the rectangle for this object (id = 0) or a child element (id > 0).
85  // rect is in screen coordinates.
86  wxAccStatus GetLocation(wxRect & rect, int elementId) override;
87 
88  // Navigates from fromId to toId/toObject.
89  wxAccStatus Navigate(wxNavDir navDir, int fromId,
90  int *toId, wxAccessible **toObject) override;
91 
92  // Gets the name of the specified object.
93  wxAccStatus GetName(int childId, wxString *name) override;
94 
95  // Gets the parent, or NULL.
96  wxAccStatus GetParent(wxAccessible **parent) override;
97 
98  // Returns a role constant.
99  wxAccStatus GetRole(int childId, wxAccRole *role) override;
100 
101  // Gets a variant representing the selected children
102  // of this object.
103  // Acceptable values:
104  // - a null variant (IsNull() returns TRUE)
105  // - a list variant (GetType() == wxT("list"))
106  // - an integer representing the selected child element,
107  // or 0 if this object is selected (GetType() == wxT("long"))
108  // - a "void*" pointer to a wxAccessible child object
109  wxAccStatus GetSelections(wxVariant *selections) override;
110 
111  // Returns a state constant.
112  wxAccStatus GetState(int childId, long *state) override;
113 
114  // Returns a localized string representing the value for the object
115  // or child.
116  wxAccStatus GetValue(int childId, wxString *strValue) override;
117 
118 #if defined(__WXMAC__)
119  // Selects the object or child.
120  wxAccStatus Select(int childId, wxAccSelectionFlags selectFlags) override;
121 #endif
122 
123 private:
124  KeyView *mView;
125  int mLastId;
126 };
127 
128 #endif
129 
130 // Various drawing constants
131 #define KV_BITMAP_SIZE 16
132 #define KV_LEFT_MARGIN 2
133 #define KV_COLUMN_SPACER 5
134 #define KV_VSCROLL_WIDTH 16 /* figure this out automatically? */
135 
136 // Define the KeyNode arrays
137 
138 // Define the event table
139 BEGIN_EVENT_TABLE(KeyView, wxVListBox)
140  EVT_LEFT_DOWN(KeyView::OnLeftDown)
141  EVT_KEY_DOWN(KeyView::OnKeyDown)
142  EVT_LISTBOX(wxID_ANY, KeyView::OnSelected)
143  EVT_SET_FOCUS(KeyView::OnSetFocus)
144  EVT_KILL_FOCUS(KeyView::OnKillFocus)
145  EVT_SIZE(KeyView::OnSize)
146  EVT_SCROLLWIN(KeyView::OnScroll)
148 
149 static wxString CommandTranslated = "Command";
150 
151 
152 // ============================================================================
153 // KeyView class
154 // ============================================================================
155 KeyView::KeyView(wxWindow *parent,
156  wxWindowID id,
157  const wxPoint & pos,
158  const wxSize & size)
159 : wxVListBox(parent, id, pos, size, wxBORDER_THEME | wxHSCROLL | wxVSCROLL),
160  mScrollX(0),
161  mWidth(0)
162 {
163 #if wxUSE_ACCESSIBILITY
164  // Create and set accessibility object
165  SetAccessible(mAx = safenew KeyViewAx(this));
166 #endif
167 
168  SetMinSize({-1, 150});
169 
170  // The default view
171  mViewType = ViewByTree;
172 
173  // Calculate measurements used for columns and scrolling
174  RecalcExtents();
175 }
176 
178 {
179 }
180 
181 //
182 // Returns the index of the selected node
183 //
184 int
186 {
187  return LineToIndex(GetSelection());
188 }
189 
190 //
191 // Returns the name of the control
192 //
193 wxString
195 {
196  // Just forward request
197  return wxVListBox::GetName();
198 }
199 
200 //
201 // Returns the label for the given index
202 //
203 wxString
204 KeyView::GetLabel(int index) const
205 {
206  // Make sure index is valid
207  if (index < 0 || index >= (int) mNodes.size())
208  {
209  wxASSERT(false);
210  return wxEmptyString;
211  }
212 
213  return mNodes[index].label;
214 }
215 
216 //
217 // Returns the prefix (if available) prepended to the label for the given index
218 //
219 wxString
220 KeyView::GetFullLabel(int index) const
221 {
222  // Make sure index is valid
223  if (index < 0 || index >= (int) mNodes.size())
224  {
225  wxASSERT(false);
226  return wxEmptyString;
227  }
228 
229  // Cache the node and label
230  const KeyNode & node = mNodes[index];
231  wxString label = node.label;
232 
233  // Prepend the prefix if available
234  if (!node.prefix.empty())
235  {
236  label = node.prefix + wxT(" - ") + label;
237  }
238 
239  return label;
240 }
241 
242 //
243 // Returns the index for the given name
244 //
245 int
247 {
248  int cnt = (int) mNodes.size();
249 
250  // Search the nodes for the key
251  for (int i = 0; i < cnt; i++)
252  {
253  if (name == mNodes[i].name)
254  {
255  return mNodes[i].index;
256  }
257  }
258 
259  return wxNOT_FOUND;
260 }
261 
262 //
263 // Returns the command manager name for the given index
264 //
265 CommandID
266 KeyView::GetName(int index) const
267 {
268  // Make sure index is valid
269  if (index < 0 || index >= (int) mNodes.size())
270  {
271  wxASSERT(false);
272  return {};
273  }
274 
275  return mNodes[index].name;
276 }
277 
278 //
279 // Returns the command manager index for the given key combination
280 //
281 CommandID
283 {
284  int cnt = (int) mNodes.size();
285 
286  // Search the nodes for the key
287  for (int i = 0; i < cnt; i++)
288  {
289  if ( key == mNodes[i].key )
290  {
291  return mNodes[i].name;
292  }
293  }
294 
295  return {};
296 }
297 
298 //
299 // Returns the index for the given key
300 //
301 int
303 {
304  int cnt = (int) mNodes.size();
305 
306  // Search the nodes for the key
307  for (int i = 0; i < cnt; i++)
308  {
309  if ( key == mNodes[i].key )
310  {
311  return mNodes[i].index;
312  }
313  }
314 
315  return wxNOT_FOUND;
316 }
317 
318 //
319 // Returns the key for the given index
320 //
322 KeyView::GetKey(int index) const
323 {
324  // Make sure index is valid
325  if (index < 0 || index >= (int) mNodes.size())
326  {
327  wxASSERT(false);
328  return {};
329  }
330 
331  return mNodes[index].key;
332 }
333 
334 //
335 // Use to determine if a key can be assigned to the given index
336 //
337 bool
338 KeyView::CanSetKey(int index) const
339 {
340  // Make sure index is valid
341  if (index < 0 || index >= (int) mNodes.size())
342  {
343  wxASSERT(false);
344  return false;
345  }
346 
347  // Parents can't be assigned keys
348  return !mNodes[index].isparent;
349 }
350 
351 //
352 // Sets the key for the given index
353 //
354 bool
356 {
357  // Make sure index is valid
358  if (index < 0 || index >= (int) mNodes.size())
359  {
360  wxASSERT(false);
361  return false;
362  }
363 
364  // Cache the node
365  KeyNode & node = mNodes[index];
366 
367  // Do not allow setting keys on branches
368  if (node.isparent)
369  {
370  return false;
371  }
372 
373  // Set the NEW key
374  node.key = key;
375 
376  // Check to see if the key column needs to be expanded
377  int x, y;
378  GetTextExtent(node.key.Display(), &x, &y);
379  if (x > mKeyWidth || y > mLineHeight)
380  {
381  // New key is wider than column so recalc extents (will refresh view)
382  RecalcExtents();
383  return true;
384  }
385 
386  // Refresh the view lines
387  RefreshAll();
388 
389  return true;
390 }
391 
392 //
393 // Sets the key for the given name
394 //
395 bool
397 {
398  int index = GetIndexByName(name);
399 
400  // Bail is the name wasn't found
401  if (index == wxNOT_FOUND)
402  {
403  return false;
404  }
405 
406  // Go set the key
407  return SetKey(index, key);
408 }
409 
410 //
411 // Sets the view type
412 //
413 void
415 {
416  int index = LineToIndex(GetSelection());
417 
418  // Handle an existing selection
419  if (index != wxNOT_FOUND)
420  {
421  // Cache the currently selected node
422  KeyNode & node = mNodes[index];
423 
424  // Expand branches if switching to Tree view and a line
425  // is currently selected
426  if (type == ViewByTree)
427  {
428  // Cache the node's depth
429  int depth = node.depth;
430 
431  // Search for its parents, setting each one as open
432  for (int i = node.index - 1; i >= 0 && depth > 1; i--)
433  {
434  if (mNodes[i].depth < depth)
435  {
436  mNodes[i].isopen = true;
437  depth = mNodes[i].depth;
438  }
439  }
440  }
441  }
442 
443  // Unselect any currently selected line...do even if none selected
444  SelectNode(-1);
445 
446  // Save NEW type
447  mViewType = type;
448 
449  // Refresh the view lines
450  RefreshLines();
451 
452  // Reselect old node (if possible)
453  if (index != wxNOT_FOUND)
454  {
455  SelectNode(index);
456  }
457 
458  // ensure that a node is selected so that when the keyview is the focus,
459  // this is indicated visually, and the Narrator screen reader reads it.
460  if ((GetSelection() == wxNOT_FOUND))
461  {
463  }
464 
465  return;
466 }
467 
468 //
469 // Sets the filter
470 //
471 void
472 KeyView::SetFilter(const wxString & filter)
473 {
474  int index = LineToIndex(GetSelection());
475 
476  // Unselect any currently selected line...do even if none selected
477  SelectNode(-1);
478 
479  // Save the filter
480  mFilter = filter.Lower();
481 
482  // Refresh the view lines
483  RefreshLines();
484 
485  // Reselect old node (if possible)
486  if (index != wxNOT_FOUND)
487  {
488  SelectNode(index);
489  }
490 
491  // ensure that a node is selected so that when the keyview is the focus,
492  // this is indicated visually, and the Narrator screen reader reads it.
493  if ((GetSelection() == wxNOT_FOUND))
494  {
496  }
497 }
498 
499 //
500 // Expand all branches
501 //
502 void
504 {
505  int cnt = (int) mNodes.size();
506 
507  // Set all parent nodes to open
508  for (int i = 0; i < cnt; i++)
509  {
510  KeyNode & node = mNodes[i];
511 
512  if (node.isparent)
513  {
514  node.isopen = true;
515  }
516  }
517 
518  RefreshLines();
519 }
520 
521 //
522 // Collapse all branches
523 //
524 void
526 {
527  int cnt = (int) mNodes.size();
528 
529  // Set all parent nodes to closed
530  for (int i = 0; i < cnt; i++)
531  {
532  KeyNode & node = mNodes[i];
533 
534  if (node.isparent)
535  {
536  node.isopen = false;
537  }
538  }
539 
540  RefreshLines();
541 }
542 
543 //
544 // Recalculate the measurements used for columns and scrolling
545 //
546 void
548 {
549  // Reset
550  mLineHeight = 0;
551  mCommandWidth = 0;
552  mKeyWidth = 0;
553 
554  // Examine all nodes
555  int cnt = (int) mNodes.size();
556  for (int i = 0; i < cnt; i++)
557  {
558  KeyNode & node = mNodes[i];
559  int x, y;
560 
561  if (node.iscat)
562  {
563  // Measure the category
564  GetTextExtent(node.category, &x, &y);
565  }
566  else if (node.ispfx)
567  {
568  // Measure the prefix
569  GetTextExtent(node.prefix, &x, &y);
570  }
571  else
572  {
573  // Measure the key
574  GetTextExtent(node.key.Display(), &x, &y);
575  mLineHeight = wxMax(mLineHeight, y);
576  mKeyWidth = wxMax(mKeyWidth, x);
577 
578  // Prepend prefix for view types other than tree
579  wxString label = node.label;
580  if (mViewType != ViewByTree && !node.prefix.empty())
581  {
582  label = node.prefix + wxT(" - ") + label;
583  }
584 
585  // Measure the label
586  GetTextExtent(label, &x, &y);
587  }
588 
589  // Finish calc for command column
590  mLineHeight = wxMax(mLineHeight, y);
591  mCommandWidth = wxMax(mCommandWidth, x);
592  }
593 
594  // Update horizontal scrollbar
595  UpdateHScroll();
596 }
597 
598 //
599 // Update the horizontal scrollbar or remove it if not needed
600 //
601 void
603 {
604  // Get the internal dimensions of the view
605  wxRect r = GetClientRect();
606 
607  // Calculate the full line width
609  mKeyWidth +
611  mCommandWidth +
613 
614  // Retrieve the current horizontal scroll amount
615  mScrollX = GetScrollPos(wxHORIZONTAL);
616 
617  if (mWidth <= r.GetWidth())
618  {
619  // Remove the scrollbar if it will fit within client width
620  SetScrollbar(wxHORIZONTAL, 0, 0, 0);
621  }
622  else
623  {
624  // Set scrollbar metrics
625  SetScrollbar(wxHORIZONTAL, mScrollX, r.GetWidth(), mWidth);
626  }
627 
628  // Refresh the entire view
629  RefreshAll();
630 }
631 
632 //
633 // Process a NEW set of bindings
634 //
635 void
637  const TranslatableStrings & categories,
638  const TranslatableStrings & prefixes,
639  const TranslatableStrings & labels,
640  const std::vector<NormalizedKeyString> & keys,
641  bool bSort
642  )
643 {
644  // Start clean
645  mNodes.clear();
646 
647  // Same as in RecalcExtents() but do it inline
648  mLineHeight = 0;
649  mKeyWidth = 0;
650  mCommandWidth = 0;
651 
652  wxString lastcat;
653  wxString lastpfx;
654  int nodecnt = 0;
655  int depth = 1;
656  bool incat = false;
657  bool inpfx = false;
658 
659  // lookup translation once only
660  CommandTranslated = _("Command");
661 
662  // Examine all names...all arrays passed have the same indexes
663  int cnt = (int) names.size();
664  for (int i = 0; i < cnt; i++)
665  {
666  auto name = names[i];
667  int x, y;
668 
669  // Remove any menu code from the category and prefix
670  wxString cat = categories[i].Translation();
671  wxString pfx = prefixes[i].Translation();
672 
673  // Append "Menu" this node is for a menu title
674  if (cat != CommandTranslated)
675  {
676  cat.Append(wxT(" "));
677  cat += _("Menu");
678  }
679 
680  // Process a NEW category
681  if (cat != lastcat)
682  {
683  // A NEW category always finishes any current subtree
684  if (inpfx)
685  {
686  // Back to category level
687  depth--;
688  inpfx = false;
689  }
690 
691  // Only time this is not true is during the first iteration
692  if (incat)
693  {
694  // Back to root level
695  depth--;
696  incat = false;
697  }
698 
699  // Remember for next iteration
700  lastcat = cat;
701 
702  // Add a NEW category node
703  if (!cat.empty())
704  {
705  KeyNode node;
706 
707  // Fill in the node info
708  node.name = CommandID{}; // don't associate branches with a command
709  node.category = cat;
710  node.prefix = pfx;
711  node.label = cat;
712  node.index = nodecnt++;
713  node.iscat = true;
714  node.isparent = true;
715  node.depth = depth++;
716  node.isopen = true;
717 
718  // Add it to the tree
719  mNodes.push_back(node);
720  incat = true;
721 
722  // Measure category
723  GetTextExtent(cat, &x, &y);
724  mLineHeight = wxMax(mLineHeight, y);
725  mCommandWidth = wxMax(mCommandWidth, x);
726  }
727  }
728 
729  // Process a NEW prefix
730  if (pfx != lastpfx)
731  {
732  // Done with prefix branch
733  if (inpfx)
734  {
735  depth--;
736  inpfx = false;
737  }
738 
739  // Remember for next iteration
740  lastpfx = pfx;
741 
742  // Add a NEW prefix node
743  if (!pfx.empty())
744  {
745  KeyNode node;
746 
747  // Fill in the node info
748  node.name = CommandID{}; // don't associate branches with a command
749  node.category = cat;
750  node.prefix = pfx;
751  node.label = pfx;
752  node.index = nodecnt++;
753  node.ispfx = true;
754  node.isparent = true;
755  node.depth = depth++;
756  node.isopen = true;
757 
758  // Add it to the tree
759  mNodes.push_back(node);
760  inpfx = true;
761  }
762  }
763 
764  // Add the key entry
765  KeyNode node;
766  node.category = cat;
767  node.prefix = pfx;
768 
769  // Labels for undo and redo change according to the last command
770  // which can be undone/redone, so give them a special check in order
771  // not to confuse users
772  if (name == wxT("Undo"))
773  {
774  node.label = _("Undo");
775  }
776  else if (name == wxT("Redo"))
777  {
778  node.label = _("Redo");
779  }
780  else
781  {
782  auto label = labels[i];
783  node.label = label.Strip().Translation();
784  }
785 
786  // Fill in remaining info
787  node.name = name;
788  node.key = keys[i];
789  node.index = nodecnt++;
790  node.depth = depth;
791 
792  // Add it to the tree
793  mNodes.push_back(node);
794 
795  // Measure key
796  GetTextExtent(node.key.Display(), &x, &y);
797  mLineHeight = wxMax(mLineHeight, y);
798  mKeyWidth = wxMax(mKeyWidth, x);
799 
800  // Prepend prefix for all view types to determine maximum
801  // column widths
802  wxString label = node.label;
803  if (!node.prefix.empty())
804  {
805  label = node.prefix + wxT(" - ") + label;
806  }
807 
808  // Measure label
809  GetTextExtent(label, &x, &y);
810  mLineHeight = wxMax(mLineHeight, y);
811  mCommandWidth = wxMax(mCommandWidth, x);
812  }
813 
814 #if 0
815  // For debugging
816  for (int j = 0; j < mNodes.size(); j++)
817  {
818  KeyNode & node = mNodes[j];
819  wxLogDebug(wxT("NODE line %4d index %4d depth %1d open %1d parent %1d cat %1d pfx %1d name %s STR %s | %s | %s"),
820  node.line,
821  node.index,
822  node.depth,
823  node.isopen,
824  node.isparent,
825  node.iscat,
826  node.ispfx,
827  node.name,
828  node.category,
829  node.prefix,
830  node.label);
831  }
832 #endif
833 
834  // Update horizontal scrollbar
835  UpdateHScroll();
836 
837  // Refresh the view lines
838  RefreshLines(bSort);
839 
840  // Set the selected node if we've just reprepared the list and nothing was selected.
841  if ((GetSelection()==wxNOT_FOUND) && bSort )
842  {
844  }
845 }
846 
847 //
848 // Refresh the list of lines within the current view
849 //
850 void
852 {
853  int cnt = (int) mNodes.size();
854  int linecnt = 0;
855  mLines.clear();
856 
857  // Process a filter if one is set
858  if (!mFilter.empty())
859  {
860  // Examine all nodes
861  for (int i = 0; i < cnt; i++)
862  {
863  KeyNode & node = mNodes[i];
864 
865  // Reset line number
866  node.line = wxNOT_FOUND;
867 
868  // Search columns based on view type
869  wxString searchit;
870  switch (mViewType)
871  {
872  // The x"01" separator is used to prevent finding a
873  // match comprising the end of the label and beginning
874  // of the key. It was chosen since it's not very likely
875  // to appear in the filter itself.
876  case ViewByTree:
877  searchit = node.label.Lower() +
878  wxT("\01x") +
879  node.key.Display().Lower();
880  break;
881 
882  case ViewByName:
883  searchit = node.label.Lower();
884  break;
885 
886  case ViewByKey:
887  searchit = node.key.Display().Lower();
888  break;
889  }
890  if (searchit.Find(mFilter) == wxNOT_FOUND)
891  {
892  // Not found so continue to next node
893  continue;
894  }
895 
896  // For the Key View, if the filter is a single character,
897  // then it has to be the last character in the searchit string,
898  // and be preceded by nothing or +.
899  if ((mViewType == ViewByKey) &&
900  (mFilter.length() == 1) &&
901  (mFilter != searchit.Last() ||
902  ((searchit.length() > 1) &&
903  ((wxString)(searchit.GetChar(searchit.length() - 2)) != wxT("+")))))
904  {
905  // Not suitable so continue to next node
906  continue;
907  }
908 
909  // For tree view, we must make sure all parent nodes are included
910  // whether they match the filter or not.
911  if (mViewType == ViewByTree)
912  {
913  std::vector<KeyNode*> queue;
914  int depth = node.depth;
915 
916  // This node is a category or prefix node, so always mark them
917  // as open.
918  //
919  // What this is really doing is resolving a situation where the
920  // the filter matches a parent node and nothing underneath. In
921  // this case, the node would never be marked as open.
922  if (node.isparent)
923  {
924  node.isopen = true;
925  }
926 
927  // Examine siblings until a parent is found.
928  for (int j = node.index - 1; j >= 0 && depth > 0; j--)
929  {
930  // Found a parent
931  if (mNodes[j].depth < depth)
932  {
933  // Examine all previously added nodes to see if this nodes
934  // ancestors need to be added prior to adding this node.
935  bool found = false;
936  for (int k = (int) mLines.size() - 1; k >= 0; k--)
937  {
938  // The node indexes match, so we've found the parent of the
939  // child node.
940  if (mLines[k]->index == mNodes[j].index)
941  {
942  found = true;
943  break;
944  }
945  }
946 
947  // The parent wasn't found so remember it for later
948  // addition. Can't add directory to mLines here since
949  // they will wind up in reverse order.
950  if (!found)
951  {
952  queue.push_back(&mNodes[j]);
953  }
954 
955  // Traverse up the tree
956  depth = mNodes[j].depth;
957  }
958  }
959 
960  // Add any queues nodes to list. This will all be
961  // parent nodes, so mark them as open.
962  for (int j = (int) queue.size() - 1; j >= 0; j--)
963  {
964  queue[j]->isopen = true;
965  queue[j]->line = linecnt++;
966  mLines.push_back(queue[j]);
967  }
968  }
969 
970  // Finally add the child node
971  node.line = linecnt++;
972  mLines.push_back(&node);
973  }
974  }
975  else
976  {
977  // Examine all nodes - non-filtered
978  for (int i = 0; i < cnt; i++)
979  {
980  KeyNode & node = mNodes[i];
981 
982  // Reset line number
983  node.line = wxNOT_FOUND;
984 
985  // Node is either a category or prefix
986  if (node.isparent)
987  {
988  // Only need to do this for tree views
989  if (mViewType != ViewByTree)
990  {
991  continue;
992  }
993 
994  // Add the node
995  node.line = linecnt++;
996  mLines.push_back(&node);
997 
998  // If this node is not open, then skip all of its descendants
999  if (!node.isopen)
1000  {
1001  bool iscat = node.iscat;
1002  bool ispfx = node.ispfx;
1003 
1004  // Skip nodes until we find a node that has a different
1005  // category or prefix
1006  while (i < cnt)
1007  {
1008  KeyNode & skip = mNodes[i];
1009 
1010  if ((iscat && skip.category != node.category) ||
1011  (ispfx && skip.prefix != node.prefix))
1012  {
1013  break;
1014  }
1015 
1016  // Bump to next node
1017  i++;
1018  }
1019 
1020  // Index is pointing to the node that was different or
1021  // past the end, so back off to last node of this branch.
1022  i--;
1023  }
1024  continue;
1025  }
1026 
1027  // Add child node to list
1028  node.line = linecnt++;
1029  mLines.push_back(&node);
1030  }
1031  }
1032 
1033  // Sorting is costly. If bSort is false, we do not have to sort.
1034  // bSort false means we know that the list will be updated again before
1035  // the user needs to see it.
1036  if( bSort )
1037  {
1038  //To see how many lines are being sorted (and how often).
1039  //wxLogDebug("Sorting %i lines for type %i", mLines.size(), mViewType);
1040 
1041  // Speed up the comparison function used in sorting
1042  // by only translating this string once.
1043  CommandTranslated = _("Command");
1044 
1045  // Sort list based on type
1046  switch (mViewType)
1047  {
1048  case ViewByTree:
1049  std::sort(mLines.begin(), mLines.end(), CmpKeyNodeByTree);
1050  break;
1051 
1052  case ViewByName:
1053  std::sort(mLines.begin(), mLines.end(), CmpKeyNodeByName);
1054  break;
1055 
1056  case ViewByKey:
1057  std::sort(mLines.begin(), mLines.end(), CmpKeyNodeByKey);
1058  break;
1059  }
1060  }
1061 
1062  // Now, reassign the line numbers
1063  for (int i = 0; i < (int) mLines.size(); i++)
1064  {
1065  mLines[i]->line = i;
1066  }
1067 
1068 #if 0
1069  // For debugging
1070  for (int j = 0; j < mLines.size(); j++)
1071  {
1072  KeyNode & node = *mLines[j];
1073  wxLogDebug(wxT("LINE line %4d index %4d depth %1d open %1d parent %1d cat %1d pfx %1d name %s STR %s | %s | %s"),
1074  node.line,
1075  node.index,
1076  node.depth,
1077  node.isopen,
1078  node.isparent,
1079  node.iscat,
1080  node.ispfx,
1081  node.name,
1082  node.category,
1083  node.prefix,
1084  node.label);
1085  }
1086 #endif
1087 
1088  // Tell listbox the NEW count and refresh the entire view
1089  SetItemCount(mLines.size());
1090  RefreshAll();
1091 
1092 #if wxUSE_ACCESSIBILITY
1093  // Let accessibility know that the list has changed
1094  if( bSort )
1095  mAx->ListUpdated();
1096 #endif
1097 }
1098 
1099 //
1100 // Select a node
1101 //
1102 // Parameter can be wxNOT_FOUND to clear selection
1103 //
1104 void
1106 {
1107  int line = IndexToLine(index);
1108 
1109  // Tell the listbox to select the line
1110  SetSelection(line);
1111 
1112 #if wxUSE_ACCESSIBILITY
1113  // And accessibility
1114  mAx->SetCurrentLine(line);
1115 #endif
1116 
1117  // Always send an event to let parent know of selection change
1118  //
1119  // Must do this ourselves because we want to send notifications
1120  // even if there isn't an item selected and SendSelectedEvent()
1121  // doesn't allow sending an event for indexes not in the listbox.
1122  wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, GetId());
1123  event.SetEventObject(this);
1124  event.SetInt(line);
1125  (void)GetEventHandler()->ProcessEvent(event);
1126 }
1127 
1128 //
1129 // Converts a line index to a node index
1130 //
1131 int
1132 KeyView::LineToIndex(int line) const
1133 {
1134  if (line < 0 || line >= (int) mLines.size())
1135  {
1136  return wxNOT_FOUND;
1137  }
1138 
1139  return mLines[line]->index;
1140 }
1141 
1142 //
1143 // Converts a node index to a line index
1144 //
1145 int
1146 KeyView::IndexToLine(int index) const
1147 {
1148  if (index < 0 || index >= (int) mNodes.size())
1149  {
1150  return wxNOT_FOUND;
1151  }
1152 
1153  return mNodes[index].line;
1154 }
1155 
1156 //
1157 // Draw the background for a given line
1158 //
1159 // This is called by the listbox when it needs to redraw the view.
1160 //
1161 void
1162 KeyView::OnDrawBackground(wxDC & dc, const wxRect & rect, size_t line) const
1163 {
1164  const KeyNode *node = mLines[line];
1165  wxRect r = rect;
1166  wxRect r2 = rect; // for just the key shortcut.
1167  wxCoord indent = 0;
1168 
1169  // When in tree view mode, each younger branch gets indented by the
1170  // width of the open/close bitmaps
1171  if (mViewType == ViewByTree)
1172  {
1173  indent += mKeyWidth + KV_COLUMN_SPACER + node->depth * KV_BITMAP_SIZE;
1174  }
1175 
1176  // Offset left side by the indentation (if any) and scroll amounts
1177  r.x = indent - mScrollX;
1178  r2.x = -mScrollX;
1179 
1180  // If the line width is less than the client width, then we want to
1181  // extend the background to the right edge of the client view. Otherwise,
1182  // go all the way to the end of the line width...this will draw past the
1183  // right edge, but that's what we want.
1184  r.width = wxMax(mWidth, r.width);
1185  r2.width = mKeyWidth;
1186 
1187  // Selected lines get a solid background
1188  if (IsSelected(line))
1189  {
1190  if (FindFocus() == this)
1191  {
1192  // Focused lines get highlighted background
1193  dc.SetPen(*wxTRANSPARENT_PEN);
1194  dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
1195  dc.DrawRectangle(r);
1196 
1197  // and they also get a dotted focus rect. This could just be left out.
1198  // The focus rect does very little for us, as it is the same size as the
1199  // rectangle itself. Consequently for themes that have black text it
1200  // disappears. But on HiContrast you do get a dotted green border which
1201  // may have some utility.
1202  AColor::DrawFocus(dc, r);
1203 
1204  if (mViewType == ViewByTree){
1205  dc.DrawRectangle(r2);
1206  AColor::DrawFocus(dc, r2);
1207  }
1208  }
1209  else
1210  {
1211  // Non focused lines get a light background
1212  dc.SetPen(*wxTRANSPARENT_PEN);
1213  dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)));
1214  dc.DrawRectangle(r);
1215  if (mViewType == ViewByTree)
1216  dc.DrawRectangle(r2);
1217  }
1218  }
1219  else
1220  {
1221  // Non-selected lines get a thin bottom border
1222  dc.SetPen(wxColour(240, 240, 240));
1223  AColor::Line(dc, r.GetLeft(), r.GetBottom(), r.GetRight(), r.GetBottom());
1224  if (mViewType == ViewByTree )
1225  AColor::Line(dc, r2.GetLeft(), r2.GetBottom(), r2.GetRight(), r2.GetBottom());
1226  }
1227 }
1228 
1229 //
1230 // Draw a line
1231 //
1232 // This is called by the listbox when it needs to redraw the view.
1233 //
1234 void
1235 KeyView::OnDrawItem(wxDC & dc, const wxRect & rect, size_t line) const
1236 {
1237  const KeyNode *node = mLines[line];
1238  wxString label = node->label;
1239 
1240  // Make sure the DC has a valid font
1241  dc.SetFont(GetFont());
1242 
1243  // Set the text color based on selection and focus
1244  if (IsSelected(line) && FindFocus() == this)
1245  {
1246  dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT));
1247  }
1248  else
1249  {
1250  dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT));
1251  }
1252 
1253  // Tree views get bitmaps
1254  if (mViewType == ViewByTree)
1255  {
1256  // Adjust left edge to account for scrolling
1257  wxCoord x = rect.x - mScrollX;
1258 
1259  if (node->iscat || node->ispfx)
1260  {
1261  wxCoord bx = x + mKeyWidth + KV_COLUMN_SPACER;
1262  wxCoord by = rect.y;
1263 
1264  if (node->ispfx)
1265  {
1266  bx += KV_BITMAP_SIZE;
1267  }
1268 
1269  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1270  dc.SetPen(*wxBLACK_PEN);
1271  dc.DrawRectangle(bx + 3, by + 4, 9, 9);
1272  if (node->isopen)
1273  {
1274  AColor::Line(dc, bx + 5, by + 8, bx + 9, by + 8);
1275  }
1276  else
1277  {
1278  AColor::Line(dc, bx + 7, by + 6, bx + 7, by + 10);
1279  AColor::Line(dc, bx + 5, by + 8, bx + 9, by + 8);
1280  }
1281  }
1282 
1283  // Indent text
1284  x += KV_LEFT_MARGIN;
1285 
1286  // Draw the key and command columns
1287  dc.DrawText(node->key.Display(), x , rect.y);
1288  dc.DrawText(label, x + mKeyWidth + KV_COLUMN_SPACER + node->depth * KV_BITMAP_SIZE, rect.y);
1289  }
1290  else
1291  {
1292  // Adjust left edge by margin and account for scrolling
1293  wxCoord x = rect.x + KV_LEFT_MARGIN - mScrollX;
1294 
1295  // Prepend prefix if available
1296  if (!node->prefix.empty())
1297  {
1298  label = node->prefix + wxT(" - ") + label;
1299  }
1300 
1301  // don't swap the columns based on view type
1302  if((mViewType == ViewByName) || (mViewType == ViewByKey))
1303  {
1304  // Draw key columnd and then command column
1305  dc.DrawText(node->key.Display(), x, rect.y);
1306  dc.DrawText(label, x + mKeyWidth + KV_COLUMN_SPACER, rect.y);
1307  }
1308  }
1309 
1310  return;
1311 }
1312 
1313 //
1314 // Provide the height of the given line
1315 //
1316 // This is called by the listbox when it needs to redraw the view.
1317 //
1318 wxCoord
1319 KeyView::OnMeasureItem(size_t WXUNUSED(line)) const
1320 {
1321  // All lines are of equal height
1322  //
1323  // (add a magic 1 for decenders...looks better...not required)
1324  return mLineHeight + 1;
1325 }
1326 
1327 //
1328 // Handle the wxEVT_LISTBOX event
1329 //
1330 void
1331 KeyView::OnSelected(wxCommandEvent & event)
1332 {
1333  // Allow further processing
1334  event.Skip();
1335 
1336 #if wxUSE_ACCESSIBILITY
1337  // Tell accessibility of the change
1338  mAx->SetCurrentLine(event.GetInt());
1339 #endif
1340 }
1341 
1342 //
1343 // Handle the wxEVT_SET_FOCUS event
1344 //
1345 void
1346 KeyView::OnSetFocus(wxFocusEvent & event)
1347 {
1348  // Allow further processing
1349  event.Skip();
1350 
1351  // Refresh the selected line to pull in any changes while
1352  // focus was away...like when setting a NEW key value. This
1353  // will also refresh the visual (highlighted) state.
1354  if (GetSelection() != wxNOT_FOUND)
1355  {
1356  RefreshRow(GetSelection());
1357  }
1358 
1359 #if wxUSE_ACCESSIBILITY
1360  // Tell accessibility of the change
1361  mAx->SetCurrentLine(GetSelection());
1362 #endif
1363 }
1364 
1365 //
1366 // Handle the wxEVT_KILL_FOCUS event
1367 //
1368 void
1369 KeyView::OnKillFocus(wxFocusEvent & event)
1370 {
1371  // Allow further processing
1372  event.Skip();
1373 
1374  // Refresh the selected line to adjust visual highlighting.
1375  if (GetSelection() != wxNOT_FOUND)
1376  {
1377  RefreshRow(GetSelection());
1378  }
1379 }
1380 
1381 //
1382 // Handle the wxEVT_SIZE event
1383 //
1384 void
1385 KeyView::OnSize(wxSizeEvent & WXUNUSED(event))
1386 {
1387  // Update horizontal scrollbar
1388  UpdateHScroll();
1389 }
1390 
1391 //
1392 // Handle the wxEVT_SCROLL event
1393 //
1394 void
1395 KeyView::OnScroll(wxScrollWinEvent & event)
1396 {
1397  // We only care bout the horizontal scrollbar.
1398  if (event.GetOrientation() != wxHORIZONTAL)
1399  {
1400  // Allow further processing
1401  event.Skip();
1402  return;
1403  }
1404 
1405  // Get NEW scroll position and scroll the view
1406  mScrollX = event.GetPosition();
1407  SetScrollPos(wxHORIZONTAL, mScrollX);
1408 
1409  // Refresh the entire view
1410  RefreshAll();
1411 }
1412 
1413 //
1414 // Handle the wxEVT_KEY_DOWN event
1415 //
1416 void
1417 KeyView::OnKeyDown(wxKeyEvent & event)
1418 {
1419  int line = GetSelection();
1420 
1421  int keycode = event.GetKeyCode();
1422  switch (keycode)
1423  {
1424  // The LEFT key moves selection to parent or collapses selected
1425  // node if it is expanded.
1426  case WXK_LEFT:
1427  {
1428  // Nothing selected...nothing to do
1429  if (line == wxNOT_FOUND)
1430  {
1431  // Allow further processing
1432  event.Skip();
1433  break;
1434  }
1435 
1436  KeyNode *node = mLines[line];
1437 
1438  // Collapse the node if it is open
1439  if (node->isopen)
1440  {
1441  // No longer open
1442  node->isopen = false;
1443 
1444  // Don't want the view to scroll vertically, so remember the current
1445  // top line.
1446  size_t topline = GetVisibleBegin();
1447 
1448  // Refresh the view now that the number of lines have changed
1449  RefreshLines();
1450 
1451  // Reset the original top line
1452  ScrollToRow(topline);
1453 
1454  // And make sure current line is still selected
1455  SelectNode(LineToIndex(line));
1456  }
1457  else
1458  {
1459  // Move selection to the parent of this node
1460  for (int i = line - 1; i >= 0; i--)
1461  {
1462  // Found the parent
1463  if (mLines[i]->depth < node->depth)
1464  {
1465  // So select it
1466  SelectNode(LineToIndex(i));
1467  break;
1468  }
1469  }
1470  }
1471 
1472  // Further processing of the event is not wanted
1473  // (we didn't call event.Skip()
1474  }
1475  break;
1476 
1477  // The RIGHT key moves the selection to the first child or expands
1478  // the node if it is a parent.
1479  case WXK_RIGHT:
1480  {
1481  // Nothing selected...nothing to do
1482  if (line == wxNOT_FOUND)
1483  {
1484  // Allow further processing
1485  event.Skip();
1486  break;
1487  }
1488 
1489  KeyNode *node = mLines[line];
1490 
1491  // Only want parent nodes
1492  if (node->isparent)
1493  {
1494  // It is open so move select to first child
1495  if (node->isopen)
1496  {
1497  // But only if there is one
1498  if (line < (int) mLines.size() - 1)
1499  {
1500  SelectNode(LineToIndex(line + 1));
1501  }
1502  }
1503  else
1504  {
1505  // Node is now open
1506  node->isopen = true;
1507 
1508  // Don't want the view to scroll vertically, so remember the current
1509  // top line.
1510  size_t topline = GetVisibleBegin();
1511 
1512  // Refresh the view now that the number of lines have changed
1513  RefreshLines();
1514 
1515  // Reset the original top line
1516  ScrollToRow(topline);
1517 
1518  // And make sure current line is still selected
1519  SelectNode(LineToIndex(line));
1520  }
1521  }
1522 
1523  // Further processing of the event is not wanted
1524  // (we didn't call event.Skip()
1525  }
1526  break;
1527 
1528  // Move selection to next node whose 1st character matches
1529  // the keycode
1530  default:
1531  {
1532  int cnt = (int) mLines.size();
1533  bool found = false;
1534 
1535  // Search the entire list if none is currently selected
1536  if (line == wxNOT_FOUND)
1537  {
1538  line = cnt;
1539  }
1540  else
1541  {
1542  // Search from the node following the current one
1543  for (int i = line + 1; i < cnt; i++)
1544  {
1545  wxString label;
1546 
1547  // Get the string to search based on view type
1548  if (mViewType == ViewByTree)
1549  {
1550  label = GetLabel(LineToIndex(i));
1551  }
1552  else if (mViewType == ViewByName)
1553  {
1555  }
1556  else if (mViewType == ViewByKey)
1557  {
1558  label = GetKey(LineToIndex(i)).Display();
1559  }
1560 
1561  // Move selection if they match
1562  if (label.Left(1).IsSameAs(keycode, false))
1563  {
1564  SelectNode(LineToIndex(i));
1565 
1566  found = true;
1567 
1568  break;
1569  }
1570  }
1571  }
1572 
1573  // A match wasn't found
1574  if (!found)
1575  {
1576  // So scan from the start of the list to the current node
1577  for (int i = 0; i < line; i++)
1578  {
1579  wxString label;
1580 
1581  // Get the string to search based on view type
1582  if (mViewType == ViewByTree)
1583  {
1584  label = GetLabel(LineToIndex(i));
1585  }
1586  else if (mViewType == ViewByName)
1587  {
1589  }
1590  else if (mViewType == ViewByKey)
1591  {
1592  label = GetKey(LineToIndex(i)).Display();
1593  }
1594 
1595  // Move selection if they match
1596  if (label.Left(1).IsSameAs(keycode, false))
1597  {
1598  SelectNode(LineToIndex(i));
1599 
1600  found = true;
1601 
1602  break;
1603  }
1604  }
1605  }
1606 
1607  // A node wasn't found so allow further processing
1608  if (!found) {
1609  event.Skip();
1610  }
1611 
1612  // Otherwise, further processing of the event is not wanted
1613  // (we didn't call event.Skip()
1614  }
1615  }
1616 }
1617 
1618 //
1619 // Handle the wxEVT_LEFT_DOWN event
1620 //
1621 void
1622 KeyView::OnLeftDown(wxMouseEvent & event)
1623 {
1624  // Only check if for tree view
1625  if (mViewType != ViewByTree)
1626  {
1627  // Allow further processing (important for focus handling)
1628  event.Skip();
1629 
1630  return;
1631  }
1632 
1633  // Get the mouse position when the button was pressed
1634  wxPoint pos = event.GetPosition();
1635 
1636  // And see if it was on a line within the view
1637  int line = VirtualHitTest(pos.y);
1638 
1639  // It was on a line
1640  if (line != wxNOT_FOUND)
1641  {
1642  KeyNode *node = mLines[line];
1643 
1644  // Toggle the open state if this is a parent node
1645  if (node->isparent)
1646  {
1647  // Toggle state
1648  node->isopen = !node->isopen;
1649 
1650  // Don't want the view to scroll vertically, so remember the current
1651  // top line.
1652  size_t topline = GetVisibleBegin();
1653 
1654  // Refresh the view now that the number of lines have changed
1655  RefreshLines();
1656 
1657  // Reset the original top line
1658  ScrollToRow(topline);
1659 
1660  // And make sure current line is still selected
1661  SelectNode(LineToIndex(line));
1662 
1663  // If a node is closed near the bottom of the tree,
1664  // the node may move down, and no longer be at the
1665  // mouse pointer position. So don't allow further processing as this
1666  // selects the line at the mouse position. Bug 1723.
1667  // So we need to set the focus.
1668  SetFocus();
1669  return;
1670  }
1671  }
1672 
1673  // Allow further processing (important for focus handling)
1674  event.Skip();
1675 }
1676 
1677 //
1678 // Sort compare function for tree view
1679 //
1680 // We want to leave the "menu" nodes alone as they are in the
1681 // order as they appear in the menus. But, we want to sort the
1682 // "command" nodes.
1683 //
1684 // To accomplish this, we prepend each label with its line number
1685 // (in hex) for "menu" nodes. This ensures they will remain in
1686 // their original order.
1687 //
1688 // We prefix all "command" nodes with "ffffffff" (highest hex value)
1689 // to allow the sort to reorder them as needed.
1690 //
1691 bool
1693 {
1694  unsigned int k1UInt= 0xffffffff;
1695  unsigned int k2UInt= 0xffffffff;
1696 
1697  // This is a "command" node if its category is "Command"
1698  // and it is a child of the "Command" category. This latter
1699  // test ensures that the "Command" parent will be handled
1700  // as a "menu" node and remain at the bottom of the list.
1701  if (t1->category != CommandTranslated || t1->isparent)
1702  k1UInt = (unsigned int) t1->line;
1703 
1704  // See above for explanation
1705  if (t2->category != CommandTranslated || t2->isparent)
1706  k2UInt = (unsigned int) t2->line;
1707 
1708  if( k1UInt < k2UInt )
1709  return true;
1710  if( k1UInt > k2UInt )
1711  return false;
1712 
1713  return ( t1->label < t2->label );
1714 }
1715 
1716 //
1717 // Sort compare function for command view
1718 //
1719 // Nothing special here, just a standard ascending sort.
1720 //
1721 bool
1723 {
1724  wxString k1 = t1->label;
1725  wxString k2 = t2->label;
1726 
1727  // Prepend prefix if available
1728  if (!t1->prefix.empty())
1729  {
1730  k1 = t1->prefix + wxT(" - ") + k1;
1731  }
1732 
1733  // Prepend prefix if available
1734  if (!t2->prefix.empty())
1735  {
1736  k2 = t2->prefix + wxT(" - ") + k2;
1737  }
1738 
1739  return (k1 < k2);
1740 }
1741 
1742 //
1743 // Sort compare function for key view
1744 //
1745 // We want all nodes with key assignments to appear in ascending order
1746 // at the top of the list and all nodes without assignment to appear in
1747 // ascending order at the bottom of the list.
1748 //
1749 // We accomplish this by prefixing all non-assigned entries with 0xff.
1750 // This will force them to the end, but still allow them to be sorted in
1751 // ascending order.
1752 //
1753 // The assigned entries simply get sorted as normal.
1754 //
1755 bool
1757 {
1758  wxString k1 = t1->key.Display();
1759  wxString k2 = t2->key.Display();
1760 
1761  // Left node is unassigned, so prefix it
1762  if(k1.empty())
1763  {
1764  k1 = wxT("\xff");
1765  }
1766 
1767  // Right node is unassigned, so prefix it
1768  if(k2.empty())
1769  {
1770  k2 = wxT("\xff");
1771  }
1772 
1773  // Add prefix if available
1774  if (!t1->prefix.empty())
1775  {
1776  k1 += t1->prefix + wxT(" - ");
1777  }
1778 
1779  // Add prefix if available
1780  if (!t2->prefix.empty())
1781  {
1782  k2 += t2->prefix + wxT(" - ");
1783  }
1784 
1785  // Add labels
1786  k1 += t1->label;
1787  k2 += t2->label;
1788 
1789  return (k1 < k2);
1790 }
1791 
1792 #if wxUSE_ACCESSIBILITY
1793 
1794 //
1795 // Return parenthood state of line
1796 //
1797 bool
1798 KeyView::HasChildren(int line)
1799 {
1800  // Make sure line is valid
1801  if (line < 0 || line >= (int) mLines.size())
1802  {
1803  wxASSERT(false);
1804  return false;
1805  }
1806 
1807  return mLines[line]->isparent;
1808 }
1809 
1810 //
1811 // Returns espanded/collapsed state of line
1812 //
1813 bool
1814 KeyView::IsExpanded(int line)
1815 {
1816  // Make sure line is valid
1817  if (line < 0 || line >= (int) mLines.size())
1818  {
1819  wxASSERT(false);
1820  return false;
1821  }
1822 
1823  return mLines[line]->isopen;
1824 }
1825 
1826 //
1827 // Returns the height of the line
1828 //
1829 wxCoord
1830 KeyView::GetLineHeight(int line)
1831 {
1832  // Make sure line is valid
1833  if (line < 0 || line >= (int) mLines.size())
1834  {
1835  wxASSERT(false);
1836  return 0;
1837  }
1838 
1839  return OnGetRowHeight(line);
1840 }
1841 
1842 //
1843 // Returns the value to be presented to accessibility
1844 //
1845 // Currently, the command and key are both provided.
1846 //
1847 wxString
1848 KeyView::GetValue(int line)
1849 {
1850  // Make sure line is valid
1851  if (line < 0 || line >= (int) mLines.size())
1852  {
1853  wxASSERT(false);
1854  return wxEmptyString;
1855  }
1856  int index = LineToIndex(line);
1857 
1858  // Get the label and key values
1859  wxString value;
1860  if (mViewType == ViewByTree)
1861  {
1862  value = GetLabel(index);
1863  }
1864  else
1865  {
1866  value = GetFullLabel(index);
1867  }
1868  wxString key = GetKey(index).Display();
1869 
1870  // Add the key if it isn't empty
1871  if (!key.empty())
1872  {
1873  if (mViewType == ViewByKey)
1874  {
1875  value = key + wxT(" ") + value;
1876  }
1877  else
1878  {
1879  value = value + wxT(" ") + key;
1880  }
1881  }
1882 
1883  return value;
1884 }
1885 
1886 //
1887 // Returns the current view type
1888 //
1889 ViewByType
1890 KeyView::GetViewType()
1891 {
1892  return mViewType;
1893 }
1894 
1895 // ============================================================================
1896 // Accessibility provider for the KeyView class
1897 // ============================================================================
1898 KeyViewAx::KeyViewAx(KeyView *view)
1899 : WindowAccessible(view)
1900 {
1901  mView = view;
1902  mLastId = -1;
1903 }
1904 
1905 //
1906 // Send an event notification to accessibility that the view
1907 // has changed.
1908 //
1909 void
1910 KeyViewAx::ListUpdated()
1911 {
1912  NotifyEvent(wxACC_EVENT_OBJECT_REORDER,
1913  mView,
1914  wxOBJID_CLIENT,
1915  0);
1916 }
1917 
1918 //
1919 // Inform accessibility a NEW line has been selected and/or a previously
1920 // selected line is being unselected
1921 //
1922 void
1923 KeyViewAx::SetCurrentLine(int line)
1924 {
1925  // Only send selection remove notification if a line was
1926  // previously selected
1927  if (mLastId != -1)
1928  {
1929  NotifyEvent(wxACC_EVENT_OBJECT_SELECTIONREMOVE,
1930  mView,
1931  wxOBJID_CLIENT,
1932  mLastId);
1933  }
1934 
1935  // Nothing is selected now
1936  mLastId = -1;
1937 
1938  // Just clearing selection
1939  if (line != wxNOT_FOUND)
1940  {
1941  // Convert line number to childId
1942  LineToId(line, mLastId);
1943 
1944  // Send notifications that the line has focus
1945  if (mView == wxWindow::FindFocus()) {
1946  NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
1947  mView,
1948  wxOBJID_CLIENT,
1949  mLastId);
1950  }
1951 
1952  // And is selected
1953  NotifyEvent(wxACC_EVENT_OBJECT_SELECTION,
1954  mView,
1955  wxOBJID_CLIENT,
1956  mLastId);
1957  }
1958 }
1959 
1960 //
1961 // Convert the childId to a line number and return FALSE if it
1962 // represents a child or TRUE if it a line
1963 //
1964 bool
1965 KeyViewAx::IdToLine(int childId, int & line)
1966 {
1967  if (childId == wxACC_SELF)
1968  {
1969  return false;
1970  }
1971 
1972  // Convert to line
1973  line = childId - 1;
1974 
1975  // Make sure id is valid
1976  if (line < 0 || line >= (int) mView->GetItemCount())
1977  {
1978  // Indicate the control itself in this case
1979  return false;
1980  }
1981 
1982  return true;
1983 }
1984 
1985 //
1986 // Convert the line number to a childId.
1987 //
1988 bool
1989 KeyViewAx::LineToId(int line, int & childId)
1990 {
1991  // Make sure line is valid
1992  if (line < 0 || line >= (int) mView->GetItemCount())
1993  {
1994  // Indicate the control itself in this case
1995  childId = wxACC_SELF;
1996  return false;
1997  }
1998 
1999  // Convert to line
2000  childId = line + 1;
2001 
2002  return true;
2003 }
2004 
2005 // Can return either a child object, or an integer
2006 // representing the child element, starting from 1.
2007 wxAccStatus
2008 KeyViewAx::HitTest(const wxPoint & pt, int *childId, wxAccessible **childObject)
2009 {
2010  // Just to be safe
2011  *childObject = NULL;
2012 
2013  wxPoint pos = mView->ScreenToClient(pt);
2014 
2015  // See if it's on a line within the view
2016  int line = mView->HitTest(pos);
2017 
2018  // It was on a line
2019  if (line != wxNOT_FOUND)
2020  {
2021  LineToId(line, *childId);
2022  return wxACC_OK;
2023  }
2024 
2025  // Let the base class handle it
2026  return wxACC_NOT_IMPLEMENTED;
2027 }
2028 
2029 // Retrieves the address of an IDispatch interface for the specified child.
2030 // All objects must support this property.
2031 wxAccStatus
2032 KeyViewAx::GetChild(int childId, wxAccessible** child)
2033 {
2034  if (childId == wxACC_SELF)
2035  {
2036  *child = this;
2037  }
2038  else
2039  {
2040  *child = NULL;
2041  }
2042 
2043  return wxACC_OK;
2044 }
2045 
2046 // Gets the number of children.
2047 wxAccStatus
2048 KeyViewAx::GetChildCount(int *childCount)
2049 {
2050  *childCount = (int) mView->GetItemCount();
2051 
2052  return wxACC_OK;
2053 }
2054 
2055 // Gets the default action for this object (0) or > 0 (the action for a child).
2056 // Return wxACC_OK even if there is no action. actionName is the action, or the empty
2057 // string if there is no action.
2058 // The retrieved string describes the action that is performed on an object,
2059 // not what the object does as a result. For example, a toolbar button that prints
2060 // a document has a default action of "Press" rather than "Prints the current document."
2061 wxAccStatus
2062 KeyViewAx::GetDefaultAction(int WXUNUSED(childId), wxString *actionName)
2063 {
2064  actionName->clear();
2065 
2066  return wxACC_OK;
2067 }
2068 
2069 // Returns the description for this object or a child.
2070 wxAccStatus
2071 KeyViewAx::GetDescription(int WXUNUSED(childId), wxString *description)
2072 {
2073  description->clear();
2074 
2075  return wxACC_OK;
2076 }
2077 
2078 // Returns help text for this object or a child, similar to tooltip text.
2079 wxAccStatus
2080 KeyViewAx::GetHelpText(int WXUNUSED(childId), wxString *helpText)
2081 {
2082  helpText->clear();
2083 
2084  return wxACC_OK;
2085 }
2086 
2087 // Returns the keyboard shortcut for this object or child.
2088 // Return e.g. ALT+K
2089 wxAccStatus
2090 KeyViewAx::GetKeyboardShortcut(int WXUNUSED(childId), wxString *shortcut)
2091 {
2092  shortcut->clear();
2093 
2094  return wxACC_OK;
2095 }
2096 
2097 // Returns the rectangle for this object (id = 0) or a child element (id > 0).
2098 // rect is in screen coordinates.
2099 wxAccStatus
2100 KeyViewAx::GetLocation(wxRect & rect, int elementId)
2101 {
2102  int line;
2103 
2104  if (IdToLine(elementId, line))
2105  {
2106  if (!mView->IsVisible(line))
2107  {
2108  return wxACC_FAIL;
2109  }
2110 
2111  wxRect rectLine;
2112 
2113  rectLine.width = mView->GetClientSize().GetWidth();
2114 
2115  // iterate over all visible lines
2116  for (int i = (int) mView->GetVisibleBegin(); i <= line; i++)
2117  {
2118  wxCoord hLine = mView->GetLineHeight(i);
2119 
2120  rectLine.height = hLine;
2121 
2122  rect = rectLine;
2123  wxPoint margins = mView->GetMargins();
2124  rect.Deflate(margins.x, margins.y);
2125  rectLine.y += hLine;
2126  }
2127 
2128  rect.SetPosition(mView->ClientToScreen(rect.GetPosition()));
2129  }
2130  else
2131  {
2132  rect = mView->GetRect();
2133  rect.SetPosition(mView->GetParent()->ClientToScreen(rect.GetPosition()));
2134  }
2135 
2136  return wxACC_OK;
2137 }
2138 
2139 wxAccStatus
2140 KeyViewAx::Navigate(wxNavDir WXUNUSED(navDir),
2141  int WXUNUSED(fromId),
2142  int *WXUNUSED(toId),
2143  wxAccessible **WXUNUSED(toObject))
2144 {
2145  return wxACC_NOT_IMPLEMENTED;
2146 }
2147 
2148 // Gets the name of the specified object.
2149 wxAccStatus
2150 KeyViewAx::GetName(int childId, wxString *name)
2151 {
2152  int line;
2153 
2154  if (!IdToLine(childId, line))
2155  {
2156  *name = mView->GetName();
2157  }
2158  else
2159  {
2160  if (IdToLine(childId, line))
2161  {
2162  *name = mView->GetValue(line);
2163  }
2164  }
2165 
2166  return wxACC_OK;
2167 }
2168 
2169 wxAccStatus
2170 KeyViewAx::GetParent(wxAccessible ** WXUNUSED(parent))
2171 {
2172  return wxACC_NOT_IMPLEMENTED;
2173 }
2174 
2175 // Returns a role constant.
2176 wxAccStatus
2177 KeyViewAx::GetRole(int childId, wxAccRole *role)
2178 {
2179  if (childId == wxACC_SELF)
2180  {
2181 #if defined(__WXMSW__)
2182  *role = mView->GetViewType() == ViewByTree ? wxROLE_SYSTEM_OUTLINE : wxROLE_SYSTEM_LIST;
2183 #endif
2184 
2185 #if defined(__WXMAC__)
2186  *role = wxROLE_SYSTEM_GROUPING;
2187 #endif
2188  }
2189  else
2190  {
2191 #if defined(__WXMAC__)
2192  *role = wxROLE_SYSTEM_TEXT;
2193 #else
2194  *role = mView->GetViewType() == ViewByTree ? wxROLE_SYSTEM_OUTLINEITEM : wxROLE_SYSTEM_LISTITEM;
2195 #endif
2196  }
2197 
2198  return wxACC_OK;
2199 }
2200 
2201 // Gets a variant representing the selected children
2202 // of this object.
2203 // Acceptable values:
2204 // - a null variant (IsNull() returns TRUE)
2205 // - a list variant (GetType() == wxT("list"))
2206 // - an integer representing the selected child element,
2207 // or 0 if this object is selected (GetType() == wxT("long"))
2208 // - a "void*" pointer to a wxAccessible child object
2209 wxAccStatus
2210 KeyViewAx::GetSelections(wxVariant *selections)
2211 {
2212  int id;
2213 
2214  LineToId(mView->GetSelection(), id);
2215 
2216  *selections = (long) id;
2217 
2218  return wxACC_OK;
2219 }
2220 
2221 // Returns a state constant.
2222 wxAccStatus
2223 KeyViewAx::GetState(int childId, long *state)
2224 {
2225  int flag = wxACC_STATE_SYSTEM_FOCUSABLE;
2226  int line;
2227 
2228  if (!IdToLine(childId, line))
2229  {
2230  *state = wxACC_STATE_SYSTEM_FOCUSABLE; // |
2231  //mView->FindFocus() == mView ? wxACC_STATE_SYSTEM_FOCUSED : 0;
2232  return wxACC_OK;
2233  }
2234 
2235 #if defined(__WXMSW__)
2236  int selected = mView->GetSelection();
2237 
2238  flag |= wxACC_STATE_SYSTEM_SELECTABLE;
2239 
2240  if (line == selected)
2241  {
2242  flag |= wxACC_STATE_SYSTEM_FOCUSED |
2243  wxACC_STATE_SYSTEM_SELECTED;
2244  }
2245 
2246  if (mView->HasChildren(line))
2247  {
2248  flag |= mView->IsExpanded(line) ?
2249  wxACC_STATE_SYSTEM_EXPANDED :
2250  wxACC_STATE_SYSTEM_COLLAPSED;
2251  }
2252 #endif
2253 
2254 #if defined(__WXMAC__1)
2255  if (mGrid->IsInSelection(row, col))
2256  {
2257  flag |= wxACC_STATE_SYSTEM_SELECTED;
2258  }
2259 
2260  if (mGrid->GetGridCursorRow() == row && mGrid->GetGridCursorCol() == col)
2261  {
2262  flag |= wxACC_STATE_SYSTEM_FOCUSED;
2263  }
2264 
2265  if (mGrid->IsReadOnly(row, col))
2266  {
2267  flag |= wxACC_STATE_SYSTEM_UNAVAILABLE;
2268  }
2269 #endif
2270 
2271  *state = flag;
2272 
2273  return wxACC_OK;
2274 }
2275 
2276 // Returns a localized string representing the value for the object
2277 // or child.
2278 wxAccStatus
2279 KeyViewAx::GetValue(int childId, wxString *strValue)
2280 {
2281  int line;
2282 
2283  strValue->clear();
2284 
2285  if (!IdToLine(childId, line))
2286  {
2287  return wxACC_NOT_IMPLEMENTED;
2288  }
2289 
2290 #if defined(__WXMSW__)
2291  if (mView->GetViewType() == ViewByTree)
2292  {
2293  KeyNode *node = mView->mLines[line];
2294  strValue->Printf(wxT("%d"), node->depth - 1);
2295  }
2296 
2297  // Don't set a value for the other view types
2298  return wxACC_NOT_IMPLEMENTED;
2299 #endif
2300 
2301 #if defined(__WXMAC__)
2302  return GetName(childId, strValue);
2303 #endif
2304 }
2305 
2306 #if defined(__WXMAC__)
2307 // Selects the object or child.
2308 wxAccStatus
2309 KeyViewAx::Select(int childId, wxAccSelectionFlags selectFlags)
2310 {
2311 #if 0
2312  int row;
2313  int col;
2314 
2315  if (GetRowCol(childId, row, col))
2316  {
2317 
2318  if (selectFlags & wxACC_SEL_TAKESELECTION)
2319  {
2320  mGrid->SetGridCursor(row, col);
2321  }
2322 
2323  mGrid->SelectBlock(row, col, row, col, selectFlags & wxACC_SEL_ADDSELECTION);
2324  }
2325 #endif
2326  return wxACC_OK;
2327 }
2328 #endif
2329 
2330 // Gets the window with the keyboard focus.
2331 // If childId is 0 and child is NULL, no object in
2332 // this subhierarchy has the focus.
2333 // If this object has the focus, child should be 'this'.
2334 wxAccStatus
2335 KeyViewAx::GetFocus(int * WXUNUSED(childId), wxAccessible **child)
2336 {
2337  *child = this;
2338 
2339  return wxACC_OK;
2340 }
2341 
2342 #endif // wxUSE_ACCESSIBILITY
size
size_t size
Definition: ffmpeg-2.3.6-single-header.h:412
KeyView::mLineHeight
wxCoord mLineHeight
Definition: KeyView.h:159
KeyView::OnSelected
void OnSelected(wxCommandEvent &event)
Definition: KeyView.cpp:1331
KeyView::mWidth
wxCoord mWidth
Definition: KeyView.h:156
KeyView::mKeyWidth
wxCoord mKeyWidth
Definition: KeyView.h:162
KeyView::OnMeasureItem
wxCoord OnMeasureItem(size_t line) const override
Definition: KeyView.cpp:1319
KeyView::RefreshBindings
void RefreshBindings(const CommandIDs &names, const TranslatableStrings &categories, const TranslatableStrings &prefixes, const TranslatableStrings &labels, const std::vector< NormalizedKeyString > &keys, bool bSort)
Definition: KeyView.cpp:636
KeyView::RefreshLines
void RefreshLines(bool bSort=true)
Definition: KeyView.cpp:851
NormalizedKeyString::Display
wxString Display(bool usesSpecialChars=false) const
Definition: Keyboard.cpp:56
KeyView::OnLeftDown
void OnLeftDown(wxMouseEvent &event)
Definition: KeyView.cpp:1622
RefreshCode::RefreshAll
@ RefreshAll
Definition: RefreshCode.h:26
flag
static std::once_flag flag
Definition: WaveformView.cpp:1119
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
KeyView::mCommandWidth
int mCommandWidth
Definition: KeyView.h:161
KeyView::GetName
wxString GetName() const override
Definition: KeyView.cpp:194
KeyView::CmpKeyNodeByName
static bool CmpKeyNodeByName(KeyNode *n1, KeyNode *n2)
Definition: KeyView.cpp:1722
ViewByType
ViewByType
Definition: KeyView.h:61
AColor::Line
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:109
KeyView::mNodes
std::vector< KeyNode > mNodes
Definition: KeyView.h:149
KeyNode::category
wxString category
Definition: KeyView.h:44
KeyNode::isparent
bool isparent
Definition: KeyView.h:53
KeyView::ExpandAll
void ExpandAll()
Definition: KeyView.cpp:503
KeyView::OnScroll
void OnScroll(wxScrollWinEvent &event)
Definition: KeyView.cpp:1395
KeyView::GetSelected
int GetSelected() const
Definition: KeyView.cpp:185
KeyNode::ispfx
bool ispfx
Definition: KeyView.h:52
KeyView::SetKey
bool SetKey(int index, const NormalizedKeyString &key)
Definition: KeyView.cpp:355
KeyView::SetKeyByName
bool SetKeyByName(const CommandID &name, const NormalizedKeyString &key)
Definition: KeyView.cpp:396
KeyNode::depth
int depth
Definition: KeyView.h:50
TranslatableString::Strip
TranslatableString & Strip(unsigned options=MenuCodes) &
Definition: TranslatableString.cpp:41
KeyNode::name
CommandID name
Definition: KeyView.h:43
CommandIDs
std::vector< CommandID > CommandIDs
Definition: Identifier.h:233
KeyView::CmpKeyNodeByKey
static bool CmpKeyNodeByKey(KeyNode *n1, KeyNode *n2)
Definition: KeyView.cpp:1756
KV_BITMAP_SIZE
#define KV_BITMAP_SIZE
Definition: KeyView.cpp:131
KeyNode::line
int line
Definition: KeyView.h:49
KeyView::GetFullLabel
wxString GetFullLabel(int index) const
Definition: KeyView.cpp:220
KeyNode::key
NormalizedKeyString key
Definition: KeyView.h:47
label
TranslatableString label
Definition: Tags.cpp:756
KeyView::mScrollX
wxCoord mScrollX
Definition: KeyView.h:155
KeyView::OnKillFocus
void OnKillFocus(wxFocusEvent &event)
Definition: KeyView.cpp:1369
KeyView::OnSize
void OnSize(wxSizeEvent &event)
Definition: KeyView.cpp:1385
KeyView::IndexToLine
int IndexToLine(int index) const
Definition: KeyView.cpp:1146
ViewByKey
@ ViewByKey
Definition: KeyView.h:64
KeyView::OnSetFocus
void OnSetFocus(wxFocusEvent &event)
Definition: KeyView.cpp:1346
name
const TranslatableString name
Definition: Distortion.cpp:98
KeyView::GetKey
NormalizedKeyString GetKey(int index) const
Definition: KeyView.cpp:322
KV_LEFT_MARGIN
#define KV_LEFT_MARGIN
Definition: KeyView.cpp:132
KeyView::GetNameByKey
CommandID GetNameByKey(const NormalizedKeyString &key) const
Definition: KeyView.cpp:282
WindowAccessible
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
ViewByTree
@ ViewByTree
Definition: KeyView.h:62
AColor::DrawFocus
static void DrawFocus(wxDC &dc, wxRect &r)
Definition: AColor.cpp:157
KeyView::GetIndexByKey
int GetIndexByKey(const NormalizedKeyString &key) const
Definition: KeyView.cpp:302
KeyView::OnKeyDown
void OnKeyDown(wxKeyEvent &event)
Definition: KeyView.cpp:1417
KV_VSCROLL_WIDTH
#define KV_VSCROLL_WIDTH
Definition: KeyView.cpp:134
NormalizedKeyString
Definition: Keyboard.h:24
id
int id
Definition: WaveTrackControls.cpp:577
ViewByName
@ ViewByName
Definition: KeyView.h:63
KeyView::mFilter
wxString mFilter
Definition: KeyView.h:153
CommandTranslated
static wxString CommandTranslated
Definition: KeyView.cpp:149
KeyView::LineToIndex
int LineToIndex(int line) const
Definition: KeyView.cpp:1132
WindowAccessible.h
key
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: CommandManager.cpp:201
KeyView::GetIndexByName
int GetIndexByName(const CommandID &name) const
Definition: KeyView.cpp:246
KV_COLUMN_SPACER
#define KV_COLUMN_SPACER
Definition: KeyView.cpp:133
TaggedIdentifier< CommandIdTag, false >
KeyView::SetView
void SetView(ViewByType type)
Definition: KeyView.cpp:414
names
static TranslatableStrings names
Definition: Tags.cpp:744
_
#define _(s)
Definition: Internat.h:75
MenuTable::Command
std::unique_ptr< CommandItem > Command(const CommandID &name, const TranslatableString &label_in, void(Handler::*pmf)(const CommandContext &), CommandFlag flags, const CommandManager::Options &options={}, CommandHandlerFinder finder=FinderScope::DefaultFinder())
Definition: CommandManager.h:675
KeyView::UpdateHScroll
void UpdateHScroll()
Definition: KeyView.cpp:602
KeyView::CmpKeyNodeByTree
static bool CmpKeyNodeByTree(KeyNode *n1, KeyNode *n2)
Definition: KeyView.cpp:1692
KeyNode::prefix
wxString prefix
Definition: KeyView.h:45
KeyView
Provides multiple views of keyboard shortcuts.
Definition: KeyView.h:73
KeyView::RecalcExtents
void RecalcExtents()
Definition: KeyView.cpp:547
KeyView::SetFilter
void SetFilter(const wxString &filter)
Definition: KeyView.cpp:472
KeyNode::isopen
bool isopen
Definition: KeyView.h:54
KeyView::~KeyView
virtual ~KeyView()
Definition: KeyView.cpp:177
END_EVENT_TABLE
END_EVENT_TABLE()
TranslatableString::Translation
wxString Translation() const
Definition: TranslatableString.h:79
KeyView::SelectNode
void SelectNode(int index)
Definition: KeyView.cpp:1105
safenew
#define safenew
Definition: MemoryX.h:10
KeyView::OnDrawItem
void OnDrawItem(wxDC &dc, const wxRect &rect, size_t line) const override
Definition: KeyView.cpp:1235
KeyView.h
AColor.h
KeyView::CanSetKey
bool CanSetKey(int index) const
Definition: KeyView.cpp:338
KeyNode::label
wxString label
Definition: KeyView.h:46
KeyNode::iscat
bool iscat
Definition: KeyView.h:51
KeyNode
Definition: KeyView.h:25
KeyView::GetLabel
wxString GetLabel(int index) const
Definition: KeyView.cpp:204
KeyView::CollapseAll
void CollapseAll()
Definition: KeyView.cpp:525
KeyNode::index
int index
Definition: KeyView.h:48
KeyView::mViewType
ViewByType mViewType
Definition: KeyView.h:152
KeyView::OnDrawBackground
void OnDrawBackground(wxDC &dc, const wxRect &rect, size_t line) const override
Definition: KeyView.cpp:1162
KeyView::mLines
std::vector< KeyNode * > mLines
Definition: KeyView.h:150