]> git.vomp.tv Git - vompclient.git/blob - boxstack.cc
Add static icons in menu
[vompclient.git] / boxstack.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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "boxstack.h"
22
23 #include "command.h"
24 #include "remote.h"
25 #include "log.h"
26
27 BoxStack* BoxStack::instance = NULL;
28
29 BoxStack::BoxStack()
30 {
31   if (instance) return;
32   instance = this;
33   initted = 0;
34   numBoxes = 0;
35 }
36
37 BoxStack::~BoxStack()
38 {
39   instance = NULL;
40 }
41
42 BoxStack* BoxStack::getInstance()
43 {
44   return instance;
45 }
46
47 int BoxStack::init()
48 {
49   if (initted) return 0;
50   initted = 1;
51   
52   return 1;
53 }
54
55 int BoxStack::shutdown()
56 {
57   if (!initted) return 0;
58
59   // FIXME don't think this can work properly, removeAll leaves the wallpaper there!
60   removeAll();
61
62   initted = 0;
63   return 1;
64 }
65
66 int BoxStack::addVideoDisplay(Boxx* box,VideoDisplay vd)
67 {
68           videoStack.push(pair<Boxx*,VideoDisplay>(box,vd));
69           Video::getInstance()->setVideoDisplay(vd);
70           return 1;
71 }
72
73 int BoxStack::add(Boxx* v)
74 {
75   if (!initted) return 0;
76   Log::getInstance()->log("BoxStack", Log::DEBUG, "add called"); 
77   boxLock.Lock();
78   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for add");  
79   
80   if (numBoxes == 16)
81   { //Error
82     Log::getInstance()->log("BoxStack", Log::ERR, "More than 16 boxes! Unlocked for add"); 
83     boxLock.Unlock();
84     return 0;
85   }
86   boxes[numBoxes++] = v;
87   VideoDisplay vd;
88   if (v->getVideoDisplay(vd)) {
89           Log::getInstance()->log("BoxStack", Log::DEBUG, "Add video display");
90           addVideoDisplay(v,vd);
91   }
92
93   boxLock.Unlock();
94
95   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for add");
96   
97   return 1;
98 }
99
100 // ---------------------------------------------------- REMOVE CODE
101
102 int BoxStack::remove(Boxx* toDelete)
103 {
104   if (!initted) return 0;
105   VideoDisplay *display = NULL;
106
107   boxLock.Lock();
108   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for remove");  
109   
110   if (numBoxes == 0)
111   {
112     boxLock.Unlock();
113     Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove numBoxes == 0");  
114     return 0;
115   }
116
117 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "entering remove, numBoxes=%i", numBoxes);
118
119   int i;
120
121   if (toDelete == NULL)
122   {
123     toDelete = boxes[numBoxes-1];
124     i = numBoxes - 1;
125   }
126   else
127   {
128     // to be deleted box is more likely to be at the top
129     for (i = numBoxes-1; i >= 0; i--)
130     {
131 //      Log::getInstance()->log("BoxStack", Log::DEBUG, "todel: %p, i=%i, boxes[i]=%p", toDelete, i, boxes[i]);
132       if (boxes[i] == toDelete) break;
133     }
134
135     if (i == -1)
136     {
137       // not a Box we have!
138       // FIXME
139       boxLock.Unlock();
140       Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove - no boxx deleted");  
141       return 0;
142     }
143   }
144
145   boxLock.Unlock();
146
147 toDelete->preDelete();
148
149   boxLock.Lock();
150
151 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Starting deleteBox");
152   deleteBox(i);
153 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Done deleteBox");
154
155   // Shift the boxes on top down one
156   --numBoxes;
157   for(int j = i; j < numBoxes; j++) boxes[j] = boxes[j+1];
158
159   // If there is only the wallpaper left signal command
160   if (numBoxes == 1)
161   {
162     Message* m = new Message();
163     m->to = Command::getInstance();
164     m->message = Message::LAST_VIEW_CLOSE;
165     Command::getInstance()->postMessageNoLock(m);
166   }
167
168   if (!videoStack.empty() && videoStack.top().first==toDelete) {
169         videoStack.pop();
170     if (!videoStack.empty()) display=&videoStack.top().second;
171   }
172   boxLock.Unlock();
173   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove");  
174  
175   // Delete the box
176   //AVO: do this delete outside the lock to allow for recursive calls within the destructor
177   //     as this box is not in the stack any more, there is no chance for a second delete
178   Log::getInstance()->log("BoxStack", Log::DEBUG, "remove: going to delete boxx %p, num %d", toDelete, numBoxes);  
179   delete toDelete;
180   if  (display) {
181           Log::getInstance()->log("BoxStack", Log::DEBUG, "setVideoDisplay %d %d %d %d %d %d", display->mode, display->fallbackMode,
182                           display->x, display->y, display->width, display->height);
183           Video::getInstance()->setVideoDisplay(*display);
184   }
185
186   return 1;
187 }
188
189 /////////////////////////////////////////////////////////////////////////////
190 // NEW STUFF
191 /////////////////////////////////////////////////////////////////////////////
192
193 void BoxStack::deleteBox(int z)
194 {
195 //  Log::getInstance()->log("BoxStack", Log::DEBUG, "Delete box %i of %i", z, numBoxes);
196   RegionList rl;
197   boxSplit(boxes[z]->area, z + 1, numBoxes, 1, rl);
198   while(!rl.empty())
199   {
200     repaintRevealed(z, rl.front());
201     rl.pop_front();
202   }
203 }
204
205 void BoxStack::redrawAllBoxes()
206 {
207   boxLock.Lock();
208
209   for (int z = 0; z < numBoxes; z++)
210   {
211           boxes[z]->draw();
212   }
213
214
215   boxLock.Unlock();
216   update(NULL,NULL); // should blt all
217 }
218
219 void BoxStack::update(Boxx* toUpdate, Region* regionToUpdate)
220 {
221   Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called");
222   if (!initted) return; // it is allowed to call this before init
223   boxLock.Lock();
224   Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for update");
225
226   // Get the z index of the box
227   int z = 0;
228   if (toUpdate)
229   {
230     for (z = 0; z < numBoxes; z++)
231     {
232       if (boxes[z] == toUpdate) break;
233     }
234
235     if (z == numBoxes)
236     {
237       // not a Box we have!
238       boxLock.Unlock();
239       Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for update! box not inside boxstack");
240       return;
241     }
242   }
243   else
244   {
245     toUpdate = boxes[0];
246   }
247
248   if (!toUpdate) {
249       boxLock.Unlock();
250           Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update, no box present");
251           return ;
252   }
253
254   // get the region for the whole box, could be less than that
255   // for smaller updates
256
257   Region r = toUpdate->area;
258
259   if (regionToUpdate)
260   {
261     // Can be null if the whole box should be updated
262     // If this is given the numbers are relative to the size of the box, not the screen
263
264     r.x += regionToUpdate->x;
265     r.y += regionToUpdate->y;
266     r.w = regionToUpdate->w;
267     r.h = regionToUpdate->h;
268   }
269
270   RegionList rl;
271
272   Region r2;
273   boxSplit(r, z+1, numBoxes, 1, rl);
274   while(!rl.empty())
275   {
276     r2 = rl.front();
277     r2.z = z;
278     boxes[z]->blt(r2);
279     rl.pop_front();
280   }
281
282   boxLock.Unlock();
283   Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update");
284 }
285
286 void BoxStack::repaintRevealed(int x, Region r)
287 {
288   RegionList rl;
289   boxSplit(r, x - 1, -1, -1, rl);
290
291   Region r2;
292   while(!rl.empty())
293   {
294     r2 = rl.front();
295     boxes[r2.z]->blt(r2);
296     rl.pop_front();
297   }
298 }
299
300 void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl)
301 {
302 //  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);
303
304   for(int z = start; z != end; z += direction)
305   {
306     if (r.overlappedBy(boxes[z]->area))
307     {
308 //      printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);
309
310       int top = r.y;
311       int btm = r.y2();
312
313       if (boxes[z]->area.y > r.y)
314       {
315 //        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, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);
316         top = boxes[z]->area.y;
317         Region newR;
318         newR.x = r.x;
319         newR.y = r.y;
320         newR.w = r.w;
321         newR.h = boxes[z]->area.y - r.y;
322         boxSplit(newR, z + direction, end, direction, rl);
323
324         if (direction == -1)
325         {
326           Region newR2;
327           newR2.x = r.x;
328           newR2.y = boxes[z]->area.y;
329           newR2.w = r.w;
330           newR2.h = r.h - newR.h;
331           boxSplit(newR2, z, end, -1, rl);
332           return;
333         }
334       }
335
336       if (boxes[z]->area.y2() < r.y2())
337       {
338 //        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, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);
339         btm = boxes[z]->area.y2();
340         Region newR;
341         newR.x = r.x;
342         newR.y = boxes[z]->area.y2() + 1;
343         newR.w = r.w;
344         newR.h = r.y2() - newR.y + 1;
345         boxSplit(newR, z + direction, end, direction, rl);
346
347         if (direction == -1)
348         {
349           Region newR2;
350           newR2.x = r.x;
351           newR2.y = r.y;
352           newR2.w = r.w;
353           newR2.h = r.h - newR.h;
354           boxSplit(newR2, z, end, -1, rl);
355           return;
356         }
357       }
358
359       if (boxes[z]->area.x > r.x)
360       {
361 //        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, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);
362         Region newR;
363         newR.x = r.x;
364         newR.y = top;
365         newR.w = boxes[z]->area.x - r.x;
366         newR.h = btm - top + 1;
367         boxSplit(newR, z + direction, end, direction, rl);
368
369         if (direction == -1)
370         {
371           Region newR2;
372           newR2.x = r.x + newR.w;
373           newR2.y = r.y;
374           newR2.w = r.w - newR.w;
375           newR2.h = r.h;
376           boxSplit(newR2, z, end, -1, rl);
377           return;
378         }
379       }
380
381       if (boxes[z]->area.x2() < r.x2())
382       {
383 //        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, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h);
384         Region newR;
385         newR.x = boxes[z]->area.x2() + 1;
386         newR.y = top;
387         newR.w = r.x2() - newR.x + 1;
388         newR.h = btm - top + 1;
389         boxSplit(newR, z + direction, end, direction, rl);
390
391         if (direction == -1)
392         {
393           Region newR2;
394           newR2.x = r.x;
395           newR2.y = r.y;
396           newR2.w = r.w - newR.w;
397           newR2.h = r.h;
398           boxSplit(newR2, z, end, -1, rl);
399           return;
400         }
401       }
402
403       if (direction == -1)
404       {
405         // we are going down the stack
406         // r is underlapped by boxes[z]
407         // but we have not split
408         // Therefore this region under test is
409         // completely covering boxes[z]
410
411         // don't go any further down, generate a region and quit
412
413 //        printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);
414         r.z = z;
415         rl.push_front(r);
416       }
417
418 //      printf("Returning from Z=%i\n", z);
419       return;
420     }
421     else
422     {
423 //      printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);
424     }
425   }
426
427   // if direction = 1 then we have come to a region that is
428   // entirely clear of higher boxes and needs to be redrawn
429
430   // if direction = -1 then we have come to a region that is on
431   // the very bottom with nothing below it to repaint.
432   // do nothing. stale window data will be left on screen?
433
434   if (direction == 1)
435   {
436     rl.push_front(r);
437   }
438 }
439
440 /////////////////////////////////////////////////////////////////////////////
441 // END NEW STUFF
442 /////////////////////////////////////////////////////////////////////////////
443
444 // ---------------------------------------------------- END OF REMOVE CODE
445
446
447 void BoxStack::removeAll()
448 {
449   // 1.. Don't delete wallpaper. No point.
450
451   // Need locking on this one??
452   
453   // This is pretty silly now that preDelete needs mutex unlocked
454   
455   Boxx* toDel = NULL;
456   VideoDisplay *display = NULL;
457   
458   while(numBoxes > 1)
459   {
460     boxLock.Lock(); 
461
462     if (numBoxes == 1)
463     {
464       boxLock.Unlock();
465       break;
466     }    
467   
468     toDel = boxes[numBoxes - 1];
469
470     boxLock.Unlock();
471
472     toDel->preDelete();
473     
474     boxLock.Lock();
475
476     // If boxes[numBoxes - 1] isn't toDel then there's a problem
477     if (boxes[numBoxes - 1] == toDel)
478     {
479       --numBoxes;
480     }
481     else
482     {
483       Log::getInstance()->log("BoxStack", Log::ERR, "Can this actually happen? Why?");
484       toDel = NULL;
485     }
486
487     if (!videoStack.empty() && videoStack.top().first==toDel) {
488         videoStack.pop();
489         if (!videoStack.empty()) display=&videoStack.top().second;
490     }
491     boxLock.Unlock();
492
493     //AVO: do the delete outside the lock to allow for recursive deletes
494     Log::getInstance()->log("BoxStack", Log::DEBUG, "going to delete boxx %p, num=%d", toDel, numBoxes);
495     if (display) Video::getInstance()->setVideoDisplay(*display);
496
497
498     if (toDel) delete toDel;
499   }
500
501
502 }
503
504 int BoxStack::handleCommand(int command)
505 {
506   int retVal;
507   int retVal2 = 0;
508   int i;
509
510   if (command != Remote::NA_NONE)
511   {
512     // handle command return values
513     // 0 - drop through to next box
514     // 1 - dont drop to next box, but not handled
515     // 2 - handled - stop command here
516     // 4 - handled - delete this box
517
518     for (i=numBoxes-1; i>=0; i--)
519     {
520 //      Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i);
521       retVal = boxes[i]->handleCommand(command);
522       if (retVal == 1)
523       {
524         // not handled but don't give to any more boxes
525         return 0;
526       }
527
528       if (retVal == 2)
529       {
530         // command handled
531         retVal2 = 1;
532         break;
533       }
534       else if (retVal == 4)
535       {
536 //        Log::getInstance()->log("BoxStack", Log::DEBUG, "Return 4: i=%i, boxes[i]=%p", i, boxes[i]);
537         remove(boxes[i]);
538         retVal2 = 1;
539         break;
540       }
541     }
542   }
543   else
544   {
545     // fake the return code
546     retVal2 = 2;
547   }
548
549   return retVal2;
550 }
551
552 void BoxStack::processMessage(Message* m)
553 {
554   if (m->to != this)
555   {
556     for (int i = numBoxes-1; i >= 0; i--)
557     {
558       if (boxes[i] == m->to)
559       {
560         Log::getInstance()->log("BoxStack", Log::DEBUG, "sending message from box %p to box %p %lu", m->from, m->to, m->message);
561         boxes[i]->processMessage(m);
562         return;
563       }
564     }
565     return;
566   }
567
568   /* Handle mouse events*/
569   // They come in with m->to = NULL? and just need to be delivered to top box?
570   if ((numBoxes > 1) && ((m->message == Message::MOUSE_MOVE) || (m->message == Message::MOUSE_LBDOWN)
571                   || (m->message == Message::MOUSE_ANDROID_SCROLL)))
572   {
573     boxes[numBoxes-1]->processMessage(m);
574     return;
575   }
576
577   Log::getInstance()->log("BoxStack", Log::DEBUG, "it's for meeee!");
578
579   switch(m->message)
580   {
581     case Message::CLOSE_ME:
582     {
583       remove((Boxx*)m->from);
584       break;
585     }
586     case Message::ADD_VIEW: // currently not used by anything but it might come in useful again
587     {
588       Boxx* toAdd = (Boxx*)m->parameter;
589       add(toAdd);
590       toAdd->draw();
591       update(toAdd);
592       break;
593     }
594   }
595 }