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 "preferences.h"
33 #include "preferences/cards_order.h"
34 #include "preferences/cardset.h"
35 #include "preferences/cards_back.h"
36 #include "preferences/iconset.h"
37 #include "preferences/background.h"
38
39 #include "ui.h"
40 #include "cards.h"
41 #include "main_window.h"
42 #include "menu.h"
43
44 #include "../../misc/preferences.h"
45 #ifdef WINDOWS
46 #include "../../utils/string.h"
47 #endif
48
49 #include "widgets/labelspinbutton.h"
50 #include "widgets/filemenu_extension.h"
51 #include "widgets/filemenu_file.h"
52 #include <gtkmm/notebook.h>
53 #include <gtkmm/label.h>
54 #include <gtkmm/entry.h>
55 #include <gtkmm/checkbutton.h>
56 #include <gtkmm/checkmenuitem.h>
57 #include <gtkmm/separatormenuitem.h>
58 #include <gtkmm/fontchooserdialog.h>
59 #include <gtkmm/colorchooserdialog.h>
60 namespace UI_GTKMM_NS {
61
62 /** constructor
63 **
64 ** @param parent the parent object
65 **/
Preferences(Base * const parent)66 Preferences::Preferences(Base* const parent) :
67 Base(parent),
68 StickyDialog("FreeDoko – " + _("Window::preferences"), false)
69 {
70 this->ui->add_window(*this);
71
72 #ifdef WORKAROUND
73 { // name: change to utf8
74 auto const name = UI_GTKMM::to_utf8(::preferences(::Preferences::Type::name));
75 ::preferences.set(::Preferences::Type::name, name);
76 } // name: change to utf8
77 #endif
78
79 this->group_notebook = Gtk::manage(new Gtk::Notebook());
80 this->group_notebook->set_tab_pos(Gtk::POS_LEFT);
81
82 this->create_backup();
83
84 this->signal_realize().connect(sigc::mem_fun(*this, &Preferences::init));
85 this->signal_show().connect(sigc::mem_fun(*this, &Preferences::update_all));
86
87 #ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
88 this->signal_key_press_event().connect(sigc::mem_fun(*this, &Preferences::on_key_press_event));
89 #endif
90 } // Preferences::Preferences(Base* parent)
91
92 /** destruktor
93 **/
~Preferences()94 Preferences::~Preferences()
95 {
96 for (auto widget : this->type_bool)
97 delete static_cast<::Preferences::Type::Bool*>(widget.second->steal_data("type"));
98
99 delete this->language_menu;
100 for (auto selector : this->font_chooser) {
101 delete static_cast<int*>(selector.second->steal_data("type"));
102 delete selector.second;
103 } // for (selector : this->font_chooser)
104 for (auto selector : this->color_chooser) {
105 delete static_cast<int*>(selector.second->steal_data("type"));
106 delete selector.second;
107 } // for (selector : this->color_chooser)
108 } // Preferences::~Preferences()
109
110 /** creates all subelements
111 **/
112 void
init()113 Preferences::init()
114 {
115 this->set_icon(this->ui->icon);
116
117 this->reset_button = Gtk::manage(new Gtk::Button(_("Button::reset")));
118 this->reset_button->set_image_from_icon_name("edit-undo");
119 this->reset_button->set_always_show_image();
120 this->add_action_widget(*this->reset_button, Gtk::RESPONSE_REJECT);
121 this->reset_button->show();
122 this->reset_button->signal_clicked().connect(sigc::mem_fun(*this,
123 &Preferences::reset)
124 );
125
126 auto close_button = add_close_button(*this);
127 close_button->signal_clicked().connect(sigc::mem_fun(*this,
128 &Preferences::save)
129 );
130
131
132
133 this->language_menu = new Gtk::Menu();
134
135 { // create the buttons
136 for (auto t : ::Preferences::Type::bool_list) {
137 this->type_bool[t] = Gtk::manage(new Gtk::CheckButton(_(t)));
138 //this->type_bool.back()->set_halign(Gtk::ALIGN_CENTER);
139 this->type_bool[t]->set_data("type", new ::Preferences::Type::Bool(t));
140 this->type_bool[t]->signal_toggled().connect(sigc::bind<int>(sigc::mem_fun(*this, &Preferences::changed),
141 t)
142 );
143 } // for (t : ::Preferences::Type::bool_list)
144 for (auto t : ::Preferences::Type::unsigned_list) {
145 this->type_unsigned[t] = Gtk::manage(new Gtk::LabelSpinButton(_(t)));
146 //this->type_unsigned.back()->set_halign(Gtk::ALIGN_CENTER);
147 this->type_unsigned[t]->set_data("type", new ::Preferences::Type::Unsigned(t));
148 this->type_unsigned[t]->signal_value_changed().connect(sigc::bind<int>(sigc::mem_fun(*this, &Preferences::changed),
149 t)
150 );
151 switch (t) {
152 case ::Preferences::Type::card_play_delay:
153 case ::Preferences::Type::full_trick_close_delay:
154 case ::Preferences::Type::gametype_window_close_delay:
155 case ::Preferences::Type::marriage_window_close_delay:
156 case ::Preferences::Type::genscher_window_close_delay:
157 case ::Preferences::Type::announcement_window_close_delay:
158 case ::Preferences::Type::swines_window_close_delay:
159 // make the precision 1/1000 seconds
160 this->type_unsigned[t]->get_spin_button()->set_digits(1);
161 this->type_unsigned[t]->get_spin_button()->set_increments(0.1, 1);
162 break;
163 #ifdef USE_THREADS
164 case ::Preferences::Type::threads_max:
165 #endif
166 case ::Preferences::Type::table_rotation:
167 case ::Preferences::Type::cards_height:
168 case ::Preferences::Type::ui_main_window_pos_x:
169 case ::Preferences::Type::ui_main_window_pos_y:
170 case ::Preferences::Type::ui_main_window_width:
171 case ::Preferences::Type::ui_main_window_height:
172 break;
173 } // switch(t)
174 } // for (t : ::Preferences::Type::unsigned_list)
175 for (auto t : ::Preferences::Type::string_list) {
176 this->type_string_label[t] = Gtk::manage(new Gtk::Label(_(t) + ":"));
177
178 this->type_string_label[t]->set_data("type", new ::Preferences::Type::String(t));
179
180 switch (t) {
181 case ::Preferences::Type::name:
182 this->type_string[t] = Gtk::manage(new Gtk::Entry());
183 dynamic_cast<Gtk::Entry*>(this->type_string[t])->signal_focus_out_event().connect(sigc::bind<int>(sigc::mem_fun(*this, &Preferences::focus_out_event), t));
184 break;
185
186 case ::Preferences::Type::language:
187 this->type_string[t] = Gtk::manage(new Gtk::Button(_(t)));
188 dynamic_cast<Gtk::Button*>(this->type_string[t])->signal_clicked().connect(sigc::mem_fun(*this->language_menu, &Widget::show_all));
189 // dynamic_cast<Gtk::Button*>(this->type_string[t])->signal_clicked().connect(sigc::mem_fun(*this, &Preferences::language_menu_create));
190 dynamic_cast<Gtk::Button*>(this->type_string[t])->signal_clicked().connect(sigc::bind<bool>(sigc::mem_fun(*this, &Preferences::language_menu_create), true));
191 // a direct signal for popping up the menu does not work
192 //dynamic_cast<Gtk::Button*>(this->type_string[t])->signal_clicked().connect(sigc::bind<guint, guint32>(sigc::mem_fun(*this->language_menu, &Gtk::Menu::popup), 0, 0));
193 break;
194
195 case ::Preferences::Type::cardset:
196 case ::Preferences::Type::cards_back:
197 case ::Preferences::Type::iconset:
198 case ::Preferences::Type::background:
199 break;
200
201 case ::Preferences::Type::name_font:
202 case ::Preferences::Type::trickpile_points_font: {
203 auto button = Gtk::manage(new Gtk::Button(_(t)));
204 this->type_string[t] = button;
205 this->font_chooser[t] = new Gtk::FontChooserDialog(_("font selector"));
206 this->font_chooser[t]->set_data("type", new int(t));
207 button->set_data("font selector", this->font_chooser[t]);
208 button->signal_clicked().connect(sigc::mem_fun0(*this->font_chooser[t], &Gtk::Window::present));
209 break;
210 }
211
212 case ::Preferences::Type::name_font_color:
213 case ::Preferences::Type::name_active_font_color:
214 case ::Preferences::Type::name_reservation_font_color:
215 case ::Preferences::Type::trickpile_points_font_color:
216 case ::Preferences::Type::poverty_shift_arrow_color: {
217 auto button = Gtk::manage(new Gtk::Button(_(t)));
218 this->type_string[t] = button;
219 this->color_chooser[t] = new Gtk::ColorChooserDialog(_("color selector"));
220 this->color_chooser[t]->set_data("type", new int(t));
221 button->set_data("color selector", this->color_chooser[t]);
222 button->signal_clicked().connect(sigc::mem_fun0(*this->color_chooser[t], &Gtk::Window::present));
223 break;
224 }
225
226 #ifdef USE_SOUND_COMMAND
227 case ::Preferences::Type::play_sound_command:
228 #endif
229 case ::Preferences::Type::browser_command:
230 this->type_string[t] = Gtk::manage(new Gtk::Entry());
231 dynamic_cast<Gtk::Entry*>(this->type_string[t])->signal_focus_out_event().connect(sigc::bind<int>(sigc::mem_fun(*this, &Preferences::focus_out_event), t));
232 break;
233
234 } // switch(t)
235 if (this->type_string[t]) {
236 this->type_string[t]->set_data("type", new ::Preferences::Type::String(t));
237 }
238 } // for (t : ::Preferences::Type::string_list)
239 } // create the buttons
240
241 { // set the pages of the notebook
242 { // General
243 auto vbox = this->add_group_box("general");
244 (void)_("Preferences::Group::general");
245
246 { // name
247 auto hbox = Gtk::manage(new Gtk::Box());
248 hbox->set_spacing(1 EX);
249 hbox->set_halign(Gtk::ALIGN_CENTER);
250 hbox->add(*this->type_string_label[::Preferences::Type::name]);
251 hbox->add(*this->type_string[::Preferences::Type::name]);
252 vbox->add(*hbox);
253 } // name
254 { // language
255 auto hbox = Gtk::manage(new Gtk::Box());
256 hbox->set_spacing(1 EX);
257 hbox->set_halign(Gtk::ALIGN_CENTER);
258 hbox->add(*this->type_string_label[::Preferences::Type::language]);
259 hbox->add(*this->type_string[::Preferences::Type::language]);
260 vbox->add(*hbox);
261 } // language
262 vbox->add(*this->type_bool[::Preferences::Type::sound]);
263 #ifdef USE_THREADS
264 vbox->add(*this->type_unsigned[::Preferences::Type::threads_max]);
265 #endif
266 vbox->add(*this->type_bool[::Preferences::Type::show_bug_report_button_in_game_finished_window]);
267 vbox->add(*this->type_bool[::Preferences::Type::save_bug_reports_on_desktop]);
268 vbox->add(*this->type_bool[::Preferences::Type::save_party_changes]);
269 vbox->add(*this->type_bool[::Preferences::Type::additional_party_settings]);
270 #ifdef USE_SOUND_COMMAND
271 { // play sound command
272 auto hbox = Gtk::manage(new Gtk::Box());
273 hbox->set_spacing(1 EX);
274 hbox->set_halign(Gtk::ALIGN_CENTER);
275 hbox->add(*this->type_string_label[::Preferences::Type::play_sound_command]);
276 hbox->add(*this->type_string[::Preferences::Type::play_sound_command]);
277 vbox->add(*hbox);
278 } // play sound command
279 #endif
280 { // browser
281 auto hbox = Gtk::manage(new Gtk::Box());
282 hbox->set_spacing(1 EX);
283 hbox->set_halign(Gtk::ALIGN_CENTER);
284 hbox->add(*this->type_string_label[::Preferences::Type::browser_command]);
285 hbox->add(*this->type_string[::Preferences::Type::browser_command]);
286 vbox->add(*hbox);
287 } // browser
288 } // General
289
290 { // Behaviour
291 auto vbox = this->add_group_box("behaviour");
292 (void)_("Preferences::Group::behaviour");
293
294 vbox->add(*this->type_bool[::Preferences::Type::announce_in_table]);
295 vbox->add(*this->type_unsigned[::Preferences::Type::card_play_delay]);
296 } // Behaviour
297 { // Assistance
298 auto vbox = this->add_group_box("assistance");
299 (void)_("Preferences::Group::assistance");
300
301 vbox->add(*this->type_bool[::Preferences::Type::show_soloplayer_in_game]);
302 vbox->add(*this->type_bool[::Preferences::Type::announce_swines_automatically]);
303 vbox->add(*this->type_bool[::Preferences::Type::show_if_valid]);
304 vbox->add(*this->type_bool[::Preferences::Type::emphasize_valid_cards]);
305 } // Assistance
306 { // Help
307 auto vbox = this->add_group_box("help");
308 (void)_("Preferences::Group::help");
309
310 vbox->add(*this->type_bool[::Preferences::Type::show_all_hands]);
311 vbox->add(*this->type_bool[::Preferences::Type::show_ai_information_hands]);
312 vbox->add(*this->type_bool[::Preferences::Type::show_known_teams_in_game]);
313 vbox->add(*this->type_bool[::Preferences::Type::show_ai_information_teams]);
314 vbox->add(*this->type_bool[::Preferences::Type::show_trickpiles_points]);
315 vbox->add(*this->type_bool[::Preferences::Type::automatic_card_suggestion]);
316 } // Help
317 { // information windows
318 auto subnotebook
319 = this->add_group_notebook("information windows");
320 (void)_("Preferences::Group::information windows");
321
322 { // gametype window
323 auto vbox = this->add_subgroup_box(*subnotebook,
324 "gametype");
325 (void)_("Preferences::Subgroup::gametype");
326
327 vbox->add(*this->type_bool[::Preferences::Type::show_gametype_window]);
328 vbox->add(*this->type_bool[::Preferences::Type::close_gametype_window_automatically]);
329 vbox->add(*this->type_unsigned[::Preferences::Type::gametype_window_close_delay]);
330 } // gametype window
331 { // full trick window
332 auto vbox = this->add_subgroup_box(*subnotebook,
333 "full trick");
334 (void)_("Preferences::Subgroup::full trick");
335
336 vbox->add(*this->type_bool[::Preferences::Type::show_full_trick_window]);
337 vbox->add(*this->type_bool[::Preferences::Type::show_full_trick_window_if_special_points]);
338 vbox->add(*this->type_bool[::Preferences::Type::close_full_trick_automatically]);
339 vbox->add(*this->type_unsigned[::Preferences::Type::full_trick_close_delay]);
340 } // full trick window
341 { // marriage finding window
342 auto vbox = this->add_subgroup_box(*subnotebook,
343 "marriage");
344 (void)_("Preferences::Subgroup::marriage");
345
346 vbox->add(*this->type_bool[::Preferences::Type::show_marriage_window]);
347 vbox->add(*this->type_bool[::Preferences::Type::close_marriage_window_automatically]);
348 vbox->add(*this->type_unsigned[::Preferences::Type::marriage_window_close_delay]);
349 } // marriage finding window
350 { // genscher window
351 auto vbox = this->add_subgroup_box(*subnotebook,
352 "genscher");
353 (void)_("Preferences::Subgroup::genscher");
354
355 vbox->add(*this->type_bool[::Preferences::Type::show_genscher_window]);
356 vbox->add(*this->type_bool[::Preferences::Type::close_genscher_window_automatically]);
357 vbox->add(*this->type_unsigned[::Preferences::Type::genscher_window_close_delay]);
358 } // genscher window
359 { // announcement window
360 auto vbox = this->add_subgroup_box(*subnotebook,
361 "announcement");
362 (void)_("Preferences::Subgroup::announcement");
363
364 vbox->add(*this->type_bool[::Preferences::Type::show_announcement_window]);
365 vbox->add(*this->type_bool[::Preferences::Type::close_announcement_window_automatically]);
366 vbox->add(*this->type_unsigned[::Preferences::Type::announcement_window_close_delay]);
367 } // announcement window
368 { // swines window
369 auto vbox = this->add_subgroup_box(*subnotebook,
370 "swines");
371 (void)_("Preferences::Subgroup::swines");
372
373 vbox->add(*this->type_bool[::Preferences::Type::show_swines_window]);
374 vbox->add(*this->type_bool[::Preferences::Type::close_swines_window_automatically]);
375 vbox->add(*this->type_unsigned[::Preferences::Type::swines_window_close_delay]);
376 } // swines window
377 } // Information windows
378 {
379 auto vbox = this->add_group_box("cards order");
380 (void)_("Preferences::Group::cards order");
381 this->cards_order = make_unique<CardsOrder>(*this);
382 vbox->add(*this->cards_order);
383 }
384 { // Appearance
385 auto subnotebook = this->add_group_notebook("appearance");
386 (void)_("Preferences::Group::appearance");
387
388 { // cardset
389 auto vbox = this->add_subgroup_box(*subnotebook,
390 "cardset");
391 (void)_("Preferences::Subgroup::cardset");
392 this->cardset = make_unique<Cardset>(*this);
393 vbox->set_valign(Gtk::ALIGN_FILL);
394 vbox->set_halign(Gtk::ALIGN_FILL);
395 vbox->pack_start(*this->cardset);
396 } // cardset
397 { // cards back
398 auto vbox = this->add_subgroup_box(*subnotebook,
399 "cards back");
400 (void)_("Preferences::Subgroup::cards back");
401 this->cards_back = make_unique<CardsBack>(*this);
402 vbox->set_valign(Gtk::ALIGN_FILL);
403 vbox->set_halign(Gtk::ALIGN_FILL);
404 vbox->pack_start(*this->cards_back);
405 } // cards back
406 { // iconset
407 auto vbox = this->add_subgroup_box(*subnotebook,
408 "iconset");
409 (void)_("Preferences::Subgroup::iconset");
410 this->iconset = make_unique<Iconset>(*this);
411 vbox->set_valign(Gtk::ALIGN_FILL);
412 vbox->set_halign(Gtk::ALIGN_FILL);
413 vbox->pack_start(*this->iconset);
414 } // iconset
415 { // background
416 auto vbox = this->add_subgroup_box(*subnotebook,
417 "background");
418 (void)_("Preferences::Subgroup::background");
419 this->background = make_unique<Background>(*this);
420 vbox->set_valign(Gtk::ALIGN_FILL);
421 vbox->set_halign(Gtk::ALIGN_FILL);
422 vbox->pack_start(*this->background);
423 } // background
424 { // table
425 auto vbox = this->add_subgroup_box(*subnotebook,
426 "table");
427 (void)_("Preferences::Subgroup::table");
428
429 vbox->add(*this->type_bool[::Preferences::Type::own_hand_on_table_bottom]);
430 vbox->add(*this->type_unsigned[::Preferences::Type::table_rotation]);
431 vbox->add(*this->type_bool[::Preferences::Type::rotate_trick_cards]);
432 vbox->add(*this->type_bool[::Preferences::Type::original_cards_size]);
433 vbox->add(*this->type_unsigned[::Preferences::Type::cards_height]);
434 } // table
435 { // Fonts and colors
436 auto vbox = this->add_subgroup_box(*subnotebook,
437 "fonts and colors");
438 (void)_("Preferences::Subgroup::fonts and colors");
439
440 { // the name
441 auto vbox2 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
442 vbox->set_halign(Gtk::ALIGN_CENTER);
443
444 vbox2->add(*this->type_string[::Preferences::Type::name_font]);
445 vbox2->add(*this->type_string[::Preferences::Type::name_font_color]);
446 vbox2->add(*this->type_string[::Preferences::Type::name_active_font_color]);
447 vbox2->add(*this->type_string[::Preferences::Type::name_reservation_font_color]);
448 vbox->add(*vbox2);
449 } // the name
450
451 { // the trickpile points
452 auto vbox2 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
453 vbox->set_halign(Gtk::ALIGN_CENTER);
454
455
456 vbox2->add(*this->type_string[::Preferences::Type::trickpile_points_font]);
457 vbox2->add(*this->type_string[::Preferences::Type::trickpile_points_font_color]);
458
459 vbox->add(*vbox2);
460 } // the trickpile points
461
462 { // poverty arrow
463 vbox->add(*this->type_string[::Preferences::Type::poverty_shift_arrow_color]);
464 } // poverty arrow
465 } // Fonts and colors
466 { // Splash screen
467 auto vbox = this->add_subgroup_box(*subnotebook,
468 "splash screen");
469 (void)_("Preferences::Subgroup::splash screen");
470
471 vbox->add(*this->type_bool[::Preferences::Type::show_splash_screen]);
472 vbox->add(*this->type_bool[::Preferences::Type::splash_screen_transparent]);
473 } // Splash screen
474 } // Appearance
475 } // set the pages of the notebook
476
477 #ifndef RELEASE
478 #ifdef DKNOF
479 { // test, whether all preferences-buttons are packed in a container
480 for (auto t : ::Preferences::Type::bool_list) {
481 if (!this->type_bool[t]->get_parent()
482 && (t != ::Preferences::Type::automatic_savings)
483 && (t != ::Preferences::Type::ui_main_window_fullscreen)
484 ) {
485 cerr << "GTKMM_DOKO::Preferences::Type::Preferences():\n"
486 << " preference '" << to_string(t) << "' not packed\n";
487 }
488 }
489 for (auto t : ::Preferences::Type::unsigned_list) {
490 if (!this->type_unsigned[t]->get_parent()
491 && (t != ::Preferences::Type::ui_main_window_pos_x)
492 && (t != ::Preferences::Type::ui_main_window_pos_y)
493 && (t != ::Preferences::Type::ui_main_window_width)
494 && (t != ::Preferences::Type::ui_main_window_height)
495 ) {
496 cerr << "GTKMM_DOKO::Preferences::Type::Preferences():\n"
497 << " preference '" << to_string(t)
498 << "' not packed\n";
499 }
500 }
501 } // test, whether all preference-buttons are packed in a container
502 #endif
503 #endif
504
505 this->get_content_area()->pack_start(*this->group_notebook);
506
507
508 { // signals
509 for (auto i : this->font_chooser) {
510 auto selector = i.second;
511 selector->signal_response().connect(sigc::bind<Gtk::FontChooserDialog*>(sigc::mem_fun(*this, &Preferences::font_chooser_response), selector));
512 } // for (selector : this->font_chooser)
513
514 for (auto i : this->color_chooser) {
515 auto selector = i.second;
516 auto const type
517 = *static_cast<::Preferences::Type::String*>(selector->get_data("type"));
518
519 selector->signal_response().connect(sigc::bind<Gtk::ColorChooserDialog*>(sigc::mem_fun(*this, &Preferences::color_chooser_response), selector));
520 #ifdef TODO
521 selector->signal_color_changed().connect(sigc::bind<int>(sigc::mem_fun(*this, &Preferences::changed), type));
522 #endif
523 (void)type;
524 } // for (selector : this->color_chooser)
525 } // signals
526
527 this->update_all();
528
529 this->show_all_children();
530
531 #ifdef RELEASE
532 #ifndef WINDOWS
533 #ifndef LINUX
534 this->type_bool[::Preferences::Type::save_bug_reports_on_desktop]->hide();
535 #endif
536 #endif
537 #endif // #ifdef RELEASE
538 } // void Preferences::init()
539
540 /** adds to the group notebook a page with a vbox for group 'name'.
541 **
542 ** @param name name of the group
543 **
544 ** @return created vbox for the preferences of the group
545 **/
546 Gtk::Box*
add_group_box(string const & name)547 Preferences::add_group_box(string const& name)
548 {
549 auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 1 EX));
550 vbox->set_halign(Gtk::ALIGN_CENTER);
551 vbox->set_valign(Gtk::ALIGN_CENTER);
552
553 this->add_group(name, *vbox);
554
555 return vbox;
556 } // Gtk::Box* Preferences::add_group_box(string name)
557
558 /** adds to the group notebook a subnotebook for group 'name'.
559 **
560 ** @param name name of the group
561 **
562 ** @return created notebook for the subgroups
563 **/
564 Gtk::Notebook*
add_group_notebook(string const & name)565 Preferences::add_group_notebook(string const& name)
566 {
567 auto notebook = Gtk::manage(new Gtk::Notebook());
568 this->add_group(name, *notebook);
569 notebook->set_tab_pos(Gtk::POS_LEFT);
570
571 return notebook;
572 } // Gtk::Notebook* Preferences::add_group_notebook(string name)
573
574 /** adds to the group notebook a page of 'widget' for group 'name'.
575 **
576 ** @param name name of the group
577 ** @param widget widget contianing the group
578 **/
579 void
add_group(string const & name,Gtk::Widget & widget)580 Preferences::add_group(string const& name, Gtk::Widget& widget)
581 {
582 widget.show();
583
584 auto label = Gtk::manage(new Gtk::Label(_("Preferences::Group::" + name)));
585 label->show();
586
587 this->group_notebook->append_page(widget, *label);
588 this->group_notebook->child_property_tab_expand(widget).set_value(true);
589 } // void Preferences::add_group(string name, Gtk::Widget& widget)
590
591 /** adds to the subgroup notebook a page with a vbox for group 'name'.
592 **
593 ** @param subnotebook notebook to add the group
594 ** @param name name of the group
595 **
596 ** @return created vbox for the preferences of the group
597 **/
598 Gtk::Box*
add_subgroup_box(Gtk::Notebook & subnotebook,string const & name)599 Preferences::add_subgroup_box(Gtk::Notebook& subnotebook,
600 string const& name)
601 {
602 auto label = Gtk::manage(new Gtk::Label(_("Preferences::Subgroup::" + name)));
603 label->show();
604
605 auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 1 EX));
606 vbox->set_halign(Gtk::ALIGN_CENTER);
607 vbox->set_valign(Gtk::ALIGN_CENTER);
608
609 subnotebook.append_page(*vbox, *label);
610 this->group_notebook->child_property_tab_expand(*vbox).set_value(true);
611 return vbox;
612 } // Gtk::Box* Preferences::add_subgroup_box(Gtk::Notebook& subnotebook, string name)
613
614 /** creates a backup
615 **/
616 void
create_backup()617 Preferences::create_backup()
618 {
619 this->backup_ = make_unique<::Preferences>(::preferences);
620 } // void Preferences::create_backup()
621
622 /** @return the backup
623 **/
624 ::Preferences const&
backup() const625 Preferences::backup() const
626 {
627 DEBUG_ASSERTION(this->backup_,
628 "Preferences::Type::backup():\n"
629 " 'this->backup_' == nullptr");
630
631 return *this->backup_;
632 } // Preferences const& Preferences::backup() const
633
634 /** save the preferences
635 **/
636 void
save()637 Preferences::save()
638 {
639 if (::preferences(::Preferences::Type::automatic_savings))
640 ::preferences.save();
641 } // void Preferences::save()
642
643 /** resets the preferences
644 **/
645 void
reset()646 Preferences::reset()
647 {
648 if (this->backup_)
649 ::preferences = *this->backup_;
650 } // void Preferences::reset()
651
652 /** update the sensitivity of all widgets
653 **/
654 void
sensitivity_update()655 Preferences::sensitivity_update()
656 {
657 for (auto i : this->type_bool) {
658 auto widget = i.second;
659 widget->set_sensitive(::preferences.dependencies(*static_cast<::Preferences::Type::Bool*>(widget->get_data("type"))));
660 }
661 for (auto i : this->type_unsigned) {
662 auto widget = i.second;
663 auto const type
664 = *static_cast<::Preferences::Type::Unsigned*>(widget->get_data("type"));
665
666 widget->set_sensitive(::preferences.dependencies(type));
667 if (type == ::Preferences::Type::cards_height) {
668 // make such a limit, so that a right-click (maximum) does not lead
669 // to a limit problem
670 widget->set_range(::preferences.min(type),
671 10 * this->ui->cards->height_original());
672 } else {
673 widget->set_range(::preferences.min(type), ::preferences.max(type));
674 }
675 } // for (widget : this->type_unsigned)
676 } // void Preferences::sensitivity_update()
677
678 /** update all
679 **/
680 void
update_all()681 Preferences::update_all()
682 {
683 for (auto t : ::Preferences::Type::bool_list)
684 this->update(t, false);
685 for (auto t : ::Preferences::Type::unsigned_list)
686 this->update(t, false);
687 for (auto t : ::Preferences::Type::string_list)
688 this->update(t, false);
689 this->update(::Preferences::Type::cards_order, false);
690
691 this->sensitivity_update();
692
693 this->reset_button->set_sensitive(this->backup() != ::preferences);
694 } // void Preferences::update_all()
695
696 /** update the preferences 'type'
697 **
698 ** @param type the type of the preference
699 **/
700 void
preference_update(int const type)701 Preferences::preference_update(int const type)
702 {
703 this->update(type);
704 this->sensitivity_update();
705 } // void Preferences::preference_update(int type)
706
707 /** update the preference 'type'
708 **
709 ** @param type the type of the preference
710 ** @param update_sensitivity whether the sensitivity shall be updated
711 ** (default: true)
712 **
713 ** @todo style of the font and color buttons
714 **/
715 void
update(int const typex,bool const update_sensitivity)716 Preferences::update(int const typex, bool const update_sensitivity)
717 {
718 if (!this->is_visible())
719 return ;
720
721 if (update_sensitivity)
722 this->sensitivity_update();
723
724 if (contains(::Preferences::Type::bool_list, typex)) {
725 auto const type = static_cast<::Preferences::Type::Bool>(typex);
726 this->type_bool[type]->set_active(::preferences.value(type));
727 switch (type) {
728 case ::Preferences::Type::emphasize_valid_cards:
729 this->ui->main_window->menu->emphasize_valid_cards->set_active(::preferences.value(type));
730 break;
731 case ::Preferences::Type::show_all_hands:
732 this->ui->main_window->menu->show_all_hands->set_active(::preferences.value(type));
733 break;
734 default:
735 break;
736 } // switch(type)
737 } else if (contains(::Preferences::Type::unsigned_list, typex)) {
738 auto const type = static_cast<::Preferences::Type::Unsigned>(typex);
739 switch (type) {
740 case ::Preferences::Type::card_play_delay:
741 case ::Preferences::Type::full_trick_close_delay:
742 case ::Preferences::Type::gametype_window_close_delay:
743 case ::Preferences::Type::marriage_window_close_delay:
744 case ::Preferences::Type::genscher_window_close_delay:
745 case ::Preferences::Type::announcement_window_close_delay:
746 case ::Preferences::Type::swines_window_close_delay:
747 // make one second to correspond with '1.0'
748 this->type_unsigned[type]->set_value((1.0 / 1000) * ::preferences.value(type));
749 break;
750 #ifdef USE_THREADS
751 case ::Preferences::Type::threads_max:
752 #endif
753 case ::Preferences::Type::table_rotation:
754 case ::Preferences::Type::cards_height:
755 this->type_unsigned[type]->set_value(::preferences.value(type));
756 break;
757 case ::Preferences::Type::ui_main_window_pos_x:
758 case ::Preferences::Type::ui_main_window_pos_y:
759 case ::Preferences::Type::ui_main_window_width:
760 case ::Preferences::Type::ui_main_window_height:
761 break;
762 } // switch(type)
763 } else if (contains(::Preferences::Type::string_list, typex)) {
764 auto const type = static_cast<::Preferences::Type::String>(typex);
765 switch (type) {
766 case ::Preferences::Type::name:
767 dynamic_cast<Gtk::Entry*>(this->type_string[type])->set_text(::preferences(type));
768 break;
769 case ::Preferences::Type::language: {
770 this->language_menu_create(false);
771 auto button = dynamic_cast<Gtk::Button*>(this->type_string[type]);
772 if (::preferences.value(type) == "") {
773 button->set_label(_("default"));
774 } else if (::preferences.value(type) == "de") {
775 button->set_label("deutsch");
776 } else if (::preferences.value(type) == "de-alt") {
777 button->set_label("deutsch – alt");
778 } else if (::preferences.value(type) == "en") {
779 button->set_label("english");
780 } else {
781 button->set_label(::preferences.value(type));
782 }
783 break;
784 }
785 case ::Preferences::Type::cardset:
786 case ::Preferences::Type::cards_back:
787 case ::Preferences::Type::iconset:
788 case ::Preferences::Type::background:
789 break;
790 case ::Preferences::Type::name_font:
791 case ::Preferences::Type::trickpile_points_font:
792 static_cast<Gtk::FontChooserDialog*>(this->type_string[type]->get_data("font selector"))->set_font(::preferences(type));
793 #ifdef POSTPONED
794 // ToDo
795 // don't work :-(
796 { // change the style
797 auto style
798 = this->type_string[type]->get_style();
799
800 style->set_font(Pango::FontDescription(::preferences(type)));
801
802 this->type_string[type]->set_style(style);
803 } // change the style
804 #endif // #ifdef POSTPONED
805 break;
806
807 case ::Preferences::Type::name_font_color:
808 case ::Preferences::Type::name_active_font_color:
809 case ::Preferences::Type::name_reservation_font_color:
810 case ::Preferences::Type::trickpile_points_font_color:
811 case ::Preferences::Type::poverty_shift_arrow_color:
812 static_cast<Gtk::ColorChooserDialog*>(this->type_string[type]->get_data("color selector"))->set_rgba(Gdk::RGBA(::preferences(type)));
813 #ifdef POSTPONED
814 // change the color of the text in the buttons
815 auto style
816 = this->type_string[type]->get_style();
817 // = static_cast<Gtk::Button*>(this->type_string[type])->get_child()->get_style();
818
819 Gdk::RGBA color(::preferences(type));
820 this->ui->colormap->alloc_color(color);
821 style->set_fg(Gtk::STATE_normal, color);
822 style->set_text(Gtk::STATE_normal, color);
823 style->set_text_aa(Gtk::STATE_normal, color);
824 style->set_base(Gtk::STATE_normal, color);
825
826 this->type_string[type]->set_style(style);
827 static_cast<Gtk::Button*>(this->type_string[type])->get_child()->set_style(style);
828 #endif // #ifdef POSTPONED
829 break;
830
831 #ifdef USE_SOUND_COMMAND
832 case ::Preferences::Type::play_sound_command:
833 #endif
834 case ::Preferences::Type::browser_command:
835 dynamic_cast<Gtk::Entry*>(this->type_string[type])->set_text(::preferences(type));
836 break;
837 } // switch(type)
838
839 } else if (typex == ::Preferences::Type::cards_order) {
840 this->cards_order->update();
841 } else {
842 DEBUG_ASSERTION(false,
843 "Preferences::update(type, update_sensitivity):\n"
844 " type '" << typex << "' unknown.");
845 }
846
847 if (update_sensitivity)
848 this->reset_button->set_sensitive(this->backup() != ::preferences);
849 } // void Preferences::update(int type, bool update_sensitivity = true)
850
851 /** a preference has been changed by the user
852 **
853 ** @param type the type of the preference
854 **/
855 void
changed(int const typex)856 Preferences::changed(int const typex)
857 {
858 if (contains(::Preferences::Type::bool_list, typex)) {
859 auto const type = static_cast<::Preferences::Type::Bool>(typex);
860 ::preferences.set(type,
861 this->type_bool[type]->get_active());
862 } else if (contains(::Preferences::Type::unsigned_list, typex)) {
863 auto const type = static_cast<::Preferences::Type::Unsigned>(typex);
864 if (this->type_unsigned[type]->get_realized()) {
865 switch (type) {
866 case ::Preferences::Type::card_play_delay:
867 case ::Preferences::Type::full_trick_close_delay:
868 case ::Preferences::Type::gametype_window_close_delay:
869 case ::Preferences::Type::marriage_window_close_delay:
870 case ::Preferences::Type::genscher_window_close_delay:
871 case ::Preferences::Type::announcement_window_close_delay:
872 case ::Preferences::Type::swines_window_close_delay:
873 // make one second to correspond with '1.0'
874 ::preferences.set(type,
875 static_cast<int>(1000 * this->type_unsigned[type]->get_value()));
876 break;
877 #ifdef USE_THREADS
878 case ::Preferences::Type::threads_max:
879 #endif
880 case ::Preferences::Type::table_rotation:
881 case ::Preferences::Type::cards_height:
882 ::preferences.set(type,
883 this->type_unsigned[type]->get_value_as_int());
884 break;
885 case ::Preferences::Type::ui_main_window_pos_x:
886 case ::Preferences::Type::ui_main_window_pos_y:
887 case ::Preferences::Type::ui_main_window_width:
888 case ::Preferences::Type::ui_main_window_height:
889 DEBUG_ASSERTION(false,
890 "Preferences type '" + to_string(type) + " cannot be changed manually\n");
891 break;
892 } // switch(type)
893 } // if (type is realized)
894 } else if (contains(::Preferences::Type::string_list, typex)) {
895 auto const type = static_cast<::Preferences::Type::String>(typex);
896 switch (type) {
897 case ::Preferences::Type::name:
898 ::preferences.set(type,
899 dynamic_cast<Gtk::Entry*>(this->type_string[type])->get_text());
900 break;
901 case ::Preferences::Type::language:
902 break;
903 case ::Preferences::Type::cardset:
904 // -> Cardset
905 break;
906 case ::Preferences::Type::cards_back:
907 // -> CardsBack
908 break;
909 case ::Preferences::Type::iconset:
910 // -> Iconset
911 break;
912 case ::Preferences::Type::background:
913 // -> Background
914 break;
915 case ::Preferences::Type::name_font:
916 case ::Preferences::Type::trickpile_points_font:
917 ::preferences.set(type,
918 static_cast<Gtk::FontChooserDialog*>(this->type_string[type]->get_data("font selector"))->get_font());
919 break;
920 case ::Preferences::Type::name_font_color:
921 case ::Preferences::Type::name_active_font_color:
922 case ::Preferences::Type::name_reservation_font_color:
923 case ::Preferences::Type::trickpile_points_font_color:
924 case ::Preferences::Type::poverty_shift_arrow_color:
925 ::preferences.set(type,
926 colorname(static_cast<Gtk::ColorChooserDialog*>(this->type_string[type]->get_data("color selector"))->get_rgba()));
927 break;
928
929 #ifdef USE_SOUND_COMMAND
930 case ::Preferences::Type::play_sound_command:
931 #endif
932 case ::Preferences::Type::browser_command:
933 ::preferences.set(type,
934 dynamic_cast<Gtk::Entry*>(this->type_string[type])->get_text());
935 break;
936 } // switch(type)
937 } // if (type == ())
938 // cards order is managed in 'preferences/cards_order.cpp'
939 } // void Preferences::changed(int type)
940
941 /** a preference has been changed by the user
942 **
943 ** @param focus ignored
944 ** @param type the type of the preference
945 **/
946 bool
focus_out_event(GdkEventFocus * event,int const type)947 Preferences::focus_out_event(GdkEventFocus* event, int const type)
948 {
949 this->changed(type);
950
951 return true;
952 } // bool Preferences::focus_out_event(GdkEventFocus* event, int type)
953
954 /** open the cards order
955 **/
956 void
open_cards_order()957 Preferences::open_cards_order()
958 {
959 this->realize();
960 this->group_notebook->set_current_page(5);
961 this->present();
962 } // void Preferences::open_cards_order()
963
964 /** create and show the language menu
965 **
966 ** @param popup whether to popup the menu (default: true)
967 **/
968 void
language_menu_create(bool const popup)969 Preferences::language_menu_create(bool const popup)
970 {
971 #if 0
972 if (this->language_menu == NULL)
973 return ;
974 #endif
975
976 { // remove all children
977 auto children = this->language_menu->get_children();
978
979 if (!children.empty()) {
980 for (auto child : children)
981 this->language_menu->remove(*child);
982 } // if (!children.empty())
983 } // remove all children
984
985 // the language menu consists of the default button, a separator and
986 // the languages (taken from '::language'
987 Gtk::MenuItem* item = nullptr;
988
989 // memory leak: the translation created in 'Ui::->translations->add' is not removed
990 item = Gtk::manage(new Gtk::MenuItem(_("default")));
991 item->signal_activate().connect(sigc::bind<string>(sigc::mem_fun(*this, &Preferences::language_selected), ""));
992 this->language_menu->add(*item);
993 item = Gtk::manage(new Gtk::SeparatorMenuItem());
994 this->language_menu->add(*item);
995
996 vector<std::pair<string, string>> const names
997 = {{"deutsch", "de"},
998 {"deutsch - alt", "de-alt"},
999 {"english", "en"}};
1000 for (auto name : names) {
1001 item = Gtk::manage(new Gtk::MenuItem(name.first));
1002 item->signal_activate().connect(sigc::bind<string>(sigc::mem_fun(*this, &Preferences::language_selected), name.second));
1003 this->language_menu->add(*item);
1004 } // for (name : names)
1005
1006 if (popup)
1007 this->language_menu->popup(0, 0);
1008
1009 this->language_menu->show_all();
1010 } // void Preferences::language_menu_create(bool popup = true)
1011
1012 /** change of the language
1013 **
1014 ** @param language new language
1015 **/
1016 void
language_selected(string const language)1017 Preferences::language_selected(string const language)
1018 {
1019 ::preferences.set(::Preferences::Type::language, language);
1020 } // void Preferences::language_selected(string language)
1021
1022 /** change of the cardset
1023 **
1024 ** @param cardset new cardset
1025 **/
1026 void
cardset_selected(string const cardset)1027 Preferences::cardset_selected(string const cardset)
1028 {
1029 ::preferences.set(::Preferences::Type::cardset, cardset);
1030 } // void Preferences::cardset_selected(string cardset)
1031
1032 /** the font chooser has emmited a response
1033 **
1034 ** @param response_id id of the response
1035 ** @param font_chooser the font selector
1036 **/
1037 void
font_chooser_response(int const response_id,Gtk::FontChooserDialog * const font_chooser)1038 Preferences::font_chooser_response(int const response_id,
1039 Gtk::FontChooserDialog* const font_chooser)
1040 {
1041 if (response_id == Gtk::RESPONSE_OK) {
1042 auto const type
1043 = *static_cast<::Preferences::Type::String*>(font_chooser->get_data("type"));
1044 font_chooser->hide();
1045 this->changed(type);
1046 } else {
1047 font_chooser->hide();
1048 }
1049 } // void Preferences::font_chooser_response(int response_id, Gtk::FontChooserDialog* font_chooser)
1050
1051 /** the color of the preference 'type' has emmited a response
1052 **
1053 ** @param response_id id of the response
1054 ** @param color_chooser the color selector
1055 **/
1056 void
color_chooser_response(int const response_id,Gtk::ColorChooserDialog * const color_chooser)1057 Preferences::color_chooser_response(int const response_id,
1058 Gtk::ColorChooserDialog* const color_chooser)
1059 {
1060 if (response_id == Gtk::RESPONSE_OK) {
1061 auto const type
1062 = *static_cast<::Preferences::Type::String*>(color_chooser->get_data("type"));
1063 //color_chooser->set_rgba(Gdk::RGBA(::preferences(type)));
1064 //::preferences.set(type, color_chooser->get_rgba().to_string());
1065 color_chooser->hide();
1066 this->changed(type);
1067 } else {
1068 color_chooser->hide();
1069 }
1070 } // void Preferences::color_chooser_response(int response_id, Gtk::ColorChooserDialog* color_chooser)
1071
1072 /** a key has been pressed
1073 ** C-o: output of the preferences on 'stdout'
1074 **
1075 ** @param key the key
1076 **
1077 ** @return whether the key was managed
1078 **/
1079 bool
on_key_press_event(GdkEventKey * key)1080 Preferences::on_key_press_event(GdkEventKey* key)
1081 {
1082 bool managed = false;
1083
1084 if ((key->state & ~GDK_SHIFT_MASK) == GDK_CONTROL_MASK) {
1085 switch (key->keyval) {
1086 case GDK_KEY_o: // ouput of the preferences
1087 cout << ::preferences;
1088 managed = true;
1089 break;
1090 } // switch (key->keyval)
1091 } // if (key->state == GDK_CONTROL_MASK)
1092
1093 return (managed
1094 || this->StickyDialog::on_key_press_event(key)
1095 || this->ui->key_press(key));
1096 } // bool Preferences::on_key_press_event(GdkEventKey* key)
1097
1098 } // namespace UI_GTKMM_NS
1099
1100 #endif // #ifdef USE_UI_GTKMM
1101