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-2017 The ManaPlus Developers
8  *
9  * This file is part of The ManaPlus Client.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #include "gui/theme.h"
26 
27 #include "configuration.h"
28 #include "graphicsmanager.h"
29 
30 #include "const/gui/theme.h"
31 
32 #include "fs/virtfs/fs.h"
33 #include "fs/virtfs/list.h"
34 
35 #include "gui/skin.h"
36 #include "gui/themeinfo.h"
37 
38 #include "resources/imagerect.h"
39 
41 
42 #include "resources/image/image.h"
43 
49 
50 #include "utils/dtor.h"
51 #include "utils/foreach.h"
52 
53 #include "debug.h"
54 
55 static std::string defaultThemePath;
56 
57 std::string Theme::mThemePath;
58 std::string Theme::mThemeName;
59 std::string Theme::mScreenDensity;
60 
61 Theme *theme = nullptr;
62 
63 // Set the theme path...
64 static void initDefaultThemePath()
65 {
66  defaultThemePath = branding.getStringValue("guiThemePath");
67 
68  logger->log("defaultThemePath: " + defaultThemePath);
69  if (!defaultThemePath.empty() &&
71  {
72  return;
73  }
74  defaultThemePath = "themes/";
75 }
76 
79  mSkins(),
80  mMinimumOpacity(-1.0F),
81  mProgressColors(ProgressColors(CAST_SIZE(
83 {
85 
86  config.addListener("guialpha", this);
87 
120 
121  // here need use outlined colors
122  mCharColors['H' | 0x80]
128  mCharColors['W' | 0x80]
130  mCharColors['w' | 0x80]
133  mCharColors['P' | 0x80]
135  mCharColors['U' | 0x80]
139  mCharColors['<' | 0x80]
144 }
145 
147 {
149  config.removeListener("guialpha", this);
152 }
153 
155  const float progress)
156 {
157  int color[3] = {0, 0, 0};
158 
159  if (theme != nullptr)
160  {
161  const DyePalette *const dye
162  = theme->mProgressColors[CAST_SIZE(type)];
163 
164  if (dye != nullptr)
165  {
166  dye->getColor(progress, color);
167  }
168  else
169  {
170  logger->log("color not found: "
171  + toString(CAST_S32(type)));
172  }
173  }
174 
175  return Color(color[0], color[1], color[2]);
176 }
177 
178 Skin *Theme::load(const std::string &filename,
179  const std::string &filename2,
180  const bool full,
181  const std::string &restrict defaultPath)
182 {
183  // Check if this skin was already loaded
184 
185  const SkinIterator skinIterator = mSkins.find(filename);
186  if (mSkins.end() != skinIterator)
187  {
188  if (skinIterator->second != nullptr)
189  skinIterator->second->instances++;
190  return skinIterator->second;
191  }
192 
193  Skin *skin = nullptr;
194  if (mScreenDensity.empty())
195  { // if no density detected
196  skin = readSkin(filename, full);
197  if ((skin == nullptr) && !filename2.empty() && filename2 != filename)
198  skin = readSkin(filename2, full);
199  if ((skin == nullptr) && filename2 != "window.xml")
200  skin = readSkin("window.xml", full);
201  }
202  else
203  { // first use correct density images
204  const std::string endStr("_" + mScreenDensity + ".xml");
205  std::string name = filename;
206  if (findCutLast(name, ".xml"))
207  skin = readSkin(name + endStr, full);
208  if (skin == nullptr)
209  skin = readSkin(filename, full);
210  if ((skin == nullptr) && !filename2.empty() && filename2 != filename)
211  {
212  name = filename2;
213  if (findCutLast(name, ".xml"))
214  skin = readSkin(name + endStr, full);
215  if (skin == nullptr)
216  skin = readSkin(filename2, full);
217  }
218  if ((skin == nullptr) && filename2 != "window.xml")
219  {
220  skin = readSkin("window" + endStr, full);
221  if (skin == nullptr)
222  skin = readSkin("window.xml", full);
223  }
224  }
225 
226  if (skin == nullptr)
227  {
228  // Try falling back on the defaultPath if this makes sense
229  if (filename != defaultPath)
230  {
231  logger->log("Error loading skin '%s', falling back on default.",
232  filename.c_str());
233 
234  skin = readSkin(defaultPath, full);
235  }
236 
237  if (skin == nullptr)
238  {
239  logger->log(strprintf("Error: Loading default skin '%s' failed. "
240  "Make sure the skin file is valid.",
241  defaultPath.c_str()));
242  }
243  }
244 
245  mSkins[filename] = skin;
246  return skin;
247 }
248 
249 void Theme::unload(Skin *const skin)
250 {
251  if (skin == nullptr)
252  return;
253  skin->instances --;
254  if (skin->instances == 0)
255  {
256  SkinIterator it = mSkins.begin();
257  const SkinIterator it_end = mSkins.end();
258  while (it != it_end)
259  {
260  if (it->second == skin)
261  {
262  mSkins.erase(it);
263  break;
264  }
265  ++ it;
266  }
267  delete skin;
268  }
269 }
270 
271 void Theme::setMinimumOpacity(const float minimumOpacity)
272 {
273  if (minimumOpacity > 1.0F)
274  return;
275 
276  mMinimumOpacity = minimumOpacity;
277  updateAlpha();
278 }
279 
281 {
282  FOR_EACH (SkinIterator, iter, mSkins)
283  {
284  Skin *const skin = iter->second;
285  if (skin != nullptr)
287  }
288 }
289 
290 void Theme::optionChanged(const std::string &name A_UNUSED)
291 {
292  updateAlpha();
293 }
294 
296 {
298  int index;
299  std::string name;
300 };
301 
302 static const SkinParameter skinParam[] =
303 {
304  {0, "top-left-corner"},
305  {0, "standart"},
306  {0, "up"},
307  {0, "hstart"},
308  {0, "in"},
309  {0, "normal"},
310  {1, "top-edge"},
311  {1, "highlighted"},
312  {1, "down"},
313  {1, "hmiddle"},
314  {1, "in-highlighted"},
315  {1, "checked"},
316  {2, "top-right-corner"},
317  {2, "pressed"},
318  {2, "left"},
319  {2, "hend"},
320  {2, "out"},
321  {2, "disabled"},
322  {3, "left-edge"},
323  {3, "disabled"},
324  {3, "right"},
325  {3, "hgrip"},
326  {3, "out-highlighted"},
327  {3, "disabled-checked"},
328  {4, "bg-quad"},
329  {4, "vstart"},
330  {4, "normal-highlighted"},
331  {5, "right-edge"},
332  {5, "vmiddle"},
333  {5, "checked-highlighted"},
334  {6, "bottom-left-corner"},
335  {6, "vend"},
336  {7, "bottom-edge"},
337  {7, "vgrip"},
338  {8, "bottom-right-corner"},
339 };
340 
341 static const SkinParameter imageParam[] =
342 {
343  {0, "closeImage"},
344  {1, "closeImageHighlighted"},
345  {2, "stickyImageUp"},
346  {3, "stickyImageDown"},
347 };
348 
350 {
352  partType(),
353  xPos(),
354  yPos(),
355  width(),
356  height(),
357  rect(),
358  node(),
359  image()
360  {
361  }
362 
364 
365  std::string partType;
366  int xPos;
367  int yPos;
368  int width;
369  int height;
370  ImageRect *rect;
371  XmlNodePtr *node;
372  Image *image;
373 
374  bool loadList(const SkinParameter *const params,
375  const size_t size) A_NONNULL(2)
376  {
377  for (size_t f = 0; f < size; f ++)
378  {
379  const SkinParameter &param = params[f];
380  if (partType == param.name)
381  {
382  rect->grid[param.index] = Loader::getSubImage(
383  image,
384  xPos, yPos,
385  width, height);
386  return true;
387  }
388  }
389  return false;
390  }
391 };
392 
393 Skin *Theme::readSkin(const std::string &filename, const bool full)
394 {
395  if (filename.empty())
396  return nullptr;
397 
398  const std::string path = resolveThemePath(filename);
399  if (!VirtFs::exists(path))
400  return nullptr;
401  XML::Document *const doc = Loader::getXml(path,
404  if (doc == nullptr)
405  return nullptr;
406  XmlNodeConstPtr rootNode = doc->rootNode();
407  if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "skinset"))
408  {
409  doc->decRef();
410  return nullptr;
411  }
412 
413  const std::string skinSetImage = XML::getProperty(rootNode, "image", "");
414 
415  if (skinSetImage.empty())
416  {
417  logger->log1("Theme::readSkin(): Skinset does not define an image!");
418  doc->decRef();
419  return nullptr;
420  }
421 
422  Image *const dBorders = Theme::getImageFromTheme(skinSetImage);
423  ImageRect *const border = new ImageRect;
424  ImageRect *const images = new ImageRect;
425  int padding = 3;
426  int titlePadding = 4;
427  int titlebarHeight = 0;
428  int titlebarHeightRelative = 0;
429  int closePadding = 3;
430  int stickySpacing = 3;
431  int stickyPadding = 3;
432  int resizePadding = 2;
433  StringIntMap *const mOptions = new StringIntMap;
434 
435  // iterate <widget>'s
436  for_each_xml_child_node(widgetNode, rootNode)
437  {
438  if (!xmlNameEqual(widgetNode, "widget"))
439  continue;
440 
441  const std::string widgetType =
442  XML::getProperty(widgetNode, "type", "unknown");
443  if (widgetType == "Window")
444  {
445  SkinHelper helper;
446  const int globalXPos = XML::getProperty(widgetNode, "xpos", 0);
447  const int globalYPos = XML::getProperty(widgetNode, "ypos", 0);
448  for_each_xml_child_node(partNode, widgetNode)
449  {
450  if (xmlNameEqual(partNode, "part"))
451  {
452  helper.partType = XML::getProperty(
453  partNode, "type", "unknown");
454  helper.xPos = XML::getProperty(
455  partNode, "xpos", 0) + globalXPos;
456  helper.yPos = XML::getProperty(
457  partNode, "ypos", 0) + globalYPos;
458  helper.width = XML::getProperty(partNode, "width", 0);
459  helper.height = XML::getProperty(partNode, "height", 0);
460  if ((helper.width == 0) || (helper.height == 0))
461  continue;
462  helper.image = dBorders;
463 
464  helper.rect = border;
465  if (!helper.loadList(skinParam,
466  sizeof(skinParam) / sizeof(SkinParameter)))
467  {
468  helper.rect = images;
469  helper.loadList(imageParam,
470  sizeof(imageParam) / sizeof(SkinParameter));
471  }
472  }
473  else if (full && xmlNameEqual(partNode, "option"))
474  {
475  const std::string name = XML::getProperty(
476  partNode, "name", "");
477  if (name == "padding")
478  {
479  padding = XML::getProperty(partNode, "value", 3);
480  }
481  else if (name == "titlePadding")
482  {
483  titlePadding = XML::getProperty(partNode, "value", 4);
484  }
485  else if (name == "closePadding")
486  {
487  closePadding = XML::getProperty(partNode, "value", 3);
488  }
489  else if (name == "stickySpacing")
490  {
491  stickySpacing = XML::getProperty(partNode, "value", 3);
492  }
493  else if (name == "stickyPadding")
494  {
495  stickyPadding = XML::getProperty(partNode, "value", 3);
496  }
497  else if (name == "titlebarHeight")
498  {
499  titlebarHeight = XML::getProperty(
500  partNode, "value", 0);
501  }
502  else if (name == "titlebarHeightRelative")
503  {
504  titlebarHeightRelative = XML::getProperty(
505  partNode, "value", 0);
506  }
507  else if (name == "resizePadding")
508  {
509  resizePadding = XML::getProperty(
510  partNode, "value", 2);
511  }
512  else
513  {
514  (*mOptions)[name] = XML::getProperty(
515  partNode, "value", 0);
516  }
517  }
518  }
519  }
520  else
521  {
522  logger->log("Theme::readSkin(): Unknown widget type '%s'",
523  widgetType.c_str());
524  }
525  }
526 
527  if (dBorders != nullptr)
528  dBorders->decRef();
529 
530  (*mOptions)["closePadding"] = closePadding;
531  (*mOptions)["stickyPadding"] = stickyPadding;
532  (*mOptions)["stickySpacing"] = stickySpacing;
533  (*mOptions)["titlebarHeight"] = titlebarHeight;
534  (*mOptions)["titlebarHeightRelative"] = titlebarHeightRelative;
535  (*mOptions)["resizePadding"] = resizePadding;
536 
537  Skin *const skin = new Skin(border, images, filename, "", padding,
538  titlePadding, mOptions);
539  delete images;
541  doc->decRef();
542  return skin;
543 }
544 
545 bool Theme::tryThemePath(const std::string &themeName)
546 {
547  if (!themeName.empty())
548  {
549  const std::string path = defaultThemePath + themeName;
550  if (VirtFs::exists(path))
551  {
552  mThemePath = path;
553  mThemeName = themeName;
554  if (theme != nullptr)
555  theme->loadColors("");
556  return true;
557  }
558  }
559 
560  return false;
561 }
562 
564 {
565  VirtFs::getDirs(branding.getStringValue("guiThemePath"), list);
566  std::sort(list.begin(), list.end());
567 }
568 
570 {
571  VirtFs::permitLinks(true);
572  VirtFs::getFiles(branding.getStringValue("fontsPath"), list);
573  std::sort(list.begin(), list.end());
574  VirtFs::permitLinks(false);
575 }
576 
578 {
579  VirtFs::List *const skins = VirtFs::enumerateFiles(
580  branding.getStringValue("systemsounds"));
581 
582  FOR_EACH (StringVectCIter, i, skins->names)
583  {
585  "systemsounds") + *i)))
586  {
587  std::string str = *i;
588  if (findCutLast(str, ".ogg"))
589  list.push_back(str);
590  }
591  }
592 
593  VirtFs::freeList(skins);
594  std::sort(list.begin(), list.end());
595 }
596 
598 {
601 }
602 
604 {
606 
607  mThemePath.clear();
608  mThemeName.clear();
609 
610  // Try theme from settings
611  if (tryThemePath(config.getStringValue("theme")))
612  return;
613 
614  // Try theme from branding
615  if (tryThemePath(branding.getStringValue("theme")))
616  return;
617 
618  if (mThemePath.empty())
619  mThemePath = "graphics/gui";
620 
621  theme->loadColors(mThemePath);
622 
623  logger->log("Selected Theme: " + mThemePath);
624 }
625 
626 std::string Theme::resolveThemePath(const std::string &path)
627 {
628  // Need to strip off any dye info for the existence tests
629  const int pos = CAST_S32(path.find('|'));
630  std::string file;
631  if (pos > 0)
632  file = path.substr(0, pos);
633  else
634  file = path;
635 
636  // File with path
637  if (file.find('/') != std::string::npos)
638  {
639  // Might be a valid path already
640  if (VirtFs::exists(file))
641  return path;
642  }
643 
644  // Try the theme
645  file = pathJoin(getThemePath(), file);
646 
647  if (VirtFs::exists(file))
648  return pathJoin(getThemePath(), path);
649 
650  // Backup
651  return pathJoin(branding.getStringValue("guiPath"), path);
652 }
653 
654 Image *Theme::getImageFromTheme(const std::string &path)
655 {
656  return Loader::getImage(resolveThemePath(path));
657 }
658 
659 ImageSet *Theme::getImageSetFromTheme(const std::string &path,
660  const int w, const int h)
661 {
662  return Loader::getImageSet(resolveThemePath(path), w, h);
663 }
664 
665 static int readColorType(const std::string &type)
666 {
667  static const std::string colors[CAST_SIZE(
669  {
670  "BROWSERBOX",
671  "BROWSERBOX_OUTLINE",
672  "SELFNICK",
673  "SELFNICK_OUTLINE",
674  "TEXT",
675  "TEXT_OUTLINE",
676  "CARET",
677  "SHADOW",
678  "OUTLINE",
679  "BORDER",
680  "PROGRESS_BAR",
681  "PROGRESS_BAR_OUTLINE",
682  "BUTTON",
683  "BUTTON_OUTLINE",
684  "BUTTON_DISABLED",
685  "BUTTON_DISABLED_OUTLINE",
686  "BUTTON_HIGHLIGHTED",
687  "BUTTON_HIGHLIGHTED_OUTLINE",
688  "BUTTON_PRESSED",
689  "BUTTON_PRESSED_OUTLINE",
690  "CHECKBOX",
691  "CHECKBOX_OUTLINE",
692  "DROPDOWN",
693  "DROPDOWN_OUTLINE",
694  "LABEL",
695  "LABEL_OUTLINE",
696  "LISTBOX",
697  "LISTBOX_OUTLINE",
698  "LISTBOX_SELECTED",
699  "LISTBOX_SELECTED_OUTLINE",
700  "RADIOBUTTON",
701  "RADIOBUTTON_OUTLINE",
702  "POPUP",
703  "POPUP_OUTLINE",
704  "TAB",
705  "TAB_OUTLINE",
706  "TAB_HIGHLIGHTED",
707  "TAB_HIGHLIGHTED_OUTLINE",
708  "TAB_SELECTED",
709  "TAB_SELECTED_OUTLINE",
710  "TEXTBOX",
711  "TEXTFIELD",
712  "TEXTFIELD_OUTLINE",
713  "WINDOW",
714  "WINDOW_OUTLINE",
715  "BATTLE_CHAT_TAB",
716  "BATTLE_CHAT_TAB_OUTLINE",
717  "CHANNEL_CHAT_TAB",
718  "CHANNEL_CHAT_TAB_OUTLINE",
719  "PARTY_CHAT_TAB",
720  "PARTY_CHAT_TAB_OUTLINE",
721  "PARTY_SOCIAL_TAB",
722  "PARTY_SOCIAL_TAB_OUTLINE",
723  "GUILD_CHAT_TAB",
724  "GUILD_CHAT_TAB_OUTLINE",
725  "GUILD_SOCIAL_TAB",
726  "GUILD_SOCIAL_TAB_OUTLINE",
727  "GM_CHAT_TAB",
728  "GM_CHAT_TAB_OUTLINE",
729  "BATTLE_CHAT_TAB_HIGHLIGHTED",
730  "BATTLE_CHAT_TAB_HIGHLIGHTED_OUTLINE",
731  "CHANNEL_CHAT_TAB_HIGHLIGHTED",
732  "CHANNEL_CHAT_TAB_HIGHLIGHTED_OUTLINE",
733  "PARTY_CHAT_TAB_HIGHLIGHTED",
734  "PARTY_CHAT_TAB_HIGHLIGHTED_OUTLINE",
735  "PARTY_SOCIAL_TAB_HIGHLIGHTED",
736  "PARTY_SOCIAL_TAB_HIGHLIGHTED_OUTLINE",
737  "GUILD_CHAT_TAB_HIGHLIGHTED",
738  "GUILD_CHAT_TAB_HIGHLIGHTED_OUTLINE",
739  "GUILD_SOCIAL_TAB_HIGHLIGHTED",
740  "GUILD_SOCIAL_TAB_HIGHLIGHTED_OUTLINE",
741  "GM_CHAT_TAB_HIGHLIGHTED",
742  "GM_CHAT_TAB_HIGHLIGHTED_OUTLINE",
743  "BATTLE_CHAT_TAB_SELECTED",
744  "BATTLE_CHAT_TAB_SELECTED_OUTLINE",
745  "CHANNEL_CHAT_TAB_SELECTED",
746  "CHANNEL_CHAT_TAB_SELECTED_OUTLINE",
747  "PARTY_CHAT_TAB_SELECTED",
748  "PARTY_CHAT_TAB_SELECTED_OUTLINE",
749  "PARTY_SOCIAL_TAB_SELECTED",
750  "PARTY_SOCIAL_TAB_SELECTED_OUTLINE",
751  "GUILD_CHAT_TAB_SELECTED",
752  "GUILD_CHAT_TAB_SELECTED_OUTLINE",
753  "GUILD_SOCIAL_TAB_SELECTED",
754  "GUILD_SOCIAL_TAB_SELECTED_OUTLINE",
755  "GM_CHAT_TAB_SELECTED",
756  "GM_CHAT_TAB_SELECTED_OUTLINE",
757  "BACKGROUND",
758  "BACKGROUND_GRAY",
759  "SCROLLBAR_GRAY",
760  "DROPDOWN_SHADOW",
761  "HIGHLIGHT",
762  "HIGHLIGHT_OUTLINE",
763  "TAB_FLASH",
764  "TAB_FLASH_OUTLINE",
765  "TAB_PLAYER_FLASH",
766  "TAB_PLAYER_FLASH_OUTLINE",
767  "SHOP_WARNING",
768  "ITEM_EQUIPPED",
769  "ITEM_EQUIPPED_OUTLINE",
770  "ITEM_NOT_EQUIPPED",
771  "ITEM_NOT_EQUIPPED_OUTLINE",
772  "CHAT",
773  "CHAT_OUTLINE",
774  "GM",
775  "GM_OUTLINE",
776  "GLOBAL",
777  "GLOBAL_OUTLINE",
778  "PLAYER",
779  "PLAYER_OUTLINE",
780  "WHISPER_TAB",
781  "WHISPER_TAB_OUTLINE",
782  "WHISPER_TAB_OFFLINE",
783  "WHISPER_TAB_OFFLINE_OUTLINE",
784  "WHISPER_TAB_HIGHLIGHTED",
785  "WHISPER_TAB_HIGHLIGHTED_OUTLINE",
786  "WHISPER_TAB_OFFLINE_HIGHLIGHTED",
787  "WHISPER_TAB_OFFLINE_HIGHLIGHTED_OUTLINE",
788  "WHISPER_TAB_SELECTED",
789  "WHISPER_TAB_SELECTED_OUTLINE",
790  "WHISPER_TAB_OFFLINE_SELECTED",
791  "WHISPER_TAB_OFFLINE_SELECTED_OUTLINE",
792  "IS",
793  "IS_OUTLINE",
794  "SERVER",
795  "SERVER_OUTLINE",
796  "LOGGER",
797  "LOGGER_OUTLINE",
798  "HYPERLINK",
799  "HYPERLINK_OUTLINE",
800  "UNKNOWN_ITEM",
801  "UNKNOWN_ITEM_OUTLINE",
802  "GENERIC",
803  "GENERIC_OUTLINE",
804  "HEAD",
805  "HEAD_OUTLINE",
806  "USABLE",
807  "USABLE_OUTLINE",
808  "TORSO",
809  "TORSO_OUTLINE",
810  "ONEHAND",
811  "ONEHAND_OUTLINE",
812  "LEGS",
813  "LEGS_OUTLINE",
814  "FEET",
815  "FEET_OUTLINE",
816  "TWOHAND",
817  "TWOHAND_OUTLINE",
818  "SHIELD",
819  "SHIELD_OUTLINE",
820  "RING",
821  "RING_OUTLINE",
822  "NECKLACE",
823  "NECKLACE_OUTLINE",
824  "ARMS",
825  "ARMS_OUTLINE",
826  "AMMO",
827  "AMMO_OUTLINE",
828  "SERVER_VERSION_NOT_SUPPORTED",
829  "SERVER_VERSION_NOT_SUPPORTED_OUTLINE",
830  "WARNING",
831  "WARNING_OUTLINE",
832  "CHARM",
833  "CHARM_OUTLINE",
834  "CARD",
835  "CARD_OUTLINE",
836  "PLAYER_ADVANCED",
837  "PLAYER_ADVANCED_OUTLINE",
838  "BUBBLE_NAME",
839  "BUBBLE_NAME_OUTLINE",
840  "BUBBLE_TEXT",
841  "BUBBLE_TEXT_OUTLINE",
842  "BLACK",
843  "BLACK_OUTLINE",
844  "RED",
845  "RED_OUTLINE",
846  "GREEN",
847  "GREEN_OUTLINE",
848  "BLUE",
849  "BLUE_OUTLINE",
850  "ORANGE",
851  "ORANGE_OUTLINE",
852  "YELLOW",
853  "YELLOW_OUTLINE",
854  "PINK",
855  "PINK_OUTLINE",
856  "PURPLE",
857  "PURPLE_OUTLINE",
858  "GRAY",
859  "GRAY_OUTLINE",
860  "BROWN",
861  "BROWN_OUTLINE",
862  "STATUSBAR_ON",
863  "STATUSBAR_OFF",
864  "TABLE_BACKGROUND",
865  "SLOTS_BAR",
866  "SLOTS_BAR_OUTLINE",
867  "HP_BAR",
868  "HP_BAR_OUTLINE",
869  "MP_BAR",
870  "MP_BAR_OUTLINE",
871  "NO_MP_BAR",
872  "NO_MP_BAR_OUTLINE",
873  "XP_BAR",
874  "XP_BAR_OUTLINE",
875  "WEIGHT_BAR",
876  "WEIGHT_BAR_OUTLINE",
877  "MONEY_BAR",
878  "MONEY_BAR_OUTLINE",
879  "ARROWS_BAR",
880  "ARROWS_BAR_OUTLINE",
881  "STATUS_BAR",
882  "STATUS_BAR_OUTLINE",
883  "JOB_BAR",
884  "JOB_BAR_OUTLINE",
885  "OLDCHAT",
886  "OLDCHAT_OUTLINE",
887  "AWAYCHAT",
888  "AWAYCHAT_OUTLINE",
889  "SKILL_COOLDOWN",
890  "TEXT_DISABLED",
891  "TEXT_DISABLED_OUTLINE"
892  };
893 
894  if (type.empty())
895  return -1;
896 
897  for (int i = 0; i < CAST_S32(ThemeColorId::THEME_COLORS_END); i++)
898  {
899  if (compareStrI(type, colors[i]) == 0)
900  return i;
901  }
902 
903  return -1;
904 }
905 
906 static Color readColor(const std::string &description)
907 {
908  const int size = static_cast<int>(description.length());
909  if (size < 7 || description[0] != '#')
910  {
911  logger->log("Error, invalid theme color palette: %s",
912  description.c_str());
913  return Palette::BLACK;
914  }
915 
916  unsigned int v = 0;
917  for (int i = 1; i < 7; ++i)
918  {
919  signed const char c = description[i];
920  int n;
921 
922  if ('0' <= c && c <= '9')
923  {
924  n = c - '0';
925  }
926  else if ('A' <= c && c <= 'F')
927  {
928  n = c - 'A' + 10;
929  }
930  else if ('a' <= c && c <= 'f')
931  {
932  n = c - 'a' + 10;
933  }
934  else
935  {
936  logger->log("Error, invalid theme color palette: %s",
937  description.c_str());
938  return Palette::BLACK;
939  }
940 
941  v = (v << 4) | n;
942  }
943 
944  return Color(v);
945 }
946 
947 static GradientTypeT readColorGradient(const std::string &grad)
948 {
949  static const std::string grads[] =
950  {
951  "STATIC",
952  "PULSE",
953  "SPECTRUM",
954  "RAINBOW"
955  };
956 
957  if (grad.empty())
958  return GradientType::STATIC;
959 
960  for (int i = 0; i < 4; i++)
961  {
962  if (compareStrI(grad, grads[i]) != 0)
963  return static_cast<GradientTypeT>(i);
964  }
965 
966  return GradientType::STATIC;
967 }
968 
969 static int readProgressType(const std::string &type)
970 {
971  static const std::string colors[CAST_SIZE(
973  {
974  "HP",
975  "HP_POISON",
976  "MP",
977  "NO_MP",
978  "EXP",
979  "INVY_SLOTS",
980  "WEIGHT",
981  "JOB",
982  "UPDATE",
983  "MONEY",
984  "ARROWS",
985  "STATUS"
986  };
987 
988  if (type.empty())
989  return -1;
990 
991  for (int i = 0; i < CAST_S32(ProgressColorId::THEME_PROG_END); i++)
992  {
993  if (compareStrI(type, colors[i]) == 0)
994  return i;
995  }
996 
997  return -1;
998 }
999 
1000 void Theme::loadColors(std::string file)
1001 {
1002  if (file.empty())
1003  file = "colors.xml";
1004  else
1005  file = pathJoin(file, "colors.xml");
1006 
1007  XML::Document *const doc = Loader::getXml(resolveThemePath(file),
1009  SkipError_false);
1010  if (doc == nullptr)
1011  return;
1012  XmlNodeConstPtrConst root = doc->rootNode();
1013 
1014  if ((root == nullptr) || !xmlNameEqual(root, "colors"))
1015  {
1016  logger->log("Error loading colors file: %s", file.c_str());
1017  doc->decRef();
1018  return;
1019  }
1020 
1021  logger->log("Loading colors file: %s", file.c_str());
1022 
1023  for_each_xml_child_node(paletteNode, root)
1024  {
1025  if (xmlNameEqual(paletteNode, "progressbar"))
1026  {
1027  const int type = readProgressType(XML::getProperty(
1028  paletteNode, "id", ""));
1029  if (type < 0)
1030  continue;
1031 
1033  paletteNode, "color", ""), 6);
1034  }
1035  else if (!xmlNameEqual(paletteNode, "palette"))
1036  {
1037  continue;
1038  }
1039 
1040  const int paletteId = XML::getProperty(paletteNode, "id", 1);
1041  if (paletteId < 0 || paletteId >= THEME_PALETTES)
1042  continue;
1043 
1044  for_each_xml_child_node(node, paletteNode)
1045  {
1046  if (xmlNameEqual(node, "color"))
1047  {
1048  const std::string id = XML::getProperty(node, "id", "");
1049  const int type = readColorType(id);
1050  if (type < 0)
1051  continue;
1052 
1053  const std::string temp = XML::getProperty(node, "color", "");
1054  if (temp.empty())
1055  continue;
1056 
1057  const Color color = readColor(temp);
1058  const GradientTypeT grad = readColorGradient(
1059  XML::getProperty(node, "effect", ""));
1060  mColors[paletteId * CAST_SIZE(
1061  ThemeColorId::THEME_COLORS_END) + type].set(
1062  type, color, grad, 10);
1063 
1064  if (!findLast(id, "_OUTLINE"))
1065  {
1066  const int type2 = readColorType(id + "_OUTLINE");
1067  if (type2 < 0)
1068  continue;
1069  const int idx = paletteId
1071  mColors[idx + type2] = mColors[idx + type];
1072  }
1073  }
1074  }
1075  }
1076  doc->decRef();
1077 }
1078 
1079 #define loadGrid() \
1080  { \
1081  const ImageRect &rect = skin->getBorder(); \
1082  for (int f = start; f <= end; f ++) \
1083  { \
1084  if (rect.grid[f]) \
1085  { \
1086  image.grid[f] = rect.grid[f]; \
1087  image.grid[f]->incRef(); \
1088  } \
1089  } \
1090  }
1091 
1093  const std::string &name,
1094  const std::string &name2,
1095  const int start,
1096  const int end)
1097 {
1098  Skin *const skin = load(name, name2, false);
1099  if (skin != nullptr)
1100  {
1101  loadGrid();
1102  unload(skin);
1103  }
1104 }
1105 
1107  const std::string &name,
1108  const std::string &name2,
1109  const int start,
1110  const int end)
1111 {
1112  Skin *const skin = load(name, name2);
1113  if (skin != nullptr)
1114  loadGrid();
1115  return skin;
1116 }
1117 
1118 void Theme::unloadRect(const ImageRect &rect,
1119  const int start,
1120  const int end)
1121 {
1122  for (int f = start; f <= end; f ++)
1123  {
1124  if (rect.grid[f] != nullptr)
1125  rect.grid[f]->decRef();
1126  }
1127 }
1128 
1129 Image *Theme::getImageFromThemeXml(const std::string &name,
1130  const std::string &name2)
1131 {
1132  if (theme == nullptr)
1133  return nullptr;
1134 
1135  Skin *const skin = theme->load(name, name2, false);
1136  if (skin != nullptr)
1137  {
1138  const ImageRect &rect = skin->getBorder();
1139  if (rect.grid[0] != nullptr)
1140  {
1141  Image *const image = rect.grid[0];
1142  image->incRef();
1143  theme->unload(skin);
1144  return image;
1145  }
1146  theme->unload(skin);
1147  }
1148  return nullptr;
1149 }
1150 
1151 ImageSet *Theme::getImageSetFromThemeXml(const std::string &name,
1152  const std::string &name2,
1153  const int w, const int h)
1154 {
1155  if (theme == nullptr)
1156  return nullptr;
1157 
1158  Skin *const skin = theme->load(name, name2, false);
1159  if (skin != nullptr)
1160  {
1161  const ImageRect &rect = skin->getBorder();
1162  if (rect.grid[0] != nullptr)
1163  {
1164  Image *const image = rect.grid[0];
1165  const SDL_Rect &rect2 = image->mBounds;
1166  if ((rect2.w != 0u) && (rect2.h != 0u))
1167  {
1168  ImageSet *const imageSet = Loader::getSubImageSet(
1169  image, w, h);
1170  theme->unload(skin);
1171  return imageSet;
1172  }
1173  }
1174  theme->unload(skin);
1175  }
1176  return nullptr;
1177 }
1178 
1179 #define readValue(name) \
1180  { \
1181  tmpData = reinterpret_cast<XmlChar*>( \
1182  XmlNodeGetContent(infoNode)); \
1183  info->name = tmpData; \
1184  XmlFree(tmpData); \
1185  }
1186 
1187 #define readIntValue(name) \
1188  { \
1189  tmpData = reinterpret_cast<XmlChar*>( \
1190  XmlNodeGetContent(infoNode)); \
1191  info->name = atoi(tmpData); \
1192  XmlFree(tmpData); \
1193  }
1194 
1195 #define readFloatValue(name) \
1196  { \
1197  tmpData = reinterpret_cast<XmlChar*>( \
1198  XmlNodeGetContent(infoNode)); \
1199  info->name = static_cast<float>(atof(tmpData)); \
1200  XmlFree(tmpData); \
1201  }
1202 
1203 ThemeInfo *Theme::loadInfo(const std::string &themeName)
1204 {
1205  std::string path;
1206  if (themeName.empty())
1207  {
1208  path = "graphics/gui/info.xml";
1209  }
1210  else
1211  {
1212  path = pathJoin(defaultThemePath,
1213  themeName,
1214  "info.xml");
1215  }
1216  logger->log("loading: " + path);
1217  XML::Document *const doc = Loader::getXml(path,
1219  SkipError_false);
1220  if (doc == nullptr)
1221  return nullptr;
1222  XmlNodeConstPtrConst rootNode = doc->rootNode();
1223 
1224  if ((rootNode == nullptr) || !xmlNameEqual(rootNode, "info"))
1225  {
1226  doc->decRef();
1227  return nullptr;
1228  }
1229 
1230  ThemeInfo *const info = new ThemeInfo;
1231 
1232  const std::string fontSize2("fontSize_" + mScreenDensity);
1233  const std::string npcfontSize2("npcfontSize_" + mScreenDensity);
1234  XmlChar *tmpData = nullptr;
1235  for_each_xml_child_node(infoNode, rootNode)
1236  {
1237  if (xmlNameEqual(infoNode, "name"))
1238  readValue(name)
1239  else if (xmlNameEqual(infoNode, "copyright"))
1240  readValue(copyright)
1241  else if (xmlNameEqual(infoNode, "font"))
1242  readValue(font)
1243  else if (xmlNameEqual(infoNode, "boldFont"))
1245  else if (xmlNameEqual(infoNode, "particleFont"))
1246  readValue(particleFont)
1247  else if (xmlNameEqual(infoNode, "helpFont"))
1248  readValue(helpFont)
1249  else if (xmlNameEqual(infoNode, "secureFont"))
1250  readValue(secureFont)
1251  else if (xmlNameEqual(infoNode, "npcFont"))
1252  readValue(npcFont)
1253  else if (xmlNameEqual(infoNode, "japanFont"))
1254  readValue(japanFont)
1255  else if (xmlNameEqual(infoNode, "chinaFont"))
1256  readValue(chinaFont)
1257  else if (xmlNameEqual(infoNode, "fontSize"))
1258  readIntValue(fontSize)
1259  else if (xmlNameEqual(infoNode, "npcfontSize"))
1260  readIntValue(npcfontSize)
1261  else if (xmlNameEqual(infoNode, "guialpha"))
1262  readFloatValue(guiAlpha)
1263  else if (xmlNameEqual(infoNode, fontSize2.c_str()))
1264  readIntValue(fontSize)
1265  else if (xmlNameEqual(infoNode, npcfontSize2.c_str()))
1266  readIntValue(npcfontSize)
1267  }
1268  doc->decRef();
1269  return info;
1270 }
1271 
1272 ThemeColorIdT Theme::getIdByChar(const signed char c, bool &valid) const
1273 {
1274  const CharColors::const_iterator it = mCharColors.find(c);
1275  if (it != mCharColors.end())
1276  {
1277  valid = true;
1278  return static_cast<ThemeColorIdT>((*it).second);
1279  }
1280 
1281  valid = false;
1282  return ThemeColorId::BROWSERBOX;
1283 }
ItemOptionDb::OptionInfos mOptions
std::string name
Definition: theme.cpp:299
#define A_DELETE_COPY(func)
Definition: localconsts.h:52
Configuration branding
#define FOR_EACH(type, iter, array)
Definition: foreach.h:24
ProgressColorId ::T ProgressColorIdT
std::string getStringValue(const std::string &key) const
const bool SkipError_false
Definition: skiperror.h:29
static ImageSet * getImageSetFromThemeXml(const std::string &name, const std::string &name2, const int w, const int h)
Definition: theme.cpp:1151
void log1(const char *const log_text)
Definition: logger.cpp:222
Font * boldFont
Definition: gui.cpp:111
std::map< std::string, int > StringIntMap
Definition: stringmap.h:27
static void fillSkinsList(StringVect &list)
Definition: theme.cpp:563
Definition: skin.h:35
StringVect::const_iterator StringVectCIter
Definition: stringvector.h:30
void unload(Skin *const skin)
Definition: theme.cpp:249
static int readProgressType(const std::string &type)
Definition: theme.cpp:969
static void unloadRect(const ImageRect &rect, const int start=0, const int end=8)
Definition: theme.cpp:1118
void loadColors(std::string file="")
Definition: theme.cpp:1000
virtual void decRef()
Definition: resource.cpp:49
GradientType ::T GradientTypeT
Definition: gradienttype.h:37
static Image * getImageFromThemeXml(const std::string &name, const std::string &name2)
Definition: theme.cpp:1129
void freeList(List *const handle)
Definition: fs.cpp:268
Skins mSkins
Definition: theme.h:187
std::string pathJoin(std::string str1, const std::string &str2)
std::string partType
Definition: theme.cpp:365
#define readFloatValue(name)
Definition: theme.cpp:1195
ImageSet * getImageSet(const std::string &imagePath, const int w, const int h)
Configuration config
#define final
Definition: localconsts.h:45
static std::string getThemePath()
Definition: theme.h:66
int getProperty(const xmlNodePtr node, const char *const name, int def)
Definition: libxml.cpp:171
static GradientTypeT readColorGradient(const std::string &grad)
Definition: theme.cpp:947
Skins::iterator SkinIterator
Definition: theme.h:185
static void selectSkin()
Definition: theme.cpp:597
Theme()
Definition: theme.cpp:77
ThemeColorIdT getIdByChar(const signed char c, bool &valid) const
Definition: theme.cpp:1272
static void prepareThemePath()
Definition: theme.cpp:603
bool isDirectory(std::string name)
Definition: fs.cpp:238
std::vector< std::string > StringVect
Definition: stringvector.h:28
static std::string mThemePath
Definition: theme.h:189
ProgressColors mProgressColors
Definition: theme.h:204
~Theme()
Definition: theme.cpp:146
static const Color BLACK
Definition: palette.h:47
#define A_NONNULL(...)
Definition: localconsts.h:179
ImageRect * rect
Definition: theme.cpp:370
void addListener(const std::string &key, ConfigListener *const listener)
int compareStrI(const std::string &a, const std::string &b)
Logger * logger
Definition: logger.cpp:95
int xPos
Definition: theme.cpp:366
#define A_DEFAULT_COPY(func)
Definition: localconsts.h:40
static void fillFontsList(StringVect &list)
Definition: theme.cpp:569
ThemeColorId ::T ThemeColorIdT
Definition: themecolorid.h:256
bool findLast(const std::string &str1, const std::string &str2)
float mMinimumOpacity
Definition: theme.h:201
static ImageSet * getImageSetFromTheme(const std::string &path, const int w, const int h)
Definition: theme.cpp:659
void updateAlpha()
Definition: theme.cpp:280
const bool UseVirtFs_true
Definition: usevirtfs.h:29
std::vector< DyePalette * > ProgressColors
Definition: theme.h:203
static bool tryThemePath(const std::string &themePath)
Definition: theme.cpp:545
#define CAST_S32
Definition: cast.h:29
bool findCutLast(std::string &str1, const std::string &str2)
std::string strprintf(const char *const format,...)
Definition: stringutils.cpp:99
void delete_all(Container &c)
Definition: dtor.h:55
const ImageRect & getBorder() const
Definition: skin.h:67
Image * grid[9]
Definition: imagerect.h:41
#define readIntValue(name)
Definition: theme.cpp:1187
Image * getImage(const std::string &idPath)
Definition: imageloader.cpp:85
bool info(InputEvent &event)
Definition: commands.cpp:56
void optionChanged(const std::string &name)
Definition: theme.cpp:290
ImageSet * getSubImageSet(Image *const parent, const int width, const int height)
static Image * getImageFromTheme(const std::string &path)
Definition: theme.cpp:654
static Color readColor(const std::string &description)
Definition: theme.cpp:906
Colors mColors
Definition: palette.h:153
void loadRect(ImageRect &image, const std::string &name, const std::string &name2, const int start=0, const int end=8)
Definition: theme.cpp:1092
Definition: theme.h:52
static void fillSoundsList(StringVect &list)
Definition: theme.cpp:577
SkinHelper()
Definition: theme.cpp:351
int height
Definition: theme.cpp:369
XML::Document * getXml(const std::string &idPath, const UseVirtFs useResman, const SkipError skipError)
Definition: xmlloader.cpp:55
void setMinimumOpacity(const float minimumOpacity)
Definition: theme.cpp:271
xmlNodePtr rootNode()
Definition: libxml.cpp:166
static std::string mScreenDensity
Definition: theme.h:191
void permitLinks(const bool val)
Definition: fs.cpp:802
Image * getSubImage(Image *const parent, const int x, const int y, const int width, const int height)
static std::string defaultThemePath
Definition: theme.cpp:55
virtual void incRef()
Definition: resource.cpp:37
Skin * load(const std::string &filename, const std::string &filename2, const bool full=true, const std::string &defaultPath=getThemePath())
Definition: theme.cpp:178
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:1774
Theme * theme
Definition: theme.cpp:61
#define A_UNUSED
Definition: localconsts.h:171
#define loadGrid()
Definition: theme.cpp:1079
void getColor(const unsigned int intensity, unsigned int(&color)[3]) const
Definition: dyepalette.cpp:151
static ThemeInfo * loadInfo(const std::string &themeName)
Definition: theme.cpp:1203
static int readColorType(const std::string &type)
Definition: theme.cpp:665
#define for_each_xml_child_node(var, parent)
Definition: libxml.h:160
bool exists(std::string name)
Definition: fs.cpp:123
Skin * readSkin(const std::string &filename0, const bool full)
Definition: theme.cpp:393
CharColors mCharColors
Definition: palette.h:154
#define readValue(name)
Definition: theme.cpp:1179
static std::string mThemeName
Definition: theme.h:190
Definition: image.h:61
StringVect names
Definition: list.h:39
static void initDefaultThemePath()
Definition: theme.cpp:64
Definition: color.h:74
#define restrict
Definition: localconsts.h:176
static Color getProgressColor(const ProgressColorIdT type, const float progress)
Definition: theme.cpp:154
int yPos
Definition: theme.cpp:367
const int THEME_PALETTES
Definition: theme.h:30
#define CAST_SIZE
Definition: cast.h:33
Skin * loadSkinRect(ImageRect &image, const std::string &name, const std::string &name2, const int start=0, const int end=8)
Definition: theme.cpp:1106
#define loadList(key, mob)
SDL_Rect mBounds
Definition: image.h:210
#define CHECKLISTENERS
Definition: localconsts.h:305
bool loadList(const SkinParameter *const params, const size_t size)
Definition: theme.cpp:374
int width
Definition: theme.cpp:368
void log(const char *const log_text,...)
Definition: logger.cpp:243
void updateAlpha(const float minimumOpacityAllowed=0.0F)
Definition: skin.cpp:103
void getDirs(std::string dirName, StringVect &list)
Definition: fs.cpp:216
const bool SkipError_true
Definition: skiperror.h:29
List * enumerateFiles(std::string dirName)
Definition: fs.cpp:146
GraphicsManager graphicsManager
void decRef()
Definition: image.cpp:521
static const SkinParameter imageParam[]
Definition: theme.cpp:341
std::string getDensityString() const
static std::string resolveThemePath(const std::string &path)
Definition: theme.cpp:626
void removeListener(const std::string &key, ConfigListener *const listener)
Image * image
Definition: theme.cpp:372
static const SkinParameter skinParam[]
Definition: theme.cpp:302
int instances
Definition: skin.h:120
void getFiles(std::string dirName, StringVect &list)
Definition: fs.cpp:171