The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
theme.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /** @file */
16 
17 #include "theme.hpp"
18 
19 #include "font/sdl_ttf.hpp"
20 #include "gettext.hpp"
22 #include "hotkey/hotkey_item.hpp"
23 #include "log.hpp"
24 #include "sdl/rect.hpp"
26 #include "wml_exception.hpp"
27 
28 static lg::log_domain log_display("display");
29 #define DBG_DP LOG_STREAM(debug, log_display)
30 #define LOG_DP LOG_STREAM(info, log_display)
31 #define ERR_DP LOG_STREAM(err, log_display)
32 
33 namespace
34 {
35 const int XDim = 1024;
36 const int YDim = 768;
37 
38 const size_t DefaultFontSize = font::SIZE_NORMAL;
39 const color_t DefaultFontRGB {200, 200, 200};
40 
41 _rect ref_rect {0, 0, 0, 0};
42 }
43 
44 static size_t compute(std::string expr, size_t ref1, size_t ref2 = 0)
45 {
46  size_t ref = 0;
47  if(expr[0] == '=') {
48  ref = ref1;
49  expr = expr.substr(1);
50  } else if((expr[0] == '+') || (expr[0] == '-')) {
51  ref = ref2;
52  }
53 
54  return ref + atoi(expr.c_str());
55 }
56 
57 // If x2 or y2 are not specified, use x1 and y1 values
58 static _rect read_rect(const config& cfg)
59 {
60  _rect rect {0, 0, 0, 0};
61  std::vector<std::string> items = utils::split(cfg["rect"].str());
62  if(items.size() >= 1)
63  rect.x1 = atoi(items[0].c_str());
64 
65  if(items.size() >= 2)
66  rect.y1 = atoi(items[1].c_str());
67 
68  if(items.size() >= 3)
69  rect.x2 = atoi(items[2].c_str());
70  else
71  rect.x2 = rect.x1;
72 
73  if(items.size() >= 4)
74  rect.y2 = atoi(items[3].c_str());
75  else
76  rect.y2 = rect.y1;
77 
78  return rect;
79 }
80 
81 static SDL_Rect read_sdl_rect(const config& cfg)
82 {
83  SDL_Rect sdlrect;
84  const _rect rect = read_rect(cfg);
85  sdlrect.x = rect.x1;
86  sdlrect.y = rect.y1;
87  sdlrect.w = (rect.x2 > rect.x1) ? (rect.x2 - rect.x1) : 0;
88  sdlrect.h = (rect.y2 > rect.y1) ? (rect.y2 - rect.y1) : 0;
89 
90  return sdlrect;
91 }
92 
93 static std::string resolve_rect(const std::string& rect_str)
94 {
95  _rect rect {0, 0, 0, 0};
96  std::stringstream resolved;
97  const std::vector<std::string> items = utils::split(rect_str.c_str());
98  if(items.size() >= 1) {
99  rect.x1 = compute(items[0], ref_rect.x1, ref_rect.x2);
100  resolved << rect.x1;
101  }
102  if(items.size() >= 2) {
103  rect.y1 = compute(items[1], ref_rect.y1, ref_rect.y2);
104  resolved << "," << rect.y1;
105  }
106  if(items.size() >= 3) {
107  rect.x2 = compute(items[2], ref_rect.x2, rect.x1);
108  resolved << "," << rect.x2;
109  }
110  if(items.size() >= 4) {
111  rect.y2 = compute(items[3], ref_rect.y2, rect.y1);
112  resolved << "," << rect.y2;
113  }
114 
115  // DBG_DP << "Rect " << rect_str << "\t: " << resolved.str() << "\n";
116 
117  ref_rect = rect;
118  return resolved.str();
119 }
120 
121 static config& find_ref(const std::string& id, config& cfg, bool remove = false)
122 {
123  static config empty_config;
124 
126  for(config::all_children_iterator i = itors.begin(); i != itors.end(); ++i) {
127  config& icfg = i->cfg;
128  if(i->cfg["id"] == id) {
129  if(remove) {
130  cfg.erase(i);
131  return empty_config;
132  } else {
133  return icfg;
134  }
135  }
136 
137  // Recursively look in children.
138  config& c = find_ref(id, icfg, remove);
139  if(&c != &empty_config) {
140  return c;
141  }
142  }
143 
144  // Not found.
145  return empty_config;
146 }
147 
148 #ifdef DEBUG
149 
150 // to be called from gdb
151 static config& find_ref(const char* id, config& cfg)
152 {
153  return find_ref(std::string(id), cfg);
154 }
155 
156 namespace
157 {
158 // avoid some compiler warnings in stricter mode.
159 static config cfg;
160 static config& result = find_ref("", cfg);
161 } // namespace
162 
163 #endif
164 
165 /**
166  * Returns a copy of the wanted resolution.
167  *
168  * The function returns a copy since our caller uses a copy of this resolution
169  * as base to expand a partial resolution.
170  *
171  * @param resolutions A config object containing the expanded
172  * resolutions.
173  * @param id The id of the resolution to return.
174  *
175  * @throw config::error If the @p id is not found.
176  *
177  * @returns A copy of the resolution config.
178  */
179 static config get_resolution(const config& resolutions, const std::string& id)
180 {
181  for(const auto& resolution : resolutions.child_range("resolution")) {
182  if(resolution["id"] == id) {
183  return resolution;
184  }
185  }
186 
187  throw config::error("[partialresolution] refers to non-existent [resolution] " + id);
188 }
189 
190 /**
191  * Returns a config with all partial resolutions of a theme expanded.
192  *
193  * @param theme The original object, whose objects need to be
194  * expanded.
195  *
196  * @returns A new object with the expanded resolutions in
197  * a theme. This object no longer contains
198  * partial resolutions.
199  */
201 {
202  config result;
203 
204  // Add all the resolutions
205  for(const auto& resolution : theme.child_range("resolution")) {
206  result.add_child("resolution", resolution);
207  }
208 
209  // Resolve all the partialresolutions
210  for(const auto& part : theme.child_range("partialresolution")) {
211  config resolution = get_resolution(result, part["inherits"]);
212  resolution.merge_attributes(part);
213 
214  for(const auto& remove : part.child_range("remove")) {
215  VALIDATE(!remove["id"].empty(), missing_mandatory_wml_key("[theme][partialresolution][remove]", "id"));
216 
217  find_ref(remove["id"], resolution, true);
218  }
219 
220  for(const auto& change : part.child_range("change")) {
221  VALIDATE(!change["id"].empty(), missing_mandatory_wml_key("[theme][partialresolution][change]", "id"));
222 
223  config& target = find_ref(change["id"], resolution, false);
224  target.merge_attributes(change);
225  }
226 
227  // cannot add [status] sub-elements, but who cares
228  for(const auto& add : part.child_range("add")) {
229  for(const auto& child : add.all_children_range()) {
230  resolution.add_child(child.key, child.cfg);
231  }
232  }
233 
234  result.add_child("resolution", resolution);
235  }
236 
237  return result;
238 }
239 
240 static void do_resolve_rects(const config& cfg, config& resolved_config, config* resol_cfg = nullptr)
241 {
242  // recursively resolve children
243  for(const config::any_child& value : cfg.all_children_range()) {
244  config& childcfg = resolved_config.add_child(value.key);
245  do_resolve_rects(value.cfg, childcfg, value.key == "resolution" ? &childcfg : resol_cfg);
246  }
247 
248  // copy all key/values
249  resolved_config.merge_attributes(cfg);
250 
251  // override default reference rect with "ref" parameter if any
252  if(!cfg["ref"].empty()) {
253  if(resol_cfg == nullptr) {
254  ERR_DP << "Use of ref= outside a [resolution] block" << std::endl;
255  } else {
256  // DBG_DP << ">> Looking for " << cfg["ref"] << "\n";
257  const config& ref = find_ref(cfg["ref"], *resol_cfg);
258 
259  if(ref["id"].empty()) {
260  ERR_DP << "Reference to non-existent rect id \"" << cfg["ref"] << "\"" << std::endl;
261  } else if(ref["rect"].empty()) {
262  ERR_DP << "Reference to id \"" << cfg["ref"] << "\" which does not have a \"rect\"\n";
263  } else {
264  ref_rect = read_rect(ref);
265  }
266  }
267  }
268  // resolve the rect value to absolute coordinates
269  if(!cfg["rect"].empty()) {
270  resolved_config["rect"] = resolve_rect(cfg["rect"]);
271  }
272 }
273 
275  : location_modified_(false)
276  , id_()
277  , loc_(sdl::empty_rect)
278  , relative_loc_(sdl::empty_rect)
279  , last_screen_(sdl::empty_rect)
280  , xanchor_(object::FIXED)
281  , yanchor_(object::FIXED)
282 {
283 }
284 
286  : location_modified_(false)
287  , id_(cfg["id"])
288  , loc_(read_sdl_rect(cfg))
289  , relative_loc_(sdl::empty_rect)
290  , last_screen_(sdl::empty_rect)
291  , xanchor_(read_anchor(cfg["xanchor"]))
292  , yanchor_(read_anchor(cfg["yanchor"]))
293 {
294 }
295 
297  : size(0.0)
298  , background_image()
299  , tile_image()
300  , show_border(true)
301 {
302 }
303 
305  : size(cfg["border_size"].to_double())
306  , background_image(cfg["background_image"])
307  , tile_image(cfg["tile_image"])
308  , show_border(cfg["show_border"].to_bool(true))
309 {
310  VALIDATE(size >= 0.0 && size <= 0.5, _("border_size should be between 0.0 and 0.5."));
311 }
312 
313 SDL_Rect& theme::object::location(const SDL_Rect& screen) const
314 {
315  if(last_screen_ == screen && !location_modified_)
316  return relative_loc_;
317 
318  last_screen_ = screen;
319 
320  switch(xanchor_) {
321  case FIXED:
322  relative_loc_.x = loc_.x;
323  relative_loc_.w = loc_.w;
324  break;
325  case TOP_ANCHORED:
326  relative_loc_.x = loc_.x;
327  relative_loc_.w = screen.w - std::min<size_t>(XDim - loc_.w, screen.w);
328  break;
329  case BOTTOM_ANCHORED:
330  relative_loc_.x = screen.w - std::min<size_t>(XDim - loc_.x, screen.w);
331  relative_loc_.w = loc_.w;
332  break;
333  case PROPORTIONAL:
334  relative_loc_.x = (loc_.x * screen.w) / XDim;
335  relative_loc_.w = (loc_.w * screen.w) / XDim;
336  break;
337  default:
338  assert(false);
339  }
340 
341  switch(yanchor_) {
342  case FIXED:
343  relative_loc_.y = loc_.y;
344  relative_loc_.h = loc_.h;
345  break;
346  case TOP_ANCHORED:
347  relative_loc_.y = loc_.y;
348  relative_loc_.h = screen.h - std::min<size_t>(YDim - loc_.h, screen.h);
349  break;
350  case BOTTOM_ANCHORED:
351  relative_loc_.y = screen.h - std::min<size_t>(YDim - loc_.y, screen.h);
352  relative_loc_.h = loc_.h;
353  break;
354  case PROPORTIONAL:
355  relative_loc_.y = (loc_.y * screen.h) / YDim;
356  relative_loc_.h = (loc_.h * screen.h) / YDim;
357  break;
358  default:
359  assert(false);
360  }
361 
362  relative_loc_.x = std::min<int>(relative_loc_.x, screen.w);
363  relative_loc_.w = std::min<int>(relative_loc_.w, screen.w - relative_loc_.x);
364  relative_loc_.y = std::min<int>(relative_loc_.y, screen.h);
365  relative_loc_.h = std::min<int>(relative_loc_.h, screen.h - relative_loc_.y);
366 
367  return relative_loc_;
368 }
369 
371 {
372  static const std::string top_anchor = "top", left_anchor = "left", bot_anchor = "bottom", right_anchor = "right",
373  proportional_anchor = "proportional";
374  if(str == top_anchor || str == left_anchor)
375  return TOP_ANCHORED;
376  else if(str == bot_anchor || str == right_anchor)
377  return BOTTOM_ANCHORED;
378  else if(str == proportional_anchor)
379  return PROPORTIONAL;
380  else
381  return FIXED;
382 }
383 
385 {
386  loc_.x = rect.x1;
387  loc_.y = rect.y1;
388  loc_.w = rect.x2 - rect.x1;
389  loc_.h = rect.y2 - rect.y1;
390  location_modified_ = true;
391 }
392 
393 void theme::object::modify_location(std::string rect_str, SDL_Rect location_ref_rect)
394 {
395  _rect rect {0, 0, 0, 0};
396  const std::vector<std::string> items = utils::split(rect_str.c_str());
397  if(items.size() >= 1) {
398  rect.x1 = compute(items[0], location_ref_rect.x, location_ref_rect.x + location_ref_rect.w);
399  }
400  if(items.size() >= 2) {
401  rect.y1 = compute(items[1], location_ref_rect.y, location_ref_rect.y + location_ref_rect.h);
402  }
403  if(items.size() >= 3) {
404  rect.x2 = compute(items[2], location_ref_rect.x + location_ref_rect.w, rect.x1);
405  }
406  if(items.size() >= 4) {
407  rect.y2 = compute(items[3], location_ref_rect.y + location_ref_rect.h, rect.y1);
408  }
409  modify_location(rect);
410 }
411 
413  : text_()
414  , icon_()
415  , font_()
416  , font_rgb_set_(false)
417  , font_rgb_(DefaultFontRGB)
418 {
419 }
420 
422  : object(cfg)
423  , text_(cfg["prefix"].str() + cfg["text"].str() + cfg["postfix"].str())
424  , icon_(cfg["icon"])
425  , font_(cfg["font_size"])
426  , font_rgb_set_(false)
427  , font_rgb_(DefaultFontRGB)
428 {
429  if(font_ == 0)
430  font_ = DefaultFontSize;
431 
432  if(cfg.has_attribute("font_rgb")) {
433  font_rgb_ = color_t::from_rgb_string(cfg["font_rgb"]);
434  font_rgb_set_ = true;
435  }
436 }
437 
439  : object(cfg)
440  , prefix_(cfg["prefix"].str() + cfg["prefix_literal"].str())
441  , postfix_(cfg["postfix_literal"].str() + cfg["postfix"].str())
442  , label_()
443  , font_(cfg["font_size"])
444  , font_rgb_set_(false)
445  , font_rgb_(DefaultFontRGB)
446 {
447  if(font_ == 0)
448  font_ = DefaultFontSize;
449 
450  if(const config& label_child = cfg.child("label")) {
451  label_ = label(label_child);
452  }
453 
454  if(cfg.has_attribute("font_rgb")) {
455  font_rgb_ = color_t::from_rgb_string(cfg["font_rgb"]);
456  font_rgb_set_ = true;
457  }
458 }
459 
461  : object(cfg)
462  , image_(cfg["image"])
463 {
464 }
465 
467  : object()
468  , title_()
469  , tooltip_()
470  , image_()
471  , overlay_()
472  , black_line_(false)
473 {
474 }
476  : object(cfg)
477  , title_(cfg["title"].str() + cfg["title_literal"].str())
478  , tooltip_(cfg["tooltip"])
479  , image_(cfg["image"])
480  , overlay_(cfg["overlay"])
481  , black_line_(cfg["black_line"].to_bool(false))
482 {
483 }
484 
486  : object()
487  , button_(true)
488  , context_(false)
489  , title_()
490  , tooltip_()
491  , image_()
492  , overlay_()
493  , items_()
494 {
495 }
496 
498  : object(cfg)
499  , button_(cfg["button"].to_bool(true))
500  , context_(cfg["is_context_menu"].to_bool(false))
501  , title_(cfg["title"].str() + cfg["title_literal"].str())
502  , tooltip_(cfg["tooltip"])
503  , image_(cfg["image"])
504  , overlay_(cfg["overlay"])
505  , items_()
506 {
507  for(const auto& item : utils::split(cfg["items"])) {
508  items_.emplace_back(config {"id", item});
509  }
510 
511  if(cfg["auto_tooltip"].to_bool() && tooltip_.empty() && items_.size() == 1) {
512  tooltip_ = hotkey::get_description(items_[0]["id"]) + hotkey::get_names(items_[0]["id"]) + "\n"
513  + hotkey::get_tooltip(items_[0]["id"]);
514  } else if(cfg["tooltip_name_prepend"].to_bool() && items_.size() == 1) {
515  tooltip_ = hotkey::get_description(items_[0]["id"]) + hotkey::get_names(items_[0]["id"]) + "\n" + tooltip_;
516  }
517 }
518 
520  : object()
521  , context_(false)
522  , auto_tooltip_(false)
523  , tooltip_name_prepend_(false)
524  , title_()
525  , tooltip_()
526  , image_()
527  , overlay_()
528  , type_()
529  , items_()
530 {
531 }
532 
534  : object(cfg)
535  , context_(cfg["is_context_menu"].to_bool())
536  , auto_tooltip_(cfg["auto_tooltip"].to_bool(false))
537  , tooltip_name_prepend_(cfg["tooltip_name_prepend"].to_bool(false))
538  , title_(cfg["title"].str() + cfg["title_literal"].str())
539  , tooltip_(cfg["tooltip"])
540  , image_(cfg["image"])
541  , overlay_(cfg["overlay"])
542  , type_(cfg["type"])
543  , items_(utils::split(cfg["items"]))
544 {
545 }
546 
548 {
549  std::stringstream result;
550  if(auto_tooltip_ && tooltip_.empty() && items_.size() > index) {
551  result << hotkey::get_description(items_[index]);
552  if(!hotkey::get_names(items_[index]).empty())
553  result << "\n" << _("Hotkey(s): ") << hotkey::get_names(items_[index]);
554  result << "\n" << hotkey::get_tooltip(items_[index]);
555  } else if(tooltip_name_prepend_ && items_.size() == 1) {
556  result << hotkey::get_description(items_[index]);
557  if(!hotkey::get_names(items_[index]).empty())
558  result << "\n" << _("Hotkey(s): ") << hotkey::get_names(items_[index]);
559  result << "\n" << tooltip_;
560  } else {
561  result << tooltip_;
562  }
563 
564  return result.str();
565 }
566 
567 theme::theme(const config& cfg, const SDL_Rect& screen)
568  : theme_reset_event_("theme_reset")
569  , cur_theme()
570  , cfg_()
571  , panels_()
572  , labels_()
573  , menus_()
574  , actions_()
575  , context_()
576  , status_()
577  , main_map_()
578  , mini_map_()
579  , unit_image_()
580  , palette_()
581  , border_()
582 {
584  set_resolution(screen);
585 }
586 
587 bool theme::set_resolution(const SDL_Rect& screen)
588 {
589  bool result = false;
590 
591  int current_rating = 1000000;
592  const config* current = nullptr;
593  for(const config& i : cfg_.child_range("resolution")) {
594  int width = i["width"];
595  int height = i["height"];
596  LOG_DP << "comparing resolution " << screen.w << "," << screen.h << " to " << width << "," << height << "\n";
597  if(screen.w >= width && screen.h >= height) {
598  LOG_DP << "loading theme: " << width << "," << height << "\n";
599  current = &i;
600  result = true;
601  break;
602  }
603 
604  const int rating = width * height;
605  if(rating < current_rating) {
606  current = &i;
607  current_rating = rating;
608  }
609  }
610 
611  if(!current) {
612  if(cfg_.child_count("resolution")) {
613  ERR_DP << "No valid resolution found" << std::endl;
614  }
615  return false;
616  }
617 
618  std::map<std::string, std::string> title_stash_menus;
620  for(m = menus_.begin(); m != menus_.end(); ++m) {
621  if(!m->title().empty() && !m->get_id().empty())
622  title_stash_menus[m->get_id()] = m->title();
623  }
624 
625  std::map<std::string, std::string> title_stash_actions;
627  for(a = actions_.begin(); a != actions_.end(); ++a) {
628  if(!a->title().empty() && !a->get_id().empty())
629  title_stash_actions[a->get_id()] = a->title();
630  }
631 
632  panels_.clear();
633  labels_.clear();
634  status_.clear();
635  menus_.clear();
636  actions_.clear();
637  sliders_.clear();
638 
639  add_object(*current);
640 
641  for(m = menus_.begin(); m != menus_.end(); ++m) {
642  if(title_stash_menus.find(m->get_id()) != title_stash_menus.end())
643  m->set_title(title_stash_menus[m->get_id()]);
644  }
645 
646  for(a = actions_.begin(); a != actions_.end(); ++a) {
647  if(title_stash_actions.find(a->get_id()) != title_stash_actions.end())
648  a->set_title(title_stash_actions[a->get_id()]);
649  }
650 
652 
653  return result;
654 }
655 
656 void theme::add_object(const config& cfg)
657 {
658  if(const config& c = cfg.child("main_map")) {
659  main_map_ = object(c);
660  }
661 
662  if(const config& c = cfg.child("mini_map")) {
663  mini_map_ = object(c);
664  }
665 
666  if(const config& c = cfg.child("palette")) {
667  palette_ = object(c);
668  }
669 
670  if(const config& status_cfg = cfg.child("status")) {
671  for(const config::any_child& i : status_cfg.all_children_range()) {
672  status_.emplace(i.key, status_item(i.cfg));
673  }
674  if(const config& unit_image_cfg = status_cfg.child("unit_image")) {
675  unit_image_ = object(unit_image_cfg);
676  } else {
677  unit_image_ = object();
678  }
679  }
680 
681  for(const config& p : cfg.child_range("panel")) {
682  panel new_panel(p);
683  set_object_location(new_panel, p["rect"], p["ref"]);
684  panels_.push_back(new_panel);
685  }
686 
687  for(const config& lb : cfg.child_range("label")) {
688  label new_label(lb);
689  set_object_location(new_label, lb["rect"], lb["ref"]);
690  labels_.push_back(new_label);
691  }
692 
693  for(const config& m : cfg.child_range("menu")) {
694  menu new_menu(m);
695  DBG_DP << "adding menu: " << (new_menu.is_context() ? "is context" : "not context") << "\n";
696  if(new_menu.is_context())
697  context_ = new_menu;
698  else {
699  set_object_location(new_menu, m["rect"], m["ref"]);
700  menus_.push_back(new_menu);
701  }
702 
703  DBG_DP << "done adding menu...\n";
704  }
705 
706  for(const config& a : cfg.child_range("action")) {
707  action new_action(a);
708  DBG_DP << "adding action: " << (new_action.is_context() ? "is context" : "not context") << "\n";
709  if(new_action.is_context())
710  action_context_ = new_action;
711  else {
712  set_object_location(new_action, a["rect"], a["ref"]);
713  actions_.push_back(new_action);
714  }
715 
716  DBG_DP << "done adding action...\n";
717  }
718 
719  for(const config& s : cfg.child_range("slider")) {
720  slider new_slider(s);
721  DBG_DP << "adding slider\n";
722  set_object_location(new_slider, s["rect"], s["ref"]);
723  sliders_.push_back(new_slider);
724 
725  DBG_DP << "done adding slider...\n";
726  }
727 
728  if(const config& c = cfg.child("main_map_border")) {
729  border_ = border_t(c);
730  }
731 }
732 
734 {
735  for(std::vector<theme::panel>::iterator p = panels_.begin(); p != panels_.end(); ++p) {
736  if(p->get_id() == id) {
737  panels_.erase(p);
738  return;
739  }
740  }
741  for(std::vector<theme::label>::iterator l = labels_.begin(); l != labels_.end(); ++l) {
742  if(l->get_id() == id) {
743  labels_.erase(l);
744  return;
745  }
746  }
747  for(std::vector<theme::menu>::iterator m = menus_.begin(); m != menus_.end(); ++m) {
748  if(m->get_id() == id) {
749  menus_.erase(m);
750  return;
751  }
752  }
753  for(std::vector<theme::action>::iterator a = actions_.begin(); a != actions_.end(); ++a) {
754  if(a->get_id() == id) {
755  actions_.erase(a);
756  return;
757  }
758  }
759  for(std::vector<theme::slider>::iterator s = sliders_.begin(); s != sliders_.end(); ++s) {
760  if(s->get_id() == id) {
761  sliders_.erase(s);
762  return;
763  }
764  }
765 }
766 
768 {
769  theme::object ref_element = element;
770  if(ref_id.empty()) {
771  ref_id = element.get_id();
772  } else {
773  ref_element = find_element(ref_id);
774  }
775  if(ref_element.get_id() == ref_id) {
776  SDL_Rect location_ref_rect = ref_element.get_location();
777  element.modify_location(rect_str, location_ref_rect);
778  }
779 }
780 
781 void theme::modify(const config& cfg)
782 {
783  std::map<std::string, std::string> title_stash;
785  for(m = menus_.begin(); m != menus_.end(); ++m) {
786  if(!m->title().empty() && !m->get_id().empty())
787  title_stash[m->get_id()] = m->title();
788  }
789 
791  for(a = actions_.begin(); a != actions_.end(); ++a) {
792  if(!a->title().empty() && !a->get_id().empty())
793  title_stash[a->get_id()] = a->title();
794  }
795 
796  // Change existing theme objects.
797  for(const config& c : cfg.child_range("change")) {
798  std::string id = c["id"];
799  std::string ref_id = c["ref"];
800  theme::object& element = find_element(id);
801  if(element.get_id() == id)
802  set_object_location(element, c["rect"], ref_id);
803  }
804 
805  // Add new theme objects.
806  for(const config& c : cfg.child_range("add")) {
807  add_object(c);
808  }
809 
810  // Remove existent theme objects.
811  for(const config& c : cfg.child_range("remove")) {
812  remove_object(c["id"]);
813  }
814 
815  for(m = menus_.begin(); m != menus_.end(); ++m) {
816  if(title_stash.find(m->get_id()) != title_stash.end())
817  m->set_title(title_stash[m->get_id()]);
818  }
819  for(a = actions_.begin(); a != actions_.end(); ++a) {
820  if(title_stash.find(a->get_id()) != title_stash.end())
821  a->set_title(title_stash[a->get_id()]);
822  }
823 }
824 
826 {
827  static theme::object empty_object;
828  theme::object* res = &empty_object;
829  for(std::vector<theme::panel>::iterator p = panels_.begin(); p != panels_.end(); ++p) {
830  if(p->get_id() == id) {
831  res = &(*p);
832  }
833  }
834  for(std::vector<theme::label>::iterator l = labels_.begin(); l != labels_.end(); ++l) {
835  if(l->get_id() == id) {
836  res = &(*l);
837  }
838  }
839  for(std::vector<theme::menu>::iterator m = menus_.begin(); m != menus_.end(); ++m) {
840  if(m->get_id() == id) {
841  res = &(*m);
842  }
843  }
844  for(std::vector<theme::action>::iterator a = actions_.begin(); a != actions_.end(); ++a) {
845  if(a->get_id() == id) {
846  res = &(*a);
847  }
848  }
849  if(id == "main-map") {
850  res = &main_map_;
851  }
852  if(id == "mini-map") {
853  res = &mini_map_;
854  }
855  if(id == "palette") {
856  res = &palette_;
857  }
858  if(id == "unit-image") {
859  res = &unit_image_;
860  }
861  return *res;
862 }
863 
865 {
866  const std::map<std::string, status_item>::const_iterator i = status_.find(key);
867  if(i != status_.end())
868  return &i->second;
869  else
870  return nullptr;
871 }
872 
873 typedef std::map<std::string, config> known_themes_map;
874 known_themes_map theme::known_themes;
875 
877 {
878  known_themes.clear();
879  if(!cfg)
880  return;
881 
882  for(const config& thm : cfg->child_range("theme")) {
883  std::string thm_id = thm["id"];
884 
885  if(!thm["hidden"].to_bool(false)) {
886  known_themes[thm_id] = thm;
887  }
888  }
889 }
890 
891 std::vector<theme_info> theme::get_known_themes()
892 {
893  std::vector<theme_info> res;
894 
895  for(known_themes_map::const_iterator i = known_themes.begin(); i != known_themes.end(); ++i) {
896  res.push_back(theme_info());
897  res.back().id = i->first;
898  res.back().name = i->second["name"].t_str();
899  res.back().description = i->second["description"].t_str();
900  }
901 
902  return res;
903 }
904 
906 {
907  for(const theme::menu& m : menus_) {
908  if(m.get_id() == key)
909  return &m;
910  }
911  return nullptr;
912 }
913 
915 {
916  for(const theme::action& a : actions_) {
917  if(a.get_id() == key)
918  return &a;
919  }
920  return nullptr;
921 }
922 
924 {
925  theme::object* res = nullptr;
926 
927  for(std::vector<theme::action>::iterator a = actions_.begin(); a != actions_.end(); ++a) {
928  if(a->get_id() == id) {
929  res = &(*a);
930  a->set_title(new_title);
931  }
932  }
933 
934  for(std::vector<theme::menu>::iterator m = menus_.begin(); m != menus_.end(); ++m) {
935  if(m->get_id() == id) {
936  res = &(*m);
937  m->set_title(new_title);
938  }
939  }
940 
941  return res;
942 }
943 
945 {
946  std::string new_title;
947 
948  const config& cfg = find_ref(id, cfg_, false);
949  if(!cfg[title_tag].empty())
950  new_title = cfg[title_tag].str();
951 
952  return refresh_title(id, new_title);
953 }
954 
955 void theme::modify_label(const std::string& id, const std::string& text)
956 {
957  theme::label* label = dynamic_cast<theme::label*>(&find_element(id));
958  if(!label) {
959  LOG_DP << "Theme contains no label called '" << id << "'.\n";
960  return;
961  }
962  label->set_text(text);
963 }
std::vector< label > labels_
Definition: theme.hpp:293
size_t y2
Definition: theme.hpp:28
events::generic_event theme_reset_event_
Definition: theme.hpp:287
config cfg_
Definition: theme.hpp:291
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:419
std::string cur_theme
Definition: theme.hpp:290
static lg::log_domain log_display("display")
std::vector< char_t > string
size_t index(const utf8::string &str, const size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
virtual void notify_observers()
Definition: theme.hpp:28
std::vector< action > actions_
Definition: theme.hpp:295
static void set_known_themes(const config *cfg)
Definition: theme.cpp:876
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
const std::string tooltip(size_t index) const
Definition: theme.cpp:547
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
#define a
child_itors child_range(config_key_type key)
Definition: config.cpp:362
std::vector< config > items_
Definition: theme.hpp:237
color_t font_rgb_
Definition: theme.hpp:141
std::vector< panel > panels_
Definition: theme.hpp:292
static config expand_partialresolution(const config &theme)
Returns a config with all partial resolutions of a theme expanded.
Definition: theme.cpp:200
std::vector< slider > sliders_
Definition: theme.hpp:296
const std::vector< std::string > items
object mini_map_
Definition: theme.hpp:303
object palette_
Definition: theme.hpp:303
static void do_resolve_rects(const config &cfg, config &resolved_config, config *resol_cfg=nullptr)
Definition: theme.cpp:240
CONSTEXPR const SDL_Rect empty_rect
Definition: rect.hpp:32
void add_object(const config &cfg)
Definition: theme.cpp:656
theme::object & find_element(const std::string &id)
Definition: theme.cpp:825
void remove_object(const std::string &id)
Definition: theme.cpp:733
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
status_item(const config &cfg)
Definition: theme.cpp:438
static std::map< std::string, config > known_themes
Definition: theme.hpp:289
SDL_Rect & location(const SDL_Rect &screen) const
Definition: theme.cpp:313
std::string missing_mandatory_wml_key(const std::string &section, const std::string &key, const std::string &primary_key, const std::string &primary_value)
Returns a standard message for a missing wml key.
const int SIZE_NORMAL
Definition: constants.cpp:19
map_location loc_
const std::string & get_tooltip(const std::string &command)
std::string get_names(const std::string &id)
Returns a comma-separated string of hotkey names.
unsigned child_count(config_key_type key) const
Definition: config.cpp:390
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
size_t x2
Definition: theme.hpp:28
static config get_resolution(const config &resolutions, const std::string &id)
Returns a copy of the wanted resolution.
Definition: theme.cpp:179
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
std::vector< menu > menus_
Definition: theme.hpp:294
const status_item * get_status_item(const std::string &item) const
Definition: theme.cpp:864
menu context_
Definition: theme.hpp:298
const std::string & get_id() const
Definition: theme.hpp:49
theme(const config &cfg, const SDL_Rect &screen)
Definition: theme.cpp:567
double size
Definition: theme.hpp:84
const menu * get_menu_item(const std::string &key) const
Definition: theme.cpp:905
void modify(const config &cfg)
Definition: theme.cpp:781
size_t x1
Definition: theme.hpp:28
all_children_iterator erase(const all_children_iterator &i)
Definition: config.cpp:638
bool has_attribute(config_key_type key) const
Definition: config.cpp:213
#define ERR_DP
Definition: theme.cpp:31
size_t size(const utf8::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
#define LOG_DP
Definition: theme.cpp:30
static _rect read_rect(const config &cfg)
Definition: theme.cpp:58
static std::vector< theme_info > get_known_themes()
Definition: theme.cpp:891
mock_party p
object * refresh_title(const std::string &id, const std::string &new_title)
Definition: theme.cpp:923
static void expr(LexState *ls, expdesc *v)
Definition: lparser.cpp:1078
std::string tooltip_
Definition: theme.hpp:236
static map_location::DIRECTION s
boost::iterator_range< all_children_iterator > all_children_itors
Definition: config.hpp:663
Definition: theme.hpp:37
void modify_label(const std::string &id, const std::string &text)
Definition: theme.cpp:955
Definitions related to theme-support.
size_t i
Definition: function.cpp:933
size_t font_
Definition: theme.hpp:113
bool is_context() const
Definition: theme.hpp:165
const action * get_action_item(const std::string &key) const
Definition: theme.cpp:914
const std::string & get_description(const std::string &command)
config & add_child(config_key_type key)
Definition: config.cpp:475
void merge_attributes(const config &)
Definition: config.cpp:734
bool set_resolution(const SDL_Rect &screen)
Definition: theme.cpp:587
#define DBG_DP
Definition: theme.cpp:29
void set_text(const std::string &text)
Definition: theme.hpp:103
static config & find_ref(const std::string &id, config &cfg, bool remove=false)
Definition: theme.cpp:121
border_t border_
Definition: theme.hpp:305
Contains the SDL_Rect helper code.
static SDL_Rect read_sdl_rect(const config &cfg)
Definition: theme.cpp:81
std::map< std::string, config > known_themes_map
Definition: theme.cpp:873
object * refresh_title2(const std::string &id, const std::string &title_tag)
Definition: theme.cpp:944
Standard logging facilities (interface).
void modify_location(const _rect &rect)
Definition: theme.cpp:384
bool is_context() const
Definition: theme.hpp:220
void set_object_location(theme::object &element, std::string rect_str, std::string ref_id)
Definition: theme.cpp:767
object unit_image_
Definition: theme.hpp:303
const SDL_Rect & get_location() const
Definition: theme.hpp:48
point resolution()
Definition: general.cpp:375
static size_t compute(std::string expr, size_t ref1, size_t ref2=0)
Definition: theme.cpp:44
color_t font_rgb_
Definition: theme.hpp:115
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
static ANCHORING read_anchor(const std::string &str)
Definition: theme.cpp:370
size_t y1
Definition: theme.hpp:28
mock_char c
bool font_rgb_set_
Definition: theme.hpp:114
std::map< std::string, status_item > status_
Definition: theme.hpp:301
static color_t from_rgb_string(const std::string &c)
Creates a new opaque color_t object from a string variable in "R,G,B" format.
Definition: color.cpp:41
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:867
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
static std::string resolve_rect(const std::string &rect_str)
Definition: theme.cpp:93
object main_map_
Definition: theme.hpp:303
panel(const config &cfg)
Definition: theme.cpp:460
action action_context_
Definition: theme.hpp:299