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