lib/dvb/dvb.cpp: allow non relative seeking even without valid decoding demux here
[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         int marked = 0;
450
451         if (m_current_marked && selected)
452                 marked = 2;
453         else if (cursorValid() && isMarked(*m_cursor))
454         {
455                 if (selected)
456                         marked = 2;
457                 else
458                         marked = 1;
459         }
460         else
461                 style.setStyle(painter, selected ? eWindowStyle::styleListboxSelected : eWindowStyle::styleListboxNormal);
462
463         eListboxStyle *local_style = 0;
464
465                 /* get local listbox style, if present */
466         if (m_listbox)
467                 local_style = m_listbox->getLocalStyle();
468
469         if (marked == 1)  // marked
470         {
471                 style.setStyle(painter, eWindowStyle::styleListboxMarked);
472                 if (m_color_set[markedForeground])
473                         painter.setForegroundColor(m_color[markedForeground]);
474                 if (m_color_set[markedBackground])
475                         painter.setBackgroundColor(m_color[markedBackground]);
476         }
477         else if (marked == 2) // marked and selected
478         {
479                 style.setStyle(painter, eWindowStyle::styleListboxMarkedAndSelected);
480                 if (m_color_set[markedForegroundSelected])
481                         painter.setForegroundColor(m_color[markedForegroundSelected]);
482                 if (m_color_set[markedBackgroundSelected])
483                         painter.setBackgroundColor(m_color[markedBackgroundSelected]);
484         }
485         else if (local_style)
486         {
487                 if (selected)
488                 {
489                         /* if we have a local background color set, use that. */
490                         if (local_style->m_background_color_selected_set)
491                                 painter.setBackgroundColor(local_style->m_background_color_selected);
492                         /* same for foreground */
493                         if (local_style->m_foreground_color_selected_set)
494                                 painter.setForegroundColor(local_style->m_foreground_color_selected);
495                 }
496                 else
497                 {
498                         /* if we have a local background color set, use that. */
499                         if (local_style->m_background_color_set)
500                                 painter.setBackgroundColor(local_style->m_background_color);
501                         /* same for foreground */
502                         if (local_style->m_foreground_color_set)
503                                 painter.setForegroundColor(local_style->m_foreground_color);
504                 }
505         }
506
507         if (!local_style || !local_style->m_transparent_background)
508                 /* if we have no transparent background */
509         {
510                 /* blit background picture, if available (otherwise, clear only) */
511                 if (local_style && local_style->m_background)
512                         painter.blit(local_style->m_background, offset, eRect(), 0);
513                 else
514                         painter.clear();
515         } else
516         {
517                 if (local_style->m_background)
518                         painter.blit(local_style->m_background, offset, eRect(), gPainter::BT_ALPHATEST);
519                 else if (selected && !local_style->m_selection)
520                         painter.clear();
521         }
522
523         if (cursorValid())
524         {
525                         /* get service information */
526                 ePtr<iStaticServiceInformation> service_info;
527                 m_service_center->info(*m_cursor, service_info);
528                 eServiceReference ref = *m_cursor;
529                 bool isPlayable = !(ref.flags & eServiceReference::isDirectory || ref.flags & eServiceReference::isMarker);
530
531                 if (!marked && isPlayable && service_info && m_is_playable_ignore.valid() && !service_info->isPlayable(*m_cursor, m_is_playable_ignore))
532                 {
533                         if (m_color_set[serviceNotAvail])
534                                 painter.setForegroundColor(m_color[serviceNotAvail]);
535                         else
536                                 painter.setForegroundColor(gRGB(0xbbbbbb));
537                 }
538
539                 if (selected && local_style && local_style->m_selection)
540                         painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST);
541
542                 int xoffset=0;  // used as offset when painting the folder/marker symbol
543
544                 for (int e = 0; e < celElements; ++e)
545                 {
546                         if (m_element_font[e])
547                         {
548                                 int flags=gPainter::RT_VALIGN_CENTER,
549                                         yoffs = 0,
550                                         xoffs = xoffset;
551                                 eRect &area = m_element_position[e];
552                                 std::string text = "<n/a>";
553                                 xoffset=0;
554
555                                 switch (e)
556                                 {
557                                 case celServiceNumber:
558                                 {
559                                         if (m_cursor->flags & eServiceReference::isMarker)
560                                                 continue;
561                                         char bla[10];
562                                 /* how we can do this better? :) */
563                                         int markers_before=0;
564                                         {
565                                                 list::iterator tmp=m_cursor;
566                                                 while(tmp != m_list.begin())
567                                                 {
568                                                         --tmp;
569                                                         if (tmp->flags & eServiceReference::isMarker)
570                                                                 ++markers_before;
571                                                 }
572                                         }
573                                         sprintf(bla, "%d", m_numberoffset + m_cursor_number + 1 - markers_before);
574                                         text = bla;
575                                         flags|=gPainter::RT_HALIGN_RIGHT;
576                                         break;
577                                 }
578                                 case celServiceName:
579                                 {
580                                         if (service_info)
581                                                 service_info->getName(*m_cursor, text);
582                                         break;
583                                 }
584                                 case celServiceInfo:
585                                 {
586                                         ePtr<eServiceEvent> evt;
587                                         if ( isPlayable && service_info && !service_info->getEvent(*m_cursor, evt) )
588                                         {
589                                                 std::string name = evt->getEventName();
590                                                 if (!name.length())
591                                                         continue;
592                                                 text = '(' + evt->getEventName() + ')';
593                                         }
594                                         else
595                                                 continue;
596                                         break;
597                                 }
598                                 }
599
600                                 eRect tmp = area;
601                                 tmp.setWidth(tmp.width()-xoffs);
602
603                                 eTextPara *para = new eTextPara(tmp);
604                                 para->setFont(m_element_font[e]);
605                                 para->renderString(text.c_str());
606
607                                 if (e == celServiceName)
608                                 {
609                                         eRect bbox = para->getBoundBox();
610                                         int name_width = bbox.width()+8;
611                                         m_element_position[celServiceInfo].setLeft(area.left()+name_width);
612                                         m_element_position[celServiceInfo].setTop(area.top());
613                                         m_element_position[celServiceInfo].setWidth(area.width()-name_width);
614                                         m_element_position[celServiceInfo].setHeight(area.height());
615                                 }
616
617                                 if (flags & gPainter::RT_HALIGN_RIGHT)
618                                         para->realign(eTextPara::dirRight);
619                                 else if (flags & gPainter::RT_HALIGN_CENTER)
620                                         para->realign(eTextPara::dirCenter);
621                                 else if (flags & gPainter::RT_HALIGN_BLOCK)
622                                         para->realign(eTextPara::dirBlock);
623
624                                 if (flags & gPainter::RT_VALIGN_CENTER)
625                                 {
626                                         eRect bbox = para->getBoundBox();
627                                         int vcentered_top = (area.height() - bbox.height()) / 2;
628                                         yoffs = vcentered_top - bbox.top();
629                                 }
630
631                                 painter.renderPara(para, offset+ePoint(xoffs, yoffs));
632                         }
633                         else if (e == celServiceTypePixmap || e == celFolderPixmap || e == celMarkerPixmap)
634                         {
635                                 int orbpos = m_cursor->getUnsignedData(4) >> 16;
636                                 ePtr<gPixmap> &pixmap =
637                                         (e == celFolderPixmap) ? m_pixmaps[picFolder] :
638                                         (e == celMarkerPixmap) ? m_pixmaps[picMarker] :
639                                         (m_cursor->flags & eServiceReference::isGroup) ? m_pixmaps[picServiceGroup] :
640                                         (orbpos == 0xFFFF) ? m_pixmaps[picDVB_C] :
641                                         (orbpos == 0xEEEE) ? m_pixmaps[picDVB_T] : m_pixmaps[picDVB_S];
642                                 if (pixmap)
643                                 {
644                                         eSize pixmap_size = pixmap->size();
645                                         int p = celServiceInfo;
646                                         if (e == celFolderPixmap)
647                                                 p = celServiceName;
648                                         else if (e == celMarkerPixmap)
649                                                 p = celServiceNumber;
650                                         eRect area = m_element_position[p];
651                                         int correction = (area.height() - pixmap_size.height()) / 2;
652
653                                         if (isPlayable)
654                                         {
655                                                 if (e != celServiceTypePixmap)
656                                                         continue;
657                                                 m_element_position[celServiceInfo] = area;
658                                                 m_element_position[celServiceInfo].setLeft(area.left() + pixmap_size.width() + 8);
659                                                 m_element_position[celServiceInfo].setWidth(area.width() - pixmap_size.width() - 8);
660                                         }
661                                         else if (m_cursor->flags & eServiceReference::isDirectory)
662                                         {
663                                                 if (e != celFolderPixmap)
664                                                         continue;
665                                                 xoffset = pixmap_size.width() + 8;
666                                         }
667                                         else if (m_cursor->flags & eServiceReference::isMarker)
668                                         {
669                                                 if (e != celMarkerPixmap)
670                                                         continue;
671                                         }
672                                         else
673                                                 eFatal("unknown service type in listboxservice");
674
675                                         area.moveBy(offset);
676                                         painter.clip(area);
677                                         painter.blit(pixmap, offset+ePoint(area.left(), correction), area, gPainter::BT_ALPHATEST);
678                                         painter.clippop();
679                                 }
680                         }
681                 }
682                 
683                 if (selected && (!local_style || !local_style->m_selection))
684                         style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry);
685         }
686         
687         painter.clippop();
688 }
689
690 void eListboxServiceContent::setIgnoreService( const eServiceReference &service )
691 {
692         m_is_playable_ignore=service;
693         if (m_listbox && m_listbox->isVisible())
694                 m_listbox->invalidate();
695 }
696
697 void eListboxServiceContent::setItemHeight(int height)
698 {
699         m_itemheight = height;
700         if (m_listbox)
701                 m_listbox->setItemHeight(height);
702 }