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