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