The Battle for Wesnoth  1.15.0-dev
help_text_area.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 #include "help/help_text_area.hpp"
16 
17 #include "config.hpp" // for config, etc
18 #include "game_config.hpp" // for debug
19 #include "help/help_impl.hpp" // for parse_error, box_width, etc
20 #include "image.hpp" // for get_image
21 #include "log.hpp" // for LOG_STREAM, log_domain, etc
22 #include "preferences/general.hpp" // for font_scaled
23 #include "sdl/rect.hpp" // for draw_rectangle, etc
24 #include "serialization/parser.hpp" // for read, write
25 #include "video.hpp" // for CVideo
26 
27 #include <algorithm> // for max, min, find_if
28 #include <ostream> // for operator<<, stringstream, etc
29 #include <vector> // for vector, etc
30 
31 static lg::log_domain log_display("display");
32 #define WRN_DP LOG_STREAM(warn, log_display)
33 
34 namespace help {
35 
36 help_text_area::help_text_area(CVideo &video, const section &toplevel) :
37  gui::scrollarea(video),
38  items_(),
39  last_row_(),
40  toplevel_(toplevel),
41  shown_topic_(nullptr),
42  title_spacing_(16),
43  curr_loc_(0, 0),
44  min_row_height_(font::get_max_height(normal_font_size)),
45  curr_row_height_(min_row_height_),
46  contents_height_(0)
47 {
48  set_scroll_rate(40);
49 }
50 
51 void help_text_area::set_inner_location(const SDL_Rect& rect)
52 {
53  bg_register(rect);
54  if (shown_topic_)
55  set_items();
56 }
57 
59 {
60  shown_topic_ = &t;
61  set_items();
62  set_dirty(true);
63 }
64 
65 
67  const std::string& reference_to, bool _floating,
68  bool _box, ALIGNMENT alignment) :
69  rect(),
70  surf(surface),
71  text(_text),
72  ref_to(reference_to),
73  floating(_floating), box(_box),
74  align(alignment)
75 {
76  rect.x = x;
77  rect.y = y;
78  rect.w = box ? surface->w + box_width * 2 : surface->w;
79  rect.h = box ? surface->h + box_width * 2 : surface->h;
80 }
81 
82 help_text_area::item::item(surface surface, int x, int y, bool _floating,
83  bool _box, ALIGNMENT alignment) :
84  rect(),
85  surf(surface),
86  text(""),
87  ref_to(""),
88  floating(_floating),
89  box(_box), align(alignment)
90 {
91  rect.x = x;
92  rect.y = y;
93  rect.w = box ? surface->w + box_width * 2 : surface->w;
94  rect.h = box ? surface->h + box_width * 2 : surface->h;
95 }
96 
98 {
99  last_row_.clear();
100  items_.clear();
101  curr_loc_.first = 0;
102  curr_loc_.second = 0;
104  // Add the title item.
105  const std::string show_title =
108  font::NORMAL_COLOR, TTF_STYLE_BOLD));
109  if (surf != nullptr) {
110  add_item(item(surf, 0, 0, show_title));
111  curr_loc_.second = title_spacing_;
113  down_one_line();
114  }
115  // Parse and add the text.
116  const config& parsed_items = shown_topic_->text.parsed_text();
117  for(auto& item : parsed_items.all_children_range()) {
118 #define TRY(name) do { \
119  if (item.key == #name) \
120  handle_##name##_cfg(item.cfg); \
121  } while (0)
122 
123  TRY(text);
124  TRY(ref);
125  TRY(img);
126  TRY(bold);
127  TRY(italic);
128  TRY(header);
129  TRY(jump);
130  TRY(format);
131 #undef TRY
132  }
133  down_one_line(); // End the last line.
134  int h = height();
135  set_position(0);
137  set_shown_size(h);
138 }
139 
141 {
142  const std::string dst = cfg["dst"];
143  const std::string text = cfg["text"];
144  bool force = cfg["force"].to_bool();
145 
146  if (dst.empty()) {
147  std::stringstream msg;
148  msg << "Ref markup must have dst attribute. Please submit a bug"
149  " report if you have not modified the game files yourself. Erroneous config: ";
150  write(msg, cfg);
151  throw parse_error(msg.str());
152  }
153 
154  if (find_topic(toplevel_, dst) == nullptr && !force) {
155  // detect the broken link but quietly silence the hyperlink for normal user
156  add_text_item(text, game_config::debug ? dst : "", true);
157 
158  // FIXME: workaround: if different campaigns define different
159  // terrains, some terrains available in one campaign will
160  // appear in the list of seen terrains, and be displayed in the
161  // help, even if the current campaign does not handle such
162  // terrains. This will lead to the unit page generator creating
163  // invalid references.
164  //
165  // Disabling this is a kludgey workaround until the
166  // encountered_terrains system is fixed
167  //
168  // -- Ayin apr 8 2005
169 #if 0
170  if (game_config::debug) {
171  std::stringstream msg;
172  msg << "Reference to non-existent topic '" << dst
173  << "'. Please submit a bug report if you have not"
174  "modified the game files yourself. Erroneous config: ";
175  write(msg, cfg);
176  throw parse_error(msg.str());
177  }
178 #endif
179  } else {
180  add_text_item(text, dst);
181  }
182 }
183 
185 {
186  const std::string src = cfg["src"];
187  const std::string align = cfg["align"];
188  bool floating = cfg["float"].to_bool();
189  bool box = cfg["box"].to_bool(true);
190  if (src.empty()) {
191  throw parse_error("Img markup must have src attribute.");
192  }
193  add_img_item(src, align, floating, box);
194 }
195 
197 {
198  const std::string text = cfg["text"];
199  if (text.empty()) {
200  throw parse_error("Bold markup must have text attribute.");
201  }
202  add_text_item(text, "", false, -1, true);
203 }
204 
206 {
207  const std::string text = cfg["text"];
208  if (text.empty()) {
209  throw parse_error("Italic markup must have text attribute.");
210  }
211  add_text_item(text, "", false, -1, false, true);
212 }
213 
215 {
216  const std::string text = cfg["text"];
217  if (text.empty()) {
218  throw parse_error("Header markup must have text attribute.");
219  }
220  add_text_item(text, "", false, title2_size, true);
221 }
222 
224 {
225  const std::string amount_str = cfg["amount"];
226  const std::string to_str = cfg["to"];
227  if (amount_str.empty() && to_str.empty()) {
228  throw parse_error("Jump markup must have either a to or an amount attribute.");
229  }
230  unsigned jump_to = curr_loc_.first;
231  if (!amount_str.empty()) {
232  unsigned amount;
233  try {
234  amount = lexical_cast<unsigned, std::string>(amount_str);
235  }
236  catch (bad_lexical_cast&) {
237  throw parse_error("Invalid amount the amount attribute in jump markup.");
238  }
239  jump_to += amount;
240  }
241  if (!to_str.empty()) {
242  unsigned to;
243  try {
244  to = lexical_cast<unsigned, std::string>(to_str);
245  }
246  catch (bad_lexical_cast&) {
247  throw parse_error("Invalid amount in the to attribute in jump markup.");
248  }
249  if (to < jump_to) {
250  down_one_line();
251  }
252  jump_to = to;
253  }
254  if (jump_to != 0 && static_cast<int>(jump_to) <
256 
257  curr_loc_.first = jump_to;
258  }
259 }
260 
262 {
263  const std::string text = cfg["text"];
264  if (text.empty()) {
265  throw parse_error("Format markup must have text attribute.");
266  }
267  bool bold = cfg["bold"].to_bool();
268  bool italic = cfg["italic"].to_bool();
269  int font_size = cfg["font_size"].to_int(normal_font_size);
270  color_t color = help::string_to_color(cfg["color"]);
271  add_text_item(text, "", false, font_size, bold, italic, color);
272 }
273 
275  add_text_item(cfg["text"]);
276 }
277 
279  bool broken_link, int _font_size, bool bold, bool italic,
280  color_t text_color
281 )
282 {
283  const int font_size = _font_size < 0 ? normal_font_size : _font_size;
284  // font::line_width(), font::get_rendered_text() are not use scaled font inside
285  const int scaled_font_size = preferences::font_scaled(font_size);
286  if (text.empty())
287  return;
288  const int remaining_width = get_remaining_width();
289  std::size_t first_word_start = text.find_first_not_of(" ");
290  if (first_word_start == std::string::npos) {
291  first_word_start = 0;
292  }
293  if (text[first_word_start] == '\n') {
294  down_one_line();
295  std::string rest_text = text;
296  rest_text.erase(0, first_word_start + 1);
297  add_text_item(rest_text, ref_dst, broken_link, _font_size, bold, italic, text_color);
298  return;
299  }
300  const std::string first_word = get_first_word(text);
301  int state = 0;
302  state |= bold ? TTF_STYLE_BOLD : 0;
303  state |= italic ? TTF_STYLE_ITALIC : 0;
304  if (curr_loc_.first != get_min_x(curr_loc_.second, curr_row_height_)
305  && remaining_width < font::line_width(first_word, scaled_font_size, state)) {
306  // The first word does not fit, and we are not at the start of
307  // the line. Move down.
308  down_one_line();
310  add_text_item(s, ref_dst, broken_link, _font_size, bold, italic, text_color);
311  }
312  else {
313  std::vector<std::string> parts = split_in_width(text, font_size, remaining_width);
314  std::string first_part = parts.front();
315  // Always override the color if we have a cross reference.
316  color_t color;
317  if(ref_dst.empty())
318  color = text_color;
319  else if(broken_link)
320  color = font::BAD_COLOR;
321  else
322  color = font::YELLOW_COLOR;
323 
324  // In split_in_width(), no_break_after() and no_break_before() are used(see marked-up_text.cpp).
325  // Thus, even if there is enough remaining_width for the next word,
326  // sometimes empty string is returned from split_in_width().
327  if (first_part.empty()) {
328  down_one_line();
329  }
330  else {
331  surface surf(font::get_rendered_text(first_part, scaled_font_size, color, state));
332  if (!surf.null())
333  add_item(item(surf, curr_loc_.first, curr_loc_.second, first_part, ref_dst));
334  }
335  if (parts.size() > 1) {
336 
337  std::string& s = parts.back();
338 
339  const std::string first_word_before = get_first_word(s);
340  const std::string first_word_after = get_first_word(remove_first_space(s));
341  if (get_remaining_width() >= font::line_width(first_word_after, scaled_font_size, state)
343  < font::line_width(first_word_before, scaled_font_size, state)) {
344  // If the removal of the space made this word fit, we
345  // must move down a line, otherwise it will be drawn
346  // without a space at the end of the line.
347  s = remove_first_space(s);
348  down_one_line();
349  }
350  else if (!(font::line_width(first_word_before, scaled_font_size, state)
351  < get_remaining_width())) {
352  s = remove_first_space(s);
353  }
354  add_text_item(s, ref_dst, broken_link, _font_size, bold, italic, text_color);
355 
356  }
357  }
358 }
359 
361  const bool floating, const bool box)
362 {
364  if (surf.null())
365  return;
366  ALIGNMENT align = str_to_align(alignment);
367  if (align == HERE && floating) {
368  WRN_DP << "Floating image with align HERE, aligning left." << std::endl;
369  align = LEFT;
370  }
371  const int width = surf->w + (box ? box_width * 2 : 0);
372  int xpos;
373  int ypos = curr_loc_.second;
374  int text_width = inner_location().w;
375  switch (align) {
376  case HERE:
377  xpos = curr_loc_.first;
378  break;
379  case LEFT:
380  default:
381  xpos = 0;
382  break;
383  case MIDDLE:
384  xpos = text_width / 2 - width / 2 - (box ? box_width : 0);
385  break;
386  case RIGHT:
387  xpos = text_width - width - (box ? box_width * 2 : 0);
388  break;
389  }
390  if (curr_loc_.first != get_min_x(curr_loc_.second, curr_row_height_)
391  && (xpos < curr_loc_.first || xpos + width > text_width)) {
392  down_one_line();
393  add_img_item(path, alignment, floating, box);
394  }
395  else {
396  if (!floating) {
397  curr_loc_.first = xpos;
398  }
399  else {
400  ypos = get_y_for_floating_img(width, xpos, ypos);
401  }
402  add_item(item(surf, xpos, ypos, floating, box, align));
403  }
404 }
405 
406 int help_text_area::get_y_for_floating_img(const int width, const int x, const int desired_y)
407 {
408  int min_y = desired_y;
409  for (std::list<item>::const_iterator it = items_.begin(); it != items_.end(); ++it) {
410  const item& itm = *it;
411  if (itm.floating) {
412  if ((itm.rect.x + itm.rect.w > x && itm.rect.x < x + width)
413  || (itm.rect.x > x && itm.rect.x < x + width)) {
414  min_y = std::max<int>(min_y, itm.rect.y + itm.rect.h);
415  }
416  }
417  }
418  return min_y;
419 }
420 
421 int help_text_area::get_min_x(const int y, const int height)
422 {
423  int min_x = 0;
424  for (std::list<item>::const_iterator it = items_.begin(); it != items_.end(); ++it) {
425  const item& itm = *it;
426  if (itm.floating) {
427  if (itm.rect.y < y + height && itm.rect.y + itm.rect.h > y && itm.align == LEFT) {
428  min_x = std::max<int>(min_x, itm.rect.w + 5);
429  }
430  }
431  }
432  return min_x;
433 }
434 
435 int help_text_area::get_max_x(const int y, const int height)
436 {
437  int text_width = inner_location().w;
438  int max_x = text_width;
439  for (std::list<item>::const_iterator it = items_.begin(); it != items_.end(); ++it) {
440  const item& itm = *it;
441  if (itm.floating) {
442  if (itm.rect.y < y + height && itm.rect.y + itm.rect.h > y) {
443  if (itm.align == RIGHT) {
444  max_x = std::min<int>(max_x, text_width - itm.rect.w - 5);
445  } else if (itm.align == MIDDLE) {
446  max_x = std::min<int>(max_x, text_width / 2 - itm.rect.w / 2 - 5);
447  }
448  }
449  }
450  }
451  return max_x;
452 }
453 
455 {
456  items_.push_back(itm);
457  if (!itm.floating) {
458  curr_loc_.first += itm.rect.w;
459  curr_row_height_ = std::max<int>(itm.rect.h, curr_row_height_);
460  contents_height_ = std::max<int>(contents_height_, curr_loc_.second + curr_row_height_);
461  last_row_.push_back(&items_.back());
462  }
463  else {
464  if (itm.align == LEFT) {
465  curr_loc_.first = itm.rect.w + 5;
466  }
467  contents_height_ = std::max<int>(contents_height_, itm.rect.y + itm.rect.h);
468  }
469 }
470 
471 
473 {
474  if (cmp_str == "left") {
475  return LEFT;
476  } else if (cmp_str == "middle") {
477  return MIDDLE;
478  } else if (cmp_str == "right") {
479  return RIGHT;
480  } else if (cmp_str == "here" || cmp_str.empty()) { // Make the empty string be "here" alignment.
481  return HERE;
482  }
483  std::stringstream msg;
484  msg << "Invalid alignment string: '" << cmp_str << "'";
485  throw parse_error(msg.str());
486 }
487 
489 {
490  adjust_last_row();
491  last_row_.clear();
493  curr_row_height_ = min_row_height_;
494  contents_height_ = std::max<int>(curr_loc_.second + curr_row_height_, contents_height_);
495  curr_loc_.first = get_min_x(curr_loc_.second, curr_row_height_);
496 }
497 
499 {
500  for (std::list<item *>::iterator it = last_row_.begin(); it != last_row_.end(); ++it) {
501  item &itm = *(*it);
502  const int gap = curr_row_height_ - itm.rect.h;
503  itm.rect.y += gap / 2;
504  }
505 }
506 
508 {
509  const int total_w = get_max_x(curr_loc_.second, curr_row_height_);
510  return total_w - curr_loc_.first;
511 }
512 
514 {
515  const SDL_Rect& loc = inner_location();
516  bg_restore();
517  surface& screen = video().getSurface();
518  clip_rect_setter clip_rect_set(screen, &loc);
519  for(std::list<item>::const_iterator it = items_.begin(), end = items_.end(); it != end; ++it) {
520  SDL_Rect dst = it->rect;
521  dst.y -= get_position();
522  if (dst.y < static_cast<int>(loc.h) && dst.y + it->rect.h > 0) {
523  dst.x += loc.x;
524  dst.y += loc.y;
525  if (it->box) {
526  for (int i = 0; i < box_width; ++i) {
527  SDL_Rect draw_rect {
528  dst.x,
529  dst.y,
530  it->rect.w - i * 2,
531  it->rect.h - i * 2
532  };
533 
534  sdl::draw_rectangle(draw_rect, {0, 0, 0, 0});
535  ++dst.x;
536  ++dst.y;
537  }
538  }
539  sdl_blit(it->surf, nullptr, screen, &dst);
540  }
541  }
542 }
543 
544 void help_text_area::scroll(unsigned int)
545 {
546  // Nothing will be done on the actual scroll event. The scroll
547  // position is checked when drawing instead and things drawn
548  // accordingly.
549  set_dirty(true);
550 }
551 
553  return sdl::point_in_rect(x_, y_, item.rect);
554 }
555 
556 std::string help_text_area::ref_at(const int x, const int y)
557 {
558  const int local_x = x - location().x;
559  const int local_y = y - location().y;
560  if (local_y < height() && local_y > 0) {
561  const int cmp_y = local_y + get_position();
562  const std::list<item>::const_iterator it =
563  std::find_if(items_.begin(), items_.end(), item_at(local_x, cmp_y));
564  if (it != items_.end()) {
565  if (!(*it).ref_to.empty()) {
566  return ((*it).ref_to);
567  }
568  }
569  }
570  return "";
571 }
572 
573 } // end namespace help
std::string jump_to(const unsigned pos)
Definition: help_impl.hpp:364
int get_max_x(const int y, const int height=0)
Analogous with get_min_x but return the maximum X.
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: image.cpp:1038
help_text_area(CVideo &video, const section &toplevel)
std::vector< char_t > string
void set_shown_size(unsigned h)
Definition: scrollarea.cpp:108
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:873
void handle_text_cfg(const config &cfg)
virtual void set_inner_location(const SDL_Rect &rect)
const int title_size
Definition: help_impl.cpp:79
Note: Specific to sdl_ttf.
void add_img_item(const std::string &path, const std::string &alignment, const bool floating, const bool box)
Add an image item with the specified attributes.
std::string remove_first_space(const std::string &text)
Definition: help_impl.cpp:1344
A section contains topics and sections along with title and ID.
Definition: help_impl.hpp:143
int get_remaining_width()
Return the width that remain on the line the current input point is at.
int get_y_for_floating_img(const int width, const int x, const int desired_y)
Find the lowest y coordinate where a floating img of the specified width and at the specified x coord...
Definition: video.hpp:35
Thrown when the help system fails to parse something.
Definition: help_impl.hpp:221
General purpose widgets.
std::list< item * > last_row_
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
void set_scroll_rate(unsigned r)
Definition: scrollarea.cpp:122
const int normal_font_size
Definition: help_impl.cpp:82
void set_items()
Update the vector with the items of the shown topic, creating surfaces for everything and putting thi...
To lexical_cast(From value)
Lexical cast converts one type to another.
color_t string_to_color(const std::string &cmp_str)
Return the color the string represents.
Definition: help_impl.cpp:1301
#define TRY(name)
-file util.hpp
Definitions for the interface to Wesnoth Markup Language (WML).
surface & getSurface()
Returns a reference to the framebuffer.
Definition: video.cpp:421
std::string bold(const std::string &s)
Definition: help_impl.hpp:378
bool null() const
Definition: surface.hpp:79
topic const * shown_topic_
void set_full_size(unsigned h)
Definition: scrollarea.cpp:115
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
Definition: parser.cpp:749
void set_dirty(bool dirty=true)
Definition: widget.cpp:215
void set_position(unsigned pos)
Definition: scrollarea.cpp:93
void add_item(const item &itm)
Add an item to the internal list, update the locations and row height.
std::string ref_at(const int x, const int y)
Return the ID that is cross-referenced at the (screen) coordinates x, y.
const SDL_Rect & location() const
Definition: widget.cpp:142
void draw_rectangle(const SDL_Rect &rect, const color_t &color)
Draw a rectangle outline.
Definition: rect.cpp:57
int font_scaled(int size)
Definition: general.cpp:452
virtual void scroll(unsigned int pos)
std::list< item > items_
const section & toplevel_
void bg_register(const SDL_Rect &rect)
Definition: widget.cpp:107
std::string path
Definition: game_config.cpp:57
void handle_ref_cfg(const config &cfg)
const unsigned min_row_height_
const color_t YELLOW_COLOR
const config & parsed_text() const
Definition: help_impl.cpp:348
void handle_img_cfg(const config &cfg)
int get_max_height(int size)
Definition: sdl_ttf.cpp:407
const color_t NORMAL_COLOR
#define WRN_DP
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:22
void handle_header_cfg(const config &cfg)
An item that is displayed in the text area.
const int box_width
Definition: help_impl.cpp:81
void handle_italic_cfg(const config &cfg)
void handle_bold_cfg(const config &cfg)
void show_topic(const topic &t)
Display the topic.
void adjust_last_row()
Adjust the heights of the items in the last row to make it look good .
void add_text_item(const std::string &text, const std::string &ref_dst="", bool broken_link=false, int font_size=-1, bool bold=false, bool italic=false, color_t color=font::NORMAL_COLOR)
Add an item with text.
std::size_t i
Definition: function.cpp:933
static map_location::DIRECTION s
SDL_Rect inner_location() const
Definition: scrollarea.cpp:136
std::pair< int, int > curr_loc_
item(surface surface, int x, int y, const std::string &text="", const std::string &reference_to="", bool floating=false, bool box=false, ALIGNMENT alignment=HERE)
surface get_rendered_text(const std::string &str, int size, const color_t &color, int style)
Definition: sdl_ttf.cpp:333
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:33
int width() const
Definition: widget.cpp:132
int w
const bool & debug
int height() const
Definition: widget.cpp:137
void bg_restore() const
Definition: widget.cpp:249
int get_min_x(const int y, const int height=0)
Return the least x coordinate at which something of the specified height can be drawn at the specifie...
CVideo & video() const
Definition: widget.hpp:84
void down_one_line()
Move the current input point to the next line.
std::string make_text_ellipsis(const std::string &text, int font_size, int max_width, int style)
If the text exceeds the specified max width, end it with an ellipsis (...)
Definition: sdl_ttf.cpp:443
std::string jump(const unsigned amount)
Definition: help_impl.hpp:371
Contains the SDL_Rect helper code.
void handle_format_cfg(const config &cfg)
double t
Definition: astarsearch.cpp:64
const topic * find_topic(const section &sec, const std::string &id)
Search for the topic with the specified identifier in the section and its subsections.
Definition: help_impl.cpp:1136
Standard logging facilities (interface).
const int title2_size
Definition: help_impl.cpp:80
A topic contains a title, an id and some text.
Definition: help_impl.hpp:111
ALIGNMENT str_to_align(const std::string &s)
Convert a string to an alignment.
bool operator()(const item &) const
SDL_Rect rect
Relative coordinates of this item.
std::string title
Definition: help_impl.hpp:136
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
static lg::log_domain log_display("display")
std::string get_first_word(const std::string &s)
Return the first word in s, not removing any spaces in the start of it.
Definition: help_impl.cpp:1352
const int font_size
Definition: button.cpp:40
int contents_height_
The height of all items in total.
Thrown when a lexical_cast fails.
int line_width(const std::string &line, int font_size, int style)
Determine the width of a line of text given a certain font size.
Definition: sdl_ttf.cpp:416
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
void handle_jump_cfg(const config &cfg)
std::vector< std::string > split_in_width(const std::string &s, const int font_size, const unsigned width)
Make a best effort to word wrap s. All parts are less than width.
Definition: help_impl.cpp:1325
topic_text text
Definition: help_impl.hpp:137
unsigned get_position() const
Definition: scrollarea.cpp:83
const color_t BAD_COLOR
Function object to find an item at the specified coordinates.