add mp chatbox widget

to be used in mp lobbay and mp connect.
This commit is contained in:
gfgtdf 2016-09-08 14:12:17 +02:00
parent e654fbbb49
commit 1c2bb78d15
9 changed files with 1225 additions and 871 deletions

View file

@ -954,7 +954,9 @@
grow_factor = 1
horizontal_grow = "true"
vertical_grow = "true"
{_GUI_CHAT_AREA}
[mp_chatbox]
id = "chat"
[/mp_chatbox]
[/column]
[column]
grow_factor = 0
@ -1041,7 +1043,9 @@
horizontal_grow = "true"
vertical_grow = "true"
{GUI_FORCE_WIDGET_MINIMUM_SIZE 0 "((screen_height * 30 / 100))" (
{_GUI_CHAT_AREA}
[mp_chatbox]
id = "chat"
[/mp_chatbox]
)}
[/column]
[/row]
@ -1060,6 +1064,42 @@
[/resolution]
[/window]
[mp_chatbox_definition]
id = "default"
description = "Mp chatbox"
[resolution]
min_width = 0
min_height = 0
default_width = 0
default_height = 0
max_width = 0
max_height = 0
[background]
[draw]
[/draw]
[/background]
[foreground]
[draw]
[/draw]
[/foreground]
{_GUI_CHAT_AREA}
[/resolution]
[/mp_chatbox_definition]
#undef _GUI_CONTROL_AREA
#undef _GUI_CHAT_AREA
#undef _GUI_FILTER_AREA

View file

