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 "reservation.h"
33 #include "table.h"
34 #include "cards_distribution.h"
35 #include "name.h"
36 #include "icongroup.h"
37 #include "ui.h"
38 #include "main_window.h"
39 
40 #include "../../party/rule.h"
41 #include "../../game/game.h"
42 #include "../../player/player.h"
43 #include "../../basetypes.h"
44 
45 #include "../../os/bug_report_replay.h"
46 
47 #include "../../utils/string.h"
48 
49 #include <gtkmm/notebook.h>
50 #include <gtkmm/radiobutton.h>
51 #include <gtkmm/grid.h>
52 #include <gtkmm/main.h>
53 namespace UI_GTKMM_NS {
54 
55 /** constructor
56  **
57  ** @param    table      the table
58  ** @param    position   the corresponding position
59  **/
Reservation(Table & table,Position const position)60 Reservation::Reservation(Table& table, Position const position) :
61   HTIN(table, position),
62   Gtk::StickyDialog("Window::reservation", *parent->ui->main_window, false)
63 {
64   this->ui->add_window(*this);
65 
66   this->signal_realize().connect(sigc::mem_fun(*this,
67                                                &Reservation::init));
68 }
69 
70 /** destructor
71  **/
72 Reservation::~Reservation() = default;
73 
74 /** @return   the corresponding player
75  **/
76 Player&
player()77 Reservation::player()
78 {
79   return this->ui->game().player(this->table().player(this->position()).no());
80 } // Player& Reservation::player()
81 
82 /** @return   the corresponding game
83  **/
84 Game&
game()85 Reservation::game()
86 {
87   return this->ui->game();
88 } // Game& Reservation::game()
89 
90 /** create all subelements
91  **/
92 void
init()93 Reservation::init()
94 {
95   this->set_icon(this->ui->icon);
96   if (this->game().players().count_humans() <= 1)
97     this->set_title(_("Window::reservation"));
98   else
99     this->set_title(_("Window::reservation (%s)",
100                       this->player().name()));
101 
102   this->set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
103 
104   this->announce_button = Gtk::manage(new Gtk::Button(_("Button::reservation announce")));
105   this->add_action_widget(*this->announce_button, Gtk::RESPONSE_ACCEPT);
106 
107   this->bock_label = Gtk::manage(new Gtk::Label(_("bock: %u", 1u)));
108 #ifdef POSTPONED
109   // does not work reliable
110   {
111     Pango::AttrList attributes;
112     auto attribute
113       = Pango::AttrShape::create_attr_weight(Pango::WEIGHT_BOLD);
114     attributes.insert(attribute);
115     this->bock_label->set_attributes(attributes);
116   }
117 #endif
118 
119   this->notebook = Gtk::manage(new Gtk::Notebook());
120   this->notebook->set_tab_pos(Gtk::POS_LEFT);
121   this->notebook->signal_switch_page().connect(sigc::mem_fun(*this,
122                                                              &Reservation::switch_page_event));
123 
124   this->swines_button = Gtk::manage(new Gtk::CheckButton(_("swines")));
125   this->hyperswines_button = Gtk::manage(new Gtk::CheckButton(_("hyperswines")));
126   this->solo_swines_button = Gtk::manage(new Gtk::CheckButton(_("swines")));
127   this->solo_hyperswines_button = Gtk::manage(new Gtk::CheckButton(_("hyperswines")));
128 
129   this->announce_button->set_can_default();
130   this->announce_button->grab_default();
131   this->announce_button->grab_focus();
132 
133   { // bock information
134     this->get_content_area()->pack_start(*this->bock_label, false, true);
135   } // bock information
136   { // the notebook
137     this->get_content_area()->pack_start(*this->notebook, true, true);
138 
139     { // create the buttons
140       Gtk::RadioButton::Group gametype_group;
141       for (auto gt : game_type_list) {
142         auto& button = this->gametype_buttons[gt];
143         button = Gtk::manage(new Gtk::RadioButton(gametype_group, _(gt)));
144         button->set_data("gametype", new GameType(gt));
145         button->signal_toggled().connect(sigc::bind<GameType>(sigc::mem_fun(*this, &Reservation::gametype_changed), gt));
146       } // for (gt : game_type_list)
147 
148       Gtk::RadioButton::Group marriage_selector_group;
149       for (auto m : marriage_selector_list) {
150         auto& button = this->marriage_selector_buttons[m];
151         button = Gtk::manage(new Gtk::RadioButton(marriage_selector_group, _(m)));
152         button->set_data("selector", new MarriageSelector(MarriageSelector(m)));
153         button->signal_toggled().connect(sigc::bind<MarriageSelector>(sigc::mem_fun(*this, &Reservation::marriage_selector_changed), m));
154       } // for (m : marriage_selector_list)
155     } // create the buttons
156 
157     { // set the pages of the notebook
158       { // General
159         auto label = Gtk::manage(new Gtk::Label(_("GameType::Group::general")));
160         auto hbox = Gtk::manage(new Gtk::Box());
161         hbox->set_spacing(1 EX);
162         hbox->set_border_width(1 EX);
163 
164         { // gametypes
165           auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 1 EX));
166           vbox->set_valign(Gtk::ALIGN_START);
167           vbox->add(*this->gametype_buttons[GameType::normal]);
168           vbox->add(*this->gametype_buttons[GameType::thrown_nines]);
169           vbox->add(*this->gametype_buttons[GameType::thrown_kings]);
170           vbox->add(*this->gametype_buttons[GameType::thrown_nines_and_kings]);
171           vbox->add(*this->gametype_buttons[GameType::thrown_richness]);
172           vbox->add(*this->gametype_buttons[GameType::fox_highest_trump]);
173           vbox->add(*this->gametype_buttons[GameType::poverty]);
174           //vbox->add(*this->gametype_buttons[GameType::redistribute]);
175           hbox->add(*vbox);
176         } // gametypes
177         { // marriage selectors
178           auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
179           vbox->set_valign(Gtk::ALIGN_START);
180           vbox->add(*this->gametype_buttons[GameType::marriage]);
181           this->marriage_selector_buttons[MarriageSelector::first_foreign]->set_margin_left(1 EM);
182           vbox->add(*this->marriage_selector_buttons[MarriageSelector::first_foreign]);
183           this->marriage_selector_buttons[MarriageSelector::first_trump]->set_margin_left(1 EM);
184           vbox->add(*this->marriage_selector_buttons[MarriageSelector::first_trump]);
185           this->marriage_selector_buttons[MarriageSelector::first_color]->set_margin_left(1 EM);
186           vbox->add(*this->marriage_selector_buttons[MarriageSelector::first_color]);
187           this->marriage_selector_buttons[MarriageSelector::first_club]->set_margin_left(1 EM);
188           vbox->add(*this->marriage_selector_buttons[MarriageSelector::first_club]);
189           this->marriage_selector_buttons[MarriageSelector::first_spade]->set_margin_left(1 EM);
190           vbox->add(*this->marriage_selector_buttons[MarriageSelector::first_spade]);
191           this->marriage_selector_buttons[MarriageSelector::first_heart]->set_margin_left(1 EM);
192           vbox->add(*this->marriage_selector_buttons[MarriageSelector::first_heart]);
193           hbox->add(*vbox);
194         } // marriage selectors
195         { // the swines
196           auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 1 EX));
197           vbox->set_valign(Gtk::ALIGN_START);
198           vbox->add(*this->swines_button);
199           vbox->add(*this->hyperswines_button);
200           hbox->add(*vbox);
201         } // the swines
202         this->notebook->append_page(*hbox, *label);
203       } // General
204       { // Solo
205         auto label = Gtk::manage(new Gtk::Label(_("GameType::Group::solo")));
206 
207         auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
208         vbox->set_halign(Gtk::ALIGN_CENTER);
209         vbox->set_valign(Gtk::ALIGN_START);
210 
211         {
212           auto grid = Gtk::manage(new Gtk::Grid());
213           grid->set_border_width(1 EX);
214           grid->set_row_spacing(0 EX);
215           grid->set_column_spacing(1 EM);
216           grid->set_valign(Gtk::ALIGN_CENTER);
217           grid->set_halign(Gtk::ALIGN_CENTER);
218 
219           grid->attach(*this->gametype_buttons[GameType::solo_club],
220                        0, 0, 1, 1);
221           grid->attach(*this->gametype_buttons[GameType::solo_spade],
222                        0, 1, 1, 1);
223           grid->attach(*this->gametype_buttons[GameType::solo_heart],
224                        0, 2, 1, 1);
225           grid->attach(*this->gametype_buttons[GameType::solo_diamond],
226                        0, 3, 1, 1);
227           grid->attach(*this->gametype_buttons[GameType::solo_meatless],
228                        1, 0, 1, 1);
229           grid->attach(*this->gametype_buttons[GameType::solo_jack],
230                        1, 1, 1, 1);
231           grid->attach(*this->gametype_buttons[GameType::solo_queen],
232                        1, 2, 1, 1);
233           grid->attach(*this->gametype_buttons[GameType::solo_king],
234                        1, 3, 1, 1);
235           grid->attach(*this->gametype_buttons[GameType::solo_queen_jack],
236                        2, 0, 1, 1);
237           grid->attach(*this->gametype_buttons[GameType::solo_king_jack],
238                        2, 1, 1, 1);
239           grid->attach(*this->gametype_buttons[GameType::solo_king_queen],
240                        2, 2, 1, 1);
241           grid->attach(*this->gametype_buttons[GameType::solo_koehler],
242                        2, 3, 1, 1);
243           vbox->add(*grid);
244         }
245         {
246           auto hbox = Gtk::manage(new Gtk::Box());
247           hbox->set_homogeneous();
248           hbox->set_spacing(1 EM);
249           hbox->set_halign(Gtk::ALIGN_CENTER);
250           hbox->add(*this->solo_swines_button);
251           hbox->add(*this->solo_hyperswines_button);
252           vbox->add(*hbox);
253         }
254 
255         {
256           this->offer_duty_solo_button
257             = Gtk::manage(new Gtk::CheckButton(_("offer duty solo")));
258 
259           vbox->add(*this->offer_duty_solo_button);
260         }
261 
262         this->notebook->append_page(*vbox, *label);
263       } // Solo
264     } // set the pages of the notebook
265   } // the notebook
266   {
267     auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 5 EM));
268     box->set_halign(Gtk::ALIGN_CENTER);
269     box->set_margin_top(1 EX);
270     box->set_margin_bottom(1 EX);
271     {
272       auto manual_distribution = Gtk::manage(new Gtk::Button(_("distribute manually")));
273       auto manual_distribution_actions =
274         [this]() {
275           if (this->position() == Position::south) {
276             this->ui->table->cards_distribution().save_hand_of_human_player(this->player().hand());
277           }
278           this->ui->thrower(GameStatus::game_manual_cards_distribution,
279                             __FILE__, __LINE__);
280         };
281       manual_distribution->signal_clicked().connect(manual_distribution_actions);
282       box->add(*manual_distribution);
283     }
284     {
285       auto& redistribute = *this->gametype_buttons[GameType::redistribute];
286       box->add(redistribute);
287     }
288     this->get_content_area()->add(*box);
289   }
290   { // tournament info
291     auto grid = Gtk::manage(new Gtk::Grid());
292     grid->set_column_spacing(2 EM);
293     { // remaining games/rounds/points
294       auto grid2 = Gtk::manage(new Gtk::Grid());
295 
296       this->remaining_rounds_label
297         = Gtk::manage(new Gtk::Label(_("remaining rounds") + ":"s));
298       this->remaining_rounds_number
299         = Gtk::manage(new Gtk::Label);
300       this->remaining_rounds_number->set_label("0");
301       this->remaining_games_label
302         = Gtk::manage(new Gtk::Label(_("remaining games") + ":"s));
303       this->remaining_games_number
304         = Gtk::manage(new Gtk::Label);
305       this->remaining_games_number->set_label("0");
306       this->remaining_points_label
307         = Gtk::manage(new Gtk::Label(_("remaining points") + ":"s));
308       this->remaining_points_number
309         = Gtk::manage(new Gtk::Label);
310       this->remaining_points_number->set_label("0");
311 
312       this->remaining_rounds_label->set_halign(Gtk::ALIGN_START);
313       this->remaining_rounds_number->set_halign(Gtk::ALIGN_END);
314       this->remaining_games_label->set_halign(Gtk::ALIGN_START);
315       this->remaining_games_number->set_halign(Gtk::ALIGN_END);
316       this->remaining_points_label->set_halign(Gtk::ALIGN_START);
317       this->remaining_points_number->set_halign(Gtk::ALIGN_END);
318       grid2->attach(*this->remaining_rounds_label,
319                     0, 0, 1, 1);
320       grid2->attach(*this->remaining_rounds_number,
321                     1, 0, 1, 1);
322       grid2->attach(*this->remaining_games_label,
323                     0, 1, 1, 1);
324       grid2->attach(*this->remaining_games_number,
325                     1, 1, 1, 1);
326       grid2->attach(*this->remaining_points_label,
327                     0, 2, 1, 1);
328       grid2->attach(*this->remaining_points_number,
329                     1, 2, 1, 1);
330 
331       grid2->set_row_spacing(1 EX);
332       grid2->set_column_spacing(static_cast<int>(0.5 EM));
333 
334       grid->add(*grid2);
335     } // remaining games/rounds/points
336     { // duty soli
337       auto grid2 = Gtk::manage(new Gtk::Grid());
338 
339       this->duty_free_soli_label
340         = Gtk::manage(new Gtk::Label(_("duty free soli") + ":"s));
341       this->duty_free_soli_number
342         = Gtk::manage(new Gtk::Label);
343       this->duty_free_soli_number->set_label("0");
344       this->duty_color_soli_label
345         = Gtk::manage(new Gtk::Label(_("duty color soli") + ":"s));
346       this->duty_color_soli_number
347         = Gtk::manage(new Gtk::Label);
348       this->duty_color_soli_number->set_label("0");
349       this->duty_picture_soli_label
350         = Gtk::manage(new Gtk::Label(_("duty picture soli") + ":"s));
351       this->duty_picture_soli_number
352         = Gtk::manage(new Gtk::Label);
353       this->duty_picture_soli_number->set_label("0");
354 
355       this->duty_free_soli_label->set_halign(Gtk::ALIGN_START);
356       this->duty_free_soli_number->set_halign(Gtk::ALIGN_END);
357       this->duty_color_soli_label->set_halign(Gtk::ALIGN_START);
358       this->duty_color_soli_number->set_halign(Gtk::ALIGN_END);
359       this->duty_picture_soli_label->set_halign(Gtk::ALIGN_START);
360       this->duty_picture_soli_number->set_halign(Gtk::ALIGN_END);
361       grid2->attach(*this->duty_free_soli_label,
362                     0, 0, 1, 1);
363       grid2->attach(*this->duty_free_soli_number,
364                     1, 0, 1, 1);
365       grid2->attach(*this->duty_color_soli_label,
366                     0, 1, 1, 1);
367       grid2->attach(*this->duty_color_soli_number,
368                     1, 1, 1, 1);
369       grid2->attach(*this->duty_picture_soli_label,
370                     0, 2, 1, 1);
371       grid2->attach(*this->duty_picture_soli_number,
372                     1, 2, 1, 1);
373 
374       grid2->set_row_spacing(1 EX);
375       grid2->set_column_spacing(static_cast<int>(0.5 EM));
376 
377       grid->add(*grid2);
378     } // duty soli
379     grid->set_border_width(1 EX);
380     grid->set_halign(Gtk::ALIGN_CENTER);
381     this->get_content_area()->add(*grid);
382   } // tournament info
383 
384   { // signals
385     this->announce_button->signal_clicked().connect(sigc::mem_fun(*this,
386                                                                   &Reservation::announce)
387                                                    );
388     this->swines_button->signal_toggled().connect(sigc::mem_fun(*this, &Reservation::swines_changed));
389     this->solo_swines_button->signal_toggled().connect(sigc::mem_fun(*this, &Reservation::swines_changed));
390     this->hyperswines_button->signal_toggled().connect(sigc::mem_fun(*this, &Reservation::swines_changed));
391     this->solo_hyperswines_button->signal_toggled().connect(sigc::mem_fun(*this, &Reservation::swines_changed));
392   } // signals
393 
394   this->show_all_children();
395 } // void Reservation::init()
396 
397 /** show the reservation for selecting before it is the turn of the player
398  **/
399 void
show_for_selection()400 Reservation::show_for_selection()
401 {
402   if (this->announced)
403     return ;
404 
405   this->realize();
406 
407   this->notebook->set_current_page(0);
408 
409   this->set_default();
410   this->sensitivity_update();
411 
412   { // adjust some texts because of changed points
413     this->remaining_rounds_number->set_label(std::to_string(this->ui->party().remaining_rounds()));
414     this->remaining_games_number->set_label(std::to_string(this->ui->party().remaining_games()));
415     this->remaining_points_number->set_label(std::to_string(this->ui->party().remaining_points()));
416 
417     this->duty_free_soli_number->set_label(std::to_string(this->player().remaining_duty_free_soli()));
418     this->duty_color_soli_number->set_label(std::to_string(this->player().remaining_duty_color_soli()));
419     this->duty_picture_soli_number->set_label(std::to_string(this->player().remaining_duty_picture_soli()));
420 
421 
422     this->bock_label->set_label(_("bock: %u",
423                                   this->ui->party().current_bock_multiplier()));
424     this->gametype_buttons[GameType::thrown_nines]->set_label(_("GameType::%u nines",
425                                                                 this->game().rule()(Rule::Type::min_number_of_throwing_nines)));
426     this->gametype_buttons[GameType::thrown_kings]->set_label(_("GameType::%u kings",
427                                                                 this->game().rule()(Rule::Type::min_number_of_throwing_kings)));
428     this->gametype_buttons[GameType::thrown_nines_and_kings]->set_label(_("GameType::%u nines and kings",
429                                                                           this->game().rule()(Rule::Type::min_number_of_throwing_nines_and_kings)));
430     this->gametype_buttons[GameType::thrown_richness]->set_label(_("GameType::%u richness",
431                                                                    this->game().rule()(Rule::Type::min_richness_for_throwing)));
432 
433     if (this->player().hand().count_club_queens()
434         == this->game().rule()(Rule::Type::number_of_same_cards)) {
435       this->gametype_buttons[GameType::normal]->set_label(_(::GameType::marriage_silent));
436     } else {
437       this->gametype_buttons[GameType::normal]->set_label(_(::GameType::normal));
438     }
439   } // adjust some texts
440   if (this->ui->party().current_bock_multiplier() != 1)
441     this->bock_label->show();
442   else
443     this->bock_label->hide();
444 
445   this->show();
446 } // void Reservation::show_for_selection()
447 
448 /** dummy function
449  **
450  ** @return   false
451  **/
452 bool
changed() const453 Reservation::changed() const
454 {
455   return false;
456 } // bool Reservation::changed() const
457 
458 /** nothing (dummy function)
459  **
460  ** @param    cr       drawing context
461  **/
462 void
draw(Cairo::RefPtr<::Cairo::Context> cr)463 Reservation::draw(Cairo::RefPtr<::Cairo::Context> cr)
464 {
465   // this function is needed because it is virtual in base class 'HTIN'
466 } // void Reservation::draw(Cairo::RefPtr<::Cairo::Context> cr)
467 
468 /** dummy function
469  **
470  ** @return   the height of the drawing
471  **/
472 Reservation::Outline
outline() const473 Reservation::outline() const
474 {
475   return {};
476 } // Outline Reservation::outline() const
477 
478 /** gets a reservation
479  **
480  ** @return   reservation
481  **/
482 ::Reservation
get()483 Reservation::get()
484 {
485   if (!this->announced) {
486 
487     if (!this->is_visible())
488       this->show_for_selection();
489 
490     do {
491       if (!this->is_visible())
492         this->present();
493 
494       while (!this->ui->thrower
495              && this->is_visible())
496         ::ui->wait();
497     } while (!this->announce_button->is_sensitive()
498              && !this->ui->thrower) ;
499   } // if (!this->announced)
500 
501   this->game().set_type(GameType::normal);
502 
503   if (!this->announced)
504     this->announce();
505 
506   this->announced = false;
507 
508   this->hide();
509   return this->player().reservation();
510 } // ::Reservation Reservation::get()
511 
512 /** set to default values
513  **/
514 void
set_default()515 Reservation::set_default()
516 {
517   this->in_update = true;
518 
519   ::Reservation const& reservation
520     = this->player().reservation_get_default();
521 
522   this->swines_button->set_active(reservation.swines);
523   this->hyperswines_button->set_active(reservation.hyperswines);
524   this->solo_swines_button->set_active(false);
525   this->solo_hyperswines_button->set_active(false);
526   this->offer_duty_solo_button->set_active(false);
527 
528   // special case: with swines announced the player does not have a poverty
529   if (   (reservation.game_type == GameType::poverty)
530       && (this->player().hand().count_poverty_cards()
531           > this->game().rule()(Rule::Type::max_number_of_poverty_trumps)) )
532     this->swines_button->set_active(false);
533 
534   this->marriage_selector_buttons[reservation.marriage_selector]->set_active();
535   this->gametype_buttons[reservation.game_type]->set_active();
536 
537   this->name().force_redraw();
538   this->table().name(this->game().startplayer()).force_redraw();
539 
540   this->in_update = false;
541 
542   this->gametype_changed(reservation.game_type);
543 
544   { // switch to the game type from the bug report replay
545     if (   ::bug_report_replay
546         && !::bug_report_replay->finished()
547         && (::bug_report_replay->soloplayer_no() == this->player().no() )) {
548 
549       this->gametype_buttons[::bug_report_replay->game_type()]->set_active();
550       if (::bug_report_replay->game_type() == GameType::marriage) {
551         this->marriage_selector_buttons[::bug_report_replay->marriage_selector()]->set_active();
552       }
553 
554       if (is_solo(::bug_report_replay->game_type()))
555         this->notebook->set_current_page(1);
556     }
557   } // switch to the game type from the bug report replay
558 
559   this->swines_changed();
560 } // void Reservation::set_default()
561 
562 /** update the sensitivity
563  **/
564 void
sensitivity_update()565 Reservation::sensitivity_update()
566 {
567   if (!this->get_realized())
568     return ;
569 
570   if (::game_status != GameStatus::game_reservation)
571     return ;
572 
573   if (this->in_update)
574     return ;
575 
576   auto const& player = this->player();
577   auto const& game = player.game();
578   auto const& rule = game.rule();
579   auto const& hand = player.hand();
580 
581   if (   rule(Rule::Type::swines)
582       && !rule(Rule::Type::swine_only_second)) {
583     this->swines_button->show();
584     this->solo_swines_button->show();
585   } else {
586     this->swines_button->hide();
587     this->solo_swines_button->hide();
588   }
589   if (!rule(Rule::Type::swines_in_solo))
590     this->solo_swines_button->hide();
591   this->swines_button->set_sensitive(game.swines().swines_announcement_valid(player));
592   this->solo_swines_button->set_sensitive(is_color_solo(game.type())
593                                           && game.swines().swines_announcement_valid(player));
594 
595   if (rule(Rule::Type::hyperswines)) {
596     this->hyperswines_button->show();
597     this->solo_hyperswines_button->show();
598   } else {
599     this->hyperswines_button->hide();
600     this->solo_hyperswines_button->hide();
601   }
602   if (!rule(Rule::Type::hyperswines_in_solo))
603     this->solo_hyperswines_button->hide();
604   this->hyperswines_button->set_sensitive(game.swines().hyperswines_announcement_valid(player)
605                                           && this->swines_button->get_active());
606   this->solo_hyperswines_button->set_sensitive(is_color_solo(game.type())
607                                                && game.swines().hyperswines_announcement_valid(player)
608                                                && this->solo_swines_button->get_active());
609   if (::preferences(::Preferences::Type::announce_swines_automatically)) {
610     this->swines_button->set_sensitive(false);
611     this->hyperswines_button->set_sensitive(false);
612     this->solo_swines_button->set_sensitive(false);
613     this->solo_hyperswines_button->set_sensitive(false);
614   } // if (::preferences(::Preferences::Type::announce_swines_automatically))
615 
616   for (auto& widget : this->gametype_buttons) {
617     if (rule(widget.first))
618       widget.second->show();
619     else
620       widget.second->hide();
621   } // for (widget)
622 
623   if (rule(Rule::Type::solo))
624     this->notebook->get_nth_page(1)->show();
625   else
626     this->notebook->get_nth_page(1)->hide();
627 
628   for (auto& widget : this->marriage_selector_buttons) {
629     if (rule(widget.first))
630       widget.second->show();
631     else
632       widget.second->hide();
633   } // for (widget)
634 
635   this->gametype_buttons[GameType::thrown_richness]->set_sensitive((hand.points() >= rule(Rule::Type::min_richness_for_throwing))
636                                                                    && rule(GameType::thrown_richness));
637   this->gametype_buttons[GameType::thrown_nines_and_kings]->set_sensitive((hand.count_nines() + hand.count_kings() >= rule(Rule::Type::min_number_of_throwing_nines_and_kings))
638                                                                           && rule(GameType::thrown_nines_and_kings));
639   this->gametype_buttons[GameType::thrown_kings]->set_sensitive((hand.count_kings() >= rule(Rule::Type::min_number_of_throwing_kings))
640                                                                 && rule(GameType::thrown_kings));
641   this->gametype_buttons[GameType::thrown_nines]->set_sensitive((hand.count_nines() >= rule(Rule::Type::min_number_of_throwing_nines))
642                                                                 && rule(GameType::thrown_nines));
643   this->gametype_buttons[GameType::poverty]->set_sensitive(hand.has_poverty());
644   this->gametype_buttons[GameType::redistribute]->set_sensitive(rule(GameType::redistribute));
645 
646   { // look, whether there is a card that is higher than the fox
647     unsigned c;
648     for (c = 0; c < player.hand().cardsnumber(); c++)
649       // check with diamond ten because of swines!
650       if (   Card::diamond_ten.less(player.hand().card(c))
651           && !player.hand().card(c).istrumpace())
652         break;
653     this->gametype_buttons[GameType::fox_highest_trump]->set_sensitive((c == player.hand().cardsnumber())
654                                                                        && rule(GameType::poverty));
655   } // look, whether there is a card that is higher than the fox
656   this->gametype_buttons[GameType::marriage]->set_sensitive((hand.count_club_queens() == rule(Rule::Type::number_of_same_cards)));
657 
658   for (auto widget : this->marriage_selector_buttons) {
659     auto const selected = this->gametype_buttons[GameType::marriage]->get_active();
660     widget.second->set_sensitive(selected);
661     //this->gametype_buttons[GameType::marriage]->sensitive());
662   }
663 
664   { // duty soli
665     if (   game.is_duty_solo()
666         && (game.startplayer() == player)) {
667       this->announce_button->set_sensitive(false);
668       this->notebook->set_current_page(1);
669       this->notebook->get_nth_page(0)->set_sensitive(false);
670       for (auto widget : this->gametype_buttons) {
671         auto const game_type = widget.first;
672         widget.second->set_sensitive(   (   is_solo(game_type)
673                                          && player.remaining_duty_free_soli())
674                                      || (   is_color_solo(game_type)
675                                          && player.remaining_duty_color_soli())
676                                      || (   is_picture_solo(game_type)
677                                          && player.remaining_duty_picture_soli())
678                                      || (game_type == GameType::redistribute)
679                                     );
680         if (   widget.second->is_sensitive()
681             && widget.second->get_active())
682           this->announce_button->set_sensitive(true);
683 
684       } // for (widget)
685     } // if (duty solo)
686 
687     if (rule(Rule::Type::offer_duty_solo)
688         && game.is_duty_solo()
689         && (game.startplayer() == player)) {
690       this->offer_duty_solo_button->show();
691     } else {
692       this->offer_duty_solo_button->hide();
693     }
694 
695     if (rule(Rule::Type::number_of_rounds_limited)) {
696       this->remaining_rounds_label->show();
697       this->remaining_rounds_number->show();
698       this->remaining_games_label->show();
699       this->remaining_games_number->show();
700     } else {
701       this->remaining_rounds_label->hide();
702       this->remaining_rounds_number->hide();
703       this->remaining_games_label->hide();
704       this->remaining_games_number->hide();
705     }
706     if (rule(Rule::Type::points_limited)) {
707       this->remaining_points_label->show();
708       this->remaining_points_number->show();
709     } else {
710       this->remaining_points_label->hide();
711       this->remaining_points_number->hide();
712     }
713     if (  rule(Rule::Type::number_of_duty_soli)
714         - rule(Rule::Type::number_of_duty_color_soli)
715         - rule(Rule::Type::number_of_duty_picture_soli)
716         > 0) {
717       this->duty_free_soli_label->show();
718       this->duty_free_soli_number->show();
719     } else {
720       this->duty_free_soli_label->hide();
721       this->duty_free_soli_number->hide();
722     }
723     if (rule(Rule::Type::number_of_duty_color_soli)) {
724       this->duty_color_soli_label->show();
725       this->duty_color_soli_number->show();
726     } else {
727       this->duty_color_soli_label->hide();
728       this->duty_color_soli_number->hide();
729     }
730     if (rule(Rule::Type::number_of_duty_picture_soli)) {
731       this->duty_picture_soli_label->show();
732       this->duty_picture_soli_number->show();
733     } else {
734       this->duty_picture_soli_label->hide();
735       this->duty_picture_soli_number->hide();
736     }
737   } // duty soli
738 } // void Reservation::sensitivity_update()
739 
740 /** the gametype has changed
741  **
742  ** @param    gametype_   changed game type
743  **/
744 void
gametype_changed(GameType const gametype)745 Reservation::gametype_changed(GameType const gametype)
746 {
747   if (!this->get_realized())
748     return ;
749 
750   if (!this->gametype_buttons[gametype]->get_active())
751     return ;
752   if (this->in_update)
753     return ;
754 
755   this->in_update = true;
756 
757   auto const& player = this->player();
758   auto const& game = player.game();
759   auto const& rule = game.rule();
760 
761   if (gametype == GameType::marriage) {
762     auto const selected = this->gametype_buttons[gametype]->get_active();
763     for (auto widget : this->marriage_selector_buttons)
764       widget.second->set_sensitive(selected);
765   } // if (gametype == GameType::marriage)
766 
767   // '::party.game()' here, because the game has to be changed,
768   // 'this->game()' is a const game
769   //GameType const old_gametype = this->>game().type();
770   this->game().set_type(static_cast<GameType>(gametype));
771 
772   { // update the swines
773     auto const& hand = player.hand();
774 
775     this->swines_button->set_sensitive(game.swines().swines_announcement_valid(player));
776     this->swines_button->set_active(this->swines_button->is_sensitive()
777                                     && rule(Rule::Type::swines_announcement_begin)
778                                     && ((game.type() != GameType::poverty)
779                                         || (hand.count_trumps() <= rule(Rule::Type::max_number_of_poverty_trumps)))
780                                    );
781     this->hyperswines_button->set_sensitive(game.swines().hyperswines_announcement_valid(player)
782                                             && this->swines_button->get_active());
783     this->hyperswines_button->set_active(this->hyperswines_button->is_sensitive()
784                                          && rule(Rule::Type::hyperswines_announcement_begin));
785     this->solo_swines_button->set_sensitive(game.swines().swines_announcement_valid(player));
786     this->solo_swines_button->set_active(this->solo_swines_button->is_sensitive()
787                                          && rule(Rule::Type::swines_announcement_begin)
788                                          && ((game.type() != GameType::poverty)
789                                              || (hand.count_trumps() <= rule(Rule::Type::max_number_of_poverty_trumps)))
790                                         );
791     this->solo_hyperswines_button->set_sensitive(game.swines().hyperswines_announcement_valid(player)
792                                                  && this->solo_swines_button->get_active());
793     this->solo_hyperswines_button->set_active(this->solo_hyperswines_button->is_sensitive()
794                                               && rule(Rule::Type::hyperswines_announcement_begin));
795 
796     if (::preferences(::Preferences::Type::announce_swines_automatically)) {
797       this->swines_button->set_sensitive(false);
798       this->hyperswines_button->set_sensitive(false);
799       this->solo_swines_button->set_sensitive(false);
800       this->solo_hyperswines_button->set_sensitive(false);
801     } // if (::preferences(::Preferences::Type::announce_swines_automatically))
802   } // update the swines
803 
804   { // update the reservation of the player
805     ::Reservation& reservation = this->player().reservation();
806 
807     reservation.game_type = static_cast<GameType>(gametype);
808     if (   (player.hand().count_club_queens()
809             == game.rule()(Rule::Type::number_of_same_cards))
810         && (gametype == GameType::normal)) {
811       reservation.game_type = GameType::marriage_silent;
812     }
813     for (auto const& widget : this->marriage_selector_buttons) {
814       if (widget.second->get_active()) {
815         reservation.marriage_selector = widget.first;
816       }
817     }
818     // swines are updated in the following 'this->swines_changed()'
819   } // update the reservation of the player
820 
821   this->name().force_redraw();
822   this->table().name(game.startplayer()).force_redraw();
823 
824   if (    game.is_duty_solo()
825       && (game.startplayer() == player)) {
826     this->announce_button->set_sensitive(is_real_solo(gametype)
827                                          || (gametype == GameType::redistribute));
828   }
829 
830   this->in_update = false;
831 
832   this->table().update_hands();
833   this->table().draw_all();
834   this->swines_changed();
835 } // void Reservation::gametype_changed(int gametype)
836 
837 /** the marriage selector has changed
838  **
839  ** @param    marriage_selector   changed marriage selector
840  **/
841 void
marriage_selector_changed(MarriageSelector const marriage_selector)842 Reservation::marriage_selector_changed(MarriageSelector const marriage_selector)
843 {
844   if (!this->get_realized())
845     return ;
846 
847   this->player().reservation().marriage_selector
848     = marriage_selector;
849   this->table().draw_all();
850 } // void Reservation::marriage_selector_changed(int marriage_selector_)
851 
852 /** the selection of swines/hyperswines has changed
853  **
854  ** @bug       the cards order is not updated (problem: information flow)
855  **/
856 void
swines_changed()857 Reservation::swines_changed()
858 {
859   if (!this->get_realized())
860     return ;
861 
862   if (this->in_update)
863     return ;
864 
865   ::Reservation& reservation = this->player().reservation();
866 
867   bool differ = (   (reservation.swines
868                      != (is_solo(this->game().type())
869                          ? this->solo_swines_button->get_active()
870                          : this->swines_button->get_active()))
871                  || (reservation.hyperswines
872                      != (is_solo(this->game().type())
873                          ? this->solo_hyperswines_button->get_active()
874                          : this->hyperswines_button->get_active())));
875   if (!differ)
876     return ;
877 
878   reservation.swines
879     = (is_solo(this->game().type())
880        ? this->solo_swines_button->get_active()
881        : this->swines_button->get_active());
882   reservation.hyperswines
883     = (is_solo(this->game().type())
884        ? this->solo_hyperswines_button->get_active()
885        : this->hyperswines_button->get_active());
886 
887   this->sensitivity_update();
888   this->table().update_hands();
889   this->table().draw_all();
890 } // void Reservation::swines_changed()
891 
892 /** update in game: gametype, swines, hyperswines according to the selected reservation
893  ** Used for the 'early' reservation, when the human can select a reservation before it is his turn. When a player before has announced, the game is resetted (gametype normal, no swines). So we have to adjust here, in order to have the cards order not changed.
894  **/
895 void
update_for_reservation()896 Reservation::update_for_reservation()
897 {
898   this->game().set_type(this->player().reservation().game_type);
899   this->swines_changed();
900 } // void Reservation::update_for_reservation()
901 
902 /** update the reservation of the player
903  **/
904 void
update_player_reservation()905 Reservation::update_player_reservation()
906 {
907   if (!this->get_realized())
908     return ;
909 
910   ::Reservation& reservation = this->player().reservation();
911 
912   for (auto const& widget : this->gametype_buttons)
913     if (widget.second->get_active())
914       reservation.game_type = widget.first;
915 
916   //reservation.game_type = static_cast<GameType>(gametype);
917   for (auto const& widget : this->marriage_selector_buttons)
918     if (widget.second->get_active())
919       reservation.marriage_selector = widget.first;
920 
921   reservation.swines
922     = (is_solo(this->game().type())
923        ? this->solo_swines_button->get_active()
924        : this->swines_button->get_active());
925   reservation.hyperswines
926     = (is_solo(this->game().type())
927        ? this->solo_hyperswines_button->get_active()
928        : this->hyperswines_button->get_active());
929 
930   reservation.offer_duty_solo
931     = this->offer_duty_solo_button->get_active();
932 } // void Reservation::update_player_reservation()
933 
934 
935 /** page change
936  ** if the user switches to the first page, activate the default reservation
937  **
938  ** @param    page     the new page
939  ** @param    pageno   the number of the page
940  **/
941 void
switch_page_event(Widget * widget,guint const pageno)942 Reservation::switch_page_event(Widget* widget, guint const pageno)
943 {
944 #ifdef WORKAROUND
945   // else the 'this->is_visible()' segfaults when starting a new tournament
946   if (::game_status != GameStatus::game_reservation)
947     return ;
948 #endif
949   if (!this->is_visible())
950     return ;
951 
952   if (::bug_report_replay) {
953     if (   (pageno == 0)
954         && is_solo(this->game().type())
955         && !is_solo(::bug_report_replay->game_type()))
956       this->set_default();
957     if (   (pageno == 1)
958         && !is_solo(this->game().type())
959         && is_solo(::bug_report_replay->game_type()))
960       this->set_default();
961   } // if (::bug_report_replay)
962 
963   if (!::bug_report_replay) {
964     if (pageno == 0) {
965       if (is_solo(this->game().type())) {
966         // reset the reservation
967         this->game().set_type(GameType::normal);
968         if (!is_solo(this->player().reservation_get_default().game_type))
969           this->set_default();
970       }
971     } // if (pageno == 0)
972   } // if (!::bug_report_replay)
973 } // void Reservation::switch_page_event(Widget*, guint pageno)
974 
975 /** announce the reservation
976  **/
977 void
announce()978 Reservation::announce()
979 {
980   if (!this->announce_button->is_sensitive())
981     return ;
982 
983   this->update_player_reservation();
984 
985   this->hide();
986 #ifdef WINDOWS
987 #ifdef WORKAROUND
988   this->ui->main_window->raise();
989 #endif
990 #endif
991 
992   this->announced = true;
993 } // void Reservation::announce()
994 
995 } // namespace UI_GTKMM_NS
996 
997 #endif // #ifdef USE_UI_GTKMM
998