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