ManaPlus
theme.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2008 The Legend of Mazzeroth Development Team
4  * Copyright (C) 2009 Aethyra Development Team
5  * Copyright (C) 2009 The Mana World Development Team
6  * Copyright (C) 2009-2010 The Mana Developers
7  * Copyright (C) 2011-2019 The ManaPlus Developers
8  * Copyright (C) 2019-2021 Andrei Karas
9  *
10  * This file is part of The ManaPlus Client.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include "gui/theme.h"
27 
28 #include "configuration.h"
29 #include "graphicsmanager.h"
30 
31 #include "const/gui/theme.h"
32 
33 #include "fs/virtfs/fs.h"
34 #include "fs/virtfs/list.h"
35 
36 #include "gui/skin.h"
37 #include "gui/themeinfo.h"
38 
39 #include "resources/imagerect.h"
40 
42 
43 #include "resources/image/image.h"
44 
50 
51 #include "utils/dtor.h"
52 #include "utils/foreach.h"
53 
54 #include "debug.h"
55 
56 static std::string defaultThemePath;
57 
58 std::string Theme::mThemePath;
59 std::string Theme::mThemeName;
60 std::string Theme::mScreenDensity;
61 
62 Theme *theme = nullptr;
63 
64 // Set the theme path...
65 static void initDefaultThemePath()
66 {
67  defaultThemePath = branding.getStringValue("guiThemePath");
68 
69  logger->log("defaultThemePath: " + defaultThemePath);
70  if (!defaultThemePath.empty() &&
72  {
73  return;
74  }
75  defaultThemePath = "themes/";
76 }
77 
79  Palette(CAST_S32(ThemeColorId::THEME_COLORS_END) * THEME_PALETTES),
80  mSkins(),
81  mMinimumOpacity(-1.0F),
82  mProgressColors(ProgressColors(CAST_SIZE(
84 {
86 
87  config.addListener("guialpha", this);
88 
89  mColors[CAST_SIZE(ThemeColorId::HIGHLIGHT)].ch = 'H';
92  mColors[CAST_SIZE(ThemeColorId::GLOBAL)].ch = 'g';
93  mColors[CAST_SIZE(ThemeColorId::PLAYER)].ch = 'Y';
94  mColors[CAST_SIZE(ThemeColorId::WHISPER_TAB)].ch = 'W';
95  mColors[CAST_SIZE(ThemeColorId::WHISPER_TAB_OFFLINE)].ch = 'w';
96  mColors[CAST_SIZE(ThemeColorId::IS)].ch = 'I';
97  mColors[CAST_SIZE(ThemeColorId::PARTY_CHAT_TAB)].ch = 'P';
98  mColors[CAST_SIZE(ThemeColorId::GUILD_CHAT_TAB)].ch = 'U';
99  mColors[CAST_SIZE(ThemeColorId::SERVER)].ch = 'S';
100  mColors[CAST_SIZE(ThemeColorId::LOGGER)].ch = 'L';
101  mColors[CAST_SIZE(ThemeColorId::HYPERLINK)].ch = '<';
102  mColors[CAST_SIZE(ThemeColorId::SELFNICK)].ch = 's';
103  mColors[CAST_SIZE(ThemeColorId::OLDCHAT)].ch = 'o';
104  mColors[CAST_SIZE(ThemeColorId::AWAYCHAT)].ch = 'a';
105  mCharColors['H'] = CAST_S32(ThemeColorId::HIGHLIGHT);
108  mCharColors['g'] = CAST_S32(ThemeColorId::GLOBAL);
109  mCharColors['Y'] = CAST_S32(ThemeColorId::PLAYER);
110  mCharColors['W'] = CAST_S32(ThemeColorId::WHISPER_TAB);
111  mCharColors['w'] = CAST_S32(ThemeColorId::WHISPER_TAB_OFFLINE);
112  mCharColors['I'] = CAST_S32(ThemeColorId::IS);
113  mCharColors['P'] = CAST_S32(ThemeColorId::PARTY_CHAT_TAB);
114  mCharColors['U'] = CAST_S32(ThemeColorId::GUILD_CHAT_TAB);
115  mCharColors['S'] = CAST_S32(ThemeColorId::SERVER);
116  mCharColors['L'] = CAST_S32(ThemeColorId::LOGGER);
117  mCharColors['<'] = CAST_S32(ThemeColorId::HYPERLINK);
118  mCharColors['s'] = CAST_S32(ThemeColorId::SELFNICK);
119  mCharColors['o'] = CAST_S32(ThemeColorId::OLDCHAT);
120  mCharColors['a'] = CAST_S32(ThemeColorId::AWAYCHAT);
121 
122  // here need use outlined colors
123  mCharColors['H' | 0x80]
124  = CAST_S32(ThemeColorId::HIGHLIGHT_OUTLINE);
125  mCharColors['C' | 0x80] = CAST_S32(ThemeColorId::CHAT_OUTLINE);
126  mCharColors['G' | 0x80] = CAST_S32(ThemeColorId::GM_OUTLINE);
127  mCharColors['g' | 0x80] = CAST_S32(ThemeColorId::GLOBAL_OUTLINE);
128  mCharColors['Y' | 0x80] = CAST_S32(ThemeColorId::PLAYER_OUTLINE);
129  mCharColors['W' | 0x80]
130  = CAST_S32(ThemeColorId::WHISPER_TAB_OUTLINE);
131  mCharColors['w' | 0x80]
132  = CAST_S32(ThemeColorId::WHISPER_TAB_OFFLINE_OUTLINE);
133  mCharColors['I' | 0x80] = CAST_S32(ThemeColorId::IS_OUTLINE);
134  mCharColors['P' | 0x80]
135  = CAST_S32(ThemeColorId::PARTY_CHAT_TAB_OUTLINE);
136  mCharColors['U' | 0x80]
137  = CAST_S32(ThemeColorId::GUILD_CHAT_TAB_OUTLINE);
138  mCharColors['S' | 0x80] = CAST_S32(ThemeColorId::SERVER_OUTLINE);
139  mCharColors['L' | 0x80] = CAST_S32(ThemeColorId::LOGGER_OUTLINE);
140  mCharColors['<' | 0x80]
141  = CAST_S32(ThemeColorId::HYPERLINK_OUTLINE);
142  mCharColors['s' | 0x80] = CAST_S32(ThemeColorId::SELFNICK_OUTLINE);
143  mCharColors['o' | 0x80] = CAST_S32(ThemeColorId::OLDCHAT_OUTLINE);
144  mCharColors['a' | 0x80] = CAST_S32(ThemeColorId::AWAYCHAT_OUTLINE);
145 }
146 
148 {
150  config.removeListener("guialpha", this);
153 }
154 
156  const float progress)
157 {
158  int color[3] = {0, 0, 0};
159 
160  if (theme != nullptr)
161  {
162  const DyePalette *const dye
163  = theme->mProgressColors[CAST_SIZE(type)];
164 
165  if (dye != nullptr)
166  {
167  dye->getColor(progress, color);
168  }
169  else
170  {
171  logger->log("color not found: "
172  + toString(CAST_S32(type)));
173  }
174  }
175 
176  return Color(color[0], color[1], color[2], 255U);
177 }
178 
179 Skin *Theme::load(const std::string &filename,
180  const std::string &filename2,
181  const bool full,
182  const std::string &restrict defaultPath)
183 {
184  // Check if this skin was already loaded
185 
186  const SkinIterator skinIterator = mSkins.find(filename);
187  if (mSkins.end() != skinIterator)
188  {
189  if (skinIterator->second != nullptr)
190  skinIterator->second->instances++;
191  return skinIterator->second;
192  }
193 
194  Skin *skin = nullptr;
195  if (mScreenDensity.empty())
196  { // if no density detected
197  skin = readSkin(filename, full);
198  if ((skin == nullptr) && !filename2.empty() && filename2 != filename)
199  skin = readSkin(filename2, full);
200  if ((skin == nullptr) && filename2 != "window.xml")
201  skin = readSkin("window.xml", full);
202  }
203  else
204  { // first use correct density images
205  const std::string endStr("_" + mScreenDensity + ".xml");
206  std::string name = filename;
207  if (findCutLast(name, ".xml"))
208  skin = readSkin(name + endStr, full);
209  if (skin == nullptr)
210  skin = readSkin(filename, full);
211  if ((skin == nullptr) && !filename2.empty() && filename2 != filename)
212  {
213  name = filename2;
214  if (findCutLast(name, ".xml"))
215  skin = readSkin(name + endStr, full);
216  if (skin == nullptr)
217  skin = readSkin(filename2, full);
218  }
219  if ((skin == nullptr) && filename2 != "window.xml")
220  {
221  skin = readSkin("window" + endStr, full);
222  if (skin == nullptr)
223  skin = readSkin("window.xml", full);
224  }
225  }
226 
227  if (skin == nullptr)
228  {
229  // Try falling back on the defaultPath if this makes sense
230  if (filename != defaultPath)
231  {
232  logger->log("Error loading skin '%s', falling back on default.",
233  filename.c_str());
234 
235  skin = readSkin(defaultPath, full);
236  }
237 
238  if (skin == nullptr)
239  {
240  logger->log(strprintf("Error: Loading default skin '%s' failed. "
241  "Make sure the skin file is valid.",
242  defaultPath.c_str()));
243  }
244  }
245 
246  mSkins[filename] = skin;
247  return skin;
248 }
249 
250 void Theme::unload(Skin *const skin)
251 {
252  if (skin == nullptr)
253  return;
254  skin->instances --;
255  if (skin->instances == 0)
256  {
257  SkinIterator it = mSkins.begin();
258  const SkinIterator it_end = mSkins.end();
259  while (it != it_end)
260  {
261  if (it->second == skin)
262  {
263  mSkins.erase(it);
264  break;
265  }
266  ++ it;
267  }
268  delete skin;
269  }
270 }
271 
272 void Theme::setMinimumOpacity(const float minimumOpacity)
273 {
274  if (minimumOpacity > 1.0F)
275  return;
276 
277  mMinimumOpacity = minimumOpacity;
278  updateAlpha();
279 }
280 
282 {
283  FOR_EACH (SkinIterator, iter, mSkins)
284  {
285  Skin *const skin = iter->second;
286  if (skin != nullptr)
288  }
289 }
290 
291 void Theme::optionChanged(const std::string &name A_UNUSED)
292 {
293  updateAlpha();
294 }
295 
297 {
299  int index;
300  std::string name;
301 };
302 
303 static const SkinParameter skinParam[] =
304 {
305  {0, "top-left-corner"},
306  {0, "standart"},
307  {0, "up"},
308  {0, "hstart"},
309  {0, "in"},
310  {0, "normal"},
311  {1, "top-edge"},
312  {1, "highlighted"},
313  {1, "down"},
314  {1, "hmiddle"},
315  {1, "in-highlighted"},
316  {1, "checked"},
317  {2, "top-right-corner"},
318  {2, "pressed"},
319  {2, "left"},
320  {2, "hend"},
321  {2, "out"},
322  {2, "disabled"},
323  {3, "left-edge"},
324  {3, "disabled"},
325  {3, "right"},
326  {3, "hgrip"},
327  {3, "out-highlighted"},
328  {3, "disabled-checked"},
329  {4, "bg-quad"},
330  {4, "vstart"},
331  {4, "normal-highlighted"},
332  {5, "right-edge"},
333  {5, "vmiddle"},
334  {5, "checked-highlighted"},
335  {6, "bottom-left-corner"},
336  {6, "vend"},
337  {7, "bottom-edge"},
338  {7, "vgrip"},
339  {8, "bottom-right-corner"},
340 };
341 
342 static const SkinParameter imageParam[] =
343 {
344  {0, "closeImage"},
345  {1, "closeImageHighlighted"},
346  {2, "stickyImageUp"},
347  {3, "stickyImageDown"},
348 };
349 
351 {
353  partType(),
354  xPos(),
355  yPos(),
356  width(),
357  height(),
358  rect(),
359  node(),
360  image()
361  {
362  }
363 
365 
366  std::string partType;
367  int xPos;
368  int yPos;
369  int width;
370  int height;
371  ImageRect *rect;
372  XmlNodePtr *node;
373  Image *image;
374 
375  bool loadList(const SkinParameter *const params,
376  const size_t size) A_NONNULL(2)
377  {
378  for (size_t f = 0; f < size; f ++)
379  {
380  const SkinParameter &param = params[f];
381  if (partType == param.name)
382  {
383  rect->grid[param.index] = Loader::getSubImage(
384  image,
385  xPos, yPos,
386  width, height);
387  return true;
388  }
389  }
390  return false;
391  }
392 };
393 
394 Skin *Theme::readSkin(const std::string &filename, const bool full)
395 {
396  if (filename.empty())
397  return nullptr;
398 
399  const std::string path = resolveThemePath(filename);
400  if (!VirtFs::exists(path))
401  return nullptr;
402  XML::Document *const doc = Loader::getXml(path,
405  if (doc == nullptr)
406  return nullptr;
407  XmlNodeConstPtr rootNode = doc->rootNode();
408  if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "skinset"))
409  {
410  doc->decRef();
411  return nullptr;
412  }
413 
414  const std::string skinSetImage = XML::getProperty(rootNode, "image", "");
415 
416  if (skinSetImage.empty())
417  {
418  logger->log1("Theme::readSkin(): Skinset does not define an image!");
419  doc->decRef();
420  return nullptr;
421  }
422 
423  Image *const dBorders = Theme::getImageFromTheme(skinSetImage);
424  ImageRect *const border = new ImageRect;
425  ImageRect *const images = new ImageRect;
426  int padding = 3;
427  int titlePadding = 4;
428  int titlebarHeight = 0;
429  int titlebarHeightRelative = 0;
430  int closePadding = 3;
431  int stickySpacing = 3;
432  int stickyPadding = 3;
433  int resizePadding = 2;
434  StringIntMap *const mOptions = new StringIntMap;
435 
436  // iterate <widget>'s
437  for_each_xml_child_node(widgetNode, rootNode)
438  {
439  if (!xmlNameEqual(widgetNode, "widget"))
440  continue;
441 
442  const std::string widgetType =
443  XML::getProperty(widgetNode, "type", "unknown");
444  if (widgetType == "Window")
445  {
446  SkinHelper helper;
447  const int globalXPos = XML::getProperty(widgetNode, "xpos", 0);
448  const int globalYPos = XML::getProperty(widgetNode, "ypos", 0);
449  for_each_xml_child_node(partNode, widgetNode)
450  {
451  if (xmlNameEqual(partNode, "part"))
452  {
453  helper.partType = XML::getProperty(
454  partNode, "type", "unknown");
455  helper.xPos = XML::getProperty(
456  partNode, "xpos", 0) + globalXPos;
457  helper.yPos = XML::getProperty(
458  partNode, "ypos", 0) + globalYPos;
459  helper.width = XML::getProperty(partNode, "width", 0);
460  helper.height = XML::getProperty(partNode, "height", 0);
461  if ((helper.width == 0) || (helper.height == 0))
462  continue;
463  helper.image = dBorders;
464 
465  helper.rect = border;
466  if (!helper.loadList(skinParam,
467  sizeof(skinParam) / sizeof(SkinParameter)))
468  {
469  helper.rect = images;
470  helper.loadList(imageParam,
471  sizeof(imageParam) / sizeof(SkinParameter));
472  }
473  }
474  else if (full && xmlNameEqual(partNode, "option"))
475  {
476  const std::string name = XML::getProperty(
477  partNode, "name", "");
478  if (name == "padding")
479  {
480  padding = XML::getProperty(partNode, "value", 3);
481  }
482  else if (name == "titlePadding")
483  {
484  titlePadding = XML::getProperty(partNode, "value", 4);
485  }
486  else if (name == "closePadding")
487  {
488  closePadding = XML::getProperty(partNode, "value", 3);
489  }
490  else if (name == "stickySpacing")
491  {
492  stickySpacing = XML::getProperty(partNode, "value", 3);
493  }
494  else if (name == "stickyPadding")
495  {
496  stickyPadding = XML::getProperty(partNode, "value", 3);
497  }
498  else if (name == "titlebarHeight")
499  {
500  titlebarHeight = XML::getProperty(
501  partNode, "value", 0);
502  }
503  else if (name == "titlebarHeightRelative")
504  {
505  titlebarHeightRelative = XML::getProperty(
506  partNode, "value", 0);
507  }
508  else if (name == "resizePadding")
509  {
510  resizePadding = XML::getProperty(
511  partNode, "value", 2);
512  }
513  else
514  {
515  (*mOptions)[name] = XML::getProperty(
516  partNode, "value", 0);
517  }
518  }
519  }
520  }
521  else
522  {
523  logger->log("Theme::readSkin(): Unknown widget type '%s'",
524  widgetType.c_str());
525  }
526  }
527 
528  if (dBorders != nullptr)
529  dBorders->decRef();
530 
531  (*mOptions)["closePadding"] = closePadding;
532  (*mOptions)["stickyPadding"] = stickyPadding;
533  (*mOptions)["stickySpacing"] = stickySpacing;
534  (*mOptions)["titlebarHeight"] = titlebarHeight;
535  (*mOptions)["titlebarHeightRelative"] = titlebarHeightRelative;
536  (*mOptions)["resizePadding"] = resizePadding;
537 
538  Skin *const skin = new Skin(border, images, filename, "", padding,
539  titlePadding, mOptions);
540  delete images;
541  skin->updateAlpha(mMinimumOpacity);
542  doc->decRef();
543  return skin;
544 }
545 
546 bool Theme::tryThemePath(const std::string &themeName)
547 {
548  if (!themeName.empty())
549  {
550  const std::string path = defaultThemePath + themeName;
551  if (VirtFs::exists(path))
552  {
553  mThemePath = path;
554  mThemeName = themeName;
555  if (theme != nullptr)
556  theme->loadColors("");
557  return true;
558  }
559  }
560 
561  return false;
562 }
563 
565 {
566  VirtFs::getDirs(branding.getStringValue("guiThemePath"), list);
567  std::sort(list.begin(), list.end());
568 }
569 
571 {
572  VirtFs::permitLinks(true);
573  VirtFs::getFiles(branding.getStringValue("fontsPath"), list);
574  std::sort(list.begin(), list.end());
575  VirtFs::permitLinks(false);
576 }
577 
579 {
580  VirtFs::List *const skins = VirtFs::enumerateFiles(
581  branding.getStringValue("systemsounds"));
582 
583  FOR_EACH (StringVectCIter, i, skins->names)
584  {
586  "systemsounds") + *i)))
587  {
588  std::string str = *i;
589  if (findCutLast(str, ".ogg"))
590  list.push_back(str);
591  }
592  }
593 
594  VirtFs::freeList(skins);
595  std::sort(list.begin(), list.end());
596 }
597 
599 {
600  prepareThemePath();
601  mScreenDensity = graphicsManager.getDensityString();
602 }
603 
605 {
607 
608  mThemePath.clear();
609  mThemeName.clear();
610 
611  // Try theme from settings
612  if (tryThemePath(config.getStringValue("theme")))
613  return;
614 
615  // Try theme from branding
616  if (tryThemePath(branding.getStringValue("theme")))
617  return;
618 
619  if (mThemePath.empty())
620  mThemePath = "graphics/gui";
621 
622  theme->loadColors(mThemePath);
623 
624  logger->log("Selected Theme: " + mThemePath);
625 }
626 
627 std::string Theme::resolveThemePath(const std::string &path)
628 {
629  // Need to strip off any dye info for the existence tests
630  const int pos = CAST_S32(path.find('|'));
631  std::string file;
632  if (pos > 0)
633  file = path.substr(0, pos);
634  else
635  file = path;
636 
637  // File with path
638  if (file.find('/') != std::string::npos)
639  {
640  // Might be a valid path already
641  if (VirtFs::exists(file))
642  return path;
643  }
644 
645  // Try the theme
646  file = pathJoin(getThemePath(), file);
647 
648  if (VirtFs::exists(file))
649  return pathJoin(getThemePath(), path);
650 
651  // Backup
652  return pathJoin(branding.getStringValue("guiPath"), path);
653 }
654 
655 Image *Theme::getImageFromTheme(const std::string &path)
656 {
657  return Loader::getImage(resolveThemePath(path));
658 }
659 
660 ImageSet *Theme::getImageSetFromTheme(const std::string &path,
661  const int w, const int h)
662 {
663  return Loader::getImageSet(resolveThemePath(path), w, h);
664 }
665 
666 #define themeEnumStart(name) #name,
667 #define themeEnum(name) #name,
668 #define themeEnumEnd(name)
669 
670 static int readColorType(const std::string &type)
671 {
672  static const std::string colors[CAST_SIZE(
673  ThemeColorId::THEME_COLORS_END)] =
674  {
675 #include "gui/themecolortype.inc"
676  };
677 
678  if (type.empty())
679  return -1;
680 
681  for (int i = 0; i < CAST_S32(ThemeColorId::THEME_COLORS_END); i++)
682  {
683  if (compareStrI(type, colors[i]) == 0)
684  return i;
685  }
686 
687  return -1;
688 }
689 
690 THEMECOLORTYPE_VOID
691 
692 #undef themeEnumStart
693 #undef themeEnum
694 #undef themeEnumEnd
695 #undef THEMECOLORTYPE_VOID
696 
697 static Color readColor(const std::string &description)
698 {
699  const int size = static_cast<int>(description.length());
700  if (size < 7 || description[0] != '#')
701  {
702  logger->log("Error, invalid theme color palette: %s",
703  description.c_str());
704  return Palette::BLACK;
705  }
706 
707  unsigned int v = 0;
708  for (int i = 1; i < 7; ++i)
709  {
710  signed const char c = description[i];
711  int n;
712 
713  if ('0' <= c && c <= '9')
714  {
715  n = c - '0';
716  }
717  else if ('A' <= c && c <= 'F')
718  {
719  n = c - 'A' + 10;
720  }
721  else if ('a' <= c && c <= 'f')
722  {
723  n = c - 'a' + 10;
724  }
725  else
726  {
727  logger->log("Error, invalid theme color palette: %s",
728  description.c_str());
729  return Palette::BLACK;
730  }
731 
732  v = (v << 4) | n;
733  }
734 
735  return Color(v);
736 }
737 
738 static GradientTypeT readColorGradient(const std::string &grad)
739 {
740  static const std::string grads[] =
741  {
742  "STATIC",
743  "PULSE",
744  "SPECTRUM",
745  "RAINBOW"
746  };
747 
748  if (grad.empty())
749  return GradientType::STATIC;
750 
751  for (int i = 0; i < 4; i++)
752  {
753  if (compareStrI(grad, grads[i]) != 0)
754  return static_cast<GradientTypeT>(i);
755  }
756 
757  return GradientType::STATIC;
758 }
759 
760 static int readProgressType(const std::string &type)
761 {
762  static const std::string colors[CAST_SIZE(
764  {
765  "HP",
766  "HP_POISON",
767  "MP",
768  "NO_MP",
769  "EXP",
770  "INVY_SLOTS",
771  "WEIGHT",
772  "JOB",
773  "UPDATE",
774  "MONEY",
775  "ARROWS",
776  "STATUS"
777  };
778 
779  if (type.empty())
780  return -1;
781 
782  for (int i = 0; i < CAST_S32(ProgressColorId::THEME_PROG_END); i++)
783  {
784  if (compareStrI(type, colors[i]) == 0)
785  return i;
786  }
787 
788  return -1;
789 }
790 
791 void Theme::loadColors(std::string file)
792 {
793  if (file.empty())
794  file = "colors.xml";
795  else
796  file = pathJoin(file, "colors.xml");
797 
798  XML::Document *const doc = Loader::getXml(resolveThemePath(file),
801  if (doc == nullptr)
802  return;
803  XmlNodeConstPtrConst root = doc->rootNode();
804 
805  if ((root == nullptr) || !xmlNameEqual(root, "colors"))
806  {
807  logger->log("Error loading colors file: %s", file.c_str());
808  doc->decRef();
809  return;
810  }
811 
812  logger->log("Loading colors file: %s", file.c_str());
813 
814  for_each_xml_child_node(paletteNode, root)
815  {
816  if (xmlNameEqual(paletteNode, "progressbar"))
817  {
818  const int type = readProgressType(XML::getProperty(
819  paletteNode, "id", ""));
820  if (type < 0)
821  continue;
822 
823  mProgressColors[type] = new DyePalette(XML::getProperty(
824  paletteNode, "color", ""), 6);
825  }
826  else if (!xmlNameEqual(paletteNode, "palette"))
827  {
828  continue;
829  }
830 
831  const int paletteId = XML::getProperty(paletteNode, "id", 1);
832  if (paletteId < 0 || paletteId >= THEME_PALETTES)
833  continue;
834 
835  for_each_xml_child_node(node, paletteNode)
836  {
837  if (xmlNameEqual(node, "color"))
838  {
839  const std::string id = XML::getProperty(node, "id", "");
840  const int type = readColorType(id);
841  if (type < 0)
842  continue;
843 
844  const std::string temp = XML::getProperty(node, "color", "");
845  if (temp.empty())
846  continue;
847 
848  const Color color = readColor(temp);
849  const GradientTypeT grad = readColorGradient(
850  XML::getProperty(node, "effect", ""));
851  mColors[paletteId * CAST_SIZE(
852  ThemeColorId::THEME_COLORS_END) + type].set(
853  type, color, grad, 10);
854 
855  if (!findLast(id, "_OUTLINE"))
856  {
857  const int type2 = readColorType(id + "_OUTLINE");
858  if (type2 < 0)
859  continue;
860  const int idx = paletteId
861  * CAST_S32(ThemeColorId::THEME_COLORS_END);
862  mColors[idx + type2] = mColors[idx + type];
863  }
864  }
865  }
866  }
867  doc->decRef();
868 }
869 
870 #define loadGrid() \
871  { \
872  const ImageRect &rect = skin->getBorder(); \
873  for (int f = start; f <= end; f ++) \
874  { \
875  if (rect.grid[f]) \
876  { \
877  image.grid[f] = rect.grid[f]; \
878  image.grid[f]->incRef(); \
879  } \
880  } \
881  }
882 
884  const std::string &name,
885  const std::string &name2,
886  const int start,
887  const int end)
888 {
889  Skin *const skin = load(name,
890  name2,
891  false,
893  if (skin != nullptr)
894  {
895  loadGrid()
896  unload(skin);
897  }
898 }
899 
901  const std::string &name,
902  const std::string &name2,
903  const int start,
904  const int end)
905 {
906  Skin *const skin = load(name,
907  name2,
908  true,
910  if (skin != nullptr)
911  loadGrid()
912  return skin;
913 }
914 
915 void Theme::unloadRect(const ImageRect &rect,
916  const int start,
917  const int end)
918 {
919  for (int f = start; f <= end; f ++)
920  {
921  if (rect.grid[f] != nullptr)
922  rect.grid[f]->decRef();
923  }
924 }
925 
927  const std::string &name2)
928 {
929  if (theme == nullptr)
930  return nullptr;
931 
932  Skin *const skin = theme->load(name,
933  name2,
934  false,
936  if (skin != nullptr)
937  {
938  const ImageRect &rect = skin->getBorder();
939  if (rect.grid[0] != nullptr)
940  {
941  Image *const image = rect.grid[0];
942  image->incRef();
943  theme->unload(skin);
944  return image;
945  }
946  theme->unload(skin);
947  }
948  return nullptr;
949 }
950 
952  const std::string &name2,
953  const int w, const int h)
954 {
955  if (theme == nullptr)
956  return nullptr;
957 
958  Skin *const skin = theme->load(name,
959  name2,
960  false,
962  if (skin != nullptr)
963  {
964  const ImageRect &rect = skin->getBorder();
965  if (rect.grid[0] != nullptr)
966  {
967  Image *const image = rect.grid[0];
968  const SDL_Rect &rect2 = image->mBounds;
969  if ((rect2.w != 0U) && (rect2.h != 0U))
970  {
971  ImageSet *const imageSet = Loader::getSubImageSet(
972  image, w, h);
973  theme->unload(skin);
974  return imageSet;
975  }
976  }
977  theme->unload(skin);
978  }
979  return nullptr;
980 }
981 
982 #define readValue(name) \
983  { \
984  tmpData = reinterpret_cast<XmlChar*>( \
985  XmlNodeGetContent(infoNode)); \
986  info->name = tmpData; \
987  XmlFree(tmpData); \
988  }
989 
990 #define readIntValue(name) \
991  { \
992  tmpData = reinterpret_cast<XmlChar*>( \
993  XmlNodeGetContent(infoNode)); \
994  info->name = atoi(tmpData); \
995  XmlFree(tmpData); \
996  }
997 
998 #define readFloatValue(name) \
999  { \
1000  tmpData = reinterpret_cast<XmlChar*>( \
1001  XmlNodeGetContent(infoNode)); \
1002  info->name = static_cast<float>(atof(tmpData)); \
1003  XmlFree(tmpData); \
1004  }
1005 
1006 ThemeInfo *Theme::loadInfo(const std::string &themeName)
1007 {
1008  std::string path;
1009  if (themeName.empty())
1010  {
1011  path = "graphics/gui/info.xml";
1012  }
1013  else
1014  {
1015  path = pathJoin(defaultThemePath,
1016  themeName,
1017  "info.xml");
1018  }
1019  logger->log("loading: " + path);
1020  XML::Document *const doc = Loader::getXml(path,
1022  SkipError_false);
1023  if (doc == nullptr)
1024  return nullptr;
1025  XmlNodeConstPtrConst rootNode = doc->rootNode();
1026 
1027  if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "info"))
1028  {
1029  doc->decRef();
1030  return nullptr;
1031  }
1032 
1033  ThemeInfo *const info = new ThemeInfo;
1034 
1035  const std::string fontSize2("fontSize_" + mScreenDensity);
1036  const std::string npcfontSize2("npcfontSize_" + mScreenDensity);
1037  XmlChar *tmpData = nullptr;
1038  for_each_xml_child_node(infoNode, rootNode)
1039  {
1040  if (xmlNameEqual(infoNode, "name"))
1041  readValue(name)
1042  else if (xmlNameEqual(infoNode, "copyright"))
1043  readValue(copyright)
1044  else if (xmlNameEqual(infoNode, "font"))
1045  readValue(font)
1046  else if (xmlNameEqual(infoNode, "boldFont"))
1048  else if (xmlNameEqual(infoNode, "particleFont"))
1049  readValue(particleFont)
1050  else if (xmlNameEqual(infoNode, "helpFont"))
1051  readValue(helpFont)
1052  else if (xmlNameEqual(infoNode, "secureFont"))
1053  readValue(secureFont)
1054  else if (xmlNameEqual(infoNode, "npcFont"))
1055  readValue(npcFont)
1056  else if (xmlNameEqual(infoNode, "japanFont"))
1057  readValue(japanFont)
1058  else if (xmlNameEqual(infoNode, "chinaFont"))
1059  readValue(chinaFont)
1060  else if (xmlNameEqual(infoNode, "fontSize"))
1061  readIntValue(fontSize)
1062  else if (xmlNameEqual(infoNode, "npcfontSize"))
1063  readIntValue(npcfontSize)
1064  else if (xmlNameEqual(infoNode, "guialpha"))
1065  readFloatValue(guiAlpha)
1066  else if (xmlNameEqual(infoNode, fontSize2.c_str()))
1067  readIntValue(fontSize)
1068  else if (xmlNameEqual(infoNode, npcfontSize2.c_str()))
1069  readIntValue(npcfontSize)
1070  }
1071  doc->decRef();
1072  return info;
1073 }
1074 
1075 ThemeColorIdT Theme::getIdByChar(const signed char c, bool &valid) const
1076 {
1077  const CharColors::const_iterator it = mCharColors.find(c);
1078  if (it != mCharColors.end())
1079  {
1080  valid = true;
1081  return static_cast<ThemeColorIdT>((*it).second);
1082  }
1083 
1084  valid = false;
1085  return ThemeColorId::BROWSERBOX;
1086 }
#define loadList(key, mob)
#define CAST_S32
Definition: cast.h:30
#define CAST_SIZE
Definition: cast.h:34
Definition: color.h:76
std::string getStringValue(const std::string &key) const
void addListener(const std::string &key, ConfigListener *const listener)
void removeListener(const std::string &key, ConfigListener *const listener)
void getColor(const unsigned int intensity, unsigned int(&color)[3]) const
Definition: dyepalette.cpp:152
std::string getDensityString() const
Image * grid[9]
Definition: imagerect.h:42
void log(const char *const log_text,...)
Definition: logger.cpp:269
void log1(const char *const log_text)
Definition: logger.cpp:238
Colors mColors
Definition: palette.h:154
CharColors mCharColors
Definition: palette.h:155
static const Color BLACK
Definition: palette.h:48
virtual void decRef()
Definition: resource.cpp:50
Definition: skin.h:37
int instances
Definition: skin.h:121
const ImageRect & getBorder() const
Definition: skin.h:68
void updateAlpha(const float minimumOpacityAllowed)
Definition: skin.cpp:104
Definition: theme.h:55
static void fillSoundsList(StringVect &list)
Definition: theme.cpp:578
void unload(Skin *const skin)
Definition: theme.cpp:250
float mMinimumOpacity
Definition: theme.h:206
void loadColors(std::string file)
Definition: theme.cpp:791
Skins mSkins
Definition: theme.h:192
static void prepareThemePath()
Definition: theme.cpp:604
std::vector< DyePalette * > ProgressColors
Definition: theme.h:208
static std::string mThemePath
Definition: theme.h:194
static std::string resolveThemePath(const std::string &path)
Definition: theme.cpp:627
static std::string getThemePath()
Definition: theme.h:67
void updateAlpha()
Definition: theme.cpp:281
static Image * getImageFromThemeXml(const std::string &name, const std::string &name2)
Definition: theme.cpp:926
void optionChanged(const std::string &name)
Definition: theme.cpp:291
static ThemeInfo * loadInfo(const std::string &themeName)
Definition: theme.cpp:1006
static ImageSet * getImageSetFromThemeXml(const std::string &name, const std::string &name2, const int w, const int h)
Definition: theme.cpp:951
Skins::iterator SkinIterator
Definition: theme.h:190
static void unloadRect(const ImageRect &rect, const int start, const int end)
Definition: theme.cpp:915
ProgressColors mProgressColors
Definition: theme.h:209
Theme()
Definition: theme.cpp:78
static void fillFontsList(StringVect &list)
Definition: theme.cpp:570
static std::string mThemeName
Definition: theme.h:195
Skin * load(const std::string &filename, const std::string &filename2, const bool full, const std::string &defaultPath)
Definition: theme.cpp:179
static void fillSkinsList(StringVect &list)
Definition: theme.cpp:564
static std::string mScreenDensity
Definition: theme.h:196
void loadRect(ImageRect &image, const std::string &name, const std::string &name2, const int start, const int end)
Definition: theme.cpp:883
void setMinimumOpacity(const float minimumOpacity)
Definition: theme.cpp:272
static bool tryThemePath(const std::string &themePath)
Definition: theme.cpp:546
Skin * loadSkinRect(ImageRect &image, const std::string &name, const std::string &name2, const int start, const int end)
Definition: theme.cpp:900
static Image * getImageFromTheme(const std::string &path)
Definition: theme.cpp:655
static void selectSkin()
Definition: theme.cpp:598
static Color getProgressColor(const ProgressColorIdT type, const float progress)
Definition: theme.cpp:155
ThemeColorIdT getIdByChar(const signed char c, bool &valid) const
Definition: theme.cpp:1075
static ImageSet * getImageSetFromTheme(const std::string &path, const int w, const int h)
Definition: theme.cpp:660
Skin * readSkin(const std::string &filename0, const bool full)
Definition: theme.cpp:394
~Theme()
Definition: theme.cpp:147
xmlNodePtr rootNode()
Definition: libxml.cpp:169
Configuration config
Configuration branding
const int THEME_PALETTES
Definition: theme.h:31
void delete_all(Container &c)
Definition: dtor.h:56
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
GradientType ::T GradientTypeT
Definition: gradienttype.h:38
GraphicsManager graphicsManager
Font * boldFont
Definition: gui.cpp:112
#define for_each_xml_child_node(var, parent)
Definition: libxml.h:161
#define restrict
Definition: localconsts.h:165
#define A_NONNULL(...)
Definition: localconsts.h:168
#define final
Definition: localconsts.h:46
#define A_DELETE_COPY(func)
Definition: localconsts.h:53
#define A_UNUSED
Definition: localconsts.h:160
#define CHECKLISTENERS
Definition: localconsts.h:277
#define A_DEFAULT_COPY(func)
Definition: localconsts.h:41
Logger * logger
Definition: logger.cpp:89
bool info(InputEvent &event)
Definition: commands.cpp:57
void load()
Definition: avatardb.cpp:46
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:1774
int size()
Definition: emotedb.cpp:306
XML::Document * getXml(const std::string &idPath, const UseVirtFs useResman, const SkipError skipError)
Definition: xmlloader.cpp:56
ImageSet * getSubImageSet(Image *const parent, const int width, const int height)
Image * getImage(const std::string &idPath)
Definition: imageloader.cpp:86
ImageSet * getImageSet(const std::string &imagePath, const int w, const int h)
Image * getSubImage(Image *const parent, const int x, const int y, const int width, const int height)
void unload()
Definition: net.cpp:180
void getDirs(std::string dirName, StringVect &list)
Definition: fs.cpp:217
void freeList(List *const handle)
Definition: fs.cpp:269
bool isDirectory(std::string name)
Definition: fs.cpp:239
void permitLinks(const bool val)
Definition: fs.cpp:803
List * enumerateFiles(std::string dirName)
Definition: fs.cpp:147
void getFiles(std::string dirName, StringVect &list)
Definition: fs.cpp:172
bool exists(std::string name)
Definition: fs.cpp:124
int getProperty(const xmlNodePtr node, const char *const name, int def)
Definition: libxml.cpp:174
ItemOptionDb::OptionInfos mOptions
std::map< std::string, DyeColor > mColors
Definition: palettedb.cpp:37
ProgressColorId ::T ProgressColorIdT
const bool SkipError_false
Definition: skiperror.h:30
const bool SkipError_true
Definition: skiperror.h:30
std::map< std::string, int > StringIntMap
Definition: stringmap.h:28
std::string strprintf(const char *const format,...)
int compareStrI(const std::string &a, const std::string &b)
bool findLast(const std::string &str1, const std::string &str2)
bool findCutLast(std::string &str1, const std::string &str2)
std::string pathJoin(std::string str1, const std::string &str2)
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:31
std::vector< std::string > StringVect
Definition: stringvector.h:29
int yPos
Definition: theme.cpp:368
int width
Definition: theme.cpp:369
bool loadList(const SkinParameter *const params, const size_t size)
Definition: theme.cpp:375
SkinHelper()
Definition: theme.cpp:352
ImageRect * rect
Definition: theme.cpp:371
int xPos
Definition: theme.cpp:367
int height
Definition: theme.cpp:370
std::string partType
Definition: theme.cpp:366
Image * image
Definition: theme.cpp:373
std::string name
Definition: theme.cpp:300
StringVect names
Definition: list.h:40
static void initDefaultThemePath()
Definition: theme.cpp:65
Theme * theme
Definition: theme.cpp:62
#define readIntValue(name)
Definition: theme.cpp:990
#define readValue(name)
Definition: theme.cpp:982
static std::string defaultThemePath
Definition: theme.cpp:56
static const SkinParameter skinParam[]
Definition: theme.cpp:303
#define loadGrid()
Definition: theme.cpp:870
static int readColorType(const std::string &type)
Definition: theme.cpp:670
static const SkinParameter imageParam[]
Definition: theme.cpp:342
static Color readColor(const std::string &description)
Definition: theme.cpp:697
#define readFloatValue(name)
Definition: theme.cpp:998
static GradientTypeT readColorGradient(const std::string &grad)
Definition: theme.cpp:738
static int readProgressType(const std::string &type)
Definition: theme.cpp:760
ThemeColorId ::T ThemeColorIdT
Definition: themecolorid.h:35
const bool UseVirtFs_true
Definition: usevirtfs.h:30