2009-08-26 Chris Toshok <toshok@ximian.com>
[moon.git] / src / control.cpp
blob4b41451af00f841042917a70414c9a75b42d2411
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * control.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include <config.h>
16 #include "rect.h"
17 #include "runtime.h"
18 #include "control.h"
19 #include "canvas.h"
20 #include "namescope.h"
21 #include "application.h"
22 #include "geometry.h"
23 #include "tabnavigationwalker.h"
25 Control::Control ()
27 SetObjectType (Type::CONTROL);
29 applied_template = NULL;
30 enabled_local = true;
31 enabled_parent = true;
32 template_root = NULL;
35 Control::~Control ()
39 void
40 Control::Dispose ()
42 if (applied_template) {
43 applied_template->unref();
44 applied_template = NULL;
47 if (template_root) {
48 template_root->unref ();
49 template_root = NULL;
52 FrameworkElement::Dispose ();
55 void
56 Control::FindElementsInHostCoordinates (cairo_t *cr, Point p, List *uielement_list)
58 if (GetIsEnabled ())
59 FrameworkElement::FindElementsInHostCoordinates (cr, p, uielement_list);
62 void
63 Control::HitTest (cairo_t *cr, Point p, List *uielement_list)
65 if (GetIsEnabled ())
66 FrameworkElement::HitTest (cr, p, uielement_list);
69 bool
70 Control::InsideObject (cairo_t *cr, double x, double y)
72 /*
73 * Controls don't get hit themselves the rendered elements
74 * do and it bubbles up
76 return false;
79 void
80 Control::OnLoaded ()
82 Types *types = Deployment::GetCurrent ()->GetTypes ();
83 UIElement *e = GetVisualParent ();
84 while (e && !types->IsSubclassOf (e->GetObjectType (), Type::CONTROL))
85 e = e->GetVisualParent ();
86 if (e)
87 ((Control *)e)->UpdateEnabled ();
89 FrameworkElement::OnLoaded ();
92 void
93 Control::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
95 if (args->GetProperty ()->GetOwnerType() != Type::CONTROL) {
96 FrameworkElement::OnPropertyChanged (args, error);
97 return;
100 if (args->GetId () == Control::TemplateProperty) {
101 if (IsLoaded())
102 ApplyTemplate ();
103 InvalidateMeasure ();
105 else if (args->GetId () == Control::PaddingProperty
106 || args->GetId () == Control::BorderThicknessProperty) {
107 InvalidateMeasure ();
108 } else if (args->GetId () == Control::IsEnabledProperty) {
109 if (!args->GetNewValue ()->AsBool ()) {
110 Surface *surface = Deployment::GetCurrent ()->GetSurface ();
111 if (surface && surface->GetFocusedElement () == this) {
112 // Ensure this element loses focus, then try to focus the next suitable element
113 surface->FocusElement (NULL);
114 TabNavigationWalker::Focus (this, true);
116 ReleaseMouseCapture ();
118 Emit (IsEnabledChangedEvent);
119 } else if (args->GetId () == Control::HorizontalContentAlignmentProperty
120 || args->GetId () == Control::VerticalContentAlignmentProperty) {
121 InvalidateArrange ();
123 NotifyListenersOfPropertyChange (args, error);
126 void
127 Control::SetVisualParent (UIElement *visual_parent)
129 FrameworkElement::SetVisualParent (visual_parent);
130 if (!UIElement::IsSubtreeLoaded (this))
131 return;
133 Types *types = Deployment::GetCurrent ()->GetTypes ();
134 if (!visual_parent) {
135 enabled_parent = true;
136 } else {
137 UIElement *parent = GetVisualParent ();
138 while (parent) {
139 if (!types->IsSubclassOf (parent->GetObjectType (), Type::CONTROL)) {
140 parent = parent->GetVisualParent ();
142 else {
143 this->enabled_parent = ((Control *)parent)->GetIsEnabled ();
144 break;
148 SetValue (Control::IsEnabledProperty, Value (enabled_local));
151 bool
152 Control::SetValueWithErrorImpl (DependencyProperty *property, Value *value, MoonError *error)
154 if (property->GetId () == Control::IsEnabledProperty) {
155 this->enabled_local = value->AsBool ();
156 if ((enabled_local && enabled_parent) == GetIsEnabled ())
157 return true;
159 Value v (enabled_local && (enabled_parent));
161 // If we don't propagate the changes down the tree here, the EnabledChanged events
162 // from the subtree are raised in the wrong order
163 bool b = FrameworkElement::SetValueWithErrorImpl (property, &v, error);
164 if (b)
165 UpdateEnabled ();
166 return b;
168 return FrameworkElement::SetValueWithErrorImpl (property, value, error);
171 bool
172 Control::ApplyTemplate ()
174 return ApplyTemplate (GetTemplate ());
177 bool
178 Control::ApplyTemplate (FrameworkTemplate *t)
180 if (applied_template == t)
181 return false;
183 ClearTemplate ();
185 if (!t)
186 return false;
188 applied_template = t;
189 applied_template->ref();
191 return ApplyTemplateRoot ((UIElement *) applied_template->GetVisualTree(this));
194 bool
195 Control::ApplyTemplateRoot (UIElement *root)
197 if (!root || root == template_root)
198 return false;
200 ElementAdded (root);
202 MoonError e;
203 root->SetParent (this, &e);
204 OnApplyTemplate ();
206 return true;
209 void
210 Control::ClearTemplate ()
212 if (applied_template) {
213 applied_template->unref();
214 applied_template = NULL;
217 ElementRemoved (template_root);
220 void
221 Control::OnApplyTemplate ()
223 Emit (TemplateAppliedEvent);
226 void
227 Control::ElementAdded (UIElement *item)
229 if (item == template_root)
230 return;
232 ElementRemoved (template_root);
234 template_root = item;
236 if (template_root) {
237 template_root->ref ();
238 FrameworkElement::ElementAdded (template_root);
241 SetSubtreeObject (template_root);
244 void
245 Control::ElementRemoved (UIElement *item)
247 if (template_root && item == template_root) {
248 template_root->unref ();
249 template_root = NULL;
250 SetSubtreeObject (NULL);
253 if (item)
254 FrameworkElement::ElementRemoved (item);
257 DependencyObject *
258 Control::GetTemplateChild (const char *name)
260 if (template_root)
261 return template_root->FindName (name);
263 return NULL;
266 bool
267 Control::Focus (bool recurse)
269 Surface *surface = GetSurface ();
270 if (!surface)
271 return false;
273 /* according to msdn, these three things must be true for an element to be focusable:
275 * 1. the element must be visible
276 * 2. the element must have IsTabStop = true
277 * 3. the element must be part of the plugin's visual tree, and must have had its Loaded event fired.
281 * If the current control is not focusable, we walk the visual tree and stop as soon
282 * as we find the first focusable child. That then becomes focused
284 Types *types = Deployment::GetCurrent ()->GetTypes ();
285 DeepTreeWalker walker (this);
286 while (UIElement *e = walker.Step ()) {
287 if (!types->IsSubclassOf (e->GetObjectType (), Type::CONTROL))
288 continue;
290 Control *c = (Control *)e;
291 if (!c->GetIsEnabled ()) {
292 if (!recurse)
293 return false;
295 walker.SkipBranch ();
296 continue;
299 // A control is focusable if it is attached to a visual tree whose root
300 // element has been loaded
301 bool loaded = false;
302 for (UIElement *check = this; !loaded && check != NULL; check = check->GetVisualParent ())
303 loaded |= check->IsLoaded ();
305 if (loaded && c->GetRenderVisible () && c->GetIsTabStop ())
306 return surface->FocusElement (c);
308 if (!recurse)
309 return false;
311 return false;
314 void
315 Control::UpdateEnabled ()
317 Types *types = Deployment::GetCurrent ()->GetTypes ();
318 DeepTreeWalker walker = DeepTreeWalker (this);
319 while (UIElement *child = walker.Step ()) {
320 if (child == this || !types->IsSubclassOf (child->GetObjectType (), Type::CONTROL))
321 continue;
323 Control *control = (Control *)child;
324 control->enabled_parent = (enabled_local && enabled_parent);
325 control->SetValue (Control::IsEnabledProperty, Value (control->enabled_local));
326 walker.SkipBranch ();