dialogs.cpp

Go to the documentation of this file.
00001 /* $Id: dialogs.cpp 26765 2008-05-22 09:56:27Z baufo $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
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 version 2
00008    or 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 //! @file dialogs.cpp
00016 //! Various dialogs: advance_unit, show_objectives, save+load game, network::connection.
00017 
00018 #include "global.hpp"
00019 
00020 #include "dialogs.hpp"
00021 #include "game_errors.hpp"
00022 #include "game_events.hpp"
00023 #include "game_preferences.hpp"
00024 #include "gettext.hpp"
00025 #include "help.hpp"
00026 #include "language.hpp"
00027 #include "log.hpp"
00028 #include "marked-up_text.hpp"
00029 #include "menu_events.hpp"
00030 #include "minimap.hpp"
00031 #include "replay.hpp"
00032 #include "thread.hpp"
00033 #include "unit.hpp"
00034 #include "wml_separators.hpp"
00035 #include "widgets/progressbar.hpp"
00036 #include "wml_exception.hpp"
00037 
00038 #include <assert.h>
00039 #include <clocale>
00040 
00041 #define LOG_NG LOG_STREAM(info, engine)
00042 #define LOG_DP LOG_STREAM(info, display)
00043 #define ERR_G  LOG_STREAM(err, general)
00044 #define ERR_CF LOG_STREAM(err, config)
00045 
00046 namespace dialogs
00047 {
00048 
00049 void advance_unit(const gamemap& map,
00050                   unit_map& units,
00051                   gamemap::location loc,
00052                   game_display& gui,
00053           bool random_choice,
00054           const bool add_replay_event)
00055 {
00056     unit_map::iterator u = units.find(loc);
00057     if(u == units.end() || u->second.advances() == false)
00058         return;
00059 
00060     LOG_DP << "advance_unit: " << u->second.type_id() << "\n";
00061 
00062     const std::vector<std::string>& options = u->second.advances_to();
00063 
00064     std::vector<std::string> lang_options;
00065 
00066     std::vector<unit> sample_units;
00067     for(std::vector<std::string>::const_iterator op = options.begin(); op != options.end(); ++op) {
00068         sample_units.push_back(::get_advanced_unit(units,loc,*op));
00069         const unit& type = sample_units.back();
00070 
00071 #ifdef LOW_MEM
00072         lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + COLUMN_SEPARATOR + type.type_name());
00073 #else
00074         lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + u->second.image_mods() + COLUMN_SEPARATOR + type.type_name());
00075 #endif
00076         preferences::encountered_units().insert(*op);
00077     }
00078 
00079     const config::child_list& mod_options = u->second.get_modification_advances();
00080 
00081     for(config::child_list::const_iterator mod = mod_options.begin(); mod != mod_options.end(); ++mod) {
00082         sample_units.push_back(::get_advanced_unit(units,loc,u->second.type_id()));
00083         sample_units.back().add_modification("advance",**mod);
00084         const unit& type = sample_units.back();
00085         if((**mod)["image"].str().size()){
00086           lang_options.push_back(IMAGE_PREFIX + (**mod)["image"].str() + COLUMN_SEPARATOR + (**mod)["description"].str());
00087         }else{
00088 #ifdef LOW_MEM
00089           lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + COLUMN_SEPARATOR + (**mod)["description"].str());
00090 #else
00091           lang_options.push_back(IMAGE_PREFIX + type.absolute_image() + u->second.image_mods() + COLUMN_SEPARATOR + (**mod)["description"].str());
00092 #endif
00093         }
00094     }
00095 
00096     LOG_DP << "options: " << options.size() << "\n";
00097 
00098     int res = 0;
00099 
00100     if(lang_options.empty()) {
00101         return;
00102     } else if(random_choice) {
00103         res = rand()%lang_options.size();
00104     } else if(lang_options.size() > 1) {
00105 
00106         units_list_preview_pane unit_preview(gui,&map,sample_units);
00107         std::vector<gui::preview_pane*> preview_panes;
00108         preview_panes.push_back(&unit_preview);
00109 
00110         gui::dialog advances = gui::dialog(gui,
00111                       _("Advance Unit"),
00112                               _("What should our victorious unit become?"),
00113                               gui::OK_ONLY);
00114         advances.set_menu(lang_options);
00115         advances.set_panes(preview_panes);
00116         res = advances.show();
00117     }
00118 
00119     if(add_replay_event) {
00120         recorder.add_advancement(loc);
00121     }
00122 
00123     recorder.choose_option(res);
00124 
00125     LOG_DP << "animating advancement...\n";
00126     animate_unit_advancement(units,loc,gui,size_t(res));
00127 
00128     // In some rare cases the unit can have enough XP to advance again,
00129     // so try to do that.
00130     // Make sure that we don't enter an infinite level loop.
00131     u = units.find(loc);
00132     if(u != units.end()) {
00133         // Level 10 unit gives 80 XP and the highest mainline is level 5
00134         if(u->second.experience() < 81) {
00135             // For all leveling up we have to add advancement to replay here because replay
00136             // doesn't handle multi advancemnet
00137             advance_unit(map, units, loc, gui, random_choice, true);
00138         } else {
00139             LOG_STREAM(err, config) << "Unit has an too high amount of " << u->second.experience()
00140                 << " XP left, cascade leveling disabled\n";
00141         }
00142     } else {
00143         LOG_STREAM(err, engine) << "Unit advanced no longer exists\n";
00144     }
00145 }
00146 
00147 bool animate_unit_advancement(unit_map& units, gamemap::location loc, game_display& gui, size_t choice)
00148 {
00149     const events::command_disabler cmd_disabler;
00150 
00151     unit_map::iterator u = units.find(loc);
00152     if(u == units.end() || u->second.advances() == false) {
00153         return false;
00154     }
00155 
00156     const std::vector<std::string>& options = u->second.advances_to();
00157     const config::child_list& mod_options = u->second.get_modification_advances();
00158 
00159     if(choice >= options.size() + mod_options.size()) {
00160         return false;
00161     }
00162 
00163     // When the unit advances, it fades to white, and then switches
00164     // to the new unit, then fades back to the normal colour
00165 
00166     if(!gui.video().update_locked()) {
00167         unit_animator animator;
00168         animator.add_animation(&u->second,"levelout",u->first);
00169         animator.start_animations();
00170         animator.wait_for_end();
00171     }
00172 
00173     if(choice < options.size()) {
00174         const std::string& chosen_unit = options[choice];
00175 		::advance_unit(units,loc,chosen_unit);
00176     } else {
00177         unit amla_unit(u->second);
00178 
00179         LOG_NG << "firing advance event (AMLA)\n";
00180         game_events::fire("advance",loc);
00181 
00182         amla_unit.get_experience(-amla_unit.max_experience()); // subtract xp required
00183         amla_unit.add_modification("advance",*mod_options[choice - options.size()]);
00184         units.replace(new std::pair<gamemap::location,unit>(loc,amla_unit));
00185 
00186         LOG_NG << "firing post_advance event (AMLA)\n";
00187         game_events::fire("post_advance",loc);
00188     }
00189 
00190     u = units.find(loc);
00191     gui.invalidate_unit();
00192 
00193     if(u != units.end() && !gui.video().update_locked()) {
00194         unit_animator animator;
00195         animator.add_animation(&u->second,"levelin",u->first);
00196         animator.start_animations();
00197         animator.wait_for_end();
00198         u->second.set_standing(u->first);
00199         gui.invalidate(loc);
00200         gui.draw();
00201         events::pump();
00202     }
00203 
00204     gui.invalidate_all();
00205     gui.draw();
00206 
00207     return true;
00208 }
00209 
00210 void show_objectives(game_display& disp, const config& level, const std::string& objectives)
00211 {
00212     static const std::string no_objectives(_("No objectives available"));
00213     const std::string& name = level["name"];
00214     std::string campaign_name = std::string(level["campaign"]);
00215     replace_underbar2space(campaign_name);
00216 
00217     gui::message_dialog(disp, "", "*~" + name +
00218             (campaign_name.empty() ? "\n" : " - " + campaign_name + "\n") +
00219                     (objectives.empty() ? no_objectives : objectives)
00220                     ).show();
00221 }
00222 
00223 bool is_illegal_file_char(char c)
00224 {
00225     return c == '/' || c == '\\' || c == ':'
00226     #ifdef WIN32
00227     || c == '?' || c == '|' || c == '<' || c == '>' || c == '*' || c == '"'
00228     #endif
00229     ;
00230 }
00231 
00232 int get_save_name(display & disp,const std::string& message, const std::string& txt_label,
00233                   std::string* fname, gui::DIALOG_TYPE dialog_type, const std::string& title,
00234                   const bool has_exit_button, const bool ask_for_filename)
00235 {
00236     static int quit_prompt = 0;
00237     const std::string& tmp_title = (title.empty()) ? _("Save Game") : title;
00238     bool ignore_opt = false;
00239     int overwrite=0;
00240     int res=0;
00241     bool ask = ask_for_filename;
00242     do {
00243         if (ask) {
00244             gui::dialog d(disp, tmp_title, message, dialog_type);
00245             d.set_textbox(txt_label, *fname);
00246             if(has_exit_button) {
00247                 d.add_button(new gui::dialog_button(disp.video(), _("Quit Game"),
00248                     gui::button::TYPE_PRESS, 2), gui::dialog::BUTTON_STANDARD);
00249                 if(quit_prompt < 0) {
00250                     res = 1;
00251                 } else if(quit_prompt > 5) {
00252                     d.add_button(new gui::dialog_button(disp.video(), _("Ignore All"),
00253                         gui::button::TYPE_CHECK), gui::dialog::BUTTON_CHECKBOX);
00254                     res = d.show();
00255                     ignore_opt = d.option_checked();
00256                 } else {
00257                     res = d.show();
00258                     if(res == 1) {
00259                         ++quit_prompt;
00260                     } else {
00261                         quit_prompt = 0;
00262                     }
00263                 }
00264             } else {
00265                 res = d.show();
00266             }
00267             *fname = d.textbox_text();
00268         } else {
00269             ask = true;
00270         }
00271 
00272         if (std::count_if(fname->begin(),fname->end(),is_illegal_file_char)) {
00273             gui::message_dialog(disp, _("Error"),
00274                 _("Save names may not contain colons, slashes, or backslashes. "
00275                 "Please choose a different name.")).show();
00276             overwrite = 1;
00277             continue;
00278         }
00279 
00280         if (is_gzip_file(*fname)) {
00281             gui::message_dialog(disp, _("Error"),
00282                 _("Save names should not end on '.gz'. "
00283                 "Please choose a different name.")).show();
00284             overwrite = 1;
00285             continue;
00286         }
00287 
00288         if (res == 0 && save_game_exists(*fname)) {
00289             std::stringstream s;
00290             s << _("Save already exists. Do you want to overwrite it?")
00291               << std::endl << _("Name: ") << *fname;
00292             overwrite = gui::dialog(disp,_("Overwrite?"),
00293                 s.str(), gui::YES_NO).show();
00294         } else {
00295             overwrite = 0;
00296         }
00297     } while ((res == 0) && (overwrite != 0));
00298 
00299     if(ignore_opt) {
00300         quit_prompt = -1;
00301     }
00302     return res;
00303 }
00304 
00305 //! Class to handle deleting a saved game.
00306 namespace {
00307 
00308 class load_game_filter_textbox : public gui::dialog_textbox {
00309 public:
00310     load_game_filter_textbox(CVideo& video, const std::vector<std::string>& items, gui::dialog& dialog)
00311       : gui::dialog_textbox(new gui::label(video, _("Filter: ")), video, 250),
00312         items_(items),
00313         dialog_(dialog),
00314         first_time_(true)
00315     {
00316         sorter_.set_alpha_sort(0).set_id_sort(1);
00317         set_text("");
00318     }
00319 
00320     int get_save_index(int index) const {
00321         //we must add one to the index to ignore the header row, and
00322         //then subtract one from the result to return the index not including
00323         //the header row.
00324         ++index;
00325         if(size_t(index) >= index_map_.size()) {
00326             return -1;
00327         }
00328         return index_map_[index]-1;
00329     }
00330 private:
00331     std::vector<std::string> items_, filtered_items_;
00332     std::vector<int> index_map_;
00333     gui::dialog& dialog_;
00334     gui::menu::basic_sorter sorter_;
00335     bool first_time_;
00336     virtual void handle_text_changed(const wide_string& text) {
00337         filtered_items_.clear();
00338         index_map_.clear();
00339         const std::string t = utils::wstring_to_string(text);
00340         for(size_t n = 0; n != items_.size(); ++n) {
00341             if(n == 0 || std::search(items_[n].begin(), items_[n].end(),
00342                                      t.begin(), t.end(), chars_equal_insensitive) != items_[n].end()) {
00343                 filtered_items_.push_back(items_[n]);
00344                 index_map_.push_back(n);
00345             }
00346         }
00347 
00348         if(first_time_) {
00349             dialog_.set_menu(filtered_items_, &sorter_);
00350             first_time_ = false;
00351         } else {
00352             dialog_.set_menu_items(filtered_items_);
00353         }
00354     }
00355 };
00356 
00357 class delete_save : public gui::dialog_button_action
00358 {
00359 public:
00360     delete_save(display& disp, load_game_filter_textbox& filter, std::vector<save_info>& saves, std::vector<config*>& save_summaries) : disp_(disp), saves_(saves), summaries_(save_summaries), filter_(filter) {}
00361 private:
00362     gui::dialog_button_action::RESULT button_pressed(int menu_selection);
00363 
00364     display& disp_;
00365     std::vector<save_info>& saves_;
00366     std::vector<config*>& summaries_;
00367     load_game_filter_textbox& filter_;
00368 };
00369 
00370 gui::dialog_button_action::RESULT delete_save::button_pressed(int menu_selection)
00371 {
00372     const size_t index = size_t(filter_.get_save_index(menu_selection));
00373     if(index < saves_.size()) {
00374 
00375         // See if we should ask the user for deletion confirmation
00376         if(preferences::ask_delete_saves()) {
00377             gui::dialog dmenu(disp_,"",
00378                            _("Do you really want to delete this game?"),
00379                            gui::YES_NO);
00380             dmenu.add_option(_("Don't ask me again!"), true);
00381             const int res = dmenu.show();
00382             // See if the user doesn't want to be asked this again
00383             if(dmenu.option_checked()) {
00384                 preferences::set_ask_delete_saves(false);
00385             }
00386 
00387             if(res != 0) {
00388                 return gui::CONTINUE_DIALOG;
00389             }
00390         }
00391 
00392         // Delete the file
00393         delete_game(saves_[index].name);
00394 
00395         // Remove it from the list of saves
00396         saves_.erase(saves_.begin() + index);
00397 
00398         if(index < summaries_.size()) {
00399             summaries_.erase(summaries_.begin() + index);
00400         }
00401 
00402         return gui::DELETE_ITEM;
00403     } else {
00404         return gui::CONTINUE_DIALOG;
00405     }
00406 }
00407 
00408 static const int save_preview_border = 10;
00409 
00410 class save_preview_pane : public gui::preview_pane
00411 {
00412 public:
00413     save_preview_pane(CVideo &video, const config& game_config, gamemap* map,
00414                       const std::vector<save_info>& info, const std::vector<config*>& summaries, const load_game_filter_textbox& textbox)
00415         : gui::preview_pane(video), game_config_(&game_config), map_(map), info_(&info), summaries_(&summaries), index_(0), textbox_(textbox)
00416     {
00417         set_measurements(minimum<int>(200,video.getx()/4),
00418                  minimum<int>(400,video.gety() * 4/5));
00419     }
00420 
00421     void draw_contents();
00422     void set_selection(int index) {
00423         index_ = textbox_.get_save_index(index);
00424         set_dirty();
00425     }
00426 
00427     bool left_side() const { return true; }
00428 
00429 private:
00430     const config* game_config_;
00431     gamemap* map_;
00432     const std::vector<save_info>* info_;
00433     const std::vector<config*>* summaries_;
00434     int index_;
00435     std::map<std::string,surface> map_cache_;
00436     const load_game_filter_textbox& textbox_;
00437 };
00438 
00439 void save_preview_pane::draw_contents()
00440 {
00441     if (size_t(index_) >= summaries_->size() || info_->size() != summaries_->size()) {
00442         return;
00443     }
00444 
00445     std::string dummy;
00446     config& summary = *(*summaries_)[index_];
00447     if (summary["label"] == ""){
00448         try {
00449             load_game_summary((*info_)[index_].name, summary, &dummy);
00450             *(*summaries_)[index_] = summary;
00451         } catch(game::load_game_failed&) {
00452             summary["corrupt"] = "yes";
00453         }
00454     }
00455 
00456     surface const screen = video().getSurface();
00457 
00458     SDL_Rect const &loc = location();
00459     const SDL_Rect area = { loc.x + save_preview_border, loc.y + save_preview_border,
00460                             loc.w - save_preview_border * 2, loc.h - save_preview_border * 2 };
00461     SDL_Rect clip_area = area;
00462     const clip_rect_setter clipper(screen,clip_area);
00463 
00464     int ypos = area.y;
00465 
00466     const unit_type_data::unit_type_map::const_iterator leader = unit_type_data::types().find(summary["leader"]);
00467     if(leader != unit_type_data::types().end()) {
00468 
00469 #ifdef LOW_MEM
00470         const surface image(image::get_image(leader->second.image()));
00471 #else
00472         const surface image(image::get_image(leader->second.image() + "~RC(" + leader->second.flag_rgb() + ">1)"));
00473 #endif
00474 
00475         if(image != NULL) {
00476             SDL_Rect image_rect = {area.x,area.y,image->w,image->h};
00477             ypos += image_rect.h + save_preview_border;
00478 
00479             SDL_BlitSurface(image,NULL,screen,&image_rect);
00480         }
00481     }
00482 
00483     std::string map_data = summary["map_data"];
00484     if(map_data.empty()) {
00485         const config* const scenario = game_config_->find_child(summary["campaign_type"],"id",summary["scenario"]);
00486         if(scenario != NULL && scenario->find_child("side","shroud","yes") == NULL) {
00487             map_data = (*scenario)["map_data"];
00488             if(map_data.empty() && (*scenario)["map"].empty() == false) {
00489                 try {
00490                     map_data = read_map((*scenario)["map"]);
00491                 } catch(io_exception& e) {
00492                     ERR_G << "could not read map '" << (*scenario)["map"] << "': " << e.what() << "\n";
00493                 }
00494             }
00495         }
00496     }
00497 
00498     surface map_surf(NULL);
00499 
00500     if(map_data.empty() == false) {
00501         const std::map<std::string,surface>::const_iterator itor = map_cache_.find(map_data);
00502         if(itor != map_cache_.end()) {
00503             map_surf = itor->second;
00504         } else if(map_ != NULL) {
00505             try {
00506 #ifdef USE_TINY_GUI
00507                 const int minimap_size = 60;
00508 #else
00509                 const int minimap_size = 100;
00510 #endif
00511                 map_->read(map_data);
00512 
00513                 map_surf = image::getMinimap(minimap_size, minimap_size, *map_);
00514                 if(map_surf != NULL) {
00515                     map_cache_.insert(std::pair<std::string,surface>(map_data,surface(map_surf)));
00516                 }
00517             } catch(gamemap::incorrect_format_exception& e) {
00518                 ERR_CF << "map could not be loaded: " << e.msg_ << '\n';
00519             } catch(twml_exception& e) {
00520                 ERR_CF << "map could not be loaded: " << e.dev_message << '\n';
00521             }
00522         }
00523     }
00524 
00525     if(map_surf != NULL) {
00526         SDL_Rect map_rect = {area.x + area.w - map_surf->w,area.y,map_surf->w,map_surf->h};
00527         ypos = maximum<int>(ypos,map_rect.y + map_rect.h + save_preview_border);
00528         SDL_BlitSurface(map_surf,NULL,screen,&map_rect);
00529     }
00530 
00531     char* old_locale = std::setlocale(LC_TIME, get_locale().localename.c_str());
00532     char time_buf[256] = {0};
00533     const save_info& save = (*info_)[index_];
00534     tm* tm_l = localtime(&save.time_modified);
00535     if (tm_l) {
00536         const size_t res = strftime(time_buf,sizeof(time_buf),_("%a %b %d %H:%M %Y"),tm_l);
00537         if(res == 0) {
00538             time_buf[0] = 0;
00539         }
00540     } else {
00541         LOG_NG << "localtime() returned null for time " << save.time_modified << ", save " << save.name;
00542     }
00543 
00544     if(old_locale) {
00545         std::setlocale(LC_TIME, old_locale);
00546     }
00547 
00548     std::stringstream str;
00549 
00550     // Escape all special characters in filenames
00551     std::string name = (*info_)[index_].name;
00552     str << font::BOLD_TEXT << utils::escape(name) << "\n" << time_buf;
00553 
00554     const std::string& campaign_type = summary["campaign_type"];
00555     if(utils::string_bool(summary["corrupt"], false)) {
00556         str << "\n" << _("#(Invalid)");
00557     } else if (!campaign_type.empty()) {
00558         str << "\n";
00559 
00560         if(campaign_type == "scenario") {
00561             const std::string campaign_id = summary["campaign"];
00562             const config* campaign = campaign_id.empty() ? NULL : game_config_->find_child("campaign", "id", campaign_id);
00563             utils::string_map symbols;
00564             if (campaign != NULL) {
00565                 symbols["campaign_name"] = (*campaign)["name"];
00566             } else {
00567                 // Fallback to nontranslatable campaign id.
00568                 symbols["campaign_name"] = "(" + campaign_id + ")";
00569             }
00570             str << vgettext("Campaign: $campaign_name", symbols);
00571 
00572             // Display internal id for debug purposes if we didn't above
00573             if (game_config::debug && (campaign != NULL)) {
00574                 str << '\n' << "(" << campaign_id << ")";
00575             }
00576         } else if(campaign_type == "multiplayer") {
00577             str << _("Multiplayer");
00578         } else if(campaign_type == "tutorial") {
00579             str << _("Tutorial");
00580         } else {
00581             str << campaign_type;
00582         }
00583 
00584         str << "\n";
00585 
00586         if(utils::string_bool(summary["replay"], false) && !utils::string_bool(summary["snapshot"], true)) {
00587             str << _("replay");
00588         } else if (!summary["turn"].empty()) {
00589             str << _("Turn") << " " << summary["turn"];
00590         } else {
00591             str << _("Scenario Start");
00592         }
00593 
00594         str << "\n" << _("Difficulty: ") << string_table[summary["difficulty"]];
00595         if(!summary["version"].empty()) {
00596             str << "\n" << _("Version: ") << summary["version"];
00597         }
00598     }
00599 
00600     font::draw_text(&video(), area, font::SIZE_SMALL, font::NORMAL_COLOUR, str.str(), area.x, ypos, true);
00601 }
00602 
00603 std::string format_time_summary(time_t t)
00604 {
00605     time_t curtime = time(NULL);
00606     const struct tm* timeptr = localtime(&curtime);
00607     if(timeptr == NULL) {
00608         return "";
00609     }
00610 
00611     const struct tm current_time = *timeptr;
00612 
00613     timeptr = localtime(&t);
00614     if(timeptr == NULL) {
00615         return "";
00616     }
00617 
00618     const struct tm save_time = *timeptr;
00619 
00620     const char* format_string = _("%b %d %y");
00621 
00622     if(current_time.tm_year == save_time.tm_year) {
00623         const int days_apart = current_time.tm_yday - save_time.tm_yday;
00624         if(days_apart == 0) {
00625             // save is from today
00626             format_string = _("%H:%M");
00627         } else if(days_apart > 0 && days_apart <= current_time.tm_wday) {
00628             // save is from this week
00629             format_string = _("%A, %H:%M");
00630         } else {
00631             // save is from current year
00632             format_string = _("%b %d");
00633         }
00634     } else {
00635         // save is from a different year
00636         format_string = _("%b %d %y");
00637     }
00638 
00639     char buf[40];
00640     const size_t res = strftime(buf,sizeof(buf),format_string,&save_time);
00641     if(res == 0) {
00642         buf[0] = 0;
00643     }
00644 
00645     return buf;
00646 }
00647 
00648 } // end anon namespace
00649 
00650 std::string load_game_dialog(display& disp, const config& game_config, bool* show_replay, bool* cancel_orders)
00651 {
00652     std::vector<save_info> games;
00653     {
00654         cursor::setter cur(cursor::WAIT);
00655         games = get_saves_list();
00656     }
00657 
00658     if(games.empty()) {
00659         gui::message_dialog(disp,
00660                          _("No Saved Games"),
00661                  _("There are no saved games to load.\n\n(Games are saved automatically when you complete a scenario)")).show();
00662         return "";
00663     }
00664 
00665     std::vector<config*> summaries;
00666     std::vector<save_info>::const_iterator i;
00667     for(i = games.begin(); i != games.end(); ++i) {
00668         config& cfg = save_summary(i->name);
00669         summaries.push_back(&cfg);
00670     }
00671 
00672     const events::event_context context;
00673 
00674     std::vector<std::string> items;
00675     std::ostringstream heading;
00676     heading << HEADING_PREFIX << _("Name") << COLUMN_SEPARATOR << _("Date");
00677     items.push_back(heading.str());
00678 
00679     for(i = games.begin(); i != games.end(); ++i) {
00680         std::string name = i->name;
00681         utils::truncate_as_wstring(name, minimum<size_t>(name.size(), 40));
00682 
00683         std::ostringstream str;
00684         str << name << COLUMN_SEPARATOR << format_time_summary(i->time_modified);
00685 
00686         items.push_back(str.str());
00687     }
00688 
00689     gui::menu::basic_sorter sorter;
00690     sorter.set_alpha_sort(0).set_id_sort(1);
00691 
00692     gamemap map_obj(game_config, "");
00693 
00694 
00695     gui::dialog lmenu(disp,
00696               _("Load Game"),
00697               _("Choose the game to load"), gui::NULL_DIALOG);
00698     lmenu.set_basic_behavior(gui::OK_CANCEL);
00699     load_game_filter_textbox* filter = new load_game_filter_textbox(disp.video(), items, lmenu);
00700     save_preview_pane save_preview(disp.video(),game_config,&map_obj,games,summaries,*filter);
00701     lmenu.set_textbox(filter);
00702     lmenu.add_pane(&save_preview);
00703     // create an option for whether the replay should be shown or not
00704     if(show_replay != NULL) {
00705         #ifdef USE_SMALL_GUI
00706             lmenu.add_option(_("Show replay"), false, gui::dialog::BUTTON_STANDARD);
00707         #else
00708             lmenu.add_option(_("Show replay"), false);
00709         #endif
00710     }
00711     if(cancel_orders != NULL) {
00712         #ifdef USE_SMALL_GUI
00713             lmenu.add_option(_("Cancel orders"), false, gui::dialog::BUTTON_STANDARD);
00714         #else
00715             lmenu.add_option(_("Cancel orders"), false);
00716         #endif
00717     }
00718     lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("OK"),0,false), gui::dialog::BUTTON_STANDARD);
00719     lmenu.add_button(new gui::standard_dialog_button(disp.video(),_("Cancel"),1,true), gui::dialog::BUTTON_STANDARD);
00720 
00721     delete_save save_deleter(disp,*filter,games,summaries);
00722     gui::dialog_button_info delete_button(&save_deleter,_("Delete Save"));
00723     #ifdef USE_SMALL_GUI
00724         //placing the buttons in one line so that none is coverd by any of the others
00725         lmenu.add_button(delete_button,gui::dialog::BUTTON_HELP);
00726     #else
00727         lmenu.add_button(delete_button);
00728     #endif
00729 
00730     int res = lmenu.show();
00731 
00732     write_save_index();
00733 
00734     if(res == -1)
00735         return "";
00736 
00737     res = filter->get_save_index(res);
00738     int option_index = 0;
00739     if(show_replay != NULL) {
00740       *show_replay = lmenu.option_checked(option_index++);
00741 
00742         const config& summary = *summaries[res];
00743         if(utils::string_bool(summary["replay"], false) && !utils::string_bool(summary["snapshot"], true)) {
00744             *show_replay = true;
00745         }
00746     }
00747     if (cancel_orders != NULL) {
00748         *cancel_orders = lmenu.option_checked(option_index++);
00749     }
00750 
00751     return games[res].name;
00752 }
00753 
00754 namespace {
00755     static const int unit_preview_border = 10;
00756 }
00757 
00758 //! Show unit-stats in a side-pane to unit-list, recall-list, etc.
00759 
00760 unit_preview_pane::unit_preview_pane(game_display& disp, const gamemap* map, TYPE type, bool on_left_side)
00761                     : gui::preview_pane(disp.video()), disp_(disp), map_(map), index_(0),
00762                       details_button_(disp.video(),_("Profile"),gui::button::TYPE_PRESS,"lite_small",gui::button::MINIMUM_SPACE),
00763                       left_(on_left_side), weapons_(type == SHOW_ALL)
00764 {
00765     unsigned w = font::relative_size(weapons_ ? 200 : 190);
00766     unsigned h = font::relative_size(weapons_ ? 370 : 140);
00767     set_measurements(w, h);
00768 }
00769 
00770 
00771 handler_vector unit_preview_pane::handler_members()
00772 {
00773     handler_vector h;
00774     h.push_back(&details_button_);
00775     return h;
00776 }
00777 
00778 bool unit_preview_pane::show_above() const
00779 {
00780     return !weapons_;
00781 }
00782 
00783 bool unit_preview_pane::left_side() const
00784 {
00785     return left_;
00786 }
00787 
00788 void unit_preview_pane::set_selection(int index)
00789 {
00790     index = minimum<int>(int(size()-1),index);
00791     if(index != index_ && index >= 0) {
00792         index_ = index;
00793         set_dirty();
00794         if(map_ != NULL) {
00795             details_button_.set_dirty();
00796         }
00797     }
00798 }
00799 
00800 void unit_preview_pane::draw_contents()
00801 {
00802     if(index_ < 0 || index_ >= int(size())) {
00803         return;
00804     }
00805 
00806     const details det = get_details();
00807 
00808     const bool right_align = left_side();
00809 
00810     surface const screen = video().getSurface();
00811 
00812     SDL_Rect const &loc = location();
00813     const SDL_Rect area = { loc.x + unit_preview_border, loc.y + unit_preview_border,
00814                             loc.w - unit_preview_border * 2, loc.h - unit_preview_border * 2 };
00815     SDL_Rect clip_area = area;
00816     const clip_rect_setter clipper(screen,clip_area);
00817 
00818     surface unit_image = det.image;
00819     if (!left_)
00820         unit_image = image::reverse_image(unit_image);
00821 
00822     SDL_Rect image_rect = {area.x,area.y,0,0};
00823 
00824     if(unit_image != NULL) {
00825         SDL_Rect rect = {right_align ? area.x : area.x + area.w - unit_image->w,area.y,unit_image->w,unit_image->h};
00826         SDL_BlitSurface(unit_image,NULL,screen,&rect);
00827         image_rect = rect;
00828     }
00829 
00830     // Place the 'unit profile' button
00831     if(map_ != NULL) {
00832         const SDL_Rect button_loc = {right_align ? area.x : area.x + area.w - details_button_.location().w,
00833                                      image_rect.y + image_rect.h,
00834                                      details_button_.location().w,details_button_.location().h};
00835         details_button_.set_location(button_loc);
00836     }
00837 
00838     SDL_Rect description_rect = {image_rect.x,image_rect.y+image_rect.h+details_button_.location().h,0,0};
00839 
00840     if(det.name.empty() == false) {
00841         std::stringstream desc;
00842         desc << font::NORMAL_TEXT << det.name;
00843         const std::string description = desc.str();
00844         description_rect = font::text_area(description, font::SIZE_NORMAL);
00845         description_rect = font::draw_text(&video(), area,
00846                             font::SIZE_NORMAL, font::NORMAL_COLOUR,
00847                             desc.str(), right_align ?  image_rect.x :
00848                             image_rect.x + image_rect.w - description_rect.w,
00849                             image_rect.y + image_rect.h + details_button_.location().h);
00850     }
00851 
00852     std::stringstream text;
00853     text << det.type_name << "\n"
00854         << font::BOLD_TEXT << _("level") << " " << det.level << "\n"
00855         << det.alignment << "\n"
00856         << det.traits << "\n";
00857 
00858     for(std::vector<std::string>::const_iterator a = det.abilities.begin(); a != det.abilities.end(); a++) {
00859         if(a != det.abilities.begin()) {
00860             text << ", ";
00861         }
00862         text << gettext(a->c_str());
00863     }
00864     text << "\n";
00865 
00866     // Use same coloring as in generate_report.cpp:
00867     text << det.hp_color << _("HP: ")
00868         << det.hitpoints << "/" << det.max_hitpoints << "\n";
00869 
00870     text << det.xp_color << _("XP: ")
00871         << det.experience << "/" << det.max_experience << "\n";
00872 
00873     if(weapons_) {
00874         text << _("Moves: ")
00875             << det.movement_left << "/" << det.total_movement << "\n";
00876 
00877         for(std::vector<attack_type>::const_iterator at_it = det.attacks.begin();
00878             at_it != det.attacks.end(); ++at_it) {
00879             // specials_context seems not needed here
00880             //at_it->set_specials_context(gamemap::location(),u);
00881 
00882             // see generate_report() in generate_report.cpp
00883             text << "<245,230,193>" << at_it->name()
00884                 << " (" << gettext(at_it->type().c_str()) << ")\n";
00885 
00886             std::string special = at_it->weapon_specials(true);
00887             if (!special.empty()) {
00888                 text << "<166,146,117>  " << special << "\n";
00889             }
00890             std::string accuracy = at_it->accuracy_parry_description();
00891             if(accuracy.empty() == false) {
00892                 accuracy += " ";
00893             }
00894 
00895             text << "<166,146,117>  " << at_it->damage() << "-" << at_it->num_attacks()
00896                 << " " << accuracy << "-- " << _(at_it->range().c_str()) << "\n";
00897         }
00898     }
00899 
00900     // we don't remove empty lines, so all fields stay at the same place
00901     const std::vector<std::string> lines = utils::split(text.str(), '\n',
00902         utils::STRIP_SPACES & !utils::REMOVE_EMPTY);
00903 
00904     SDL_Rect cur_area = area;
00905 
00906     if(weapons_) {
00907         cur_area.y += image_rect.h + description_rect.h + details_button_.location().h;
00908     }
00909 
00910     for(std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line) {
00911         int xpos = cur_area.x;
00912         if(right_align && !weapons_) {
00913             const SDL_Rect& line_area = font::text_area(*line,font::SIZE_SMALL);
00914             xpos = cur_area.x + cur_area.w - line_area.w;
00915         }
00916 
00917         cur_area = font::draw_text(&video(),location(),font::SIZE_SMALL,font::NORMAL_COLOUR,*line,xpos,cur_area.y);
00918         cur_area.y += cur_area.h;
00919     }
00920 }
00921 
00922 units_list_preview_pane::units_list_preview_pane(game_display& disp, const gamemap* map, const unit& u, TYPE type, bool on_left_side)
00923                     : unit_preview_pane(disp, map, type, on_left_side),
00924                       units_(&unit_store_)
00925 {
00926     unit_store_.push_back(u);
00927 }
00928 
00929 units_list_preview_pane::units_list_preview_pane(game_display& disp, const gamemap* map, std::vector<unit>& units, TYPE type, bool on_left_side)
00930                     : unit_preview_pane(disp, map, type, on_left_side),
00931                       units_(&units)
00932 {}
00933 
00934 size_t units_list_preview_pane::size() const
00935 {
00936     return (units_!=NULL) ? units_->size() : 0;
00937 }
00938 
00939 //unit_preview_pane::
00940 const unit_preview_pane::details units_list_preview_pane::get_details() const
00941 {
00942     unit& u = (*units_)[index_];
00943     details det;
00944 
00945     det.image = u.still_image();
00946 
00947     det.name = u.name();
00948     det.type_name = u.type_name();
00949     det.level = u.level();
00950     det.alignment = unit_type::alignment_description(u.alignment());
00951     det.traits = u.traits_description();
00952 
00953     //we filter to remove the tooltips (increment by 2)
00954     const std::vector<std::string>& abilities = u.unit_ability_tooltips();
00955     for(std::vector<std::string>::const_iterator a = abilities.begin();
00956          a != abilities.end(); a+=2) {
00957         det.abilities.push_back(*a);
00958     }
00959 
00960     det.hitpoints = u.hitpoints();
00961     det.max_hitpoints = u.max_hitpoints();
00962     det.hp_color = font::color2markup(u.hp_color());
00963 
00964     det.experience = u.experience();
00965     det.max_experience = u.max_experience();
00966     det.xp_color = font::color2markup(u.xp_color());
00967 
00968     det.movement_left = u.movement_left();
00969     det.total_movement= u.total_movement();
00970 
00971     det.attacks = u.attacks();
00972     return det;
00973 }
00974 
00975 void units_list_preview_pane::process_event()
00976 {
00977     if(map_ != NULL && details_button_.pressed() && index_ >= 0 && index_ < int(size())) {
00978         show_unit_description(disp_, (*units_)[index_]);
00979     }
00980 }
00981 
00982 unit_types_preview_pane::unit_types_preview_pane(game_display& disp, const gamemap* map, std::vector<const unit_type*>& unit_types, int side, TYPE type, bool on_left_side)
00983                     : unit_preview_pane(disp, map, type, on_left_side),
00984                       unit_types_(&unit_types), side_(side)
00985 {}
00986 
00987 size_t unit_types_preview_pane::size() const
00988 {
00989     return (unit_types_!=NULL) ? unit_types_->size() : 0;
00990 }
00991 
00992 const unit_types_preview_pane::details unit_types_preview_pane::get_details() const
00993 {
00994     const unit_type* t = (*unit_types_)[index_];
00995     details det;
00996 
00997     if (t==NULL)
00998         return det;
00999 
01000     //FIXME: There should be a better way to deal with this
01001     unit_type_data::types().find(t->id(), unit_type::WITHOUT_ANIMATIONS);
01002 
01003     std::string mod = "~RC(" + t->flag_rgb() + ">" + team::get_side_colour_index(side_) + ")";
01004     det.image = image::get_image(t->image()+mod);
01005 
01006     det.name = "";
01007     det.type_name = t->type_name();
01008     det.level = t->level();
01009     det.alignment = unit_type::alignment_description(t->alignment());
01010 
01011     //FIXME: This probably must be move into a unit_type function
01012     const std::vector<config*> traits = t->possible_traits();
01013     for(std::vector<config*>::const_iterator i = traits.begin(); i != traits.end(); i++) {
01014         if((**(i))["availability"] == "musthave") {
01015             std::string gender_string = (!t->genders().empty() && t->genders().front()== unit_race::FEMALE) ? "female_name" : "male_name";
01016             t_string name = (**i)[gender_string];
01017             if (name.empty()) {
01018                 name = (**i)["name"];
01019             }
01020             if (!name.empty()) {
01021                 if (i != traits.begin()) {
01022                     det.traits += ", ";
01023                 }
01024                 det.traits += name;
01025             }
01026         }
01027     }
01028 
01029     det.abilities = t->abilities();
01030 
01031     det.hitpoints = t->hitpoints();
01032     det.max_hitpoints = t->hitpoints();
01033     det.hp_color = "<33,225,0>"; // from unit::hp_color()
01034 
01035     det.experience = 0;
01036     det.max_experience = t->experience_needed();
01037     det.xp_color = "<0,160,225>"; // from unit::xp_color()
01038 
01039     // Check if AMLA color is needed
01040     // FIXME: not sure if it's fully accurate (but not very important for unit_type)
01041     // xp_color also need a simpler function for doing this
01042     const config::child_list& advances = t->modification_advancements();
01043     for(config::child_list::const_iterator j = advances.begin(); j != advances.end(); ++j) {
01044         if (!utils::string_bool((**j)["strict_amla"]) || !t->can_advance()) {
01045             det.xp_color = "<100,0,150>"; // from unit::xp_color()
01046             break;
01047         }
01048     }
01049 
01050     det.movement_left = 0;
01051     det.total_movement= t->movement();
01052 
01053     det.attacks = t->attacks();
01054     return det;
01055 }
01056 
01057 void unit_types_preview_pane::process_event()
01058 {
01059     if(map_ != NULL && details_button_.pressed() && index_ >= 0 && index_ < int(size())) {
01060         const unit_type* type = (*unit_types_)[index_];
01061         if (type != NULL)
01062             show_unit_description(disp_, *type);
01063     }
01064 }
01065 
01066 
01067 void show_unit_description(game_display &disp, const unit& u)
01068 {
01069     const unit_type* t = u.type();
01070     if (t != NULL)
01071         show_unit_description(disp, *t);
01072     else
01073         // can't find type, try open the id page to have feedback and unit error page
01074       help::show_unit_help(disp, u.type_id());
01075 }
01076 
01077 void show_unit_description(game_display &disp, const unit_type& t)
01078 {
01079     help::show_unit_help(disp, t.id(), t.hide_help());
01080 }
01081 
01082 
01083 namespace {
01084     static const int campaign_preview_border = font::relative_size(10);
01085 }
01086 
01087 campaign_preview_pane::campaign_preview_pane(CVideo &video,std::vector<std::pair<std::string,std::string> >* desc) : gui::preview_pane(video),descriptions_(desc),index_(0)
01088 {
01089 // size of the campaign info window with the campaign description and image in pixel
01090 #ifdef USE_TINY_GUI
01091     set_measurements(160, 200);
01092 #else
01093     set_measurements(430, 440);
01094 #endif
01095 }
01096 
01097 bool campaign_preview_pane::show_above() const { return false; }
01098 bool campaign_preview_pane::left_side() const { return false; }
01099 
01100 void campaign_preview_pane::set_selection(int index)
01101 {
01102     index = minimum<int>(descriptions_->size()-1,index);
01103     if(index != index_ && index >= 0) {
01104         index_ = index;
01105         set_dirty();
01106     }
01107 }
01108 
01109 void campaign_preview_pane::draw_contents()
01110 {
01111     if (size_t(index_) >= descriptions_->size()) {
01112         return;
01113     }
01114 
01115     const SDL_Rect area = {
01116         location().x+campaign_preview_border,
01117         location().y,
01118         location().w-campaign_preview_border*2,
01119         location().h };
01120 
01121     /* background frame */
01122     gui::dialog_frame f(video(), "", gui::dialog_frame::preview_style, false);
01123     f.layout(area);
01124     f.draw_background();
01125     f.draw_border();
01126 
01127     /* description text */
01128     std::string desc_text;
01129     try {
01130         desc_text = font::word_wrap_text((*descriptions_)[index_].first,
01131             font::SIZE_SMALL, area.w - 2 * campaign_preview_border);
01132     } catch (utils::invalid_utf8_exception&) {
01133         LOG_STREAM(err, engine) << "Invalid utf-8 found, campaign description is ignored.\n";
01134     }
01135     const std::vector<std::string> lines = utils::split(desc_text, '\n',utils::STRIP_SPACES);
01136     SDL_Rect txt_area = { area.x+campaign_preview_border,area.y+campaign_preview_border,0,0 };
01137 
01138     for(std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line) {
01139       txt_area = font::draw_text(&video(),location(),font::SIZE_SMALL,font::NORMAL_COLOUR,*line,txt_area.x,txt_area.y);
01140         txt_area.y += txt_area.h;
01141     }
01142 
01143     /* description image */
01144     surface img(NULL);
01145     const std::string desc_img_name = (*descriptions_)[index_].second;
01146     if(!desc_img_name.empty()) {
01147         img.assign(image::get_image(desc_img_name));
01148     }
01149     if (!img.null()) {
01150         SDL_Rect src_rect,dst_rect;
01151         int max_height = area.h-(txt_area.h+txt_area.y-area.y);
01152 
01153         src_rect.x = src_rect.y = 0;
01154         src_rect.w = minimum<int>(area.w,img->w);
01155         src_rect.h = minimum<int>(max_height,img->h);
01156         dst_rect.x = area.x+(area.w-src_rect.w)/2;
01157         dst_rect.y = txt_area.y+((max_height-src_rect.h)*8)/13;
01158         if(dst_rect.y - txt_area.h - txt_area.y >= 120) {
01159             //for really tall dialogs, just put it under the text
01160             dst_rect.y = txt_area.y + font::get_max_height(font::SIZE_SMALL)*5;
01161         }
01162 
01163         SDL_BlitSurface(img,&src_rect,video().getSurface(),&dst_rect);
01164 
01165     }
01166 }
01167 
01168 static network::connection network_data_dialog(display& disp, const std::string& msg, config& cfg, network::connection connection_num, network::statistics (*get_stats)(network::connection handle))
01169 {
01170 #ifdef USE_TINY_GUI
01171     const size_t width = 200;
01172     const size_t height = 40;
01173     const size_t border = 10;
01174 #else
01175     const size_t width = 300;
01176     const size_t height = 80;
01177     const size_t border = 20;
01178 #endif
01179     const int left = disp.w()/2 - width/2;
01180     const int top  = disp.h()/2 - height/2;
01181 
01182     const events::event_context dialog_events_context;
01183 
01184     gui::button cancel_button(disp.video(),_("Cancel"));
01185     std::vector<gui::button*> buttons_ptr(1,&cancel_button);
01186 
01187     gui::dialog_frame frame(disp.video(), msg, gui::dialog_frame::default_style, false, &buttons_ptr);
01188     frame.layout(left,top,width,height);
01189     frame.draw();
01190 
01191     const SDL_Rect progress_rect = {left+border,top+border,width-border*2,height-border*2};
01192     gui::progress_bar progress(disp.video());
01193     progress.set_location(progress_rect);
01194 
01195     events::raise_draw_event();
01196     disp.flip();
01197 
01198     network::statistics old_stats = get_stats(connection_num);
01199 
01200     cfg.clear();
01201     for(;;) {
01202         const network::connection res = network::receive_data(cfg,connection_num,100);
01203         const network::statistics stats = get_stats(connection_num);
01204         if(stats.current_max != 0 && stats != old_stats) {
01205             old_stats = stats;
01206             progress.set_progress_percent((stats.current*100)/stats.current_max);
01207             std::ostringstream stream;
01208             stream << stats.current/1024 << "/" << stats.current_max/1024 << _("KB");
01209             progress.set_text(stream.str());
01210         }
01211 
01212         events::raise_draw_event();
01213         disp.flip();
01214         events::pump();
01215 
01216         if(res != 0) {
01217             return res;
01218         }
01219 
01220 
01221         if(cancel_button.pressed()) {
01222             return res;
01223         }
01224     }
01225 }
01226 
01227 network::connection network_send_dialog(display& disp, const std::string& msg, config& cfg, network::connection connection_num)
01228 {
01229     return network_data_dialog(disp, msg, cfg, connection_num,
01230                                network::get_send_stats);
01231 }
01232 
01233 network::connection network_receive_dialog(display& disp, const std::string& msg, config& cfg, network::connection connection_num)
01234 {
01235     return network_data_dialog(disp, msg, cfg, connection_num,
01236                                network::get_receive_stats);
01237 }
01238 
01239 } // end namespace dialogs
01240 
01241 namespace {
01242 
01243 class connect_waiter : public threading::waiter
01244 {
01245 public:
01246     connect_waiter(display& disp, gui::button& button) : disp_(disp), button_(button)
01247     {}
01248     ACTION process();
01249 
01250 private:
01251     display& disp_;
01252     gui::button& button_;
01253 };
01254 
01255 connect_waiter::ACTION connect_waiter::process()
01256 {
01257     events::raise_draw_event();
01258     disp_.flip();
01259     events::pump();
01260     if(button_.pressed()) {
01261         return ABORT;
01262     } else {
01263         return WAIT;
01264     }
01265 }
01266 
01267 }
01268 
01269 namespace dialogs
01270 {
01271 
01272 network::connection network_connect_dialog(display& disp, const std::string& msg, const std::string& hostname, int port)
01273 {
01274 #ifdef USE_TINY_GUI
01275     const size_t width = 200;
01276     const size_t height = 20;
01277 #else
01278     const size_t width = 250;
01279     const size_t height = 20;
01280 #endif
01281     const int left = disp.w()/2 - width/2;
01282     const int top  = disp.h()/2 - height/2;
01283 
01284     const events::event_context dialog_events_context;
01285 
01286     gui::button cancel_button(disp.video(),_("Cancel"));
01287     std::vector<gui::button*> buttons_ptr(1,&cancel_button);
01288 
01289     gui::dialog_frame frame(disp.video(), msg, gui::dialog_frame::default_style, false, &buttons_ptr);
01290     frame.layout(left,top,width,height);
01291     frame.draw();
01292 
01293     events::raise_draw_event();
01294     disp.flip();
01295 
01296     connect_waiter waiter(disp,cancel_button);
01297     return network::connect(hostname,port,waiter);
01298 }
01299 
01300 } // end namespace dialogs

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs