]> git.vomp.tv Git - vompclient.git/blob - viewman.cc
Fix for possible crash when removing bar
[vompclient.git] / viewman.cc
1 /*
2     Copyright 2004-2005 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "viewman.h"
22 #include "command.h"
23
24 ViewMan* ViewMan::instance = NULL;
25
26 ViewMan::ViewMan()
27 {
28   if (instance) return;
29   instance = this;
30   initted = 0;
31   numViews = 0;
32 }
33
34 ViewMan::~ViewMan()
35 {
36   instance = NULL;
37 }
38
39 ViewMan* ViewMan::getInstance()
40 {
41   return instance;
42 }
43
44 int ViewMan::init()
45 {
46   if (initted) return 0;
47   initted = 1;
48   return 1;
49 }
50
51 int ViewMan::shutdown()
52 {
53   if (!initted) return 0;
54
55   // FIXME don't think this can work properly, removeAll leaves the wallpaper there!
56   removeAll();
57
58   initted = 0;
59   return 1;
60 }
61
62 int ViewMan::add(View* v)
63 {
64   if (!initted) return 0;
65   if (numViews == 16) return 0;
66
67   views[numViews++] = v;
68
69   return 1;
70 }
71
72 // ---------------------------------------------------- REMOVE CODE
73
74 int ViewMan::removeView(View* toDelete)
75 {
76   if (!initted) return 0;
77   if (numViews == 0) return 0;
78
79 //  Log::getInstance()->log("ViewMan", Log::DEBUG, "entering remove, numViews=%i", numViews);
80
81   int i;
82
83   if (toDelete == NULL)
84   {
85     toDelete = views[numViews-1];
86     i = numViews - 1;
87   }
88   else
89   {
90     // to be deleted view is more likely to be at the top
91     for (i = numViews-1; i >= 0; i--)
92     {
93 //      Log::getInstance()->log("ViewMan", Log::DEBUG, "todel: %p, i=%i, views[i]=%p", toDelete, i, views[i]);
94       if (views[i] == toDelete) break;
95     }
96
97     if (i == -1)
98     {
99       // not a View we have!
100       return 0;
101     }
102   }
103
104 //  Log::getInstance()->log("ViewMan", Log::DEBUG, "Starting deleteView");
105   deleteView(i);
106 //  Log::getInstance()->log("ViewMan", Log::DEBUG, "Done deleteView");
107
108   // Shift the views on top down one
109   --numViews;
110   for(int j = i; j < numViews; j++) views[j] = views[j+1];
111
112   // Delete the view
113   delete toDelete;
114
115   // If there is only the wallpaper left signal command
116   if (numViews == 1)
117   {
118     Message* m = new Message();
119     m->to = Command::getInstance();
120     m->message = Message::LAST_VIEW_CLOSE;
121     Command::getInstance()->postMessageNoLock(m);
122   }
123
124   return 1;
125 }
126
127 /////////////////////////////////////////////////////////////////////////////
128 // NEW STUFF
129 /////////////////////////////////////////////////////////////////////////////
130
131 void ViewMan::deleteView(int z)
132 {
133 //  Log::getInstance()->log("ViewMan", Log::DEBUG, "Delete view %i of %i", z, numViews);
134   RegionList rl;
135   boxSplit(views[z]->area, z + 1, numViews, 1, rl);
136   while(!rl.empty())
137   {
138     repaintRevealed(z, rl.front());
139     rl.pop_front();
140   }
141 }
142
143 void ViewMan::updateView(View* toUpdate, Region* regionToUpdate)
144 {
145 //  Log::getInstance()->log("ViewMan", Log::DEBUG, "UpdateView called");
146   // Get the z index of the view
147
148   int z;
149   for (z = 0; z < numViews; z++)
150   {
151     if (views[z] == toUpdate) break;
152   }
153
154   if (z == numViews)
155   {
156     // not a View we have!
157     return;
158   }
159
160   // get the region for the whole view, could be less than that
161   // for smaller updates
162
163   Region r = views[z]->area;
164
165   if (regionToUpdate)
166   {
167     // Can be null if the whole view should be updated
168     // If this is given the numbers are relative to the size of the view, not the screen
169
170     r.x += regionToUpdate->x;
171     r.y += regionToUpdate->y;
172     r.w = regionToUpdate->w;
173     r.h = regionToUpdate->h;
174   }
175
176   RegionList rl;
177
178   Region r2;
179   boxSplit(r, z+1, numViews, 1, rl);
180   while(!rl.empty())
181   {
182     r2 = rl.front();
183     r2.z = z;
184     views[z]->blt(r2);
185     rl.pop_front();
186   }
187 }
188
189 void ViewMan::repaintRevealed(int x, Region r)
190 {
191   RegionList rl;
192   boxSplit(r, x - 1, -1, -1, rl);
193
194   Region r2;
195   while(!rl.empty())
196   {
197     r2 = rl.front();
198     views[r2.z]->blt(r2);
199     rl.pop_front();
200   }
201 }
202
203 void ViewMan::boxSplit(Region r, int start, int end, int direction, RegionList& rl)
204 {
205 //  printf("Y=  S=%i E=%i D=%i: Boxsplit: %i %i %i %i\n", start, end, direction, r.x, r.y, r.w, r.h);
206
207   for(int z = start; z != end; z += direction)
208   {
209     if (r.overlappedBy(views[z]->area))
210     {
211 //      printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);
212
213       int top = r.y;
214       int btm = r.y2();
215
216       if (views[z]->area.y > r.y)
217       {
218 //        printf("Z=%i S=%i E=%i D=%i: Case 1 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, views[z]->area.x, views[z]->area.y, views[z]->area.w, views[z]->area.h);
219         top = views[z]->area.y;
220         Region newR;
221         newR.x = r.x;
222         newR.y = r.y;
223         newR.w = r.w;
224         newR.h = views[z]->area.y - r.y;
225         boxSplit(newR, z + direction, end, direction, rl);
226
227         if (direction == -1)
228         {
229           Region newR2;
230           newR2.x = r.x;
231           newR2.y = views[z]->area.y;
232           newR2.w = r.w;
233           newR2.h = r.h - newR.h;
234           boxSplit(newR2, z, end, -1, rl);
235           return;
236         }
237       }
238
239       if (views[z]->area.y2() < r.y2())
240       {
241 //        printf("Z=%i S=%i E=%i D=%i: Case 2 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, views[z]->area.x, views[z]->area.y, views[z]->area.w, views[z]->area.h);
242         btm = views[z]->area.y2();
243         Region newR;
244         newR.x = r.x;
245         newR.y = views[z]->area.y2() + 1;
246         newR.w = r.w;
247         newR.h = r.y2() - newR.y + 1;
248         boxSplit(newR, z + direction, end, direction, rl);
249
250         if (direction == -1)
251         {
252           Region newR2;
253           newR2.x = r.x;
254           newR2.y = r.y;
255           newR2.w = r.w;
256           newR2.h = r.h - newR.h;
257           boxSplit(newR2, z, end, -1, rl);
258           return;
259         }
260       }
261
262       if (views[z]->area.x > r.x)
263       {
264 //        printf("Z=%i S=%i E=%i D=%i: Case 3 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, views[z]->area.x, views[z]->area.y, views[z]->area.w, views[z]->area.h);
265         Region newR;
266         newR.x = r.x;
267         newR.y = top;
268         newR.w = views[z]->area.x - r.x;
269         newR.h = btm - top + 1;
270         boxSplit(newR, z + direction, end, direction, rl);
271
272         if (direction == -1)
273         {
274           Region newR2;
275           newR2.x = r.x + newR.w;
276           newR2.y = r.y;
277           newR2.w = r.w - newR.w;
278           newR2.h = r.h;
279           boxSplit(newR2, z, end, -1, rl);
280           return;
281         }
282       }
283
284       if (views[z]->area.x2() < r.x2())
285       {
286 //        printf("Z=%i S=%i E=%i D=%i: Case 4 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, views[z]->area.x, views[z]->area.y, views[z]->area.w, views[z]->area.h);
287         Region newR;
288         newR.x = views[z]->area.x2() + 1;
289         newR.y = top;
290         newR.w = r.x2() - newR.x + 1;
291         newR.h = btm - top + 1;
292         boxSplit(newR, z + direction, end, direction, rl);
293
294         if (direction == -1)
295         {
296           Region newR2;
297           newR2.x = r.x;
298           newR2.y = r.y;
299           newR2.w = r.w - newR.w;
300           newR2.h = r.h;
301           boxSplit(newR2, z, end, -1, rl);
302           return;
303         }
304       }
305
306       if (direction == -1)
307       {
308         // we are going down the stack
309         // r is underlapped by views[z]
310         // but we have not split
311         // Therefore this region under test is
312         // completely covering views[z]
313
314         // don't go any further down, generate a region and quit
315
316 //        printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);
317         r.z = z;
318         rl.push_front(r);
319       }
320
321 //      printf("Returning from Z=%i\n", z);
322       return;
323     }
324     else
325     {
326 //      printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);
327     }
328   }
329
330   // if direction = 1 then we have come to a region that is
331   // entirely clear of higher views and needs to be redrawn
332
333   // if direction = -1 then we have come to a region that is on
334   // the very bottom with nothing below it to repaint.
335   // do nothing. stale window data will be left on screen?
336
337   if (direction == 1)
338   {
339     rl.push_front(r);
340   }
341 }
342
343 /////////////////////////////////////////////////////////////////////////////
344 // END NEW STUFF
345 /////////////////////////////////////////////////////////////////////////////
346
347 // ---------------------------------------------------- END OF REMOVE CODE
348
349
350 void ViewMan::removeAll()
351 {
352   // 1.. Don't delete wallpaper. No point.
353   for (; numViews > 1; --numViews)
354   {
355     delete views[numViews-1];
356   }
357 }
358
359 int ViewMan::handleCommand(int command)
360 {
361   int retVal;
362   int retVal2 = 0;
363   int i;
364
365   if (command != Remote::NA_NONE)
366   {
367     // handle command return values
368     // 0 - drop through to next view
369     // 1 - dont drop to next view, but not handled
370     // 2 - handled - stop command here
371     // 4 - handled - delete this view
372
373     for (i=numViews-1; i>=0; i--)
374     {
375 //      Log::getInstance()->log("ViewMan", Log::DEBUG, "Giving command to i=%i", i);
376       retVal = views[i]->handleCommand(command);
377       if (retVal == 1)
378       {
379         // not handled but don't give to any more views
380         return 0;
381       }
382
383       if (retVal == 2)
384       {
385         // command handled
386         retVal2 = 1;
387         break;
388       }
389       else if (retVal == 4)
390       {
391 //        Log::getInstance()->log("ViewMan", Log::DEBUG, "Return 4: i=%i, views[i]=%p", i, views[i]);
392         removeView(views[i]);
393         retVal2 = 1;
394         break;
395       }
396     }
397   }
398   else
399   {
400     // fake the return code
401     retVal2 = 2;
402   }
403
404   return retVal2;
405 }
406
407 void ViewMan::processMessage(Message* m)
408 {
409   if (m->to != this)
410   {
411     for (int i = numViews-1; i >= 0; i--)
412     {
413       if (views[i] == m->to)
414       {
415         Log::getInstance()->log("ViewMan", Log::DEBUG, "sending message from view %p to view %p %lu", m->from, m->to, m->message);
416         views[i]->processMessage(m);
417         return;
418       }
419     }
420     return;
421   }
422
423   /* Handle mouse events*/
424   // They come in with m->to = NULL? and just need to be delivered to top view?
425   if ((numViews > 1) && ((m->message == Message::MOUSE_MOVE) || (m->message == Message::MOUSE_LBDOWN)))
426   {
427     views[numViews-1]->processMessage(m);
428     return;
429   }
430
431   Log::getInstance()->log("ViewMan", Log::DEBUG, "it's for meeee!");
432
433   switch(m->message)
434   {
435     case Message::CLOSE_ME:
436     {
437       removeView((View*)m->from);
438       break;
439     }
440     case Message::ADD_VIEW: // currently not used by anything but it might come in useful again
441     {
442       View* toAdd = (View*)m->parameter;
443       add(toAdd);
444       toAdd->draw();
445       updateView(toAdd);
446       break;
447     }
448     case Message::REDRAW:
449     {
450       View* toRedraw = (View*)m->from;
451       Region* toRedrawRegion = (Region*)m->parameter;
452       updateView(toRedraw, toRedrawRegion);
453       break;
454     }
455   }
456 }