gui/auxiliary/window_builder.cpp

Go to the documentation of this file.
00001 /* $Id: window_builder.cpp 54217 2012-05-19 08:46:12Z mordante $ */
00002 /*
00003    Copyright (C) 2008 - 2012 by Mark de Wever <koraq@xs4all.nl>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #define GETTEXT_DOMAIN "wesnoth-lib"
00017 
00018 #include "gui/auxiliary/window_builder.hpp"
00019 
00020 #include "asserts.hpp"
00021 #include "foreach.hpp"
00022 #include "gettext.hpp"
00023 #include "gui/auxiliary/log.hpp"
00024 #include "gui/auxiliary/window_builder/helper.hpp"
00025 #if 1 // See the #if in create_builder_widget.
00026 #include "gui/auxiliary/window_builder/scrollbar_panel.hpp"
00027 #include "gui/auxiliary/window_builder/horizontal_scrollbar.hpp"
00028 #include "gui/auxiliary/window_builder/repeating_button.hpp"
00029 #include "gui/auxiliary/window_builder/stacked_widget.hpp"
00030 #include "gui/auxiliary/window_builder/vertical_scrollbar.hpp"
00031 #include "gui/auxiliary/window_builder/label.hpp"
00032 #include "gui/auxiliary/window_builder/image.hpp"
00033 #include "gui/auxiliary/window_builder/toggle_button.hpp"
00034 #include "gui/auxiliary/window_builder/slider.hpp"
00035 #include "gui/auxiliary/window_builder/scroll_label.hpp"
00036 #include "gui/auxiliary/window_builder/minimap.hpp"
00037 #include "gui/auxiliary/window_builder/button.hpp"
00038 #include "gui/auxiliary/window_builder/drawing.hpp"
00039 #include "gui/auxiliary/window_builder/password_box.hpp"
00040 #endif
00041 #include "gui/widgets/settings.hpp"
00042 #include "gui/widgets/window.hpp"
00043 #include "formula_string_utils.hpp"
00044 
00045 #include <boost/bind.hpp>
00046 
00047 namespace gui2 {
00048 
00049 namespace {
00050 
00051 static std::map<std::string, boost::function<tbuilder_widget_ptr(config)> >&
00052 builder_widget_lookup()
00053 {
00054     static std::map<std::string, boost::function<tbuilder_widget_ptr(config)> >
00055             result;
00056     return result;
00057 }
00058 
00059 tbuilder_widget_ptr create_builder_widget(const config& cfg)
00060 {
00061     config::all_children_itors children = cfg.all_children_range();
00062     size_t nb_children = std::distance(children.first, children.second);
00063     VALIDATE(nb_children == 1, "Grid cell does not have exactly 1 child.");
00064 
00065     typedef
00066             std::pair<
00067                   std::string
00068                 , boost::function<tbuilder_widget_ptr(config)> >
00069             thack;
00070     foreach(const thack& item, builder_widget_lookup()) {
00071         if(item.first == "window" || item.first == "tooltip") {
00072             continue;
00073         }
00074         if(const config &c = cfg.child(item.first)) {
00075             return item.second(c);
00076         }
00077     }
00078 
00079     if(const config &c = cfg.child("grid")) {
00080         return new tbuilder_grid(c);
00081     }
00082 /*
00083  * This is rather odd, when commented out the classes no longer seem to be in
00084  * the executable, no real idea why, except maybe of an overzealous optimizer
00085  * while linking. It seems that all these classes aren't explicitly
00086  * instantiated but only implicitly. Also when looking at the symbols in
00087  * libwesnoth-game.a the repeating button is there regardless of this #if but
00088  * in the final binary only if the #if is enabled.
00089  *
00090  * If this code is executed, which it will cause an assertion failure.
00091  */
00092 #if 1
00093 #define TRY(name)                                                          \
00094     do {                                                                   \
00095         if(const config &c = cfg.child(#name)) {                           \
00096             tbuilder_widget_ptr p = new implementation::tbuilder_##name(c);\
00097             assert(false);                                                 \
00098         }                                                                  \
00099     } while (0)
00100 
00101     TRY(stacked_widget);
00102     TRY(scrollbar_panel);
00103     TRY(horizontal_scrollbar);
00104     TRY(repeating_button);
00105     TRY(vertical_scrollbar);
00106     TRY(label);
00107     TRY(image);
00108     TRY(toggle_button);
00109     TRY(slider);
00110     TRY(scroll_label);
00111     TRY(minimap);
00112     TRY(button);
00113     TRY(drawing);
00114     TRY(password_box);
00115 #undef TRY
00116 #endif
00117 
00118     std::cerr << cfg;
00119     ERROR_LOG(false);
00120 }
00121 
00122 } // namespace
00123 
00124 /*WIKI
00125  * @page = GUIWidgetInstanceWML
00126  * @order = 1
00127  *
00128  * {{Autogenerated}}
00129  *
00130  * = Widget instance =
00131  *
00132  * Inside a grid (which is inside all container widgets) a widget is
00133  * instantiated. With this instantiation some more variables of a widget can
00134  * be tuned. This page will describe what can be tuned.
00135  *
00136  */
00137 twindow *build(CVideo &video, const twindow_builder::tresolution *definition)
00138 {
00139     // We set the values from the definition since we can only determine the
00140     // best size (if needed) after all widgets have been placed.
00141     twindow* window = new twindow(video
00142             , definition->x
00143             , definition->y
00144             , definition->width
00145             , definition->height
00146             , definition->automatic_placement
00147             , definition->horizontal_placement
00148             , definition->vertical_placement
00149             , definition->maximum_width
00150             , definition->maximum_height
00151             , definition->definition
00152             , definition->tooltip
00153             , definition->helptip);
00154     assert(window);
00155 
00156     foreach(const twindow_builder::tresolution::tlinked_group& lg,
00157             definition->linked_groups) {
00158 
00159         if(window->has_linked_size_group(lg.id)) {
00160             utils::string_map symbols;
00161             symbols["id"] = lg.id;
00162             t_string msg = vgettext(
00163                       "Linked '$id' group has multiple definitions."
00164                     , symbols);
00165 
00166             VALIDATE(false, msg);
00167         }
00168 
00169         window->init_linked_size_group(
00170                 lg.id, lg.fixed_width, lg.fixed_height);
00171     }
00172 
00173     window->set_click_dismiss(definition->click_dismiss);
00174 
00175     boost::intrusive_ptr<const twindow_definition::tresolution> conf =
00176             boost::dynamic_pointer_cast<
00177                 const twindow_definition::tresolution>(window->config());
00178     assert(conf);
00179 
00180     if(conf->grid) {
00181         window->init_grid(conf->grid);
00182         window->finalize(definition->grid);
00183     } else {
00184         window->init_grid(definition->grid);
00185     }
00186 
00187     window->add_to_keyboard_chain(window);
00188 
00189     return window;
00190 }
00191 
00192 twindow *build(CVideo &video, const std::string &type)
00193 {
00194     std::vector<twindow_builder::tresolution>::const_iterator
00195         definition = get_window_builder(type);
00196     twindow *window = build(video, &*definition);
00197     window->set_id(type);
00198     return window;
00199 }
00200 
00201 tbuilder_widget::tbuilder_widget(const config& cfg)
00202     : id(cfg["id"])
00203     , linked_group(cfg["linked_group"])
00204 #ifndef LOW_MEM
00205     , debug_border_mode(cfg["debug_border_mode"])
00206     , debug_border_color(decode_color(cfg["debug_border_color"]))
00207 #endif
00208 {
00209 }
00210 
00211 void register_builder_widget(const std::string& id
00212         , boost::function<tbuilder_widget_ptr(config)> functor)
00213 {
00214     builder_widget_lookup().insert(std::make_pair(id, functor));
00215 }
00216 
00217 const std::string& twindow_builder::read(const config& cfg)
00218 {
00219 /*WIKI
00220  * @page = GUIToolkitWML
00221  * @order = 1_window
00222  * @begin{parent}{name="gui/"}
00223  * = Window definition =
00224  * @begin{tag}{name="window"}{min="0"}{max="-1"}
00225  *
00226  * A window defines how a window looks in the game.
00227  *
00228  * @begin{table}{config}
00229  *     id & string & &                  Unique id for this window. $
00230  *     description & t_string & &       Unique translatable name for this window. $
00231  *
00232  *     resolution & section & &        The definitions of the window in various
00233  *                                   resolutions. $
00234  * @end{table}
00235  * @end{tag}{name="window"}
00236  * @end{parent}{name="gui/"}
00237  *
00238  *
00239  */
00240 
00241     id_ = cfg["id"].str();
00242     description_ = cfg["description"].str();
00243 
00244     VALIDATE(!id_.empty(), missing_mandatory_wml_key("window", "id"));
00245     VALIDATE(!description_.empty(), missing_mandatory_wml_key("window", "description"));
00246 
00247     DBG_GUI_P << "Window builder: reading data for window " << id_ << ".\n";
00248 
00249     config::const_child_itors cfgs = cfg.child_range("resolution");
00250     VALIDATE(cfgs.first != cfgs.second, _("No resolution defined."));
00251     foreach (const config &i, cfgs) {
00252         resolutions.push_back(tresolution(i));
00253     }
00254 
00255     return id_;
00256 }
00257 
00258 twindow_builder::tresolution::tresolution(const config& cfg) :
00259     window_width(cfg["window_width"]),
00260     window_height(cfg["window_height"]),
00261     automatic_placement(cfg["automatic_placement"].to_bool(true)),
00262     x(cfg["x"]),
00263     y(cfg["y"]),
00264     width(cfg["width"]),
00265     height(cfg["height"]),
00266     vertical_placement(
00267             implementation::get_v_align(cfg["vertical_placement"])),
00268     horizontal_placement(
00269             implementation::get_h_align(cfg["horizontal_placement"])),
00270     maximum_width(cfg["maximum_width"]),
00271     maximum_height(cfg["maximum_height"]),
00272     click_dismiss(cfg["click_dismiss"].to_bool()),
00273     definition(cfg["definition"]),
00274     linked_groups(),
00275     tooltip(cfg.child_or_empty("tooltip")),
00276     helptip(cfg.child_or_empty("helptip")),
00277     grid(0)
00278 {
00279 /*WIKI
00280  * @page = GUIToolkitWML
00281  * @order = 1_window
00282  * @begin{parent}{name=gui/window/}
00283  * == Resolution ==
00284  * @begin{tag}{name="resolution"}{min="0"}{max="-1"}
00285  * @begin{table}{config}
00286  * window_width & unsigned & 0 &   Width of the application window. $
00287  * window_height & unsigned & 0 &  Height of the application window. $
00288  *
00289  *
00290  * automatic_placement & bool & true &
00291  *     Automatically calculate the best size for the window and place it. If
00292  *     automatically placed ''vertical_placement'' and ''horizontal_placement''
00293  *     can be used to modify the final placement. If not automatically placed
00294  *     the ''width'' and ''height'' are mandatory. $
00295  *
00296  *
00297  * x & f_unsigned & 0 &            X coordinate of the window to show. $
00298  * y & f_unsigned & 0 &            Y coordinate of the window to show. $
00299  * width & f_unsigned & 0 &        Width of the window to show. $
00300  * height & f_unsigned & 0 &       Height of the window to show. $
00301  *
00302  *
00303  * vertical_placement & v_align & "" &
00304  *     The vertical placement of the window. $
00305  *
00306  * horizontal_placement & h_align & "" &
00307  *     The horizontal placement of the window. $
00308  *
00309  *
00310  * maximum_width & unsigned & 0 &
00311  *     The maximum width of the window (only used for automatic placement). $
00312  *
00313  * maximum_height & unsigned & 0 &
00314  *     The maximum height of the window (only used for automatic placement). $
00315  *
00316  *
00317  * click_dismiss & bool & false &
00318  *     Does the window need click dismiss behaviour? Click dismiss behaviour
00319  *     means that any mouse click will close the dialog. Note certain widgets
00320  *     will automatically disable this behaviour since they need to process the
00321  *     clicks as well, for example buttons do need a click and a misclick on
00322  *     button shouldn't close the dialog. NOTE with some widgets this behaviour
00323  *     depends on their contents (like scrolling labels) so the behaviour might
00324  *     get changed depending on the data in the dialog. NOTE the default
00325  *     behaviour might be changed since it will be disabled when can't be used
00326  *     due to widgets which use the mouse, including buttons, so it might be
00327  *     wise to set the behaviour explicitly when not wanted and no mouse using
00328  *     widgets are available. This means enter, escape or an external source
00329  *     needs to be used to close the dialog (which is valid). $
00330  *
00331  *
00332  * definition & string & "default" &
00333  *     Definition of the window which we want to show. $
00334  *
00335  *
00336  * linked_group & sections & [] &  A group of linked widget sections. $
00337  *
00338  *
00339  * tooltip & section & &
00340  *     Information regarding the tooltip for this window. $
00341  *
00342  * helptip & section & &
00343  *     Information regarding the helptip for this window. $
00344  *
00345  *
00346  * grid & grid & &                 The grid with the widgets to show. $
00347  * @end{table}
00348  * @begin{tag}{name="linked_group"}{min=0}{max=-1}
00349  * A linked_group section has the following fields:
00350  * @begin{table}{config}
00351  *     id & string & &                   The unique id of the group (unique in this
00352  *                                   window). $
00353  *     fixed_width & bool & false &    Should widget in this group have the same
00354  *                                   width. $
00355  *     fixed_height & bool & false &   Should widget in this group have the same
00356  *                                   height. $
00357  * @end{table}
00358  * @end{tag}{name="linked_group"}
00359  * A linked group needs to have at least one size fixed.
00360  * @begin{tag}{name="tooltip"}{min=0}{max=1}
00361  * A tooltip and helptip section have the following field:
00362  * @begin{table}{config}
00363  *     id & string & &               The id of the tip to show.
00364  * Note more fields will probably be added later on.
00365  * @end{table}{config}
00366  * @end{tag}{name=tooltip}
00367  * @begin{tag}{name="foreground"}{min=0}{max=1}
00368  * @end{tag}{name="foreground"}
00369  * @begin{tag}{name="background"}{min=0}{max=1}
00370  * @end{tag}{name="background"}
00371  * @end{tag}{name="resolution"}
00372  * @end{parent}{name=gui/window/}
00373  * @begin{parent}{name=gui/window/resolution/}
00374  * @begin{tag}{name="helptip"}{min=0}{max=1}{super="gui/window/resolution/tooltip"}
00375  * @end{tag}{name="helptip"}
00376  * @end{parent}{name=gui/window/resolution/}
00377  */
00378 
00379     const config &c = cfg.child("grid");
00380 
00381     VALIDATE(c, _("No grid defined."));
00382 
00383     grid = new tbuilder_grid(c);
00384 
00385     if(!automatic_placement) {
00386         VALIDATE(width.has_formula() || width(),
00387             missing_mandatory_wml_key("resolution", "width"));
00388         VALIDATE(height.has_formula() || height(),
00389             missing_mandatory_wml_key("resolution", "height"));
00390     }
00391 
00392     DBG_GUI_P << "Window builder: parsing resolution "
00393         << window_width << ',' << window_height << '\n';
00394 
00395     if(definition.empty()) {
00396         definition = "default";
00397     }
00398 
00399     foreach (const config &lg, cfg.child_range("linked_group")) {
00400         tlinked_group linked_group;
00401         linked_group.id = lg["id"].str();
00402         linked_group.fixed_width = lg["fixed_width"].to_bool();
00403         linked_group.fixed_height = lg["fixed_height"].to_bool();
00404 
00405         VALIDATE(!linked_group.id.empty()
00406                 , missing_mandatory_wml_key("linked_group", "id"));
00407 
00408         if(!(linked_group.fixed_width || linked_group.fixed_height)) {
00409             utils::string_map symbols;
00410             symbols["id"] = linked_group.id;
00411             t_string msg = vgettext(
00412                       "Linked '$id' group needs a 'fixed_width' or "
00413                         "'fixed_height' key."
00414                     , symbols);
00415 
00416             VALIDATE(false, msg);
00417         }
00418 
00419         linked_groups.push_back(linked_group);
00420     }
00421 }
00422 
00423 twindow_builder::tresolution::ttip::ttip(const config& cfg)
00424     : id(cfg["id"])
00425 {
00426     VALIDATE(!id.empty()
00427             , missing_mandatory_wml_key("[window][resolution][tip]", "id"));
00428 }
00429 
00430 tbuilder_grid::tbuilder_grid(const config& cfg) :
00431     tbuilder_widget(cfg),
00432     rows(0),
00433     cols(0),
00434     row_grow_factor(),
00435     col_grow_factor(),
00436     flags(),
00437     border_size(),
00438     widgets()
00439 {
00440 /*WIKI
00441  * @page = GUIToolkitWML
00442  * @order = 2_cell
00443  * @begin{parent}{name="gui/window/resolution/"}
00444  * = Cell =
00445  * @begin{tag}{name="grid"}{min="1"}{max="1"}
00446  * @begin{table}{config}
00447  *     id & string & "" &      A grid is a widget and can have an id. This isn't
00448  *                                      used that often, but is allowed. $
00449  *     linked_group & string & 0 &       $
00450  * @end{table}
00451  *
00452  * Every grid cell has some cell configuration values and one widget in the grid
00453  * cell. Here we describe the what is available more information about the usage
00454  * can be found here [[GUILayout]].
00455  *
00456  * == Row values ==
00457  * @begin{tag}{name="row"}{min="0"}{max="-1"}
00458  * For every row the following variables are available:
00459  *
00460  * @begin{table}{config}
00461  *     grow_factor & unsigned & 0 &      The grow factor for a row. $
00462  * @end{table}
00463  *
00464  * == Cell values ==
00465  * @begin{tag}{name="column"}{min="0"}{max="-1"}
00466  * @allow{link}{name="gui/window/resolution/grid"}
00467  * For every column the following variables are available:
00468  * @begin{table}{config}
00469  *     grow_factor & unsigned & 0 &      The grow factor for a column, this value
00470  *                                     is only read for the first row. $
00471  *
00472  *     border_size & unsigned & 0 &      The border size for this grid cell. $
00473  *     border & border & "" &            Where to place the border in this grid
00474  *                                     cell. $
00475  *
00476  *     vertical_alignment & v_align & "" &
00477  *                                     The vertical alignment of the widget in
00478  *                                     the grid cell. (This value is ignored if
00479  *                                     vertical_grow is true.) $
00480  *     horizontal_alignment & h_align & "" &
00481  *                                     The horizontal alignment of the widget in
00482  *                                     the grid cell.(This value is ignored if
00483  *                                     horizontal_grow is true.) $
00484  *
00485  *     vertical_grow & bool & false &    Does the widget grow in vertical
00486  *                                     direction when the grid cell grows in the
00487  *                                     vertical direction. This is used if the
00488  *                                     grid cell is wider as the best width for
00489  *                                     the widget. $
00490  *     horizontal_grow & bool & false &  Does the widget grow in horizontal
00491  *                                     direction when the grid cell grows in the
00492  *                                     horizontal direction. This is used if the
00493  *                                     grid cell is higher as the best width for
00494  *                                     the widget. $
00495  * @end{table}
00496  * @end{tag}{name="column"}
00497  * @end{tag}{name="row"}
00498  * @end{tag}{name="grid"}
00499  * @end{parent}{name="gui/window/resolution/"}
00500  *
00501  */
00502     log_scope2(log_gui_parse, "Window builder: parsing a grid");
00503 
00504     foreach (const config &row, cfg.child_range("row"))
00505     {
00506         unsigned col = 0;
00507 
00508         row_grow_factor.push_back(row["grow_factor"]);
00509 
00510         foreach (const config &c, row.child_range("column"))
00511         {
00512             flags.push_back(implementation::read_flags(c));
00513             border_size.push_back(c["border_size"]);
00514             if(rows == 0) {
00515                 col_grow_factor.push_back(c["grow_factor"]);
00516             }
00517 
00518             widgets.push_back(create_builder_widget(c));
00519 
00520             ++col;
00521         }
00522 
00523         ++rows;
00524         if (rows == 1) {
00525             cols = col;
00526         } else {
00527             VALIDATE(col, _("A row must have a column."));
00528             VALIDATE(col == cols, _("Number of columns differ."));
00529         }
00530 
00531     }
00532 
00533     DBG_GUI_P << "Window builder: grid has "
00534         << rows << " rows and " << cols << " columns.\n";
00535 }
00536 
00537 tgrid* tbuilder_grid::build() const
00538 {
00539     return build(new tgrid());
00540 }
00541 
00542 tgrid* tbuilder_grid::build (tgrid* grid) const
00543 {
00544     grid->set_id(id);
00545     grid->set_linked_group(linked_group);
00546     grid->set_rows_cols(rows, cols);
00547 
00548     log_scope2(log_gui_general, "Window builder: building grid");
00549 
00550     DBG_GUI_G << "Window builder: grid '" << id
00551         << "' has " << rows << " rows and "
00552         << cols << " columns.\n";
00553 
00554     for(unsigned x = 0; x < rows; ++x) {
00555         grid->set_row_grow_factor(x, row_grow_factor[x]);
00556         for(unsigned y = 0; y < cols; ++y) {
00557 
00558             if(x == 0) {
00559                 grid->set_column_grow_factor(y, col_grow_factor[y]);
00560             }
00561 
00562             DBG_GUI_G << "Window builder: adding child at " << x << ',' << y << ".\n";
00563 
00564             twidget* widget = widgets[x * cols + y]->build();
00565             grid->set_child(widget, x, y, flags[x * cols + y],  border_size[x * cols + y]);
00566         }
00567     }
00568 
00569     return grid;
00570 }
00571 
00572 } // namespace gui2
00573 /*WIKI
00574  * @page = GUIToolkitWML
00575  * @order = ZZZZZZ_footer
00576  *
00577  * [[Category: WML Reference]]
00578  * [[Category: GUI WML Reference]]
00579  */
00580 
00581 /*WIKI
00582  * @page = GUIWidgetInstanceWML
00583  * @order = ZZZZZZ_footer
00584  *
00585  * [[Category: WML Reference]]
00586  * [[Category: GUI WML Reference]]
00587  *
00588  */
00589 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:57 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs