/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2009  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 "players.h"
#include "player.h"
#include "aiconfig.h"
#include "players_db.h"
#include "ui.h"
#include "translations.h"
#include "utils.h"
#include "widgets/stock_button.h"

#include "../../party/party.h"
#include "../../player/ai/ai.h"
#include "../../misc/translator.h"

#include "widgets/labelspinbutton.h"
#include <gtkmm/table.h>
#include <gtkmm/label.h>
#include <gtkmm/frame.h>
#include <gtkmm/stock.h>
#include <gtkmm/alignment.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/entry.h>
#include <gtkmm/notebook.h>

namespace UI_GTKMM_NS {

  /**
   ** constructor
   **
   ** @param     parent   the parent object
   **
   ** @return    -
   ** 
   ** @author    Diether Knof
   **
   ** @version   0.5.4
   **/
  Players::Players(Base* const parent) :
    Base(parent),
  StickyDialog("Players", false),
  player_(),
    players_notebook(NULL)
  {
    this->ui->add_window(*this);

    this->signal_realize().connect(sigc::mem_fun(*this, &Players::init));
    this->signal_show().connect(sigc::mem_fun(*this, &Players::sensitivity_update));
#ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
    this->signal_key_press_event().connect(sigc::mem_fun(*this, &Players::on_key_press_event));
#endif

    this->create_backup();

    return ;
  } // Players::Players(Base* const parent)

  /**
   ** Destruktor
   **
   ** @param     -
   **
   ** @return    -
   ** 
   ** @author    Diether Knof
   **
   ** @version   0.5.4
   **/
  Players::~Players()
  {
    for (vector<Player*>::iterator player = this->player_.begin();
	 player != this->player_.end();
	 player++)
      delete *player;

    return ;
  } // Players::~Players()

