The Battle for Wesnoth  1.15.2+dev
display_chat_manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "display_chat_manager.hpp"
16 
18 #include "display.hpp"
19 #include "floating_label.hpp"
20 #include "game_board.hpp" // <-- only needed for is_observer()
21 #include "preferences/game.hpp"
22 #include "log.hpp"
23 #include "font/marked-up_text.hpp"
24 #include "mp_ui_alerts.hpp"
26 #include "color.hpp"
29 
30 #include <SDL2/SDL_timer.h>
31 
32 static lg::log_domain log_engine("engine");
33 #define ERR_NG LOG_STREAM(err, log_engine)
34 
35 namespace {
36  const int chat_message_border = 5;
37  const int chat_message_x = 10;
38  const color_t chat_message_color {255,255,255,SDL_ALPHA_OPAQUE};
39  const color_t chat_message_bg {0,0,0,140};
40 }
41 
43  : speaker_handle(speaker), handle(h), created_at(SDL_GetTicks())
44 {}
45 
46 
47 void display_chat_manager::add_chat_message(const std::time_t& time, const std::string& speaker,
48  int side, const std::string& message, events::chat_handler::MESSAGE_TYPE type,
49  bool bell)
50 {
51  const bool whisper = speaker.find("whisper: ") == 0;
52  std::string sender = speaker;
53  if (whisper) {
54  sender.assign(speaker, 9, speaker.size());
55  add_whisperer( sender );
56  }
57  //remove disconnected user from whisperer
58  std::string::size_type pos = message.find(" has disconnected");
59  if (pos != std::string::npos){
60  for(std::set<std::string>::const_iterator w = whisperers().begin(); w != whisperers().end(); ++w){
61  if (*w == message.substr(0,pos)) remove_whisperer(*w);
62  }
63  }
64 
65  if (!preferences::parse_should_show_lobby_join(sender, message)) return;
66  if (preferences::is_ignored(sender)) return;
67 
69 
70  bool is_observer = false;
71  { //TODO: Clean this block up somehow
72 
73  const game_board * board = dynamic_cast<const game_board*>(&my_disp_.get_disp_context());
74 
75  if (board) {
76  is_observer = board->is_observer();
77  }
78  }
79 
80  if (bell) {
81  if ((type == events::chat_handler::MESSAGE_PRIVATE && (!is_observer || whisper))
82  || utils::word_match(message, preferences::login())) {
83  mp_ui_alerts::private_message(false, sender, message);
84  } else if (preferences::is_friend(sender)) {
85  mp_ui_alerts::friend_message(false, sender, message);
86  } else if (sender == "server") {
87  mp_ui_alerts::server_message(false, sender, message);
88  } else {
89  mp_ui_alerts::public_message(false, sender, message);
90  }
91  }
92 
93  bool action = false;
94 
95  std::string msg;
96 
97  if (message.compare(0,4,"/me ") == 0) {
98  msg.assign(message, 4, message.size());
99  action = true;
100  } else {
101  msg = message;
102  }
103 
104  try {
105  // We've had a joker who send an invalid utf-8 message to crash clients
106  // so now catch the exception and ignore the message.
108  } catch (utf8::invalid_utf8_exception&) {
109  ERR_NG << "Invalid utf-8 found, chat message is ignored." << std::endl;
110  return;
111  }
112 
113  int ypos = chat_message_x;
114  for(std::vector<chat_message>::const_iterator m = chat_messages_.begin(); m != chat_messages_.end(); ++m) {
115  ypos += std::max(font::get_floating_label_rect(m->handle).h,
116  font::get_floating_label_rect(m->speaker_handle).h);
117  }
118  color_t speaker_color {255,255,255,SDL_ALPHA_OPAQUE};
119  if(side >= 1) {
120  speaker_color = team::get_side_color_range(side).mid();
121  }
122 
123  color_t message_color = chat_message_color;
124  std::stringstream str;
125  std::stringstream message_str;
126 
128  if(action) {
129  str << "<" << speaker << " " << msg << ">";
130  message_color = speaker_color;
131  message_str << " ";
132  } else {
133  if (!speaker.empty())
134  str << "<" << speaker << ">";
135  message_str << msg;
136  }
137  } else {
138  if(action) {
139  str << "*" << speaker << " " << msg << "*";
140  message_color = speaker_color;
141  message_str << " ";
142  } else {
143  if (!speaker.empty())
144  str << "*" << speaker << "*";
145  message_str << msg;
146  }
147  }
148 
149  // Prepend message with timestamp.
150  std::stringstream message_complete;
151  message_complete << preferences::get_chat_timestamp(time) << str.str();
152 
153  const SDL_Rect rect = my_disp_.map_outside_area();
154 
155  font::floating_label spk_flabel(message_complete.str());
156  spk_flabel.set_font_size(font::SIZE_15);
157  spk_flabel.set_color(speaker_color);
158  spk_flabel.set_position(rect.x + chat_message_x, rect.y + ypos);
159  spk_flabel.set_clip_rect(rect);
160  spk_flabel.set_alignment(font::LEFT_ALIGN);
161  spk_flabel.set_bg_color(chat_message_bg);
162  spk_flabel.set_border_size(chat_message_border);
163  spk_flabel.use_markup(false);
164 
165  int speaker_handle = font::add_floating_label(spk_flabel);
166 
167  font::floating_label msg_flabel(message_str.str());
168  msg_flabel.set_font_size(font::SIZE_15);
169  msg_flabel.set_color(message_color);
170  msg_flabel.set_position(rect.x + chat_message_x + font::get_floating_label_rect(speaker_handle).w,
171  rect.y + ypos);
172  msg_flabel.set_clip_rect(rect);
173  msg_flabel.set_alignment(font::LEFT_ALIGN);
174  msg_flabel.set_bg_color(chat_message_bg);
175  msg_flabel.set_border_size(chat_message_border);
176  msg_flabel.use_markup(false);
177 
178  int message_handle = font::add_floating_label(msg_flabel);
179 
180  chat_messages_.emplace_back(speaker_handle,message_handle);
181 
183 }
184 
185 static unsigned int safe_subtract(unsigned int a, unsigned int b)
186 {
187  return (a > b) ? a - b : 0;
188 }
189 
191 {
192  //NOTE: prune_chat_messages(false) seems to be only called when a new message is added, which in
193  // particular means the aging feature won't work unless new messages are addded regularly
194  const unsigned message_aging = preferences::chat_message_aging();
195  const unsigned max_chat_messages = preferences::chat_lines();
196  const bool enable_aging = message_aging != 0;
197 
198  const unsigned remove_before = enable_aging ? safe_subtract(SDL_GetTicks(), message_aging * 60 * 1000) : 0;
199  int movement = 0;
200 
201  if(enable_aging || remove_all || chat_messages_.size() > max_chat_messages) {
202  while (!chat_messages_.empty() &&
203  (remove_all ||
204  chat_messages_.front().created_at < remove_before ||
205  chat_messages_.size() > max_chat_messages))
206  {
207  const chat_message &old = chat_messages_.front();
208  movement += font::get_floating_label_rect(old.handle).h;
211  chat_messages_.erase(chat_messages_.begin());
212  }
213  }
214 
215  for(const chat_message &cm : chat_messages_) {
216  font::move_floating_label(cm.speaker_handle, 0, - movement);
217  font::move_floating_label(cm.handle, 0, - movement);
218  }
219 }
Game board class.
Definition: game_board.hpp:50
static unsigned int safe_subtract(unsigned int a, unsigned int b)
const std::set< std::string > & whisperers() const
#define a
static const color_range get_side_color_range(int side)
Definition: team.cpp:936
void remove_floating_label(int handle)
removes the floating label given by &#39;handle&#39; from the screen
void parse_admin_authentication(const std::string &sender, const std::string &message)
Definition: game.cpp:185
std::string word_wrap_text(const std::string &unwrapped_text, int font_size, int max_width, int max_height, int max_lines, bool partial_line)
Wrap text.
int chat_lines()
Definition: game.cpp:912
#define h
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
void set_font_size(int font_size)
std::vector< chat_message > chat_messages_
void add_chat_message(const std::time_t &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
void move_floating_label(int handle, double xmove, double ymove)
moves the floating label given by &#39;handle&#39; by (xmove,ymove)
#define b
void friend_message(bool is_lobby, const std::string &sender, const std::string &message)
bool is_friend(const std::string &nick)
Definition: game.cpp:303
void add_whisperer(const std::string &nick)
const int SIZE_15
Definition: constants.cpp:25
void private_message(bool is_lobby, const std::string &sender, const std::string &message)
#define ERR_NG
map_display and display: classes which take care of displaying the map and game-data on the screen...
void prune_chat_messages(bool remove_all=false)
void remove_whisperer(const std::string &nick)
bool is_ignored(const std::string &nick)
Definition: game.cpp:316
std::string login()
bool faked() const
Definition: video.hpp:60
static lg::log_domain log_engine("engine")
Thrown by operations encountering invalid UTF-8 data.
bool is_observer() const
Check if we are an observer in this game.
void public_message(bool is_lobby, const std::string &sender, const std::string &message)
const SDL_Rect & map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.hpp:237
const display_context & get_disp_context() const
Definition: display.hpp:168
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
SDL_Rect get_floating_label_rect(int handle)
void server_message(bool is_lobby, const std::string &sender, const std::string &message)
int w
std::string get_chat_timestamp(const std::time_t &t)
Definition: game.cpp:892
int chat_message_aging()
Definition: game.cpp:927
Standard logging facilities (interface).
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:196
bool parse_should_show_lobby_join(const std::string &sender, const std::string &message)
Definition: game.cpp:342
std::shared_ptr< halo_record > handle
Definition: halo.hpp:31
color_t mid() const
Average color shade.
Definition: color_range.hpp:87
bool word_match(const std::string &message, const std::string &word)
Check if a message contains a word.