Merge branch 'master' of git.opendreambox.org:/git/enigma2
[enigma2.git] / lib / gui / ewidget.cpp
1 #include <lib/gui/ewidget.h>
2 #include <lib/gui/ewidgetdesktop.h>
3
4 extern void dumpRegion(const gRegion &region);
5
6 eWidget::eWidget(eWidget *parent): m_animation(this), m_parent(parent ? parent->child() : 0)
7 {
8         m_vis = 0;
9         m_layer = 0;
10         m_desktop = 0;
11         m_have_background_color = 0;
12         m_z_position = 0;
13         m_client_offset = eSize(0, 0);
14         if (m_parent)
15                 m_vis = wVisShow;
16         if (m_parent)
17         {
18                 insertIntoParent();
19                 m_parent->getStyle(m_style);
20         }
21
22         m_current_focus = 0;
23         m_focus_owner = 0;
24         m_notify_child_on_position_change = 1;
25 }
26
27 void eWidget::move(ePoint pos)
28 {
29         pos = pos + m_client_offset;
30         if (m_position == pos)
31                 return;
32
33                         /* ?? what about native move support? */
34         invalidate();
35
36         m_position = pos;
37         event(evtChangedPosition);
38         if (m_notify_child_on_position_change)
39                 for (ePtrList<eWidget>::iterator i(m_childs.begin()); i != m_childs.end(); ++i)
40                         i->event(evtParentChangedPosition);
41                 recalcClipRegionsWhenVisible();
42                 /* try native move if supported. */
43         if ((m_vis & wVisShow) && ((!m_desktop) || m_desktop->movedWidget(this)))
44                 invalidate();
45 }
46
47 void eWidget::resize(eSize size)
48 {
49                 /* same strategy as with move: we first check if
50                    the size changed at all, and if it did, we
51                    invalidate both the old and new area. 
52                    TODO: check if either the old or new area
53                    fits into the other completely, and invalidate
54                    only once. */
55         eSize old_size = m_size;
56         eSize old_offset = m_client_offset;
57         m_client_size = size;
58         m_client_offset = eSize(0, 0);
59         event(evtWillChangeSize, &size, &m_client_offset);
60         if (old_size == m_size)
61                 return;
62         move(position() - old_offset);
63         invalidate();
64         event(evtChangedSize);
65
66         if (m_notify_child_on_position_change)
67                 for (ePtrList<eWidget>::iterator i(m_childs.begin()); i != m_childs.end(); ++i)
68                         i->event(evtParentChangedPosition); /* position/size is the same here */
69
70         recalcClipRegionsWhenVisible(); invalidate();
71 }
72
73 void eWidget::invalidate(const gRegion &region)
74 {
75                 /* we determine the area to redraw, and re-position this
76                    area to the absolute position, and then call the
77                    desktop's invalidate() with that, which adds this
78                    area into the dirty region. */
79         gRegion res = m_visible_with_childs;
80         if (region.valid())
81                 res &= region;
82
83         if (res.empty())
84                 return;
85         eWidget *root = this;
86         ePoint abspos = position();
87         int target_layer = m_layer;
88
89         while (root && !root->m_desktop)
90         {
91                 root = root->m_parent;
92                 ASSERT(root);
93                 if (root->m_layer != -1)
94                         target_layer = root->m_layer;
95                 abspos += root->position();
96         }
97         res.moveBy(abspos);
98 //      eDebug("region to invalidate:");
99 //      dumpRegion(res);
100         root->m_desktop->invalidate(res, this, target_layer);
101 }
102
103 void eWidget::show()
104 {
105         if (m_vis & wVisShow)
106                 return;
107
108         m_vis |= wVisShow;
109 //      eDebug("show widget %p", this);
110         notifyShowHide();
111
112                 /* TODO: optimize here to only recalc what's required. possibly merge with hide. */
113         eWidget *root = this;
114         ePoint abspos = position();
115         int target_layer = m_layer;
116
117         while (root && !root->m_desktop)
118         {
119                 root = root->m_parent;
120                 if (!root)
121                 {
122                                 /* oops: our root widget does not have a desktop associated. 
123                                         probably somebody already erased the root, but tries some
124                                         operations on a child window. 
125                                                                         ignore them for now. */
126                         /* ASSERT(root); */
127                         return;
128                 }
129                 if (root->m_layer != -1)
130                         target_layer = root->m_layer;
131                 abspos += root->position();
132         }
133
134         root->m_desktop->recalcClipRegions(root);
135
136         gRegion abs = m_visible_with_childs;
137         abs.moveBy(abspos);
138         root->m_desktop->invalidate(abs, this, target_layer);
139 }
140
141 void eWidget::hide()
142 {
143                 /* TODO: when hiding an upper level widget, widgets get hidden but keep the */
144                 /* wVisShow flag (because when the widget is shown again, the widgets must */
145                 /* become visible again. */
146         if (!(m_vis & wVisShow))
147                 return;
148         m_vis &= ~wVisShow;
149
150                 /* this is a workaround to the above problem. when we are in the delete phase, 
151                    don't hide childs. */
152         if (!(m_parent || m_desktop))
153                 return;
154         notifyShowHide();
155
156                 /* TODO: optimize here to only recalc what's required. possibly merge with show. */
157         eWidget *root = this;
158         ePoint abspos = position();
159         while (root && !root->m_desktop)
160         {
161                 root = root->m_parent;
162                 if (!root)
163                         return;
164                 abspos += root->position();
165         }
166         ASSERT(root->m_desktop);
167
168         gRegion abs = m_visible_with_childs;
169         abs.moveBy(abspos);
170
171         root->m_desktop->recalcClipRegions(root);
172         root->m_desktop->invalidate(abs);
173 }
174
175 void eWidget::destruct()
176 {
177         if (m_parent)
178                 m_parent->m_childs.remove(this);
179         delete this;
180 }
181
182 void eWidget::setBackgroundColor(const gRGB &col)
183 {
184         m_background_color = col;
185         m_have_background_color = 1;
186 }
187
188 void eWidget::clearBackgroundColor()
189 {
190         m_have_background_color = 0;
191 }
192
193 void eWidget::setZPosition(int z)
194 {
195         m_z_position = z;
196         if (!m_parent)
197                 return;
198         m_parent->m_childs.remove(this);
199         insertIntoParent(); /* now at the new Z position */
200 }
201
202 void eWidget::setTransparent(int transp)
203 {
204         if (isTransparent() != transp)
205         {
206                 if (transp)
207                         m_vis |= wVisTransparent;
208                 else
209                         m_vis &=~wVisTransparent;
210                 recalcClipRegionsWhenVisible();
211         }
212 }
213
214 ePoint eWidget::getAbsolutePosition()
215 {
216         eWidget *root = this;
217         ePoint abspos = position();
218
219         while (root && !root->m_desktop)
220         {
221                 root = root->m_parent;
222                 ASSERT(root);
223                 abspos += root->position();
224         }
225
226         return abspos;
227 }
228
229 void eWidget::mayKillFocus()
230 {
231         setFocus(0);
232                 /* when we have the focus, remove it first. */
233         if (m_focus_owner)
234                 m_focus_owner->setFocus(0);
235 }
236
237 eWidget::~eWidget()
238 {
239         hide();
240         if (m_parent)
241                 m_parent->m_childs.remove(this);
242
243         m_parent = 0;
244
245                 /* tell all childs that the parent is not anymore existing */
246         ePtrList<eWidget>::iterator i(m_childs.begin());
247         while (i != m_childs.end())
248         {
249                 (*i)->parentRemoved();
250                 i = m_childs.erase(i);
251         }
252 }
253
254 void eWidget::insertIntoParent()
255 {
256         ePtrList<eWidget>::iterator i = m_parent->m_childs.begin();
257         for(;;)
258         {
259                 if ((i == m_parent->m_childs.end()) || (i->m_z_position > m_z_position))
260                 {
261                         m_parent->m_childs.insert(i, this);
262                         return;
263                 }
264                 ++i;
265         }
266 }
267
268 void eWidget::doPaint(gPainter &painter, const gRegion &r, int layer)
269 {
270         if (m_visible_with_childs.empty())
271                 return;
272         gRegion region = r, childs = r;
273                         /* we were in parent's space, now we are in local space */
274         region.moveBy(-position());
275         painter.moveOffset(position());
276                 /* check if there's anything for us to paint */
277         if (layer == m_layer)
278         {
279                 region &= m_visible_region;
280                 if (!region.empty())
281                 {
282                         painter.resetClip(region);
283                         event(evtPaint, &region, &painter);
284                 }
285         }
286
287         childs.moveBy(-position());
288                 /* walk all childs */
289         for (ePtrList<eWidget>::iterator i(m_childs.begin()); i != m_childs.end(); ++i)
290                 i->doPaint(painter, childs, layer);
291         painter.moveOffset(-position());
292 }
293
294 void eWidget::recalcClipRegionsWhenVisible()
295 {
296         eWidget *t = this;
297         do
298         {
299                 if (!(t->m_vis & wVisShow))
300                         break;
301                 if (t->m_desktop)
302                 {
303                         t->m_desktop->recalcClipRegions(t);
304                         break;
305                 }
306                 t = t->m_parent;
307                 ASSERT(t);
308         } while(1);
309 }
310
311 void eWidget::parentRemoved()
312 {
313         m_parent = 0;
314 }
315
316 int eWidget::event(int event, void *data, void *data2)
317 {
318         switch (event)
319         {
320         case evtPaint:
321         {
322                 gPainter &painter = *(gPainter*)data2;
323         //              eDebug("eWidget::evtPaint");
324 //              dumpRegion(*(gRegion*)data);
325                 if (!isTransparent())
326                 {
327                         if (!m_have_background_color)
328                         {
329                                 ePtr<eWindowStyle> style;
330                                 if (!getStyle(style))
331                                         style->paintBackground(painter, ePoint(0, 0), size());
332                         } else
333                         {
334                                 painter.setBackgroundColor(m_background_color);
335                                 painter.clear();
336                         }
337                 } else
338                 {
339                         eWidget *w = this;
340                                         while (w && !w->m_have_background_color)
341                                 w = w->m_parent;
342
343                         if (w)
344                                 painter.setBackgroundColor(w->m_background_color);
345                 }
346                 break;
347         }
348         case evtKey:
349                 break;
350         case evtWillChangeSize:
351                 m_size = *static_cast<eSize*>(data);
352                 break;
353         case evtChangedSize:
354                 m_clip_region = gRegion(eRect(ePoint(0, 0),  m_size));
355                 break;
356         case evtParentChangedPosition:
357                 for (ePtrList<eWidget>::iterator i(m_childs.begin()); i != m_childs.end(); ++i)
358                         i->event(evtParentChangedPosition);
359                 break;
360         case evtFocusGot:
361                 m_focus_owner = (eWidget*)data;
362                 break;
363         case evtFocusLost:
364                 m_focus_owner = 0;
365                 break;
366         default:
367                 return -1;
368         }
369         return 0;
370 }
371
372 void eWidget::setFocus(eWidget *focus)
373 {
374         if (m_current_focus)
375                 m_current_focus->event(evtFocusLost, this);
376         m_current_focus = focus;
377
378         if (m_current_focus)
379                 m_current_focus->event(evtFocusGot, this);
380 }
381
382 void eWidget::notifyShowHide()
383 {
384         event(evtParentVisibilityChanged);
385         for (ePtrList<eWidget>::iterator i(m_childs.begin()); i != m_childs.end(); ++i)
386                 i->notifyShowHide();
387 }