  /**
   ** creates all subelements
   **
   ** @param     -
   **
   ** @return    -
   ** 
   ** @author    Diether Knof
   **
   ** @version   0.6.6
   **
   ** @todo	support for more than four players
   **/
  void
    Players::init()
    {
      this->set_icon(this->ui->icon);
      this->ui->translations->add(*this, ::translation("FreeDoko %ttitle%",
						      ::translation("~players")));

      { // set default size
	Gdk::Geometry geometry;
	geometry.min_width = 70 ex;
	geometry.min_height = 10 ex;

	this->set_geometry_hints(*this, geometry, Gdk::HINT_MIN_SIZE);
      } // set default size

      for (unsigned p = 0; p < ::party.playerno(); p++)
	this->player_.push_back(new Player(this, p));

      Gtk::StockButton* close_button
	= Gtk::manage(new Gtk::StockButton(Gtk::Stock::CLOSE, "close"));
      close_button->signal_clicked().connect(sigc::mem_fun(*this, &Gtk::Widget::hide));
      this->ui->translations->add(*close_button,
				  ::translation("close"));

      { // layout
	{ // action area
	  this->add_action_widget(*close_button, Gtk::RESPONSE_CLOSE);
	  close_button->grab_focus();
	} // action area

	{ // players configurations
	  this->players_notebook = Gtk::manage(new Gtk::Notebook());
	  this->players_notebook->set_tab_pos(Gtk::POS_LEFT);
	  this->get_vbox()->add(*this->players_notebook);

	  vector<Gtk::Notebook*> player_notebook;
	  for (unsigned p = 0; p < ::party.playerno(); ++p) {
	    Players::Player& player = this->player(p);

	    Gtk::VBox* player_box = Gtk::manage(new Gtk::VBox(false, 3 ex / 2));

	    this->players_notebook->append_page(*player_box,
						*(player.name_label));

	    { // buttons
	      Gtk::Alignment* alignment
		= Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER,
						 Gtk::ALIGN_CENTER,
						 0, 0));
	      player_box->pack_end(*alignment, false, true);
	      alignment->set_border_width(1 ex / 2);
	      Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox(true, 0));
	      alignment->add(*hbox);
	      { // reset button
		hbox->add(*player.reset_button);
	      } // reset button
	      { // empty space
		hbox->add(*(Gtk::manage(new Gtk::Alignment())));
	      } // empty space
	      { // statistics button
		Gtk::Button* statistics_button
		  = Gtk::manage(new Gtk::Button("statistics"));
		this->ui->translations->add(*statistics_button,
					    ::translation("statistics"));
		hbox->add(*statistics_button);
		statistics_button->signal_clicked().connect(sigc::mem_fun0(*(this->ui->players_db), &Gtk::Window::present));
	      } // statistics button
	    } // buttons


	    player_notebook.push_back(Gtk::manage(new Gtk::Notebook()));
	    player_box->pack_start(*(player_notebook.back()), true, true);
	    { // general page
	      Gtk::Label* general_label
		= Gtk::manage(new Gtk::Label("general"));
	      this->ui->translations->add(*general_label,
					  ::translation("general"));

	      Gtk::Alignment* alignment
		= Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER,
						 Gtk::ALIGN_CENTER,
						 0, 0));
	      player_notebook.back()->append_page(*alignment, *general_label);

	      Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox(false, 2 ex));
	      vbox->set_border_width(1 ex);
	      alignment->add(*vbox);

	      { // the name widgets
		Gtk::Alignment* alignment
		  = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER,
						   0, 0)); 
		vbox->add(*alignment);

		Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox(false, 10));
		alignment->add(*hbox);

		Gtk::Label* name_label = Gtk::manage(new Gtk::Label("name:"));
		this->ui->translations->add(*name_label,
					    ::translation("%ttranslation%: ",
							  ::translation("name")));

		hbox->add(*name_label);
		player.name_entry->set_width_chars(12);
		hbox->add(*player.name_entry);
		hbox->add(*player.random_name_button);
	      } // the name widgets
	      { // the voice widgets
		Gtk::Alignment* alignment
		  = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER,
						   0, 0)); 
		vbox->add(*alignment);

		Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox(false, 10));
		alignment->add(*hbox);

		Gtk::Label* voice_label = Gtk::manage(new Gtk::Label("voice:"));
		this->ui->translations->add(*voice_label,
					    ::translation("%ttranslation%: ",
							  ::translation("voice")));

		hbox->add(*voice_label);
		hbox->add(*player.voice_button);
	      } // the voice widgets
	      { // the type widgets
		Gtk::Alignment* alignment
		  = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER,
						   0, 0)); 
		vbox->add(*alignment);

		Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox(false, 1 ex / 2));
		alignment->add(*vbox);

		for (vector<Gtk::RadioButton*>::iterator type
		     = player.type_button.begin();
		     type != player.type_button.end();
		     type++)
		  vbox->add(**type);
	      } // the type widgets
	      { // the ai difficulty
		vbox->add(*player.aiconfig().difficulty_container);
	      } // the ai difficulty
	    } // general page
	    { // aiconfig
	      Gtk::Label* aiconfig_label
		= Gtk::manage(new Gtk::Label("aiconfig"));
	      this->ui->translations->add(*aiconfig_label,
					  ::translation("aiconfig"));

	      player_notebook.back()->append_page(*player.aiconfig().container,
						  *aiconfig_label);

	    } // aiconfig
	  } // for (p < ::party.playerno())

	  // set the signals for the notebook changes

	  for (vector<Gtk::Notebook*>::iterator notebook1
	       = player_notebook.begin();
	       notebook1 != player_notebook.end();
	       notebook1++)
	    for (vector<Gtk::Notebook*>::iterator notebook2
		 = player_notebook.begin();
		 notebook2 != player_notebook.end();
		 notebook2++) {
	      if (notebook1 == notebook2)
		continue;

	      (*notebook1)->signal_switch_page().connect(sigc::bind<Gtk::Notebook*>(sigc::mem_fun(*this, &Players::notebook_switch_page_signal), *notebook2));
	    }

	  for (vector<Player*>::iterator player1 = this->player_.begin();
	       player1 != this->player_.end();
	       player1++) {
	    for (vector<Player*>::iterator player2 = this->player_.begin();
		 player2 != this->player_.end();
		 player2++) {
	      if (player1 == player2)
		continue;

	      (*player1)->aiconfig().notebook->signal_switch_page().connect(sigc::bind<Gtk::Notebook*>(sigc::mem_fun(*this, &Players::notebook_switch_page_signal), (*player2)->aiconfig().notebook));

	      for (unsigned i = 0;
		   i < (*player1)->aiconfig().notebooks.size();
		   ++i)
		(*player1)->aiconfig().notebooks[i]->signal_switch_page().connect(sigc::bind<Gtk::Notebook*>(sigc::mem_fun(*this, &Players::notebook_switch_page_signal), (*player2)->aiconfig().notebooks[i]));
	    } // for (player2)
	  } // for (player1)
	} // players configurations
