The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
chat_log.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2016 by Yurii Chernyi <[email protected]>
3  Part of the Battle for Wesnoth Project http://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 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "gui/dialogs/chat_log.hpp"
18 
20 #include "gui/dialogs/helper.hpp"
21 #include "gui/widgets/button.hpp"
22 #ifdef GUI2_EXPERIMENTAL_LISTBOX
23 #include "gui/widgets/list.hpp"
24 #else
25 #include "gui/widgets/listbox.hpp"
26 #endif
27 #include "gui/widgets/settings.hpp"
28 #include "gui/widgets/text_box.hpp"
29 #include "gui/widgets/window.hpp"
30 #include "gui/widgets/slider.hpp"
31 
32 #include "desktop/clipboard.hpp"
34 #include "game_preferences.hpp"
35 #include "log.hpp"
36 #include "resources.hpp"
37 #include "replay.hpp"
38 #include "gettext.hpp"
39 
40 #include <vector>
41 #include "utils/functional.hpp"
42 #include <boost/shared_ptr.hpp>
43 
44 static lg::log_domain log_chat_log("chat_log");
45 #define DBG_CHAT_LOG LOG_STREAM(debug, log_chat_log)
46 #define LOG_CHAT_LOG LOG_STREAM(info, log_chat_log)
47 #define WRN_CHAT_LOG LOG_STREAM(warn, log_chat_log)
48 #define ERR_CHAT_LOG LOG_STREAM(err, log_chat_log)
49 
50 namespace gui2
51 {
52 
53 /*WIKI
54  * @page = GUIWindowDefinitionWML
55  * @order = 3_chat_log
56  *
57  * == Settings manager ==
58  *
59  * This shows the settings manager
60  *
61  */
62 
63 
65 
66 // The model is an interface defining the data to be displayed or otherwise
67 // acted upon in the user interface.
69 {
70 public:
71  model(const vconfig& c, replay* r)
72  : cfg(c)
73  , msg_label(nullptr)
74  , chat_log_history(r->build_chat_log())
75  , page(0)
76  , page_number()
77  , page_label()
78  , previous_page()
79  , next_page()
80  , filter()
81  , copy_button()
82  {
83  LOG_CHAT_LOG << "entering tchat_log::model...\n";
84  LOG_CHAT_LOG << "finished tchat_log::model...\n";
85  }
86 
89  const std::vector<chat_msg>& chat_log_history;
90  int page;
91  static const int COUNT_PER_PAGE = 100;
98 
100  {
101  msg_label->set_label("");
102  }
103 
105  {
106  int size = chat_log_history.size();
107  return (size % COUNT_PER_PAGE == 0) ? (size / COUNT_PER_PAGE)
108  : (size / COUNT_PER_PAGE) + 1;
109  }
110 
111  void stream_log(std::ostringstream& s,
112  int first,
113  int last,
114  bool raw = false)
115  {
116  if(first >= last) {
117  return;
118  }
119 
120  const std::string& lcfilter = utf8::lowercase(filter->get_value());
121  LOG_CHAT_LOG << "entering tchat_log::model::stream_log\n";
122 
123  for(const auto & t : make_pair(chat_log_history.begin() + first,
124  chat_log_history.begin() + last))
125  {
126  const std::string& timestamp
128 
129  if(!lcfilter.empty()) {
130  const std::string& lcsample = utf8::lowercase(timestamp)
131  + utf8::lowercase(t.nick())
132  + utf8::lowercase(t.text());
133 
134  if(lcsample.find(lcfilter) == std::string::npos) {
135  continue;
136  }
137  }
138 
139  const std::string me_prefix = "/me";
140  const bool is_me = t.text().compare(0, me_prefix.size(),
141  me_prefix) == 0;
142 
143  std::string nick_prefix, nick_suffix;
144 
145  if(!raw) {
146  nick_prefix = "<span color=\"" + t.color() + "\">";
147  nick_suffix = "</span> ";
148  } else {
149  nick_suffix = " ";
150  }
151 
152  const std::string lbracket = raw ? "<" : "&lt;";
153  const std::string rbracket = raw ? ">" : "&gt;";
154 
155  //
156  // Chat line format:
157  //
158  // is_me == true: "<[TS] nick message text here>\n"
159  // is_me == false: "<[TS] nick> message text here\n"
160  //
161 
162  s << nick_prefix << lbracket;
163 
164  if(raw) {
165  s << timestamp
166  << t.nick();
167  } else {
168  s << font::escape_text(timestamp)
169  << font::escape_text(t.nick());
170  }
171 
172  if(is_me) {
173  if(!raw) {
174  s << font::escape_text(t.text().substr(3));
175  } else {
176  s << t.text().substr(3);
177  }
178  s << rbracket << nick_suffix;
179  } else {
180  // <[TS] nick> message text here
181  s << rbracket << nick_suffix;
182  if(!raw) {
183  s << font::escape_text(t.text());
184  } else {
185  s << t.text();
186  }
187  }
188 
189  s << '\n';
190  }
191  }
192 
193  void populate_chat_message_list(int first, int last)
194  {
195  std::ostringstream s;
196  stream_log(s, first, last);
197  msg_label->set_label(s.str());
198  }
199 
201  {
202  std::ostringstream s;
203  stream_log(s, first, last, true);
205  }
206 };
207 
208 // The controller acts upon the model. It retrieves data from repositories,
209 // persists it, manipulates it, and determines how it will be displayed in the
210 // view.
212 {
213 public:
215  {
216  LOG_CHAT_LOG << "Entering tchat_log::controller" << std::endl;
217  LOG_CHAT_LOG << "Exiting tchat_log::controller" << std::endl;
218  }
219 
220  void next_page()
221  {
222  LOG_CHAT_LOG << "Entering tchat_log::controller::next_page"
223  << std::endl;
224  if(model_.page >= model_.count_of_pages() - 1) {
225  return;
226  }
227  model_.page++;
228  LOG_CHAT_LOG << "Set page to " << model_.page + 1 << std::endl;
230  LOG_CHAT_LOG << "Exiting tchat_log::controller::next_page" << std::endl;
231  }
232 
234  {
235  LOG_CHAT_LOG << "Entering tchat_log::controller::previous_page"
236  << std::endl;
237  if(model_.page == 0) {
238  return;
239  }
240  model_.page--;
241  LOG_CHAT_LOG << "Set page to " << model_.page + 1 << std::endl;
243  LOG_CHAT_LOG << "Exiting tchat_log::controller::previous_page"
244  << std::endl;
245  }
246 
247  void filter()
248  {
249  LOG_CHAT_LOG << "Entering tchat_log::controller::filter" << std::endl;
251  LOG_CHAT_LOG << "Exiting tchat_log::controller::filter" << std::endl;
252  }
253 
255  {
257  << "Entering tchat_log::controller::handle_page_number_changed"
258  << std::endl;
260  LOG_CHAT_LOG << "Set page to " << model_.page + 1 << std::endl;
263  << "Exiting tchat_log::controller::handle_page_number_changed"
264  << std::endl;
265  }
266 
267  std::pair<int, int> calculate_log_line_range()
268  {
269  const int log_size = model_.chat_log_history.size();
270  const int page_size = model_.COUNT_PER_PAGE;
271 
272  const int page = model_.page;
273  const int count_of_pages = std::max(1, model_.count_of_pages());
274 
275  LOG_CHAT_LOG << "Page: " << page + 1 << " of " << count_of_pages
276  << '\n';
277 
278  const int first = page * page_size;
279  const int last = page < (count_of_pages - 1)
280  ? first + page_size
281  : log_size;
282 
283  LOG_CHAT_LOG << "First " << first << ", last " << last << '\n';
284 
285  return std::make_pair(first, last);
286  }
287 
288  void update_view_from_model(bool select_last_page = false)
289  {
290  LOG_CHAT_LOG << "Entering tchat_log::controller::update_view_from_model"
291  << std::endl;
293  int size = model_.chat_log_history.size();
294  LOG_CHAT_LOG << "Number of chat messages: " << size << std::endl;
295  // determine count of pages
296  const int count_of_pages = std::max(1, model_.count_of_pages());
297  if(select_last_page) {
298  model_.page = count_of_pages - 1;
299  }
300  // get page
301  const int page = model_.page;
302  // determine first and last
303  const std::pair<int, int>& range = calculate_log_line_range();
304  const int first = range.first;
305  const int last = range.second;
306  // determine has previous, determine has next
307  bool has_next = page + 1 < count_of_pages;
308  bool has_previous = page > 0;
309  model_.previous_page->set_active(has_previous);
310  model_.next_page->set_active(has_next);
311  model_.populate_chat_message_list(first, last);
313  model_.page_number->set_maximum_value(count_of_pages);
314  model_.page_number->set_active(count_of_pages > 1);
315  LOG_CHAT_LOG << "Maximum value of page number slider: "
316  << count_of_pages << std::endl;
317  model_.page_number->set_value(page + 1);
318 
319  std::ostringstream cur_page_text;
320  cur_page_text << (page + 1) << '/' << std::max(1, count_of_pages);
321  model_.page_label->set_label(cur_page_text.str());
322 
323  LOG_CHAT_LOG << "Exiting tchat_log::controller::update_view_from_model"
324  << std::endl;
325  }
326 
328  {
329  const std::pair<int, int>& range = calculate_log_line_range();
330  model_.chat_message_list_to_clipboard(range.first, range.second);
331  }
332 
333 private:
335 };
336 
337 
338 // The view is an interface that displays data (the model) and routes user
339 // commands to the controller to act upon that data.
341 {
342 public:
343  view(const vconfig& cfg, replay* r) : model_(cfg, r), controller_(model_)
344  {
345  }
346 
347  void pre_show(twindow& window)
348  {
349  LOG_CHAT_LOG << "Entering tchat_log::view::pre_show" << std::endl;
351  window.invalidate_layout(); // workaround for assertion failure
352  LOG_CHAT_LOG << "Exiting tchat_log::view::pre_show" << std::endl;
353  }
354 
356  {
358  window.invalidate_layout(); // workaround for assertion failure
359  }
360 
361  void next_page(twindow& window)
362  {
364  window.invalidate_layout(); // workaround for assertion failure
365  }
366 
367  void previous_page(twindow& window)
368  {
370  window.invalidate_layout(); // workaround for assertion failure
371  }
372 
373  void filter(twindow& window)
374  {
376  window.invalidate_layout(); // workaround for assertion failure
377  }
378 
380  {
382  }
383 
384  void bind(twindow& window)
385  {
386  LOG_CHAT_LOG << "Entering tchat_log::view::bind" << std::endl;
387  model_.msg_label = &find_widget<tcontrol>(&window, "msg", false);
389  = &find_widget<tslider>(&window, "page_number", false);
393  this,
394  std::ref(window)));
395 
397  = &find_widget<tbutton>(&window, "previous_page", false);
399  std::bind(&view::previous_page, this, std::ref(window)));
400 
401  model_.next_page = &find_widget<tbutton>(&window, "next_page", false);
403  std::bind(&view::next_page, this, std::ref(window)));
404 
405  model_.filter = &find_widget<ttext_box>(&window, "filter", false);
407  std::bind(&view::filter, this, std::ref(window)));
408  window.keyboard_capture(model_.filter);
409 
410  model_.copy_button = &find_widget<tbutton>(&window, "copy", false);
414  this,
415  std::ref(window)));
417  model_.copy_button->set_active(false);
418  model_.copy_button->set_tooltip(_("Clipboard support not found, contact your packager"));
419  }
420 
421  model_.page_label = &find_widget<tcontrol>(&window, "page_label", false);
422 
423  LOG_CHAT_LOG << "Exiting tchat_log::view::bind" << std::endl;
424  }
425 
426 private:
429 };
430 
431 
432 tchat_log::tchat_log(const vconfig& cfg, replay* r) : view_()
433 {
434  LOG_CHAT_LOG << "Entering tchat_log::tchat_log" << std::endl;
435  view_ = boost::shared_ptr<view>(new view(cfg, r));
436  LOG_CHAT_LOG << "Exiting tchat_log::tchat_log" << std::endl;
437 }
438 
440 {
441  return view_;
442 }
443 
445 {
446  return build(video, window_id());
447 }
448 
450 {
451  LOG_CHAT_LOG << "Entering tchat_log::pre_show" << std::endl;
452  view_->bind(window);
453  view_->pre_show(window);
454  LOG_CHAT_LOG << "Exiting tchat_log::pre_show" << std::endl;
455 }
456 
457 } // end of namespace gui2
view(const vconfig &cfg, replay *r)
Definition: chat_log.cpp:343
virtual void set_active(const bool active) override
See tcontrol::set_active.
Definition: button.cpp:58
model(const vconfig &c, replay *r)
Definition: chat_log.cpp:71
void bind(twindow &window)
Definition: chat_log.cpp:384
boost::shared_ptr< view > get_view()
Definition: chat_log.cpp:439
this class memorizes a chat session.
Definition: data.hpp:40
bool available()
Whether wesnoth was compiled with support for a clipboard.
Definition: clipboard.cpp:61
twindow * build(CVideo &video, const twindow_builder::tresolution *definition)
Builds a window.
GLenum GLint * range
Definition: glew.h:3025
const GLfloat * c
Definition: glew.h:12741
tbutton * previous_page
Definition: chat_log.cpp:94
void next_page(twindow &window)
Definition: chat_log.cpp:361
void connect_click_handler(const event::tsignal_function &signal)
Inherited from tclickable.
Definition: button.hpp:49
void filter(twindow &window)
Definition: chat_log.cpp:373
Definition: video.hpp:58
void connect_signal_notify_modified(tdispatcher &dispatcher, const tsignal_notification_function &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.hpp:725
void handle_page_number_changed(twindow &window)
Definition: chat_log.cpp:355
This file contains the window object, this object is a top level container which has the event manage...
REGISTER_DIALOG(label_settings)
void set_minimum_value(const int minimum_value)
Inherited from tinteger_selector_.
Definition: slider.cpp:92
virtual void set_label(const t_string &label)
Definition: control.cpp:330
ttext_box * filter
Definition: chat_log.cpp:96
utf8::string lowercase(const utf8::string &s)
Returns a lowercased version of the string.
Definition: unicode.cpp:53
void connect_signal_mouse_left_click(tdispatcher &dispatcher, const tsignal_function &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.hpp:710
Replay control code.
void set_maximum_value(const int maximum_value)
Inherited from tinteger_selector_.
Definition: slider.cpp:116
void set_value(const int value)
Inherited from tinteger_selector_.
Definition: slider.cpp:77
static lg::log_domain log_chat_log("chat_log")
Class for a single line text area.
Definition: text_box.hpp:118
boost::shared_ptr< view > view_
Definition: chat_log.hpp:48
GLdouble GLdouble t
Definition: glew.h:1366
std::string get_value() const
Definition: text.hpp:76
virtual void set_active(const bool active) override
See tcontrol::set_active.
Definition: scrollbar.cpp:110
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
Simple push button.
Definition: button.hpp:32
virtual void set_use_markup(bool use_markup)
Definition: control.cpp:342
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
This file contains the settings handling of the widget library.
int get_value() const
Inherited from tinteger_selector_.
Definition: slider.hpp:48
#define LOG_CHAT_LOG
Definition: chat_log.cpp:46
static const int COUNT_PER_PAGE
Definition: chat_log.cpp:91
void set_tooltip(const t_string &tooltip)
Definition: control.hpp:265
virtual const std::string & window_id() const
Inherited from tdialog, implemented by REGISTER_DIALOG.
twindow * build_window(CVideo &video)
Inherited from tdialog.
Definition: chat_log.cpp:444
std::string escape_text(const std::string &text)
Escapes the markup characters in a text.
Definition: text.cpp:75
void update_view_from_model(bool select_last_page=false)
Definition: chat_log.cpp:288
void stream_log(std::ostringstream &s, int first, int last, bool raw=false)
Definition: chat_log.cpp:111
tcontrol * page_label
Definition: chat_log.cpp:93
void populate_chat_message_list(int first, int last)
Definition: chat_log.cpp:193
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
tcontrol * msg_label
Definition: chat_log.cpp:88
Base class for all visible items.
Definition: control.hpp:34
GLsizeiptr size
Definition: glew.h:1649
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glew.h:3448
void pre_show(twindow &window)
Inherited from tdialog.
Definition: chat_log.cpp:449
void copy_to_clipboard(const std::string &text, const bool)
Copies text to the clipboard.
Definition: clipboard.cpp:40
GLenum GLint ref
Definition: glew.h:1813
const GLdouble * m
Definition: glew.h:6968
static bool timestamp
Definition: log.cpp:46
std::string get_chat_timestamp(const time_t &t)
controller controller_
Definition: chat_log.cpp:428
A variable-expanding proxy for the config class.
Definition: variable.hpp:36
Standard logging facilities (interface).
GLint * first
Definition: glew.h:1496
std::pair< int, int > calculate_log_line_range()
Definition: chat_log.cpp:267
void handle_copy_button_clicked(twindow &)
Definition: chat_log.cpp:379
void chat_message_list_to_clipboard(int first, int last)
Definition: chat_log.cpp:200
tchat_log(const vconfig &cfg, replay *replay)
Definition: chat_log.cpp:432
A slider.
Definition: slider.hpp:30
GLdouble s
Definition: glew.h:1358
void previous_page(twindow &window)
Definition: chat_log.cpp:367
GLsizei const GLcharARB ** string
Definition: glew.h:4503
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:941
void pre_show(twindow &window)
Definition: chat_log.cpp:347
const std::vector< chat_msg > & chat_log_history
Definition: chat_log.cpp:89
void set_text_changed_callback(std::function< void(ttext_ *textbox, const std::string text)> cb)
Set the text_changed callback.
Definition: text.hpp:87