gui/dialogs/lobby_main.cpp

Go to the documentation of this file.
00001 /* $Id: lobby_main.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2009 - 2012 by Tomasz Sniatowski <kailoran@gmail.com>
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 #define GETTEXT_DOMAIN "wesnoth-lib"
00016 
00017 #include "gui/dialogs/lobby_main.hpp"
00018 #include "gui/dialogs/lobby_player_info.hpp"
00019 #include "gui/dialogs/field.hpp"
00020 #include "gui/dialogs/helper.hpp"
00021 
00022 #include "gui/auxiliary/log.hpp"
00023 #include "gui/auxiliary/timer.hpp"
00024 #include "gui/widgets/button.hpp"
00025 #include "gui/widgets/image.hpp"
00026 #include "gui/widgets/label.hpp"
00027 #ifdef GUI2_EXPERIMENTAL_LISTBOX
00028 #include "gui/widgets/list.hpp"
00029 #else
00030 #include "gui/widgets/listbox.hpp"
00031 #endif
00032 #include "gui/widgets/minimap.hpp"
00033 #include "gui/widgets/multi_page.hpp"
00034 #include "gui/widgets/scroll_label.hpp"
00035 #include "gui/widgets/settings.hpp"
00036 #include "gui/widgets/text_box.hpp"
00037 #include "gui/widgets/toggle_button.hpp"
00038 #include "gui/widgets/toggle_panel.hpp"
00039 #include "gui/widgets/tree_view_node.hpp"
00040 
00041 #include "foreach.hpp"
00042 #include "formula_string_utils.hpp"
00043 #include "game_preferences.hpp"
00044 #include "gettext.hpp"
00045 #include "lobby_preferences.hpp"
00046 #include "log.hpp"
00047 #include "network.hpp"
00048 #include "playmp_controller.hpp"
00049 #include "preferences_display.hpp"
00050 #include "sound.hpp"
00051 
00052 #include <boost/bind.hpp>
00053 
00054 static lg::log_domain log_network("network");
00055 #define DBG_NW LOG_STREAM(debug, log_network)
00056 #define LOG_NW LOG_STREAM(info, log_network)
00057 #define ERR_NW LOG_STREAM(err, log_network)
00058 
00059 static lg::log_domain log_engine("engine");
00060 #define LOG_NG LOG_STREAM(info, log_engine)
00061 #define ERR_NG LOG_STREAM(err, log_engine)
00062 
00063 static lg::log_domain log_config("config");
00064 #define ERR_CF LOG_STREAM(err, log_config)
00065 
00066 static lg::log_domain log_lobby("lobby");
00067 #define DBG_LB LOG_STREAM(debug, log_lobby)
00068 #define LOG_LB LOG_STREAM(info, log_lobby)
00069 #define ERR_LB LOG_STREAM(err, log_lobby)
00070 #define SCOPE_LB log_scope2(log_lobby, __func__)
00071 
00072 
00073 namespace gui2 {
00074 
00075 REGISTER_DIALOG(lobby_main)
00076 
00077 void tsub_player_list::init(gui2::twindow &w, const std::string &id)
00078 {
00079     list = find_widget<tlistbox>(&w, id, false, true);
00080     show_toggle = find_widget<ttoggle_button>(&w, id + "_show_toggle"
00081             , false, true);
00082     show_toggle->set_icon_name("lobby/group-expanded.png");
00083     show_toggle->set_callback_state_change(
00084         boost::bind(&tsub_player_list::show_toggle_callback, this, _1));
00085     count = find_widget<tlabel>(&w, id + "_count", false, true);
00086     label = find_widget<tlabel>(&w, id + "_label", false, true);
00087 
00088     ttree_view& parent_tree = find_widget<ttree_view>(&w
00089             , "player_tree"
00090             , false);
00091 
00092     string_map tree_group_field;
00093     std::map<std::string, string_map> tree_group_item;
00094 
00095     tree_group_field["label"] = id;
00096     tree_group_item["tree_view_node_label"] = tree_group_field;
00097     tree = &parent_tree.add_node("player_group", tree_group_item);
00098 
00099     tree_label = find_widget<tlabel>(tree
00100                 , "tree_view_node_label"
00101                 , false
00102                 , true);
00103 
00104     tree_label->set_label(label->label());
00105 
00106 }
00107 
00108 void tsub_player_list::show_toggle_callback(gui2::twidget* /*widget*/)
00109 {
00110     if (show_toggle->get_value()) {
00111         list->set_visible(twidget::INVISIBLE);
00112         show_toggle->set_icon_name("lobby/group-folded.png");
00113     } else {
00114         list->set_visible(twidget::VISIBLE);
00115         show_toggle->set_icon_name("lobby/group-expanded.png");
00116     }
00117 }
00118 
00119 void tsub_player_list::auto_hide()
00120 {
00121     assert(tree);
00122     assert(tree_label);
00123     if(tree->empty()) {
00124         /**
00125          * @todo Make sure setting visible resizes the widget.
00126          *
00127          * It doesn't work here since invalidate_layout is blocked, but the
00128          * widget should also be able to handle it itself. Once done the
00129          * setting of the label text can also be removed.
00130          */
00131         assert(label);
00132         tree_label->set_label(label->label() + " (0)");
00133 //          tree_label->set_visible(twidget::INVISIBLE);
00134     } else {
00135         assert(label);
00136         std::stringstream ss;
00137         ss << label->label() << " (" << tree->size() << ")";
00138         tree_label->set_label(ss.str());
00139 //          tree_label->set_visible(twidget::VISIBLE);
00140         }
00141 }
00142 
00143 void tplayer_list::init(gui2::twindow &w)
00144 {
00145     active_game.init(w, "active_game");
00146     active_room.init(w, "active_room");
00147     other_rooms.init(w, "other_rooms");
00148     other_games.init(w, "other_games");
00149     sort_by_name = find_widget<ttoggle_button>(&w
00150             , "player_list_sort_name", false, true);
00151     sort_by_relation = find_widget<ttoggle_button>(&w
00152             , "player_list_sort_relation", false, true);
00153 
00154     tree = find_widget<ttree_view>(&w
00155             , "player_tree"
00156             , false
00157             , true);
00158 
00159     find_widget<twidget>(&w, "old_player_list", false)
00160             .set_visible(twidget::INVISIBLE);
00161 
00162     /**
00163      * @todo This is a hack to fold the items.
00164      *
00165      * This hack can be removed when folding is properly implemented.
00166      */
00167     assert(active_room.tree);
00168     find_widget<ttoggle_button>(active_room.tree
00169                 , "tree_view_node_icon"
00170                 , false).set_value(true);
00171     assert(other_rooms.tree);
00172     find_widget<ttoggle_button>(other_rooms.tree
00173                 , "tree_view_node_icon"
00174                 , false).set_value(true);
00175     assert(other_games.tree);
00176     find_widget<ttoggle_button>(other_games.tree
00177                 , "tree_view_node_icon"
00178                 , false).set_value(true);
00179 }
00180 
00181 void tplayer_list::update_sort_icons()
00182 {
00183     if (sort_by_name->get_value()) {
00184         sort_by_name->set_icon_name("lobby/sort-az.png");
00185     } else {
00186         sort_by_name->set_icon_name("lobby/sort-az-off.png");
00187     }
00188     if (sort_by_relation->get_value()) {
00189         sort_by_relation->set_icon_name("lobby/sort-friend.png");
00190     } else {
00191         sort_by_relation->set_icon_name("lobby/sort-friend-off.png");
00192     }
00193 }
00194 void tlobby_main::send_chat_message(const std::string& message, bool /*allies_only*/)
00195 {
00196     config data, msg;
00197     msg["message"] = message;
00198     msg["sender"] = preferences::login();
00199     data.add_child("message", msg);
00200 
00201     add_chat_message(time(NULL), preferences::login(), 0, message); //local echo
00202     network::send_data(data, 0);
00203 }
00204 
00205 void tlobby_main::user_relation_changed(const std::string& /*name*/)
00206 {
00207     player_list_dirty_ = true;
00208 }
00209 
00210 void tlobby_main::add_chat_message(const time_t& /*time*/, const std::string& speaker,
00211     int /*side*/, const std::string& message, events::chat_handler::MESSAGE_TYPE /*type*/)
00212 {
00213     std::stringstream ss;
00214     ss << "<" << speaker << "> ";
00215     ss << message;
00216     append_to_chatbox(ss.str());
00217 }
00218 
00219 
00220 void tlobby_main::add_whisper_sent(const std::string& receiver, const std::string& message)
00221 {
00222     if (whisper_window_active(receiver)) {
00223         add_active_window_message(preferences::login(), message);
00224     } else if (tlobby_chat_window* t =  whisper_window_open(receiver, preferences::auto_open_whisper_windows())) {
00225         switch_to_window(t);
00226         add_active_window_message(preferences::login(), message);
00227     } else {
00228         utils::string_map symbols;
00229         symbols["receiver"] = receiver;
00230         add_active_window_whisper(VGETTEXT("whisper to $receiver", symbols), message);
00231     }
00232     lobby_info_.get_whisper_log(receiver).add_message(preferences::login(), message);
00233 }
00234 
00235 void tlobby_main::add_whisper_received(const std::string& sender, const std::string& message)
00236 {
00237     bool can_go_to_active = !preferences::whisper_friends_only() || preferences::is_friend(sender);
00238     bool can_open_new = preferences::auto_open_whisper_windows() && can_go_to_active;
00239     lobby_info_.get_whisper_log(sender).add_message(sender, message);
00240     if (whisper_window_open(sender, can_open_new)) {
00241         if (whisper_window_active(sender)) {
00242             add_active_window_message(sender, message);
00243             do_notify(NOTIFY_WHISPER);
00244         } else {
00245             add_whisper_window_whisper(sender, message);
00246             increment_waiting_whsipers(sender);
00247             do_notify(NOTIFY_WHISPER_OTHER_WINDOW);
00248         }
00249     } else if (can_go_to_active) {
00250         add_active_window_whisper(sender, message);
00251         do_notify(NOTIFY_WHISPER);
00252     } else {
00253         LOG_LB << "Ignoring whisper from " << sender << "\n";
00254     }
00255 }
00256 
00257 void tlobby_main::add_chat_room_message_sent(const std::string& room,
00258     const std::string& message)
00259 {
00260     //do not open room window here, player should be in the room before sending
00261     //messages yo it should be allowed to happen
00262     if (tlobby_chat_window* t = room_window_open(room, false)) {
00263         room_info* ri = lobby_info_.get_room(room);
00264         assert(ri);
00265         if (!room_window_active(room)) {
00266             switch_to_window(t);
00267         }
00268         ri->log().add_message(preferences::login(), message);
00269         add_active_window_message(preferences::login(), message);
00270     } else {
00271         LOG_LB << "Cannot add sent message to ui for room " << room
00272             << ", player not in the room\n";
00273     }
00274 }
00275 
00276 void tlobby_main::add_chat_room_message_received(const std::string& room,
00277     const std::string& speaker, const std::string& message)
00278 {
00279     if (room_info* ri = lobby_info_.get_room(room)) {
00280         t_notify_mode notify_mode = NOTIFY_NONE;
00281         ri->log().add_message(speaker, message);
00282         if (room_window_active(room)) {
00283             add_active_window_message(speaker, message);
00284             notify_mode = NOTIFY_MESSAGE;
00285         } else {
00286             add_room_window_message(room, speaker, message);
00287             increment_waiting_messages(room);
00288             notify_mode = NOTIFY_MESSAGE_OTHER_WINDOW;
00289         }
00290         if (speaker == "server") {
00291             notify_mode = NOTIFY_SERVER_MESSAGE;
00292         } else if (utils::word_match(message, preferences::login())) {
00293             notify_mode = NOTIFY_OWN_NICK;
00294         } else if (preferences::is_friend(speaker)) {
00295             notify_mode = NOTIFY_FRIEND_MESSAGE;
00296         }
00297         do_notify(notify_mode);
00298     } else {
00299         LOG_LB << "Discarding message to room " << room
00300             << " from " << speaker << " (room not open)\n";
00301     }
00302 }
00303 
00304 void tlobby_main::append_to_chatbox(const std::string& text)
00305 {
00306     append_to_chatbox(text, active_window_);
00307 }
00308 
00309 void tlobby_main::append_to_chatbox(const std::string& text, size_t id)
00310 {
00311     tgrid& grid = chat_log_container_->page_grid(id);
00312     tscroll_label& log = find_widget<tscroll_label>(&grid, "log_text", false);
00313     log.set_label(log.label() + "\n" + preferences::get_chat_timestamp(time(0)) + text);
00314     log.scroll_vertical_scrollbar(tscrollbar_::END);
00315 }
00316 
00317 void tlobby_main::do_notify(t_notify_mode mode)
00318 {
00319     if (preferences::lobby_sounds()) {
00320         switch (mode) {
00321             case NOTIFY_WHISPER:
00322             case NOTIFY_WHISPER_OTHER_WINDOW:
00323             case NOTIFY_OWN_NICK:
00324                 sound::play_UI_sound(game_config::sounds::receive_message_highlight);
00325                 break;
00326             case NOTIFY_FRIEND_MESSAGE:
00327                 sound::play_UI_sound(game_config::sounds::receive_message_friend);
00328                 break;
00329             case NOTIFY_SERVER_MESSAGE:
00330                 sound::play_UI_sound(game_config::sounds::receive_message_server);
00331                 break;
00332             case NOTIFY_LOBBY_QUIT:
00333                 sound::play_UI_sound(game_config::sounds::user_leave);
00334                 break;
00335             case NOTIFY_LOBBY_JOIN:
00336                 sound::play_UI_sound(game_config::sounds::user_arrive);
00337                 break;
00338             case NOTIFY_MESSAGE:
00339                 break;
00340             default:
00341                 break;
00342         }
00343     }
00344 }
00345 
00346 tlobby_main::tlobby_main(const config& game_config
00347         , lobby_info& info
00348         , display& disp)
00349     : legacy_result_(QUIT)
00350     , game_config_(game_config)
00351     , gamelistbox_(NULL)
00352     , userlistbox_(NULL)
00353     , roomlistbox_(NULL)
00354     , chat_log_container_(NULL)
00355     , chat_input_(NULL)
00356     , window_(NULL)
00357     , lobby_info_(info)
00358     , preferences_callback_(NULL)
00359     , open_windows_()
00360     , active_window_(0)
00361     , filter_friends_(NULL)
00362     , filter_ignored_(NULL)
00363     , filter_slots_(NULL)
00364     , filter_invert_(NULL)
00365     , filter_text_(NULL)
00366     , selected_game_id_()
00367     , player_list_()
00368     , player_list_dirty_(false)
00369     , gamelist_dirty_(false)
00370     , last_gamelist_update_(0)
00371     , gamelist_diff_update_(true)
00372     , disp_(disp)
00373     , lobby_update_timer_(0)
00374     , preferences_wrapper_()
00375     , gamelist_id_at_row_()
00376     , delay_playerlist_update_(false)
00377     , delay_gamelist_update_(false)
00378 {
00379 }
00380 
00381 struct lobby_delay_gamelist_update_guard
00382 {
00383     lobby_delay_gamelist_update_guard(tlobby_main& l) : l(l) {
00384         l.delay_gamelist_update_ = true;
00385     }
00386     ~lobby_delay_gamelist_update_guard() {
00387         l.delay_gamelist_update_ = false;
00388     }
00389     tlobby_main& l;
00390 };
00391 
00392 void tlobby_main::set_preferences_callback(boost::function<void ()> cb)
00393 {
00394     preferences_callback_ = cb;
00395 }
00396 
00397 tlobby_main::~tlobby_main()
00398 {
00399     if(lobby_update_timer_) {
00400         remove_timer(lobby_update_timer_);
00401     }
00402 }
00403 
00404 static void signal_handler_sdl_video_resize(
00405           const event::tevent event
00406         , bool& handled
00407         , bool& halt
00408         , const tpoint& new_size
00409         , CVideo& video)
00410 {
00411     DBG_GUI_E << __func__  << ": " << event << ".\n";
00412 
00413     if(new_size.x < preferences::min_allowed_width()
00414             || new_size.y < preferences::min_allowed_height()) {
00415 
00416         DBG_GUI_E << __func__ << ": resize aborted, too small.\n";
00417         handled = true;
00418         halt = true;
00419         return;
00420     }
00421 
00422     if(new_size.x == static_cast<int>(settings::screen_width)
00423             && new_size.y == static_cast<int>(settings::screen_height)) {
00424 
00425         DBG_GUI_E << __func__ << ": resize not needed.\n";
00426         handled = true;
00427         return;
00428     }
00429 
00430     if(!preferences::set_resolution(video , new_size.x, new_size.y)) {
00431 
00432         LOG_GUI_E << __func__
00433                 << ": resize aborted, resize failed.\n";
00434     }
00435 }
00436 
00437 static bool fullscreen(CVideo& video)
00438 {
00439     preferences::set_fullscreen(video , !preferences::fullscreen());
00440 
00441     // Setting to fullscreen doesn't seem to generate a resize event.
00442     const SDL_Rect& rect = screen_area();
00443 
00444     SDL_Event event;
00445     event.type = SDL_VIDEORESIZE;
00446     event.resize.type = SDL_VIDEORESIZE;
00447     event.resize.w = rect.w;
00448     event.resize.h = rect.h;
00449 
00450     SDL_PushEvent(&event);
00451 
00452     return true;
00453 }
00454 
00455 void tlobby_main::post_build(CVideo& video, twindow& window)
00456 {
00457     /** @todo Should become a global hotkey after 1.8, then remove it here. */
00458     window.register_hotkey(hotkey::HOTKEY_FULLSCREEN
00459             , boost::bind(fullscreen, boost::ref(video)));
00460 
00461 
00462     /** @todo Remove this code once the resizing in twindow is finished. */
00463     window.connect_signal<event::SDL_VIDEO_RESIZE>(
00464               boost::bind(&signal_handler_sdl_video_resize
00465                   , _2, _3, _4, _5, boost::ref(video))
00466             , event::tdispatcher::front_child);
00467 
00468     /*** Local hotkeys. ***/
00469     preferences_wrapper_ = boost::bind(
00470               &tlobby_main::show_preferences_button_callback
00471             , this
00472             , boost::ref(window));
00473 
00474     window.register_hotkey(
00475               hotkey::HOTKEY_PREFERENCES
00476             , boost::bind(function_wrapper<bool, boost::function<void()> >
00477                 , true
00478                 , boost::cref(preferences_wrapper_)));
00479 }
00480 
00481 namespace {
00482 
00483 void add_label_data(std::map<std::string, string_map>& map,
00484                     const std::string& key, const std::string& label)
00485 {
00486     string_map item;
00487     item["label"] = label;
00488     map.insert(std::make_pair(key, item));
00489 }
00490 
00491 void add_tooltip_data(std::map<std::string, string_map>& map,
00492                     const std::string& key, const std::string& label)
00493 {
00494     string_map item;
00495     item["tooltip"] = label;
00496     map.insert(std::make_pair(key, item));
00497 }
00498 
00499 void modify_grid_with_data(tgrid* grid, const std::map<std::string, string_map>& map)
00500 {
00501     typedef std::map<std::string, string_map> strstrmap;
00502     foreach (const strstrmap::value_type v, map) {
00503         const std::string& key = v.first;
00504         const string_map& strmap = v.second;
00505         twidget* w = grid->find(key, false);
00506         if (w == NULL) continue;
00507         tcontrol* c = dynamic_cast<tcontrol*>(w);
00508         if (c == NULL) continue;
00509         foreach (const string_map::value_type& vv, strmap) {
00510             if (vv.first == "label") {
00511                 c->set_label(vv.second);
00512             } else if (vv.first == "tooltip") {
00513                 c->set_tooltip(vv.second);
00514             }
00515         }
00516     }
00517 }
00518 
00519 void set_visible_if_exists(tgrid* grid, const char* id, bool visible)
00520 {
00521     twidget* w = grid->find(id, false);
00522     if (w) {
00523         w->set_visible(visible ? twidget::VISIBLE : twidget::INVISIBLE);
00524     }
00525 }
00526 
00527 std::string colorize(const std::string& str, const std::string& color)
00528 {
00529     return "<span color=\"" + color +"\">" + str + "</span>";
00530 }
00531 
00532 std::string tag(const std::string& str, const std::string& tag)
00533 {
00534     return "<" + tag + ">" + str + "</" + tag + ">";
00535 }
00536 
00537 } //end anonymous namespace
00538 
00539 void tlobby_main::update_gamelist()
00540 {
00541     SCOPE_LB;
00542     gamelistbox_->clear();
00543     gamelist_id_at_row_.clear();
00544     lobby_info_.make_games_vector();
00545     int select_row = -1;
00546     for (unsigned i = 0; i < lobby_info_.games().size(); ++i) {
00547         const game_info& game = *lobby_info_.games()[i];
00548         if (game.id == selected_game_id_) {
00549             select_row = i;
00550         }
00551         gamelist_id_at_row_.push_back(game.id);
00552         LOG_LB << "Adding game to listbox (1)" << game.id << "\n";
00553         gamelistbox_->add_row(make_game_row_data(game));
00554         tgrid* grid = gamelistbox_->get_row_grid(gamelistbox_->get_item_count() - 1);
00555         adjust_game_row_contents(game, gamelistbox_->get_item_count() - 1, grid);
00556     }
00557     if (select_row >= 0 && select_row != gamelistbox_->get_selected_row()) {
00558         gamelistbox_->select_row(select_row);
00559     }
00560     update_selected_game();
00561     gamelist_dirty_ = false;
00562     last_gamelist_update_ = SDL_GetTicks();
00563     lobby_info_.sync_games_display_status();
00564     lobby_info_.apply_game_filter();
00565     update_gamelist_header();
00566     gamelistbox_->set_row_shown(lobby_info_.games_visibility());
00567 }
00568 
00569 void tlobby_main::update_gamelist_diff()
00570 {
00571     SCOPE_LB;
00572     lobby_info_.make_games_vector();
00573     int select_row = -1;
00574     unsigned list_i = 0;
00575     int list_rows_deleted = 0;
00576     std::vector<int> next_gamelist_id_at_row;
00577     for (unsigned i = 0; i < lobby_info_.games().size(); ++i) {
00578         const game_info& game = *lobby_info_.games()[i];
00579         if (game.display_status == game_info::NEW) {
00580             LOG_LB << "Adding game to listbox " << game.id << "\n";
00581             if (list_i != gamelistbox_->get_item_count()) {
00582                 gamelistbox_->add_row(make_game_row_data(game), list_i);
00583                 DBG_LB << "Added a game listbox row not at the end"
00584                     << list_i << " " << gamelistbox_->get_item_count() << "\n";
00585                 list_rows_deleted--;
00586             } else {
00587                 gamelistbox_->add_row(make_game_row_data(game));
00588             }
00589             tgrid* grid = gamelistbox_->get_row_grid(gamelistbox_->get_item_count() - 1);
00590             adjust_game_row_contents(game, gamelistbox_->get_item_count() - 1, grid);
00591             list_i++;
00592             next_gamelist_id_at_row.push_back(game.id);
00593         } else {
00594             if (list_i >= gamelistbox_->get_item_count()) {
00595                 ERR_LB << "Ran out of listbox items -- triggering a full refresh\n";
00596                 network::send_data(config("refresh_lobby"), 0);
00597                 return;
00598             }
00599             if (list_i + list_rows_deleted >= gamelist_id_at_row_.size()) {
00600                 ERR_LB << "gamelist_id_at_row_ overflow! "
00601                     << list_i << " + " << list_rows_deleted
00602                     << " >= " << gamelist_id_at_row_.size()
00603                     << " -- triggering a full refresh\n";
00604                 network::send_data(config("refresh_lobby"), 0);
00605                 return;
00606             }
00607             int listbox_game_id = gamelist_id_at_row_[list_i + list_rows_deleted];
00608             if (game.id != listbox_game_id) {
00609                 ERR_LB << "Listbox game id does not match expected id "
00610                     << listbox_game_id << " " << game.id << " (row " << list_i << ")\n";
00611                 network::send_data(config("refresh_lobby"), 0);
00612                 return;
00613             }
00614             if (game.display_status == game_info::UPDATED) {
00615                 LOG_LB << "Modyfying game in listbox " << game.id << " (row " << list_i << ")\n";
00616                 tgrid* grid = gamelistbox_->get_row_grid(list_i);
00617                 modify_grid_with_data(grid, make_game_row_data(game));
00618                 adjust_game_row_contents(game, list_i, grid);
00619                 ++list_i;
00620                 next_gamelist_id_at_row.push_back(game.id);
00621             } else if (game.display_status == game_info::DELETED) {
00622                 LOG_LB << "Deleting game from listbox " << game.id << " (row " << list_i << ")\n";
00623                 gamelistbox_->remove_row(list_i);
00624                 ++list_rows_deleted;
00625             } else {
00626                 //clean
00627                 LOG_LB << "Clean game in listbox " << game.id << " (row " << list_i << ")\n";
00628                 next_gamelist_id_at_row.push_back(game.id);
00629                 ++list_i;
00630             }
00631         }
00632     }
00633     for (unsigned i = 0; i < next_gamelist_id_at_row.size(); ++i) {
00634         if (next_gamelist_id_at_row[i] == selected_game_id_) {
00635             select_row = i;
00636         }
00637     }
00638     next_gamelist_id_at_row.swap(gamelist_id_at_row_);
00639     if (select_row >= static_cast<int>(gamelistbox_->get_item_count())) {
00640         ERR_LB << "Would select a row beyond the listbox"
00641             << select_row << " " << gamelistbox_->get_item_count() << "\n";
00642         select_row = gamelistbox_->get_item_count() - 1;
00643     }
00644     if (select_row >= 0 && select_row != gamelistbox_->get_selected_row()) {
00645         gamelistbox_->select_row(select_row);
00646     }
00647     update_selected_game();
00648     gamelist_dirty_ = false;
00649     last_gamelist_update_ = SDL_GetTicks();
00650     lobby_info_.sync_games_display_status();
00651     lobby_info_.apply_game_filter();
00652     update_gamelist_header();
00653     gamelistbox_->set_row_shown(lobby_info_.games_visibility());
00654 }
00655 
00656 void tlobby_main::update_gamelist_header()
00657 {
00658 #ifndef GUI2_EXPERIMENTAL_LISTBOX
00659     utils::string_map symbols;
00660     symbols["num_shown"] = lexical_cast<std::string>(lobby_info_.games_filtered().size());
00661     symbols["num_total"] = lexical_cast<std::string>(lobby_info_.games().size());
00662     std::string games_string = VGETTEXT("Games: showing $num_shown out of $num_total", symbols);
00663     find_widget<tlabel>(gamelistbox_, "map", false).set_label(games_string);
00664 #endif
00665 }
00666 
00667 std::map<std::string, string_map> tlobby_main::make_game_row_data(const game_info& game)
00668 {
00669     std::map<std::string, string_map> data;
00670 
00671     const char* color_string;
00672     if (game.vacant_slots > 0) {
00673         if (game.reloaded || game.started) {
00674             color_string = "yellow";
00675         } else {
00676             color_string = "green";
00677         }
00678     } else {
00679         if (game.observers) {
00680             color_string = "#ddd";
00681         } else {
00682             color_string = "red";
00683         }
00684     }
00685     if (!game.have_era && (game.vacant_slots > 0 || game.observers)) {
00686         color_string = "#444";
00687     }
00688 
00689     add_label_data(data, "status", colorize(game.status, color_string));
00690     add_label_data(data, "name", colorize(game.name, color_string));
00691 
00692     add_label_data(data, "era", game.era);
00693     add_label_data(data, "era_short", game.era_short);
00694     add_label_data(data, "map_info", game.map_info);
00695     add_label_data(data, "scenario", game.scenario);
00696     add_label_data(data, "map_size_text", game.map_size_info);
00697     add_label_data(data, "time_limit", game.time_limit);
00698     add_label_data(data, "gold_text", game.gold);
00699     add_label_data(data, "xp_text", game.xp);
00700     add_label_data(data, "vision_text", game.vision);
00701     add_label_data(data, "time_limit_text", game.time_limit);
00702     add_label_data(data, "status", game.status);
00703     if (game.observers) {
00704         add_label_data(data, "observer_icon", "misc/eye.png");
00705         add_tooltip_data(data, "observer_icon", _("Observers allowed"));
00706     } else {
00707         add_label_data(data, "observer_icon", "misc/no_observer.png");
00708         add_tooltip_data(data, "observer_icon", _("Observers not allowed"));
00709     }
00710 
00711     const char* vision_icon;
00712     if (game.fog) {
00713         if (game.shroud) {
00714             vision_icon = "misc/vision-fog-shroud.png";
00715         } else {
00716             vision_icon = "misc/vision-fog.png";
00717         }
00718     } else {
00719         if (game.shroud) {
00720             vision_icon = "misc/vision-shroud.png";
00721         } else {
00722             vision_icon = "misc/vision-none.png";
00723         }
00724     }
00725     add_label_data(data, "vision_icon", vision_icon);
00726     add_tooltip_data(data, "vision_icon", game.vision);
00727     return data;
00728 }
00729 
00730 void tlobby_main::adjust_game_row_contents(const game_info& game, int idx, tgrid* grid)
00731 {
00732     find_widget<tcontrol>(grid, "name", false).set_use_markup(true);
00733 
00734     find_widget<tcontrol>(grid, "status", false).set_use_markup(true);
00735 
00736     ttoggle_panel& row_panel =
00737             find_widget<ttoggle_panel>(grid, "panel", false);
00738 
00739     row_panel.set_callback_mouse_left_double_click(boost::bind(
00740         &tlobby_main::join_or_observe, this, idx));
00741 
00742     set_visible_if_exists(grid, "time_limit_icon", !game.time_limit.empty());
00743     set_visible_if_exists(grid, "vision_fog", game.fog);
00744     set_visible_if_exists(grid, "vision_shroud", game.shroud);
00745     set_visible_if_exists(grid, "vision_none", !(game.fog || game.shroud));
00746     set_visible_if_exists(grid, "observers_yes", game.observers);
00747     set_visible_if_exists(grid, "shuffle_sides_icon", game.shuffle_sides);
00748     set_visible_if_exists(grid, "observers_no", !game.observers);
00749     set_visible_if_exists(grid, "needs_password", game.password_required);
00750     set_visible_if_exists(grid, "reloaded", game.reloaded);
00751     set_visible_if_exists(grid, "started", game.started);
00752     set_visible_if_exists(grid, "use_map_settings", game.use_map_settings);
00753     set_visible_if_exists(grid, "no_era", !game.have_era);
00754 
00755 
00756     tbutton* join_button = dynamic_cast<tbutton*>(grid->find("join", false));
00757     if (join_button) {
00758         connect_signal_mouse_left_click(*join_button, boost::bind(
00759                   &tlobby_main::join_button_callback
00760                 , this
00761                 , boost::ref(*window_)));
00762         join_button->set_active(game.can_join());
00763     }
00764     tbutton* observe_button = dynamic_cast<tbutton*>(grid->find("observe", false));
00765     if (observe_button) {
00766         connect_signal_mouse_left_click(*observe_button, boost::bind(
00767                   &tlobby_main::observe_button_callback
00768                 , this
00769                 , boost::ref(*window_)));
00770         observe_button->set_active(game.can_observe());
00771     }
00772     tminimap* minimap = dynamic_cast<tminimap*>(grid->find("minimap", false));
00773     if (minimap) {
00774         minimap->set_config(&game_config_);
00775         minimap->set_map_data(game.map_data);
00776     }
00777 }
00778 
00779 void tlobby_main::update_gamelist_filter()
00780 {
00781     DBG_LB << "tlobby_main::update_gamelist_filter\n";
00782     lobby_info_.apply_game_filter();
00783     DBG_LB << "Games in lobby_info: " << lobby_info_.games().size()
00784         << ", games in listbox: " << gamelistbox_->get_item_count() << "\n";
00785     assert(lobby_info_.games().size() == gamelistbox_->get_item_count());
00786     gamelistbox_->set_row_shown(lobby_info_.games_visibility());
00787 }
00788 
00789 
00790 void tlobby_main::update_playerlist()
00791 {
00792     if (delay_playerlist_update_) return;
00793     SCOPE_LB;
00794     DBG_LB << "Playerlist update: " << lobby_info_.users().size() << "\n";
00795     lobby_info_.update_user_statuses(selected_game_id_, active_window_room());
00796     lobby_info_.sort_users(player_list_.sort_by_name->get_value(), player_list_.sort_by_relation->get_value());
00797 
00798     bool lobby = false;
00799     if (room_info* ri = active_window_room()) {
00800         if (ri->name() == "lobby") {
00801             lobby = true;
00802         }
00803     }
00804     player_list_.active_game.list->clear();
00805     player_list_.active_room.list->clear();
00806     player_list_.other_rooms.list->clear();
00807     player_list_.other_games.list->clear();
00808 
00809     assert(player_list_.active_game.tree);
00810     assert(player_list_.active_room.tree);
00811     assert(player_list_.other_games.tree);
00812     assert(player_list_.other_rooms.tree);
00813 
00814     player_list_.active_game.tree->clear();
00815     player_list_.active_room.tree->clear();
00816     player_list_.other_games.tree->clear();
00817     player_list_.other_rooms.tree->clear();
00818 
00819     foreach (user_info* userptr, lobby_info_.users_sorted())
00820     {
00821         user_info& user = *userptr;
00822         tsub_player_list* target_list(NULL);
00823         std::map<std::string, string_map> data;
00824         std::stringstream icon_ss;
00825         std::string name = user.name;
00826         icon_ss << "lobby/status";
00827         switch (user.state) {
00828             case user_info::SEL_ROOM:
00829                 icon_ss << "-lobby";
00830                 target_list = &player_list_.active_room;
00831                 if (lobby) {
00832                     target_list = &player_list_.other_rooms;
00833                 }
00834                 break;
00835             case user_info::LOBBY:
00836                 icon_ss << "-lobby";
00837                 target_list = &player_list_.other_rooms;
00838                 break;
00839             case user_info::SEL_GAME:
00840                 name = colorize(name, "cyan");
00841                 icon_ss << (user.observing ? "-obs" : "-playing");
00842                 target_list = &player_list_.active_game;
00843                 break;
00844             case user_info::GAME:
00845                 name = colorize(name, "red");
00846                 icon_ss << (user.observing ? "-obs" : "-playing");
00847                 target_list = &player_list_.other_games;
00848                 break;
00849             default:
00850                 ERR_LB << "Bad user state in lobby: " << user.name << ": " << user.state << "\n";
00851                 continue;
00852         }
00853         switch (user.relation) {
00854             case user_info::ME:
00855                 /* fall through */
00856             case user_info::NEUTRAL:
00857                 icon_ss << "-n";
00858                 break;
00859             case user_info::FRIEND:
00860                 icon_ss << "-f";
00861                 break;
00862             case user_info::IGNORED:
00863                 icon_ss << "-i";
00864                 break;
00865             default:
00866                 ERR_LB << "Bad user relation in lobby: " << user.relation << "\n";
00867         }
00868         if (user.registered) {
00869             name = tag(name, "b");
00870         }
00871         icon_ss << ".png";
00872         add_label_data(data, "player", name);
00873         add_label_data(data, "main_icon", icon_ss.str());
00874         if (!preferences::playerlist_group_players()) {
00875             target_list = &player_list_.other_rooms;
00876         }
00877 
00878         assert(target_list->tree);
00879 
00880         string_map tree_group_field;
00881         std::map<std::string, string_map> tree_group_item;
00882 
00883         /*** Add tree item ***/
00884         tree_group_field["label"] = icon_ss.str();
00885         tree_group_item["icon"] = tree_group_field;
00886 
00887         tree_group_field["label"] = name;
00888         tree_group_field["use_markup"] = "true";
00889         tree_group_item["name"] = tree_group_field;
00890 
00891         ttree_view_node& player =
00892                 target_list->tree->add_child("player", tree_group_item);
00893 
00894         find_widget<ttoggle_panel>(&player, "tree_view_node_label", false)
00895                 .set_callback_mouse_left_double_click(boost::bind(
00896                     &tlobby_main::user_dialog_callback, this, userptr));
00897     }
00898     player_list_.active_game.auto_hide();
00899     player_list_.active_room.auto_hide();
00900     player_list_.other_rooms.auto_hide();
00901     player_list_.other_games.auto_hide();
00902 
00903     player_list_dirty_ = false;
00904 }
00905 
00906 void tlobby_main::update_selected_game()
00907 {
00908     int idx = gamelistbox_->get_selected_row();
00909     bool can_join = false, can_observe = false;
00910     if (idx >= 0) {
00911         const game_info& game = *lobby_info_.games()[idx];
00912         can_observe = game.can_observe();
00913         can_join = game.can_join();
00914         selected_game_id_ = game.id;
00915     } else {
00916         selected_game_id_ = 0;
00917     }
00918     find_widget<tbutton>(window_, "observe_global", false)
00919             .set_active(can_observe);
00920 
00921     find_widget<tbutton>(window_, "join_global", false).set_active(can_join);
00922 
00923     player_list_dirty_ = true;
00924 }
00925 
00926 void tlobby_main::pre_show(CVideo& /*video*/, twindow& window)
00927 {
00928     SCOPE_LB;
00929     roomlistbox_ = find_widget<tlistbox>(&window, "room_list", false, true);
00930 #ifdef GUI2_EXPERIMENTAL_LISTBOX
00931     connect_signal_notify_modified(*roomlistbox_, boost::bind(
00932                   &tlobby_main::room_switch_callback
00933                 , *this
00934                 , boost::ref(window)));
00935 #else
00936     roomlistbox_->set_callback_value_change(
00937             dialog_callback<tlobby_main, &tlobby_main::room_switch_callback>);
00938 #endif
00939 
00940     gamelistbox_ =  find_widget<tlistbox>(&window, "game_list", false, true);
00941 #ifdef GUI2_EXPERIMENTAL_LISTBOX
00942     connect_signal_notify_modified(*gamelistbox_, boost::bind(
00943                   &tlobby_main::gamelist_change_callback
00944                 , *this
00945                 , boost::ref(window)));
00946 #else
00947     gamelistbox_->set_callback_value_change(
00948             dialog_callback<tlobby_main
00949                 , &tlobby_main::gamelist_change_callback>);
00950 #endif
00951 
00952     player_list_.init(window);
00953 
00954     player_list_.sort_by_name->set_value(preferences::playerlist_sort_name());
00955     player_list_.sort_by_relation->set_value(preferences::playerlist_sort_relation());
00956     player_list_.update_sort_icons();
00957 
00958     player_list_.sort_by_name->set_callback_state_change(boost::bind(
00959         &tlobby_main::player_filter_callback, this, _1));
00960     player_list_.sort_by_relation->set_callback_state_change(boost::bind(
00961         &tlobby_main::player_filter_callback, this, _1));
00962 
00963     chat_log_container_ = find_widget<tmulti_page>(
00964             &window, "chat_log_container", false, true);
00965 
00966     window.set_enter_disabled(true);
00967 
00968     window_ = &window;
00969 
00970     chat_input_ = find_widget<ttext_box>(&window, "chat_input", false, true);
00971     assert(chat_input_);
00972     connect_signal_pre_key_press(*chat_input_, boost::bind(
00973               &tlobby_main::chat_input_keypress_callback
00974             , this
00975             , _3
00976             , _4
00977             , _5
00978             , boost::ref(window)));
00979 
00980     connect_signal_mouse_left_click(
00981               find_widget<tbutton>(&window, "send_message", false)
00982             , boost::bind(
00983                   &tlobby_main::send_message_button_callback
00984                 , this
00985                 , boost::ref(window)));
00986 
00987     connect_signal_mouse_left_click(
00988               find_widget<tbutton>(&window, "create", false)
00989             , boost::bind(
00990                   &tlobby_main::create_button_callback
00991                 , this
00992                 , boost::ref(window)));
00993 
00994     connect_signal_mouse_left_click(
00995               find_widget<tbutton>(&window, "refresh", false)
00996             , boost::bind(
00997                   &tlobby_main::refresh_button_callback
00998                 , this
00999                 , boost::ref(window)));
01000 
01001     connect_signal_mouse_left_click(
01002               find_widget<tbutton>(&window, "show_preferences", false)
01003             , boost::bind(
01004                   &tlobby_main::show_preferences_button_callback
01005                 , this
01006                 , boost::ref(window)));
01007 
01008     connect_signal_mouse_left_click(
01009               find_widget<tbutton>(&window, "join_global", false)
01010             , boost::bind(
01011                   &tlobby_main::join_global_button_callback
01012                 , this
01013                 , boost::ref(window)));
01014     find_widget<tbutton>(&window, "join_global", false).set_active(false);
01015 
01016     connect_signal_mouse_left_click(
01017               find_widget<tbutton>(&window, "observe_global", false)
01018             , boost::bind(
01019                   &tlobby_main::observe_global_button_callback
01020                 , this
01021                 , boost::ref(window)));
01022     find_widget<tbutton>(&window, "observe_global", false).set_active(false);
01023 
01024     connect_signal_mouse_left_click(
01025               find_widget<tbutton>(&window, "close_window", false)
01026             , boost::bind(
01027                   &tlobby_main::close_window_button_callback
01028                 , this
01029                 , boost::ref(window)));
01030 
01031     ttoggle_button& skip_replay =
01032             find_widget<ttoggle_button>(&window, "skip_replay", false);
01033     skip_replay.set_value(preferences::skip_mp_replay());
01034     skip_replay.set_callback_state_change(boost::bind(&tlobby_main::skip_replay_changed_callback, this, _1));
01035 
01036     filter_friends_ = find_widget<ttoggle_button>(
01037             &window, "filter_with_friends", false, true);
01038     filter_ignored_ = find_widget<ttoggle_button>(
01039             &window, "filter_without_ignored", false, true);
01040     filter_slots_ = find_widget<ttoggle_button>(
01041             &window, "filter_vacant_slots", false, true);
01042     filter_invert_ = find_widget<ttoggle_button>(
01043             &window, "filter_invert", false, true);
01044     filter_text_= find_widget<ttext_box>(&window, "filter_text", false, true);
01045 
01046     filter_friends_->set_callback_state_change(
01047         boost::bind(&tlobby_main::game_filter_change_callback, this, _1));
01048     filter_ignored_->set_callback_state_change(
01049         boost::bind(&tlobby_main::game_filter_change_callback, this, _1));
01050     filter_slots_->set_callback_state_change(
01051         boost::bind(&tlobby_main::game_filter_change_callback, this, _1));
01052     filter_invert_->set_callback_state_change(
01053         boost::bind(&tlobby_main::game_filter_change_callback, this, _1));
01054     connect_signal_pre_key_press(*filter_text_, boost::bind(
01055               &tlobby_main::game_filter_keypress_callback
01056             , this
01057             , _5));
01058 
01059     room_window_open("lobby", true);
01060     active_window_changed();
01061     game_filter_reload();
01062 
01063     // Force first update to be directly.
01064     tlobby_main::network_handler();
01065     lobby_update_timer_ = add_timer(game_config::lobby_network_timer
01066             , boost::bind(&tlobby_main::network_handler, this)
01067             , true);
01068 }
01069 
01070 void tlobby_main::post_show(twindow& /*window*/)
01071 {
01072     window_ = NULL;
01073     remove_timer(lobby_update_timer_);
01074     lobby_update_timer_ = 0;
01075 }
01076 
01077 room_info* tlobby_main::active_window_room()
01078 {
01079     const tlobby_chat_window& t = open_windows_[active_window_];
01080     if (t.whisper) return NULL;
01081     return lobby_info_.get_room(t.name);
01082 }
01083 
01084 tlobby_chat_window* tlobby_main::room_window_open(const std::string& room, bool open_new)
01085 {
01086     return search_create_window(room, false, open_new);
01087 }
01088 
01089 tlobby_chat_window* tlobby_main::whisper_window_open(const std::string& name, bool open_new)
01090 {
01091     return search_create_window(name, true, open_new);
01092 }
01093 
01094 tlobby_chat_window* tlobby_main::search_create_window(const std::string& name, bool whisper, bool open_new)
01095 {
01096     foreach (tlobby_chat_window& t, open_windows_) {
01097         if (t.name == name && t.whisper == whisper) return &t;
01098     }
01099     if (open_new) {
01100         open_windows_.push_back(tlobby_chat_window(name, whisper));
01101         std::map<std::string, string_map> data;
01102         utils::string_map symbols;
01103         symbols["name"] = name;
01104         if (whisper) {
01105             add_label_data(data, "log_header", "<" + name + ">");
01106             add_label_data(data, "log_text", VGETTEXT(
01107                 "Whisper session with $name started. "
01108                 "If you don’t want to receive messages from this user, "
01109                 "type /ignore $name\n", symbols));
01110         } else {
01111             add_label_data(data, "log_header", name);
01112             add_label_data(data, "log_text", VGETTEXT(
01113                 "Room $name joined", symbols));
01114             lobby_info_.open_room(name);
01115         }
01116         chat_log_container_->add_page(data);
01117         std::map<std::string, string_map> data2;
01118         add_label_data(data2, "room", whisper ? "<" + name + ">" : name);
01119         roomlistbox_->add_row(data2);
01120 
01121         return &open_windows_.back();
01122     }
01123     return NULL;
01124 }
01125 
01126 bool tlobby_main::whisper_window_active(const std::string& name)
01127 {
01128     const tlobby_chat_window& t = open_windows_[active_window_];
01129     return t.name == name && t.whisper == true;
01130 }
01131 
01132 bool tlobby_main::room_window_active(const std::string& room)
01133 {
01134     const tlobby_chat_window& t = open_windows_[active_window_];
01135     return t.name == room && t.whisper == false;
01136 }
01137 
01138 void tlobby_main::increment_waiting_whsipers(const std::string& name)
01139 {
01140     if (tlobby_chat_window* t = whisper_window_open(name, false)) {
01141         t->pending_messages++;
01142         if (t->pending_messages == 1) {
01143             DBG_LB << "do whisper pending mark row "
01144                 << (t - &open_windows_[0]) << " with " << t->name << "\n";
01145             tgrid* grid = roomlistbox_->get_row_grid(t - &open_windows_[0]);
01146             //this breaks for some reason
01147             //tlabel& label = grid->get_widget<tlabel>("room", false);
01148             //label.set_use_markup(true);
01149             //label.set_label(colorize("<" + t->name + ">", "red"));
01150             find_widget<timage>(grid, "pending_messages", false)
01151                     .set_visible(twidget::VISIBLE);
01152         }
01153     }
01154 }
01155 
01156 void tlobby_main::increment_waiting_messages(const std::string& room)
01157 {
01158     if (tlobby_chat_window* t = room_window_open(room, false)) {
01159         t->pending_messages++;
01160         if (t->pending_messages == 1) {
01161             int idx = t - &open_windows_[0];
01162             DBG_LB << "do room pending mark row "
01163                 << idx << " with " << t->name << "\n";
01164             tgrid* grid = roomlistbox_->get_row_grid(idx);
01165             //this breaks for some reason
01166             //tlabel& label = grid->get_widget<tlabel>("room", false);
01167             //label.set_use_markup(true);
01168             //label.set_label(colorize(t->name, "red"));
01169             find_widget<timage>(grid, "pending_messages", false)
01170                     .set_visible(twidget::VISIBLE);
01171         }
01172     }
01173 }
01174 
01175 void tlobby_main::add_whisper_window_whisper(const std::string& sender, const std::string& message)
01176 {
01177     std::stringstream ss;
01178     ss << "<" << sender << ">" << message;
01179     tlobby_chat_window* t = whisper_window_open(sender, false);
01180     if (!t) {
01181         ERR_LB << "Whisper window not open in add_whisper_window_whisper for " << sender << "\n";
01182         return;
01183     }
01184     append_to_chatbox(ss.str(), t - &open_windows_[0]);
01185 }
01186 
01187 void tlobby_main::add_active_window_whisper(const std::string& sender, const std::string& message)
01188 {
01189     std::stringstream ss;
01190     ss << "<" << "whisper: " << sender << ">" << message;
01191     append_to_chatbox(ss.str());
01192 }
01193 
01194 void tlobby_main::add_room_window_message(const std::string& room,
01195     const std::string& sender, const std::string& message)
01196 {
01197     std::stringstream ss;
01198     ss << "<" << sender << ">" << message;
01199     tlobby_chat_window* t = room_window_open(room, false);
01200     if (!t) {
01201         ERR_LB << "Room window not open in add_room_window_message for " << room << "\n";
01202         return;
01203     }
01204     append_to_chatbox(ss.str(), t - &open_windows_[0]);
01205 }
01206 
01207 void tlobby_main::add_active_window_message(const std::string& sender, const std::string& message)
01208 {
01209     std::stringstream ss;
01210     ss << "<" << sender << ">" << message;
01211     append_to_chatbox(ss.str());
01212 }
01213 
01214 void tlobby_main::switch_to_window(tlobby_chat_window* t)
01215 {
01216     switch_to_window(t - &open_windows_[0]);
01217 }
01218 
01219 void tlobby_main::switch_to_window(size_t id)
01220 {
01221     active_window_ = id;
01222     assert(active_window_ < open_windows_.size());
01223     chat_log_container_->select_page(active_window_);
01224     roomlistbox_->select_row(active_window_);
01225     active_window_changed();
01226 }
01227 
01228 void tlobby_main::active_window_changed()
01229 {
01230     tlabel& header = find_widget<tlabel>(
01231               &chat_log_container_->page_grid(active_window_)
01232             , "log_header"
01233             , false);
01234 
01235     tlobby_chat_window& t = open_windows_[active_window_];
01236     std::string expected_label;
01237     if (t.whisper) {
01238         expected_label = "<" + t.name + ">";
01239     } else {
01240         expected_label = t.name;
01241     }
01242     if (header.label() != expected_label) {
01243         ERR_LB << "Chat log header not what it should be! "
01244             << header.label() << " vs " << expected_label << "\n";
01245     }
01246 
01247     bool close_button_active = (t.whisper || (t.name != "lobby"));
01248 
01249     DBG_LB << "active window changed to " << active_window_ << " "
01250         << (t.whisper ? "w" : "r") << " "
01251         << t.name << " " << t.pending_messages << " : "
01252         << expected_label
01253         << " close button:" << close_button_active << "\n";
01254 
01255     //clear pending messages notification in room listbox
01256     tgrid* grid = roomlistbox_->get_row_grid(active_window_);
01257     //this breaks for some reason
01258     //tlabel& label = grid->get_widget<tlabel>("room", false);
01259     //label.set_label(expected_label);
01260     find_widget<timage>(grid, "pending_messages", false)
01261             .set_visible(twidget::HIDDEN);
01262     t.pending_messages = 0;
01263 
01264     find_widget<tbutton>(window_, "close_window", false)
01265             .set_active(close_button_active);
01266     player_list_dirty_ = true;
01267 }
01268 
01269 
01270 void tlobby_main::close_active_window()
01271 {
01272     DBG_LB << "Close window button clicked\n";
01273     return close_window(active_window_);
01274 }
01275 
01276 void tlobby_main::close_window(size_t idx)
01277 {
01278     const tlobby_chat_window& t = open_windows_[idx];
01279     bool active_changed = idx == active_window_;
01280     DBG_LB << "Close window " << idx << " - " << t.name << "\n";
01281     if (t.name == "lobby" && t.whisper == false) return;
01282     if (open_windows_.size() == 1) return;
01283     if (t.whisper == false) {
01284         //closing a room window -- send a part to the server
01285         config data, msg;
01286         msg["room"] = t.name;
01287         msg["player"] = preferences::login();
01288         data.add_child("room_part", msg);
01289         network::send_data(data, 0);
01290     }
01291     if (active_window_ == open_windows_.size() - 1) {
01292         active_window_--;
01293     }
01294     if (t.whisper) {
01295         lobby_info_.get_whisper_log(t.name).clear();
01296     } else {
01297         lobby_info_.close_room(t.name);
01298     }
01299     open_windows_.erase(open_windows_.begin() + idx);
01300     roomlistbox_->remove_row(idx);
01301     roomlistbox_->select_row(active_window_);
01302     chat_log_container_->remove_page(idx);
01303     chat_log_container_->select_page(active_window_);
01304     if (active_changed) active_window_changed();
01305 }
01306 
01307 void tlobby_main::network_handler()
01308 {
01309     if (gamelist_dirty_ && !delay_gamelist_update_
01310         &&(SDL_GetTicks() - last_gamelist_update_ > game_config::lobby_refresh)) {
01311         if (gamelist_diff_update_) {
01312             update_gamelist_diff();
01313         } else {
01314             update_gamelist();
01315             gamelist_diff_update_ = true;
01316         }
01317     }
01318     if (player_list_dirty_) {
01319         update_gamelist_filter();
01320         update_playerlist();
01321     }
01322     try {
01323         config data;
01324         const network::connection sock = network::receive_data(data);
01325         if(sock) {
01326             process_network_data(data);
01327         }
01328     } catch(network::error& e) {
01329         LOG_LB << "caught network::error in network_handler: " << e.message << "\n";
01330         throw;
01331     }
01332 }
01333 
01334 void tlobby_main::process_network_data(const config &data)
01335 {
01336     if (const config &c = data.child("error")) {
01337         throw network::error(c["message"]);
01338     } else if (const config &c = data.child("message")) {
01339         process_message(c);
01340     } else if (const config &c = data.child("whisper")) {
01341         process_message(c, true);
01342     } else if(data.child("gamelist")) {
01343         process_gamelist(data);
01344     } else if (const config &c = data.child("gamelist_diff")) {
01345         process_gamelist_diff(c);
01346     } else if (const config &c = data.child("room_join")) {
01347         process_room_join(c);
01348     } else if (const config &c = data.child("room_part")) {
01349         process_room_part(c);
01350     } else if (const config &c = data.child("room_query_response")) {
01351         process_room_query_response(c);
01352     }
01353 }
01354 
01355 void tlobby_main::process_message(const config &data, bool whisper /*= false*/)
01356 {
01357     std::string sender = data["sender"];
01358     DBG_LB << "process message from " << sender << " "
01359         << (whisper ? "(w)" : "") << ", len " << data["message"].str().size() << '\n';
01360     if (preferences::is_ignored(sender)) return;
01361     const std::string& message = data["message"];
01362     preferences::parse_admin_authentication(sender, message);
01363     if (whisper) {
01364         add_whisper_received(sender, message);
01365     } else {
01366         std::string room = data["room"];
01367         if (room.empty()) {
01368             LOG_LB << "Message without a room from " << sender << ", assuming lobby\n";
01369             room = "lobby";
01370         }
01371         add_chat_room_message_received(room, sender, message);
01372     }
01373 }
01374 
01375 void tlobby_main::process_gamelist(const config &data)
01376 {
01377     lobby_info_.process_gamelist(data);
01378     DBG_LB << "Received gamelist\n";
01379     gamelist_dirty_ = true;
01380     gamelist_diff_update_ = false;
01381 }
01382 
01383 void tlobby_main::process_gamelist_diff(const config &data)
01384 {
01385     if (lobby_info_.process_gamelist_diff(data)) {
01386         DBG_LB << "Received gamelist diff\n";
01387         gamelist_dirty_ = true;
01388     } else {
01389         ERR_LB << "process_gamelist_diff failed!\n";
01390     }
01391     int joined = data.child_count("insert_child");
01392     int left = data.child_count("remove_child");
01393     if (joined > 0 || left > 0) {
01394         if (left > joined) {
01395             do_notify(NOTIFY_LOBBY_QUIT);
01396         } else {
01397             do_notify(NOTIFY_LOBBY_JOIN);
01398         }
01399     }
01400 }
01401 
01402 void tlobby_main::process_room_join(const config &data)
01403 {
01404     const std::string& room = data["room"];
01405     const std::string& player = data["player"];
01406     room_info* r = lobby_info_.get_room(room);
01407     DBG_LB << "room join: " << room << " " << player << " "
01408             << static_cast<void*>(r) << "\n";
01409 
01410     if (r) {
01411         if (player == preferences::login()) {
01412             if (const config& members = data.child("members")) {
01413                 r->process_room_members(members);
01414             }
01415         } else {
01416             r->add_member(player);
01417             /* TODO: add/use preference */
01418             utils::string_map symbols;
01419             symbols["player"] = player;
01420             add_room_window_message(room, "server", VGETTEXT("$player has entered the room", symbols));
01421         }
01422         if (r == active_window_room()) {
01423             player_list_dirty_ = true;
01424         }
01425     } else {
01426         if (player == preferences::login()) {
01427             tlobby_chat_window* t = room_window_open(room, true);
01428             lobby_info_.open_room(room);
01429             r = lobby_info_.get_room(room);
01430             assert(r);
01431             if (const config& members = data.child("members")) {
01432                 r->process_room_members(members);
01433             }
01434             switch_to_window(t);
01435 
01436             const std::string& topic = data["topic"];
01437             if (!topic.empty()) {
01438                 add_chat_room_message_received("room", "server", room + ": " + topic);
01439             }
01440         } else {
01441             LOG_LB << "Discarding join info for a room the player is not in\n";
01442         }
01443     }
01444 }
01445 
01446 void tlobby_main::process_room_part(const config &data)
01447 {
01448     //todo close room window when the part message is sent
01449     const std::string& room = data["room"];
01450     const std::string& player = data["player"];
01451     DBG_LB << "Room part: " << room << " " << player << "\n";
01452     room_info* r = lobby_info_.get_room(room);
01453     if (r) {
01454         r->remove_member(player);
01455         /* TODO: add/use preference */
01456         utils::string_map symbols;
01457         symbols["player"] = player;
01458         add_room_window_message(room, "server", VGETTEXT("$player has left the room", symbols));
01459         if (active_window_room() == r) {
01460             player_list_dirty_ = true;
01461         }
01462     } else {
01463         LOG_LB << "Discarding part info for a room the player is not in\n";
01464     }
01465 }
01466 
01467 void tlobby_main::process_room_query_response(const config& data)
01468 {
01469     const std::string& room = data["room"];
01470     const std::string& message = data["message"];
01471     DBG_LB << "room query response: " << room << " " << message << "\n";
01472     if (room.empty()) {
01473         if (!message.empty()) {
01474             add_active_window_message("server", message);
01475         }
01476         if (const config& rooms = data.child("rooms")) {
01477             //TODO: this should really open a nice join room dialog instead
01478             std::stringstream ss;
01479             ss << "Rooms:";
01480             foreach (const config& r, rooms.child_range("room")) {
01481                 ss << " " << r["name"];
01482             }
01483             add_active_window_message("server", ss.str());
01484         }
01485     } else {
01486         if (room_window_open(room, false)) {
01487             if (!message.empty()) {
01488                 add_chat_room_message_received(room, "server", message);
01489             }
01490             if (const config& members = data.child("members")) {
01491                 room_info* r = lobby_info_.get_room(room);
01492                 assert(r);
01493                 r->process_room_members(members);
01494                 if (r == active_window_room()) {
01495                     player_list_dirty_ = true;
01496                 }
01497             }
01498         } else {
01499             if (!message.empty()) {
01500                 add_active_window_message("server", room + ": " + message);
01501             }
01502         }
01503     }
01504 }
01505 
01506 void tlobby_main::join_button_callback(gui2::twindow &window)
01507 {
01508     LOG_LB << "join_button_callback\n";
01509     join_global_button_callback(window);
01510 }
01511 
01512 void tlobby_main::observe_button_callback(gui2::twindow &window)
01513 {
01514     LOG_LB << "observe_button_callback\n";
01515     observe_global_button_callback(window);
01516 }
01517 
01518 void tlobby_main::observe_global_button_callback(gui2::twindow &window)
01519 {
01520     LOG_LB << "observe_global_button_callback\n";
01521     if (do_game_join(gamelistbox_->get_selected_row(), true)) {
01522         legacy_result_ = OBSERVE;
01523         window.close();
01524     }
01525 }
01526 
01527 void tlobby_main::join_global_button_callback(gui2::twindow &window)
01528 {
01529     LOG_LB << "join_global_button_callback\n";
01530     if (do_game_join(gamelistbox_->get_selected_row(), false)) {
01531         legacy_result_ = JOIN;
01532         window.close();
01533     }
01534 }
01535 
01536 void tlobby_main::join_or_observe(int idx)
01537 {
01538     const game_info& game = *lobby_info_.games()[idx];
01539     if (do_game_join(idx, !game.can_join())) {
01540         legacy_result_ = game.can_join() ? JOIN : OBSERVE;
01541         window_->close();
01542     }
01543 }
01544 
01545 bool tlobby_main::do_game_join(int idx, bool observe)
01546 {
01547     if (idx < 0 || idx > static_cast<int>(lobby_info_.games().size())) {
01548         ERR_LB << "Requested join/observe of a game with index out of range: "
01549             << idx << ", games size is " << lobby_info_.games().size() << "\n";
01550         return false;
01551     }
01552     const game_info& game = *lobby_info_.games()[idx];
01553     if (observe) {
01554         if (!game.can_observe()) {
01555             ERR_LB << "Requested observe of a game with observers disabled\n";
01556             return false;
01557         }
01558     } else {
01559         if (!game.can_join()) {
01560             ERR_LB << "Requested join to a game with no vacant slots\n";
01561             return false;
01562         }
01563     }
01564     config response;
01565     config& join = response.add_child("join");
01566     join["id"] = lexical_cast<std::string>(game.id);
01567     join["observe"] = observe;
01568     if (join && !observe && game.password_required) {
01569         std::string password;
01570         //TODO replace with a gui2 dialog
01571         const int res = gui::show_dialog(disp_, NULL, _("Password Required"),
01572                   _("Joining this game requires a password."),
01573                   gui::OK_CANCEL, NULL, NULL, _("Password: "), &password);
01574         if (res != 0) {
01575             return false;
01576         }
01577         if(!password.empty()) {
01578             join["password"] = password;
01579         }
01580     }
01581     network::send_data(response, 0);
01582     if (observe && game.started) {
01583         playmp_controller::set_replay_last_turn(game.current_turn);
01584     }
01585     return true;
01586 }
01587 
01588 void tlobby_main::send_message_button_callback(gui2::twindow &/*window*/)
01589 {
01590     const std::string& input = chat_input_->get_value();
01591     if (input.empty()) return;
01592     if (input[0] == '/') {
01593         //TODO: refactor do_speak so it uses context information about
01594         //      opened window, so e.g. /ignore in a whisper session ignores
01595         //      the other party without having to specify it's nick.
01596         chat_handler::do_speak(input);
01597     } else {
01598         config msg;
01599         send_message_to_active_window(input);
01600     }
01601     chat_input_->save_to_history();
01602     chat_input_->set_value("");
01603 }
01604 
01605 void tlobby_main::send_message_to_active_window(const std::string& input)
01606 {
01607     tlobby_chat_window& t = open_windows_[active_window_];
01608     if (t.whisper) {
01609         send_whisper(t.name, input);
01610         add_whisper_sent(t.name, input);
01611     } else {
01612         send_chat_room_message(t.name, input);
01613         add_chat_room_message_sent(t.name, input);
01614     }
01615 }
01616 
01617 void tlobby_main::close_window_button_callback(twindow& /*window*/)
01618 {
01619     close_active_window();
01620 }
01621 
01622 void tlobby_main::create_button_callback(gui2::twindow& window)
01623 {
01624     legacy_result_ = CREATE;
01625     window.close();
01626 }
01627 
01628 void tlobby_main::refresh_button_callback(gui2::twindow& /*window*/)
01629 {
01630     network::send_data(config("refresh_lobby"), 0);
01631 }
01632 
01633 
01634 void tlobby_main::show_preferences_button_callback(gui2::twindow& window)
01635 {
01636     if (preferences_callback_) {
01637         preferences_callback_();
01638 
01639         /**
01640          * The screen size might have changed force an update of the size.
01641          *
01642          * @todo This might no longer be needed when gui2 is done.
01643          */
01644         window.invalidate_layout();
01645 
01646         network::send_data(config("refresh_lobby"), 0);
01647     }
01648 }
01649 
01650 void tlobby_main::room_switch_callback(twindow& /*window*/)
01651 {
01652     switch_to_window(roomlistbox_->get_selected_row());
01653 }
01654 
01655 void tlobby_main::chat_input_keypress_callback(
01656           bool& handled
01657         , bool& halt
01658         , const SDLKey key
01659         , twindow& window)
01660 {
01661     if (key == SDLK_RETURN || key == SDLK_KP_ENTER) {
01662         send_message_button_callback(window);
01663         handled = true;
01664         halt = true;
01665     } else if(key == SDLK_TAB) {
01666         std::string text = chat_input_->get_value();
01667         const std::vector<user_info>& match_infos = lobby_info_.users();
01668         std::vector<std::string> matches;
01669 
01670         foreach(const user_info& ui, match_infos) {
01671             if(ui.name != preferences::login()) {
01672                 matches.push_back(ui.name);
01673             }
01674         }
01675         const bool line_start = utils::word_completion(text, matches);
01676 
01677         if (matches.empty()) {
01678             return;
01679         }
01680 
01681         if (matches.size() == 1) {
01682             text.append(line_start ? ": " : " ");
01683         } else {
01684             std::string completion_list = utils::join(matches, " ");
01685             append_to_chatbox(completion_list);
01686         }
01687         chat_input_->set_value(text);
01688         handled = true;
01689         halt = true;
01690     }
01691 }
01692 
01693 void tlobby_main::game_filter_reload()
01694 {
01695     lobby_info_.clear_game_filter();
01696 
01697     foreach (const std::string& s, utils::split(filter_text_->get_value(), ' ')) {
01698         lobby_info_.add_game_filter(new game_filter_general_string_part(s));
01699     }
01700     //TODO: make changing friend/ignore lists trigger a refresh
01701     if (filter_friends_->get_value()) {
01702         lobby_info_.add_game_filter(
01703             new game_filter_value<bool, &game_info::has_friends>(true));
01704     }
01705     if (filter_ignored_->get_value()) {
01706         lobby_info_.add_game_filter(
01707             new game_filter_value<bool, &game_info::has_ignored>(false));
01708     }
01709     if (filter_slots_->get_value()) {
01710         lobby_info_.add_game_filter(
01711             new game_filter_value<size_t, &game_info::vacant_slots, std::greater<size_t> >(0));
01712     }
01713     lobby_info_.set_game_filter_invert(filter_invert_->get_value());
01714 }
01715 
01716 void tlobby_main::game_filter_keypress_callback(const SDLKey key)
01717 {
01718     if (key == SDLK_RETURN || key == SDLK_KP_ENTER) {
01719         game_filter_reload();
01720         update_gamelist_filter();
01721     }
01722 }
01723 
01724 void tlobby_main::game_filter_change_callback(gui2::twidget* /*widget*/)
01725 {
01726     game_filter_reload();
01727     update_gamelist_filter();
01728 }
01729 
01730 void tlobby_main::gamelist_change_callback(gui2::twindow &/*window*/)
01731 {
01732     update_selected_game();
01733 }
01734 
01735 void tlobby_main::player_filter_callback(gui2::twidget* /*widget*/)
01736 {
01737     player_list_.update_sort_icons();
01738     preferences::set_playerlist_sort_relation(player_list_.sort_by_relation->get_value());
01739     preferences::set_playerlist_sort_name(player_list_.sort_by_name->get_value());
01740     player_list_dirty_ = true;
01741     //window_->invalidate_layout();
01742 }
01743 
01744 void tlobby_main::user_dialog_callback(user_info* info)
01745 {
01746     tlobby_player_info dlg(*this, *info, lobby_info_);
01747     lobby_delay_gamelist_update_guard g(*this);
01748     dlg.show(window_->video());
01749     delay_playerlist_update_ = true;
01750     if (dlg.result_open_whisper()) {
01751         tlobby_chat_window* t = whisper_window_open(info->name, true);
01752         switch_to_window(t);
01753         window_->invalidate_layout();
01754     }
01755     selected_game_id_ = info->game_id;
01756     //the commented out code below should be enough, but that'd delete the
01757     //playerlist and the widget calling this callback, so instead the game
01758     //will be selected on the netxt gamelist update.
01759     /*
01760     if (info->game_id != 0) {
01761         for (unsigned i = 0; i < lobby_info_.games_filtered().size(); ++i) {
01762         game_info& g = *lobby_info_.games_filtered()[i];
01763             if (info->game_id == g.id) {
01764             gamelistbox_->select_row(i);
01765                 update_selected_game();
01766                 break;
01767             }
01768         }
01769     }
01770     */
01771     //do not update here as it can cause issues with removing the widget
01772     //from within it's event handler. Should get updated as soon as possible
01773     //update_gamelist();
01774     delay_playerlist_update_ = false;
01775     player_list_dirty_ = true;
01776     network::send_data(config("refresh_lobby"), 0);
01777 }
01778 
01779 void tlobby_main::skip_replay_changed_callback(twidget* w)
01780 {
01781     ttoggle_button* tb = dynamic_cast<ttoggle_button*>(w);
01782     assert(tb);
01783     preferences::set_skip_mp_replay(tb->get_value());
01784 }
01785 
01786 } // 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