- add listbox
[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_parent(parent ? parent->child() : 0)
7 {
8         m_vis = 0;
9         m_desktop = 0;
10         
11         if (m_parent)
12                 m_vis = wVisShow;
13                 
14         if (m_parent)
15         {
16                 m_parent->m_childs.push_back(this);
17                 m_parent->getStyle(m_style);
18         }
19 }
20
21 void eWidget::move(ePoint pos)
22 {
23         if (m_position == pos)
24                 return;
25         
26         m_position = pos;
27         
28                 /* we invalidate before and after the move to
29                    cause a correct redraw. The area which is
30                    included both before and after isn't redrawn
31                    twice because a invalidate doesn't immediately
32                    redraws the region. */
33         invalidate();
34         event(evtChangedPosition);
35         recalcClipRegionsWhenVisible(); 
36         invalidate();
37 }
38
39 void eWidget::resize(eSize size)
40 {
41                 /* same strategy as with move: we first check if
42                    the size changed at all, and if it did, we
43                    invalidate both the old and new area. 
44                    TODO: check if either the old or new area
45                    fits into the other completely, and invalidate
46                    only once. */
47         eSize old_size = m_size;
48         event(evtWillChangeSize, &size);
49         if (old_size == m_size)
50                 return;
51
52         invalidate();
53         event(evtChangedSize);
54         recalcClipRegionsWhenVisible(); 
55         invalidate();
56 }
57
58 void eWidget::invalidate(const gRegion &region)
59 {
60                 /* we determine the area to redraw, and re-position this
61                    area to the absolute position, and then call the
62                    desktop's invalidate() with that, which adds this
63                    area into the dirty region. */
64         gRegion res = m_visible_with_childs;
65         if (region.valid())
66                 res &= region;
67
68         if (res.empty())
69                 return;
70         
71         eWidget *root = this;
72         ePoint abspos = position();
73         while (root && !root->m_desktop)
74         {
75                 root = root->m_parent;
76                 assert(root);
77                 abspos += root->position();
78         }
79         
80         res.moveBy(abspos);
81 //      eDebug("region to invalidate:");
82 //      dumpRegion(res);
83         root->m_desktop->invalidate(res);
84 }
85
86 void eWidget::show()
87 {
88         if (m_vis & wVisShow)
89                 return;
90         
91         m_vis |=  wVisShow;
92
93                 /* TODO: optimize here to only recalc what's required. possibly merge with hide. */
94         eWidget *root = this;
95         ePoint abspos = position();
96         while (root && !root->m_desktop)
97         {
98                 root = root->m_parent;
99                 assert(root);
100                 abspos += root->position();
101         }
102
103         root->m_desktop->recalcClipRegions();
104
105         gRegion abs = m_visible_with_childs;
106         abs.moveBy(abspos);
107         root->m_desktop->invalidate(abs);
108 }
109
110 void eWidget::hide()
111 {
112                 /* TODO: when hiding an upper level widget, widgets get hidden but keep the */
113                 /* wVisShow flag (because when the widget is shown again, the widgets must */
114                 /* become visible again. */
115         if (!(m_vis & wVisShow))
116                 return;
117         m_vis &= ~wVisShow;
118         
119                 /* this is a workaround to the above problem. when we are in the delete phase, 
120                    don't hide childs. */
121         if (!(m_parent || m_desktop))
122                 return;
123
124                 /* TODO: optimize here to only recalc what's required. possibly merge with show. */
125         eWidget *root = this;
126         ePoint abspos = position();
127         while (root && !root->m_desktop)
128         {
129                 root = root->m_parent;
130                 abspos += root->position();
131         }
132         assert(root->m_desktop);
133
134         gRegion abs = m_visible_with_childs;
135         abs.moveBy(abspos);
136
137         root->m_desktop->recalcClipRegions();
138         root->m_desktop->invalidate(abs);
139 }
140
141 void eWidget::destruct()
142 {
143         if (m_parent)
144                 m_parent->m_childs.remove(this);
145         delete this;
146 }
147
148 eWidget::~eWidget()
149 {
150         hide();
151         
152         if (m_parent)
153                 m_parent->m_childs.remove(this);
154
155         m_parent = 0;
156
157                 /* destroy all childs */
158         ePtrList<eWidget>::iterator i(m_childs.begin());
159         while (i != m_childs.end())
160         {
161                 (*i)->m_parent = 0;
162                 delete *i;
163                 i = m_childs.erase(i);
164         }
165 }
166
167 void eWidget::doPaint(gPainter &painter, const gRegion &r)
168 {
169         if (m_visible_with_childs.empty())
170                 return;
171         
172         gRegion region = r;
173                         /* we were in parent's space, now we are in local space */
174         region.moveBy(-position());
175         
176         painter.moveOffset(position());
177                 /* walk all childs */
178         for (ePtrList<eWidget>::iterator i(m_childs.begin()); i != m_childs.end(); ++i)
179                 i->doPaint(painter, region);
180         
181                 /* check if there's anything for us to paint */
182         region &= m_visible_region;
183         
184         if (!region.empty())
185         {
186                 painter.resetClip(region);
187                 event(evtPaint, &region, &painter);
188         }
189         
190         painter.moveOffset(-position());
191 }
192
193 void eWidget::recalcClipRegionsWhenVisible()
194 {
195         eWidget *t = this;
196         do
197         {
198                 if (!(t->m_vis & wVisShow))
199                         break;
200                 if (t->m_desktop)
201                 {
202                         t->m_desktop->recalcClipRegions();
203                         break;
204                 }
205                 t = t->m_parent;
206                 assert(t);
207         } while(1);
208 }
209
210 int eWidget::event(int event, void *data, void *data2)
211 {
212         switch (event)
213         {
214         case evtPaint:
215         {
216                 gPainter &painter = *(gPainter*)data2;
217                 
218 //              eDebug("eWidget::evtPaint");
219 //              dumpRegion(*(gRegion*)data);
220                 ePtr<eWindowStyle> style;
221                 if (!getStyle(style))
222                         style->paintBackground(painter, ePoint(0, 0), size());
223                 break;
224         }
225         case evtKey:
226                 break;
227         case evtWillChangeSize:
228                 m_size = *static_cast<eSize*>(data);
229                 break;
230         case evtChangedSize:
231         {
232                 m_clip_region = gRegion(eRect(ePoint(0, 0),  m_size));
233                 break;
234         }
235         default:
236                 return -1;
237         }
238         return 0;
239 }
240