gui/widgets/settings.cpp

Go to the documentation of this file.
00001 /* $Id: settings.cpp 53589 2012-03-21 04:10:51Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2007 - 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 /**
00017  * @file
00018  * Implementation of settings.hpp.
00019  */
00020 
00021 #define GETTEXT_DOMAIN "wesnoth-lib"
00022 
00023 #include "gui/widgets/settings.hpp"
00024 
00025 #include "asserts.hpp"
00026 #include "config_cache.hpp"
00027 #include "filesystem.hpp"
00028 #include "foreach.hpp"
00029 #include "gettext.hpp"
00030 #include "gui/auxiliary/log.hpp"
00031 #include "gui/auxiliary/tips.hpp"
00032 #include "gui/widgets/window.hpp"
00033 #include "serialization/parser.hpp"
00034 #include "serialization/preprocessor.hpp"
00035 #include "serialization/schema_validator.hpp"
00036 #include "formula_string_utils.hpp"
00037 
00038 namespace gui2 {
00039 
00040 bool new_widgets = false;
00041 
00042 namespace settings {
00043     unsigned screen_width = 0;
00044     unsigned screen_height = 0;
00045 
00046     unsigned gamemap_width = 0;
00047     unsigned gamemap_height = 0;
00048 
00049     unsigned popup_show_delay = 0;
00050     unsigned popup_show_time = 0;
00051     unsigned help_show_time = 0;
00052     unsigned double_click_time = 0;
00053     unsigned repeat_button_repeat_time = 0;
00054 
00055     std::string sound_button_click = "";
00056     std::string sound_toggle_button_click = "";
00057     std::string sound_toggle_panel_click = "";
00058     std::string sound_slider_adjust = "";
00059 
00060     t_string has_helptip_message;
00061 
00062     std::vector<ttip> tips;
00063 
00064     std::vector<ttip> get_tips()
00065     {
00066         return tips::shuffle(tips);
00067     }
00068 
00069 } // namespace settings
00070 
00071 /**
00072  * Returns the list of registered windows.
00073  *
00074  * The function can be used the look for registered windows or to add them.
00075  */
00076 static std::vector<std::string>& registered_window_types()
00077 {
00078     static std::vector<std::string> result;
00079     return result;
00080 }
00081 
00082 typedef std::map<
00083           std::string
00084         , boost::function<void(
00085               tgui_definition&
00086               , const std::string&
00087               , const config&
00088               , const char *key)> > tregistered_widget_type;
00089 
00090 static tregistered_widget_type& registred_widget_type()
00091 {
00092     static tregistered_widget_type result;
00093     return result;
00094 }
00095 
00096 struct tgui_definition
00097 {
00098     tgui_definition()
00099         : id()
00100         , description()
00101         , control_definition()
00102         , windows()
00103         , window_types()
00104         , popup_show_delay_(0)
00105         , popup_show_time_(0)
00106         , help_show_time_(0)
00107         , double_click_time_(0)
00108         , repeat_button_repeat_time_(0)
00109         , sound_button_click_()
00110         , sound_toggle_button_click_()
00111         , sound_toggle_panel_click_()
00112         , sound_slider_adjust_()
00113         , has_helptip_message_()
00114         , tips_()
00115     {
00116     }
00117 
00118     std::string id;
00119     t_string description;
00120 
00121     const std::string& read(const config& cfg);
00122 
00123     /** Activates a gui. */
00124     void activate() const;
00125 
00126     typedef std::map <std::string /*control type*/,
00127         std::map<std::string /*id*/, tcontrol_definition_ptr> >
00128         tcontrol_definition_map;
00129 
00130     tcontrol_definition_map control_definition;
00131 
00132     std::map<std::string, twindow_definition> windows;
00133 
00134     std::map<std::string, twindow_builder> window_types;
00135 
00136     void load_widget_definitions(
00137               const std::string& definition_type
00138             , const std::vector<tcontrol_definition_ptr>& definitions);
00139 private:
00140 
00141     unsigned popup_show_delay_;
00142     unsigned popup_show_time_;
00143     unsigned help_show_time_;
00144     unsigned double_click_time_;
00145     unsigned repeat_button_repeat_time_;
00146 
00147     std::string sound_button_click_;
00148     std::string sound_toggle_button_click_;
00149     std::string sound_toggle_panel_click_;
00150     std::string sound_slider_adjust_;
00151 
00152     t_string has_helptip_message_;
00153 
00154     std::vector<ttip> tips_;
00155 };
00156 
00157 const std::string& tgui_definition::read(const config& cfg)
00158 {
00159 /*WIKI
00160  * @page = GUIToolkitWML
00161  * @order = 1
00162  *
00163  * {{Autogenerated}}
00164  *
00165  * = GUI =
00166  *
00167  * The gui class contains the definitions of all widgets and windows used in
00168  * the game. This can be seen as a skin and it allows the user to define the
00169  * visual aspect of the various items. The visual aspect can be determined
00170  * depending on the size of the game window.
00171  *
00172  * Widgets have a definition and an instance, the definition contains the
00173  * general info/looks of a widget and the instance the actual looks. Eg the
00174  * where the button text is placed is the same for every button, but the
00175  * text of every button might differ.
00176  *
00177  * The default gui has the id ''default'' and must exist, in the default gui
00178  * there must a definition of every widget with the id ''default'' and every
00179  * window needs to be defined. If the definition of a widget with a certain
00180  * id doesn't exist it will fall back to default in the current gui, if it's
00181  * not defined there either it will fall back to the default widget in the
00182  * default theme. That way it's possible to slowly create your own gui and
00183  * test it.
00184  *
00185  * @begin{parent}{name="/"}
00186  * @begin{tag}{name="gui"}{min="0"}{max="1"}
00187  * The gui has the following data:
00188  * @begin{table}{config}
00189  *     id & string & &                  Unique id for this gui (theme). $
00190  *     description & t_string & &       Unique translatable name for this gui. $
00191  *
00192  *     widget_definitions & section & & The definitions of all
00193  *                                   [[#widget_list|widgets]]. $
00194  *     window & section & &             The definitions of all
00195  *                                   [[#window_list|windows]]. $
00196  *     settings & section & &           The settings for the gui. $
00197  * @end{table}
00198  *
00199  * <span id="widget_list"></span>List of available widgets:
00200  * @begin{table}{widget_overview}
00201  * Button &                       @macro = button_description $
00202  * Image &                        @macro = image_description $
00203  * Horizontal_listbox &           @macro = horizontal_listbox_description $
00204  * Horizontal_scrollbar &         @macro = horizontal_scrollbar_description $
00205  * Label &                        @macro = label_description $
00206  * Listbox &                      @macro = listbox_description $
00207  * Minimap &                      @macro = minimap_description $
00208  * Multi_page &                   @macro = multi_page_description $
00209  * Panel &                        @macro = panel_description $
00210  * Repeating_button &             @macro = repeating_button_description $
00211  * Scroll_label &                 @macro = scroll_label_description $
00212  * Slider &                       @macro = slider_description $
00213  * Spacer &                       @macro = spacer_description $
00214  * Stacked_widget &
00215  *     A stacked widget is a control several widgets can be stacked on top of
00216  *     each other in the same space. This is mainly intended for over- and
00217  *     underlays. (The widget is still experimental.) $
00218  *
00219  * Text_box &                     A single line text box. $
00220  * Tree_view &                    @macro = tree_view_description $
00221  * Toggle_button &
00222  *     A kind of button with two 'states' normal and selected. This is a more
00223  *     generic widget which is used for eg checkboxes and radioboxes. $
00224  *
00225  * Toggle_panel &
00226  *     Like a toggle button but then as panel so can hold multiple items in a
00227  *     grid. $
00228  *
00229  * Tooltip &                      A small tooltip with help. $
00230  * Tree_view &                    A tree view widget. $
00231  * Vertical_scrollbar &           A vertical scrollbar. $
00232  * Window &                       A window. $
00233  * @end{table}
00234  *
00235  * <span id="window_list"></span>List of available windows:
00236  * @begin{table}{window_overview}
00237  *     Addon_connect &                The dialog to connect to the addon server
00238  *                                   and maintain locally installed addons. $
00239  *     Addon_list &                   Shows the list of the addons to install or
00240  *                                   update. $
00241  *     Campaign_selection &           Shows the list of campaigns, to select one
00242  *                                   to play. $
00243  *     Language_selection &           The dialog to select the primairy language. $
00244  *     WML_message_left &             The ingame message dialog with a portrait
00245  *                                   on the left side. (Used for the WML messages.) $
00246  *     WML_message_right &            The ingame message dialog with a portrait
00247  *                                   on the right side. (Used for the WML
00248  *                                   messages.) $
00249  *     Message &                      A generic message dialog. $
00250  *     MP_connect &                   The dialog to connect to the MP server. $
00251  *     MP_method_selection &          The dialog to select the kind of MP game
00252  *                                   to play. Official server, local etc. $
00253  *     MP_server_list &               List of the 'official' MP servers. $
00254  *     MP_login &                     The dialog to provide a password for registered
00255  *                                   usernames, request a password reminder or
00256  *                                   choose a different username. $
00257  *     MP_cmd_wrapper &               Perform various actions on the selected user
00258  *                                   (e.g. whispering or kicking). $
00259  *     MP_create_game &               The dialog to select and create an MP game. $
00260  *     Title_screen &                 The title screen. $
00261  *     Editor_new_map &               Creates a new map in the editor. $
00262  *     Editor_generate_map &          Generates a random map in the editor. $
00263  *     Editor_resize_map &            Resizes a map in the editor. $
00264  *     Editor_settings &              The settings specific for the editor. $
00265  * @end{table}
00266  * @end{tag}{name=gui}
00267  * @end{parent}{name="/"}
00268  */
00269     id = cfg["id"].str();
00270     description = cfg["description"];
00271 
00272     VALIDATE(!id.empty(), missing_mandatory_wml_key("gui", "id"));
00273     VALIDATE(!description.empty(), missing_mandatory_wml_key("gui", "description"));
00274 
00275     DBG_GUI_P << "Parsing gui " << id << '\n';
00276 
00277     /***** Control definitions *****/
00278     typedef std::pair<
00279               const std::string
00280             , boost::function<void(
00281                   tgui_definition&
00282                   , const std::string&
00283                   , const config&
00284                   , const char *key)> > thack;
00285 
00286     foreach(thack& widget_type, registred_widget_type()) {
00287         widget_type.second(*this, widget_type.first, cfg, NULL);
00288     }
00289 
00290     /***** Window types *****/
00291     foreach (const config &w, cfg.child_range("window")) {
00292         std::pair<std::string, twindow_builder> child;
00293         child.first = child.second.read(w);
00294         window_types.insert(child);
00295     }
00296 
00297     if(id == "default") {
00298         // The default gui needs to define all window types since we're the
00299         // fallback in case another gui doesn't define the window type.
00300         for(std::vector<std::string>::const_iterator itor
00301                 = registered_window_types().begin()
00302                 ; itor != registered_window_types().end()
00303                 ; ++itor) {
00304 
00305             const std::string error_msg("Window not defined in WML: '" +
00306                                          *itor +
00307                                          "'. Perhaps a mismatch between data and source versions."
00308                                          " Try --data-dir <trunk-dir>" );
00309             VALIDATE(window_types.find(*itor) != window_types.end(), error_msg );
00310         }
00311     }
00312 
00313     /***** settings *****/
00314 /*WIKI
00315  * @page = GUIToolkitWML
00316  * @order = 1
00317  *
00318  * @begin{parent}{name="gui/"}
00319  * @begin{tag}{name="settings"}{min="0"}{max="1"}
00320  * A setting section has the following variables:
00321  * @begin{table}{config}
00322  *     popup_show_delay & unsigned & 0 & The time it take before the popup shows
00323  *                                     if the mouse moves over the widget. 0
00324  *                                     means show directly. $
00325  *     popup_show_time & unsigned & 0 &  The time a shown popup remains visible.
00326  *                                     0 means until the mouse leaves the
00327  *                                     widget. $
00328  *     help_show_time & unsigned & 0 &   The time a shown help remains visible.
00329  *                                     0 means until the mouse leaves the
00330  *                                     widget. $
00331  *     double_click_time & unsigned & &   The time between two clicks to still be a
00332  *                                     double click. $
00333  *     repeat_button_repeat_time & unsigned & 0 &
00334  *                                     The time a repeating button waits before
00335  *                                     the next event is issued if the button
00336  *                                     is still pressed down. $
00337  *
00338  *     sound_button_click & string & "" &
00339  *                                     The sound played if a button is
00340  *                                     clicked. $
00341  *     sound_toggle_button_click & string & "" &
00342  *                                     The sound played if a toggle button is
00343  *                                     clicked. $
00344  *     sound_toggle_panel_click & string & "" &
00345  *                                     The sound played if a toggle panel is
00346  *                                     clicked. Normally the toggle panels
00347  *                                     are the items in a listbox. If a
00348  *                                     toggle button is in the listbox it's
00349  *                                     sound is played. $
00350  *     sound_slider_adjust & string & "" &
00351  *                                     The sound played if a slider is
00352  *                                     adjusted. $
00353  *
00354  *     has_helptip_message & t_string & &
00355  *                                     The string used to append the tooltip
00356  *                                     if there is also a helptip. The WML
00357  *                                     variable @$hotkey can be used to get show
00358  *                                     the name of the hotkey for the help. $
00359  * @end{table}
00360  * @end{tag}{name="settings"}
00361  */
00362 
00363 /*WIKI
00364  * @begin{tag}{name="tip"}{min="0"}{max="-1"}
00365  * @begin{table}{config}
00366  *     source & t_string & & Author
00367  *     text & t_string & & Text of the tip.
00368  * @end{table}
00369  * @end{tag}{name="tip"}
00370  * @end{parent}{name="gui/"}
00371 */
00372 
00373 /**
00374  * @todo Regarding sounds:
00375  * Need to evaluate but probably we want the widget definition be able to:
00376  * - Override the default (and clear it). This will allow toggle buttons in a
00377  *   listbox to sound like a toggle panel.
00378  * - Override the default and above per instance of the widget, some buttons
00379  *   can give a different sound.
00380  */
00381     const config &settings = cfg.child("settings");
00382 
00383     popup_show_delay_ = settings["popup_show_delay"];
00384     popup_show_time_ = settings["popup_show_time"];
00385     help_show_time_ = settings["help_show_time"];
00386     double_click_time_ = settings["double_click_time"];
00387 
00388     repeat_button_repeat_time_ = settings["repeat_button_repeat_time"];
00389 
00390     VALIDATE(double_click_time_, missing_mandatory_wml_key("settings", "double_click_time"));
00391 
00392     sound_button_click_ = settings["sound_button_click"].str();
00393     sound_toggle_button_click_ = settings["sound_toggle_button_click"].str();
00394     sound_toggle_panel_click_ = settings["sound_toggle_panel_click"].str();
00395     sound_slider_adjust_ = settings["sound_slider_adjust"].str();
00396 
00397     has_helptip_message_ = settings["has_helptip_message"];
00398 
00399     VALIDATE(!has_helptip_message_.empty(),
00400             missing_mandatory_wml_key("[settings]", "has_helptip_message"));
00401 
00402     tips_ = tips::load(cfg);
00403 
00404     return id;
00405 }
00406 
00407 void tgui_definition::activate() const
00408 {
00409     settings::popup_show_delay = popup_show_delay_;
00410     settings::popup_show_time = popup_show_time_;
00411     settings::help_show_time = help_show_time_;
00412     settings::double_click_time = double_click_time_;
00413     settings::repeat_button_repeat_time = repeat_button_repeat_time_;
00414     settings::sound_button_click = sound_button_click_;
00415     settings::sound_toggle_button_click = sound_toggle_button_click_;
00416     settings::sound_toggle_panel_click = sound_toggle_panel_click_;
00417     settings::sound_slider_adjust = sound_slider_adjust_;
00418     settings::has_helptip_message = has_helptip_message_;
00419     settings::tips = tips_;
00420 }
00421 
00422 void tgui_definition::load_widget_definitions(
00423           const std::string& definition_type
00424         , const std::vector<tcontrol_definition_ptr>& definitions)
00425 {
00426     foreach(const tcontrol_definition_ptr& def, definitions) {
00427 
00428         // We assume all definitions are unique if not we would leak memory.
00429         assert(control_definition[definition_type].find(def->id)
00430                 == control_definition[definition_type].end());
00431 
00432         control_definition[definition_type]
00433                 .insert(std::make_pair(def->id, def));
00434     }
00435 
00436     utils::string_map symbols;
00437     symbols["definition"] = definition_type;
00438     symbols["id"] = "default";
00439     t_string msg(vgettext(
00440               "Widget definition '$definition' "
00441               "doesn't contain the definition for '$id'."
00442             , symbols));
00443 
00444     VALIDATE(control_definition[definition_type].find("default")
00445             != control_definition[definition_type].end(), msg);
00446 
00447 }
00448 
00449     /** Map with all known windows, (the builder class builds a window). */
00450     std::map<std::string, twindow_builder> windows;
00451 
00452     /** Map with all known guis. */
00453     std::map<std::string, tgui_definition> guis;
00454 
00455     /** Points to the current gui. */
00456     std::map<std::string, tgui_definition>::const_iterator current_gui = guis.end();
00457 
00458 void register_window(const std::string& id)
00459 {
00460     const std::vector<std::string>::iterator itor = std::find(
00461               registered_window_types().begin()
00462             , registered_window_types().end()
00463             , id);
00464 
00465     if(itor == registered_window_types().end()) {
00466         registered_window_types().push_back(id);
00467     }
00468 }
00469 
00470 std::vector<std::string> tunit_test_access_only::get_registered_window_list()
00471 {
00472     return gui2::registered_window_types();
00473 }
00474 
00475 void load_settings()
00476 {
00477     LOG_GUI_G << "Setting: init gui.\n";
00478 
00479     // Init.
00480     twindow::update_screen_size();
00481 
00482     // Read file.
00483     config cfg;
00484     try {
00485         schema_validation::schema_validator
00486                 validator (get_wml_location("gui/schema.cfg"));
00487         preproc_map preproc(
00488                 game_config::config_cache::instance().get_preproc_map());
00489         scoped_istream stream = preprocess_file(get_wml_location("gui/default.cfg"), &preproc);
00490 
00491         read(cfg, *stream, &validator);
00492     } catch(config::error& e) {
00493         ERR_GUI_P << e.what() << '\n';
00494         ERR_GUI_P << "Setting: could not read file 'data/gui/default.cfg'.\n";
00495     }
00496     catch(const abstract_validator::error& e){
00497             ERR_GUI_P << "Setting: could not read file 'data/gui/schema.cfg'.\n";
00498             ERR_GUI_P << e.message;
00499     }
00500     // Parse guis
00501     foreach (const config &g, cfg.child_range("gui")) {
00502         std::pair<std::string, tgui_definition> child;
00503         child.first = child.second.read(g);
00504         guis.insert(child);
00505     }
00506 
00507     VALIDATE(guis.find("default") != guis.end(), _ ("No default gui defined."));
00508 
00509     current_gui = guis.find("default");
00510     current_gui->second.activate();
00511 }
00512 
00513 tstate_definition::tstate_definition(const config &cfg) :
00514     canvas()
00515 {
00516 /*WIKI
00517  * @page = GUIToolkitWML
00518  * @order = 1_widget
00519  *
00520  * == State ==
00521  *
00522  * @begin{parent}{name="generic/"}
00523  * @begin{tag}{name="state"}{min=0}{max=1}
00524  * Definition of a state. A state contains the info what to do in a state.
00525  * Atm this is rather focussed on the drawing part, might change later.
00526  * Keys:
00527  * @begin{table}{config}
00528  *     draw & section & &                 Section with drawing directions for a canvas. $
00529  * @end{table}
00530  * @end{tag}{name="state"}
00531  * @end{parent}{name="generic/"}
00532  *
00533  */
00534 
00535     const config &draw = *(cfg ? &cfg.child("draw") : &cfg);
00536 
00537     VALIDATE(draw, _("No state or draw section defined."));
00538 
00539     canvas.set_cfg(draw);
00540 }
00541 
00542 void register_widget(const std::string& id
00543         , boost::function<void(
00544               tgui_definition& gui_definition
00545             , const std::string& definition_type
00546             , const config& cfg
00547             , const char *key)> functor)
00548 {
00549     registred_widget_type().insert(std::make_pair(id, functor));
00550 }
00551 
00552 void load_widget_definitions(
00553       tgui_definition& gui_definition
00554     , const std::string& definition_type
00555     , const std::vector<tcontrol_definition_ptr>& definitions)
00556 {
00557     DBG_GUI_P << "Load definition '" << definition_type << "'.\n";
00558     gui_definition.load_widget_definitions(definition_type, definitions);
00559 }
00560 
00561 tresolution_definition_ptr get_control(
00562         const std::string& control_type, const std::string& definition)
00563 {
00564     const tgui_definition::tcontrol_definition_map::const_iterator
00565 #ifdef GUI2_EXPERIMENTAL_LISTBOX
00566         control_definition = (control_type == "list")
00567                 ? current_gui->second.control_definition.find("listbox")
00568                 : current_gui->second.control_definition.find(control_type);
00569 #else
00570         control_definition =
00571                 current_gui->second.control_definition.find(control_type);
00572 #endif
00573 
00574     ASSERT_LOG(control_definition != current_gui->second.control_definition.end(),
00575             "Type '" << control_type << "' is unknown.");
00576 
00577     std::map<std::string, tcontrol_definition_ptr>::const_iterator
00578         control = control_definition->second.find(definition);
00579 
00580     if(control == control_definition->second.end()) {
00581         LOG_GUI_G << "Control: type '" << control_type << "' definition '"
00582             << definition << "' not found, falling back to 'default'.\n";
00583         control = control_definition->second.find("default");
00584         assert(control != control_definition->second.end());
00585     }
00586 
00587     for(std::vector<tresolution_definition_ptr>::const_iterator
00588             itor = (*control->second).resolutions.begin(),
00589             end = (*control->second).resolutions.end();
00590             itor != end;
00591             ++itor) {
00592 
00593         if(settings::screen_width <= (**itor).window_width &&
00594                 settings::screen_height <= (**itor).window_height) {
00595 
00596             return *itor;
00597         } else if (itor == end - 1) {
00598             return *itor;
00599         }
00600     }
00601 
00602     ERROR_LOG(false);
00603 }
00604 
00605 std::vector<twindow_builder::tresolution>::const_iterator get_window_builder(
00606         const std::string& type)
00607 {
00608     twindow::update_screen_size();
00609 
00610     std::map<std::string, twindow_builder>::const_iterator
00611         window = current_gui->second.window_types.find(type);
00612 
00613     if(true) { // FIXME Test for default gui.
00614         if(window == current_gui->second.window_types.end()) {
00615             throw twindow_builder_invalid_id();
00616         }
00617     } else {
00618         // FIXME Get the definition in the default gui and do an assertion test.
00619     }
00620 
00621     for(std::vector<twindow_builder::tresolution>::const_iterator
00622             itor = window->second.resolutions.begin(),
00623             end = window->second.resolutions.end();
00624             itor != end;
00625             ++itor) {
00626 
00627         if(settings::screen_width <= itor->window_width &&
00628                 settings::screen_height <= itor->window_height) {
00629 
00630             return itor;
00631         } else if (itor == end - 1) {
00632             return itor;
00633         }
00634     }
00635 
00636     ERROR_LOG(false);
00637 }
00638 
00639 /*WIKI
00640  * @page = GUIWidgetDefinitionWML
00641  * @order = ZZZZZZ_footer
00642  *
00643  * [[Category: WML Reference]]
00644  * [[Category: GUI WML Reference]]
00645  *
00646  */
00647 
00648 } // namespace gui2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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