cvs20080628 - trunk
[gitenigma.git] / lib / gui / eskin.cpp
blob922324c052257d25edd33be501ed317513b9302a
1 #include <stdio.h>
2 #include <errno.h>
3 #include <stdlib.h>
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>
15 * Notes on images/colors:
17 * When loading images, the colors they use are parsed, and added to our palette.
18 * Because we only have room for 256 colors in our 8bit palette, we cannot simply
19 * allocate all required colors for every image.
20 * Therefore, only colors that are farther apart than a certain 'maxcolordistance',
21 * are added to the palette.
23 * Colors defined in the skin are always added to the palette first, and the remaining
24 * free slots in the palette are used for image colors.
25 * Images are parsed in the order in which they appear in the skin file, until
26 * the palette is completely filled.
28 * Skinmakers can influence color allocation in the following ways:
30 * -change the order in which the images are placed in the skin file
31 * -define a 'maxcolordistance' per image. When omitted, the maximum color distance
32 * defaults to 15500
34 * or the hard way:
36 * -explicitly list all colors that need to be allocated
39 std::map< eString,tWidgetCreator > eSkin::widget_creator;
41 eSkin *eSkin::active;
43 eNamedColor *eSkin::searchColor(const eString &name)
45 for (std::list<eNamedColor>::iterator i(colors.begin()); i != colors.end(); ++i)
47 if (!i->name.compare(name))
48 return &*i;
50 return 0;
53 int eSkin::findColorDistance(const gRGB &rgb)
55 int difference = 1 << 30;
56 for (int t = 0; t < maxcolors; t++)
58 if (!colorused[t]) break;
59 int ttd;
60 int td = (signed)(rgb.r - palette[t].r); td *= td; td *= (255 - palette[t].a);
61 ttd = td;
62 if (ttd >= difference) continue;
63 td = (signed)(rgb.g - palette[t].g); td *= td; td *= (255 - palette[t].a);
64 ttd += td;
65 if (ttd >= difference) continue;
66 td = (signed)(rgb.b - palette[t].b); td *= td; td *= (255 - palette[t].a);
67 ttd += td;
68 if (ttd >= difference) continue;
69 td = (signed)(rgb.a - palette[t].a); td *= td; td *= 255;
70 ttd += td;
71 if (ttd >= difference) continue;
72 difference = ttd;
74 return difference;
77 void eSkin::clear()
81 void eSkin::addWidgetCreator(const eString &name, tWidgetCreator creator)
83 widget_creator[name] = creator; // add this tWidgetCreator to map... if exist.. overwrite
86 void eSkin::removeWidgetCreator(const eString &name, tWidgetCreator creator)
88 widget_creator.erase(name);
91 int eSkin::parseColor(const eString &name, const char* color, gRGB &col)
93 if (color[0]=='#')
95 unsigned long vcol=0;
96 if (sscanf(color+1, "%lx", &vcol)!=1)
98 eDebug("invalid color named \"%s\" (value: %s)", name.c_str(), color+1);
99 return -1;
101 col.r=(vcol>>16)&0xFF;
102 col.g=(vcol>>8)&0xFF;
103 col.b=vcol&0xFF;
104 col.a=(vcol>>24)&0xFF;
105 } else
107 eNamedColor *n=searchColor(color);
108 if (!n)
110 eDebug("invalid color named \"%s\" (alias to: \"%s\")", name.c_str(), color);
111 return -1;
113 col=n->value;
115 return 0;
118 int eSkin::parseColors(XMLTreeNode *xcolors)
120 XMLTreeNode *node;
122 std::list<eNamedColor>::iterator newcolors=colors.end();
124 for (node=xcolors->GetChild(); node; node=node->GetNext())
126 if (strcmp(node->GetType(), "color"))
128 eDebug("junk found in colorsection (%s)", node->GetType());
129 continue;
132 const char *name=node->GetAttributeValue("name"), *color=node->GetAttributeValue("color"), *end=node->GetAttributeValue("end");
134 if (!color || !name)
136 eDebug("no color/name specified");
137 continue;
140 eNamedColor col;
141 col.name=name;
143 const char *size=node->GetAttributeValue("size");
145 if (size)
146 col.size=atoi(size);
147 else
148 col.size=0;
150 if (!col.size)
151 col.size=1;
153 if ((col.size>1) && (!end))
155 eDebug("no end specified in \"%s\" but is gradient", name);
156 continue;
159 if (parseColor(name, color, col.value))
160 continue;
162 if (end && parseColor(name, end, col.end))
163 continue;
165 colors.push_back(col);
166 if (newcolors == colors.end())
167 --newcolors;
170 for (std::list<eNamedColor>::iterator i(newcolors); i != colors.end(); ++i)
172 eNamedColor &col=*i;
173 int d;
174 for (d=0; d<maxcolors; d+=col.size)
176 int s;
177 for (s=0; s<col.size; s++)
178 if ((d+s>maxcolors) || colorused[d+s])
179 break;
180 if (s==col.size)
181 break;
183 if (d==maxcolors)
184 continue;
185 col.index=gColor(d);
186 for (int s=0; s<col.size; s++, d++)
188 colorused[d]=1;
189 if (s)
191 int rdiff=-col.value.r+col.end.r;
192 int gdiff=-col.value.g+col.end.g;
193 int bdiff=-col.value.b+col.end.b;
194 int adiff=-col.value.a+col.end.a;
195 rdiff*=s; rdiff/=(col.size-1);
196 gdiff*=s; gdiff/=(col.size-1);
197 bdiff*=s; bdiff/=(col.size-1);
198 adiff*=s; adiff/=(col.size-1);
199 palette[d].r=col.value.r+rdiff;
200 palette[d].g=col.value.g+gdiff;
201 palette[d].b=col.value.b+bdiff;
202 palette[d].a=col.value.a+adiff;
203 } else
204 palette[d]=col.value;
207 return 0;
210 int eSkin::parseScheme(XMLTreeNode *xscheme)
212 XMLTreeNode *node;
213 for (node=xscheme->GetChild(); node; node=node->GetNext())
215 if (strcmp(node->GetType(), "map"))
217 eDebug("illegal scheme entry found: %s", node->GetType());
218 continue;
220 char *name=node->GetAttributeValue("name"), *color=node->GetAttributeValue("color");
221 if (!name || !color)
223 eDebug("no name or color specified in colorscheme");
224 continue;
226 eString base=color;
227 int offset=0, p;
228 if ((p=base.find('+'))!=-1)
230 offset=atoi(base.mid(p).c_str());
231 base=base.left(p);
233 eNamedColor *n=searchColor(base);
234 if (!n)
236 eDebug("illegal color \"%s\" specified", base.c_str());
237 continue;
239 scheme[name] = gColor(n->index+offset);
241 return 0;
244 int eSkin::parseFontAlias(XMLTreeNode *xscheme)
246 XMLTreeNode *node;
247 for (node=xscheme->GetChild(); node; node=node->GetNext())
249 if (strcmp(node->GetType(), "map"))
251 eDebug("illegal fontalias entry found: %s", node->GetType());
252 continue;
254 char *font=node->GetAttributeValue("font"),
255 *name=node->GetAttributeValue("name"),
256 *size=node->GetAttributeValue("size");
258 if (!name || !font || !size)
260 eDebug("no name, alias or size spezified in fontaliase");
261 continue;
264 std::map<eString, gFont>::iterator it = fontAlias.find(name);
265 if (it != fontAlias.end())
267 eDebug("fontalias %s does exist, skip make alias for font %s", name, font);
268 continue;
271 std::map<eString, eString>::iterator i = fonts.find(font);
272 if (i == fonts.end())
274 eDebug("font %s not found, skip make alias %s", font, name);
275 continue;
277 fontAlias[name]=gFont(i->second, atoi(size));
279 return 0;
282 int eSkin::parseImages(XMLTreeNode *inode)
284 char *abasepath=inode->GetAttributeValue("basepath");
285 if (!abasepath)
286 abasepath="";
287 eString basepath=eString("/enigma/pictures/");
288 if (abasepath[0] == '/') // allow absolute paths
289 basepath="";
290 basepath+=abasepath;
291 if (basepath[basepath.length()-1]!='/')
292 basepath+="/";
294 int d;
295 for (d = 0; d < maxcolors; d++)
297 if (!colorused[d]) break;
300 for (XMLTreeNode *node=inode->GetChild(); node; node=node->GetNext())
302 if (strcmp(node->GetType(), "img"))
304 eDebug("illegal image entry found: %s", node->GetType());
305 continue;
307 const char *name=node->GetAttributeValue("name");
308 if (!name)
310 eDebug("illegal <img> entry: no name");
311 continue;
313 const char *src=node->GetAttributeValue("src");
314 if (!src)
316 eDebug("image/img=\"%s\" no src given", name);
317 continue;
319 std::map<eString, gPixmap*>::iterator it = images.find(name);
320 if (it != images.end())
322 eDebug("Image with name %s already loaded, skip %s", name, src);
323 continue;
325 gPixmap *image=0;
326 eString filename=basepath + eString(src);
327 if (abasepath[0] != '/')
329 // search first in CONFIGDIR
330 image=loadPNG((eString(CONFIGDIR)+filename).c_str());
331 if (!image)
332 image=loadPNG((eString(TUXBOXDATADIR)+filename).c_str());
334 else // abs path
335 image=loadPNG(filename.c_str());
337 if (!image)
339 eDebug("image/img=\"%s\" - %s: file not found", name, filename.c_str());
340 continue;
343 if (d < maxcolors)
345 int maxcolordistance = 15500;
346 char *distance = node->GetAttributeValue("maxcolordistance");
347 if (distance) maxcolordistance = atoi(distance);
349 /* add the image's colors to our palette, but only if they are not too close to colors we already have */
350 for (int i = 0; i < image->clut.colors; i++)
352 if (findColorDistance(image->clut.data[i]) > maxcolordistance)
354 colorused[d] = 1;
355 palette[d++] = image->clut.data[i];
356 if (d >= maxcolors) break;
360 if (paldummy && !node->GetAttributeValue("nomerge"))
362 gPixmapDC mydc(image);
363 gPainter p(mydc);
364 p.mergePalette(*paldummy);
366 images[name] = image;
367 image->cancompress = true;
368 image->compressdata();
370 return 0;
373 int eSkin::parseImageAlias(XMLTreeNode *xvalues)
375 for (XMLTreeNode *node=xvalues->GetChild(); node; node=node->GetNext())
377 if (strcmp(node->GetType(), "map"))
379 eDebug("illegal values entry %s", node->GetType());
380 continue;
382 const char *name=node->GetAttributeValue("name"),
383 *img=node->GetAttributeValue("img");
384 if (!name || !img)
386 eDebug("map entry has no name or img");
387 continue;
389 std::map<eString, eString>::iterator it = imageAlias.find(name);
390 if (it != imageAlias.end())
392 eDebug("imagealias %s does exist, skip make alias for image %s", name, img);
393 continue;
395 std::map<eString, gPixmap*>::iterator i = images.find(img);
396 if (i == images.end())
398 eDebug("image %s not found, skip make alias %s", img , name);
399 continue;
401 imageAlias[name]=img;
403 return 0;
406 int eSkin::parseFonts(XMLTreeNode *xfonts)
408 const char *abasepath=xfonts->GetAttributeValue("basepath");
409 eString basepath=abasepath?abasepath:FONTDIR;
411 if (basepath.length())
412 if (basepath[basepath.length()-1]!='/')
413 basepath+="/";
415 for (XMLTreeNode *node=xfonts->GetChild(); node; node=node->GetNext())
417 if (strcmp(node->GetType(), "font"))
419 eDebug("illegal fonts entry %s", node->GetType());
420 continue;
422 const char *file=node->GetAttributeValue("file");
423 if (!file)
425 eDebug("fonts entry has no file");
426 continue;
428 const char *name=node->GetAttributeValue("name");
429 if (!name)
431 eDebug("fonts entry has no name use filename %s as name", file);
432 name = file;
434 std::map<eString, eString>::iterator it = fonts.find(name);
435 const char *ascale=node->GetAttributeValue("scale");
436 int scale=0;
437 if (ascale)
438 scale=atoi(ascale);
439 if (!scale)
440 scale=100;
441 if (it != fonts.end())
443 eDebug("Font with name %s already loaded, skip %s", name, file);
444 continue;
446 fonts[name]=fontRenderClass::getInstance()->AddFont(basepath+eString(file), name, scale);
447 if (node->GetAttributeValue("replacement"))
448 eTextPara::setReplacementFont(name);
450 return 0;
453 int eSkin::parseValues(XMLTreeNode *xvalues)
455 for (XMLTreeNode *node=xvalues->GetChild(); node; node=node->GetNext())
457 if (strcmp(node->GetType(), "value"))
459 eDebug("illegal values entry %s", node->GetType());
460 continue;
462 const char *name=node->GetAttributeValue("name");
463 if (!name)
465 eDebug("values entry has no name");
466 continue;
468 const char *value=node->GetAttributeValue("value");
469 if (!value)
471 eDebug("values entry has no value");
472 continue;
474 std::map<eString, int>::iterator it = values.find(name);
475 if (it != values.end())
477 eDebug("value %s does exist, skip make value %s=%i", name, value);
478 continue;
480 values[name]=atoi(value);
482 return 0;
485 gDC *eSkin::getDCbyName(const char *name)
487 gPixmapDC *dc=0;
488 if (!strcmp(name, "fb"))
489 dc=gFBDC::getInstance();
490 #ifndef DISABLE_LCD
491 else if (!strcmp(name, "lcd"))
492 dc=gLCDDC::getInstance();
493 #endif
494 return dc;
497 int eSkin::build(eWidget *widget, XMLTreeNode *node)
499 // eDebug("building a %s", node->GetType());
500 /* if (widget->getType() != node->GetType())
501 return -1;*/
503 for (XMLAttribute *attrib=node->GetAttributes(); attrib; attrib=attrib->GetNext())
505 // eDebug("setting %s := %s", attrib->GetName(), attrib->GetValue());
506 if (widget->setProperty(attrib->GetName(), attrib->GetValue()))
508 eDebug("failed");
509 return -1;
512 for (XMLTreeNode *c=node->GetChild(); c; c=c->GetNext())
514 eWidget *w=0;
516 const char *name=c->GetAttributeValue("name");
518 if (name)
519 w=widget->search(name);
521 if (!w)
523 std::map< eString, tWidgetCreator >::iterator it = widget_creator.find(c->GetType());
525 if ( it == widget_creator.end() )
527 eWarning("widget class %s does not exist", c->GetType());
528 return -ENOENT;
530 w = (it->second)(widget);
532 if (!w)
534 // eDebug("failed.");
535 return -EINVAL;
537 w->zOrderRaise();
538 int err;
539 if ((err=build(w, c)))
541 return err;
544 return 0;
547 eSkin::eSkin()
549 maxcolors=256;
551 palette=new gRGB[maxcolors];
553 memset(palette, 0, sizeof(gRGB)*maxcolors);
554 paldummy=new gImage(eSize(1, 1), 8);
555 paldummy->clut.data=palette;
556 paldummy->clut.colors=maxcolors;
558 colorused=new int[maxcolors];
559 memset(colorused, 0, maxcolors*sizeof(int));
562 eSkin::~eSkin()
564 if (active==this)
565 active=0;
567 clear();
569 delete [] colorused;
571 for (std::map<eString, gPixmap*>::iterator it(images.begin()); it != images.end(); ++it)
572 delete it->second;
574 if (paldummy)
575 delete paldummy;
578 int eSkin::load(const char *filename)
580 eDebug("loading skin: %s", filename);
581 FILE *in=fopen(filename, "rt");
582 if (!in)
583 return -1;
585 parsers.push_front(new XMLTreeParser("ISO-8859-1"));
586 parsers.setAutoDelete(true);
587 XMLTreeParser &parser=*parsers.first();
588 char buf[2048];
590 int done;
593 unsigned int len=fread(buf, 1, sizeof(buf), in);
594 done=len<sizeof(buf);
595 if (!parser.Parse(buf, len, done))
597 eDebug("parse error: %s at line %d",
598 parser.ErrorString(parser.GetErrorCode()),
599 parser.GetCurrentLineNumber());
600 parsers.pop_front();
601 fclose(in);
602 return -1;
604 } while (!done);
605 fclose(in);
607 XMLTreeNode *root=parser.RootNode();
608 if (!root)
609 return -1;
610 if (strcmp(root->GetType(), "eskin"))
612 eDebug("not an eskin");
613 return -1;
616 return 0;
619 void eSkin::parseSkins()
621 for (ePtrList<XMLTreeParser>::reverse_iterator it(parsers); it != parsers.rend(); it++)
623 XMLTreeNode *node=it->RootNode();
625 for (node=node->GetChild(); node; node=node->GetNext())
626 if (!strcmp(node->GetType(), "colors"))
627 parseColors(node);
630 for (ePtrList<XMLTreeParser>::reverse_iterator it(parsers); it != parsers.rend(); it++)
632 XMLTreeNode *node=it->RootNode();
634 for (node=node->GetChild(); node; node=node->GetNext())
635 if (!strcmp(node->GetType(), "colorscheme"))
636 parseScheme(node);
639 for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
641 XMLTreeNode *node=it->RootNode();
643 for (node=node->GetChild(); node; node=node->GetNext())
644 if (!strcmp(node->GetType(), "fonts"))
645 parseFonts(node);
648 for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
650 XMLTreeNode *node=it->RootNode();
652 for (node=node->GetChild(); node; node=node->GetNext())
653 if (!strcmp(node->GetType(), "fontalias"))
654 parseFontAlias(node);
657 for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
659 XMLTreeNode *node=it->RootNode();
661 for (node=node->GetChild(); node; node=node->GetNext())
662 if (!strcmp(node->GetType(), "images"))
663 parseImages(node);
667 for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
669 XMLTreeNode *node=it->RootNode();
671 for (node=node->GetChild(); node; node=node->GetNext())
672 if (!strcmp(node->GetType(), "imagealias"))
673 parseImageAlias(node);
677 for (ePtrList<XMLTreeParser>::iterator it(parsers); it != parsers.end(); it++)
679 XMLTreeNode *node=it->RootNode();
681 for (node=node->GetChild(); node; node=node->GetNext())
682 if (!strcmp(node->GetType(), "values"))
683 parseValues(node);
688 int eSkin::build(eWidget *widget, const char *name)
690 for (parserList::iterator i(parsers.begin()); i!=parsers.end(); ++i)
692 XMLTreeNode *node=i->RootNode();
693 node=node->GetChild();
694 while (node)
696 if (!strcmp(node->GetType(), "object"))
698 const char *oname=node->GetAttributeValue("name");
699 if (!std::strcmp(name, oname))
701 node=node->GetChild();
702 return build(widget, node);
705 node=node->GetNext();
708 eDebug("didn't found it");
709 return -ENOENT;
712 void eSkin::setPalette(gPixmapDC *pal)
714 if (palette)
716 gPainter p(*pal);
717 p.setPalette(palette, 0, 256);
721 eSkin *eSkin::getActive()
723 if (!active)
724 eFatal("no active skin");
725 return active;
728 void eSkin::makeActive()
730 active=this;
733 gColor eSkin::queryScheme(const eString& name) const
735 eString base=name;
736 int offset=0, p;
737 if ((p=base.find('+'))!=-1)
739 offset=atoi(base.mid(p).c_str());
740 base=base.left(p);
743 std::map<eString, gColor>::const_iterator it = scheme.find(base);
745 if (it != scheme.end())
746 return it->second + offset;
748 // eWarning("%s does not exist", name.c_str());
750 return gColor(0);
753 gPixmap *eSkin::queryImage(const eString& name) const
755 eString img;
757 std::map<eString, eString>::const_iterator i = imageAlias.find(name);
759 if (i != imageAlias.end())
760 img = i->second;
761 else
762 img = name;
764 std::map<eString, gPixmap*>::const_iterator it = images.find(img);
766 if (it != images.end())
767 return it->second;
769 return 0;
772 int eSkin::queryValue(const eString& name, int d) const
774 std::map<eString, int>::const_iterator it = values.find(name);
776 if (it != values.end())
777 return it->second;
779 return d;
782 gColor eSkin::queryColor(const eString& name)
784 char *end;
786 int numcol=strtol(name.c_str(), &end, 10);
788 if (!*end)
789 return gColor(numcol);
791 eString base=name;
792 int offset=0, p;
793 if ((p=base.find('+'))!=-1)
795 offset=atoi(base.mid(p).c_str());
796 base=base.left(p);
799 eNamedColor *col=searchColor(base);
801 if (!col)
803 return queryScheme(name);
804 } else
805 return col->index + offset;
808 gFont eSkin::queryFont(const eString& name)
810 std::map<eString, gFont>::iterator it = fontAlias.find(name); // check if name is a font alias
812 if ( it != fontAlias.end() ) // font alias found
813 return it->second;
815 eString family;
816 int size=0;
818 unsigned int sem = name.rfind(';'); // check if exist ';' in name
819 if (sem != eString::npos) // then exist
821 family=name.left(sem);
822 size = atoi( name.mid(sem+1).c_str() );
823 if (size<=0)
824 size=16;
827 std::map<eString, eString>::iterator i = fonts.find(family); // check if family is a font name
828 if ( i != fonts.end() ) // font exist
829 return gFont(i->second, size);
831 for (i = fonts.begin() ; i != fonts.end(); i++) // as last check if family name is a complete font Face
832 if ( i->second == family)
833 return gFont(i->second, size);
835 eFatal("Font %s does not exist", name.c_str() ); // halt Programm now... Font does not exist
837 return gFont();