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