2009-11-13 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / control.cpp
blob4770f87cb75cceb83e6992fd3aec1e31aba05262
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"
24 #include "deployment.h"
26 Control::Control ()
28 SetObjectType (Type::CONTROL);
30 enabled_local = true;
31 enabled_parent = true;
32 template_root = NULL;
35 Control::~Control ()
39 void
40 Control::FindElementsInHostCoordinates (cairo_t *cr, Point p, List *uielement_list)
42 if (GetIsEnabled ())
43 FrameworkElement::FindElementsInHostCoordinates (cr, p, uielement_list);
46 void
47 Control::HitTest (cairo_t *cr, Point p, List *uielement_list)
49 if (GetIsEnabled ())
50 FrameworkElement::HitTest (cr, p, uielement_list);
53 bool
54 Control::InsideObject (cairo_t *cr, double x, double y)
56 /*
57 * Controls don't get hit themselves the rendered elements
58 * do and it bubbles up
60 return false;
63 void
64 Control::OnLoaded ()
66 Types *types = Deployment::GetCurrent ()->GetTypes ();
67 UIElement *e = GetVisualParent ();
68 while (e && !types->IsSubclassOf (e->GetObjectType (), Type::CONTROL))
69 e = e->GetVisualParent ();
70 if (e)
71 ((Control *)e)->UpdateEnabled ();
73 FrameworkElement::OnLoaded ();
76 void
77 Control::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
79 if (args->GetProperty ()->GetOwnerType() != Type::CONTROL) {
80 FrameworkElement::OnPropertyChanged (args, error);
81 return;
84 if (args->GetId () == Control::TemplateProperty) {
85 if (GetSubtreeObject ())
86 ElementRemoved ((UIElement *) GetSubtreeObject ());
88 else if (args->GetId () == Control::PaddingProperty
89 || args->GetId () == Control::BorderThicknessProperty) {
90 InvalidateMeasure ();
91 } else if (args->GetId () == Control::IsEnabledProperty) {
92 if (!args->GetNewValue ()->AsBool ()) {
93 Surface *surface = Deployment::GetCurrent ()->GetSurface ();
94 if (surface && surface->GetFocusedElement () == this) {
95 // Ensure this element loses focus, then try to focus the next suitable element
96 surface->FocusElement (NULL);
97 TabNavigationWalker::Focus (this, true);
99 ReleaseMouseCapture ();
101 args->ref (); // to counter the unref in Emit
102 Emit (IsEnabledChangedEvent, args);
103 } else if (args->GetId () == Control::HorizontalContentAlignmentProperty
104 || args->GetId () == Control::VerticalContentAlignmentProperty) {
105 InvalidateArrange ();
107 NotifyListenersOfPropertyChange (args, error);
110 void
111 Control::SetVisualParent (UIElement *visual_parent)
113 FrameworkElement::SetVisualParent (visual_parent);
114 if (!UIElement::IsSubtreeLoaded (this))
115 return;
117 this->enabled_parent = GetParentEnabledState (this);
118 SetValue (Control::IsEnabledProperty, Value (enabled_local));
121 bool
122 Control::GetParentEnabledState (UIElement *element)
124 do {
125 element = element->GetVisualParent ();
126 } while (element && !element->Is (Type::CONTROL));
128 return element ? ((Control *) element)->GetIsEnabled () : true;
131 bool
132 Control::SetValueWithErrorImpl (DependencyProperty *property, Value *value, MoonError *error)
134 if (property->GetId () == Control::IsEnabledProperty) {
135 this->enabled_local = value->AsBool ();
136 if ((enabled_local && enabled_parent) == GetIsEnabled ())
137 return true;
139 Value v (enabled_local && (enabled_parent));
141 // If we don't propagate the changes down the tree here, the EnabledChanged events
142 // from the subtree are raised in the wrong order
143 bool b = FrameworkElement::SetValueWithErrorImpl (property, &v, error);
144 if (b)
145 UpdateEnabled ();
146 return b;
148 return FrameworkElement::SetValueWithErrorImpl (property, value, error);
151 bool
152 Control::DoApplyTemplate ()
154 ControlTemplate *t = GetTemplate ();
155 if (!t)
156 return FrameworkElement::DoApplyTemplate ();
158 // If the template expands to an element which is *not* a UIElement
159 // we don't apply the template.
160 DependencyObject *root = t->GetVisualTree (this);
161 if (root && !root->Is (Type::UIELEMENT)) {
162 g_warning ("Control::DoApplyTemplate () Template root was not a UIElement");
163 root->unref ();
164 root = NULL;
167 if (!root)
168 return FrameworkElement::DoApplyTemplate ();
170 // No need to ref template_root here as ElementAdded refs it
171 // and it is cleared when ElementRemoved is called.
172 template_root = (UIElement *)root;
173 ElementAdded (template_root);
175 if (GetSurface()) {
176 bool post = false;
178 ((UIElement*)root)->WalkTreeForLoadedHandlers (&post, true, true);
180 if (post)
181 Deployment::GetCurrent()->PostLoaded ();
184 return true;
187 void
188 Control::ElementAdded (UIElement *item)
190 MoonError e;
191 item->SetParent (this, &e);
192 SetSubtreeObject (item);
193 FrameworkElement::ElementAdded (item);
196 void
197 Control::ElementRemoved (UIElement *item)
199 template_root = NULL;
200 MoonError e;
201 item->SetParent (NULL, &e);
202 FrameworkElement::ElementRemoved (item);
205 DependencyObject *
206 Control::GetTemplateChild (const char *name)
208 if (template_root)
209 return template_root->FindName (name);
211 return NULL;
214 bool
215 Control::Focus (bool recurse)
217 Surface *surface = GetSurface ();
218 if (!surface)
219 return false;
221 /* according to msdn, these three things must be true for an element to be focusable:
223 * 1. the element must be visible
224 * 2. the element must have IsTabStop = true
225 * 3. the element must be part of the plugin's visual tree, and must have had its Loaded event fired.
229 * If the current control is not focusable, we walk the visual tree and stop as soon
230 * as we find the first focusable child. That then becomes focused
232 Types *types = Deployment::GetCurrent ()->GetTypes ();
233 DeepTreeWalker walker (this);
234 while (UIElement *e = walker.Step ()) {
235 if (!types->IsSubclassOf (e->GetObjectType (), Type::CONTROL))
236 continue;
238 Control *c = (Control *)e;
239 if (!c->GetIsEnabled ()) {
240 if (!recurse)
241 return false;
243 walker.SkipBranch ();
244 continue;
247 // A control is focusable if it is attached to a visual tree whose root
248 // element has been loaded
249 bool loaded = false;
250 for (UIElement *check = this; !loaded && check != NULL; check = check->GetVisualParent ())
251 loaded |= check->IsLoaded ();
253 if (loaded && c->GetRenderVisible () && c->GetIsTabStop ())
254 return surface->FocusElement (c);
256 if (!recurse)
257 return false;
259 return false;
262 void
263 Control::UpdateEnabled ()
265 Types *types = Deployment::GetCurrent ()->GetTypes ();
266 DeepTreeWalker walker = DeepTreeWalker (this);
267 while (UIElement *child = walker.Step ()) {
268 if (child == this || !types->IsSubclassOf (child->GetObjectType (), Type::CONTROL))
269 continue;
271 Control *control = (Control *)child;
272 control->enabled_parent = (enabled_local && enabled_parent);
273 control->SetValue (Control::IsEnabledProperty, Value (control->enabled_local));
274 walker.SkipBranch ();