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