Audacity 3.2.0
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 "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
38class KeyViewAx final : public WindowAccessible
39{
40public:
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
123private:
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
139BEGIN_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
149static wxString CommandTranslated = "Command";
150
151
152// ============================================================================
153// KeyView class
154// ============================================================================
155KeyView::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//
184int
186{
187 return LineToIndex(GetSelection());
188}
189
190//
191// Returns the name of the control
192//
193wxString
195{
196 // Just forward request
197 return wxVListBox::GetName();
198}
199
200//
201// Returns the label for the given index
202//
203wxString
204KeyView::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//
219wxString
220KeyView::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//
245int
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//
266KeyView::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//
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//
301int
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//
322KeyView::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//
337bool
338KeyView::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//
354bool
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)
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//
395bool
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//
413void
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//
471void
472KeyView::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//
502void
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//
524void
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//
546void
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
596}
597
598//
599// Update the horizontal scrollbar or remove it if not needed
600//
601void
603{
604 // Get the internal dimensions of the view
605 wxRect r = GetClientRect();
606
607 // Calculate the full line width
609 mKeyWidth +
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//
635void
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
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//
850void
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//
1104void
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//
1131int
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//
1145int
1146KeyView::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//
1161void
1162KeyView::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//
1234void
1235KeyView::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//
1318wxCoord
1319KeyView::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//
1330void
1331KeyView::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//
1345void
1346KeyView::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//
1368void
1369KeyView::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//
1384void
1385KeyView::OnSize(wxSizeEvent & WXUNUSED(event))
1386{
1387 // Update horizontal scrollbar
1388 UpdateHScroll();
1389}
1390
1391//
1392// Handle the wxEVT_SCROLL event
1393//
1394void
1395KeyView::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//
1416void
1417KeyView::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
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 {
1551 }
1552 else if (mViewType == ViewByName)
1553 {
1555 }
1556 else if (mViewType == ViewByKey)
1557 {
1559 }
1560
1561 // Move selection if they match
1562 if (label.Left(1).IsSameAs(keycode, false))
1563 {
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 {
1585 }
1586 else if (mViewType == ViewByName)
1587 {
1589 }
1590 else if (mViewType == ViewByKey)
1591 {
1593 }
1594
1595 // Move selection if they match
1596 if (label.Left(1).IsSameAs(keycode, false))
1597 {
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//
1621void
1622KeyView::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//
1691bool
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//
1721bool
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//
1755bool
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//
1797bool
1798KeyView::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//
1813bool
1814KeyView::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//
1829wxCoord
1830KeyView::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//
1847wxString
1848KeyView::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//
1890KeyView::GetViewType()
1891{
1892 return mViewType;
1893}
1894
1895// ============================================================================
1896// Accessibility provider for the KeyView class
1897// ============================================================================
1898KeyViewAx::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//
1909void
1910KeyViewAx::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//
1922void
1923KeyViewAx::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//
1964bool
1965KeyViewAx::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//
1988bool
1989KeyViewAx::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.
2007wxAccStatus
2008KeyViewAx::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.
2031wxAccStatus
2032KeyViewAx::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.
2047wxAccStatus
2048KeyViewAx::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."
2061wxAccStatus
2062KeyViewAx::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.
2070wxAccStatus
2071KeyViewAx::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.
2079wxAccStatus
2080KeyViewAx::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
2089wxAccStatus
2090KeyViewAx::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.
2099wxAccStatus
2100KeyViewAx::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
2139wxAccStatus
2140KeyViewAx::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.
2149wxAccStatus
2150KeyViewAx::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
2169wxAccStatus
2170KeyViewAx::GetParent(wxAccessible ** WXUNUSED(parent))
2171{
2172 return wxACC_NOT_IMPLEMENTED;
2173}
2174
2175// Returns a role constant.
2176wxAccStatus
2177KeyViewAx::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
2209wxAccStatus
2210KeyViewAx::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.
2222wxAccStatus
2223KeyViewAx::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.
2278wxAccStatus
2279KeyViewAx::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.
2308wxAccStatus
2309KeyViewAx::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'.
2334wxAccStatus
2335KeyViewAx::GetFocus(int * WXUNUSED(childId), wxAccessible **child)
2336{
2337 *child = this;
2338
2339 return wxACC_OK;
2340}
2341
2342#endif // wxUSE_ACCESSIBILITY
wxT("CloseDown"))
std::vector< CommandID > CommandIDs
Definition: Identifier.h:233
#define _(s)
Definition: Internat.h:73
#define KV_LEFT_MARGIN
Definition: KeyView.cpp:132
static wxString CommandTranslated
Definition: KeyView.cpp:149
END_EVENT_TABLE()
#define KV_VSCROLL_WIDTH
Definition: KeyView.cpp:134
#define KV_BITMAP_SIZE
Definition: KeyView.cpp:131
#define KV_COLUMN_SPACER
Definition: KeyView.cpp:133
ViewByType
Definition: KeyView.h:61
@ ViewByTree
Definition: KeyView.h:62
@ ViewByKey
Definition: KeyView.h:64
@ ViewByName
Definition: KeyView.h:63
#define safenew
Definition: MemoryX.h:10
static const AudacityProject::AttachedObjects::RegisteredFactory key
wxString name
Definition: TagsEditor.cpp:166
TranslatableString label
Definition: TagsEditor.cpp:165
static TranslatableStrings names
Definition: TagsEditor.cpp:153
std::vector< TranslatableString > TranslatableStrings
int id
static std::once_flag flag
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:194
static void DrawFocus(wxDC &dc, wxRect &r)
Definition: AColor.cpp:247
int depth
Definition: KeyView.h:50
bool isopen
Definition: KeyView.h:54
wxString label
Definition: KeyView.h:46
CommandID name
Definition: KeyView.h:43
NormalizedKeyString key
Definition: KeyView.h:47
bool isparent
Definition: KeyView.h:53
bool iscat
Definition: KeyView.h:51
int index
Definition: KeyView.h:48
wxString category
Definition: KeyView.h:44
bool ispfx
Definition: KeyView.h:52
wxString prefix
Definition: KeyView.h:45
int line
Definition: KeyView.h:49
Provides multiple views of keyboard shortcuts.
Definition: KeyView.h:73
bool CanSetKey(int index) const
Definition: KeyView.cpp:338
std::vector< KeyNode * > mLines
Definition: KeyView.h:150
int mCommandWidth
Definition: KeyView.h:161
void CollapseAll()
Definition: KeyView.cpp:525
void OnSetFocus(wxFocusEvent &event)
Definition: KeyView.cpp:1346
void OnLeftDown(wxMouseEvent &event)
Definition: KeyView.cpp:1622
void ExpandAll()
Definition: KeyView.cpp:503
void OnDrawBackground(wxDC &dc, const wxRect &rect, size_t line) const override
Definition: KeyView.cpp:1162
ViewByType mViewType
Definition: KeyView.h:152
int GetIndexByName(const CommandID &name) const
Definition: KeyView.cpp:246
int GetIndexByKey(const NormalizedKeyString &key) const
Definition: KeyView.cpp:302
int GetSelected() const
Definition: KeyView.cpp:185
wxCoord mScrollX
Definition: KeyView.h:155
void OnKillFocus(wxFocusEvent &event)
Definition: KeyView.cpp:1369
void OnDrawItem(wxDC &dc, const wxRect &rect, size_t line) const override
Definition: KeyView.cpp:1235
bool SetKey(int index, const NormalizedKeyString &key)
Definition: KeyView.cpp:355
void OnSelected(wxCommandEvent &event)
Definition: KeyView.cpp:1331
wxString GetLabel(int index) const
Definition: KeyView.cpp:204
void OnSize(wxSizeEvent &event)
Definition: KeyView.cpp:1385
void RefreshLines(bool bSort=true)
Definition: KeyView.cpp:851
void SelectNode(int index)
Definition: KeyView.cpp:1105
wxCoord mWidth
Definition: KeyView.h:156
void SetView(ViewByType type)
Definition: KeyView.cpp:414
void SetFilter(const wxString &filter)
Definition: KeyView.cpp:472
void RecalcExtents()
Definition: KeyView.cpp:547
static bool CmpKeyNodeByTree(KeyNode *n1, KeyNode *n2)
Definition: KeyView.cpp:1692
wxCoord OnMeasureItem(size_t line) const override
Definition: KeyView.cpp:1319
wxCoord mKeyWidth
Definition: KeyView.h:162
void OnScroll(wxScrollWinEvent &event)
Definition: KeyView.cpp:1395
std::vector< KeyNode > mNodes
Definition: KeyView.h:149
wxString GetFullLabel(int index) const
Definition: KeyView.cpp:220
void OnKeyDown(wxKeyEvent &event)
Definition: KeyView.cpp:1417
static bool CmpKeyNodeByName(KeyNode *n1, KeyNode *n2)
Definition: KeyView.cpp:1722
int IndexToLine(int index) const
Definition: KeyView.cpp:1146
wxCoord mLineHeight
Definition: KeyView.h:159
NormalizedKeyString GetKey(int index) const
Definition: KeyView.cpp:322
void UpdateHScroll()
Definition: KeyView.cpp:602
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
bool SetKeyByName(const CommandID &name, const NormalizedKeyString &key)
Definition: KeyView.cpp:396
virtual ~KeyView()
Definition: KeyView.cpp:177
wxString mFilter
Definition: KeyView.h:153
wxString GetName() const override
Definition: KeyView.cpp:194
CommandID GetNameByKey(const NormalizedKeyString &key) const
Definition: KeyView.cpp:282
int LineToIndex(int line) const
Definition: KeyView.cpp:1132
static bool CmpKeyNodeByKey(KeyNode *n1, KeyNode *n2)
Definition: KeyView.cpp:1756
TranslatableString & Strip(unsigned options=MenuCodes) &
wxString Translation() const
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
void SetFocus(const WindowPlacement &focus)
Set the window that accepts keyboard input.
Definition: BasicUI.h:392
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:383
bool HitTest(const RectangleArgs &args, const wxPoint &mousePos)
constexpr auto Command
Definition: MenuRegistry.h:456
wxString Display(bool usesSpecialChars=false) const
Definition: Keyboard.cpp:56