The Battle for Wesnoth  1.15.1+dev
label.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 https://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 #include "map/label.hpp"
16 #include "color.hpp"
17 #include "display.hpp"
18 #include "floating_label.hpp"
19 #include "formula/string_utils.hpp"
20 #include "game_board.hpp"
21 #include "game_data.hpp"
22 #include "resources.hpp"
23 #include "tooltips.hpp"
24 
25 /**
26  * Our definition of map labels being obscured is if the tile is obscured,
27  * or the tile below is obscured. This is because in the case where the tile
28  * itself is visible, but the tile below is obscured, the bottom half of the
29  * tile will still be shrouded, and the label being drawn looks weird.
30  */
31 inline bool is_shrouded(const display* disp, const map_location& loc)
32 {
33  return disp->shrouded(loc) || disp->shrouded(loc.get_direction(map_location::SOUTH));
34 }
35 
36 /**
37  * Rather simple test for a hex being fogged.
38  * This only exists because is_shrouded() does. (The code looks nicer if
39  * the test for being fogged looks similar to the test for being shrouded.)
40  */
41 inline bool is_fogged(const display* disp, const map_location& loc)
42 {
43  return disp->fogged(loc);
44 }
45 
47  : team_(team)
48  , labels_()
49  , enabled_(true)
50  , categories_dirty(true)
51 {
52 }
53 
55  : team_(other.team_)
56  , labels_()
57  , enabled_(true)
58 {
59  config cfg;
60  other.write(cfg);
61  read(cfg);
62 }
63 
65 {
66  clear_all();
67 }
68 
70 {
71  if(this != &other) {
72  this->~map_labels();
73  new(this) map_labels(other);
74  }
75 
76  return *this;
77 }
78 
79 void map_labels::write(config& res) const
80 {
81  for(const auto& group : labels_) {
82  for(const auto& label : group.second) {
83  config item;
84  label.second.write(item);
85 
86  res.add_child("label", std::move(item));
87  }
88  }
89 }
90 
91 void map_labels::read(const config& cfg)
92 {
93  clear_all();
94 
95  for(const config& i : cfg.child_range("label")) {
96  add_label(*this, i);
97  }
98 
100 }
101 
103 {
104  auto label_map = labels_.find(team_name);
105  if(label_map != labels_.end()) {
106  auto itor = label_map->second.find(loc);
107  if(itor != label_map->second.end()) {
108  return &itor->second;
109  }
110  }
111 
112  return nullptr;
113 }
114 
116 {
117  const terrain_label* res = get_label(loc, team_name());
118 
119  // No such team label. Try to find global label, except if that's what we just did.
120  // NOTE: This also avoid infinite recursion
121  if(res == nullptr && !team_name().empty()) {
122  return get_label(loc, "");
123  }
124 
125  return res;
126 }
127 
128 const std::string& map_labels::team_name() const
129 {
130  if(team_) {
131  return team_->team_name();
132  }
133 
134  static const std::string empty;
135  return empty;
136 }
137 
139 {
140  if(team_ != team) {
141  team_ = team;
142  categories_dirty = true;
143  }
144 }
145 
147  const t_string& text,
148  const int creator,
149  const std::string& team_name,
150  const color_t color,
151  const bool visible_in_fog,
152  const bool visible_in_shroud,
153  const bool immutable,
154  const std::string& category,
155  const t_string& tooltip)
156 {
157  terrain_label* res = nullptr;
158 
159  // See if there is already a label in this location for this team.
160  // (We do not use get_label_private() here because we might need
161  // the label_map as well as the terrain_label.)
162  team_label_map::iterator current_label_map = labels_.find(team_name);
163  label_map::iterator current_label;
164 
165  if(current_label_map != labels_.end() &&
166  (current_label = current_label_map->second.find(loc)) != current_label_map->second.end())
167  {
168  // Found old checking if need to erase it
169  if(text.str().empty()) {
170  // Erase the old label.
171  current_label_map->second.erase(current_label);
172 
173  // Restore the global label in the same spot, if any.
174  if(terrain_label* global_label = get_label_private(loc, "")) {
175  global_label->recalculate();
176  }
177  } else {
178  current_label->second.update_info(
179  text, creator, tooltip, team_name, color, visible_in_fog, visible_in_shroud, immutable, category);
180 
181  res = &current_label->second;
182  }
183  } else if(!text.str().empty()) {
184  // See if we will be replacing a global label.
185  terrain_label* global_label = get_label_private(loc, "");
186 
187  // Add the new label.
188  res = add_label(
189  *this, text, creator, team_name, loc, color, visible_in_fog, visible_in_shroud, immutable, category, tooltip);
190 
191  // Hide the old label.
192  if(global_label != nullptr) {
193  global_label->recalculate();
194  }
195  }
196 
197  categories_dirty = true;
198  return res;
199 }
200 
201 template<typename... T>
203 {
204  categories_dirty = true;
205 
206  terrain_label t(std::forward<T>(args)...);
207  return &(*labels_[t.team_name()].emplace(t.location(), std::move(t)).first).second;
208 }
209 
210 void map_labels::clear(const std::string& team_name, bool force)
211 {
212  team_label_map::iterator i = labels_.find(team_name);
213  if(i != labels_.end()) {
214  clear_map(i->second, force);
215  }
216 
217  i = labels_.find("");
218  if(i != labels_.end()) {
219  clear_map(i->second, force);
220  }
221 
222  categories_dirty = true;
223 }
224 
225 void map_labels::clear_map(label_map& m, bool force)
226 {
227  label_map::iterator i = m.begin();
228  while(i != m.end()) {
229  if(!i->second.immutable() || force) {
230  m.erase(i++);
231  } else {
232  ++i;
233  }
234  }
235 
236  categories_dirty = true;
237 }
238 
240 {
241  labels_.clear();
242 }
243 
245 {
246  for(auto& m : labels_) {
247  for(auto& l : m.second) {
248  l.second.recalculate();
249  }
250  }
251 }
252 
253 void map_labels::enable(bool is_enabled)
254 {
255  if(is_enabled != enabled_) {
256  enabled_ = is_enabled;
258  }
259 }
260 
261 /**
262  * Returns whether or not a global (non-team) label can be shown at a
263  * specified location.
264  * (Global labels are suppressed in favor of team labels.)
265  */
267 {
268  if(team_ == nullptr) {
269  // We're in the editor. All global labels can be shown.
270  return true;
271  }
272 
273  const team_label_map::const_iterator glabels = labels_.find(team_name());
274  return glabels == labels_.end() || glabels->second.find(loc) == glabels->second.end();
275 }
276 
278 {
279  for(auto& m : labels_) {
280  for(auto& l : m.second) {
281  l.second.calculate_shroud();
282  }
283  }
284 }
285 
286 const std::vector<std::string>& map_labels::all_categories() const
287 {
288  if(categories_dirty) {
289  categories_dirty = false;
290  categories.clear();
291  categories.push_back("team");
292 
293  for(std::size_t i = 1; i <= resources::gameboard->teams().size(); i++) {
294  categories.push_back("side:" + std::to_string(i));
295  }
296 
297  std::set<std::string> unique_cats;
298  for(const auto& m : labels_) {
299  for(const auto& l : m.second) {
300  if(l.second.category().empty()) {
301  continue;
302  }
303 
304  unique_cats.insert("cat:" + l.second.category());
305  }
306  }
307 
308  std::copy(unique_cats.begin(), unique_cats.end(), std::back_inserter(categories));
309  }
310 
311  return categories;
312 }
313 
314 /** Create a new label. */
316  const t_string& text,
317  const int creator,
318  const std::string& team_name,
319  const map_location& loc,
320  const color_t color,
321  const bool visible_in_fog,
322  const bool visible_in_shroud,
323  const bool immutable,
324  const std::string& category,
325  const t_string& tooltip)
326  : handle_(0)
327  , text_(text)
328  , tooltip_(tooltip)
329  , category_(category)
330  , team_name_(team_name)
331  , visible_in_fog_(visible_in_fog)
332  , visible_in_shroud_(visible_in_shroud)
333  , immutable_(immutable)
334  , creator_(creator)
335  , color_(color)
336  , parent_(&parent)
337  , loc_(loc)
338 {
339  draw();
340 }
341 
342 /** Load label from config. */
344  : handle_(0)
345  , tooltip_handle_(0)
346  , text_()
347  , tooltip_()
348  , team_name_()
349  , visible_in_fog_(true)
350  , visible_in_shroud_(false)
351  , immutable_(true)
352  , creator_(-1)
353  , color_()
354  , parent_(&parent)
355  , loc_()
356 {
357  read(cfg);
358 }
359 
361  : handle_(l.handle_)
363  , text_(l.text_)
364  , tooltip_(l.tooltip_)
365  , category_(l.category_)
370  , creator_(l.creator_)
371  , color_(l.color_)
372  , parent_(l.parent_)
373  , loc_(l.loc_)
374 {
375  l.handle_ = 0;
376  l.tooltip_handle_ = 0;
377 }
378 
380 {
381  clear();
382 }
383 
384 void terrain_label::read(const config& cfg)
385 {
386  const variable_set& vs = *resources::gamedata;
387 
388  loc_ = map_location(cfg, &vs);
390 
391  std::string tmp_color = cfg["color"];
392 
393  text_ = cfg["text"];
394  tooltip_ = cfg["tooltip"];
395  team_name_ = cfg["team_name"].str();
396  visible_in_fog_ = cfg["visible_in_fog"].to_bool(true);
397  visible_in_shroud_ = cfg["visible_in_shroud"].to_bool();
398  immutable_ = cfg["immutable"].to_bool(true);
399  category_ = cfg["category"].str();
400 
401  int side = cfg["side"].to_int(-1);
402  if(side >= 0) {
403  creator_ = side - 1;
404  } else if(cfg["side"].str() == "current") {
405  config::attribute_value current_side = vs.get_variable_const("side_number");
406  if(!current_side.empty()) {
407  creator_ = current_side.to_int();
408  }
409  }
410 
411  // Not moved to rendering, as that would depend on variables at render-time
413 
415  tmp_color = utils::interpolate_variables_into_string(tmp_color, vs);
416 
417  if(!tmp_color.empty()) {
418  try {
419  color = color_t::from_rgb_string(tmp_color);
420  } catch(const std::invalid_argument&) {
421  // Prior to the color_t conversion, labels were written to savefiles with an alpha key, despite alpha not
422  // being accepted in color=. Because of this, this enables the loading of older saves without an exception
423  // throwing.
424  color = color_t::from_rgba_string(tmp_color);
425  }
426  }
427 
428  color_ = color;
429 }
430 
431 void terrain_label::write(config& cfg) const
432 {
433  loc_.write(cfg);
434 
435  cfg["text"] = text();
436  cfg["tooltip"] = tooltip();
437  cfg["team_name"] = (this->team_name());
438  cfg["color"] = color_.to_rgb_string();
439  cfg["visible_in_fog"] = visible_in_fog_;
440  cfg["visible_in_shroud"] = visible_in_shroud_;
441  cfg["immutable"] = immutable_;
442  cfg["category"] = category_;
443  cfg["side"] = creator_ + 1;
444 }
445 
447  const int creator,
448  const t_string& tooltip,
449  const std::string& team_name,
450  const color_t color)
451 {
452  color_ = color;
453  text_ = text;
454  tooltip_ = tooltip;
456  creator_ = creator;
457 
458  draw();
459 }
460 
462  const int creator,
463  const t_string& tooltip,
464  const std::string& team_name,
465  const color_t color,
466  const bool visible_in_fog,
467  const bool visible_in_shroud,
468  const bool immutable,
469  const std::string& category)
470 {
475 
476  update_info(text, creator, tooltip, team_name, color);
477 }
478 
480 {
481  draw();
482 }
483 
485 {
486  if(handle_) {
488  }
489 
490  if(tooltip_.empty() || hidden()) {
492  tooltip_handle_ = 0;
493  return;
494  }
495 
496  // tooltips::update_tooltip(tooltip_handle, get_rect(), tooltip_.str(), "", true);
497 
498  if(tooltip_handle_) {
500  } else {
502  }
503 }
504 
505 SDL_Rect terrain_label::get_rect() const
506 {
507  SDL_Rect rect {0, 0, 0, 0};
508 
510  if(!disp) {
511  return rect;
512  }
513 
514  int hex_size = disp->hex_size();
515 
516  rect.x = disp->get_location_x(loc_) + hex_size / 4;
517  rect.y = disp->get_location_y(loc_);
518  rect.h = disp->hex_size();
519  rect.w = disp->hex_size() - hex_size / 2;
520 
521  return rect;
522 }
523 
525 {
527  if(!disp) {
528  return;
529  }
530 
531  if(text_.empty() && tooltip_.empty()) {
532  return;
533  }
534 
535  clear();
536 
537  if(!viewable(*disp)) {
538  return;
539  }
540 
541  // Note: the y part of loc_nextx is not used at all.
544  const int xloc = (disp->get_location_x(loc_) + disp->get_location_x(loc_nextx) * 2) / 3;
545  const int yloc = disp->get_location_y(loc_nexty) - font::SIZE_NORMAL;
546 
547  // If a color is specified don't allow to override it with markup. (prevents faking map labels for example)
548  // FIXME: @todo Better detect if it's team label and not provided by the scenario.
549  bool use_markup = color_ == font::LABEL_COLOR;
550 
551  font::floating_label flabel(text_.str());
552  flabel.set_color(color_);
553  flabel.set_position(xloc, yloc);
554  flabel.set_clip_rect(disp->map_outside_area());
555  flabel.set_width(font::SIZE_NORMAL * 13);
556  flabel.set_height(font::SIZE_NORMAL * 4);
557  flabel.set_scroll_mode(font::ANCHOR_LABEL_MAP);
558  flabel.use_markup(use_markup);
559 
561 
563 }
564 
565 /**
566  * This is a lightweight test used to see if labels are revealed as a result
567  * of unit actions (i.e. fog/shroud clearing). It should not contain any tests
568  * that are invariant during unit movement (disregarding potential WML events);
569  * those belong in visible().
570  */
572 {
574  if(!disp) {
575  return false;
576  }
577 
578  // Respect user's label preferences
579  std::string category = "cat:" + category_;
580  std::string creator = "side:" + std::to_string(creator_ + 1);
581  const std::vector<std::string>& hidden_categories = disp->get_disp_context().hidden_label_categories();
582 
583  if(std::find(hidden_categories.begin(), hidden_categories.end(), category) != hidden_categories.end()) {
584  return true;
585  }
586 
587  if(creator_ >= 0 &&
588  std::find(hidden_categories.begin(), hidden_categories.end(), creator) != hidden_categories.end())
589  {
590  return true;
591  }
592 
593  if(!team_name().empty() &&
594  std::find(hidden_categories.begin(), hidden_categories.end(), "team") != hidden_categories.end())
595  {
596  return true;
597  }
598 
599  // Fog can hide some labels.
600  if(!visible_in_fog_ && is_fogged(disp, loc_)) {
601  return true;
602  }
603 
604  // Shroud can hide some labels.
605  if(!visible_in_shroud_ && is_shrouded(disp, loc_)) {
606  return true;
607  }
608 
609  return false;
610 }
611 
612 /**
613  * This is a test used to see if we should bother with the overhead of actually
614  * creating a label. Conditions that can change during unit movement (disregarding
615  * potential WML events) should not be listed here; they belong in hidden().
616  */
617 bool terrain_label::viewable(const display& disp) const
618 {
619  if(!parent_->enabled()) {
620  return false;
621  }
622 
623  // In the editor, all labels are viewable.
624  if(disp.in_editor()) {
625  return true;
626  }
627 
628  // Observers are not privvy to team labels.
629  const bool can_see_team_labels = !disp.get_disp_context().is_observer();
630 
631  // Global labels are shown unless covered by a team label.
632  if(team_name_.empty()) {
633  return !can_see_team_labels || parent_->visible_global_label(loc_);
634  }
635 
636  // Team labels are only shown to members of the team.
637  return can_see_team_labels && parent_->team_name() == team_name_;
638 }
639 
641 {
642  if(handle_) {
644  handle_ = 0;
645  }
646 
647  if(tooltip_handle_) {
649  tooltip_handle_ = 0;
650  }
651 }
const map_labels * parent_
Definition: label.hpp:228
bool empty() const
Tests for an attribute that either was never set or was set to "".
void recalculate_shroud()
Definition: label.cpp:277
const map_location & location() const
Definition: label.hpp:176
const color_t & color() const
Definition: label.hpp:181
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:88
int creator_
Definition: label.hpp:224
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with &#39;$&#39; in the string &#39;str&#39; with the equivalent ...
map_labels & operator=(const map_labels &)
Definition: label.cpp:69
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:92
static int hex_size()
Function which returns the size of a hex in pixels (from top tip to bottom tip or left edge to right ...
Definition: display.hpp:255
void write(config &res) const
Definition: label.cpp:431
const std::vector< std::string > & all_categories() const
Definition: label.cpp:286
Variant for storing WML attributes.
const std::string & category() const
Definition: label.hpp:156
void show_floating_label(int handle, bool value)
hides or shows a floating label
t_string tooltip_
Definition: label.hpp:216
const t_string & tooltip() const
Definition: label.hpp:141
bool visible_in_fog_
Definition: label.hpp:221
const t_string & text() const
Definition: label.hpp:136
bool is_fogged(const display *disp, const map_location &loc)
Rather simple test for a hex being fogged.
Definition: label.cpp:41
child_itors child_range(config_key_type key)
Definition: config.cpp:362
void remove_tooltip(int id)
Definition: tooltips.cpp:169
void remove_floating_label(int handle)
removes the floating label given by &#39;handle&#39; from the screen
terrain_label(const terrain_label &)=delete
Delete copy ctor and assignment ops.
std::string to_rgb_string() const
Returns the stored color as an "R,G,B" string.
Definition: color.cpp:122
bool is_shrouded(const display *disp, const map_location &loc)
Our definition of map labels being obscured is if the tile is obscured, or the tile below is obscured...
Definition: label.cpp:31
const std::string & team_name() const
Definition: label.hpp:151
const color_t LABEL_COLOR
bool visible_in_shroud() const
Definition: label.hpp:166
int creator() const
Definition: label.hpp:146
team_label_map labels_
Definition: label.hpp:98
game_data * gamedata
Definition: resources.cpp:22
const int SIZE_NORMAL
Definition: constants.cpp:19
map_location get_direction(DIRECTION dir, unsigned int n=1u) const
Definition: location.cpp:359
map_location loc_
virtual bool in_editor() const
Definition: display.hpp:202
bool viewable(const display &disp) const
This is a test used to see if we should bother with the overhead of actually creating a label...
Definition: label.cpp:617
bool visible_in_shroud_
Definition: label.hpp:222
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
bool enabled() const
Definition: label.hpp:66
std::map< map_location, terrain_label > label_map
Definition: label.hpp:34
std::vector< std::string > categories
Definition: label.hpp:101
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const color_t color=font::NORMAL_COLOR, const bool visible_in_fog=true, const bool visible_in_shroud=false, const bool immutable=false, const std::string &category="", const t_string &tooltip="")
Definition: label.cpp:146
void clear(const std::string &, bool force)
Definition: label.cpp:210
map_labels(const map_labels &)
Definition: label.cpp:54
const team * team_
Definition: label.hpp:96
game_board * gameboard
Definition: resources.cpp:20
void clear()
Definition: label.cpp:640
void write(config &res) const
Definition: label.cpp:79
std::string category_
Definition: label.hpp:218
void enable(bool is_enabled)
Definition: label.cpp:253
map_display and display: classes which take care of displaying the map and game-data on the screen...
color_t color_
Definition: label.hpp:226
const terrain_label * get_label(const map_location &loc, const std::string &team_name) const
Definition: label.hpp:47
bool immutable_
Definition: label.hpp:223
bool enabled_
Definition: label.hpp:99
int tooltip_handle_
Definition: label.hpp:213
bool visible_global_label(const map_location &) const
Returns whether or not a global (non-team) label can be shown at a specified location.
Definition: label.cpp:266
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:711
Encapsulates the map of the game.
Definition: location.hpp:42
void set_color(const color_t &color)
void set_team(const team *)
Definition: label.cpp:138
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.cpp:706
~map_labels()
Definition: label.cpp:64
void recalculate_labels()
Definition: label.cpp:244
SDL_Rect get_rect() const
Definition: label.cpp:505
bool visible_in_fog() const
Definition: label.hpp:161
std::size_t i
Definition: function.cpp:933
bool is_observer() const
Check if we are an observer in this game.
void read(const config &cfg)
Definition: label.cpp:384
void clear_map(label_map &, bool)
Definition: label.cpp:225
int get_location_y(const map_location &loc) const
Definition: display.cpp:721
const SDL_Rect & map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.hpp:237
const display_context & get_disp_context() const
Definition: display.hpp:168
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
const std::string & team_name() const
Definition: label.cpp:128
config & add_child(config_key_type key)
Definition: config.cpp:476
void draw()
Definition: label.cpp:524
void read(const config &cfg)
Definition: label.cpp:91
std::string team_name_
Definition: label.hpp:219
bool empty() const
Definition: tstring.hpp:182
int get_location_x(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition: display.cpp:716
To store label data Class implements logic for rendering.
Definition: label.hpp:107
void recalculate()
Definition: label.cpp:479
virtual config::attribute_value get_variable_const(const std::string &id) const =0
terrain_label * add_label(T &&... args)
Definition: label.cpp:202
double t
Definition: astarsearch.cpp:64
t_string text_
Definition: label.hpp:215
bool find(E event, F functor)
Tests whether an event handler is available.
~terrain_label()
Definition: label.cpp:379
bool hidden() const
This is a lightweight test used to see if labels are revealed as a result of unit actions (i...
Definition: label.cpp:571
void calculate_shroud()
Definition: label.cpp:484
void clear_all()
Definition: label.cpp:239
bool immutable() const
Definition: label.hpp:171
virtual const std::vector< std::string > & hidden_label_categories() const =0
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
static color_t from_rgba_string(const std::string &c)
Creates a new color_t object from a string variable in "R,G,B,A" format.
Definition: color.cpp:20
const std::string & str() const
Definition: tstring.hpp:186
bool update_tooltip(int id, const SDL_Rect &rect, const std::string &message, const std::string &action, bool use_markup)
Definition: tooltips.cpp:144
t_string interpolate_variables_into_tstring(const t_string &tstr, const variable_set &variables)
Function that does the same as the above, for t_stringS.
int add_tooltip(const SDL_Rect &rect, const std::string &message, const std::string &action, bool use_markup, const surface &foreground)
Definition: tooltips.cpp:175
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
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
void update_info(const t_string &, const int creator, const t_string &, const std::string &, const color_t)
Definition: label.cpp:446
map_location loc_
Definition: label.hpp:229
const std::string & team_name() const
Definition: team.hpp:297
terrain_label * get_label_private(const map_location &loc, const std::string &team_name)
Definition: label.cpp:102
bool categories_dirty
Definition: label.hpp:102
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:371
void write(config &cfg) const
Definition: location.cpp:211