/********************************************************************** * * FreeDoko a Doppelkopf-Game * * Copyright (C) 2001-2007 by Diether Knof and Borg Enders * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You can find this license in the file 'gpl.txt'. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * Contact: * Diether Knof dknof@gmx.de * Borg Enders borg@borgsoft.de * *********************************************************************/ #include "constants.h" #ifdef USE_UI_GTKMM #include "ui.h" #include "translations.h" #include "preferences.h" #include "table.h" #include "players.h" #include "gameinfo_dialog.h" #include "game_finished.h" #include "game_review.h" #include "party_points.h" #include "main_window.h" #include "menu.h" #include "chatter.h" #include "network_log.h" #include "bug_report.h" #include "error.h" #include "splash_screen.h" #include "first_run.h" #include "program_updated.h" #include "../help.h" #include "../../basistypes.h" #include "../../party/party.h" #include "../../game/game.h" #include "../../card/trick.h" #include "../../player/human/human.h" #include "../../misc/setting.h" #ifndef RELEASE #include "../../player/ai/cards_information.h" #include "../../player/ai/team_information.h" #endif #include "widgets/stock_button.h" #include #include #include #include #include #include #include namespace UI_GTKMM_NS { /** ** -> result ** ** @param text text to convert ** ** @return utf8 format of 'text' ** ** @author Diether Knof ** ** @version 0.7.3 **/ Glib::ustring UI_GTKMM::to_utf8(string const& text) { // I do not know any function to identify the encoding of a string. // So we decide according to the german umlauts latin1-characters. // searches for german umlauts if (text.find_first_of("\xf6\xe4\xc4\xfc\xdc\xd6\xdf") == string::npos) return text; try { return Glib::locale_to_utf8(text); } catch (Glib::ConvertError const error) { try { return Glib::convert(text, "utf8", "latin1"); } catch (Glib::ConvertError const error) { } // try } // try return text; } // static Glib::ustring UI_GTKMM::to_utf8(string text) /** ** add a part to the list ** ** @param base part to add ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void UI_GTKMM::add_part(Base& base) { this->parts.push_back(&base); return ; } // void UI_GTKMM::add_part(Base& base) /** ** add a window to the list ** ** @param window window to add ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void UI_GTKMM::add_window(Gtk::Window& window) { this->windows.push_back(&window); return ; } // void UI_GTKMM::add_window(Gtk::Window& window) /** ** updates the UI ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.5.4 **/ void UI_GTKMM::update() { this->thrower.inc_depth(); while(this->kit->events_pending()) this->kit->iteration(); this->thrower.dec_depth(); return ; } // void UI_GTKMM::update() /** ** sleeps the given time ** (in miliseconds, UINT_MAX for infinity) ** ** @param sleep_usec time to sleep ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.1 ** ** @todo remove variable 'sleeping' **/ void UI_GTKMM::sleep(unsigned const sleep_usec) { sigc::connection timeout_connection = Glib::signal_timeout().connect(&UI_GTKMM::stop_sleeping, //sigc::mem_fun(*(this->kit), //&Gtk::Main::quit), sleep_usec); UI_GTKMM::sleeping = true; this->thrower.inc_depth(); while (!this->thrower && UI_GTKMM::sleeping) Gtk::Main::iteration(); timeout_connection.disconnect(); this->thrower.dec_depth(); return ; } // void UI_GTKMM::sleep(unsigned const sleep_usec) /** ** quits the innermost main loop of gtk ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.5.4 **/ void UI_GTKMM::main_quit() { if (this->kit->level() > 0) this->kit->quit(); return ; } // void UI_GTKMM::main_quit() /** ** quits the innermost main loop of gtk ** ** @param - ** ** @return true ** ** @author Diether Knof ** ** @version 0.6.1 **/ bool UI_GTKMM::stop_sleeping() { if (Gtk::Main::level() > 0) Gtk::Main::quit(); UI_GTKMM::sleeping = false; return true; } // static bool UI_GTKMM::stop_sleeping() /** ** quits the program ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.5.4 **/ void UI_GTKMM::quit_program() { this->thrower(GAMESTATUS::QUIT, __FILE__, __LINE__); this->main_quit(); return ; } // void UI_GTKMM::quit_program() /** ** a key has been pressed ** ** @param key the key ** ** @return true (stop other handlers from being invoked for the event) ** ** @author Diether Knof ** ** @version 0.6.1 ** ** @todo some shortcuts (search 'ToDo') **/ bool UI_GTKMM::key_press(GdkEventKey const* const key) { #if 0 struct GdkEventKey { GdkEventType type; GdkWindow *window; gint8 send_event; guint32 time; guint state; guint keyval; gint length; gchar *string; guint16 hardware_keycode; guint8 group; }; #endif if (this->table && this->table->in_game_review()) if (!this->table->game_finished_->game_review->key_press(key)) return true; if (key->state == GDK_SHIFT_MASK) { switch (key->keyval) { case GDK_Escape: // quit the program this->quit_program(); break; } // switch (key->keyval) } // if (key->state == GDK_SHIFT_MASK) if (::game_status == GAMESTATUS::GAME_PLAY) { // play a card? Player& player = const_cast(this->game().player_current()); if (player.type() == Player::HUMAN) { SortedHand& hand = player.sorted_hand(); if (!(key->state & ~GDK_SHIFT_MASK)) { { // request a card to play unsigned requested_cardno = UINT_MAX; switch (key->keyval) { case GDK_1: requested_cardno = 0; break; case GDK_2: requested_cardno = 1; break; case GDK_3: requested_cardno = 2; break; case GDK_4: requested_cardno = 3; break; case GDK_5: requested_cardno = 4; break; case GDK_6: requested_cardno = 5; break; case GDK_7: requested_cardno = 6; break; case GDK_8: requested_cardno = 7; break; case GDK_9: requested_cardno = 8; break; case GDK_0: case GDK_a: requested_cardno = 9; break; case GDK_b: requested_cardno = 10; break; case GDK_c: requested_cardno = 11; break; } // switch (key->keyval) if (this->table->get_card()) { if ( (requested_cardno != UINT_MAX) && !hand.played(requested_cardno) && player.game().trick_current().isvalid(hand.card_all(requested_cardno)) ) { hand.request_position_all(requested_cardno); this->thrower(hand.requested_card(), __FILE__, __LINE__); } } // if (this->table->card_get()) } // request a card to play } // if ((key->state & ~GDK_SHIFT_MASK) == 0) } // if (player.type() == Player::HUMAN) } // if (::game_status == GAMESTATUS::GAME_PLAY) if (!(key->state & ~GDK_SHIFT_MASK)) { switch (key->keyval) { case GDK_Clear: // close windows #ifdef DKNOF COUT << "Space" << endl; #endif // @todo all break; #ifdef USE_NETWORK // when network is activated, the menu takes care of the key case GDK_c: // show chat window // -> menu.cpp this->main_window->menu->chatter->activate(); break; case GDK_L: // show network log this->network_log->present(); break; #endif case GDK_l: // show last trick // -> menu.cpp if (this->main_window->menu->last_trick->is_sensitive()) this->main_window->menu->last_trick->activate(); break; #ifndef RELEASE case GDK_i: // show game information // -> menu.cpp if (this->main_window->menu->game_debug->is_sensitive()) this->main_window->menu->game_debug->activate(); break; #endif case GDK_n: // next announcement if ((::game_status >= GAMESTATUS::GAME_PLAY) && (::game_status < GAMESTATUS::GAME_FINISHED)) if (this->game().humanno() == 1) { this->game().make_announcement(this->game().human_player()->next_announcement(), *this->game().human_player() ); } // if (only one human break; case GDK_m: // announcement reply if ((::game_status >= GAMESTATUS::GAME_PLAY) && (::game_status < GAMESTATUS::GAME_FINISHED)) if (this->game().humanno() == 1) this->game().make_announcement(ANNOUNCEMENT::REPLY, *(this->game().human_player()) ); break; case GDK_h: // card suggestion // -> menu.cpp if (this->main_window->menu->card_suggestion->is_sensitive()) this->main_window->menu->card_suggestion->activate(); break; case GDK_r: // show the rules // -> menu.cpp this->main_window->menu->rules->activate(); break; case GDK_p: // show the party points // -> menu.cpp if (this->main_window->menu->party_points->is_sensitive()) { this->ui->table->party_points_->present(); this->ui->table->party_points_->notebook->set_current_page(0); } break; case GDK_g: // show the party points graph if (this->main_window->menu->party_points->is_sensitive()) { this->ui->table->party_points_->present(); this->ui->table->party_points_->notebook->set_current_page(1); } break; case GDK_P: // show the players // -> menu.cpp this->main_window->menu->players->activate(); break; #ifndef RELEASE case GDK_e: // print human cards information if ((::game_status & GAMESTATUS::GAME) && this->game().human_player()) { cout << '\n' << this->game().human_player()->name() << ": " << this->game().human_player()->cards_information() << '\n'; } break; case GDK_t: // print human team information if ((::game_status & GAMESTATUS::GAME) && this->game().human_player()) { cout << '\n' << this->game().human_player()->name() << ": " << this->game().human_player()->team_information() << '\n'; } break; case GDK_T: // print all team information if (::game_status & GAMESTATUS::GAME) { for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { if (!dynamic_cast(*p)) { cout << '\n' << (*p)->name() << ": no ai\n"; continue; } cout << '\n' << (*p)->name() << ": " << dynamic_cast(*p)->team_information() << '\n'; } // for (p) } break; #endif // #ifndef RELEASE case GDK_F1: // show manual // -> menu.cpp this->main_window->menu->help->activate(); break; case GDK_F2: // show preferences // -> menu.cpp this->main_window->menu->preferences->activate(); break; case GDK_Pause: // create a bug report case GDK_Break: // create a bug report this->bug_report->create_report(); break; case GDK_period: // toggle show valid cards // -> menu.cpp this->main_window->menu->show_all_hands->activate(); break; case GDK_Escape: break; } // switch (key->keyval) } // if (key->state == 0) if ((key->state & ~GDK_SHIFT_MASK) == GDK_CONTROL_MASK) { switch (key->keyval) { case GDK_c: // exit the program exit(EXIT_SUCCESS); break; case GDK_f: // toggle fullscreen if (this->main_window->get_window()->get_state() & Gdk::WINDOW_STATE_FULLSCREEN) this->main_window->unfullscreen(); else this->main_window->fullscreen(); break; case GDK_n: // start new party // -> menu.cpp this->main_window->menu->new_party->activate(); break; case GDK_l: // load party // -> menu.cpp this->main_window->menu->load_party->activate(); break; case GDK_s: // save party // -> menu.cpp this->main_window->menu->save_party->activate(); break; case GDK_q: // quit the program // -> menu.cpp this->main_window->menu->quit->activate(); break; case GDK_r: // redraw all this->redraw_all(); break; case GDK_0: // default card size ::setting.set(Setting::ORIGINAL_CARDS_SIZE, true); break; case GDK_plus: // increase card size ::setting.set(Setting::CARDS_HEIGHT, ::setting(Setting::CARDS_HEIGHT) + 1); ::setting.set(Setting::ORIGINAL_CARDS_SIZE, false); break; case GDK_minus: // decrease card size ::setting.set(Setting::CARDS_HEIGHT, ::setting(Setting::CARDS_HEIGHT) - 1); ::setting.set(Setting::ORIGINAL_CARDS_SIZE, false); break; } // switch (key->keyval) } // if (key->state == GDK_CONTROL_MASK) return true; } // bool UI_GTKMM::key_press(GdkEventKey const* const key) /** ** ** @param page the page to be loaded ** ** @return - ** ** @author Diether Knof ** ** @version 0.5.2 ** ** @todo all **/ void UI_GTKMM::help_load(string const& page) { return ; } // void UI_GTKMM::help_load(string const& page) /** ** ** @param player name of the player who has chatted ** @param message message ** @param self whether this player has chattet ** (else the chatter comes from the network) ** default: true ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.8 **/ void UI_GTKMM::chat(string const& player, string const& message, bool const self) { this->chatter->chat(player, message); return ; } // void UI_GTKMM::chat(string player, string message, bool self = true) /** ** this is the first run of the program ** ** @param message message for the user ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.0 **/ void UI_GTKMM::first_run(Translator::Translation const& message) { DEBUG_ASSERTION((this->first_run_window == NULL), "UI_GTKMM::first_run(message)\n" " the window is already created."); this->first_run_window = new FirstRun(this, message); this->first_run_window->show(); return ; } // void UI_GTKMM::first_run(Translator::Translation message) /** ** the program is updated ** ** @param old_version the old version ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.0 **/ void UI_GTKMM::program_updated(DK::Utils::Version const& old_version) { DEBUG_ASSERTION((this->program_updated_window == NULL), "UI_GTKMM::program_updated(old_version)\n" " the window is already created."); this->program_updated_window = new ProgramUpdated(this, old_version); this->program_updated_window->show(); return ; } // void UI_GTKMM::program_updated(DK::Utils::Version old_version) /** ** ** @param message message to display ** @param type the type of the message ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.4 ** ** @todo remove memory leak ** @todo add a parameter for a title ** @todo put the windows on top of all windows ** ** @bug memory leak (the dialog is never deleted) **/ void UI_GTKMM::information(Translator::Translation const& message, INFORMATION::Type const type) { Gtk::MessageType message_type = Gtk::MESSAGE_INFO; switch (type) { case INFORMATION::NORMAL: message_type = Gtk::MESSAGE_INFO; break; case INFORMATION::WARNING: message_type = Gtk::MESSAGE_WARNING; break; case INFORMATION::PROBLEM: message_type = Gtk::MESSAGE_ERROR; break; } // switch(type) Gtk::MessageDialog* information = new Gtk::MessageDialog(message.translation(), false, message_type, Gtk::BUTTONS_NONE, false); try { this->icon = Gdk::Pixbuf::create_from_file(::setting.path(Setting::ICON_FILE)); } catch (Glib::FileError file_error) { ::ui->error(::translation("Error::loading icon (%sfile%)", ::setting.path(Setting::ICON_FILE)).translation()); } catch (Gdk::PixbufError pixbuf_error) { ::ui->error(::translation("Error::loading icon (%sfile%)", ::setting.path(Setting::ICON_FILE)).translation()); } // try // memory leak: no call of 'ui->remove_from_translation(*close_button)' Gtk::Button* close_button = Gtk::manage(new Gtk::StockButton(Gtk::Stock::CLOSE, "close")); this->translations->add(*close_button, ::translation("close")); information->add_action_widget(*close_button, Gtk::RESPONSE_CLOSE); close_button->show_all(); close_button->signal_clicked().connect(sigc::mem_fun(*information, &Gtk::Widget::hide) ); #ifdef OUTDATED if (this->main_window) information->set_transient_for(*this->main_window); #endif if (type == INFORMATION::PROBLEM) { information->set_modal(); Gtk::Main::run(*information); delete information; } else { information->present(); } return ; } // void UI_GTKMM::information(Translator::Translation message, INFORMATION::Type type) /** ** shown an error window ** and quit the program ** ** @param message message to display ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.3 **/ void UI_GTKMM::error(string const& message) { static bool already_running = false; if (already_running) { cerr << '\n' << ::translation("Press to exit.").translation() << endl; cin.get(); } else { // if !(already_running) already_running = true; if (this->splash_screen) this->splash_screen->hide(); Error error(message, this); #ifdef RELEASE error.run(); #else error.present(); while (error.is_visible()) { this->sleep(1000); this->thrower.clear(); } #endif } // if !(already_running) #ifdef ASSERTION_GENERATES_SEGFAULT cerr << ::translation("Creating segmentation fault.").translation() << endl; SEGFAULT; #endif // #ifdef ASSERTION_GENERATES_SEGFAULT ::exit(EXIT_FAILURE); return ; } // void UI_GTKMM::error(string const& message) /** ** generate an error (for testings) ** ** @param error_message error message ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.3 **/ void UI_GTKMM::generate_error(string const error_message) { ::generate_error(error_message); return ; } // void UI_GTKMM::generate_error(string const error_message) /** ** iconifies all windows ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void UI_GTKMM::iconify_all_windows() { for (std::list::iterator w = this->windows.begin(); w != this->windows.end(); ++w) (*w)->iconify(); return ; } // void UI_GTKMM::conify_all_windows() /** ** deiconifies all windows ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void UI_GTKMM::deiconify_all_windows() { for (std::list::iterator w = this->windows.begin(); w != this->windows.end(); ++w) (*w)->deiconify(); return ; } // void UI_GTKMM::deiconify_all_windows() } // namespace UI_GTKMM_NS #endif // #ifdef USE_UI_GTKMM