@ -534,6 +534,7 @@ set(wesnoth-gui_widget_SRC
gui/widgets/matrix.cpp
gui/widgets/menu_button.cpp
gui/widgets/minimap.cpp
gui/widgets/mp_chatbox.cpp
gui/widgets/multi_page.cpp
gui/widgets/pane.cpp
gui/widgets/panel.cpp

View file

@ -447,6 +447,7 @@ wesnoth_sources = Split("""
gui/widgets/matrix.cpp
gui/widgets/menu_button.cpp
gui/widgets/minimap.cpp
gui/widgets/mp_chatbox.cpp
gui/widgets/multi_page.cpp
gui/widgets/pane.cpp
gui/widgets/panel.cpp

View file

@ -23,6 +23,7 @@
#include "map/exception.hpp"
#include "wml_exception.hpp"
#include "wesnothd_connection.hpp"
#include "mp_ui_alerts.hpp"
#include <iterator>
static lg::log_domain log_engine("engine");
@ -65,7 +66,36 @@ void lobby_info::delete_games()
delete v.second;
}
}
namespace gui2
{
void do_mp_notify(t_notify_mode mode, const std::string & sender, const std::string & message)
{
switch (mode) {
case NOTIFY_WHISPER:
case NOTIFY_WHISPER_OTHER_WINDOW:
case NOTIFY_OWN_NICK:
mp_ui_alerts::private_message(true, sender, message);
break;
case NOTIFY_FRIEND_MESSAGE:
mp_ui_alerts::friend_message(true, sender, message);
break;
case NOTIFY_SERVER_MESSAGE:
mp_ui_alerts::server_message(true, sender, message);
break;
case NOTIFY_LOBBY_QUIT:
mp_ui_alerts::player_leaves(true);
break;
case NOTIFY_LOBBY_JOIN:
mp_ui_alerts::player_joins(true);
break;
case NOTIFY_MESSAGE:
mp_ui_alerts::public_message(true, sender, message);
break;
default:
break;
}
}
}
namespace
{

View file

@ -96,6 +96,7 @@ public:
return users_;
}
const std::vector<user_info*>& users_sorted() const;
twesnothd_connection& wesnothd_connection() const { return wesnothd_connection_; }
private:
void process_userlist();
@ -119,4 +120,23 @@ private:
twesnothd_connection& wesnothd_connection_;
};
enum t_notify_mode {
NOTIFY_NONE,
NOTIFY_MESSAGE,
NOTIFY_MESSAGE_OTHER_WINDOW,
NOTIFY_SERVER_MESSAGE,
NOTIFY_OWN_NICK,
NOTIFY_FRIEND_MESSAGE,
NOTIFY_WHISPER,
NOTIFY_WHISPER_OTHER_WINDOW,
NOTIFY_LOBBY_JOIN,
NOTIFY_LOBBY_QUIT,
NOTIFY_COUNT
};
namespace gui2
{
void do_mp_notify(t_notify_mode mode, const std::string & sender, const std::string & message);
inline void do_mp_notify(t_notify_mode mode) { do_mp_notify(mode, "", ""); }
}
#endif

View file

@ -33,6 +33,7 @@
#endif
#include "gui/widgets/menu_button.hpp"
#include "gui/widgets/minimap.hpp"
#include "gui/widgets/mp_chatbox.hpp"
#include "gui/widgets/multi_page.hpp"
#include "gui/widgets/scroll_label.hpp"
#include "gui/widgets/settings.hpp"
@ -49,7 +50,6 @@
#include "gettext.hpp"
#include "lobby_preferences.hpp"
#include "log.hpp"
#include "mp_ui_alerts.hpp"
#include "playmp_controller.hpp"
#include "wesnothd_connection.hpp"
@ -132,179 +132,6 @@ void tplayer_list::update_sort_icons()
sort_by_relation->set_icon_name(sort_by_relation->get_value() ? "lobby/sort-friend.png" : "lobby/sort-friend-off.png");
}
void tlobby_main::send_chat_message(const std::string& message,
bool /*allies_only*/)
{
config data, msg;
msg["message"] = message;
msg["sender"] = preferences::login();
data.add_child("message", msg);
add_chat_message(time(nullptr), preferences::login(), 0, message); // local
// echo
wesnothd_connection_.send_data(data);
}
void tlobby_main::user_relation_changed(const std::string& /*name*/)
{
player_list_dirty_ = true;
}
void tlobby_main::add_chat_message(const time_t& /*time*/,
const std::string& speaker,
int /*side*/,
const std::string& message,
events::chat_handler::MESSAGE_TYPE /*type*/)
{
std::stringstream ss;
ss << "<b>" << speaker << ":</b> ";
ss << font::escape_text(message);
append_to_chatbox(ss.str());
}
void tlobby_main::add_whisper_sent(const std::string& receiver,
const std::string& message)
{
if(whisper_window_active(receiver)) {
add_active_window_message(preferences::login(), message, true);
} else if(tlobby_chat_window* t = whisper_window_open(
receiver, preferences::auto_open_whisper_windows())) {
switch_to_window(t);
add_active_window_message(preferences::login(), message, true);
} else {
utils::string_map symbols;
symbols["receiver"] = receiver;
add_active_window_whisper(VGETTEXT("whisper to $receiver", symbols),
message, true);
}
lobby_info_.get_whisper_log(receiver)
.add_message(preferences::login(), message);
}
void tlobby_main::add_whisper_received(const std::string& sender,
const std::string& message)
{
bool can_go_to_active = !preferences::whisper_friends_only()
|| preferences::is_friend(sender);
bool can_open_new = preferences::auto_open_whisper_windows()
&& can_go_to_active;
lobby_info_.get_whisper_log(sender).add_message(sender, message);
if(whisper_window_open(sender, can_open_new)) {
if(whisper_window_active(sender)) {
add_active_window_message(sender, message);
do_notify(NOTIFY_WHISPER, sender, message);
} else {
add_whisper_window_whisper(sender, message);
increment_waiting_whsipers(sender);
do_notify(NOTIFY_WHISPER_OTHER_WINDOW, sender, message);
}
} else if(can_go_to_active) {
add_active_window_whisper(sender, message);
do_notify(NOTIFY_WHISPER, sender, message);
} else {
LOG_LB << "Ignoring whisper from " << sender << "\n";
}
}
void tlobby_main::add_chat_room_message_sent(const std::string& room,
const std::string& message)
{
// do not open room window here, player should be in the room before sending
// messages yo it should be allowed to happen
if(tlobby_chat_window* t = room_window_open(room, false)) {
room_info* ri = lobby_info_.get_room(room);
assert(ri);
if(!room_window_active(room)) {
switch_to_window(t);
}
ri->log().add_message(preferences::login(), message);
add_active_window_message(preferences::login(), message, true);
} else {
LOG_LB << "Cannot add sent message to ui for room " << room
<< ", player not in the room\n";
}
}
void tlobby_main::add_chat_room_message_received(const std::string& room,
const std::string& speaker,
const std::string& message)
{
if(room_info* ri = lobby_info_.get_room(room)) {
t_notify_mode notify_mode = NOTIFY_NONE;
ri->log().add_message(speaker, message);
if(room_window_active(room)) {
add_active_window_message(speaker, message);
notify_mode = NOTIFY_MESSAGE;
} else {
add_room_window_message(room, speaker, message);
increment_waiting_messages(room);
notify_mode = NOTIFY_MESSAGE_OTHER_WINDOW;
}
if(speaker == "server") {
notify_mode = NOTIFY_SERVER_MESSAGE;
} else if(utils::word_match(message, preferences::login())) {
notify_mode = NOTIFY_OWN_NICK;
} else if(preferences::is_friend(speaker)) {
notify_mode = NOTIFY_FRIEND_MESSAGE;
}
do_notify(notify_mode, speaker, message);
} else {
LOG_LB << "Discarding message to room " << room << " from " << speaker
<< " (room not open)\n";
}
}
void tlobby_main::append_to_chatbox(const std::string& text, const bool force_scroll)
{
append_to_chatbox(text, active_window_, force_scroll);
}
void tlobby_main::append_to_chatbox(const std::string& text, size_t id, const bool force_scroll)
{
tgrid& grid = chat_log_container_->page_grid(id);
tscroll_label& log = find_widget<tscroll_label>(&grid, "log_text", false);
const bool chatbox_at_end = log.vertical_scrollbar_at_end();
const unsigned chatbox_position = log.get_vertical_scrollbar_item_position();
log.set_use_markup(true);
log.set_label(log.label() + "\n" +
"<span color='#bcb088'>" + preferences::get_chat_timestamp(time(0)) + text + "</span>");
if(chatbox_at_end || force_scroll) {
log.scroll_vertical_scrollbar(tscrollbar_::END);
} else {
log.set_vertical_scrollbar_item_position(chatbox_position);
}
}
void tlobby_main::do_notify(t_notify_mode mode, const std::string & sender, const std::string & message)
{
switch(mode) {
case NOTIFY_WHISPER:
case NOTIFY_WHISPER_OTHER_WINDOW:
case NOTIFY_OWN_NICK:
mp_ui_alerts::private_message(true, sender, message);
break;
case NOTIFY_FRIEND_MESSAGE:
mp_ui_alerts::friend_message(true, sender, message);
break;
case NOTIFY_SERVER_MESSAGE:
mp_ui_alerts::server_message(true, sender, message);
break;
case NOTIFY_LOBBY_QUIT:
mp_ui_alerts::player_leaves(true);
break;
case NOTIFY_LOBBY_JOIN:
mp_ui_alerts::player_joins(true);
break;
case NOTIFY_MESSAGE:
mp_ui_alerts::public_message(true, sender, message);
break;
default:
break;
}
}
bool tlobby_main::logout_prompt()
{
return show_prompt(_("Do you really want to log out?"));
@ -317,13 +144,9 @@ tlobby_main::tlobby_main(const config& game_config,
, legacy_result_(QUIT)
, game_config_(game_config)
, gamelistbox_(nullptr)
, roomlistbox_(nullptr)
, chat_log_container_(nullptr)
, chat_input_(nullptr)
, window_(nullptr)
, lobby_info_(info)
, preferences_callback_()
, open_windows_()
, active_window_(0)
, filter_friends_(nullptr)
, filter_ignored_(nullptr)
@ -729,12 +552,12 @@ void tlobby_main::update_playerlist()
return;
SCOPE_LB;
DBG_LB << "Playerlist update: " << lobby_info_.users().size() << "\n";
lobby_info_.update_user_statuses(selected_game_id_, active_window_room());
lobby_info_.update_user_statuses(selected_game_id_, chatbox_->active_window_room());
lobby_info_.sort_users(player_list_.sort_by_name->get_value_bool(),
player_list_.sort_by_relation->get_value_bool());
bool lobby = false;
if(room_info* ri = active_window_room()) {
if(room_info* ri = chatbox_->active_window_room()) {
if(ri->name() == "lobby") {
lobby = true;
}
@ -880,17 +703,6 @@ void tlobby_main::signal_handler_key_down(SDLKey key, bool& handled, bool& halt)
void tlobby_main::pre_show(twindow& window)
{
SCOPE_LB;
roomlistbox_ = find_widget<tlistbox>(&window, "room_list", false, true);
#ifdef GUI2_EXPERIMENTAL_LISTBOX
connect_signal_notify_modified(
*roomlistbox_,
std::bind(&tlobby_main::room_switch_callback,
*this,
std::ref(window)));
#else
roomlistbox_->set_callback_value_change(
dialog_callback<tlobby_main, &tlobby_main::room_switch_callback>);
#endif
gamelistbox_ = find_widget<tlistbox>(&window, "game_list", false, true);
#ifdef GUI2_EXPERIMENTAL_LISTBOX
@ -918,8 +730,6 @@ void tlobby_main::pre_show(twindow& window)
player_list_.sort_by_relation->set_callback_state_change(
std::bind(&tlobby_main::player_filter_callback, this, _1));
chat_log_container_ = find_widget<tmulti_page>(&window, "chat_log_container", false, true);
window.set_enter_disabled(true);
window.set_escape_disabled(true);
// A new key handler to deal with escape in a different manner.
@ -929,16 +739,9 @@ void tlobby_main::pre_show(twindow& window)
window_ = &window;
chat_input_ = find_widget<ttext_box>(&window, "chat_input", false, true);
assert(chat_input_);
connect_signal_pre_key_press(
*chat_input_,
std::bind(&tlobby_main::chat_input_keypress_callback,
this,
_3,
_4,
_5,
std::ref(window)));
chatbox_ = find_widget<tmp_chatbox>(&window, "chat", false, true);
chatbox_->set_lobby_info(lobby_info_);
chatbox_->set_active_window_changed_callback([this]() { player_list_dirty_ = true; });
connect_signal_mouse_left_click(
find_widget<tbutton>(&window, "create", false),
@ -1004,8 +807,8 @@ void tlobby_main::pre_show(twindow& window)
*filter_text_,
std::bind(&tlobby_main::game_filter_keypress_callback, this, _5));
room_window_open("lobby", true);
active_window_changed();
chatbox_->room_window_open("lobby", true);
chatbox_->active_window_changed();
game_filter_reload();
// Force first update to be directly.
@ -1042,7 +845,7 @@ void tlobby_main::pre_show(twindow& window)
plugins_context_->set_callback("create", [this, &window](const config&) { create_button_callback(window); }, true);
plugins_context_->set_callback("quit", [&window](const config&) { window.set_retval(twindow::CANCEL); }, false);
plugins_context_->set_callback("chat", [this](const config& cfg) { send_chat_message(cfg["message"], false); }, true);
plugins_context_->set_callback("chat", [this](const config& cfg) { chatbox_->send_chat_message(cfg["message"], false); }, true);
plugins_context_->set_callback("select_game", [this](const config& cfg) {
selected_game_id_ = cfg.has_attribute("id") ? cfg["id"].to_int() : lobby_info_.games()[cfg["index"].to_int()]->id;
}, true);
@ -1059,242 +862,6 @@ void tlobby_main::post_show(twindow& /*window*/)
plugins_context_.reset();
}
room_info* tlobby_main::active_window_room()
{
const tlobby_chat_window& t = open_windows_[active_window_];
if(t.whisper)
return nullptr;
return lobby_info_.get_room(t.name);
}
tlobby_chat_window* tlobby_main::room_window_open(const std::string& room,
bool open_new)
{
return search_create_window(room, false, open_new);
}
tlobby_chat_window* tlobby_main::whisper_window_open(const std::string& name,
bool open_new)
{
return search_create_window(name, true, open_new);
}
tlobby_chat_window* tlobby_main::search_create_window(const std::string& name,
bool whisper,
bool open_new)
{
for(auto & t : open_windows_) {
if(t.name == name && t.whisper == whisper)
return &t;
}
if(open_new) {
open_windows_.push_back(tlobby_chat_window(name, whisper));
std::map<std::string, string_map> data;
utils::string_map symbols;
symbols["name"] = name;
if(whisper) {
add_label_data(data, "log_text",
VGETTEXT("Whisper session with $name started. "
"If you do not want to receive messages "
"from this user, "
"type /ignore $name\n",
symbols));
} else {
add_label_data(data, "log_text", VGETTEXT("<i>Room $name joined</i>", symbols));
lobby_info_.open_room(name);
}
chat_log_container_->add_page(data);
std::map<std::string, string_map> data2;
add_label_data(data2, "room", whisper ? font::escape_text("<" + name + ">") : name);
tgrid* row_grid = &roomlistbox_->add_row(data2);
tbutton& close_button = find_widget<tbutton>(row_grid, "close_window", false);
connect_signal_mouse_left_click(close_button,
std::bind(&tlobby_main::close_window_button_callback, this, open_windows_.back(),
std::placeholders::_3, std::placeholders::_4));
if(name == "lobby") {
close_button.set_visible(tcontrol::tvisible::hidden);
}
return &open_windows_.back();
}
return nullptr;
}
bool tlobby_main::whisper_window_active(const std::string& name)
{
const tlobby_chat_window& t = open_windows_[active_window_];
return t.name == name && t.whisper == true;
}
bool tlobby_main::room_window_active(const std::string& room)
{
const tlobby_chat_window& t = open_windows_[active_window_];
return t.name == room && t.whisper == false;
}
void tlobby_main::increment_waiting_whsipers(const std::string& name)
{
if(tlobby_chat_window* t = whisper_window_open(name, false)) {
t->pending_messages++;
if(t->pending_messages == 1) {
DBG_LB << "do whisper pending mark row " << (t - &open_windows_[0])
<< " with " << t->name << "\n";
tgrid* grid = roomlistbox_->get_row_grid(t - &open_windows_[0]);
// this breaks for some reason
// tlabel& label = grid->get_widget<tlabel>("room", false);
// label.set_use_markup(true);
// label.set_label(colorize("<" + t->name + ">", "red"));
find_widget<timage>(grid, "pending_messages", false)
.set_visible(twidget::tvisible::visible);
}
}
}
void tlobby_main::increment_waiting_messages(const std::string& room)
{
if(tlobby_chat_window* t = room_window_open(room, false)) {
t->pending_messages++;
if(t->pending_messages == 1) {
int idx = t - &open_windows_[0];
DBG_LB << "do room pending mark row " << idx << " with " << t->name
<< "\n";
tgrid* grid = roomlistbox_->get_row_grid(idx);
// this breaks for some reason
// tlabel& label = grid->get_widget<tlabel>("room", false);
// label.set_use_markup(true);
// label.set_label(colorize(t->name, "red"));
find_widget<timage>(grid, "pending_messages", false)
.set_visible(twidget::tvisible::visible);
}
}
}
void tlobby_main::add_whisper_window_whisper(const std::string& sender,
const std::string& message)
{
std::stringstream ss;
ss << "<b>" << sender << ":</b> " << font::escape_text(message);
tlobby_chat_window* t = whisper_window_open(sender, false);
if(!t) {
ERR_LB << "Whisper window not open in add_whisper_window_whisper for "
<< sender << "\n";
return;
}
append_to_chatbox(ss.str(), t - &open_windows_[0], false);
}
void tlobby_main::add_active_window_whisper(const std::string& sender,
const std::string& message,
const bool force_scroll)
{
std::stringstream ss;
ss << "<b>"
<< "whisper: " << sender << ":</b> " << font::escape_text(message);
append_to_chatbox(ss.str(), force_scroll);
}
void tlobby_main::add_room_window_message(const std::string& room,
const std::string& sender,
const std::string& message)
{
std::stringstream ss;
ss << "<b>" << sender << ":</b> " << font::escape_text(message);
tlobby_chat_window* t = room_window_open(room, false);
if(!t) {
ERR_LB << "Room window not open in add_room_window_message for " << room
<< "\n";
return;
}
append_to_chatbox(ss.str(), t - &open_windows_[0], false);
}
void tlobby_main::add_active_window_message(const std::string& sender,
const std::string& message,
const bool force_scroll)
{
std::stringstream ss;
ss << "<b>" << sender << ":</b> " << font::escape_text(message);
append_to_chatbox(ss.str(), force_scroll);
}
void tlobby_main::switch_to_window(tlobby_chat_window* t)
{
switch_to_window(t - &open_windows_[0]);
}
void tlobby_main::switch_to_window(size_t id)
{
active_window_ = id;
assert(active_window_ < open_windows_.size());
chat_log_container_->select_page(active_window_);
roomlistbox_->select_row(active_window_);
active_window_changed();
}
void tlobby_main::active_window_changed()
{
tlobby_chat_window& t = open_windows_[active_window_];
DBG_LB << "active window changed to " << active_window_ << " "
<< (t.whisper ? "w" : "r") << " " << t.name << "\n";
// clear pending messages notification in room listbox
tgrid* grid = roomlistbox_->get_row_grid(active_window_);
find_widget<timage>(grid, "pending_messages", false).set_visible(twidget::tvisible::hidden);
t.pending_messages = 0;
player_list_dirty_ = true;
}
void tlobby_main::close_active_window()
{
DBG_LB << "Close window button clicked\n";
return close_window(active_window_);
}
void tlobby_main::close_window(size_t idx)
{
const tlobby_chat_window& t = open_windows_[idx];
bool active_changed = idx == active_window_;
DBG_LB << "Close window " << idx << " - " << t.name << "\n";
if((t.name == "lobby" && t.whisper == false) || open_windows_.size() == 1) {
return;
}
if(t.whisper == false) {
// closing a room window -- send a part to the server
config data, msg;
msg["room"] = t.name;
msg["player"] = preferences::login();
data.add_child("room_part", msg);
wesnothd_connection_.send_data(data);
}
if(active_window_ == open_windows_.size() - 1) {
active_window_--;
}
if(t.whisper) {
lobby_info_.get_whisper_log(t.name).clear();
} else {
lobby_info_.close_room(t.name);
}
open_windows_.erase(open_windows_.begin() + idx);
roomlistbox_->remove_row(idx);
roomlistbox_->select_row(active_window_);
chat_log_container_->remove_page(idx);
chat_log_container_->select_page(active_window_);
if(active_changed)
active_window_changed();
}
void tlobby_main::network_handler()
{
if(gamelist_dirty_ && !delay_gamelist_update_
@ -1327,44 +894,14 @@ void tlobby_main::network_handler()
void tlobby_main::process_network_data(const config& data)
{
if(const config& c = data.child("error")) {
if (const config& c = data.child("error")) {
throw wesnothd_error(c["message"]);
} else if(const config& c = data.child("message")) {
process_message(c);
} else if(const config& c = data.child("whisper")) {
process_message(c, true);
} else if (chatbox_->process_network_data(data)) {
} else if(data.child("gamelist")) {
process_gamelist(data);
} else if(const config& c = data.child("gamelist_diff")) {
process_gamelist_diff(c);
} else if(const config& c = data.child("room_join")) {
process_room_join(c);
} else if(const config& c = data.child("room_part")) {
process_room_part(c);
} else if(const config& c = data.child("room_query_response")) {
process_room_query_response(c);
}
}
void tlobby_main::process_message(const config& data, bool whisper /*= false*/)
{
std::string sender = data["sender"];
DBG_LB << "process message from " << sender << " " << (whisper ? "(w)" : "")
<< ", len " << data["message"].str().size() << '\n';
if(preferences::is_ignored(sender))
return;
const std::string& message = data["message"];
preferences::parse_admin_authentication(sender, message);
if(whisper) {
add_whisper_received(sender, message);
} else {
std::string room = data["room"];
if(room.empty()) {
LOG_LB << "Message without a room from " << sender
<< ", assuming lobby\n";
room = "lobby";
}
add_chat_room_message_received(room, sender, message);
}
}
@ -1395,116 +932,6 @@ void tlobby_main::process_gamelist_diff(const config& data)
}
}
void tlobby_main::process_room_join(const config& data)
{
const std::string& room = data["room"];
const std::string& player = data["player"];
room_info* r = lobby_info_.get_room(room);
DBG_LB << "room join: " << room << " " << player << " "
<< static_cast<void*>(r) << "\n";
if(r) {
if(player == preferences::login()) {
if(const config& members = data.child("members")) {
r->process_room_members(members);
}
} else {
r->add_member(player);
/* TODO: add/use preference */
utils::string_map symbols;
symbols["player"] = player;
add_room_window_message(
room,
"server",
VGETTEXT("$player has entered the room", symbols));
}
if(r == active_window_room()) {
player_list_dirty_ = true;
}
} else {
if(player == preferences::login()) {
tlobby_chat_window* t = room_window_open(room, true);
lobby_info_.open_room(room);
r = lobby_info_.get_room(room);
assert(r);
if(const config& members = data.child("members")) {
r->process_room_members(members);
}
switch_to_window(t);
const std::string& topic = data["topic"];
if(!topic.empty()) {
add_chat_room_message_received(
"room", "server", room + ": " + topic);
}
} else {
LOG_LB << "Discarding join info for a room the player is not in\n";
}
}
}
void tlobby_main::process_room_part(const config& data)
{
// todo close room window when the part message is sent
const std::string& room = data["room"];
const std::string& player = data["player"];
DBG_LB << "Room part: " << room << " " << player << "\n";
room_info* r = lobby_info_.get_room(room);
if(r) {
r->remove_member(player);
/* TODO: add/use preference */
utils::string_map symbols;
symbols["player"] = player;
add_room_window_message(
room, "server", VGETTEXT("$player has left the room", symbols));
if(active_window_room() == r) {
player_list_dirty_ = true;
}
} else {
LOG_LB << "Discarding part info for a room the player is not in\n";
}
}
void tlobby_main::process_room_query_response(const config& data)
{
const std::string& room = data["room"];
const std::string& message = data["message"];
DBG_LB << "room query response: " << room << " " << message << "\n";
if(room.empty()) {
if(!message.empty()) {
add_active_window_message("server", message);
}
if(const config& rooms = data.child("rooms")) {
// TODO: this should really open a nice join room dialog instead
std::stringstream ss;
ss << "Rooms:";
for(const auto & r : rooms.child_range("room"))
{
ss << " " << r["name"];
}
add_active_window_message("server", ss.str());
}
} else {
if(room_window_open(room, false)) {
if(!message.empty()) {
add_chat_room_message_received(room, "server", message);
}
if(const config& members = data.child("members")) {
room_info* r = lobby_info_.get_room(room);
assert(r);
r->process_room_members(members);
if(r == active_window_room()) {
player_list_dirty_ = true;
}
}
} else {
if(!message.empty()) {
add_active_window_message("server", room + ": " + message);
}
}
}
}
void tlobby_main::observe_global_button_callback(twindow& window)
{
if(do_game_join(gamelistbox_->get_selected_row(), true)) {
@ -1636,48 +1063,6 @@ bool tlobby_main::do_game_join(int idx, bool observe)
return true;
}
void tlobby_main::send_message_button_callback(twindow& /*window*/)
{
const std::string& input = chat_input_->get_value();
if(input.empty())
return;
if(input[0] == '/') {
// TODO: refactor do_speak so it uses context information about
// opened window, so e.g. /ignore in a whisper session ignores
// the other party without having to specify it's nick.
chat_handler::do_speak(input);
} else {
config msg;
send_message_to_active_window(input);
}
chat_input_->save_to_history();
chat_input_->set_value("");
}
void tlobby_main::send_message_to_active_window(const std::string& input)
{
tlobby_chat_window& t = open_windows_[active_window_];
if(t.whisper) {
send_whisper(t.name, input);
add_whisper_sent(t.name, input);
} else {
send_chat_room_message(t.name, input);
add_chat_room_message_sent(t.name, input);
}
}
void tlobby_main::close_window_button_callback(tlobby_chat_window& chat_window, bool& handled, bool& halt)
{
const int index = std::find_if(open_windows_.begin(), open_windows_.end(), [&chat_window](const tlobby_chat_window& room) {
return room.name == chat_window.name;
}) - open_windows_.begin();
close_window(index);
handled = true;
halt = true;
}
void tlobby_main::create_button_callback(twindow& window)
{
legacy_result_ = CREATE;
@ -1705,49 +1090,6 @@ void tlobby_main::show_preferences_button_callback(twindow& window)
}
}
void tlobby_main::room_switch_callback(twindow& /*window*/)
{
switch_to_window(roomlistbox_->get_selected_row());
}
void tlobby_main::chat_input_keypress_callback(bool& handled,
bool& halt,
const SDLKey key,
twindow& window)
{
if(key == SDLK_RETURN || key == SDLK_KP_ENTER) {
send_message_button_callback(window);
handled = true;
halt = true;
} else if(key == SDLK_TAB) {
std::string text = chat_input_->get_value();
const std::vector<user_info>& match_infos = lobby_info_.users();
std::vector<std::string> matches;
for(const auto & ui : match_infos)
{
if(ui.name != preferences::login()) {
matches.push_back(ui.name);
}
}
const bool line_start = utils::word_completion(text, matches);
if(matches.empty()) {
return;
}
if(matches.size() == 1) {
text.append(line_start ? ": " : " ");
} else {
std::string completion_list = utils::join(matches, " ");
append_to_chatbox(completion_list);
}
chat_input_->set_value(text);
handled = true;
halt = true;
}
}
void tlobby_main::game_filter_reload()
{
lobby_info_.clear_game_filter();
@ -1812,7 +1154,7 @@ void tlobby_main::player_filter_callback(gui2::twidget& /*widget*/)
void tlobby_main::user_dialog_callback(user_info* info)
{
tlobby_player_info dlg(*this, *info, lobby_info_);
tlobby_player_info dlg(*chatbox_, *info, lobby_info_);
lobby_delay_gamelist_update_guard g(*this);
@ -1821,8 +1163,8 @@ void tlobby_main::user_dialog_callback(user_info* info)
delay_playerlist_update_ = true;
if(dlg.result_open_whisper()) {
tlobby_chat_window* t = whisper_window_open(info->name, true);
switch_to_window(t);
tlobby_chat_window* t = chatbox_->whisper_window_open(info->name, true);
chatbox_->switch_to_window(t);
}
selected_game_id_ = info->game_id;

View file

@ -41,17 +41,7 @@ class ttext_box;
class twindow;
class tmulti_page;
class ttoggle_button;
struct tlobby_chat_window
{
tlobby_chat_window(const std::string& name, bool whisper)
: name(name), whisper(whisper), pending_messages(0)
{
}
std::string name;
bool whisper;
int pending_messages;
};
class tmp_chatbox;
struct tsub_player_list
{
@ -77,7 +67,7 @@ struct tplayer_list
ttree_view* tree;
};
class tlobby_main : public tdialog, public quit_confirmation, private events::chat_handler, private plugin_executor
class tlobby_main : public tdialog, public quit_confirmation, private plugin_executor
{
public:
tlobby_main(const config& game_config, lobby_info& info, twesnothd_connection &wesnothd_connection);
@ -91,7 +81,7 @@ public:
void update_gamelist();
void send_to_server(const config& cfg) override;
void send_to_server(const config& cfg);
protected:
void update_gamelist_header();
@ -120,165 +110,19 @@ public:
return legacy_result_;
}
enum t_notify_mode {
NOTIFY_NONE,
NOTIFY_MESSAGE,
NOTIFY_MESSAGE_OTHER_WINDOW,
NOTIFY_SERVER_MESSAGE,
NOTIFY_OWN_NICK,
NOTIFY_FRIEND_MESSAGE,
NOTIFY_WHISPER,
NOTIFY_WHISPER_OTHER_WINDOW,
NOTIFY_LOBBY_JOIN,
NOTIFY_LOBBY_QUIT,
NOTIFY_COUNT
};
void do_notify(t_notify_mode mode) { do_notify(mode, "", ""); }
void do_notify(t_notify_mode mode, const std::string & sender, const std::string & message);
void do_notify(t_notify_mode mode, const std::string & sender, const std::string & message) { do_mp_notify(mode, sender, message); }
protected:
/** inherited form chat_handler */
virtual void send_chat_message(const std::string& message,
bool /*allies_only*/) override;
virtual void user_relation_changed(const std::string& name) override;
/** inherited from chat_handler */
virtual void add_chat_message(const time_t& time,
const std::string& speaker,
int side,
const std::string& message,
events::chat_handler::MESSAGE_TYPE type
= events::chat_handler::MESSAGE_PRIVATE) override;
/** inherited from chat_handler */
virtual void add_whisper_sent(const std::string& receiver,
const std::string& message) override;
/** inherited from chat_handler */
virtual void add_whisper_received(const std::string& sender,
const std::string& message) override;
/** inherited from chat_handler */
virtual void add_chat_room_message_sent(const std::string& room,
const std::string& message) override;
/** inherited from chat_handler */
virtual void add_chat_room_message_received(const std::string& room,
const std::string& speaker,
const std::string& message) override;
private:
void update_selected_game();
/**
* Append some text to the active chat log
*/
void append_to_chatbox(const std::string& text, const bool force_scroll = false);
/**
* Append some text to the chat log for window "id"
*/
void append_to_chatbox(const std::string& text, size_t id, const bool force_scroll = false);
/**
* Result flag for interfacing with other MP dialogs
*/
legacy_result legacy_result_;
/**
* Get the room* corresponding to the currently active window, or nullptr
* if a whisper window is active at the moment
*/
room_info* active_window_room();
/**
* Check if a room window for "room" is open, if open_new is true
* then it will be created if not found.
* @return valid ptr if the window was found or added, null otherwise
*/
tlobby_chat_window* room_window_open(const std::string& room,
bool open_new);
/**
* Check if a whisper window for user "name" is open, if open_new is true
* then it will be created if not found.
* @return valid ptr if the window was found or added, null otherwise
*/
tlobby_chat_window* whisper_window_open(const std::string& name,
bool open_new);
/**
* Helper function to find and open a new window, used by *_window_open
*/
tlobby_chat_window*
search_create_window(const std::string& name, bool whisper, bool open_new);
/**
* @return true if the whisper window for "name" is the active window
*/
bool whisper_window_active(const std::string& name);
/**
* @return true if the room window for "room" is the active window
*/
bool room_window_active(const std::string& room);
/**
* Mark the whisper window for "name" as having one more pending message
*/
void increment_waiting_whsipers(const std::string& name);
/**
* Mark the room window for "room" as having one more pending message
*/
void increment_waiting_messages(const std::string& room);
/**
* Add a whisper message to the whisper window
*/
void add_whisper_window_whisper(const std::string& sender,
const std::string& message);
/**
* Add a whisper message to the current window which is not the whisper
* window
* for "name".
*/
void add_active_window_whisper(const std::string& sender,
const std::string& message,
const bool force_scroll = false);
/**
* Add a message to the window for room "room"
*/
void add_room_window_message(const std::string& room,
const std::string& sender,
const std::string& message);
/**
* Add a message to the window for room "room"
*/
void add_active_window_message(const std::string& sender,
const std::string& message,
const bool force_scroll = false);
/**
* Switch to the window given by a valid pointer (e.g. received from a call
* to *_window_open)
*/
void switch_to_window(tlobby_chat_window* t);
void switch_to_window(size_t id);
void active_window_changed();
void close_active_window();
void close_window(size_t idx);
/**
* Network polling callback
*/
@ -286,18 +130,10 @@ private:
void process_network_data(const config& data);
void process_message(const config& data, bool whisper = false);
void process_gamelist(const config& data);
void process_gamelist_diff(const config& data);
void process_room_join(const config& data);
void process_room_part(const config& data);
void process_room_query_response(const config& data);
void join_global_button_callback(twindow& window);
void observe_global_button_callback(twindow& window);
@ -312,27 +148,12 @@ private:
*/
bool do_game_join(int idx, bool observe);
void send_message_button_callback(twindow& window);
void send_message_to_active_window(const std::string& input);
void close_window_button_callback(tlobby_chat_window& chat_window, bool& handled, bool& halt);
void create_button_callback(twindow& window);
void show_preferences_button_callback(twindow& window);
void refresh_button_callback(twindow& window);
void quit_button_callback(twindow& window);
void room_switch_callback(twindow& window);
void chat_input_keypress_callback(bool& handled,
bool& halt,
const SDLKey key,
twindow& window);
void game_filter_reload();
void game_filter_change_callback(twidget& widget);
@ -367,23 +188,13 @@ private:
tlistbox* gamelistbox_;
tlistbox* roomlistbox_;
tmulti_page* chat_log_container_;
ttext_box* chat_input_;
twindow* window_;
lobby_info& lobby_info_;
std::function<void()> preferences_callback_;
tmp_chatbox* chatbox_;
/**
* This represents the open chat windows (rooms and whispers at the moment)
* with 1 to 1 correspondence to what the user sees in the interface
*/
std::vector<tlobby_chat_window> open_windows_;
std::function<void()> preferences_callback_;
size_t active_window_;

View file

@ -0,0 +1,819 @@
/*
Copyright (C) 2016 The Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#define GETTEXT_DOMAIN "wesnoth-lib"
#include "gui/widgets/mp_chatbox.hpp"
#include "gui/auxiliary/find_widget.hpp"
#include "gui/core/register_widget.hpp"
#include "gui/widgets/button.hpp"
#include "gui/widgets/listbox.hpp"
#include "gui/widgets/label.hpp"
#include "gui/widgets/text_box.hpp"
#include "gui/widgets/settings.hpp"
#include "gui/widgets/scroll_label.hpp"
#include "gui/widgets/window.hpp"
#include "gui/widgets/image.hpp"
#include "gui/widgets/multi_page.hpp"
#include "formatter.hpp"
#include "formula/string_utils.hpp"
#include "gettext.hpp"
#include "marked-up_text.hpp"
#include "wesnothd_connection.hpp"
#include "config_assign.hpp"
#include "game_preferences.hpp"
#include "lobby_preferences.hpp"
#include "log.hpp"
static lg::log_domain log_network("network");
#define DBG_NW LOG_STREAM(debug, log_network)
#define LOG_NW LOG_STREAM(info, log_network)
#define ERR_NW LOG_STREAM(err, log_network)
static lg::log_domain log_engine("engine");
#define LOG_NG LOG_STREAM(info, log_engine)
#define ERR_NG LOG_STREAM(err, log_engine)
static lg::log_domain log_config("config");
#define ERR_CF LOG_STREAM(err, log_config)
static lg::log_domain log_lobby("lobby");
#define DBG_LB LOG_STREAM(debug, log_lobby)
#define LOG_LB LOG_STREAM(info, log_lobby)
#define ERR_LB LOG_STREAM(err, log_lobby)
#define SCOPE_LB log_scope2(log_lobby, __func__)
namespace
{
void add_label_data(std::map<std::string, string_map>& map,
const std::string& key,
const std::string& label)
{
string_map item;
item["label"] = label;
item["use_markup"] = "true";
map.emplace(key, item);
}
}
namespace gui2
{
// ------------ WIDGET -----------{
REGISTER_WIDGET(mp_chatbox)
tmp_chatbox::tmp_chatbox()
: tcontainer_(1)
, roomlistbox_(nullptr)
, chat_log_container_(nullptr)
, chat_input_(nullptr)
, active_window_(0)
, active_window_changed_callback_()
, lobby_info_(nullptr)
{
}
void tmp_chatbox::active_window_changed()
{
tlobby_chat_window& t = open_windows_[active_window_];
// clear pending messages notification in room listbox
tgrid* grid = roomlistbox_->get_row_grid(active_window_);
find_widget<timage>(grid, "pending_messages", false).set_visible(twidget::tvisible::hidden);
t.pending_messages = 0;
if (active_window_changed_callback_) {
active_window_changed_callback_();
}
}
void tmp_chatbox::switch_to_window(tlobby_chat_window* t)
{
switch_to_window(t - &open_windows_[0]);
}
void tmp_chatbox::switch_to_window(size_t id)
{
active_window_ = id;
assert(active_window_ < open_windows_.size());
chat_log_container_->select_page(active_window_);
roomlistbox_->select_row(active_window_);
active_window_changed();
}
void tmp_chatbox::finalize_setup()
{
roomlistbox_ = find_widget<tlistbox>(this, "room_list", false, true);
chat_log_container_ = find_widget<tmulti_page>(this, "chat_log_container", false, true);
chat_input_ = find_widget<ttext_box>(this, "chat_input", false, true);
roomlistbox_->set_callback_value_change(
[this](twidget&) { switch_to_window(roomlistbox_->get_selected_row()); });
connect_signal_pre_key_press(
*chat_input_,
std::bind(&tmp_chatbox::chat_input_keypress_callback,
this,
_3,
_4,
_5));
}
void tmp_chatbox::send_message_button_callback()
{
const std::string& input = chat_input_->get_value();
if (input.empty())
return;
if (input[0] == '/') {
// TODO: refactor do_speak so it uses context information about
// opened window, so e.g. /ignore in a whisper session ignores
// the other party without having to specify it's nick.
//chat_handler::do_speak(input);
}
else {
tlobby_chat_window& t = open_windows_[active_window_];
if (t.whisper) {
send_whisper(t.name, input);
add_whisper_sent(t.name, input);
}
else {
send_chat_room_message(t.name, input);
add_chat_room_message_sent(t.name, input);
}
}
chat_input_->save_to_history();
chat_input_->set_value("");
}
void tmp_chatbox::chat_input_keypress_callback(bool& handled, bool& halt, const SDLKey key)
{
if (key == SDLK_RETURN || key == SDLK_KP_ENTER) {
send_message_button_callback();
handled = true;
halt = true;
}
else if (key == SDLK_TAB) {
std::string text = chat_input_->get_value();
std::vector<std::string> matches;
for (const auto & ui : lobby_info().users())
{
if (ui.name != preferences::login()) {
matches.push_back(ui.name);
}
}
const bool line_start = utils::word_completion(text, matches);
if (matches.empty()) {
return;
}
if (matches.size() == 1) {
text.append(line_start ? ": " : " ");
}
else {
std::string completion_list = utils::join(matches, " ");
append_to_chatbox(completion_list);
}
chat_input_->set_value(text);
handled = true;
halt = true;
}
}
void tmp_chatbox::append_to_chatbox(const std::string& text, const bool force_scroll)
{
append_to_chatbox(text, active_window_, force_scroll);
}
void tmp_chatbox::append_to_chatbox(const std::string& text, size_t id, const bool force_scroll)
{
tgrid& grid = chat_log_container_->page_grid(id);
tscroll_label& log = find_widget<tscroll_label>(&grid, "log_text", false);
const bool chatbox_at_end = log.vertical_scrollbar_at_end();
const unsigned chatbox_position = log.get_vertical_scrollbar_item_position();
log.set_use_markup(true);
log.set_label(log.label() + "\n" +
"<span color='#bcb088'>" + preferences::get_chat_timestamp(time(0)) + text + "</span>");
if (chatbox_at_end || force_scroll) {
log.scroll_vertical_scrollbar(tscrollbar_::END);
}
else {
log.set_vertical_scrollbar_item_position(chatbox_position);
}
}
void tmp_chatbox::set_active(const bool /*active*/)
{
/* DO NOTHING */
}
bool tmp_chatbox::get_active() const
{
return true;
}
const std::string& tmp_chatbox::get_control_type() const
{
static const std::string type = "mp_chatbox";
return type;
}
void tmp_chatbox::set_self_active(const bool /*active*/)
{
/* DO NOTHING */
}
void tmp_chatbox::send_chat_message(const std::string& message,
bool /*allies_only*/)
{
::config c = config_of("message", config_of("message", message)("sender", preferences::login()));
add_chat_message(time(nullptr), preferences::login(), 0, message); // local
// echo
lobby_info().wesnothd_connection().send_data(c);
}
void tmp_chatbox::user_relation_changed(const std::string& /*name*/)
{
if (active_window_changed_callback_) {
active_window_changed_callback_();
}
}
void tmp_chatbox::add_chat_message(const time_t& /*time*/,
const std::string& speaker,
int /*side*/,
const std::string& message,
events::chat_handler::MESSAGE_TYPE /*type*/)
{
std::stringstream ss;
ss << "<b>" << speaker << ":</b> ";
ss << font::escape_text(message);
append_to_chatbox(ss.str());
}
void tmp_chatbox::add_whisper_sent(const std::string& receiver,
const std::string& message)
{
if (whisper_window_active(receiver)) {
add_active_window_message(preferences::login(), message, true);
}
else if (tlobby_chat_window* t = whisper_window_open(
receiver, preferences::auto_open_whisper_windows())) {
switch_to_window(t);
add_active_window_message(preferences::login(), message, true);
}
else {
utils::string_map symbols;
symbols["receiver"] = receiver;
add_active_window_whisper(VGETTEXT("whisper to $receiver", symbols),
message, true);
}
lobby_info().get_whisper_log(receiver)
.add_message(preferences::login(), message);
}
void tmp_chatbox::add_whisper_received(const std::string& sender,
const std::string& message)
{
bool can_go_to_active = !preferences::whisper_friends_only()
|| preferences::is_friend(sender);
bool can_open_new = preferences::auto_open_whisper_windows()
&& can_go_to_active;
lobby_info().get_whisper_log(sender).add_message(sender, message);
if (whisper_window_open(sender, can_open_new)) {
if (whisper_window_active(sender)) {
add_active_window_message(sender, message);
do_notify(NOTIFY_WHISPER, sender, message);
}
else {
add_whisper_window_whisper(sender, message);
increment_waiting_whsipers(sender);
do_notify(NOTIFY_WHISPER_OTHER_WINDOW, sender, message);
}
}
else if (can_go_to_active) {
add_active_window_whisper(sender, message);
do_notify(NOTIFY_WHISPER, sender, message);
}
else {
LOG_LB << "Ignoring whisper from " << sender << "\n";
}
}
void tmp_chatbox::add_chat_room_message_sent(const std::string& room,
const std::string& message)
{
// do not open room window here, player should be in the room before sending
// messages yo it should be allowed to happen
if (tlobby_chat_window* t = room_window_open(room, false)) {
room_info* ri = lobby_info().get_room(room);
assert(ri);
if (!room_window_active(room)) {
switch_to_window(t);
}
ri->log().add_message(preferences::login(), message);
add_active_window_message(preferences::login(), message, true);
}
else {
LOG_LB << "Cannot add sent message to ui for room " << room
<< ", player not in the room\n";
}
}
void tmp_chatbox::add_chat_room_message_received(const std::string& room,
const std::string& speaker,
const std::string& message)
{
if (room_info* ri = lobby_info().get_room(room)) {
t_notify_mode notify_mode = NOTIFY_NONE;
ri->log().add_message(speaker, message);
if (room_window_active(room)) {
add_active_window_message(speaker, message);
notify_mode = NOTIFY_MESSAGE;
}
else {
add_room_window_message(room, speaker, message);
increment_waiting_messages(room);
notify_mode = NOTIFY_MESSAGE_OTHER_WINDOW;
}
if (speaker == "server") {
notify_mode = NOTIFY_SERVER_MESSAGE;
}
else if (utils::word_match(message, preferences::login())) {
notify_mode = NOTIFY_OWN_NICK;
}
else if (preferences::is_friend(speaker)) {
notify_mode = NOTIFY_FRIEND_MESSAGE;
}
do_notify(notify_mode, speaker, message);
}
else {
LOG_LB << "Discarding message to room " << room << " from " << speaker
<< " (room not open)\n";
}
}
bool tmp_chatbox::whisper_window_active(const std::string& name)
{
const tlobby_chat_window& t = open_windows_[active_window_];
return t.name == name && t.whisper == true;
}
bool tmp_chatbox::room_window_active(const std::string& room)
{
const tlobby_chat_window& t = open_windows_[active_window_];
return t.name == room && t.whisper == false;
}
tlobby_chat_window* tmp_chatbox::room_window_open(const std::string& room,
bool open_new)
{
return search_create_window(room, false, open_new);
}
tlobby_chat_window* tmp_chatbox::whisper_window_open(const std::string& name,
bool open_new)
{
return search_create_window(name, true, open_new);
}
tlobby_chat_window* tmp_chatbox::search_create_window(const std::string& name,
bool whisper,
bool open_new)
{
for (auto & t : open_windows_) {
if (t.name == name && t.whisper == whisper)
return &t;
}
if (open_new) {
open_windows_.push_back(tlobby_chat_window(name, whisper));
std::map<std::string, string_map> data;
utils::string_map symbols;
symbols["name"] = name;
if (whisper) {
add_label_data(data, "log_text",
VGETTEXT("Whisper session with $name started. "
"If you do not want to receive messages "
"from this user, "
"type /ignore $name\n",
symbols));
}
else {
add_label_data(data, "log_text", VGETTEXT("<i>Room $name joined</i>", symbols));
lobby_info().open_room(name);
}
chat_log_container_->add_page(data);
std::map<std::string, string_map> data2;
add_label_data(data2, "room", whisper ? font::escape_text("<" + name + ">") : name);
tgrid* row_grid = &roomlistbox_->add_row(data2);
tbutton& close_button = find_widget<tbutton>(row_grid, "close_window", false);
connect_signal_mouse_left_click(close_button,
std::bind(&tmp_chatbox::close_window_button_callback, this, open_windows_.back(),
_3, _4));
if (name == "lobby") {
close_button.set_visible(tcontrol::tvisible::hidden);
}
return &open_windows_.back();
}
return nullptr;
}
void tmp_chatbox::close_window_button_callback(tlobby_chat_window& chat_window, bool& handled, bool& halt)
{
const int index = std::find_if(open_windows_.begin(), open_windows_.end(), [&chat_window](const tlobby_chat_window& room) {
return room.name == chat_window.name;
}) - open_windows_.begin();
close_window(index);
handled = true;
halt = true;
}
void tmp_chatbox::send_to_server(const ::config& cfg)
{
lobby_info().wesnothd_connection().send_data(cfg);
}
void tmp_chatbox::increment_waiting_whsipers(const std::string& name)
{
if (tlobby_chat_window* t = whisper_window_open(name, false)) {
t->pending_messages++;
if (t->pending_messages == 1) {
DBG_LB << "do whisper pending mark row " << (t - &open_windows_[0])
<< " with " << t->name << "\n";
tgrid* grid = roomlistbox_->get_row_grid(t - &open_windows_[0]);
// this breaks for some reason
// tlabel& label = grid->get_widget<tlabel>("room", false);
// label.set_use_markup(true);
// label.set_label(colorize("<" + t->name + ">", "red"));
find_widget<timage>(grid, "pending_messages", false)
.set_visible(twidget::tvisible::visible);
}
}
}
void tmp_chatbox::increment_waiting_messages(const std::string& room)
{
if (tlobby_chat_window* t = room_window_open(room, false)) {
t->pending_messages++;
if (t->pending_messages == 1) {
int idx = t - &open_windows_[0];
DBG_LB << "do room pending mark row " << idx << " with " << t->name
<< "\n";
tgrid* grid = roomlistbox_->get_row_grid(idx);
// this breaks for some reason
// tlabel& label = grid->get_widget<tlabel>("room", false);
// label.set_use_markup(true);
// label.set_label(colorize(t->name, "red"));
find_widget<timage>(grid, "pending_messages", false)
.set_visible(twidget::tvisible::visible);
}
}
}
void tmp_chatbox::add_whisper_window_whisper(const std::string& sender,
const std::string& message)
{
std::stringstream ss;
ss << "<b>" << sender << ":</b> " << font::escape_text(message);
tlobby_chat_window* t = whisper_window_open(sender, false);
if (!t) {
ERR_LB << "Whisper window not open in add_whisper_window_whisper for "
<< sender << "\n";
return;
}
append_to_chatbox(ss.str(), t - &open_windows_[0], false);
}
void tmp_chatbox::add_active_window_whisper(const std::string& sender,
const std::string& message,
const bool force_scroll)
{
std::stringstream ss;
ss << "<b>"
<< "whisper: " << sender << ":</b> " << font::escape_text(message);
append_to_chatbox(ss.str(), force_scroll);
}
void tmp_chatbox::close_window(size_t idx)
{
const tlobby_chat_window& t = open_windows_[idx];
bool active_changed = idx == active_window_;
DBG_LB << "Close window " << idx << " - " << t.name << "\n";
if ((t.name == "lobby" && t.whisper == false) || open_windows_.size() == 1) {
return;
}
if (t.whisper == false) {
// closing a room window -- send a part to the server
::config data, msg;
msg["room"] = t.name;
msg["player"] = preferences::login();
data.add_child("room_part", msg);
send_to_server(data);
}
if (active_window_ == open_windows_.size() - 1) {
active_window_--;
}
if (t.whisper) {
lobby_info().get_whisper_log(t.name).clear();
}
else {
lobby_info().close_room(t.name);
}
open_windows_.erase(open_windows_.begin() + idx);
roomlistbox_->remove_row(idx);
roomlistbox_->select_row(active_window_);
chat_log_container_->remove_page(idx);
chat_log_container_->select_page(active_window_);
if (active_changed)
active_window_changed();
}
void tmp_chatbox::add_room_window_message(const std::string& room,
const std::string& sender,
const std::string& message)
{
std::stringstream ss;
ss << "<b>" << sender << ":</b> " << font::escape_text(message);
tlobby_chat_window* t = room_window_open(room, false);
if (!t) {
ERR_LB << "Room window not open in add_room_window_message for " << room
<< "\n";
return;
}
append_to_chatbox(ss.str(), t - &open_windows_[0], false);
}
void tmp_chatbox::add_active_window_message(const std::string& sender,
const std::string& message,
const bool force_scroll)
{
std::stringstream ss;
ss << "<b>" << sender << ":</b> " << font::escape_text(message);
append_to_chatbox(ss.str(), force_scroll);
}
room_info* tmp_chatbox::active_window_room()
{
const tlobby_chat_window& t = open_windows_[active_window_];
if (t.whisper)
return nullptr;
return lobby_info().get_room(t.name);
}
void tmp_chatbox::process_room_join(const ::config& data)
{
const std::string& room = data["room"];
const std::string& player = data["player"];
room_info* r = lobby_info().get_room(room);
DBG_LB << "room join: " << room << " " << player << " "
<< static_cast<void*>(r) << "\n";
if (r) {
if (player == preferences::login()) {
if (const auto& members = data.child("members")) {
r->process_room_members(members);
}
}
else {
r->add_member(player);
/* TODO: add/use preference */
utils::string_map symbols;
symbols["player"] = player;
add_room_window_message(
room,
"server",
VGETTEXT("$player has entered the room", symbols));
}
if (r == active_window_room()) {
active_window_changed_callback_();
}
}
else {
if (player == preferences::login()) {
tlobby_chat_window* t = room_window_open(room, true);
lobby_info().open_room(room);
r = lobby_info().get_room(room);
assert(r);
if (const auto& members = data.child("members")) {
r->process_room_members(members);
}
switch_to_window(t);
const std::string& topic = data["topic"];
if (!topic.empty()) {
add_chat_room_message_received(
"room", "server", room + ": " + topic);
}
}
else {
LOG_LB << "Discarding join info for a room the player is not in\n";
}
}
}
void tmp_chatbox::process_room_part(const ::config& data)
{
// todo close room window when the part message is sent
const std::string& room = data["room"];
const std::string& player = data["player"];
DBG_LB << "Room part: " << room << " " << player << "\n";
room_info* r = lobby_info().get_room(room);
if (r) {
r->remove_member(player);
/* TODO: add/use preference */
utils::string_map symbols;
symbols["player"] = player;
add_room_window_message(
room, "server", VGETTEXT("$player has left the room", symbols));
if (active_window_room() == r) {
active_window_changed_callback_();
}
}
else {
LOG_LB << "Discarding part info for a room the player is not in\n";
}
}
void tmp_chatbox::process_room_query_response(const ::config& data)
{
const std::string& room = data["room"];
const std::string& message = data["message"];
DBG_LB << "room query response: " << room << " " << message << "\n";
if (room.empty()) {
if (!message.empty()) {
add_active_window_message("server", message);
}
if (const ::config& rooms = data.child("rooms")) {
// TODO: this should really open a nice join room dialog instead
std::stringstream ss;
ss << "Rooms:";
for (const auto & r : rooms.child_range("room"))
{
ss << " " << r["name"];
}
add_active_window_message("server", ss.str());
}
}
else {
if (room_window_open(room, false)) {
if (!message.empty()) {
add_chat_room_message_received(room, "server", message);
}
if (const ::config& members = data.child("members")) {
room_info* r = lobby_info().get_room(room);
assert(r);
r->process_room_members(members);
if (r == active_window_room()) {
active_window_changed_callback_();
}
}
}
else {
if (!message.empty()) {
add_active_window_message("server", room + ": " + message);
}
}
}
}
void tmp_chatbox::process_message(const ::config& data, bool whisper /*= false*/)
{
std::string sender = data["sender"];
DBG_LB << "process message from " << sender << " " << (whisper ? "(w)" : "")
<< ", len " << data["message"].str().size() << '\n';
if (preferences::is_ignored(sender))
return;
const std::string& message = data["message"];
preferences::parse_admin_authentication(sender, message);
if (whisper) {
add_whisper_received(sender, message);
}
else {
std::string room = data["room"];
if (room.empty()) {
LOG_LB << "Message without a room from " << sender
<< ", assuming lobby\n";
room = "lobby";
}
add_chat_room_message_received(room, sender, message);
}
}
bool tmp_chatbox::process_network_data(const ::config& data)
{
if (const ::config& c = data.child("message")) {
process_message(c);
}
else if (const ::config& c = data.child("whisper")) {
process_message(c, true);
}
else if (const ::config& c = data.child("room_join")) {
process_room_join(c);
}
else if (const ::config& c = data.child("room_part")) {
process_room_part(c);
}
else if (const ::config& c = data.child("room_query_response")) {
process_room_query_response(c);
}
else {
return false;
}
return true;
}
// }---------- DEFINITION ---------{
tmp_chatbox_definition::tmp_chatbox_definition(const config& cfg)
: tcontrol_definition(cfg)
{
load_resolutions<tresolution>(cfg);
}
tmp_chatbox_definition::tresolution::tresolution(const config& cfg)
: tresolution_definition_(cfg), grid()
{
state.push_back(tstate_definition(cfg.child("background")));
state.push_back(tstate_definition(cfg.child("foreground")));
const config& child = cfg.child("grid");
VALIDATE(child, _("No grid defined."));
grid = std::make_shared<tbuilder_grid>(child);
}
// }---------- BUILDER -----------{
namespace implementation
{
tbuilder_mp_chatbox::tbuilder_mp_chatbox(const config& cfg)
: tbuilder_control(cfg)
{
}
twidget* tbuilder_mp_chatbox::build() const
{
tmp_chatbox* widget = new tmp_chatbox();
init_control(widget);
DBG_GUI_G << "Window builder: placed unit preview pane '" << id
<< "' with definition '" << definition << "'.\n";
std::shared_ptr<const tmp_chatbox_definition::tresolution> conf
= std::static_pointer_cast<
const tmp_chatbox_definition::tresolution>(widget->config());
assert(conf);
widget->init_grid(conf->grid);
widget->finalize_setup();
return widget;
}
} // namespace implementation
// }------------ END --------------
} // namespace gui2

View file

@ -0,0 +1,290 @@
/*
Copyright (C) 2016 by the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include "gui/widgets/container.hpp"
#include "gui/dialogs/lobby/data.hpp"
#include "gui/dialogs/lobby/info.hpp"
#include "chat_events.hpp"
#include <string>
class config;
namespace gui2
{
// ------------ WIDGET -----------{
class tbutton;
class tlistbox;
class tlabel;
class tmulti_page;
class ttext_box;
namespace implementation
{
struct tbuilder_mp_chatbox;
}
struct tlobby_chat_window
{
tlobby_chat_window(const std::string& name, bool whisper)
: name(name), whisper(whisper), pending_messages(0)
{
}
std::string name;
bool whisper;
int pending_messages;
};
class tmp_chatbox : public tcontainer_, public events::chat_handler
{
friend struct implementation::tbuilder_mp_chatbox;
public:
tmp_chatbox();
/** See @ref tcontrol::set_active. */
virtual void set_active(const bool active) override;
/** See @ref tcontrol::get_active. */
virtual bool get_active() const override;
/** See @ref tcontrol::get_state. */
virtual unsigned get_state() const override { return 0; };
void send_to_server(const ::config& cfg) override;
void set_active_window_changed_callback(const std::function<void(void)>& f) { active_window_changed_callback_ = f; }
void set_lobby_info(lobby_info& i) { lobby_info_ = &i; }
protected:
/**
* Initializes the interneral sub-widget pointers.
* Should be called when building the window, so the pointers
* are initilized when set_displayed_type() is called.
*/
void finalize_setup();
virtual void user_relation_changed(const std::string& name) override;
/** inherited form chat_handler */
virtual void add_chat_message(const time_t& time,
const std::string& speaker,
int side,
const std::string& message,
events::chat_handler::MESSAGE_TYPE type
= events::chat_handler::MESSAGE_PRIVATE) override;
/** inherited form chat_handler */
virtual void add_whisper_sent(const std::string& receiver,
const std::string& message) override;
/** inherited form chat_handler */
virtual void add_whisper_received(const std::string& sender,
const std::string& message) override;
/** inherited form chat_handler */
virtual void add_chat_room_message_sent(const std::string& room,
const std::string& message) override;
/** inherited form chat_handler */
virtual void add_chat_room_message_received(const std::string& room,
const std::string& speaker,
const std::string& message) override;
private:
tlistbox* roomlistbox_;
tmulti_page* chat_log_container_;
ttext_box* chat_input_;
std::vector<tlobby_chat_window> open_windows_;
size_t active_window_;
std::function<void(void)> active_window_changed_callback_;
lobby_info* lobby_info_;
lobby_info& lobby_info() { return *lobby_info_; }
/** See @ref tcontrol::get_control_type. */
virtual const std::string& get_control_type() const override;
/** See @ref tcontainer_::set_self_active. */
virtual void set_self_active(const bool active) override;
void chat_input_keypress_callback(bool& handled, bool& halt, const SDLKey key);
void append_to_chatbox(const std::string& text, const bool force_scroll = false);
void append_to_chatbox(const std::string& text, size_t id, const bool force_scroll = false);
/**
* @return true if the whisper window for "name" is the active window
*/
bool whisper_window_active(const std::string& name);
/**
* @return true if the room window for "room" is the active window
*/
bool room_window_active(const std::string& room);
/**
* Mark the whisper window for "name" as having one more pending message
*/
void increment_waiting_whsipers(const std::string& name);
/**
* Mark the room window for "room" as having one more pending message
*/
void increment_waiting_messages(const std::string& room);
/**
* Add a whisper message to the whisper window
*/
void add_whisper_window_whisper(const std::string& sender,
const std::string& message);
/**
* Add a whisper message to the current window which is not the whisper
* window
* for "name".
*/
void add_active_window_whisper(const std::string& sender,
const std::string& message,
const bool force_scroll = false);
/**
* Add a message to the window for room "room"
*/
void add_room_window_message(const std::string& room,
const std::string& sender,
const std::string& message);
/**
* Add a message to the window for room "room"
*/
void add_active_window_message(const std::string& sender,
const std::string& message,
const bool force_scroll = false);
void close_window(size_t idx);
void send_message_button_callback();
public:
/** inherited form chat_handler */
virtual void send_chat_message(const std::string& message,
bool /*allies_only*/) override;
/**
* Switch to the window given by a valid pointer (e.g. received from a call
* to *_window_open)
*/
void switch_to_window(tlobby_chat_window* t);
void switch_to_window(size_t id);
void active_window_changed();
/**
* Get the room* corresponding to the currently active window, or nullptr
* if a whisper window is active at the moment
*/
room_info* active_window_room();
/**
* Check if a room window for "room" is open, if open_new is true
* then it will be created if not found.
* @return valid ptr if the window was found or added, null otherwise
*/
tlobby_chat_window* room_window_open(const std::string& room,
bool open_new);
/**
* Check if a whisper window for user "name" is open, if open_new is true
* then it will be created if not found.
* @return valid ptr if the window was found or added, null otherwise
*/
tlobby_chat_window* whisper_window_open(const std::string& name,
bool open_new);
/**
* Helper function to find and open a new window, used by *_window_open
*/
tlobby_chat_window* search_create_window(const std::string& name, bool whisper, bool open_new);
void do_notify(t_notify_mode mode) { do_notify(mode, "", ""); }
void do_notify(t_notify_mode mode, const std::string & sender, const std::string & message) { do_mp_notify(mode, sender, message); }
void close_window_button_callback(tlobby_chat_window& chat_window, bool& handled, bool& halt);
void process_room_join(const ::config& data);
void process_room_part(const ::config& data);
void process_room_query_response(const ::config& data);
void process_message(const ::config& data, bool whisper = false);
bool process_network_data(const ::config& data);
};
// }---------- DEFINITION ---------{
struct tmp_chatbox_definition : public tcontrol_definition
{
explicit tmp_chatbox_definition(const config& cfg);
struct tresolution : public tresolution_definition_
{
explicit tresolution(const config& cfg);
tbuilder_grid_ptr grid;
};
};
// }---------- BUILDER -----------{
namespace implementation
{
struct tbuilder_mp_chatbox : public tbuilder_control
{
public:
explicit tbuilder_mp_chatbox(const config& cfg);
using tbuilder_control::build;
twidget* build() const;
private:
};
} // namespace implementation
// }------------ END --------------
} // namespace gui2