fix
[enigma2.git] / lib / gui / ewidgetdesktop.cpp
1 #include <lib/gui/ewidgetdesktop.h>
2 #include <lib/gui/ewidget.h>
3 #include <lib/base/ebase.h>
4 #include <lib/gdi/grc.h>
5
6 extern void dumpRegion(const gRegion &region);
7
8 void eWidgetDesktop::addRootWidget(eWidget *root, int top)
9 {
10         assert(!root->m_desktop);
11         
12                 /* buffered mode paints back-to-front, while immediate mode is front-to-back. */
13         if (m_comp_mode == cmBuffered)
14                 top = !top;
15         
16         if (top)
17                 m_root.push_back(root);
18         else
19                 m_root.push_front(root);
20         root->m_desktop = this;
21
22                 /* the creation will be postponed. */
23         root->m_comp_buffer = 0;
24 }
25
26 void eWidgetDesktop::removeRootWidget(eWidget *root)
27 {
28         if (m_comp_mode == cmBuffered)
29                 removeBufferForWidget(root);
30
31         m_root.remove(root);
32 }
33
34 int eWidgetDesktop::movedWidget(eWidget *root)
35 {
36         if ((m_comp_mode == cmBuffered) && (root->m_comp_buffer))
37         {
38                 root->m_comp_buffer->m_position = root->position();
39 //              redrawComposition(0);
40                 return 0;
41         }
42         
43         return -1;
44 }
45
46 void eWidgetDesktop::calcWidgetClipRegion(eWidget *widget, gRegion &parent_visible)
47 {
48                 /* start with our clip region, clipped with the parent's */
49         if (widget->m_vis & eWidget::wVisShow)
50         {
51                 widget->m_visible_region = widget->m_clip_region;
52                 widget->m_visible_region.moveBy(widget->position());
53                 widget->m_visible_region &= parent_visible; // in parent space!
54                         /* TODO: check transparency here! */
55
56                         /* remove everything this widget will contain from parent's visible list */
57                 parent_visible -= widget->m_visible_region; // will remove child regions too!
58
59                         /* now prepare for recursing to childs */
60                 widget->m_visible_region.moveBy(-widget->position());            // now in local space
61         } else
62                 widget->m_visible_region = gRegion();
63
64         widget->m_visible_with_childs = widget->m_visible_region;
65         
66         for (ePtrList<eWidget>::iterator i(widget->m_childs.begin()); i != widget->m_childs.end(); ++i)
67                 if (i->m_vis & eWidget::wVisShow)
68                         calcWidgetClipRegion(*i, widget->m_visible_region);
69                 else
70                         clearVisibility(*i);
71 }
72
73 void eWidgetDesktop::recalcClipRegions(eWidget *root)
74 {
75         if (m_comp_mode == cmImmediate)
76         {
77                 gRegion background_before = m_screen.m_background_region;
78                 
79                 m_screen.m_background_region = gRegion(eRect(ePoint(0, 0), m_screen.m_screen_size));
80         
81                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
82                 {
83                         if (!(i->m_vis & eWidget::wVisShow))
84                         {
85                                 clearVisibility(i);
86                                 continue;
87                         }
88                         
89                         gRegion visible_before = i->m_visible_with_childs;
90
91                         calcWidgetClipRegion(*i, m_screen.m_background_region);
92                         
93                         gRegion redraw = (i->m_visible_with_childs - visible_before) | (visible_before - i->m_visible_with_childs);
94
95                         redraw.moveBy(i->position());
96                         
97                         invalidate(redraw);
98                 }
99                 
100                 gRegion redraw = (background_before - m_screen.m_background_region) | (m_screen.m_background_region - background_before);
101                 invalidate(redraw);
102         } else if (m_comp_mode == cmBuffered)
103         {
104                 if (!root->m_vis & eWidget::wVisShow)
105                 {
106                         clearVisibility(root);
107                         removeBufferForWidget(root);
108                         return;
109                 }
110                 if ((!root->m_comp_buffer) || (root->size() != root->m_comp_buffer->m_screen_size))
111                         createBufferForWidget(root);
112
113                 eWidgetDesktopCompBuffer *comp = root->m_comp_buffer;
114
115                 gRegion visible_before = root->m_visible_with_childs;
116                  
117                 comp->m_background_region = gRegion(eRect(comp->m_position, comp->m_screen_size));
118                 
119                 gRegion visible_new = root->m_visible_with_childs - visible_before;
120                 gRegion visible_lost = visible_before - root->m_visible_with_childs;
121                 visible_new.moveBy(root->position());
122                 visible_lost.moveBy(root->position());
123                 
124                         /* this sucks, obviously. */
125                 invalidate(visible_new);
126                 invalidate(visible_lost);
127                 
128                 calcWidgetClipRegion(root, comp->m_background_region);
129         }
130 }
131
132 void eWidgetDesktop::invalidate(const gRegion &region)
133 {
134         if (region.empty())
135                 return;
136         
137         if (m_timer && !m_require_redraw)
138                 m_timer->start(0, 1); // start singleshot redraw timer
139         
140         m_require_redraw = 1;
141         
142         if (m_comp_mode == cmImmediate)
143                 m_screen.m_dirty_region |= region;
144         else
145                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
146                 {
147                         if (!(i->m_vis & eWidget::wVisShow))
148                                 continue;
149                         
150                         eWidgetDesktopCompBuffer *comp = i->m_comp_buffer;
151                         
152                         gRegion mregion = region;
153                         comp->m_dirty_region |= mregion;
154                 }
155 }
156
157 void eWidgetDesktop::setBackgroundColor(eWidgetDesktopCompBuffer *comp, gRGB col)
158 {
159         comp->m_background_color = col;
160         
161                 /* if there's something visible from the background, redraw it with the new color. */
162         if (comp->m_dc && comp->m_background_region.valid() && !comp->m_background_region.empty())
163         {
164                         /* todo: split out "setBackgroundColor / clear"... maybe? */
165                 gPainter painter(comp->m_dc);
166                 painter.resetClip(comp->m_background_region);
167                 painter.setBackgroundColor(comp->m_background_color);
168                 painter.clear();
169         }
170 }
171
172 void eWidgetDesktop::setBackgroundColor(gRGB col)
173 {
174         setBackgroundColor(&m_screen, col);
175
176         if (m_comp_mode == cmBuffered)
177                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
178                         setBackgroundColor(i->m_comp_buffer, col);
179 }
180
181 void eWidgetDesktop::setPalette(gPixmap &pm)
182 {
183 //      if (m_comp_mode == cmImmediate)
184         {
185                 ASSERT(m_screen.m_dc);
186                 gPainter painter(m_screen.m_dc);
187                 painter.setPalette(&pm);
188         }
189         
190         if (m_comp_mode == cmBuffered)
191         {
192                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
193                 {
194                         ASSERT(i->m_comp_buffer->m_dc);
195                         gPainter painter(i->m_comp_buffer->m_dc);
196                         painter.setPalette(&pm);
197                 }
198         }
199 }
200
201 void eWidgetDesktop::paintBackground(eWidgetDesktopCompBuffer *comp)
202 {
203         comp->m_dirty_region &= comp->m_background_region;
204         
205         gPainter painter(comp->m_dc);
206         
207         painter.resetClip(comp->m_dirty_region);
208         painter.setBackgroundColor(comp->m_background_color);
209         painter.clear();
210         painter.flush();
211         
212         comp->m_dirty_region = gRegion();
213 }
214
215 void eWidgetDesktop::paint()
216 {
217         m_require_redraw = 0;
218         
219                 /* walk all root windows. */
220         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
221         {
222                 eWidgetDesktopCompBuffer *comp = (m_comp_mode == cmImmediate) ? &m_screen : i->m_comp_buffer;
223                 
224                 if (!(i->m_vis & eWidget::wVisShow))
225                         continue;
226                 
227                 {
228                         gPainter painter(comp->m_dc);
229                         painter.moveOffset(-comp->m_position);
230                         i->doPaint(painter, comp->m_dirty_region);
231                         painter.resetOffset();
232                 }
233
234                 if (m_comp_mode != cmImmediate)
235                         paintBackground(comp);
236         }
237         
238         if (m_comp_mode == cmImmediate)
239                 paintBackground(&m_screen);
240         
241         if (m_comp_mode == cmBuffered)
242         {
243 //              redrawComposition(0);
244         }
245 }
246
247 void eWidgetDesktop::setDC(gDC *dc)
248 {
249         m_screen.m_dc = dc;
250         if (m_comp_mode == cmBuffered)
251                 redrawComposition(1);
252 }
253
254 void eWidgetDesktop::setRedrawTask(eMainloop &ml)
255 {
256         if (m_mainloop)
257         {
258                 delete m_timer;
259                 m_timer = 0;
260                 m_mainloop = 0;
261         }
262         m_mainloop = &ml;
263         m_timer = new eTimer(m_mainloop);
264         CONNECT(m_timer->timeout, eWidgetDesktop::paint);
265         
266         if (m_require_redraw)
267                 m_timer->start(0, 1);
268 }
269
270 void eWidgetDesktop::makeCompatiblePixmap(gPixmap &pm)
271 {
272         if (m_comp_mode != cmImmediate)
273                 return;
274         
275 //      eDebug("widgetDesktop: make compatible pixmap of %p", &pm);
276         if (!m_screen.m_dc)
277         {
278                 eWarning("eWidgetDesktop: no DC to make pixmap compatible with!");
279                 return;
280         }
281
282         ePtr<gDC> pixmap_dc = new gDC(&pm);
283         gPainter pixmap_painter(pixmap_dc);
284         
285         ePtr<gPixmap> target_pixmap;
286         m_screen.m_dc->getPixmap(target_pixmap);
287         
288         assert(target_pixmap);
289         
290         pixmap_painter.mergePalette(target_pixmap);
291 }
292
293 void eWidgetDesktop::setCompositionMode(int mode)
294 {
295         m_comp_mode = mode;
296         
297         if (mode == cmBuffered)
298                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
299                         createBufferForWidget(*i);
300         else
301                 for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
302                         removeBufferForWidget(*i);
303 }
304
305 eWidgetDesktop::eWidgetDesktop(eSize size): m_mainloop(0), m_timer(0)
306 {
307         m_screen.m_dirty_region = gRegion(eRect(ePoint(0, 0), size));
308         m_screen.m_screen_size = size;
309         m_require_redraw = 0;
310
311         CONNECT(gRC::getInstance()->notify, eWidgetDesktop::notify);
312         setCompositionMode(cmImmediate);
313 }
314
315 eWidgetDesktop::~eWidgetDesktop()
316 {
317                 /* destroy all buffers */
318         setCompositionMode(-1);
319 }
320
321 void eWidgetDesktop::createBufferForWidget(eWidget *widget)
322 {
323         removeBufferForWidget(widget);
324         
325         eWidgetDesktopCompBuffer *comp = widget->m_comp_buffer = new eWidgetDesktopCompBuffer;
326         
327         eDebug("create buffer for widget, %d x %d\n", widget->size().width(), widget->size().height());
328         
329         eRect bbox = eRect(widget->position(), widget->size());
330         comp->m_position = bbox.topLeft();
331         comp->m_dirty_region = gRegion(eRect(ePoint(0, 0), bbox.size()));
332         comp->m_screen_size = bbox.size();
333                 /* TODO: configurable bit depth. */
334         
335                 /* clone palette. FIXME. */
336         ePtr<gPixmap> pm = new gPixmap(comp->m_screen_size, 32, 1), pm_screen;
337         pm->surface->clut.data = new gRGB[256];
338         pm->surface->clut.colors = 256;
339         pm->surface->clut.start = 0;
340         
341         
342         m_screen.m_dc->getPixmap(pm_screen);
343         
344         memcpy(pm->surface->clut.data, pm_screen->surface->clut.data, 256 * sizeof(gRGB));
345
346         comp->m_dc = new gDC(pm);
347 }
348
349 void eWidgetDesktop::removeBufferForWidget(eWidget *widget)
350 {
351         if (widget->m_comp_buffer)
352         {
353                 delete widget->m_comp_buffer;
354                 widget->m_comp_buffer = 0;
355         }
356 }
357
358 void eWidgetDesktop::redrawComposition(int notified)
359 {
360         if (m_comp_mode != cmBuffered)
361                 return;
362         
363         assert(m_screen.m_dc);
364         
365         gPainter p(m_screen.m_dc);
366         p.resetClip(eRect(ePoint(0, 0), m_screen.m_screen_size));
367         p.setBackgroundColor(m_screen.m_background_color);
368         p.clear();
369         
370         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
371         {
372                 if (!i->isVisible())
373                         continue;
374                 ePtr<gPixmap> pm;
375                 ASSERT(i->m_comp_buffer);
376                 i->m_comp_buffer->m_dc->getPixmap(pm);
377                 p.blit(pm, i->m_comp_buffer->m_position, eRect(), gPixmap::blitAlphaBlend);
378         }
379
380                 // flip activates on next vsync.        
381         p.flip();
382         p.waitVSync();
383
384         if (notified)
385                 p.notify();
386
387         for (ePtrList<eWidget>::iterator i(m_root.begin()); i != m_root.end(); ++i)
388                 if (i->m_animation.m_active)
389                         i->m_animation.tick(1);
390 }
391
392 void eWidgetDesktop::notify()
393 {
394         redrawComposition(1);
395 }
396
397 void eWidgetDesktop::clearVisibility(eWidget *widget)
398 {
399         widget->m_visible_with_childs = gRegion();
400         for (ePtrList<eWidget>::iterator i(widget->m_childs.begin()); i != widget->m_childs.end(); ++i)
401                 clearVisibility(*i);
402 }