612a80ee304db007418e4c0a470269e7bd80a4b1
[vuplus_dvbapp] / lib / gui / eskin.cpp
1 #include <stdio.h>
2 #include <errno.h>
3 #include <stdlib.h>
4
5 #include <lib/gui/eskin.h>
6 #include <lib/gui/ewidget.h>
7 #include <lib/gdi/gfbdc.h>
8 #include <lib/gdi/glcddc.h>
9 #include <lib/gdi/epng.h>
10 #include <lib/base/eerror.h>
11 #include <lib/gdi/font.h>
12 #include <lib/base/eptrlist.h>
13
14 std::map< eString,tWidgetCreator > eSkin::widget_creator;
15
16 eSkin *eSkin::active;
17
18 eNamedColor *eSkin::searchColor(const eString &name)
19 {
20         for (std::list<eNamedColor>::iterator i(colors.begin()); i != colors.end(); ++i)
21         {
22                 if (!i->name.compare(name))
23                         return &*i;
24         }
25         return 0;
26 }
27
28 void eSkin::clear()
29 {
30 }
31
32 void eSkin::addWidgetCreator(const eString &name, tWidgetCreator creator)
33 {
34         widget_creator[name] = creator; // add this tWidgetCreator to map... if exist.. overwrite
35 }
36
37 void eSkin::removeWidgetCreator(const eString &name, tWidgetCreator creator)
38 {
39         widget_creator.erase(name);
40 }
41
42 int eSkin::parseColor(const eString &name, const char* color, gRGB &col)
43 {
44         if (color[0]=='#')
45         {
46                 unsigned long vcol=0;
47                 if (sscanf(color+1, "%lx", &vcol)!=1)
48                 {
49                         eDebug("invalid color named \"%s\" (value: %s)", name.c_str(), color+1);
50                         return -1;
51                 }
52                 col.r=(vcol>>16)&0xFF;
53                 col.g=(vcol>>8)&0xFF;
54                 col.b=vcol&0xFF;
55                 col.a=(vcol>>24)&0xFF;
56         } else
57         {
58                 eNamedColor *n=searchColor(color);
59                 if (!n)
60                 {
61                         eDebug("invalid color named \"%s\" (alias to: \"%s\")", name.c_str(), color);
62                         return -1;
63                 }
64                 col=n->value;
65         }
66         return 0;
67 }
68
69 int eSkin::parseColors(XMLTreeNode *xcolors)
70 {
71         XMLTreeNode *node;
72         
73         std::list<eNamedColor>::iterator newcolors=colors.end();
74         
75         for (node=xcolors->GetChild(); node; node=node->GetNext())
76         {
77                 if (strcmp(node->GetType(), "color"))
78                 {
79                         eDebug("junk found in colorsection (%s)", node->GetType());
80                         continue;
81                 }
82                 
83                 const char *name=node->GetAttributeValue("name"), *color=node->GetAttributeValue("color"), *end=node->GetAttributeValue("end");
84
85                 if (!color || !name)
86                 {
87                         eDebug("no color/name specified");
88                         continue;
89                 }
90
91                 eNamedColor col;
92                 col.name=name;
93
94                 const char *size=node->GetAttributeValue("size");
95
96                 if (size)
97                         col.size=atoi(size);
98                 else
99                         col.size=0;
100                 
101                 if (!col.size)
102                         col.size=1;
103                 
104                 if ((col.size>1) && (!end))
105                 {
106                         eDebug("no end specified in \"%s\" but is gradient", name);
107                         continue;
108                 }
109
110                 if (parseColor(name, color, col.value))
111                         continue;
112
113                 if (end && parseColor(name, end, col.end))
114                         continue;
115
116                 colors.push_back(col);
117                 if (newcolors == colors.end())
118                         --newcolors;
119         }
120         
121         for (std::list<eNamedColor>::iterator i(newcolors); i != colors.end(); ++i)
122         {
123                 eNamedColor &col=*i;
124                 int d;
125                 for (d=0; d<maxcolors; d+=col.size)
126                 {
127                         int s;
128                         for (s=0; s<col.size; s++)
129                                 if ((d+s>maxcolors) || colorused[d+s])
130                                         break;
131                         if (s==col.size)
132                                 break;
133                 }
134                 if (d==maxcolors)
135                         continue;
136                 col.index=gColor(d);
137                 for (int s=0; s<col.size; s++, d++)
138                 {
139                         colorused[d]=1;
140                         if (s)
141                         {
142                                 int rdiff=-col.value.r+col.end.r;
143                                 int gdiff=-col.value.g+col.end.g;
144                                 int bdiff=-col.value.b+col.end.b;
145                                 int adiff=-col.value.a+col.end.a;
146                                 rdiff*=s; rdiff/=(col.size-1);
147                                 gdiff*=s; gdiff/=(col.size-1);
148                                 bdiff*=s; bdiff/=(col.size-1);
149                                 adiff*=s; adiff/=(col.size-1);
150                                 palette[d].r=col.value.r+rdiff;
151                                 palette[d].g=col.value.g+gdiff;
152                                 palette[d].b=col.value.b+bdiff;
153                                 palette[d].a=col.value.a+adiff;
154                         } else
155                                 palette[d]=col.value;
156                 }
157         }
158         return 0;
159 }
160
161 int eSkin::parseScheme(XMLTreeNode *xscheme)
162 {
163         XMLTreeNode *node;
164         for (node=xscheme->GetChild(); node; node=node->GetNext())
165         {
166                 if (strcmp(node->GetType(), "map"))
167                 {
168                         eDebug("illegal scheme entry found: %s", node->GetType());
169                         continue;
170                 }
171                 char *name=node->GetAttributeValue("name"), *color=node->GetAttributeValue("color");
172                 if (!name || !color)
173                 {
174                         eDebug("no name or color specified in colorscheme");
175                         continue;
176                 }
177                 eString base=color;
178                 int offset=0, p;
179                 if ((p=base.find('+'))!=-1)
180                 {
181                         offset=atoi(base.mid(p).c_str());
182                         base=base.left(p);
183                 }
184                 eNamedColor *n=searchColor(base);
185                 if (!n)
186                 {
187                         eDebug("illegal color \"%s\" specified", base.c_str());
188                         continue;
189                 }
190                 scheme[name] = gColor(n->index+offset);
191         }
192         return 0;
193 }
194
195 int eSkin::parseFontAlias(XMLTreeNode *xscheme)
196 {
197         XMLTreeNode *node;
198         for (node=xscheme->GetChild(); node; node=node->GetNext())
199         {
200                 if (strcmp(node->GetType(), "map"))
201                 {
202                         eDebug("illegal fontalias entry found: %s", node->GetType());
203                         continue;
204                 }
205                 char *font=node->GetAttributeValue("font"),
206                                  *name=node->GetAttributeValue("name"),
207                                  *size=node->GetAttributeValue("size");
208
209                 if (!name || !font || !size)
210                 {
211                         eDebug("no name, alias or size spezified in fontaliase");
212                         continue;
213                 }
214
215                 std::map<eString, gFont>::iterator it = fontAlias.find(name);
216                 if (it != fontAlias.end())
217                         continue;
218
219                 std::map<eString, eString>::iterator i = fonts.find(font);
220                 if (i == fonts.end())
221                 {
222                         eDebug("font %s not found, skip make alias %s", font, name);
223                         continue;
224                 }
225                 fontAlias[name]=gFont(i->second, atoi(size));
226         }
227         return 0;
228 }
229
230 int eSkin::parseImages(XMLTreeNode *inode)
231 {
232         char *abasepath=inode->GetAttributeValue("basepath");
233         if (!abasepath)
234                 abasepath="";
235         eString basepath=eString("/enigma/pictures/");
236         if (abasepath[0] == '/') // allow absolute paths
237                 basepath="";
238         basepath+=abasepath;
239         if (basepath[basepath.length()-1]!='/')
240                 basepath+="/";
241
242         for (XMLTreeNode *node=inode->GetChild(); node; node=node->GetNext())
243         {
244                 if (strcmp(node->GetType(), "img"))
245                 {
246                         eDebug("illegal image entry found: %s", node->GetType());
247                         continue;
248                 }
249                 const char *name=node->GetAttributeValue("name");
250                 if (!name)
251                 {
252                         eDebug("illegal <img> entry: no name");
253                         continue;
254                 }
255                 const char *src=node->GetAttributeValue("src");
256                 if (!src)
257                 {
258                         eDebug("image/img=\"%s\" no src given", name);
259                         continue;
260                 }
261                 std::map<eString, ePtr<gPixmap> >::iterator it = images.find(name);
262                 if (it != images.end())
263                 {
264 //                      eDebug("Image with name %s already loaded, skip %s", name, src);
265                         continue;
266                 }
267                 ePtr<gPixmap> image=0;
268                 eString filename=basepath + eString(src);
269                 if (abasepath[0] != '/')
270                 {
271                         // search first in CONFIGDIR
272                         image=loadPNG((eString(CONFIGDIR)+filename).c_str());
273                         if (!image)
274                                 image=loadPNG((eString(DATADIR)+filename).c_str());
275                 }
276                 else // abs path
277                         image=loadPNG(filename.c_str());
278
279                 if (!image)
280                 {
281                         eDebug("image/img=\"%s\" - %s: file not found", name, filename.c_str());
282                         continue;
283                 }
284
285                 if (paldummy && !node->GetAttributeValue("nomerge"))
286                 {
287                         gPixmapDC mydc(image);
288                         gPainter p(mydc);
289                         p.mergePalette(paldummy);
290                 }
291                 images[name] = image;
292         }
293         return 0;
294 }
295
296 int eSkin::parseImageAlias(XMLTreeNode *xvalues)
297 {
298         for (XMLTreeNode *node=xvalues->GetChild(); node; node=node->GetNext())
299         {
300                 if (strcmp(node->GetType(), "map"))
301                 {
302                         eDebug("illegal values entry %s", node->GetType());
303                         continue;
304                 }
305                 const char *name=node->GetAttributeValue("name"),
306                                                          *img=node->GetAttributeValue("img");
307                 if (!name || !img)
308                 {
309                         eDebug("map entry has no name or img");
310                         continue;
311                 }
312                 std::map<eString, eString>::iterator it = imageAlias.find(name);
313                 if (it != imageAlias.end())
314                 {
315                         eDebug("imagealias %s does exist, skip make alias for image %s", name, img);
316                         continue;
317                 }
318                 std::map<eString, ePtr<gPixmap> >::iterator i = images.find(img);
319                 if (i == images.end())
320                 {
321                         eDebug("image %s not found, skip make alias %s", img , name);
322                         continue;
323                 }
324                 imageAlias[name]=img;
325         }
326         return 0;
327 }
328
329 int eSkin::parseFonts(XMLTreeNode *xfonts)
330 {
331         const char *abasepath=xfonts->GetAttributeValue("basepath");
332         eString basepath=abasepath?abasepath:FONTDIR;
333
334         if (basepath.length())
335                 if (basepath[basepath.length()-1]!='/')
336                         basepath+="/";
337
338         for (XMLTreeNode *node=xfonts->GetChild(); node; node=node->GetNext())
339         {
340                 if (strcmp(node->GetType(), "font"))
341                 {
342                         eDebug("illegal fonts entry %s", node->GetType());
343                         continue;
344                 }
345                 const char *file=node->GetAttributeValue("file");
346                 if (!file)
347                 {
348                         eDebug("fonts entry has no file");
349                         continue;
350                 }
351                 const char *name=node->GetAttributeValue("name");
352                 if (!name)
353                 {
354                         eDebug("fonts entry has no name use filename %s as name", file);
355                         name = file;
356                 }
357                 std::map<eString, eString>::iterator it = fonts.find(name);
358                 const char *ascale=node->GetAttributeValue("scale");
359                 int scale=0;
360                 if (ascale)
361                         scale=atoi(ascale);
362                 if (!scale)
363                         scale=100;
364                 if (it != fonts.end())
365                 {
366                         eDebug("Font with name %s already loaded, skip %s", name, file);
367                         continue;
368                 }
369                 fonts[name]=fontRenderClass::getInstance()->AddFont(basepath+eString(file), name, scale);
370                 if (node->GetAttributeValue("replacement"))
371                         eTextPara::setReplacementFont(name);
372         }
373         return 0;
374 }
375
376 int eSkin::parseValues(XMLTreeNode *xvalues)
377 {
378         for (XMLTreeNode *node=xvalues->GetChild(); node; node=node->GetNext())
379         {
380                 if (strcmp(node->GetType(), "value"))
381                 {
382                         eDebug("illegal values entry %s", node->GetType());
383                         continue;
384                 }
385                 const char *name=node->GetAttributeValue("name");
386                 if (!name)
387                 {
388                         eDebug("values entry has no name");
389                         continue;
390                 }
391                 const char *value=node->GetAttributeValue("value");
392                 if (!value)
393                 {
394                         eDebug("values entry has no value");
395                         continue;
396                 }
397                 std::map<eString, int>::iterator it = values.find(name);
398                 if (it != values.end())
399                 {
400                         eDebug("value %s does exist, skip make value %s=%i", name, value);
401                         continue;
402                 }
403                 values[name]=atoi(value);
404         }
405         return 0;
406 }
407
408 gDC *eSkin::getDCbyName(const char *name)
409 {
410         gPixmapDC *dc=0;
411         if (!strcmp(name, "fb"))
412                 dc=gFBDC::getInstance();
413 #ifndef DISABLE_LCD
414         else if (!strcmp(name, "lcd"))
415                 dc=gLCDDC::getInstance();
416 #endif
417         return dc;
418 }
419
420 int eSkin::build(eWidget *widget, XMLTreeNode *node)
421 {
422 //       eDebug("building a %s", node->GetType());
423 /*       if (widget->getType() != node->GetType())
424                         return -1;*/
425         
426         for (XMLAttribute *attrib=node->GetAttributes(); attrib; attrib=attrib->GetNext())
427         {
428 //              eDebug("setting %s := %s", attrib->GetName(), attrib->GetValue());
429                 if (widget->setProperty(attrib->GetName(), attrib->GetValue()))
430                 {
431                         eDebug("failed");
432                         return -1;
433                 }
434         }
435         for (XMLTreeNode *c=node->GetChild(); c; c=c->GetNext())
436         {
437                 eWidget *w=0;
438
439                 const char *name=c->GetAttributeValue("name");
440
441                 if (name)
442                         w=widget->search(name);
443
444                 if (!w)
445                 {
446                         std::map< eString, tWidgetCreator >::iterator it = widget_creator.find(c->GetType());
447
448                         if ( it == widget_creator.end() )
449                         {
450                                 eWarning("widget class %s does not exist", c->GetType());
451                                 return -ENOENT;
452                         }
453                         w = (it->second)(widget);
454                 }
455                 if (!w)
456                 {
457                         // eDebug("failed.");
458                         return -EINVAL;
459                 }
460                 w->zOrderRaise();
461                 int err;
462                 if ((err=build(w, c)))
463                 {
464                         return err;
465                 }
466         }
467         return 0;
468 }
469
470 eSkin::eSkin()
471 {
472         maxcolors=256;
473
474         palette=new gRGB[maxcolors];
475         
476         memset(palette, 0, sizeof(gRGB)*maxcolors);
477         paldummy=new gImage(eSize(1, 1), 8);
478         paldummy->clut.data=palette;
479         paldummy->clut.colors=maxcolors;
480
481         colorused=new int[maxcolors];
482         memset(colorused, 0, maxcolors*sizeof(int));
483 }
484
485 eSkin::~eSkin()
486 {
487         if (active==this)
488                 active=0;
489
490         clear();
491
492         delete colorused;
493
494         for (std::map<eString, ePtr<gPixmap> >::iterator it(images.begin()); it != images.end(); it++)
495                 delete it->second;      
496 }
497
498 int eSkin::load(const char *filename)
499 {
500         eDebug("loading skin: %s", filename);
501         FILE *in=fopen(filename, "rt");
502         if (!in)
503                 return -1;
504
505         parsers.push_front(new XMLTreeParser("ISO-8859-1"));
506         XMLTreeParser &parser=*parsers.first();
507         char buf[2048];
508
509         int done;
510         do
511         {
512                 unsigned int len=fread(buf, 1, sizeof(buf), in);
513                 done=len<sizeof(buf);
514                 if (!parser.Parse(buf, len, done))
515                 {
516                         eDebug("parse error: %s at line %d",
517                                 parser.ErrorString(parser.GetErrorCode()),
518                                 parser.GetCurrentLineNumber());
519                         parsers.pop_front();
520                         fclose(in);
521                         return -1;
522                 }
523         } while (!done);
524         fclose(in);
525
526         XMLTreeNode *root=parser.RootNode();
527         if (!root)
528                 return -1;
529         if (strcmp(root->GetType(), "eskin"))
530         {
531                 eDebug("not an eskin");
532                 return -1;
533         }
534         
535         return 0;
536 }
537
538 void eSkin::parseSkins()
539 {
540         for (ePtrList<XMLTreeParser>::reverse_iterator it(parsers); it != parsers.rend(); it++)
541         {
542                 XMLTreeNode *node=it->RootNode();
543         
544                 for (node=node->GetChild(); node; node=node->GetNext())
545                         if (!strcmp(node->GetType(), "colors"))
546                                 parseColors(node);
547          }
548
549         for (ePtrList<XMLTreeParser>::reverse_iterator it(parsers); it != parsers.rend(); it++)
550         {
551                 XMLTreeNode *node=it->RootNode();
552         
553                 for (node=node->GetChild(); node; node=node->GetNext())
554                         if (!strcmp(node->GetType(), "colorscheme"))
555                                 parseScheme(node);
556          }
557
558         for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
559         {
560                 XMLTreeNode *node=it->RootNode();
561         
562                 for (node=node->GetChild(); node; node=node->GetNext())
563                         if (!strcmp(node->GetType(), "fonts"))
564                                 parseFonts(node);
565          }
566
567         for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
568         {
569                 XMLTreeNode *node=it->RootNode();
570         
571                 for (node=node->GetChild(); node; node=node->GetNext())
572                         if (!strcmp(node->GetType(), "fontalias"))
573                                 parseFontAlias(node);
574          }
575
576         for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
577         {
578                 XMLTreeNode *node=it->RootNode();
579         
580                 for (node=node->GetChild(); node; node=node->GetNext())
581                         if (!strcmp(node->GetType(), "images"))
582                                 parseImages(node);
583
584          }
585
586         for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
587         {
588                 XMLTreeNode *node=it->RootNode();
589         
590                 for (node=node->GetChild(); node; node=node->GetNext())
591                         if (!strcmp(node->GetType(), "imagealias"))
592                                 parseImageAlias(node);
593
594          }
595
596         for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
597         {
598                 XMLTreeNode *node=it->RootNode();
599         
600                 for (node=node->GetChild(); node; node=node->GetNext())
601                         if (!strcmp(node->GetType(), "values"))
602                                 parseValues(node);
603         }
604 }
605
606
607 int eSkin::build(eWidget *widget, const char *name)
608 {
609         for (parserList::iterator i(parsers.begin()); i!=parsers.end(); ++i)
610         {
611                 XMLTreeNode *node=i->RootNode();
612                         node=node->GetChild();
613                 while (node)
614                 {
615                         if (!strcmp(node->GetType(), "object"))
616                         {
617                                 const char *oname=node->GetAttributeValue("name");
618                                 if (!std::strcmp(name, oname))
619                                 {
620                                         node=node->GetChild();
621                                         return build(widget, node);
622                                 }
623                         }
624                         node=node->GetNext();
625                 }
626         }
627         eDebug("didn't found it");
628         return -ENOENT;
629 }
630
631 void eSkin::setPalette(gPixmapDC *pal)
632 {
633         if (palette)
634         {
635                 gPainter p(*pal);
636                 p.setPalette(palette, 0, 256);
637         }
638 }
639
640 eSkin *eSkin::getActive()
641 {
642         if (!active)
643                 eFatal("no active skin");
644         return active;
645 }
646
647 void eSkin::makeActive()
648 {
649         active=this;
650 }
651
652 gColor eSkin::queryScheme(const eString& name) const
653 {
654         eString base=name;
655         int offset=0, p;
656         if ((p=base.find('+'))!=-1)
657         {
658                 offset=atoi(base.mid(p).c_str());
659                 base=base.left(p);
660         }
661
662         std::map<eString, gColor>::const_iterator it = scheme.find(base);
663
664         if (it != scheme.end())
665                 return it->second + offset;
666
667 //      eWarning("%s does not exist", name.c_str());
668         
669         return gColor(0);
670 }
671
672 RESULT eSkin::queryImage(ePtr<gPixmap> &ptr, const eString& name) const
673 {
674         eString img;
675
676         std::map<eString, eString>::const_iterator i = imageAlias.find(name);
677                 
678         if (i != imageAlias.end())
679                 img = i->second;
680         else
681                 img = name;
682
683         std::map<eString, ePtr<gPixmap> >::const_iterator it = images.find(img);
684
685         if (it != images.end())
686                 ptr = it->second;
687         
688         return 0;
689 }
690
691 int eSkin::queryValue(const eString& name, int d) const
692 {
693         std::map<eString, int>::const_iterator it = values.find(name);
694
695         if (it != values.end())
696                 return it->second;
697         
698         return d;
699 }
700
701 gColor eSkin::queryColor(const eString& name)
702 {
703         char *end;
704
705         int numcol=strtol(name.c_str(), &end, 10);
706
707         if (!*end)
708                 return gColor(numcol);
709
710         eString base=name;
711         int offset=0, p;
712         if ((p=base.find('+'))!=-1)
713         {
714                 offset=atoi(base.mid(p).c_str());
715                 base=base.left(p);
716         }
717
718         eNamedColor *col=searchColor(base);
719
720         if (!col)
721         {
722                 return queryScheme(name);
723         } else
724                 return col->index + offset;
725 }
726
727 gFont eSkin::queryFont(const eString& name)
728 {
729         std::map<eString, gFont>::iterator it = fontAlias.find(name);  // check if name is a font alias
730         
731         if ( it != fontAlias.end() )            // font alias found
732                 return it->second;
733
734         eString family;
735         int size=0;
736
737         unsigned int sem = name.rfind(';');             // check if exist ';' in name
738         if (sem != eString::npos)                                               // then exist
739         {
740                 family=name.left(sem);          
741                 size = atoi( name.mid(sem+1).c_str() );
742                 if (size<=0)
743                         size=16;
744         }
745         
746         std::map<eString, eString>::iterator i = fonts.find(family);   // check if family is a font name
747         if ( i != fonts.end() ) // font exist
748                 return gFont(i->second, size);
749
750         for (i = fonts.begin() ; i != fonts.end(); i++)                         // as last check if family name is a complete font Face
751                 if ( i->second == family)
752                         return gFont(i->second, size);
753
754         eFatal("Font %s does not exist", name.c_str() );                        //  halt Programm now... Font does not exist
755
756         return gFont();
757 }