/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2004  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 "party_points.h"
#include "game_overview.h"

#include "ui.h"
#include "translations.h"
#include "main_window.h"
#include "cards.h"
#include "icons.h"
#include "bug_report.h"

#include "../../party/party.h"
#include "../../game/game_summary.h"
#include "../../misc/setting.h"

#include "../../utils/string.h"

#include "widgets/stock_button.h"
#include <gtkmm/stock.h>
#include <gtkmm/treeview.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/alignment.h>
#include <gtkmm/checkbutton.h>
#include <gtkmm/radiobutton.h>
#include <gtkmm/label.h>
#include <gtkmm/main.h>


//#define OFFSET_ROUND	0
//#define OFFSET_GAMENO	(OFFSET_ROUND + 1)
//#define OFFSET_PLAYER	(OFFSET_GAMENO + 1)
#define OFFSET_PLAYER	1
#ifdef POSTPHONED
#define OFFSET_GAMEPOINTS	(OFFSET_PLAYER + this->ui->party().playerno())
#else
#define OFFSET_GAMEPOINTS	(OFFSET_PLAYER + ::party.playerno())
#endif
#define OFFSET_GAMETYPE	(OFFSET_GAMEPOINTS + 1)
namespace UI_GTKMM_NS {

  /**
   **
   ** Constructor for the model
   **
   ** @param	playerno	number of players in the party
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.6.1
   **
   ** @todo	the row can be deselected
   ** @todo	extra window for the game summary
   **
   **/
  PartyPoints::PartyPointsModel::PartyPointsModel(unsigned const playerno) :
    Gtk::TreeModel::ColumnRecord(),
    empty(),
    round(),
    round_str(),
    round_color(),
    gameno(),
    gameno_str(),
    playerpoints(playerno),
    gamepoints(),
    gamepoints_str(),
    gametype()
  {
    this->add(this->empty);
    this->add(this->round);
    this->add(this->round_str);
    this->add(this->round_color);
    this->add(this->gameno);
    this->add(this->gameno_str);
    for (unsigned p = 0; p < playerno; p++)
      this->add(this->playerpoints[p]);
    this->add(this->gamepoints_str);
    this->add(this->gametype);

    return ;
  } // PartyPoints::PartyPointsModel::PartyPointsModel(unsigned const playerno)

