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