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