  /**
   **
   ** Constructor
   **
   ** @param	-
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.6.1
   **
   **/
  PartyPoints::PartyPoints(Base* const parent) :
    Base(parent),
    StickyDialog("Party points", false, true),
    party_points_list(),
    party_points_treeview(NULL),
    round_rows(),
    game_rows(),
    party_points_sum_treeview(NULL),
    show_game_overview_button(NULL),
    add_up_points(NULL),
    collapse_rounds(NULL),
    expand_rounds(NULL),
    close_button(NULL),
    game_overview(NULL)
  {
    this->signal_realize().connect(SigC::slot(*this, &PartyPoints::init));
#ifndef OUTDATED
    // when 'GamePoints' doesn't need a pointer to Game any more
    this->realize();
#endif

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

  /**
   **
   ** Destruktor
   **
   ** @param	-
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  PartyPoints::~PartyPoints()
  {
    delete this->party_points_model;
    delete this->game_overview;

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

  /**
   **
   ** create all subelements
   **
   ** @param	-
   **
   ** @return	-
   ** 
   ** @author	Diether Knof
   **
   ** @version	0.6.1
   **
   **/
  void
    PartyPoints::init()
    {
      this->ui->translations->add(*this, ::translator("Party points"));

      this->set_icon(this->ui->main_window->icon);

      this->game_overview = new GameOverview(this);
      this->signal_show().connect(SigC::slot(*this,
					     &PartyPoints::show_game_overview_toggled));


      { // vbox
	Gtk::VBox* window_vbox = Gtk::manage(new Gtk::VBox(false));
	window_vbox->set_border_width(1 ex);
	window_vbox->set_spacing(1 ex);
	this->get_vbox()->add(*window_vbox);

	{ // the points table
	  this->party_points_model = new PartyPointsModel(::party.playerno());

	  // of the sum treeview only the header is neeeded
	  // (workaround since there exists no foot for a treeview)
	  this->party_points_sum_treeview
	    = Gtk::manage(new Gtk::TreeView(Gtk::TreeStore::create(*(this->party_points_model))));

	  this->party_points_list
	    = Gtk::TreeStore::create(*(this->party_points_model));
	  this->party_points_treeview
	    = Gtk::manage(new Gtk::TreeView(this->party_points_list));
	  this->party_points_treeview->get_selection()->set_mode(Gtk::SELECTION_SINGLE);

	  this->party_points_treeview->append_column("",
						     this->party_points_model->empty);
	  this->party_points_sum_treeview->append_column("",
							 this->party_points_model->empty);
	  this->party_points_sum_treeview->get_column(0)->set_alignment(Gtk::ALIGN_RIGHT);

	  for (unsigned p = 0; p < ::party.playerno(); p++) {
	    this->party_points_treeview->append_column(Glib::locale_to_utf8(::party.player(p).name()),
						       this->party_points_model->playerpoints[p]);
	    this->party_points_treeview->get_column_cell_renderer(p + OFFSET_PLAYER)->property_xalign() = 0.5;
	    this->party_points_treeview->get_column(p + OFFSET_PLAYER)->set_alignment(Gtk::ALIGN_CENTER);
	    this->party_points_sum_treeview->append_column("0",
							   this->party_points_model->playerpoints[p]);
	    this->party_points_sum_treeview->get_column(p + OFFSET_PLAYER)->set_resizable(false);
	    this->party_points_sum_treeview->get_column(p + OFFSET_PLAYER)->set_alignment(Gtk::ALIGN_CENTER);
	  } // for (p < ::party.playerno())
	  this->party_points_treeview->append_column("game points",
						     this->party_points_model->gamepoints_str);
	  this->party_points_treeview->get_column_cell_renderer(OFFSET_GAMEPOINTS)->property_xalign() = 0.5;
	  this->party_points_sum_treeview->append_column("",
							 this->party_points_model->gamepoints_str);
	  this->party_points_sum_treeview->get_column(OFFSET_GAMEPOINTS)->set_alignment(Gtk::ALIGN_CENTER);
	  this->party_points_treeview->append_column("game type",
						     this->party_points_model->gametype);
	  this->party_points_sum_treeview->append_column("",
							 this->party_points_model->gametype);

	  
#ifdef LINUX
	  this->party_points_treeview->set_rules_hint(true);
#endif
	  for (unsigned c = 0;
	       c < this->party_points_treeview->get_columns().size();
	       ++c) {
	    Gtk::TreeView::Column* column
	      = this->party_points_treeview->get_column(c);
	    column->property_width().signal_changed().connect(SigC::slot(*this, &PartyPoints::update_player_columns_size));
	    //column->property_width().signal_changed().connect(SigC::slot(*this, &PartyPoints::sum_columns_size_update));
#ifdef WINDOWS
	    column->add_attribute(*column->get_first_cell_renderer(),
				  "background", 3);
#endif
	    column->set_cell_data_func(*column->get_first_cell_renderer(),
				       SigC::bind<unsigned>(SigC::slot<void, Gtk::CellRenderer*, Gtk::TreeIter const&>(*this, &PartyPoints::set_cell_color), c));
	  }

	} // the points table

	{ // point list
	  Gtk::VBox* table_box = Gtk::manage(new Gtk::VBox(false));
	  Gtk::Alignment* alignment
	    = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER,
					     Gtk::ALIGN_CENTER,
					     0, 1));
	  window_vbox->pack_start(*alignment);
	  alignment->add(*table_box);
	  { // the scrolled window
	    Gtk::ScrolledWindow* scrolled_window
	      = Gtk::manage(new Gtk::ScrolledWindow);
	    scrolled_window->set_policy(Gtk::POLICY_NEVER,
					Gtk::POLICY_AUTOMATIC);
	    scrolled_window->add(*(this->party_points_treeview));
	    scrolled_window->set_hadjustment(this->party_points_treeview->property_hadjustment());
	    scrolled_window->set_vadjustment(this->party_points_treeview->property_vadjustment());

	    table_box->pack_start(*scrolled_window, true, true);
	  } // the scrolled window
	  table_box->pack_end(*this->party_points_sum_treeview, false, true);
	} // point list

	{ // the buttons
	  { // show game overview
	    Gtk::Alignment* alignment
	      = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER,
					       Gtk::ALIGN_CENTER,
					       0, 0));
	    this->show_game_overview_button
	      = Gtk::manage(new Gtk::CheckButton("show game overview"));
	    this->ui->translations->add(*(this->show_game_overview_button),
					::translator("show game overview"));
	    alignment->add(*(this->show_game_overview_button));
	    window_vbox->pack_end(*alignment, Gtk::PACK_SHRINK);
	    this->show_game_overview_button->signal_toggled().connect(SigC::slot(*this, &PartyPoints::show_game_overview_toggled));
	  } // show game overview
	  { // add up points
	    Gtk::Alignment* alignment
	      = Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER,
					       Gtk::ALIGN_CENTER,
					       0, 0));
	    this->add_up_points
	      = Gtk::manage(new Gtk::CheckButton("add up points"));
	    this->ui->translations->add(*this->add_up_points,
					::translator("add up points"));
	    alignment->add(*this->add_up_points);
	    window_vbox->pack_end(*alignment, Gtk::PACK_SHRINK);
	    this->add_up_points->signal_toggled().connect(SigC::slot(*this, &PartyPoints::recalc_party_points));
	  } // add up points

	  { // collapse rounds
	    Gtk::HBox* vbox
	      = Gtk::manage(new Gtk::HBox(true));
	    Gtk::RadioButton::Group group;
	    window_vbox->pack_end(*vbox, Gtk::PACK_SHRINK);
	    {
	      Gtk::Alignment* alignment
		= Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER,
						 Gtk::ALIGN_CENTER,
						 0, 0));
	      vbox->add(*alignment);
	      this->collapse_rounds
		= Gtk::manage(new Gtk::RadioButton(group, "collapse rounds"));
	      this->ui->translations->add(*this->collapse_rounds,
					  ::translator("collapse rounds"));
	      alignment->add(*this->collapse_rounds);
	      this->collapse_rounds->signal_toggled().connect(SigC::slot(*this, &PartyPoints::collaps_setting_changed));
	    }
	    {
	      Gtk::Alignment* alignment
		= Gtk::manage(new Gtk::Alignment(Gtk::ALIGN_CENTER,
						 Gtk::ALIGN_CENTER,
						 0, 0));
	      vbox->add(*alignment);
	      this->expand_rounds
		= Gtk::manage(new Gtk::RadioButton(group, "expand rounds"));
	      this->ui->translations->add(*this->expand_rounds,
					  ::translator("expand rounds"));
	      alignment->add(*this->expand_rounds);
	      this->expand_rounds->signal_toggled().connect(SigC::slot(*this, &PartyPoints::collaps_setting_changed));
	    }
	  } // collapse rounds
	} // the buttons
      } // vbox

      { // action area
	this->close_button
	  = Gtk::manage(new Gtk::StockButton(Gtk::Stock::CLOSE, "close"));
	this->ui->translations->add(*(this->close_button),
				    ::translator("close"));
	this->add_action_widget(*(this->close_button), Gtk::RESPONSE_CLOSE);

	this->close_button->grab_default();
	this->close_button->signal_clicked().connect(SigC::slot(*this, &Gtk::Widget::hide));
      } // action area

      this->show_all_children();

      this->signal_hide().connect(SigC::bind<bool>(SigC::slot(*this->show_game_overview_button,
							      &Gtk::ToggleButton::set_active),
						   false));

      this->party_points_treeview->get_selection()->signal_changed().connect(SigC::slot(*this, &PartyPoints::row_selection_changed));

      this->signal_show().connect(SigC::slot(*this,
					     &PartyPoints::update_player_columns_size));
      this->signal_show().connect(SigC::slot(*this, &PartyPoints::language_update));
      this->party_points_treeview->signal_row_collapsed().connect(SigC::slot(*this, &PartyPoints::row_collapsed_or_expanded));
      this->party_points_treeview->signal_row_expanded().connect(SigC::slot(*this, &PartyPoints::row_collapsed_or_expanded));

      this->update_player_columns_size();

      this->language_update();

      this->set_default_size(0, 3 * this->ui->cards->height());

      this->show_game_overview_button->set_sensitive(false);

      { // create all rows till the current game
	if (::game_status & GAMESTATUS::GAME) {
	  for (unsigned g = 0; g < this->ui->party().gameno(); ++g)
	    this->add_game(g);

	  if (::game_status == GAMESTATUS::GAME_FINISHED)
	    this->game_finished();
	} // if (::game_status & GAMESTATUS::GAME)

      } // create all rows till the current game

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

  /**
   **
   ** the party is opened
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::party_start_round(unsigned const round)
    {
      if (this->collapse_rounds->get_active())
	if (round > 0)
	  this->party_points_treeview->collapse_row(this->party_points_list->get_path(this->round_rows.back()));

      return ;
    } // void PartyPoints::party_start_round(unsigned const round)

  /**
   **
   ** the party is closed
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.6
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::party_close()
    {
      this->party_points_list->clear();
      this->round_rows.clear();
      this->game_rows.clear();

      return ;
    } // void PartyPoints::party_close()

  /**
   **
   ** the game has finished:
   ** add a new row with the current game
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::game_finished()
    {
      if (!this->is_realized())
	return ;

      this->add_game(::party.gameno());
      this->party_points_treeview->scroll_to_row(Gtk::TreePath(this->game_rows.back()));

      return ;
    } // void PartyPoints::game_finished()

  /**
   **
   ** the name of 'player' has changed
   **
   ** @param	player	the player with the changed name
   **
   ** @return	-
   **
   ** @version	0.6.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::name_changed(Player const& player)
    {
      if (!this->is_realized())
	return ;

      // ToDo: Here 'player.no()' means the number in the party, not in the game
      this->party_points_treeview->get_column(player.no() + OFFSET_PLAYER)->set_title(" " + Glib::locale_to_utf8(player.name()) + " ");
      this->game_overview->name_changed(player);

      this->party_points_treeview->columns_autosize();

      return ;
    } // void PartyPoints::name_changed(Player const& player)

  /**
   **
   ** recalculates the party points
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::recalc_party_points()
    {
      if (!this->is_realized())
	return ;

      if (::game_status < GAMESTATUS::PARTY_INIT)
	return ;

      for (unsigned g = 0; g < this->game_rows.size(); g++)
	this->set_points(g);

      return ;
    } // void PartyPoints::recalc_party_points()

  /**
   **
   ** add the row for the game 'gameno'
   **
   ** @param	gameno	number of the game to be added
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   ** @todo	color the row according to the gametype
   **
   **/
  void
    PartyPoints::add_game(unsigned const gameno)
    {
      if (!this->is_realized())
	return ;

      //Gtk::TreeModel::Row row = *(this->party_points_list->append());
      if (this->ui->party().starts_new_round(gameno)) {
	this->round_rows.push_back(*this->party_points_list->append());
	this->game_rows.push_back(this->round_rows.back());
      } else {
	this->game_rows.push_back(*(this->party_points_list->append(this->round_rows.back().children())));
	if (this->round_rows.back()[this->party_points_model->gameno] + 1
	    == gameno)
	  this->party_points_treeview->expand_row(this->party_points_list->get_path(this->round_rows.back()), true);
      }

      this->set_points(gameno);

      this->update_sum();

      return ;
    } // void PartyPoints::add_game(unsigned const gameno)

  /**
   **
   ** update the values of 'round'
   **
   ** @param	gameno	game number
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::set_points(unsigned const gameno)
    {
      Gtk::TreeModel::Row& row = this->game_rows[gameno];
      Party const& party = this->ui->party();

      ::GameSummary const& game_summary
	= party.game_summary(gameno);

      if (party.starts_new_round(gameno)
	  && row.children())
	if (!this->party_points_treeview->row_expanded(this->party_points_list->get_path(row))) {
	  this->update_round(row[this->party_points_model->round]);
	  return ;
	}

      row[this->party_points_model->round] = party.round_of_game(gameno);
      if (party.round_of_game(gameno) % 2 == 0)
	row[this->party_points_model->round_color]
	  = "#F8F8F8";
      else
	row[this->party_points_model->round_color]
	  = "#F0F0F0";
      row[this->party_points_model->gameno] = gameno;
      row[this->party_points_model->gameno_str]
	= DK::Utils::String::to_string(gameno);
      row[this->party_points_model->gamepoints_str]
	= DK::Utils::String::to_string(game_summary.points());
#ifdef POSTPHONED
      if (game_summary.game_type() == GAMETYPE::NORMAL)
	;
      //row[this->party_points_model->gametype] = NULL;
      else {
	Glib::RefPtr<Gdk::Pixbuf> pixbuf
	  = this->ui->icons->icon(g_ame_points.game_type());
	int const height = 20;
	row[this->party_points_model->gametype]
	  = pixbuf->scale_simple(pixbuf->get_width() * height / pixbuf->get_height(),
				 height, Gdk::INTERP_TILES);
      }
#else
      if (game_summary.game_type() == GAMETYPE::NORMAL)
	row[this->party_points_model->gametype] = "";
      else
	row[this->party_points_model->gametype]
	  = Glib::locale_to_utf8(::translator(game_summary.game_type()).translation());
#endif
      for (unsigned p = 0; p < party.playerno(); p++) {
	// ToDo: p is here the player in the party, not in the game
	if ((game_summary.team(p) == game_summary.winnerteam())
	    || (game_summary.winnerteam() == TEAM::NOTEAM)) {
	  if (this->add_up_points->get_active())
	    row[this->party_points_model->playerpoints[p]]
	      = DK::Utils::String::to_string(party.pointsum_till_game(gameno, p));
	  else
	    row[this->party_points_model->playerpoints[p]]
	      = DK::Utils::String::to_string(party.game_summary(gameno).points(p));
	} else {
	  row[this->party_points_model->playerpoints[p]]
	    = "";
	}
      } // for (p < party.playerno())

      return ;
    } // void PartyPoints::set_points(unsigned const gameno)

  /**
   **
   ** a row has been collapsed/expanded
   **
   ** @param	iterator
   ** @param	path
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::row_collapsed_or_expanded(Gtk::TreeModel::iterator const& iterator,
					   Gtk::TreeModel::Path const& path)
    {
      // ToDo: verify that it is a round-row
      Gtk::TreeModel::Row row
	= *this->party_points_treeview->get_model()->get_iter(path);

      if (this->party_points_treeview->row_expanded(path))
	this->set_points(row[this->party_points_model->gameno]);
      else
	this->update_round(row[this->party_points_model->round]);

      return ;
    } // void PartyPoints::row_collapsed_or_expanded(Gtk::TreeModel::iterator const& iterator, Gtk::TreeModel::Path const& path)

  /**
   **
   ** update the values of 'round'
   **
   ** @param	round	round which is to be updated
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::update_round(unsigned const round)
    {
      DEBUG_ASSERTION((this->round_rows.size() > round),
		      "PartyPoints::update_round(round):\n"
		      "  round too great: round = " << round
		      << " >= this->round_rows.size() = "
		      << this->round_rows.size());
      if (::game_status < GAMESTATUS::PARTY_PLAY)
	return ;

      Party const& party = this->ui->party();
      Gtk::TreeModel::Row& row = this->round_rows[round];

      row[this->party_points_model->gameno_str] = "";
      row[this->party_points_model->gametype] = "";
      if (this->add_up_points->get_active()) {
	for (unsigned p = 0; p < party.playerno(); ++p)
	  row[this->party_points_model->playerpoints[p]]
	    = DK::Utils::String::to_string(party.pointsum_till_round(round, p));
	row[this->party_points_model->gamepoints_str]
	  = DK::Utils::String::to_string(party.pointsum_till_round(round));
      } else {
	for (unsigned p = 0; p < party.playerno(); ++p)
	  row[this->party_points_model->playerpoints[p]]
	    = DK::Utils::String::to_string(party.pointsum_in_round(round, p));
	row[this->party_points_model->gamepoints_str]
	  = DK::Utils::String::to_string(party.pointsum_in_round(round));
      }


      return ;
    } // void PartyPoints::update_round(unsigned const round)

  /**
   **
   ** update the sum
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::update_sum()
    {
      Party const& party = this->ui->party();

      // udpate the sum
      for (unsigned p = 0; p < party.playerno(); ++p)
	this->party_points_sum_treeview->get_column(p + OFFSET_PLAYER)->set_title(DK::Utils::String::to_string(party.pointsum(p)));
      this->party_points_sum_treeview->get_column(OFFSET_GAMEPOINTS)->set_title(DK::Utils::String::to_string(party.pointsum()));
      this->party_points_sum_treeview->get_column(0)->set_title(" " + DK::Utils::String::to_string(party.gameno() + 1) + " ");

      this->sum_columns_size_update();

      return ;
    } // void PartyPoints::update_sum()

  /**
   **
   ** update the column size of the players
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   ** @todo	understand why this works
   **
   **/
  void
    PartyPoints::update_player_columns_size()
    {
      if (!this->is_visible())
	return ;

      Party const& party = this->ui->party();


      for (unsigned c = OFFSET_PLAYER;
	   c < OFFSET_PLAYER + party.playerno();
	   ++c) {
	this->party_points_treeview->get_column(c)->set_min_width(-1);
      } // for (c)
      {
	int width = -1;
	for (unsigned c = OFFSET_PLAYER;
	     c < OFFSET_PLAYER + party.playerno();
	     ++c) {
	  width = std::max(width, 
			   this->party_points_treeview->get_column(c)->get_width());
	} // for (c)
	for (unsigned c = OFFSET_PLAYER;
	     c < OFFSET_PLAYER + party.playerno();
	     ++c) {
	  if (this->party_points_treeview->get_column(c)->get_min_width()
	      < width)
	    this->party_points_treeview->get_column(c)->set_min_width(width);
	} // for (c)
      }

      this->sum_columns_size_update();

      return ;
    } // void PartyPoints::update_player_columns_size()

  /**
   **
   ** the row selection has changed:
   ** show the belonging game summary
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::row_selection_changed()
    {
      Glib::RefPtr<Gtk::TreeView::Selection> selection
	= this->party_points_treeview->get_selection();

      this->show_game_overview_button->set_sensitive(selection->get_selected_rows().size() == 1);

      if (selection->get_selected()) {
	Gtk::TreeRow row = *(selection->get_selected());

	unsigned const gameno = row[this->party_points_model->gameno];

	this->game_overview->set_gameno(gameno);
      } else {	// if !(selection->get_selected())
	this->game_overview->set_gameno(UINT_MAX);
	this->game_overview->hide();
      }	// if !(selection->get_selected())

      return ;
    } // void PartyPoints::row_selection_changed()

  /**
   **
   ** update the column size of the sum treeview
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::sum_columns_size_update()
    {
      if (!this->is_realized())
	return ;

      for (unsigned c = 0; c < this->party_points_treeview->get_columns().size(); ++c)
	if (this->party_points_treeview->get_column(c)->get_visible()) {
	  int const width
	    = this->party_points_treeview->get_column(c)->get_width();
#ifdef WORKAROUND
	  this->party_points_sum_treeview->get_column(c)->set_min_width(width);
	  this->party_points_sum_treeview->get_column(c)->set_max_width(width);
#else
	  // does not work
	  this->party_points_sum_treeview->get_column(c)->set_fixed_width(width);
	  this->party_points_sum_treeview->get_column(c)->set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED);
#endif
	}

      return ;
    } // void PartyPoints::sum_columns_size_update()

  /**
   **
   ** clear the selection
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.3
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::clear_selection()
    {
      if (!this->is_realized())
	return ;

      this->party_points_treeview->get_selection()->unselect_all();

      return ;
    } // void PartyPoints::clear_selection()

  /**
   **
   ** toggle the showing of the 'game overview' window
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.3
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::show_game_overview_toggled()
    {
      if (!this->is_realized())
	return ;


      if (this->game_overview) {
	if (this->show_game_overview_button->get_active()) {
	  this->row_selection_changed();
	  this->game_overview->present();
#ifdef WORKAROUND
	  // 'game_overview' cannot get the gameno in the initialisation
	  if (this->game_overview->gameno() == UINT_MAX)
	    this->row_selection_changed();
#endif
	} else {
	  this->game_overview->hide();
	}
      } // if (this->game_overview)

      return ;
    } // void PartyPoints::show_game_overview_toggled()

  /**
   **
   ** toggle the showing of the 'game overview' window
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.3
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::collaps_setting_changed()
    {
      if (this->collapse_rounds->get_active())
	this->party_points_treeview->collapse_all();
      else
	this->party_points_treeview->expand_all();

      return ;
    } // void PartyPoints::collaps_setting_changed()

  /**
   **
   ** updates all language elements
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::language_update()
    {
      if (!this->is_realized())
	return ;

      if (::game_status < GAMESTATUS::PARTY_INIT)
	return ;

#ifdef OUTDATED
      this->party_points_treeview->get_column(OFFSET_ROUND)->set_title(Glib::locale_to_utf8(::translator("round").translation()));
      this->party_points_treeview->get_column(OFFSET_GAMENO)->set_title(Glib::locale_to_utf8(::translator("game").translation()));
#endif
      this->party_points_treeview->get_column(OFFSET_GAMETYPE)->set_title(Glib::locale_to_utf8(::translator("gametype").translation()));
      this->party_points_treeview->get_column(OFFSET_GAMEPOINTS)->set_title(Glib::locale_to_utf8(::translator("game points").translation()));

      for (vector<Gtk::TreeModel::Row>::iterator row = this->game_rows.begin();
	   row != this->game_rows.end();
	   ++row) {
	::GameSummary const& game_summary
	  = this->ui->party().game_summary((*row)[this->party_points_model->gameno]);
	if (game_summary.game_type() == GAMETYPE::NORMAL)
	  (*row)[this->party_points_model->gametype] = "";
	else
	  (*row)[this->party_points_model->gametype]
	    = Glib::locale_to_utf8(::translator(game_summary.game_type()).translation());
      } // for (row \in this->game_rows)
      for (vector<Gtk::TreeModel::Row>::iterator row = this->round_rows.begin();
	   row != this->round_rows.end();
	   ++row) {
	this->update_round((*row)[this->party_points_model->round]);
      } // for (rw \in this->round_rows)

      return ;
    } // void PartyPoints::language_update()

  /**
   **
   ** changes the color of the cell at 'iterator'
   **
   ** @param	cell_renderer	cell renderer to change
   ** @param	iterator	row
   **
   ** @return	-
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    PartyPoints::set_cell_color(Gtk::CellRenderer* cell_renderer,
				Gtk::TreeModel::iterator const& iterator,
				unsigned const column)
    {
      Gtk::TreeModel::Row const row = *iterator;

      Gdk::Color color; 
      unsigned round;
      if (this->party_points_list->iter_depth(iterator) == 0)
	round = row[this->party_points_model->round];
      else {
	Gtk::TreePath path = this->party_points_list->get_path(iterator);
	path.up();
	round = (*this->party_points_treeview->get_model()->get_iter(path)
		)[this->party_points_model->round];
      }

      unsigned const gameno = row[this->party_points_model->gameno];
      Party const& party = this->ui->party();
      ::GameSummary const& game_summary = party.game_summary(gameno);

      if ( (game_summary.soloplayer_no() != UINT_MAX)
	   && ( (column == OFFSET_GAMETYPE)
		|| (column == OFFSET_PLAYER + game_summary.soloplayer_no()) )
	 )
	color = this->color(game_summary);

      dynamic_cast<Gtk::CellRendererText*>(cell_renderer)->property_foreground_gdk()
	= color;

#ifdef POSTPHONED
      // the whole column is made italic

      // make the name of the startplayer bold
      if ((column >= OFFSET_PLAYER)
	  && (column < OFFSET_PLAYER + party.playerno())
	  && (game_summary.soloplayer_no() != UINT_MAX)
	  && (column - OFFSET_PLAYER == game_summary.soloplayer_no())) {
	dynamic_cast<Gtk::CellRendererText*>(cell_renderer)->property_style()
	  = Pango::STYLE_ITALIC;
      } // if (column of the soloplayer)
#endif

      return ;
    } // void PartyPoints::set_cell_color(Gtk::CellRenderer* cell_renderer, Gtk::TreeModel::iterator const& iterator, unsigned const column)

  /**
   **
   ** -> result
   **
   ** @param	gameno	number of the game
   **
   ** @return	(background) color for the game 'gameno'
   **
   ** @version	0.6.7
   **
   ** @author	Diether Knof
   **
   **/
  Gdk::Color
    PartyPoints::color(::GameSummary const& game_summary)
    {
      Gdk::Color color;

      switch (game_summary.game_type()) {
      case GAMETYPE::NORMAL:
	color.set("black");
	break;
      case GAMETYPE::FIVE_NINES:
      case GAMETYPE::SIX_KINGS:
      case GAMETYPE::FOX_HIGHEST_TRUMP:
	color.set("#ffff00");
	break;
      case GAMETYPE::POVERTY:
	color.set("#B8860B");
	break;
      case GAMETYPE::GENSCHER:
	color.set("#7FFFD4");
	break;
      case GAMETYPE::MARRIAGE:
	color.set("#1E90FF");
	break;
      case GAMETYPE::MARRIAGE_SOLO:
      case GAMETYPE::MARRIAGE_SILENT:
	color.set("#00BFFF");
	break;
      case GAMETYPE::SOLO_JACK:
      case GAMETYPE::SOLO_QUEEN:
      case GAMETYPE::SOLO_KING:
      case GAMETYPE::SOLO_QUEEN_JACK:
      case GAMETYPE::SOLO_KING_JACK:
      case GAMETYPE::SOLO_KING_QUEEN:
      case GAMETYPE::SOLO_KOEHLER:
      case GAMETYPE::SOLO_CLUB:
      case GAMETYPE::SOLO_SPADE:
      case GAMETYPE::SOLO_HEART:
      case GAMETYPE::SOLO_DIAMOND:
      case GAMETYPE::SOLO_MEATLESS:
	if (game_summary.is_duty_solo())
	  color.set("#FF0000");
	else
	  color.set("#ffaf00");

	break;
      } // switch (game_type)

      return color;
    } // Gdk::Color PartyPoints::color(::GameSummary const& game_summary)

} // namespace UI_GTKMM_NS

#endif // #ifdef USE_UI_GTKMM
