1 // /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 // * tabnavigationwalker.cpp
6 // * Moonlight List (moonlight-list@lists.ximian.com)
8 // * Copyright 2009 Novell, Inc. (http://www.novell.com)
10 // * See the LICENSE file included with the distribution for details.
15 #include "deployment.h"
16 #include "tabnavigationwalker.h"
19 TabNavigationWalker::TabNavigationWalker (UIElement
*root
, UIElement
*current
, bool forwards
, Types
*types
)
22 this->current
= current
;
23 this->forwards
= forwards
;
26 this->tab_sorted
= g_ptr_array_new ();
29 TabNavigationWalker::~TabNavigationWalker ()
31 g_ptr_array_free (tab_sorted
, true);
35 TabNavigationWalker::FocusChild ()
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
);
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);
60 // Find the index of the currently selected node so we know which node to
62 for (uint i
= 0; i
< tab_sorted
->len
; i
++)
63 if (tab_sorted
->pdata
[i
] == current
)
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
);
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
)
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 ())
92 // When tabbing backwards, we recurse all children *before* attempting to select this node
93 if (ReverseTab () && WalkChildren (child
))
96 if (child_is_control
&& TabTo ((Control
*)child
))
99 if (ForwardTab () && WalkChildren (child
))
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
116 TabNavigationWalker::TabTo (Control
*control
)
118 return control
->GetIsEnabled () && control
->GetIsTabStop () && control
->Focus (false);
122 TabNavigationWalker::WalkChildren (UIElement
*root
, UIElement
*current
)
124 return TabNavigationWalker::WalkChildren (root
, current
, ForwardTab (), types
);
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.
142 root
= root
->GetVisualParent ();
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
)
157 root
= root
->GetVisualParent ();
158 } while (!focused
&& root
);
161 focused
|= WalkChildren (current
, NULL
, forwards
, types
);
166 KeyboardNavigationMode
167 TabNavigationWalker::GetActiveNavigationMode (UIElement
*element
, Types
*types
)
170 if (types
->IsSubclassOf (element
->GetObjectType (), Type::CONTROL
))
171 return ((Control
*)element
)->GetTabNavigation ();
173 element
= element
->GetVisualParent ();
175 return KeyboardNavigationModeLocal
;
179 TabNavigationWalker::Sort (GPtrArray
*array
, Types
*types
)
181 int end
= array
->len
;
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
;
209 TabNavigationWalker::TabCompare (Control
* left
, Control
* right
)
212 return !right
? 0 : -1;
216 Value
*v1
= left
->ReadLocalValue (Control::TabIndexProperty
);
217 Value
*v2
= right
->ReadLocalValue (Control::TabIndexProperty
);
224 int l
= v1
->AsInt32 ();
225 int r
= v2
->AsInt32 ();
228 return l
== r
? 0 : -1;
233 TabNavigationWalker::WalkChildren (UIElement
*root
, UIElement
*current
, bool forwards
, Types
*types
)
235 TabNavigationWalker
walker (root
, current
, forwards
, types
);
236 return walker
.FocusChild ();