use local listbox styles for servicelist
[enigma2.git] / lib / service / listboxservice.cpp
1 #include <lib/service/listboxservice.h>
2 #include <lib/service/service.h>
3 #include <lib/gdi/font.h>
4 #include <lib/dvb/epgcache.h>
5 #include <lib/dvb/pmt.h>
6 #include <lib/python/connections.h>
7
8 void eListboxServiceContent::addService(const eServiceReference &service, bool beforeCurrent)
9 {
10         if (beforeCurrent && m_size)
11                 m_list.insert(m_cursor, service);
12         else
13                 m_list.push_back(service);
14         if (m_size++)
15         {
16                 ++m_cursor_number;
17                 if (m_listbox)
18                         m_listbox->entryAdded(m_cursor_number-1);
19         }
20         else
21         {
22                 m_cursor = m_list.begin();
23                 m_cursor_number=0;
24                 m_listbox->entryAdded(0);
25         }
26 }
27
28 void eListboxServiceContent::removeCurrent()
29 {
30         if (m_size && m_listbox)
31         {
32                 if (m_cursor_number == --m_size)
33                 {
34                         m_list.erase(m_cursor--);
35                         if (m_size)
36                         {
37                                 --m_cursor_number;
38                                 m_listbox->entryRemoved(m_cursor_number+1);
39                         }
40                         else
41                                 m_listbox->entryRemoved(m_cursor_number);
42                 }
43                 else
44                 {
45                         m_list.erase(m_cursor++);
46                         m_listbox->entryRemoved(m_cursor_number);
47                 }
48         }
49 }
50
51 void eListboxServiceContent::FillFinished()
52 {
53         m_size = m_list.size();
54         cursorHome();
55
56         if (m_listbox)
57                 m_listbox->entryReset();
58 }
59
60 void eListboxServiceContent::setRoot(const eServiceReference &root, bool justSet)
61 {
62         m_list.clear();
63         m_root = root;
64
65         if (justSet)
66         {
67                 m_lst=0;
68                 return;
69         }
70         assert(m_service_center);
71         
72         if (m_service_center->list(m_root, m_lst))
73                 eDebug("no list available!");
74         else if (m_lst->getContent(m_list))
75                 eDebug("getContent failed");
76
77         FillFinished();
78 }
79
80 void eListboxServiceContent::setCurrent(const eServiceReference &ref)
81 {
82         int index=0;
83         for (list::iterator i(m_list.begin()); i != m_list.end(); ++i, ++index)
84                 if ( *i == ref )
85                 {
86                         m_cursor = i;
87                         m_cursor_number = index;
88                         break;
89                 }
90         if (m_listbox)
91                 m_listbox->moveSelectionTo(index);
92 }
93
94 void eListboxServiceContent::getCurrent(eServiceReference &ref)
95 {
96         if (cursorValid())
97                 ref = *m_cursor;
98         else
99                 ref = eServiceReference();
100 }
101
102 int eListboxServiceContent::getNextBeginningWithChar(char c)
103 {
104 //      printf("Char: %c\n", c);
105         int index=0;
106         for (list::iterator i(m_list.begin()); i != m_list.end(); ++i, ++index)
107         {
108                 std::string text;
109                 ePtr<iStaticServiceInformation> service_info;
110                 m_service_center->info(*i, service_info);
111                 service_info->getName(*i, text);
112 //              printf("%c\n", text.c_str()[0]);
113                 int idx=0;
114                 int len=text.length();
115                 while ( idx <= len )
116                 {
117                         char cc = text[idx++];
118                         if ( cc >= 33 && cc < 127)
119                         {
120                                 if (cc == c)
121                                         return index;
122                                 break;
123                         }
124                 }
125         }
126         return 0;
127 }
128
129 int eListboxServiceContent::getPrevMarkerPos()
130 {
131         if (!m_listbox)
132                 return 0;
133         list::iterator i(m_cursor);
134         int index = m_cursor_number;
135         while (index)
136         {
137                 --i;
138                 --index;
139                 if (i->flags & eServiceReference::isMarker)
140                         break;
141         }
142         return index;
143 }
144
145 int eListboxServiceContent::getNextMarkerPos()
146 {
147         if (!m_listbox)
148                 return 0;
149         list::iterator i(m_cursor);
150         int index = m_cursor_number;
151         while (index < (m_size-1))
152         {
153                 ++i;
154                 ++index;
155                 if (i->flags & eServiceReference::isMarker)
156                         break;
157         }
158         return index;
159 }
160
161 void eListboxServiceContent::initMarked()
162 {
163         m_marked.clear();
164 }
165
166 void eListboxServiceContent::addMarked(const eServiceReference &ref)
167 {
168         m_marked.insert(ref);
169         if (m_listbox)
170                 m_listbox->entryChanged(lookupService(ref));
171 }
172
173 void eListboxServiceContent::removeMarked(const eServiceReference &ref)
174 {
175         m_marked.erase(ref);
176         if (m_listbox)
177                 m_listbox->entryChanged(lookupService(ref));
178 }
179
180 int eListboxServiceContent::isMarked(const eServiceReference &ref)
181 {
182         return m_marked.find(ref) != m_marked.end();
183 }
184
185 void eListboxServiceContent::markedQueryStart()
186 {
187         m_marked_iterator = m_marked.begin();
188 }
189
190 int eListboxServiceContent::markedQueryNext(eServiceReference &ref)
191 {
192         if (m_marked_iterator == m_marked.end())
193                 return -1;
194         ref = *m_marked_iterator++;
195         return 0;
196 }
197
198 int eListboxServiceContent::lookupService(const eServiceReference &ref)
199 {
200                 /* shortcut for cursor */
201         if (ref == *m_cursor)
202                 return m_cursor_number;
203                 /* otherwise, search in the list.. */
204         int index = 0;
205         for (list::const_iterator i(m_list.begin()); i != m_list.end(); ++i, ++index);
206         
207                 /* this is ok even when the index was not found. */
208         return index;
209 }
210
211 void eListboxServiceContent::setVisualMode(int mode)
212 {
213         for (int i=0; i < celElements; ++i)
214         {
215                 m_element_position[i] = eRect();
216                 m_element_font[i] = 0;
217         }
218
219         m_visual_mode = mode;
220
221         if (m_visual_mode == visModeSimple)
222         {
223                 m_element_position[celServiceName] = eRect(ePoint(0, 0), m_itemsize);
224                 m_element_font[celServiceName] = new gFont("Regular", 23);
225         }
226 }
227
228 void eListboxServiceContent::setElementPosition(int element, eRect where)
229 {
230         if ((element >= 0) && (element < celElements))
231                 m_element_position[element] = where;
232 }
233
234 void eListboxServiceContent::setElementFont(int element, gFont *font)
235 {
236         if ((element >= 0) && (element < celElements))
237                 m_element_font[element] = font;
238 }
239
240 void eListboxServiceContent::setPixmap(int type, ePtr<gPixmap> &pic)
241 {
242         if ((type >=0) && (type < picElements))
243                 m_pixmaps[type] = pic;
244 }
245
246 void eListboxServiceContent::sort()
247 {
248         if (!m_lst)
249                 m_service_center->list(m_root, m_lst);
250         if (m_lst)
251         {
252                 m_list.sort(iListableServiceCompare(m_lst));
253                         /* FIXME: is this really required or can we somehow keep the current entry? */
254                 cursorHome();
255                 if (m_listbox)
256                         m_listbox->entryReset();
257         }
258 }
259
260 DEFINE_REF(eListboxServiceContent);
261
262 eListboxServiceContent::eListboxServiceContent()
263         :m_visual_mode(visModeSimple), m_size(0), m_current_marked(false), m_numberoffset(0), m_itemheight(25)
264 {
265         memset(m_color_set, 0, sizeof(m_color_set));
266         cursorHome();
267         eServiceCenter::getInstance(m_service_center);
268 }
269
270 void eListboxServiceContent::setColor(int color, gRGB &col)
271 {
272         if ((color >= 0) && (color < colorElements))
273         {
274                 m_color_set[color] = true;
275                 m_color[color] = col;
276         }
277 }
278
279 void eListboxServiceContent::cursorHome()
280 {
281         if (m_current_marked && m_saved_cursor == m_list.end())
282         {
283                 if (m_cursor_number >= m_size)
284                 {
285                         m_cursor_number = m_size-1;
286                         --m_cursor;
287                 }
288                 while (m_cursor_number)
289                 {
290                         std::iter_swap(m_cursor--, m_cursor);
291                         --m_cursor_number;
292                         if (m_listbox && m_cursor_number)
293                                 m_listbox->entryChanged(m_cursor_number);
294                 }
295         }
296         else
297         {
298                 m_cursor = m_list.begin();
299                 m_cursor_number = 0;
300         }
301 }
302
303 void eListboxServiceContent::cursorEnd()
304 {
305         if (m_current_marked && m_saved_cursor == m_list.end())
306         {
307                 while (m_cursor != m_list.end())
308                 {
309                         list::iterator prev = m_cursor++;
310                         ++m_cursor_number;
311                         if ( prev != m_list.end() && m_cursor != m_list.end() )
312                         {
313                                 std::iter_swap(m_cursor, prev);
314                                 if ( m_listbox )
315                                         m_listbox->entryChanged(m_cursor_number);
316                         }
317                 }
318         }
319         else
320         {
321                 m_cursor = m_list.end();
322                 m_cursor_number = m_size;
323         }
324 }
325
326 int eListboxServiceContent::setCurrentMarked(bool state)
327 {
328         bool prev = m_current_marked;
329         m_current_marked = state;
330
331         if (state != prev && m_listbox)
332         {
333                 m_listbox->entryChanged(m_cursor_number);
334                 if (!state)
335                 {
336                         if (!m_lst)
337                                 m_service_center->list(m_root, m_lst);
338                         if (m_lst)
339                         {
340                                 ePtr<iMutableServiceList> list;
341                                 if (m_lst->startEdit(list))
342                                         eDebug("no editable list");
343                                 else
344                                 {
345                                         eServiceReference ref;
346                                         getCurrent(ref);
347                                         if(!ref)
348                                                 eDebug("no valid service selected");
349                                         else
350                                         {
351                                                 int pos = cursorGet();
352                                                 eDebugNoNewLine("move %s to %d ", ref.toString().c_str(), pos);
353                                                 if (list->moveService(ref, cursorGet()))
354                                                         eDebug("failed");
355                                                 else
356                                                         eDebug("ok");
357                                         }
358                                 }
359                         }
360                         else
361                                 eDebug("no list available!");
362                 }
363         }
364
365         return 0;
366 }
367
368 int eListboxServiceContent::cursorMove(int count)
369 {
370         int prev = m_cursor_number, last = m_cursor_number + count;
371         if (count > 0)
372         {
373                 while(count && m_cursor != m_list.end())
374                 {
375                         list::iterator prev_it = m_cursor++;
376                         if ( m_current_marked && m_cursor != m_list.end() && m_saved_cursor == m_list.end() )
377                         {
378                                 std::iter_swap(prev_it, m_cursor);
379                                 if ( m_listbox && prev != m_cursor_number && last != m_cursor_number )
380                                         m_listbox->entryChanged(m_cursor_number);
381                         }
382                         ++m_cursor_number;
383                         --count;
384         }
385         } else if (count < 0)
386         {
387                 while (count && m_cursor != m_list.begin())
388                 {
389                         list::iterator prev_it = m_cursor--;
390                         if ( m_current_marked && m_cursor != m_list.end() && prev_it != m_list.end() && m_saved_cursor == m_list.end() )
391                         {
392                                 std::iter_swap(prev_it, m_cursor);
393                                 if ( m_listbox && prev != m_cursor_number && last != m_cursor_number )
394                                         m_listbox->entryChanged(m_cursor_number);
395                         }
396                         --m_cursor_number;
397                         ++count;
398                 }
399         }
400         return 0;
401 }
402
403 int eListboxServiceContent::cursorValid()
404 {
405         return m_cursor != m_list.end();
406 }
407
408 int eListboxServiceContent::cursorSet(int n)
409 {
410         cursorHome();
411         cursorMove(n);
412         return 0;
413 }
414
415 int eListboxServiceContent::cursorGet()
416 {
417         return m_cursor_number;
418 }
419
420 void eListboxServiceContent::cursorSave()
421 {
422         m_saved_cursor = m_cursor;
423         m_saved_cursor_number = m_cursor_number;
424 }
425
426 void eListboxServiceContent::cursorRestore()
427 {
428         m_cursor = m_saved_cursor;
429         m_cursor_number = m_saved_cursor_number;
430         m_saved_cursor = m_list.end();
431 }
432
433 int eListboxServiceContent::size()
434 {
435         return m_size;
436 }
437         
438 void eListboxServiceContent::setSize(const eSize &size)
439 {
440         m_itemsize = size;
441         if (m_visual_mode == visModeSimple)
442                 setVisualMode(m_visual_mode);
443 }
444
445 void eListboxServiceContent::paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected)
446 {
447         painter.clip(eRect(offset, m_itemsize));
448
449         bool marked = m_current_marked || (cursorValid() && isMarked(*m_cursor));
450
451         if (marked)
452                 style.setStyle(painter, selected ? eWindowStyle::styleListboxMarkedAndSelected : eWindowStyle::styleListboxMarked);
453         else
454                 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
455
456         eListboxStyle *local_style = 0;
457
458                 /* get local listbox style, if present */
459         if (m_listbox)
460                 local_style = m_listbox->getLocalStyle();
461
462         if (marked)
463         {
464                 if (selected)
465                 {
466                         if (m_color_set[markedForegroundSelected])
467                                 painter.setForegroundColor(m_color[markedForegroundSelected]);
468                         if (m_color_set[markedBackgroundSelected])
469                                 painter.setBackgroundColor(m_color[markedBackgroundSelected]);
470                 }
471                 else
472                 {
473                         if (m_color_set[markedForeground])
474                                 painter.setForegroundColor(m_color[markedForeground]);
475                         if (m_color_set[markedBackground])
476                                 painter.setBackgroundColor(m_color[markedBackground]);
477                 }
478         }
479         else if (local_style)
480         {
481                 if (selected)
482                 {
483                         /* if we have a local background color set, use that. */
484                         if (local_style->m_background_color_selected_set)
485                                 painter.setBackgroundColor(local_style->m_background_color_selected);
486                         /* same for foreground */
487                         if (local_style->m_foreground_color_selected_set)
488                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
489                 }
490                 else
491                 {
492                         /* if we have a local background color set, use that. */
493                         if (local_style->m_background_color_set)
494                                 painter.setBackgroundColor(local_style->m_background_color);
495                         /* same for foreground */
496                         if (local_style->m_foreground_color_set)
497                                 painter.setForegroundColor(local_style->m_foreground_color);
498                 }
499         }
500
501         if (!local_style || !local_style->m_transparent_background)
502                 /* if we have no transparent background */
503         {
504                 /* blit background picture, if available (otherwise, clear only) */
505                 if (local_style && local_style->m_background)
506                         painter.blit(local_style->m_background, offset, eRect(), 0);
507                 else
508                         painter.clear();
509         } else
510         {
511                 if (local_style->m_background)
512                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
513                 else if (selected && !local_style->m_selection)
514                         painter.clear();
515         }
516
517         painter.clear();
518         
519         if (cursorValid())
520         {
521                         /* get service information */
522                 ePtr<iStaticServiceInformation> service_info;
523                 m_service_center->info(*m_cursor, service_info);
524                 eServiceReference ref = *m_cursor;
525                 bool isPlayable = !(ref.flags & eServiceReference::isDirectory || ref.flags & eServiceReference::isMarker);
526
527                 if (!marked && isPlayable && service_info && !service_info->isPlayable(*m_cursor, m_is_playable_ignore))
528                 {
529                         if (m_color_set[serviceNotAvail])
530                                 painter.setForegroundColor(m_color[serviceNotAvail]);
531                         else
532                                 painter.setForegroundColor(gRGB(0xbbbbbb));
533                 }
534
535                 if (selected && local_style && local_style->m_selection)
536                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
537
538                 int xoffset=0;  // used as offset when painting the folder/marker symbol
539
540                 for (int e = 0; e < celElements; ++e)
541                 {
542                         if (m_element_font[e])
543                         {
544                                 int flags=gPainter::RT_VALIGN_CENTER,
545                                         yoffs = 0,
546                                         xoffs = xoffset;
547                                 eRect &area = m_element_position[e];
548                                 std::string text = "<n/a>";
549                                 xoffset=0;
550
551                                 switch (e)
552                                 {
553                                 case celServiceNumber:
554                                 {
555                                         if (m_cursor->flags & eServiceReference::isMarker)
556                                                 continue;
557                                         char bla[10];
558                                 /* how we can do this better? :) */
559                                         int markers_before=0;
560                                         {
561                                                 list::iterator tmp=m_cursor;
562                                                 while(tmp != m_list.begin())
563                                                 {
564                                                         --tmp;
565                                                         if (tmp->flags & eServiceReference::isMarker)
566                                                                 ++markers_before;
567                                                 }
568                                         }
569                                         sprintf(bla, "%d", m_numberoffset + m_cursor_number + 1 - markers_before);
570                                         text = bla;
571                                         flags|=gPainter::RT_HALIGN_RIGHT;
572                                         break;
573                                 }
574                                 case celServiceName:
575                                 {
576                                         if (service_info)
577                                                 service_info->getName(*m_cursor, text);
578                                         break;
579                                 }
580                                 case celServiceInfo:
581                                 {
582                                         ePtr<eServiceEvent> evt;
583                                         if ( service_info && !service_info->getEvent(*m_cursor, evt) )
584                                         {
585                                                 std::string name = evt->getEventName();
586                                                 if (!name.length())
587                                                         continue;
588                                                 text = '(' + evt->getEventName() + ')';
589                                         }
590                                         else
591                                                 continue;
592                                         break;
593                                 }
594                                 }
595
596                                 eRect tmp = area;
597                                 tmp.setWidth(tmp.width()-xoffs);
598
599                                 eTextPara *para = new eTextPara(tmp);
600                                 para->setFont(m_element_font[e]);
601                                 para->renderString(text.c_str());
602
603                                 if (e == celServiceName)
604                                 {
605                                         eRect bbox = para->getBoundBox();
606                                         int name_width = bbox.width()+8;
607                                         m_element_position[celServiceInfo].setLeft(area.left()+name_width);
608                                         m_element_position[celServiceInfo].setTop(area.top());
609                                         m_element_position[celServiceInfo].setWidth(area.width()-name_width);
610                                         m_element_position[celServiceInfo].setHeight(area.height());
611                                 }
612
613                                 if (flags & gPainter::RT_HALIGN_RIGHT)
614                                         para->realign(eTextPara::dirRight);
615                                 else if (flags & gPainter::RT_HALIGN_CENTER)
616                                         para->realign(eTextPara::dirCenter);
617                                 else if (flags & gPainter::RT_HALIGN_BLOCK)
618                                         para->realign(eTextPara::dirBlock);
619
620                                 if (flags & gPainter::RT_VALIGN_CENTER)
621                                 {
622                                         eRect bbox = para->getBoundBox();
623                                         int vcentered_top = (area.height() - bbox.height()) / 2;
624                                         yoffs = vcentered_top - bbox.top();
625                                 }
626
627                                 painter.renderPara(para, offset+ePoint(xoffs, yoffs));
628                         }
629                         else if (e == celServiceTypePixmap || e == celFolderPixmap || e == celMarkerPixmap)
630                         {
631                                 int orbpos = m_cursor->getUnsignedData(4) >> 16;
632                                 ePtr<gPixmap> &pixmap =
633                                         (e == celFolderPixmap) ? m_pixmaps[picFolder] :
634                                         (e == celMarkerPixmap) ? m_pixmaps[picMarker] :
635                                         (m_cursor->flags & eServiceReference::isGroup) ? m_pixmaps[picServiceGroup] :
636                                         (orbpos == 0xFFFF) ? m_pixmaps[picDVB_C] :
637                                         (orbpos == 0xEEEE) ? m_pixmaps[picDVB_T] : m_pixmaps[picDVB_S];
638                                 if (pixmap)
639                                 {
640                                         eSize pixmap_size = pixmap->size();
641                                         int p = celServiceInfo;
642                                         if (e == celFolderPixmap)
643                                                 p = celServiceName;
644                                         else if (e == celMarkerPixmap)
645                                                 p = celServiceNumber;
646                                         eRect area = m_element_position[p];
647                                         int correction = (area.height() - pixmap_size.height()) / 2;
648
649                                         if (isPlayable)
650                                         {
651                                                 if (e != celServiceTypePixmap)
652                                                         continue;
653                                                 m_element_position[celServiceInfo] = area;
654                                                 m_element_position[celServiceInfo].setLeft(area.left() + pixmap_size.width() + 8);
655                                                 m_element_position[celServiceInfo].setWidth(area.width() - pixmap_size.width() - 8);
656                                         }
657                                         else if (m_cursor->flags & eServiceReference::isDirectory)
658                                         {
659                                                 if (e != celFolderPixmap)
660                                                         continue;
661                                                 xoffset = pixmap_size.width() + 8;
662                                         }
663                                         else if (m_cursor->flags & eServiceReference::isMarker)
664                                         {
665                                                 if (e != celMarkerPixmap)
666                                                         continue;
667                                         }
668                                         else
669                                                 eFatal("unknown service type in listboxservice");
670
671                                         area.moveBy(offset);
672                                         painter.clip(area);
673                                         painter.blit(pixmap, offset+ePoint(area.left(), correction), area, gPainter::BT_ALPHATEST);
674                                         painter.clippop();
675                                 }
676                         }
677                 }
678                 
679                 if (selected && (!local_style || !local_style->m_selection))
680                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
681         }
682         
683         painter.clippop();
684 }
685
686 void eListboxServiceContent::setIgnoreService( const eServiceReference &service )
687 {
688         m_is_playable_ignore=service;
689 }
690
691 int eListboxServiceContent::setItemHeight(int height)
692 {
693         m_itemheight = height;
694         if (m_listbox)
695                 m_listbox->setItemHeight(height);
696 }