2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / control.cpp
blobfb7c87e857f79f5ecabf26337ccd854cbfce7454
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 (!args->GetNewValue() || args->GetNewValue()->GetIsNull())
102 ClearTemplate ();
103 else if (IsLoaded())
104 ApplyTemplate ();
105 InvalidateMeasure ();
107 else if (args->GetId () == Control::PaddingProperty
108 || args->GetId () == Control::BorderThicknessProperty) {
109 InvalidateMeasure ();
110 } else if (args->GetId () == Control::IsEnabledProperty) {
111 if (!args->GetNewValue ()->AsBool ()) {
112 Surface *surface = Deployment::GetCurrent ()->GetSurface ();
113 if (surface && surface->GetFocusedElement () == this) {
114 // Ensure this element loses focus, then try to focus the next suitable element
115 surface->FocusElement (NULL);
116 TabNavigationWalker::Focus (this, true);
118 ReleaseMouseCapture ();
120 args->ref (); // to counter the unref in Emit
121 Emit (IsEnabledChangedEvent, args);
122 } else if (args->GetId () == Control::HorizontalContentAlignmentProperty
123 || args->GetId () == Control::VerticalContentAlignmentProperty) {
124 InvalidateArrange ();
126 NotifyListenersOfPropertyChange (args, error);
129 void
130 Control::SetVisualParent (UIElement *visual_parent)
132 FrameworkElement::SetVisualParent (visual_parent);
133 if (!UIElement::IsSubtreeLoaded (this))
134 return;
136 Types *types = Deployment::GetCurrent ()->GetTypes ();
137 if (!visual_parent) {
138 enabled_parent = true;
139 } else {
140 UIElement *parent = GetVisualParent ();
141 while (parent) {
142 if (!types->IsSubclassOf (parent->GetObjectType (), Type::CONTROL)) {
143 parent = parent->GetVisualParent ();
145 else {
146 this->enabled_parent = ((Control *)parent)->GetIsEnabled ();
147 break;
151 SetValue (Control::IsEnabledProperty, Value (enabled_local));
154 bool
155 Control::SetValueWithErrorImpl (DependencyProperty *property, Value *value, MoonError *error)
157 if (property->GetId () == Control::IsEnabledProperty) {
158 this->enabled_local = value->AsBool ();
159 if ((enabled_local && enabled_parent) == GetIsEnabled ())
160 return true;
162 Value v (enabled_local && (enabled_parent));
164 // If we don't propagate the changes down the tree here, the EnabledChanged events
165 // from the subtree are raised in the wrong order
166 bool b = FrameworkElement::SetValueWithErrorImpl (property, &v, error);
167 if (b)
168 UpdateEnabled ();
169 return b;
171 return FrameworkElement::SetValueWithErrorImpl (property, value, error);
174 bool
175 Control::ApplyTemplate ()
177 return ApplyTemplate (GetTemplate ()) == TemplateStatusApplied;
180 TemplateStatus
181 Control::ApplyTemplate (FrameworkTemplate *t)
183 // If the template is null, we won't apply it
184 if (!t)
185 return TemplateStatusNotApplied;
187 if (applied_template == t)
188 return TemplateStatusAlreadyApplied;
190 ClearTemplate ();
192 applied_template = t;
193 applied_template->ref();
195 // If the template expands to an element which is *not* a UIElement
196 // we don't apply the template.
197 DependencyObject *root = applied_template->GetVisualTree (this);
198 if (root && !root->Is (Type::UIELEMENT)) {
199 g_warning ("Control::ApplyTemplate (FrameworkTemplate*) Template root was not a UIElement");
200 root->unref ();
201 return TemplateStatusNotApplied;
203 return ApplyTemplateRoot ((UIElement *) root);
206 TemplateStatus
207 Control::ApplyTemplateRoot (UIElement *root)
209 // If the template root is null, we treat the template as
210 // not being applied
211 if (!root)
212 return TemplateStatusNotApplied;
213 if (root == template_root)
214 return TemplateStatusAlreadyApplied;
216 ElementAdded (root);
218 MoonError e;
219 root->SetParent (this, &e);
220 OnApplyTemplate ();
222 return TemplateStatusApplied;
225 void
226 Control::ClearTemplate ()
228 if (applied_template) {
229 applied_template->unref();
230 applied_template = NULL;
233 ElementRemoved (template_root);
234 template_root = NULL;
237 void
238 Control::OnApplyTemplate ()
240 Emit (TemplateAppliedEvent);
243 void
244 Control::ElementAdded (UIElement *item)
246 if (item == template_root)
247 return;
249 ElementRemoved (template_root);
251 template_root = item;
253 if (template_root) {
254 template_root->ref ();
255 FrameworkElement::ElementAdded (template_root);
258 SetSubtreeObject (template_root);
261 void
262 Control::ElementRemoved (UIElement *item)
264 if (template_root && item == template_root) {
265 template_root->unref ();
266 template_root = NULL;
267 SetSubtreeObject (NULL);
270 if (item)
271 FrameworkElement::ElementRemoved (item);
274 DependencyObject *
275 Control::GetTemplateChild (const char *name)
277 if (template_root)
278 return template_root->FindName (name);
280 return NULL;
283 bool
284 Control::Focus (bool recurse)
286 Surface *surface = GetSurface ();
287 if (!surface)
288 return false;
290 /* according to msdn, these three things must be true for an element to be focusable:
292 * 1. the element must be visible
293 * 2. the element must have IsTabStop = true
294 * 3. the element must be part of the plugin's visual tree, and must have had its Loaded event fired.
298 * If the current control is not focusable, we walk the visual tree and stop as soon
299 * as we find the first focusable child. That then becomes focused
301 Types *types = Deployment::GetCurrent ()->GetTypes ();
302 DeepTreeWalker walker (this);
303 while (UIElement *e = walker.Step ()) {
304 if (!types->IsSubclassOf (e->GetObjectType (), Type::CONTROL))
305 continue;
307 Control *c = (Control *)e;
308 if (!c->GetIsEnabled ()) {
309 if (!recurse)
310 return false;
312 walker.SkipBranch ();
313 continue;
316 // A control is focusable if it is attached to a visual tree whose root
317 // element has been loaded
318 bool loaded = false;
319 for (UIElement *check = this; !loaded && check != NULL; check = check->GetVisualParent ())
320 loaded |= check->IsLoaded ();
322 if (loaded && c->GetRenderVisible () && c->GetIsTabStop ())
323 return surface->FocusElement (c);
325 if (!recurse)
326 return false;
328 return false;
331 void
332 Control::UpdateEnabled ()
334 Types *types = Deployment::GetCurrent ()->GetTypes ();
335 DeepTreeWalker walker = DeepTreeWalker (this);
336 while (UIElement *child = walker.Step ()) {
337 if (child == this || !types->IsSubclassOf (child->GetObjectType (), Type::CONTROL))
338 continue;
340 Control *control = (Control *)child;
341 control->enabled_parent = (enabled_local && enabled_parent);
342 control->SetValue (Control::IsEnabledProperty, Value (control->enabled_local));
343 walker.SkipBranch ();