The Battle for Wesnoth  1.15.12+dev
debug.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2018 by Mark de Wever <koraq@xs4all.nl>
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 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 
18 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
19 
20 #include "gui/widgets/debug.hpp"
21 
22 #include "formatter.hpp"
24 #include "gui/widgets/listbox.hpp"
26 #include "gui/widgets/window.hpp"
28 
29 #include <fstream>
30 #include <iomanip>
31 #include <iostream>
32 
33 namespace gui2
34 {
35 
36 namespace
37 {
38 
39 /**
40  * Gets the id of a grid child cell.
41  *
42  * @param parent_id The id of the parent grid.
43  * @param row Row number in the grid.
44  * @param col Column number in the grid.
45  *
46  * @returns The id of the child cell.
47  */
48 std::string get_child_id(const std::string& parent_id,
49  const unsigned row,
50  const unsigned col)
51 {
52  // Originally used this formatter function but it managed to return empty
53  // strings. No idea why so switched to using the good old lexical_cast
54  // instead.
55 
56  // return formatter() << parent_id << "_C_" << row << '_' << col;
57  std::string result = parent_id + "_C_" + std::to_string(row)
58  + '_' + std::to_string(col);
59 
60  return result;
61 }
62 
63 /**
64  * Gets the id of a widget in a grid child cell.
65  *
66  * @param parent_id The id of the parent grid.
67  * @param row Row number in the grid.
68  * @param col Column number in the grid.
69  *
70  * @returns The id of the widget.
71  */
72 std::string get_child_widget_id(const std::string& parent_id,
73  const unsigned row,
74  const unsigned col)
75 {
76  return get_child_id(parent_id, row, col) + "_W";
77 }
78 
79 /** Gets the prefix of the filename. */
80 std::string get_base_filename()
81 {
82  std::ostringstream ss;
83 
84  std::time_t t = std::time(nullptr);
85  ss << std::put_time(std::localtime(&t), "%Y%m%d_%H%M%S");
86 
87  static unsigned counter = 0;
88  ++counter;
89 
90  ss << '_' << counter << '_';
91 
92  return ss.str();
93 }
94 /***** ***** ***** ***** FLAGS ***** ***** ***** *****/
95 
96 const unsigned ALL = UINT_MAX; /**< All levels/domains */
97 
98 const unsigned SIZE_INFO = 1 << 0; /**<
99  * Shows the size info of
100  * children/widgets.
101  */
102 const unsigned STATE_INFO = 1 << 1; /**<
103  * Shows the state info of widgets.
104  */
105 unsigned level_ = 0;
106 unsigned domain_ = 0;
107 } // namespace
108 
109 debug_layout_graph::debug_layout_graph(const window* window)
110  : window_(window), sequence_number_(0), filename_base_(get_base_filename())
111 {
112 }
113 
114 void debug_layout_graph::set_level(const std::string& level)
115 {
116  if(level.empty()) {
117  level_ = ALL; /** @todo Should default to 0. */
118  return;
119  }
120 
121  std::vector<std::string> params = utils::split(level);
122 
123  for(const auto & param : params)
124  {
125  if(param == "all") {
126  level_ = ALL;
127  // No need to look further even though invalid items are now
128  // ignored.
129  return;
130  } else if(param == "size") {
131  level_ |= SIZE_INFO;
132  } else if(param == "state") {
133  level_ |= STATE_INFO;
134  } else {
135  // logging might not be up yet.
136  std::cerr << "Unknown level '" << param << "' is ignored.\n";
137  }
138  }
139 }
140 
141 void debug_layout_graph::set_domain(const std::string& domain)
142 {
143  if(domain.empty()) {
144  // return error and die
145  domain_ = ALL; /** @todo Should default to 0. */
146  return;
147  }
148 
149  std::vector<std::string> params = utils::split(domain);
150 
151  for(const auto & param : params)
152  {
153  if(param == "all") {
154  domain_ = ALL;
155  // No need to look further even though invalid items are now
156  // ignored.
157  return;
158  } else if(param == "show") {
159  domain_ |= SHOW;
160  } else if(param == "layout") {
161  domain_ |= LAYOUT;
162  } else {
163  // logging might not be up yet.
164  std::cerr << "Unknown domain '" << param << "' is ignored.\n";
165  }
166  }
167 }
168 
169 void debug_layout_graph::generate_dot_file(const std::string& generator,
170  const unsigned domain)
171 {
172  // domain == 0 must also evaluate to true.
173  if((domain_ & domain) != domain) {
174  return;
175  }
176 
177  std::string id = window_->id();
178  if(!id.empty()) {
179  id += '_';
180  }
181  const std::string filename = filename_base_ + id
182  + std::to_string(++sequence_number_)
183  + "-" + generator + ".dot";
184 
185  std::ofstream file(filename.c_str());
186 
187  file << "//Basic layout graph for window id '" << window_->id()
188  << "' using definition '" << window_->definition_ << "'.\n"
189  << "digraph window {\n"
190  << "\tnode [shape=record, style=filled, fillcolor=\"bisque\"];\n"
191  << "\trankdir=LR;\n";
192 
193  widget_generate_info(file, window_, "root");
194 
195  file << "}\n";
196 }
197 
198 void debug_layout_graph::widget_generate_info(std::ostream& out,
199  const widget* widget,
200  const std::string& id,
201  const bool embedded) const
202 {
203  assert(!id.empty());
204 
205  out << "\t" << id
206  << " [label=<<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">";
207 
208  widget_generate_basic_info(out, widget);
209  if(level_ & STATE_INFO)
210  widget_generate_state_info(out, widget);
211  if(level_ & SIZE_INFO)
212  widget_generate_size_info(out, widget);
213 
214  out << "</table>>";
215  if(embedded) {
216  out << ", fillcolor=\"palegoldenrod\"";
217  }
218  out << "];\n";
219 
220  const grid* grid = dynamic_cast<const class grid*>(widget);
221  if(!grid) {
222  const container_base* container = dynamic_cast<const container_base*>(widget);
223 
224  if(container) {
225 
226  widget_generate_info(out, &container->get_grid(), id + "_G", true);
227  out << "\t" << id << " -> " << id << "_G"
228  << " [label=\"(grid)\"];\n";
229  }
230 
231  const scrollbar_container* scrollbar_container
232  = dynamic_cast<const class scrollbar_container*>(widget);
233 
234  if(scrollbar_container) {
235  widget_generate_info(
236  out, scrollbar_container->content_grid_.get(), id + "_C", true);
237  out << "\t" << id << " -> " << id << "_C"
238  << " [label=\"(content)\"];\n";
239  }
240 
241  const listbox* listbox = dynamic_cast<const class listbox*>(widget);
242  if(listbox) {
243  assert(listbox->generator_);
244  }
245 
246  const generator_base* generator = dynamic_cast<const generator_base*>(widget);
247 
248  if(generator) {
249  for(std::size_t i = 0; i < generator->get_item_count(); ++i) {
250 
251  const std::string child_id = id + "_I_"
252  + std::to_string(i);
253 
254  widget_generate_info(out, &generator->item(i), child_id, true);
255 
256  out << "\t" << id << " -> " << child_id
257  << " [label=\"(item)\"];\n";
258  }
259  }
260  }
261  if(grid) {
262  grid_generate_info(out, grid, id);
263  }
264 }
265 
266 static std::string format_label(std::string label)
267 {
268  if(label.size() > 50) {
269  label = label.substr(0, 50) + "...";
270  }
271 
272  // Replace characters that break the dot file/
273  std::replace(label.begin(), label.end(), '>', '_');
274 
275  return label;
276 }
277 
278 void debug_layout_graph::widget_generate_basic_info(std::ostream& out,
279  const widget* widget)
280  const
281 {
282  std::string header_background
283  = level_ & (SIZE_INFO | STATE_INFO) ? " bgcolor=\"gray\"" : "";
284  const styled_widget* control = dynamic_cast<const class styled_widget*>(widget);
285 
286  out << "<tr><td" << header_background << ">" << '\n'
287  << "type=" << get_type(widget) << '\n' << "</td></tr>" << '\n'
288  << "<tr><td" << header_background << ">" << '\n'
289  << "id=" << widget->id() << '\n' << "</td></tr>" << '\n' << "<tr><td"
290  << header_background << ">" << '\n' << "address=" << widget << '\n'
291  << "</td></tr>" << '\n' << "<tr><td" << header_background << ">" << '\n'
292  << "parent=" << widget->parent_ << '\n' << "</td></tr>" << '\n';
293  if(control) {
294  out << "<tr><td" << header_background << ">" << '\n'
295  << "label=" << format_label(control->get_label()) << '\n' << "<tr><td"
296  << header_background << ">" << '\n'
297  << "definition=" << control->definition_ << '\n' << "</td></tr>"
298  << '\n' << "</td></tr>\n";
299  }
300 }
301 
302 void debug_layout_graph::widget_generate_state_info(std::ostream& out,
303  const widget* widget)
304  const
305 {
306  const styled_widget* control = dynamic_cast<const styled_widget*>(widget);
307  if(!control) {
308  return;
309  }
310 
311  out << "<tr><td>\n"
312  << "tooltip=" << control->tooltip() << '\n' << "</td></tr>\n"
313  << "<tr><td>\n"
314  << "help message" << control->help_message() << '\n'
315  // FIXME add value and other specific items
316  << "</td></tr>\n"
317  << "<tr><td>\n"
318  << "active=" << control->get_active() << '\n' << "</td></tr>\n"
319  << "<tr><td>\n"
320  << "visible=" << static_cast<int>(control->get_visible()) << '\n' << "</td></tr>\n"
321  << "<tr><td>\n"
322  << "drawing action=" << static_cast<int>(control->get_drawing_action()) << '\n'
323  << "</td></tr>\n"
324  << "<tr><td>\n"
325  << "clip rect=" << control->clipping_rectangle_ << '\n'
326  << "</td></tr>\n"
327  << "<tr><td>\n"
328  << "use tooltip on label overflow="
329  << control->get_use_tooltip_on_label_overflow() << '\n'
330  << "</td></tr>\n"
331  << "<tr><td>\n"
332  << "does block click dismiss=" << control->disable_click_dismiss()
333  << '\n' << "</td></tr>\n";
334 
335  const scrollbar_container* scrollbar_container
336  = dynamic_cast<const class scrollbar_container*>(widget);
337 
338  if(scrollbar_container) {
339  out << "<tr><td>\n"
340  << "vertical_scrollbar_mode_="
341  << scrollbar_container->vertical_scrollbar_mode_ << '\n'
342  << "</td></tr>\n"
343  << "<tr><td>\n"
344  << "horizontal_scrollbar_mode_="
345  << scrollbar_container->horizontal_scrollbar_mode_ << '\n'
346  << "</td></tr>\n";
347  }
348 }
349 
350 void debug_layout_graph::widget_generate_size_info(std::ostream& out,
351  const widget* widget) const
352 {
353  out << "<tr><td>\n"
354  << "can wrap=" << widget->can_wrap() << '\n' << "</td></tr>\n"
355  << "<tr><td>\n"
356  << "size=" << widget->get_size() << '\n' << "</td></tr>\n"
357  << "<tr><td>\n"
358  << "position=" << widget->get_origin() << '\n' << "</td></tr>\n"
359  << "<tr><td>\n"
360  << "last_best_size_=" << widget->last_best_size_ << '\n'
361  << "</td></tr>\n"
362  << "<tr><td>\n"
363  << "layout_size_=" << widget->layout_size_ << '\n' << "</td></tr>\n";
364 
365 
366  const styled_widget* control = dynamic_cast<const styled_widget*>(widget);
367 
368  if(control) {
369  out << "<tr><td>\n"
370  << "minimum config size=" << control->get_config_minimum_size()
371  << '\n' << "</td></tr>\n"
372  << "<tr><td>\n"
373  << "default config size=" << control->get_config_default_size()
374  << '\n' << "</td></tr>\n"
375  << "<tr><td>\n"
376  << "maximum config size=" << control->get_config_maximum_size()
377  << '\n' << "</td></tr>\n"
378  << "<tr><td>\n"
379  << "shrunken_=" << control->shrunken_ << '\n' << "</td></tr>\n";
380  }
381 
382  const container_base* container = dynamic_cast<const container_base*>(widget);
383 
384  if(container) {
385  out << "<tr><td>\n"
386  << "border_space=" << container->border_space() << '\n'
387  << "</td></tr>\n";
388  }
389 }
390 
391 void debug_layout_graph::grid_generate_info(std::ostream& out,
392  const grid* grid,
393  const std::string& parent_id) const
394 {
395  assert(!parent_id.empty());
396 
397  // maybe change the order to links, child, widgets so the output of the
398  // dot file might look better.
399 
400  out << "\n\n\t// The children of " << parent_id << ".\n";
401 
402  for(unsigned row = 0; row < grid->get_rows(); ++row) {
403  for(unsigned col = 0; col < grid->get_cols(); ++col) {
404 
405  const widget* widget = grid->get_widget(row, col);
406  assert(widget);
407 
408  widget_generate_info(
409  out, widget, get_child_widget_id(parent_id, row, col));
410  }
411  }
412 
413  out << "\n\t// The grid child data of " << parent_id << ".\n";
414 
415  for(unsigned row = 0; row < grid->get_rows(); ++row) {
416  for(unsigned col = 0; col < grid->get_cols(); ++col) {
417 
418  child_generate_info(out,
419  grid->get_child(row, col),
420  get_child_id(parent_id, row, col));
421  }
422  }
423 
424 
425  out << "\n\t// The links of " << parent_id << ".\n";
426 
427  for(unsigned row = 0; row < grid->get_rows(); ++row) {
428  for(unsigned col = 0; col < grid->get_cols(); ++col) {
429 
430  // grid -> child
431  out << "\t" << parent_id << " -> "
432  << get_child_id(parent_id, row, col) << " [label=\"(" << row
433  << ',' << col << ")\"];\n";
434 
435  // child -> widget
436  out << "\t" << get_child_id(parent_id, row, col) << " -> "
437  << get_child_widget_id(parent_id, row, col) << ";\n";
438  }
439  }
440 }
441 
442 void debug_layout_graph::child_generate_info(std::ostream& out,
443  const grid::child& child,
444  const std::string& id) const
445 {
446  assert(!id.empty());
447 
448  unsigned flags = child.get_flags();
449 
450  out << "\t" << id << " [style=\"\", label=<<table border=\"0\" "
451  "cellborder=\"1\" cellspacing=\"0\">\n";
452  out << "<tr><td>\n"
453  << "vertical flag=";
454 
455  switch(flags & grid::VERTICAL_MASK) {
457  out << "send to client";
458  break;
460  out << "align to top";
461  break;
463  out << "center";
464  break;
466  out << "align to bottom";
467  break;
468  default:
469  out << "unknown value("
470  << ((flags & grid::VERTICAL_MASK) >> grid::VERTICAL_SHIFT)
471  << ")";
472  }
473 
474  out << "\n</td></tr>\n"
475  << "<tr><td>\n"
476  << "horizontal flag=";
477 
478  switch(flags & grid::HORIZONTAL_MASK) {
480  out << "send to client";
481  break;
483  out << "align to left";
484  break;
486  out << "center";
487  break;
489  out << "align to right";
490  break;
491  default:
492  out << "unknown value("
494  << ")";
495  }
496 
497  out << "\n</td></tr>\n"
498  << "<tr><td>\n"
499  << "border location=";
500 
501  if((flags & grid::BORDER_ALL) == 0) {
502  out << "none";
503  } else if((flags & grid::BORDER_ALL) == grid::BORDER_ALL) {
504  out << "all";
505  } else {
506  std::string result;
507  if(flags & grid::BORDER_TOP)
508  result += "top, ";
509  if(flags & grid::BORDER_BOTTOM)
510  result += "bottom, ";
511  if(flags & grid::BORDER_LEFT)
512  result += "left, ";
513  if(flags & grid::BORDER_RIGHT)
514  result += "right, ";
515 
516  if(!result.empty()) {
517  result.resize(result.size() - 2);
518  }
519 
520  out << result;
521  }
522 
523  out << "\n</td></tr>\n"
524  << "<tr><td>\n"
525  << "border_size=" << child.get_border_size() << "\n</td></tr>\n";
526 
527  out << "</table>>];\n";
528 }
529 
530 std::string debug_layout_graph::get_type(const widget* widget) const
531 {
532  const styled_widget* control = dynamic_cast<const styled_widget*>(widget);
533  if(control) {
534  return control->get_control_type();
535  } else {
536  const grid* grid = dynamic_cast<const class grid*>(widget);
537  const generator_base* generator = dynamic_cast<const generator_base*>(widget);
538 
539  if(grid) {
540  return "grid";
541  } else if(generator) {
542  return "generator";
543  } else {
544  return "unknown";
545  }
546  }
547 }
548 
549 } // namespace gui2
550 #endif
static const unsigned BORDER_TOP
Definition: grid.hpp:61
This file contains the window object, this object is a top level container which has the event manage...
static const unsigned BORDER_ALL
Definition: grid.hpp:65
static const unsigned VERTICAL_SHIFT
Definition: grid.hpp:47
static const unsigned HORIZONTAL_MASK
Definition: grid.hpp:59
Generic file dialog.
Definition: field-fwd.hpp:22
static const unsigned HORIZONTAL_SHIFT
Definition: grid.hpp:54
static const unsigned HORIZONTAL_ALIGN_RIGHT
Definition: grid.hpp:58
static const unsigned BORDER_BOTTOM
Definition: grid.hpp:62
void set_level(const std::string &value)
Definition: game.cpp:704
static const unsigned VERTICAL_ALIGN_TOP
Definition: grid.hpp:49
std::size_t i
Definition: function.cpp:940
static const unsigned BORDER_RIGHT
Definition: grid.hpp:64
static const unsigned VERTICAL_ALIGN_CENTER
Definition: grid.hpp:50
static const unsigned HORIZONTAL_ALIGN_CENTER
Definition: grid.hpp:57
static const unsigned VERTICAL_MASK
Definition: grid.hpp:52
static const unsigned HORIZONTAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:55
static const unsigned VERTICAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:48
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
bool grid()
Definition: general.cpp:519
static const unsigned VERTICAL_ALIGN_BOTTOM
Definition: grid.hpp:51
double t
Definition: astarsearch.cpp:64
std::vector< std::string > split(const config_attribute_value &val)
static const unsigned HORIZONTAL_ALIGN_LEFT
Definition: grid.hpp:56
static const unsigned BORDER_LEFT
Definition: grid.hpp:63