gui/dialogs/mp_change_control.cpp

Go to the documentation of this file.
00001 /*
00002    Copyright (C) 2011 - 2012 by Lukasz Dobrogowski <lukasz.dobrogowski@gmail.com>
00003    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published by
00007    the Free Software Foundation; either version 2 of the License, or
00008    (at your option) any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 #define GETTEXT_DOMAIN "wesnoth-lib"
00016 
00017 #include "gui/dialogs/mp_change_control.hpp"
00018 
00019 #include "gui/dialogs/helper.hpp"
00020 #include "gui/widgets/button.hpp"
00021 #ifdef GUI2_EXPERIMENTAL_LISTBOX
00022 #include "gui/widgets/list.hpp"
00023 #else
00024 #include "gui/widgets/listbox.hpp"
00025 #endif
00026 #include "gui/widgets/settings.hpp"
00027 #include "gui/widgets/window.hpp"
00028 
00029 #include "formula_string_utils.hpp"
00030 #include "game_display.hpp"
00031 #include "game_preferences.hpp"
00032 #include "log.hpp"
00033 #include "marked-up_text.hpp"
00034 #include "resources.hpp"
00035 #include "team.hpp"
00036 
00037 #include <vector>
00038 #include <boost/bind.hpp>
00039 #include <boost/shared_ptr.hpp>
00040 
00041 static lg::log_domain log_gui("gui/dialogs/mp_change_control");
00042 #define ERR_GUI LOG_STREAM(err, log_gui)
00043 #define WRN_GUI LOG_STREAM(warn, log_gui)
00044 #define LOG_GUI LOG_STREAM(info, log_gui)
00045 #define DBG_GUI LOG_STREAM(debug, log_gui)
00046 
00047 namespace gui2 {
00048 
00049 /*WIKI
00050  * @page = GUIWindowDefinitionWML
00051  * @order = 2_mp_change_control
00052  *
00053  * == Change control dialog ==
00054  *
00055  * This shows the multiplayer change control dialog.
00056  *
00057  * @begin{table}{dialog_widgets}
00058  * sides_list & & listbox & m &
00059  *         List of sides participating in the MP game. $
00060  *
00061  * nicks_list & & listbox & m &
00062  *         List of nicks of all clients playing or observing the MP game. $
00063  *
00064  * @end{table}
00065  *
00066  */
00067 
00068 template <class D, class V, void (V::*fptr)(twindow&)>
00069 void dialog_view_callback(twidget* caller)
00070 {
00071         D* dialog = dynamic_cast<D*>(caller->dialog());
00072         assert(dialog);
00073         twindow* window = dynamic_cast<twindow*>(caller->get_window());
00074         assert(window);
00075         (*(dialog->get_view()).*fptr)(*window);
00076 }
00077 
00078 /**
00079  * The model is an interface defining the data to be displayed or otherwise
00080  * acted upon in the user interface.
00081  */
00082 class tmp_change_control::model {
00083 public:
00084     model()
00085         : sides_list(NULL)
00086         , nicks_list(NULL)
00087         , sides()
00088         , nicks()
00089     {
00090     }
00091 
00092     tlistbox *sides_list;
00093     tlistbox *nicks_list;
00094 
00095     // contains the mapping from listbox labels to actual sides
00096     // (note that due to hidden= attribute nth list item doesn't have to be nth side)
00097     std::vector<int> sides;
00098     // contains the mapping from listbox labels to actual nicks
00099     std::vector<std::string> nicks;
00100 
00101     void clear_sides()
00102     {
00103         DBG_GUI << "Sides list: clearing\n";
00104         sides_list->clear();
00105         sides.clear();
00106     }
00107 
00108     void add_side(int side_num, const std::string &label)
00109     {
00110         sides.push_back(side_num);
00111         DBG_GUI << "Sides list: adding item (side_num: \"" << side_num
00112             << "\" label: \"" << label << "\")\n";
00113         std::map<std::string, string_map> data;
00114         string_map item;
00115         item["id"] = std::string("side_")+str_cast(side_num);
00116         item["label"] = label;
00117         item["use_markup"] = "true";
00118         data.insert(std::make_pair("side", item));
00119         sides_list->add_row(data);
00120     }
00121 
00122     void clear_nicks()
00123     {
00124         DBG_GUI << "Nicks list: clearing\n";
00125         nicks_list->clear();
00126         nicks.clear();
00127     }
00128 
00129     void add_nick(const std::string &nick, const std::string &label)
00130     {
00131         DBG_GUI << "Nicks list: adding item (nick: \"" << nick
00132             << "\" label: \"" << label << "\")\n";
00133         nicks.push_back(nick);
00134         std::map<std::string, string_map> data;
00135         string_map item;
00136         item["id"] = nick;
00137         item["label"] = label;
00138         item["use_markup"] = "true";
00139         data.insert(std::make_pair("nick", item));
00140         nicks_list->add_row(data);
00141     }
00142 };
00143 
00144 class side_controller {
00145 public:
00146     side_controller(const std::string& name, tmp_change_control::model &m, int side_number)
00147         : model_(m), name_(name), side_number_(side_number)
00148     {
00149     }
00150 
00151     ~side_controller()
00152     {
00153     }
00154 
00155     std::string name() const
00156     {
00157         return name_;
00158     }
00159 
00160     int side_number() const
00161     {
00162         return side_number_;
00163     }
00164 
00165     void show_nicks_list()
00166     {
00167         DBG_GUI << "Nicks list: showing for side " << side_number_ << '\n';
00168         //model_.selected_side = side_number_;
00169         model_.clear_nicks();
00170 
00171         std::set<std::string> nicks;
00172         for (std::vector<team>::const_iterator it = resources::teams->begin();
00173              it != resources::teams->end(); ++it)
00174         {
00175             if (!it->is_ai() && it->controller()!=team::team_info::EMPTY && it->current_player().size()>0)
00176                 nicks.insert(it->current_player());
00177         }
00178 
00179         const std::set<std::string> &observers = resources::screen->observers();
00180 
00181         nicks.insert(observers.begin(),observers.end());
00182         nicks.insert(preferences::login()); // in case we are an observer, it isn't in the observers set then
00183         // and has to be added manually
00184 
00185         int i = 0; // because we need to know which row contains the controlling player
00186 
00187         foreach (const std::string &nick, nicks)
00188         {
00189             if (side_number_ <= static_cast<int>(resources::teams->size()) &&
00190                 resources::teams->at(side_number_-1).current_player() == nick)
00191             {
00192                 std::string label_str = "<b>" + nick + "</b>";
00193                 model_.add_nick(nick,label_str);
00194                 model_.nicks_list->select_row(i);
00195             }
00196             else
00197                 model_.add_nick(nick,nick);
00198             ++i;
00199         }
00200     }
00201     void handle_nicks_list_selection()
00202     {
00203         int selected = model_.nicks_list->get_selected_row();
00204         DBG_GUI << "Nicks list: row " << selected
00205             << " selected, it contains " << model_.nicks[selected] << '\n';
00206     }
00207     void update_view_from_model()
00208     {
00209         show_nicks_list();
00210     }
00211 
00212 private:
00213     tmp_change_control::model &model_;
00214     std::string const name_;
00215     int side_number_;
00216 };
00217 
00218 /**
00219  * The controller acts upon the model.
00220  *
00221  * It retrieves data from repositories, persists it, manipulates it, and
00222  * determines how it will be displayed in the view.
00223  */
00224 class tmp_change_control::controller {
00225 public:
00226     typedef std::vector< boost::shared_ptr<side_controller> > side_controller_ptr_vector;
00227     controller(model &m)
00228         : model_(m)
00229         , side_controllers_()
00230     {
00231     }
00232 
00233     void show_sides_list()
00234     {
00235         DBG_GUI << "Sides list: filling\n";
00236         model_.clear_sides();
00237         int sides = resources::teams ? static_cast<int>((*resources::teams).size()) : 0;
00238         for( int side = 1; side<=sides; ++side)
00239         {
00240             if (!resources::teams->at(side-1).hidden())
00241             {
00242                 string_map symbols;
00243                 symbols["side"] = str_cast(side);
00244                 std::string side_str = vgettext("Side $side", symbols);
00245                 side_str = font::span_color(team::get_side_color(side)) + side_str + "</span>";
00246                 model_.add_side(side,side_str);
00247                 side_controllers_.push_back(boost::shared_ptr<side_controller>(
00248                     new side_controller(side_str,model_,side)));
00249             }
00250         }
00251     }
00252 
00253     boost::shared_ptr<side_controller> get_side_controller()
00254     {
00255         int selected = model_.sides_list->get_selected_row();
00256         if (selected < 0 || selected >= static_cast<int>(side_controllers_.size()))
00257             return boost::shared_ptr<side_controller>(); // null pointer
00258         else
00259             return side_controllers_.at(selected);
00260     }
00261 
00262     void handle_sides_list_item_clicked()
00263     {
00264         int selected = model_.sides_list->get_selected_row();
00265         DBG_GUI << "Sides list: selected row: " << selected
00266             << " for side " << model_.sides[selected] << '\n';
00267         if (get_side_controller())
00268             get_side_controller()->update_view_from_model();
00269     }
00270 
00271     void handle_nicks_list_item_clicked()
00272     {
00273         int selected = model_.sides_list->get_selected_row();
00274         DBG_GUI << "Nicks list: selected row: " << selected
00275             << " with nick " << model_.nicks[selected] << '\n';
00276         if (get_side_controller())
00277             get_side_controller()->handle_nicks_list_selection();
00278     }
00279 
00280     void update_view_from_model()
00281     {
00282         if (get_side_controller())
00283             get_side_controller()->update_view_from_model();
00284     }
00285 
00286     void change_control(events::menu_handler *mh)
00287     {
00288         int selected_side = model_.sides_list->get_selected_row();
00289         int selected_nick = model_.nicks_list->get_selected_row();
00290         DBG_GUI << "Main: changing control of side " << model_.sides[selected_side]
00291                 << " to nick " << model_.nicks[selected_nick] << '\n';
00292         if (mh) // since in unit tests we pass a null pointer to it
00293             mh->request_control_change(model_.sides[selected_side], model_.nicks[selected_nick]);
00294     }
00295 
00296 private:
00297     model &model_;
00298     side_controller_ptr_vector side_controllers_;
00299 };
00300 
00301 
00302 /**
00303  * The view is an interface that displays data (the model) and routes user
00304  * commands to the controller to act upon that data.
00305  */
00306 class tmp_change_control::view {
00307 public:
00308     view()
00309         : model_(),controller_(model_)
00310     {
00311     }
00312 
00313     void pre_show(CVideo &/*video*/, twindow &window)
00314     {
00315         model_.clear_sides();
00316         controller_.show_sides_list();
00317         model_.clear_nicks();
00318         controller_.update_view_from_model();
00319         window.invalidate_layout();//workaround for assertion failure
00320     }
00321 
00322     void handle_sides_list_item_clicked(twindow &window)
00323     {
00324         controller_.handle_sides_list_item_clicked();
00325         window.invalidate_layout();//workaround for assertion failure
00326     }
00327 
00328     void handle_nicks_list_item_clicked(twindow &window)
00329     {
00330         controller_.handle_nicks_list_item_clicked();
00331         window.invalidate_layout();//workaround for assertion failure
00332     }
00333 
00334     void bind(twindow &window)
00335     {
00336         DBG_GUI << "Main: Binding widgets and callbacks\n";
00337         model_.sides_list = &find_widget<tlistbox>(&window, "sides_list", false);
00338         model_.nicks_list = &find_widget<tlistbox>(&window, "nicks_list", false);
00339 
00340 #ifdef GUI2_EXPERIMENTAL_LISTBOX
00341         connect_signal_notify_modified(*model_.sides_list, boost::bind(
00342                   &tmp_change_control::view::handle_sides_list_item_clicked
00343                 , this
00344                 , boost::ref(window)));
00345 
00346         connect_signal_notify_modified(*model_.nicks_list, boost::bind(
00347                   &tmp_change_control::view::handle_nicks_list_item_clicked
00348                 , this
00349                 , boost::ref(window)));
00350 #else
00351         model_.sides_list->set_callback_value_change(
00352             dialog_view_callback<tmp_change_control, tmp_change_control::view, &tmp_change_control::view::handle_sides_list_item_clicked>);
00353 
00354         model_.nicks_list->set_callback_value_change(
00355             dialog_view_callback<tmp_change_control, tmp_change_control::view, &tmp_change_control::view::handle_nicks_list_item_clicked>);
00356 #endif
00357     }
00358 
00359     void post_show(int retval, events::menu_handler *mh)
00360     {
00361         if (retval == twindow::OK)
00362         {
00363             controller_.change_control(mh);
00364         }
00365     }
00366 
00367 private:
00368     model model_;
00369     controller controller_;
00370 };
00371 
00372 REGISTER_DIALOG(mp_change_control)
00373 
00374 tmp_change_control::tmp_change_control(events::menu_handler *mh)
00375     : menu_handler_(mh), view_()
00376 {
00377     view_ = boost::shared_ptr<view>(new view());
00378 }
00379 
00380 boost::shared_ptr<tmp_change_control::view> tmp_change_control::get_view()
00381 {
00382     return view_;
00383 }
00384 
00385 void tmp_change_control::pre_show(CVideo& video, twindow& window)
00386 {
00387     view_->bind(window);
00388     view_->pre_show(video,window);
00389 }
00390 
00391 void tmp_change_control::post_show(twindow& /*window*/)
00392 {
00393     view_->post_show(get_retval(), menu_handler_);
00394 }
00395 
00396 } //end of 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:58 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs