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