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