#ifdef OUTDATED
	{ // players configurations
	  Gtk::ScrolledWindow* scrolled_window
	    = Gtk::manage(new Gtk::ScrolledWindow());
	  scrolled_window->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	  this->get_vbox()->add(*scrolled_window);

	  int const rows_num = (1 // name
				+ 1 // random name
				+ 1 // aiconfig
				+ AITYPE::NUMBER - 1
				+ Aiconfig::BOOL_NUMBER
				+ Aiconfig::UNSIGNED_NUMBER
				+ 10 // groups
				+ 5 // aiconfig buttons
				+ 1); // reset buttons
	  Gtk::Table* table = Gtk::manage(new Gtk::Table(rows_num, 2));
	  table->set_border_width(static_cast<int>(1.5 ex));
	  table->set_col_spacings(2 em);
	  table->set_row_spacings(0);

	  scrolled_window->add(*table);

	  int row = 0;
	  int group_depth = 1;
	  { // player names
	    { // name
	      Gtk::Table* table2 = Gtk::manage(new Gtk::Table(1, ::party.playerno(),
							      true));
	      table->attach(*table2, 1, 2, row, row + 1,
			    Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
	      table2->set_col_spacings(1 em / 2);
	      for (unsigned p = 0; p < ::party.playerno(); ++p) {
		this->player(p).name_entry->set_width_chars(8);
		table2->attach(*(this->player(p).name_entry),
			       p, p + 1, 0, 1);
	      } // for (p < ::party.playerno())
	      row += 1;
	    } // name
	    { // random name
	      Gtk::Table* table2 = Gtk::manage(new Gtk::Table(1, ::party.playerno(),
							      true));
	      table->attach(*table2, 1, 2, row, row + 1,
			    Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
	      table2->set_col_spacings(1 em / 2);
	      for (unsigned p = 0; p < ::party.playerno(); ++p) {
		table2->attach(*(this->player(p).random_name_button),
			       p, p + 1, 0, 1);
	      } // for (p < ::party.playerno())
	      row += 1;
	    } // random name
	  } // player names

	  { // type
	    Gtk::Alignment* alignment
	      = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_TOP,
					       0, 0));
	    table->attach(*alignment, 0, 1, row, row + 1,
			  Gtk::FILL | Gtk::EXPAND, Gtk::FILL);

	    Gtk::CheckButton* player_type_button
	      = Gtk::manage(new Gtk::CheckButton("player type"));
	    this->ui->translations->add(*player_type_button,
					::translation("player type"));
	    alignment->add(*player_type_button);
	    table->set_row_spacing(row - 1, 1 ex);
	    player_type_button->set_active();

	    Gtk::Table* table2
	      = Gtk::manage(new Gtk::Table(1, ::party.playerno(), true));
	    set_signal_toggle_view(table2, player_type_button);
	    table->attach(*table2, 1, 2, row, row + 1,
			  Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
	    table2->set_col_spacings(1 em / 2);
	    for (unsigned p = 0; p < ::party.playerno(); ++p) {
	      Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox(true, 1 ex / 2));
	      table2->attach(*vbox, p, p + 1, 0, 1);

	      for (vector<Gtk::RadioButton*>::iterator type
		   = this->player(p).type_button.begin();
		   type != this->player(p).type_button.end();
		   type++)
		vbox->add(**type);
	    } // for (p < ::party.playerno())

	    row += 1;
	  } // type

	  { // aiconfig
	    Gtk::CheckButton* aiconfig_button
	      = Gtk::manage(new Gtk::CheckButton("aiconfig"));
	    table->attach(*aiconfig_button, 0, 1, row, row + 1,
			  Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
	    table->set_row_spacing(row - 1, 1 ex);
	    row += 1;

	    { // general
	      Gtk::ToggleButton* general_button
		= this->add_group("general", table, row, group_depth,
				  aiconfig_button);

	      this->add_aiconfig(Aiconfig::FAIRPLAYHANDS,
				 table, row, general_button);
	      this->add_aiconfig(Aiconfig::FAIRPLAYTEAMS,
				 table, row, general_button);
	      this->add_aiconfig(Aiconfig::TRUSTING,
				 table, row, general_button);
	      this->add_aiconfig(Aiconfig::REMEMBERTRICKS,
				 table, row, general_button);
	      this->add_aiconfig(Aiconfig::FUTURE_LIMIT,
				 table, row, general_button);
	    } // general
	    { // ai type
	      Gtk::Alignment* alignment
		= Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_TOP,
						 0, 0));
	      alignment->set_border_width(1 ex / 2);
	      table->attach(*alignment, 0, 1, row, row + 1,
			    Gtk::FILL | Gtk::EXPAND, Gtk::FILL,
			    group_depth em);
	      set_signal_toggle_view(alignment, aiconfig_button);

	      Gtk::CheckButton* ai_type_button
		= Gtk::manage(new Gtk::CheckButton("ai type"));
	      this->ui->translation->add(*ai_type_button,
					 ::translation("ai type"));
	      alignment->add(*ai_type_button);

	      Gtk::Table* table2
		= Gtk::manage(new Gtk::Table(1, ::party.playerno(), true));
	      set_signal_toggle_view(table2, ai_type_button);
	      table->attach(*table2, 1, 2, row, row + 1,
			    Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
	      table2->set_col_spacings(1 em / 2);
	      for (unsigned p = 0; p < ::party.playerno(); ++p) {
		Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox(true, 1 ex / 2));
		table2->attach(*vbox, p, p + 1, 0, 1);

		for (vector<Gtk::RadioButton*>::iterator type
		     = this->player(p).aiconfig().aitype.begin() + 1;
		     // not 'no aitype'
		     type != this->player(p).aiconfig().aitype.end();
		     type++)
		  vbox->add(**type);
	      } // for (p < ::party.playerno())

	      row += 1;
	    } // ai type
	    { // announcement
	      Gtk::ToggleButton* announcement_button
		= this->add_group("announcement", table, row,
				  group_depth, aiconfig_button);

	      this->add_aiconfig(Aiconfig::ANNOUNCELIMIT,
				 table, row, announcement_button);
	      this->add_aiconfig(Aiconfig::ANNOUNCELIMITDEC,
				 table, row, announcement_button);
	      this->add_aiconfig(Aiconfig::ANNOUNCECONFIG,
				 table, row, announcement_button);
	      this->add_aiconfig(Aiconfig::ANNOUNCELIMITREPLY,
				 table, row, announcement_button);
	      this->add_aiconfig(Aiconfig::ANNOUNCECONFIGREPLY,
				 table, row, announcement_button);
	    } // announcement
	    { // trick decision
	      Gtk::ToggleButton* trick_decision_button
		= this->add_group("trick decision", table, row,
				  group_depth, aiconfig_button);

	      this->add_aiconfig(Aiconfig::FEHLCREATIONONFIRSTCARD,
				 table, row, trick_decision_button);
	      this->add_aiconfig(Aiconfig::LASTHEURISTICTRICK,
				 table, row, trick_decision_button);
	      this->add_aiconfig(Aiconfig::LASTFEHLCREATION,
				 table, row, trick_decision_button);
	      this->add_aiconfig(Aiconfig::LIMITQUEEN,
				 table, row, trick_decision_button);
	      this->add_aiconfig(Aiconfig::LIMITDOLLE,
				 table, row, trick_decision_button);
	    } // trick decision
	    { // game decision
	      Gtk::ToggleButton* game_decision_button
		= this->add_group("game decision", table, row,
				  group_depth, aiconfig_button);

	      this->add_aiconfig(Aiconfig::TAKEPOVERTY,
				 table, row, game_decision_button);
	      this->add_aiconfig(Aiconfig::SINGLESOLO,
				 table, row, game_decision_button);
	      this->add_aiconfig(Aiconfig::DOUBLESOLO,
				 table, row, game_decision_button);
	      this->add_aiconfig(Aiconfig::TRIPLESOLO,
				 table, row, game_decision_button);
	      this->add_aiconfig(Aiconfig::COLORSOLO,
				 table, row, game_decision_button);
	      this->add_aiconfig(Aiconfig::MEATLESS,
				 table, row, game_decision_button);
	    } // game decision
	    { // heuristics
	      Gtk::ToggleButton* heuristics_button
		= this->add_group("heuristics", table, row, group_depth,
				  aiconfig_button);

	      group_depth += 1;
	      { // normal
		Gtk::ToggleButton* normal_button
		  = this->add_group("normal", table, row, group_depth,
				    heuristics_button);

		this->add_aiconfig(Aiconfig::CHOOSE_ACE,
				   table, row, normal_button);
		this->add_aiconfig(Aiconfig::CHOOSE_FOR_COLOR_TRICK,
				   table, row, normal_button);
		this->add_aiconfig(Aiconfig::CHOOSE_PFUND,
				   table, row, normal_button);
		this->add_aiconfig(Aiconfig::JAB_FOR_ACE,
				   table, row, normal_button);
		this->add_aiconfig(Aiconfig::CREATE_FEHL,
				   table, row, normal_button);
		this->add_aiconfig(Aiconfig::BEST_WINNING,
				   table, row, normal_button);
		this->add_aiconfig(Aiconfig::LOW_HIGH,
				   table, row, normal_button);
		this->add_aiconfig(Aiconfig::PLAY_FOR_TEAM,
				   table, row, normal_button);
		this->add_aiconfig(Aiconfig::JAB_FOX,
				   table, row, normal_button);
		this->add_aiconfig(Aiconfig::DRAW_TRUMP,
				   table, row, normal_button);
	      } // normal
	      { // color solo
		Gtk::ToggleButton* color_solo_button
		  = this->add_group("color solo", table, row, group_depth,
				    heuristics_button);

		this->add_aiconfig(Aiconfig::COLOR_CHOOSE_ACE,
				   table, row, color_solo_button);
		this->add_aiconfig(Aiconfig::COLOR_BEST_WINNING,
				   table, row, color_solo_button);
		this->add_aiconfig(Aiconfig::COLOR_JAB_FOR_ACE,
				   table, row, color_solo_button);
		this->add_aiconfig(Aiconfig::COLOR_LOW_HIGH,
				   table, row, color_solo_button);
	      } // color solo
	      { // picture solo
		Gtk::ToggleButton* picture_solo_button
		  = this->add_group("picture solo", table, row, group_depth,
				    heuristics_button);

		this->add_aiconfig(Aiconfig::PICTURE_SECONDBESTTRUMP,
				   table, row, picture_solo_button);
	      } // picture solo
	      { // meatless
		Gtk::ToggleButton* meatless_solo_button
		  = this->add_group("meatless solo", table, row, group_depth,
				    heuristics_button);

		this->add_aiconfig(Aiconfig::MEATLESS_PLAYHIGHESTCOLOR,
				   table, row, meatless_solo_button);
	      } // meatless
	    } // heuristics
	    { // loading/saving
	      Gtk::Table* table2
		= Gtk::manage(new Gtk::Table(1, ::party.playerno(), true));
	      set_signal_toggle_view(table2, aiconfig_button);
	      table->attach(*table2, 1, 2, row, row + 1,
			    Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
	      table2->set_col_spacings(3 em / 2);
	      table2->set_border_width(1 em / 2);
	      for (unsigned p = 0; p < ::party.playerno(); ++p) {
		Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox(true, 5));
		table2->attach(*vbox, p, p + 1, 0, 1);

		vbox->add(*(this->player(p).aiconfig().hardcoded_button));
		vbox->add(*(this->player(p).aiconfig().adopt_button));
		vbox->add(*(this->player(p).aiconfig().load_button));
		vbox->add(*(this->player(p).aiconfig().allot_button));
		vbox->add(*(this->player(p).aiconfig().save_button));
	      } // for (p < ::party.playerno())

	      row += 1;
	    } // loading/saving

	    group_depth -= 1;
	  } // aiconfig

	  { // actions
	    Gtk::Table* table2 = Gtk::manage(new Gtk::Table(1, ::party.playerno(),
							    true));

	    table->attach(*table2, 1, 2, row, row + 1,
			  Gtk::FILL | Gtk::EXPAND, Gtk::FILL | Gtk::EXPAND);
	    table->set_row_spacing(row - 1, 3 ex);
	    table2->set_col_spacings(1 em / 2);
	    for (unsigned p = 0; p < ::party.playerno(); ++p) {
	      Gtk::Alignment* alignment
		= Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER,
						 Gtk::ALIGN_BOTTOM,
						 1, 0));
	      table2->attach(*alignment, p, p + 1, 0, 1);

	      Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox(true, 1 ex / 2));
	      alignment->add(*vbox);

	      vbox->pack_end(*(this->player(p).reset_button));
	    } // for (p < ::party.playerno())
	    row += 1;
	  } // actions
	} // players configuration
