1 /**********************************************************************
2 *
3 * FreeDoko a Doppelkopf-Game
4 *
5 * Copyright (C) 2001 – 2018 by Diether Knof and Borg Enders
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You can find this license in the file 'gpl.txt'.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21 * MA 02111-1307 USA
22 *
23 * Contact:
24 * Diether Knof dknof@posteo.de
25 *
26 *********************************************************************/
27
28 #include "constants.h"
29
30 #ifdef USE_UI_GTKMM
31
32 #include "bug_report_replay.h"
33 #include "gameplay_actions.h"
34
35 #include "ui.h"
36 #include "thrower.h"
37 #include "bug_report.h"
38 #include "main_window.h"
39 #include "cards.h"
40
41 #include "../../os/bug_report_replay.h"
42 #include "../../game/game.h"
43 #include "../../player/player.h"
44 #include "../../player/aiconfig.h"
45
46 #include "../../utils/string.h"
47
48 #include "game_summary.h"
49 #include <gtkmm/notebook.h>
50 #include <gtkmm/label.h>
51 #include <gtkmm/textview.h>
52 #include <gtkmm/scrolledwindow.h>
53
54 namespace UI_GTKMM_NS {
55
56 /** Constructor
57 **
58 ** @param parent the parent widget
59 **/
BugReportReplay(Base * const parent)60 BugReportReplay::BugReportReplay(Base* const parent) :
61 Base(parent),
62 StickyDialog("FreeDoko – " + _("Window::Bug report replay"), false)
63 {
64 this->ui->add_window(*this);
65
66 this->actions = Gtk::manage(new GameplayActions(this));
67 this->human_actions = Gtk::manage(new GameplayActions(this));
68 this->game_summary = Gtk::manage(new GameSummary(this));
69
70 this->signal_realize().connect(sigc::mem_fun(*this, &BugReportReplay::init));
71
72 this->ui->bug_report->set_dnd_destination(*this);
73
74 Party::signal_open().connect_back([this](Party& party) {
75 party.signal_get_settings().connect_back(*this, &BugReportReplay::update_info, this->disconnector_);
76 },
77 this->disconnector_);
78 Game::signal_open().connect_back([this](Game& game) {
79 this->update_info();
80 game.signal_redistribute().connect_back(*this, &BugReportReplay::update_info, this->disconnector_);
81 game.signal_gameplay_action().connect_back(*this, &BugReportReplay::gameplay_action, this->disconnector_);
82 },
83 this->disconnector_);
84 OS_NS::BugReportReplay::signal_open().connect_back(*this,
85 &UI_GTKMM_NS::BugReportReplay::bug_report_replay_open,
86 this->disconnector_);
87 } // BugReportReplay::BugReportReplay(Base* parent)
88
89 /** destructor
90 **/
91 BugReportReplay::~BugReportReplay() = default;
92
93 /** create all subelements
94 **/
95 void
init()96 BugReportReplay::init()
97 {
98 this->set_icon(this->ui->icon);
99
100 { // action area
101 auto end_button
102 = Gtk::manage(new Gtk::Button(_("Button::end bug report")));
103
104 end_button->signal_clicked().connect(sigc::mem_fun(*this,
105 &BugReportReplay::end_bug_report));
106
107 add_close_button(*this);
108 } // action area
109
110 { // informations
111 this->version = Gtk::manage(new Gtk::Label(""));
112 this->version->set_justify(Gtk::JUSTIFY_LEFT);
113 this->compiled = Gtk::manage(new Gtk::Label(""));
114 this->compiled->set_justify(Gtk::JUSTIFY_LEFT);
115 this->system = Gtk::manage(new Gtk::Label(""));
116 this->system->set_justify(Gtk::JUSTIFY_LEFT);
117 this->time = Gtk::manage(new Gtk::Label(""));
118 this->time->set_justify(Gtk::JUSTIFY_LEFT);
119 this->language = Gtk::manage(new Gtk::Label(""));
120 this->language->set_justify(Gtk::JUSTIFY_LEFT);
121 this->trick = Gtk::manage(new Gtk::Label(""));
122 this->trick->set_justify(Gtk::JUSTIFY_LEFT);
123 this->reset_ais_button = Gtk::manage(new Gtk::Button(_("Button::reset ais")));
124 this->reset_ais_button->set_image_from_icon_name("edit-clear-all");
125 this->reset_ais_button->set_always_show_image();
126 this->message = Gtk::manage(new Gtk::TextView());
127 this->message->get_buffer()->set_text("");
128 this->message->set_wrap_mode(Gtk::WRAP_WORD);
129 #ifdef RELEASE
130 this->message->set_editable(false);
131 this->message->set_cursor_visible(false);
132 #else
133 // Note: the changes in the message box are not saved.
134 #endif
135
136 this->seed = Gtk::manage(new Gtk::Label(""));
137 this->seed->set_justify(Gtk::JUSTIFY_LEFT);
138
139 this->startplayer = Gtk::manage(new Gtk::Label(""));
140 this->startplayer->set_justify(Gtk::JUSTIFY_LEFT);
141
142 this->soloplayer = Gtk::manage(new Gtk::Label(""));
143 this->soloplayer->set_justify(Gtk::JUSTIFY_LEFT);
144
145 // hide the 'player' column
146 this->human_actions->get_column(1)->set_visible(false);
147 } // informations
148 { // vbox
149 this->notebook = Gtk::manage(new Gtk::Notebook());
150 { // general
151 auto label = Gtk::manage(new Gtk::Label(_("BugReportReplay::Group::general")));
152
153 auto general_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 1 EM));
154 general_box->set_border_width(1 EM);
155
156 { // game box
157 auto game_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 1 EX));
158
159 game_box->pack_start(*this->version, Gtk::PACK_EXPAND_WIDGET);
160 game_box->pack_start(*this->system, Gtk::PACK_EXPAND_WIDGET);
161 game_box->pack_start(*this->seed, Gtk::PACK_EXPAND_WIDGET);
162 game_box->pack_start(*this->startplayer, Gtk::PACK_EXPAND_WIDGET);
163 game_box->pack_start(*this->soloplayer, Gtk::PACK_EXPAND_WIDGET);
164 game_box->pack_start(*this->trick, Gtk::PACK_EXPAND_WIDGET);
165 this->reset_ais_button->set_halign(Gtk::ALIGN_CENTER);
166 game_box->pack_start(*this->reset_ais_button, Gtk::PACK_EXPAND_WIDGET);
167
168 general_box->pack_start(*game_box, false, true);
169 } // game box
170 { // message
171 general_box->add(*this->message);
172 } // message
173
174 this->notebook->append_page(*general_box, *label);
175 } // general
176 { // actions
177 auto label = Gtk::manage(new Gtk::Label(_("BugReportReplay::Group::actions")));
178
179 auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 1 EX));
180
181 auto scrolled_window = Gtk::manage(new Gtk::ScrolledWindow);
182 scrolled_window->set_policy(Gtk::POLICY_AUTOMATIC,
183 Gtk::POLICY_AUTOMATIC);
184
185 this->actions->get_selection()->set_mode(Gtk::SELECTION_SINGLE);
186 scrolled_window->add(*this->actions);
187
188 vbox->pack_start(*scrolled_window, Gtk::PACK_EXPAND_WIDGET);
189
190 this->auto_actions_button
191 = Gtk::manage(new Gtk::Button(_("Button::auto actions")));
192 vbox->pack_end(*this->auto_actions_button, Gtk::PACK_SHRINK);
193
194 this->notebook->append_page(*vbox, *label);
195 } // actions
196 { // human actions
197 auto label = Gtk::manage(new Gtk::Label(_("BugReportReplay::Group::human actions")));
198 auto scrolled_window = Gtk::manage(new Gtk::ScrolledWindow);
199 scrolled_window->set_policy(Gtk::POLICY_AUTOMATIC,
200 Gtk::POLICY_AUTOMATIC);
201
202 scrolled_window->add(*this->human_actions);
203
204 this->notebook->append_page(*scrolled_window, *label);
205 } // human actions
206 { // game summary
207 auto label = Gtk::manage(new Gtk::Label(_("BugReportReplay::Group::game summary")));
208
209 this->notebook->append_page(*this->game_summary, *label);
210 } // game summary
211
212 this->get_content_area()->pack_start(*this->notebook, true, true);
213 } // vbox
214
215 { // signals
216 reset_ais_button->signal_clicked().connect(sigc::mem_fun(*this, &BugReportReplay::reset_ais));
217 this->actions->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &BugReportReplay::update_auto_actions_button));
218 this->auto_actions_button->signal_clicked().connect(sigc::mem_fun(*this, &BugReportReplay::set_auto_actions));
219 this->update_auto_actions_button();
220 } // signals
221
222 this->show_all_children();
223
224
225 {
226 Gdk::Geometry geometry;
227 geometry.min_width = 3 * this->ui->cards->height();
228 geometry.min_height = 4 * this->ui->cards->height();
229
230 this->set_geometry_hints(*this, geometry, Gdk::HINT_MIN_SIZE);
231 }
232 } // void BugReportReplay::init()
233
234 /** a bug report replay is opened
235 **
236 ** @param bug_report_replay the bug report replay that is opened
237 **/
238 void
bug_report_replay_open(OS_NS::BugReportReplay & bug_report_replay)239 BugReportReplay::bug_report_replay_open(OS_NS::BugReportReplay&
240 bug_report_replay)
241 {
242 DEBUG_ASSERTION((&bug_report_replay == ::bug_report_replay.get()),
243 "BugReportReplay::bug_report_replay_open(bug_report_replay)\n"
244 " the bug report replay "
245 " = " << &bug_report_replay
246 << " != " << ::bug_report_replay.get()
247 << " = global one");
248 this->bug_report_replay_ = &bug_report_replay;
249 this->bug_report_replay_->signal_close().connect_back(*this,
250 &UI_GTKMM_NS::BugReportReplay::bug_report_replay_close,
251 this->disconnector_);
252
253 if (!this->get_realized())
254 this->realize();
255
256 this->actions->set_actions(bug_report_replay.actions());
257 this->actions->set_discrepancies(bug_report_replay.actions_discrepancies());
258 this->human_actions->set_actions(bug_report_replay.human_actions());
259 this->human_actions->set_discrepancies(bug_report_replay.human_actions_discrepancies());
260 if (bug_report_replay.game_summary()) {
261 this->game_summary->set_game_summary(*bug_report_replay.game_summary());
262 this->game_summary->show();
263 } else {
264 this->game_summary->hide();
265 }
266 // ToDo: hide the last page if there is no game summary in the bug report
267
268 this->update_info();
269 this->notebook->set_current_page(0);
270
271 if (!(::fast_play & FastPlay::hide_bug_report_window)) {
272 this->show();
273 }
274 } // void BugReportReplay::bug_report_replay_open(OS_NS::BugReportReplay bug_report_replay)
275
276 /** the bug report replay is closed
277 **/
278 void
bug_report_replay_close()279 BugReportReplay::bug_report_replay_close()
280 {
281 this->actions->remove_actions();
282 this->human_actions->remove_actions();
283 if ( this->bug_report_replay_
284 && ::bug_report_replay
285 && this->bug_report_replay_ == ::bug_report_replay.get()) {
286 // 'if' is workaround: first hide then show leaves the window hidden
287 this->bug_report_replay_ = nullptr;
288 this->hide();
289 }
290 } // BugReportReplay::bug_report_replay_close()
291
292 /** there happened a gameplay action
293 **
294 ** @param action gameplay action
295 **/
296 void
gameplay_action(GameplayAction const & action)297 BugReportReplay::gameplay_action(GameplayAction const& action)
298 {
299 if (!this->bug_report_replay_)
300 return ;
301
302 this->actions->set_current_action_no(this->bug_report_replay_->current_action_no());
303 this->human_actions->set_current_action_no(this->bug_report_replay_->current_human_action_no());
304 this->update_auto_actions_button();
305 } // void BugReportReplay::gameplay_action(GameplayAction action)
306
307 /** updates the information
308 **/
309 void
update_info()310 BugReportReplay::update_info()
311 {
312 if (!this->bug_report_replay_)
313 return ;
314
315 DEBUG_ASSERTION((this->bug_report_replay_ == ::bug_report_replay.get()),
316 "BugReportReplay::update_info():\n"
317 " this->bug_report_replay_ != ::bug_report_replay:\n"
318 " " << this->bug_report_replay_
319 << " != " << ::bug_report_replay.get());
320
321 if (::game_status == GameStatus::programstart)
322 return ;
323 #if 0
324 if (!&this->party())
325 return ;
326 #endif
327
328
329 #ifdef WORKAROUND
330 // This function can be called before this party is set
331 // (by ::bug_report_replay). So we take a party here that always exists.
332 // An alternitive would be to check, whether this party is set, but
333 // 'UI::party_' is private.
334 Party const& party = *::party;
335 #else
336 Party const& party = this->ui->party();
337 #endif
338
339 this->version->set_label(_("Version: %s",
340 static_cast<string const>(this->bug_report_replay_->version())));
341 this->compiled->set_label(_("Compiled: %s",
342 this->bug_report_replay_->compiled()));
343 this->system->set_label(_("System: %s",
344 this->bug_report_replay_->system()));
345 this->time->set_label(_("Time: %s",
346 this->bug_report_replay_->time()));
347 this->language->set_label(_("Language: %s",
348 this->bug_report_replay_->language()));
349 this->trick->set_label(_("Trick: %u",
350 this->bug_report_replay_->trickno()));
351
352 #ifdef WORKAROUND
353 // message: change iso to utf8
354 this->message->get_buffer()->set_text(UI_GTKMM::to_utf8(this->bug_report_replay_->message()));
355 #else
356 this->message->get_buffer()->set_text(this->bug_report_replay_->message());
357 #endif
358
359
360 this->seed->set_label(String::to_string(this->bug_report_replay_->seed()));
361
362 this->startplayer->set_label(String::to_string(this->bug_report_replay_->startplayer_no()));
363 this->seed->set_label(_("Seed: %u", this->bug_report_replay_->seed()));
364
365 if (this->bug_report_replay_->startplayer_no() != UINT_MAX)
366 this->startplayer->set_label(_("Startplayer: %u",
367 this->bug_report_replay_->startplayer_no())
368 + " (" + party.players()[this->bug_report_replay_->startplayer_no()].name() + ")");
369 else
370 this->startplayer->set_label(_("Startplayer: %u",
371 this->bug_report_replay_->startplayer_no())
372 + " (-)"
373 );
374
375 if (this->bug_report_replay_->soloplayer_no() != UINT_MAX)
376 this->soloplayer->set_label(_("Soloplayer: %u",
377 this->bug_report_replay_->soloplayer_no())
378 + " (" + party.players()[this->bug_report_replay_->soloplayer_no()].name() + ")"
379 );
380 else
381 this->soloplayer->set_label(_("Soloplayer: -"));
382
383 this->actions->update();
384 this->human_actions->update();
385
386 if (this->bug_report_replay_->game_summary())
387 this->game_summary->update();
388
389 this->update_actions_past();
390 } // void BugReportReplay::update_info()
391
392 /** update the showing of the actions already made
393 **
394 ** @todo all
395 **/
396 void
update_actions_past()397 BugReportReplay::update_actions_past()
398 {
399 if (!this->get_realized())
400 return ;
401
402 #if 0
403 for (auto& row : this->actions->get_model()->children()) {
404 for (auto col : this->actions->get_columns()) {
405 // ToDo
406 } // for (col \in this->actsion->get_columns())
407 } // for (row \in this->actions)
408 #endif
409 } // void BugReportReplay::update_actions_past()
410
411 /** update the auto actions button
412 **/
413 void
update_auto_actions_button()414 BugReportReplay::update_auto_actions_button()
415 {
416 if (!this->get_realized())
417 return ;
418
419 auto selection = this->actions->get_selection();
420
421 if (selection->get_selected()) {
422 auto row = *(selection->get_selected());
423
424 unsigned const actionno = row[this->actions->model.no];
425
426 if (actionno > this->bug_report_replay_->current_action_no()) {
427 this->auto_actions_button->set_sensitive(true);
428 } else {
429 this->auto_actions_button->set_sensitive(false);
430 }
431 } else { // if !(selection->get_selected())
432 this->auto_actions_button->set_sensitive(false);
433 } // if !(selection->get_selected())
434 } // void BugReportReplay::update_auto_actions_button()
435
436 /** automatic execute till the selected action
437 **/
438 void
set_auto_actions()439 BugReportReplay::set_auto_actions()
440 {
441 auto selection = this->actions->get_selection();
442
443 DEBUG_ASSERTION(selection->get_selected(),
444 "BugReportReplay::set_auto_actions()\n"
445 " no action selected");
446 auto row = *(selection->get_selected());
447
448 unsigned const actionno = row[this->actions->model.no];
449
450 this->bug_report_replay_->set_auto_action_end(actionno);
451 if ( (::game_status == GameStatus::game_play)
452 && (this->ui->game().players().current_player().type() == Player::Type::human) ) {
453 auto& player = this->ui->game().players().current_player();
454 player.hand().request_card(player.card_get());
455 this->ui->thrower(player.hand().requested_card(), __FILE__, __LINE__);
456 } // if (human has to play a card)
457 } // void BugReportReplay::set_auto_actions()
458
459 /** end the bug report
460 **/
461 void
end_bug_report()462 BugReportReplay::end_bug_report()
463 {
464 this->bug_report_replay_ = nullptr;
465 } // void BugReportReplay::end_bug_report()
466
467 /** reset the ais
468 **/
469 void
reset_ais()470 BugReportReplay::reset_ais()
471 {
472 int n = 0;
473 for (auto& p : this->ui->party().players()) {
474 if (dynamic_cast<Aiconfig*>(&p))
475 dynamic_cast<Aiconfig&>(p).reset_to_hardcoded(n);
476 n += 1;
477 }
478 } // void BugReportReplay::reset_ais()
479
480 /** the name of 'player' has changed
481 **
482 ** @param player player whose name has changed
483 **/
484 void
name_changed(Player const & player)485 BugReportReplay::name_changed(Player const& player)
486 {
487 if (!this->get_realized())
488 return ;
489
490 if (!this->bug_report_replay_)
491 return ;
492
493 if (::game_status & GameStatus::game) {
494 if (this->bug_report_replay_->startplayer_no()
495 == player.no())
496 this->startplayer->set_label(_("Startplayer: %u",
497 player.no())
498 + " (" + player.name() + ")"
499 );
500
501 if (this->bug_report_replay_->soloplayer_no() == player.no())
502 this->soloplayer->set_label(_("Soloplayer: %u",
503 player.no())
504 + " (" + player.name() + ")"
505 );
506 } // if (::game_status & GameStatus::game)
507
508 this->actions->name_changed(player);
509 this->human_actions->name_changed(player);
510 this->game_summary->name_changed(player);
511 } // void BugReportReplay::name_changed(Player player)
512
513 } // namespace UI_GTKMM_NS
514
515 #endif // #ifdef USE_UI_GTKMM
516