2009-11-13 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / tabnavigationwalker.cpp
blob5e5a2689e953ccaae76103c69a4033bb3d29407d
1 // /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 // /*
3 // * tabnavigationwalker.cpp
4 // *
5 // * Contact:
6 // * Moonlight List (moonlight-list@lists.ximian.com)
7 // *
8 // * Copyright 2009 Novell, Inc. (http://www.novell.com)
9 // *
10 // * See the LICENSE file included with the distribution for details.
11 // */
12 //
14 #include "control.h"
15 #include "deployment.h"
16 #include "tabnavigationwalker.h"
17 #include "type.h"
19 TabNavigationWalker::TabNavigationWalker (UIElement *root, UIElement *current, bool forwards, Types *types)
21 this->root = root;
22 this->current = current;
23 this->forwards = forwards;
24 this->types = types;
26 this->tab_sorted = g_ptr_array_new ();
29 TabNavigationWalker::~TabNavigationWalker ()
31 g_ptr_array_free (tab_sorted, true);
34 bool
35 TabNavigationWalker::FocusChild ()
37 UIElement *child;
38 bool child_is_control;
39 int current_index = -1;
41 // Add each branch of the visual tree to the array and then sort them
42 // based on the TabIndex of the first Control in each branch
43 VisualTreeWalker child_walker (root);
44 while ((child = child_walker.Step ()))
45 g_ptr_array_add (tab_sorted, child);
47 if (tab_sorted->len > 1) {
48 Sort (tab_sorted, types);
50 if (ReverseTab ()) {
51 GPtrArray *reverse = g_ptr_array_sized_new (tab_sorted->len);
52 for (int i = tab_sorted->len - 1; i >= 0; i--)
53 g_ptr_array_add (reverse, tab_sorted->pdata [i]);
55 g_ptr_array_free (tab_sorted, true);
56 tab_sorted = reverse;
60 // Find the index of the currently selected node so we know which node to
61 // tab to next
62 for (uint i = 0; i < tab_sorted->len; i++)
63 if (tab_sorted->pdata [i] == current)
64 current_index = i;
66 // If a child of the root element is Focused and we're forward-tabbing, it means we should
67 // skip the entire subtree and go to the next 'root'. If we're reverse-tabbing it means we
68 // should Focus the root.
69 if (current_index != -1 && GetActiveNavigationMode (root, types) == KeyboardNavigationModeOnce) {
70 // If we're tabbing backwards and a child of this control is currently Focused, we
71 // should focus this control.
72 if (ReverseTab () && types->IsSubclassOf (root->GetObjectType (), Type::CONTROL))
73 return TabTo ((Control *)root);
74 return false;
77 if (tab_sorted->len > 0) {
78 // If the currently selected element was found at index 'i' we need to tab
79 // to the *next* index. If the currently selected item was not here, we
80 // need to start at index 0.
81 for (unsigned int i = 0; i < tab_sorted->len; i++) {
82 // If we are not cycling, it means we've tabbed to the last element of this node and so should
83 if ((i + current_index + 1) == tab_sorted->len && GetActiveNavigationMode (root, types) != KeyboardNavigationModeCycle)
84 break;
86 child = (UIElement *) tab_sorted->pdata [(i + current_index + 1) % tab_sorted->len];
87 child_is_control = types->IsSubclassOf (child->GetObjectType (), Type::CONTROL);
89 if (child_is_control && !((Control *)child)->GetIsEnabled ())
90 continue;
92 // When tabbing backwards, we recurse all children *before* attempting to select this node
93 if (ReverseTab () && WalkChildren (child))
94 return true;
96 if (child_is_control && TabTo ((Control *)child))
97 return true;
99 if (ForwardTab () && WalkChildren (child))
100 return true;
104 // If we're tabbing backwards and a child of this control is currently Focused, we
105 // should focus this control.
106 if (current_index != -1 && ReverseTab ()) {
107 if (types->IsSubclassOf (root->GetObjectType (), Type::CONTROL))
108 return TabTo ((Control *)root);
111 // Nothing could be tabbed to on this visual level
112 return false;
115 bool
116 TabNavigationWalker::TabTo (Control *control)
118 return control->GetIsEnabled () && control->GetIsTabStop () && control->Focus (false);
121 bool
122 TabNavigationWalker::WalkChildren (UIElement *root, UIElement *current)
124 return TabNavigationWalker::WalkChildren (root, current, ForwardTab (), types);
128 // Static Methods
131 bool
132 TabNavigationWalker::Focus (UIElement *element, bool forwards)
134 bool focused = false;
135 UIElement *current = element;
136 UIElement *root = element;
137 Types *types = Deployment::GetCurrent ()->GetTypes ();
139 // If tabbing in reverse, we immediately go up a level from initial root as
140 // we know we will not be focusing any of its children.
141 if (!forwards)
142 root = root->GetVisualParent ();
144 do {
145 // If we're starting a tab sequence and the parent of the current element is set to
146 // the 'Once' navigation mode, we should not traverse the children
147 if (!(root == current &&
148 root->GetVisualParent () &&
149 GetActiveNavigationMode (root->GetVisualParent (), types) == KeyboardNavigationModeOnce)) {
150 focused |= WalkChildren (root, current, forwards, types);
153 if (!focused && GetActiveNavigationMode (root, types) == KeyboardNavigationModeCycle)
154 return true;
156 current = root;
157 root = root->GetVisualParent ();
158 } while (!focused && root);
160 if (!focused)
161 focused |= WalkChildren (current, NULL, forwards, types);
163 return focused;
166 KeyboardNavigationMode
167 TabNavigationWalker::GetActiveNavigationMode (UIElement *element, Types *types)
169 while (element) {
170 if (types->IsSubclassOf (element->GetObjectType (), Type::CONTROL))
171 return ((Control *)element)->GetTabNavigation ();
172 else
173 element = element->GetVisualParent ();
175 return KeyboardNavigationModeLocal;
178 void
179 TabNavigationWalker::Sort (GPtrArray *array, Types *types)
181 int end = array->len;
182 bool swapped;
184 do {
185 end --;
186 swapped = false;
187 for (int i = 0; i < end; i++) {
188 UIElement *left = NULL;
189 UIElement *right = NULL;
191 DeepTreeWalker left_walker ((UIElement *) array->pdata [i], Logical, types);
192 DeepTreeWalker right_walker ((UIElement *) array->pdata [i + 1], Logical, types);
194 while ((left = left_walker.Step ()) && !types->IsSubclassOf (left->GetObjectType (), Type::CONTROL)) { }
195 while ((right = right_walker.Step ()) && !types->IsSubclassOf (right->GetObjectType (), Type::CONTROL)) { }
197 if (TabCompare ((Control *)left, (Control *)right) > 0) {
198 left = (UIElement *) array->pdata [i];
199 array->pdata [i] = array->pdata [i + 1];
200 array->pdata [i + 1] = left;
201 swapped = true;
204 } while (swapped);
209 TabNavigationWalker::TabCompare (Control* left, Control* right)
211 if (!left)
212 return !right ? 0 : -1;
213 if (!right)
214 return 1;
216 Value *v1 = left->ReadLocalValue (Control::TabIndexProperty);
217 Value *v2 = right->ReadLocalValue (Control::TabIndexProperty);
219 if (!v1) {
220 return v2 ? -1 : 0;
221 } else if (!v2) {
222 return 1;
223 } else {
224 int l = v1->AsInt32 ();
225 int r = v2->AsInt32 ();
226 if (l > r)
227 return 1;
228 return l == r ? 0 : -1;
232 bool
233 TabNavigationWalker::WalkChildren (UIElement *root, UIElement *current, bool forwards, Types *types)
235 TabNavigationWalker walker (root, current, forwards, types);
236 return walker.FocusChild ();