#endif
      } // layout


      this->show_all_children();

      this->update();

      return ;
    } // void Players::init()

  /**
   ** -> result
   **
   ** @param     playerno   the player number
   **
   ** @return    the player configuration of player 'playerno'
   **
   ** @author    Diether Knof
   **
   ** @version   0.5.4
   **/
  Players::Player&
    Players::player(unsigned const playerno)
    {
      DEBUG_ASSERTION((this->player_.size() > playerno),
		      "Players::player(playerno):\n"
		      "  playerno too great: "
		      << playerno << " >= " << this->player_.size());

      return *(this->player_[playerno]);
    } // Players::Player& Players::player(unsigned const playerno)

  /**
   ** creates a backup
   **
   ** @param     -
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.5.4
   **/
  void
    Players::create_backup()
    {
      for (vector<Player*>::iterator player = this->player_.begin();
	   player != this->player_.end();
	   player++)
	(*player)->create_backup();

      return ;
    } // void Players::create_backup()

  /**
   ** resets the settings
   **
   ** @param     -
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.5.4
   **
   ** @todo	set the elements
   **/
  void
    Players::reset()
    {
      if (!this->is_visible())
	return ;

      for (vector<Player*>::iterator player = this->player_.begin();
	   player != this->player_.end();
	   player++)
	(*player)->reset();
      this->update();

      return ;
    } // void Players::reset()

  /**
   ** update the sensitivity of all widgets
   **
   ** @param     -
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.5.4
   **/
  void
    Players::sensitivity_update()
    {
      if (!this->is_visible())
	return ;

      for (vector<Player*>::iterator player = this->player_.begin();
	   player != this->player_.end();
	   player++)
	(*player)->sensitivity_update();

      return ;
    } // void Players::sensitivity_update()

  /**
   ** update all widgets
   **
   ** @param     -
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.5.4
   **/
  void
    Players::update()
    {
      if (!this->is_visible())
	return ;

      for (vector<Player*>::iterator player = this->player_.begin();
	   player != this->player_.end();
	   player++)
	(*player)->update();

      return ;
    } // void Players::update()

  /**
   ** the players 'player_a' and 'player_b' have been switched
   **
   ** @param     player_a   first player
   ** @param     player_b   second player
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.7.2
   **/
  void
    Players::players_switched(::Player const& player_a,
			      ::Player const& player_b)
    {
      if (!this->is_visible())
	return ;

#ifdef POSTPHONED
      std::swap(this->player(player_a.no()), this->player(player_a.no()));
      // Problem: cannot swap notebook pages
#else
      this->player(player_a.no()).update();
      this->player(player_b.no()).update();
#endif

      return ;
    } // void Players::players_switched(Player player_a, Player player_b)

  /**
   ** update 'player'
   **
   ** @param     player   player to update
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.5.4
   **/
  void
    Players::player_update(::Player const& player)
    {
      if (!this->is_visible())
	return ;

      this->player(player.no()).update();

      return ;
    } // void Players::player_update(::Player const& player)

  /**
   ** update the name of 'player'
   **
   ** @param     player   player with the new name
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.5.4
   **/
  void
    Players::name_update(::Player const& player)
    {
      if (!this->is_visible())
	return ;

      this->player(player.no()).name_update();

      return ;
    } // void Players::name_update(::Player player)

  /**
   ** update the voice of 'player'
   **
   ** @param     player   player with the new voice
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.7.5
   **/
  void
    Players::voice_update(::Player const& player)
    {
      if (!this->is_visible())
	return ;

      this->player(player.no()).voice_update();

      return ;
    } // void Players::voice_update(::Player player)

  /**
   ** attaches a group named 'name'
   ** the row is added at 'table';
   ** the row is 'current_row', which is incremented;
   **
   ** @param     name   		the name of the group (untranslated)
   ** @param     table   		the table to add the row
   ** @param     current_row   	the row number to insert the row
   ** @param     depth   		the depth of the group
   ** @param     dependent_button   the button which hides/shows the row
   **
   ** @return    the button for the group
   **
   ** @author    Diether Knof
   **
   ** @version   0.6.6
   **/
  Gtk::ToggleButton*
    Players::add_group(string const& name,
		       Gtk::Table* table, int& current_row,
		       int const depth,
		       Gtk::ToggleButton* dependent_button)
    {
      Gtk::Alignment* alignment
	= Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_TOP,
					 0, 0));
      alignment->set_border_width(1 ex / 2);

      table->attach(*alignment, 0, 1, current_row, current_row + 1,
		    Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK,
		    depth em);

      Gtk::CheckButton* group_button
	= Gtk::manage(new Gtk::CheckButton(name));
      this->ui->translations->add(*group_button, ::translation(name));

      alignment->add(*group_button);

      if (dependent_button)
	set_signal_toggle_view(alignment, dependent_button);


      current_row += 1;

      return group_button;
    } // Gtk::ToggleButton* Players::add_group(Aiconfig::TypeBool const type, Gtk::Table* table, int& row, int const depth, Gtk::ToggleButton* dependent_button)

  /**
   ** attaches the aiconfig 'type'
   ** the row for 'type' is added at 'table';
   ** the row is 'current_row', which is incremented;
   ** the showing is dependent on the state of 'dependent_button'
   **
   ** @param     type   		the aiconfig type to add a row for
   ** @param     table   		the table to add the row
   ** @param     current_row   	the row number to insert the row
   ** @param     dependent_button   the button which hides/shows the row
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.6.6
   **/
  void
    Players::add_aiconfig(int const type,
			  Gtk::Table* table, int& current_row,
			  Gtk::ToggleButton* dependent_button)
    {
      Gtk::Alignment* alignment
	= Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_RIGHT, Gtk::ALIGN_CENTER,
					 0, 0));

      table->attach(*alignment, 0, 1, current_row, current_row + 1,
		    Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);

      Gtk::Label* label = NULL;
      if ((type >= Aiconfig::BOOL_FIRST)
	  && (type <= Aiconfig::BOOL_LAST)) {
	label = Gtk::manage(new Gtk::Label(::name(static_cast<Aiconfig::TypeBool>(type))));
	this->ui->translations->add(*label, ::translation(static_cast<Aiconfig::TypeBool>(type)));
      } else if ((type >= Aiconfig::UNSIGNED_FIRST)
		 && (type <= Aiconfig::UNSIGNED_LAST)) {
	label = Gtk::manage(new Gtk::Label(::name(static_cast<Aiconfig::TypeUnsigned>(type))));
	this->ui->translations->add(*label, ::translation(static_cast<Aiconfig::TypeUnsigned>(type)));
      } else {
	DEBUG_ASSERTION(false,
			"Players::add_aiconfig():\n"
			"  type '" << type << "' not known as Aiconfig::Type*");
      } // if (type \in Aiconfig::Type*)

      alignment->add(*label);

      Gtk::Table* table2 = Gtk::manage(new Gtk::Table(1, ::party.playerno(),
						      true));
      table->attach(*table2, 1, 2, current_row, current_row + 1,
		    Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK);
      table2->set_col_spacings(1 em / 2);
      table2->set_row_spacing(0, 1 ex / 2);

      for (unsigned p = 0; p < ::party.playerno(); ++p) {
	Gtk::Alignment* alignment
	  = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER,
					   1, 0));
	table2->attach(*alignment, p, p + 1, 0, 1,
		       Gtk::FILL | Gtk::EXPAND, Gtk::SHRINK,
		       0, 1 ex / 4);

	Gtk::Widget* widget = NULL;
	if ((type >= Aiconfig::BOOL_FIRST)
	    && (type <= Aiconfig::BOOL_LAST)) {
	  widget = this->player(p).aiconfig().type_bool[type - Aiconfig::BOOL_FIRST];
	} else if ((type >= Aiconfig::UNSIGNED_FIRST)
		   && (type <= Aiconfig::UNSIGNED_LAST)) {
	  widget = this->player(p).aiconfig().type_unsigned[type - Aiconfig::UNSIGNED_FIRST];
	} else {
	  DEBUG_ASSERTION(false,
			  "Players::add_aiconfig():\n"
			  "  type '" << type << "' not known as Aiconfig::Type*");
	} // if (type \in Aiconfig::Type*)
	alignment->add(*widget);
      } // for (p < ::party.playerno())

      set_signal_toggle_view(alignment, dependent_button);
      set_signal_toggle_view(table2, dependent_button);

      current_row += 1;

      return ;
    } // void Players::add_aiconfig(int const type, Gtk::Table* table, int& current_row, Gtk::ToggleButton* dependent_widget)

  /**
   ** signal: the page of the notebook is changed
   **
   ** @param     page      current page
   ** @param     pageno    current page number
   ** @param     notebook   notebook to change the page to 'pageno'
   **
   ** @return    -
   **
   ** @author    Diether Knof
   **
   ** @version   0.6.6
   **/
  void
    Players::notebook_switch_page_signal(GtkNotebookPage* page,
					 guint pageno,
					 Gtk::Notebook* notebook)
    {
      // Note: the signal for this member is binded in 'Players'
      // because it is called by the other 'AiConfig's which have to be
      // created first.

      if (static_cast<unsigned>(notebook->get_current_page()) != pageno)
	notebook->set_current_page(pageno);

      return ;
    } // void Players::notebook_switch_page_signal(GtkNotebookPage* page, guint pageno, Gtk::Notebook* notebook)

  /**
   ** a key has been pressed
   ** C-o: output of the active player on 'stdout'
   **
   ** @param     key   the key
   **
   ** @return    whether the key was managed
   **
   ** @author    Diether Knof
   **
   ** @version   0.7.2
   **/
  bool
    Players::on_key_press_event(GdkEventKey* key)
    {
      bool managed = false;

      if ((key->state & ~GDK_SHIFT_MASK) == GDK_CONTROL_MASK) {
	switch (key->keyval) {
	case GDK_o: { // ouput of the player
	  if (this->players_notebook->get_current_page() == -1)
	    break;
	  bool const write_database_bak = ::Player::write_database;
	  ::Player::write_database = false;
	  cout << this->player(this->players_notebook->get_current_page()).player();
	  ::Player::write_database = write_database_bak;
	  managed = true;
	} break;
	case GDK_u: // update the entries
	  if (this->players_notebook->get_current_page() == -1)
	    break;
	  this->player(this->players_notebook->get_current_page()).update();
	  managed = true;
	  break;
	} // switch (key->keyval)
      } // if (key->state == GDK_CONTROL_MASK)

      return (managed
	      || this->StickyDialog::on_key_press_event(key)
	      || this->ui->key_press(key));
    } // bool Players::on_key_press_event(GdkEventKey* key)

} // namespace UI_GTKMM_NS

#endif // #ifdef USE_UI_GTKMM
