/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2005  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 "table.h"
#include "poverty.h"
#include "party_points.h"
#include "party_finished.h"
#include "last_trick.h"
#include "trick.h"
#include "hand.h"
#include "trickpile.h"
#include "icongroup.h"
#include "name.h"
#include "reservation.h"

#include "ui.h"
#include "cards.h"
#include "utils.h"
#include "gameinfo_dialog.h"
#include "full_trick.h"
#include "game_finished.h"

#include "../../party/party.h"
#include "../../game/game.h"
#include "../../player/player.h"
#include "../../card/trick.h"
#include "../../misc/setting.h"
#include "../../utils/string.h"

#include <gtkmm/drawingarea.h>

namespace UI_GTKMM_NS {
  /**
   **
   ** clear the full pixmap
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::clear_all()
    {
      this->clear(Gdk::Rectangle(0, 0, this->width(), this->height()));

      return ;
    } // void Table::clear_all()

  /**
   **
   ** clears the rectangle
   **
   ** @param	area	area to clear
   **
   ** @return	-
   **
   ** @version	0.6.2
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::clear(Gdk::Rectangle const& area)
    {
      this->pixmap->draw_rectangle(this->gc, true,
				   area.get_x(), area.get_y(),
				   area.get_width(), area.get_height());

      return ;
    } // void Table::clear(Gdk::Rectangle const& area)

  /**
   **
   ** draw all
   **
   ** @param	update	whether to update the area
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::draw_all(bool const update)
    {
      this->clear_all();

      if (::game_status & GAMESTATUS::GAME) {
	for (vector<Hand*>::iterator h = this->hand_.begin();
	     h != this->hand_.end();
	     h++)
	  (*h)->draw(false);
	for (vector<TrickPile*>::iterator t = this->trickpile_.begin();
	     t != this->trickpile_.end();
	     t++)
	  (*t)->draw(false);
	for (vector<Icongroup*>::iterator t = this->icongroup_.begin();
	     t != this->icongroup_.end();
	     t++)
	  (*t)->draw(false);
	for (vector<Name*>::iterator n = this->name_.begin();
	     n != this->name_.end();
	     n++)
	  (*n)->draw(false);
	if (this->trick_)
	  this->trick_->draw(false);

	if ( (this->ui->game().type() == GAMETYPE::POVERTY)
	     && (((::game_status == GAMESTATUS::GAME_POVERTY_SHIFT)
		  || (::game_status == GAMESTATUS::GAME_REDISTRIBUTE) ) )
	     && this->poverty_)
	  this->poverty().draw(false);

      } else if (::game_status == GAMESTATUS::PARTY_FINISHED) {
#ifdef OUTDATED
	this->draw_party_summary();
#endif
      } else { // if (::game_status)
	this->draw_logo();
      } // if (::game_status)

      if (update)
	this->update_full_area();

      this->mouse_cursor_update();

      return ;
    } // void Table::draw_all(bool const update = true)

  /**
   **
   ** draw the logo
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.8
   **
   ** @author	Diether Knof
   **
   ** @bug	clip mask does not work (GTK-bug?)
   **
   **/
  void
    Table::draw_logo()
    {
      this->pixmap->draw_pixbuf(this->gc,
				this->ui->logo,
				0, 0,
				(this->width()
				 - this->ui->logo->get_width()) / 2,
				(this->height()
				 - this->ui->logo->get_height()) / 2,
				-1, -1,
				Gdk::RGB_DITHER_NONE,
				0, 0);
      { // draw a border with the cards
	vector<Card> border_card; // the border cards
	unsigned const card_width = this->ui->cards->width();
	unsigned const card_height = this->ui->cards->height();

	// the bitmap of the border cards on the top
	vector<Glib::RefPtr<Gdk::Bitmap> > border_card_top_bitmap;

	// the border of the top card row
	unsigned border_top_x;

	{ // top
	  border_card.clear();
	  border_card.push_back(Card(Card::HEART,	Card::TEN));
	  border_card.push_back(Card(Card::CLUB,	Card::QUEEN));
	  border_card.push_back(Card(Card::SPADE,	Card::QUEEN));
	  border_card.push_back(Card(Card::HEART,	Card::QUEEN));
	  border_card.push_back(Card(Card::DIAMOND,	Card::QUEEN));
	  border_card.push_back(Card(Card::CLUB,	Card::JACK));
	  border_card.push_back(Card(Card::SPADE,	Card::JACK));

	  border_top_x
	    = (card_width
	       + ( (border_card.size() * card_height
		    + card_width >= this->width())
		   ? 0
		   : ((this->width() - (border_card.size() * card_height
					+ card_width)
		      ) / border_card.size())
		 )
	      );

	  for (unsigned n = 0; n < border_card.size(); n++) {
	    this->pixmap->draw_pixbuf(this->gc,
				      this->ui->cards->card(border_card[n],
							    LEFT),
				      0, 0,
				      border_top_x +
				      n * (this->width() - 2 * border_top_x
					   - card_height)
				      / (border_card.size() - 1),
				      card_width / 2,
				      -1, -1,
				      Gdk::RGB_DITHER_NONE,
				      0, 0);
	    // set the bitmap
	    border_card_top_bitmap.push_back(bitmap(this->ui->cards->card(border_card[n], LEFT)));
	  } // for (n < border_card.size())
	} // top
	{ // right
	  border_card.clear();
	  border_card.push_back(Card(Card::HEART,	Card::JACK));
	  border_card.push_back(Card(Card::DIAMOND,	Card::JACK));
	  border_card.push_back(Card(Card::DIAMOND,	Card::ACE));
	  border_card.push_back(Card(Card::DIAMOND,	Card::TEN));
	  border_card.push_back(Card(Card::DIAMOND,	Card::KING));

	  unsigned const border_y
	    = (card_width
	       + ( (border_card.size() * card_height
		    + card_width >= this->height())
		   ? 0
		   : ((this->height() - (border_card.size() * card_height
					 + card_width)
		      ) / border_card.size())
		 )
	      );

	  for (unsigned n = 0; n < border_card.size(); n++)
	    this->pixmap->draw_pixbuf(this->gc,
				      this->ui->cards->card(border_card[n],
							    UP),
				      0, 0,
				      this->width() - 3 * card_width / 2,
				      border_y +
				      n * (this->height() - 2 * border_y
					   - card_height)
				      / (border_card.size() - 1),
				      -1, -1,
				      Gdk::RGB_DITHER_NONE,
				      0, 0);
	} // right
	{ // bottom
	  border_card.clear();
	  border_card.push_back(Card(Card::DIAMOND,	Card::NINE));
	  border_card.push_back(Card(Card::CLUB,	Card::ACE));
	  border_card.push_back(Card(Card::CLUB,	Card::TEN));
	  border_card.push_back(Card(Card::CLUB,	Card::KING));
	  border_card.push_back(Card(Card::CLUB,	Card::NINE));
	  border_card.push_back(Card(Card::HEART,	Card::ACE));
	  border_card.push_back(Card(Card::HEART,	Card::KING));

	  unsigned const border_x
	    = (card_width
	       + ( (border_card.size() * card_height
		    + card_width >= this->width())
		   ? 0
		   : ((this->width() - (border_card.size() * card_height
					+ card_width)
		      ) / border_card.size())
		 )
	      );

	  for (unsigned n = 0; n < border_card.size(); n++)
	    this->pixmap->draw_pixbuf(this->gc,
				      this->ui->cards->card(border_card[n],
							    RIGHT),
				      0, 0,
				      border_x +
				      (border_card.size() - 1 - n)
				      * (this->width() - 2 * border_x
					 - card_height)
				      / (border_card.size() - 1),
				      this->height()
				      - 3 * card_width / 2,
				      -1, -1,
				      Gdk::RGB_DITHER_NONE,
				      0, 0);
	} // bottom
	{ // left
	  border_card.clear();
	  border_card.push_back(Card(Card::HEART,	Card::NINE));
	  border_card.push_back(Card(Card::SPADE,	Card::ACE));
	  border_card.push_back(Card(Card::SPADE,	Card::TEN));
	  border_card.push_back(Card(Card::SPADE,	Card::KING));
	  border_card.push_back(Card(Card::SPADE,	Card::NINE));


	  unsigned const border_y
	    = (card_width
	       + ( (border_card.size() * card_height
		    + card_width >= this->height())
		   ? 0
		   : ((this->height() - (border_card.size() * card_height
					 + card_width)
		      ) / border_card.size())
		 )
	      );

	  for (unsigned n = 0; n < border_card.size(); n++) {
	    int const pos_x = card_width / 2;
	    int const pos_y = (border_y +
			       (border_card.size() - 1 - n)
			       * (this->height() - 2 * border_y
				  - card_height)
			       / (border_card.size() - 1));

	    // for the overlapping of the left with the upper row
	    // use a separate gc
	    Glib::RefPtr<Gdk::Bitmap> bitmap_overlapping
	      = bitmap(this->ui->cards->card(border_card[n],
					     DOWN));
	    // for adding the bitmaps
	    Glib::RefPtr<Gdk::GC> gc_add
	      = (!bitmap_overlapping
		 ? Gdk::GC::create(this->pixmap)
		 : Gdk::GC::create(bitmap_overlapping));
	    gc_add->set_function(Gdk::AND_INVERT);
	    if (bitmap_overlapping)
	      for (unsigned i = 0; i < border_card_top_bitmap.size(); i++) {
		bitmap_overlapping->draw_drawable(gc_add,
						  border_card_top_bitmap[i],
						  0, 0,
						  border_top_x +
						  i * (this->width() - 2 * border_top_x
						       - card_height)
						  / (border_card.size() - 1)
						  - pos_x,
						  card_width / 2
						  - pos_y,
						  card_height, card_width);
	      } // for (i < border_card_top_bitmap.size())

	    Glib::RefPtr<Gdk::GC> gc_overlapping
	      = Gdk::GC::create(this->pixmap);
	    if (bitmap_overlapping)
	      gc_overlapping->set_clip_mask(bitmap_overlapping);
	    gc_overlapping->set_clip_origin(pos_x, pos_y);

#if 0
	    this->pixmap->draw_rectangle(gc_overlapping,
					 true,
					 pos_x, pos_y,
					 card_width, card_height);
#else
	    this->pixmap->draw_pixbuf(gc_overlapping,
				      this->ui->cards->card(border_card[n],
							    DOWN),
				      0, 0,
				      pos_x, pos_y,
				      -1, -1,
				      Gdk::RGB_DITHER_NONE,
				      0, 0);
#endif
	  }
	} // left
      } // draw a border with the cards

      return ;
    } // void Table::draw_logo()

  /**
   **
   ** draw the party summary
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.8
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::draw_party_summary()
    {
      /*
       *                           _________
       *   _________               |       |
       *   |       |________       |       |
       *   |       |       |       |       |
       *   |  75   |  50   |  -50  |  100  |
       *    Diether  Erwin | Theo  |  Rudi |
       *                   |_______|
       */

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

      int min_points = 0; // minimal points made by the players
      int max_points = 0; // maximal points made by the players

      for (vector<Player*>::const_iterator p = party.players().begin();
	   p != party.players().end();
	   ++p) {
	int const points = party.pointsum(**p);
	if (points < min_points)
	  min_points = points;
	if (points > max_points)
	  max_points = points;
      } // for (p \in party.players())
      if (max_points == min_points)
	max_points += 1;

      Gdk::Color color_pillar("brown");
      Gdk::Color color_name(::setting(Setting::NAME_FONT_COLOR));
      Gdk::Color color_winner_pillar("gold");
      Gdk::Color color_winner_name(::setting(Setting::NAME_ACTIVE_FONT_COLOR));

      int const border_x = this->width() / 10;
      int const border_y = this->height() / 10;
      int const total_width = this->width() - 2 * border_x;
      int const total_height = this->height() - 2 * border_y;

      int const baseline = (border_y
			    + (max_points
			    * total_height
			    / (max_points - min_points)) );
      int const width = total_width / party.playerno();
      for (unsigned p = 0; p < party.playerno(); ++p) {
	Player const& player = party.player(p);
	bool winner = (party.rang(player) == 0);
	int const points = party.pointsum(player);

	int const pos_x = border_x + p * width;
	int height = (points
		      * total_height
		      / (max_points - min_points));

	{ // text
	  Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(this->pixmap);
	  gc->set_rgb_fg_color( winner
				? color_winner_name
				: color_name);

	  { // draw name
	    this->name_layout->set_text(Glib::locale_to_utf8(player.name()));
	    int layout_width, layout_height;
	    this->name_layout->get_pixel_size(layout_width,
					      layout_height);
	    // bug: there seems to be a problem with italic characters
	    layout_width += 2 * layout_height / 5;
	    this->pixmap->draw_layout(gc,
				      pos_x + (width - layout_width) / 2,
				      ( (height >= 0)
					? (baseline - layout_height * 6 / 5)
					: baseline ),
				      this->name_layout);
	  } // draw name
	  { // draw points
	    this->trickpile_points_layout->set_text(Glib::locale_to_utf8(DK::Utils::String::to_string(points)));
	    int layout_width, layout_height;
	    this->trickpile_points_layout->get_pixel_size(layout_width,
							  layout_height);
	    // bug: there seems to be a problem with italic characters
	    layout_width += 2 * layout_height / 5;

	    // fix y-position so that the name and the points do not overlap
	    if ( (height >= 0)
		 && (height < layout_height) )
	      height = layout_height;
	    else if ( (height < 0)
		      && (height > -layout_height) )
	      height = -layout_height;

	    this->pixmap->draw_layout(gc,
				      pos_x + (width - layout_width) / 2,
				      baseline - height
				      + ( (height >= 0)
					  ? ( - layout_height * 6 / 5)
					  : ( layout_height * 1 / 5)),
				      this->trickpile_points_layout);
	  } // draw points
	} // text
      } // for ( p < party.playerno())
      for (unsigned p = 0; p < party.playerno(); ++p) {
	Player const& player = party.player(p);
	int const points = party.pointsum(player);
	bool winner = (party.rang(player) == 0);

	int const pos_x = border_x + p * width;

	{ // draw pillar
	  int const height = (points
			      * total_height
			      / (max_points - min_points));

	  int const x = pos_x;
	  int const y = ( (height >= 0) ? baseline - height : baseline );
	  int const w = width;
	  int const h = ( (height >= 0) ? height : -height );

	  // ToDo: make the darkening faster

	  Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(this->pixmap);
	  gc->set_function(Gdk::COPY);

	  Glib::RefPtr<Gdk::Image> image
	    = this->pixmap->get_image(x, y, w, h);
	  Gdk::Color c;
	  int const round_border = w / 10;
	  for (int i = 0; i < w; ++i)
	    for (int j = 0; j < h; ++j) {
	      // round border
	      if (height >= 0) {
		if ((j < round_border)
		    && ( ( (i < round_border)
			   && ( ( QUAD(round_border - i)
				  + QUAD(round_border - j) )
				> QUAD(round_border) ) )
			 || ((i > w - round_border)
			     && ( ( QUAD(round_border - (w - i))
				    + QUAD(round_border - j) )
				  > QUAD(round_border) ) ) ) )
		  continue;
	      } else {
		if ((j > h - round_border)
		    && ( ( (i < round_border)
			   && ( ( QUAD(round_border - i)
				  + QUAD(round_border - (h - j)) )
				> QUAD(round_border) ) )
			 || ((i > w - round_border)
			     && ( ( QUAD(round_border - (w - i))
				    + QUAD(round_border - (h - j)) )
				  > QUAD(round_border) ) ) ) )
		  continue;
	      }

	      image->get_colormap()->query_color(image->get_pixel(i, j), c);
#if 0
	      if (winner)
		c.set_rgb_p(c.get_red_p() / 2,
			    c.get_green_p() / 2,
			    c.get_blue_p() / 2);
	      else
		c.set_rgb_p(c.get_red_p() / 1.5,
			    c.get_green_p() / 1.5,
			    c.get_blue_p() / 1.5);
#endif
	      if (winner)
		c.set_rgb_p(std::pow(c.get_red_p(), 3),
			    std::pow(c.get_green_p(), 3),
			    std::pow(c.get_blue_p(), 3));
	      else
		c.set_rgb_p(std::pow(c.get_red_p(), 2),
			    std::pow(c.get_green_p(), 2),
			    std::pow(c.get_blue_p(), 2));
	      gc->set_rgb_fg_color(c);
	      this->pixmap->draw_point(gc, x + i, y + j);
	    }
	} // draw pillar
      } // for ( p < party.playerno())

      return ;
    } // void Table::draw_party_summary()

  /**
   **
   ** draw all hands
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::draw_all_hands()
    {
      if (::game_status & GAMESTATUS::GAME) {
	for (vector<Hand*>::iterator h = this->hand_.begin();
	     h != this->hand_.end();
	     h++)
	  (*h)->draw(true);

      } // if (::game_status & GAMESTATUS::GAME)

      return ;
    } // void Table::draw_all_hands()

  /**
   **
   ** update all cards
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::cards_update()
    {
      this->draw_all();

      return ;
    } // void Table::cards_update()

  /**
   **
   ** update all cards back
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::cards_back_update()
    {
      this->draw_all_hands();

      return ;
    } // void Table::cards_back_update()

  /**
   **
   ** update all icons
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::icons_update()
    {
      // update all, because the size of the icons can have changed
      this->draw_all();

      return ;
    } // void Table::icons_update()

  /**
   **
   ** update the full area
   ** (just copy the pixmap to the drawing_area)
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::update_full_area()
    {
      this->update(Gdk::Rectangle(0, 0, -1, -1));

      return ;
    } // void Table::update_full_area()

  /**
   **
   ** update the area of the drawing area
   ** (just copy the area from the pixmap)
   **
   ** @param	area	area to update
   **
   ** @return	-
   **
   ** @version	0.6.2
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::update(Gdk::Rectangle const& area)
    {
      this->get_window()->draw_drawable(this->gc,
					this->pixmap,
					area.get_x(), area.get_y(),
					area.get_x(), area.get_y(),
					area.get_width(), area.get_height());

      return ;
    } // void Table::update(Gdk::Rectangle const& area);

  /**
   **
   ** loads the background
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::load_background()
    {
      vector<string> file;

      file.push_back(::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/"
		     + ::setting(Setting::BACKGROUND));
      file.push_back(::setting(Setting::PUBLIC_DATA_DIRECTORY) + "/"
		     + ::setting(Setting::BACKGROUND));
      file.push_back("./" + ::setting(Setting::BACKGROUND));
      file.push_back(::setting_default(Setting::PUBLIC_DATA_DIRECTORY) + "/"
		     + ::setting(Setting::BACKGROUND));
      file.push_back(::setting_default(Setting::PUBLIC_DATA_DIRECTORY) + "/"
		     + ::setting_default(Setting::BACKGROUND));

      Glib::RefPtr<Gdk::Pixmap> back_new;
      for (vector<string>::const_iterator f = file.begin();
	   f != file.end();
	   f++) {
	try {
	  if (Glib::file_test(*f + "." + ::setting(Setting::GRAPHIC_EXTENSION),
			      Glib::FILE_TEST_IS_REGULAR)) {
	    back_new = Gdk::Pixmap::create_from_xpm(this->pixmap,
						    Gdk::Color(),
						    *f + "." + ::setting(Setting::GRAPHIC_EXTENSION));
	    break;
	  } // if (Glib::file_test(*file, Glib::FILE_TEST_IS_REGULAR))
	} // try
	catch (Glib::FileError file_error) { }

      } // for (f \in file)

      if (back_new)
	this->gc->set_tile(back_new);

      this->draw_all();

      return ;
    } // void Table::load_background()

  /**
   **
   ** the name of 'player' has changed
   **
   ** @param	player	the player with the changed name
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::name_changed(Player const& player)
    {
      this->draw_all();
      if (this->party_points_)
	this->party_points_->name_changed(player);

      for (vector<GameinfoDialog*>::iterator
	   gid = this->gameinfo_.begin();
	   gid != this->gameinfo_.end();
	   ++gid)
	(*gid)->name_changed(player);

      if (this->last_trick_)
	this->last_trick_->name_changed(player);

      if (this->game_finished_)
	this->game_finished_->name_changed(player);

      if (this->party_finished_)
	this->party_finished_->name_changed(player);

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

  /**
   **
   ** updates the font
   **
   ** @param	fontname	the name of the new font
   ** @param	type		the type of the font
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::new_font(string const& fontname, Setting::TypeString const type)
    {
      switch (type) {
      case Setting::NAME_FONT:
	if (!this->name_layout)
	  break;

	this->name_layout->set_font_description(Pango::FontDescription(fontname));

	this->draw_all();
	break;

      case Setting::TRICKPILE_POINTS_FONT:
	if (!this->trickpile_points_layout)
	  break;

	this->trickpile_points_layout->set_font_description(Pango::FontDescription(fontname));

	for (vector<TrickPile*>::iterator t = this->trickpile_.begin();
	     t != this->trickpile_.end();
	     t++)
	  (*t)->draw(true);
	break;

      default:
	break;
      } // switch (type)

      return ;
    } // void Table::new_font(string const& fontname, Setting::TypeString const type)

  /**
   **
   ** updates the 
   **
   ** @param	colorname	the name of the new color
   ** @param	type		the type of the font
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   ** @todo	using 'set_foreground'
   **
   **/
  void
    Table::new_color(string const& colorname, Setting::TypeString const type)
    {
      switch (type) {
      case Setting::NAME_FONT_COLOR:
      case Setting::NAME_ACTIVE_FONT_COLOR:
	if ( ( (type == Setting::NAME_FONT_COLOR)
	       && !this->name_gc)
	     || ( (type == Setting::NAME_ACTIVE_FONT_COLOR)
		  && !this->name_active_gc))
	  break;

	if (type == Setting::NAME_FONT_COLOR) {
	  this->name_gc->set_rgb_fg_color(Gdk::Color(colorname));
#ifdef POSTPHONED
	  this->name_gc->set_foreground(Gdk::Color(colorname));
#endif
	} else if (type == Setting::NAME_ACTIVE_FONT_COLOR) {
	  this->name_active_gc->set_rgb_fg_color(Gdk::Color(colorname));
	} else {
	  DEBUG_ASSERTION(false,
			  "Table::new_color(colorname, type):\n"
			  "  'type' = '" << type << "' "
			  "neither 'NAME_FONT_COLOR' "
			  "nor 'NAME_ACTIVE_FONT_COLOR'");
	} // if (type == Setting::..._FONT)

	for (vector<Name*>::iterator n = this->name_.begin();
	     n != this->name_.end();
	     n++)
	  (*n)->draw();
	break;

      case Setting::TRICKPILE_POINTS_FONT_COLOR:
	if (!this->trickpile_points_gc)
	  break;

	this->trickpile_points_gc->set_rgb_fg_color(Gdk::Color(colorname));
#ifdef POSTPHONED
	this->trickpile_points_gc->set_foreground(Gdk::Color(colorname));
#endif

	for (vector<TrickPile*>::iterator t = this->trickpile_.begin();
	     t != this->trickpile_.end();
	     t++)
	  (*t)->draw_points(true);
	break;

      case Setting::POVERTY_SHIFT_ARROW_COLOR:
	if (!this->poverty_shift_arrow_gc)
	  break;

	this->poverty_shift_arrow_gc->set_rgb_fg_color(Gdk::Color(colorname));
	if ((::game_status == GAMESTATUS::GAME_POVERTY_SHIFT)
	    && this->poverty_)
	  this->poverty().draw();

	break;

      default:
	break;
      } // switch(type)

      return ;
    } // void Table::new_color(string const& colorname, Setting::TypeString const type)

  /**
   **
   ** the setting has changed
   **
   ** @param	type	the type that has changed
   **
   ** @return	-
   **
   ** @version	0.6.1
   **
   ** @author	Diether Knof
   **
   **/
  void
    Table::setting_update(int const type)
    {
      switch(type) {
      case Setting::SHOW_ALL_HANDS:
	this->draw_all_hands();
	if ((::game_status == GAMESTATUS::GAME_POVERTY_SHIFT)
	    && this->poverty_)
	  this->poverty().draw();
	break;
      case Setting::EMPHASIZE_VALID_CARDS:
	if ( (::game_status == GAMESTATUS::GAME_PLAY)
	     && (::party.game().player_current().type() == Player::HUMAN) ) {
	  this->draw_all();
	  this->mouse_cursor_update();
	}
	break ;
      case Setting::AUTOMATIC_CARD_SUGGESTION:
	if (::setting(Setting::AUTOMATIC_CARD_SUGGESTION))
	  this->show_card_suggestion(false);
	break;
      case Setting::SHOW_TRICKPILES_POINTS:
	for (vector<TrickPile*>::iterator t = this->trickpile_.begin();
	     t != this->trickpile_.end();
	     t++)
	  (*t)->draw();
	break;
      case Setting::ANNOUNCE_SWINES_AUTOMATICALLY:
	if (::game_status == GAMESTATUS::GAME_RESERVATION) {
	  for (vector<Reservation*>::iterator r = this->reservation_.begin();
	       r != this->reservation_.end();
	       ++r)
	    (*r)->sensitivity_update();
	} // if (::game_status == GAMESTATUS::GAME_RESERVATION)
      case Setting::BACKGROUND:
	this->load_background();
	break;
      case Setting::ROTATE_TRICK_CARDS:
#ifdef POSTPHONED
	// The old trick is not cleared fully
	this->trick_->draw();
#else
	this->draw_all();
#endif
	break;
      case Setting::NAME_FONT:
      case Setting::TRICKPILE_POINTS_FONT:
	this->new_font(::setting(static_cast<Setting::TypeString const>(type)),
		       static_cast<Setting::TypeString const>(type));
	break;
      case Setting::NAME_FONT_COLOR:
      case Setting::NAME_ACTIVE_FONT_COLOR:
      case Setting::TRICKPILE_POINTS_FONT_COLOR:
      case Setting::POVERTY_SHIFT_ARROW_COLOR:
	this->new_color(::setting(static_cast<Setting::TypeString const>(type)),
			static_cast<Setting::TypeString const>(type));
	break;
      default:
	break;
      } // switch(type)

      return ;
    } // void Table::setting_update(int const type)

  /**
   **
   ** update the mouse cursor
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @version	0.6.5
   **
   ** @author	Diether Knof
   **
   ** @todo	accept poverty
   ** @todo	shift poverty
   ** @todo	take card from poverty
   ** @todo	close full trick / window
   ** @todo	make announcement
   ** @todo	show last trick
   **
   **/
  void
    Table::mouse_cursor_update()
    {
      int x, y;
      this->get_pointer(x, y);

      CursorType new_cursor_type = DEFAULT;
      if (this->ui->busy())
	new_cursor_type = BUSY;

      if (::game_status == GAMESTATUS::GAME_POVERTY_SHIFT) {
	DEBUG_ASSERTION(this->poverty_,
			"Table::mouse_cursor_update():\n"
			"  'this->poverty_' == NULL");
	switch (this->poverty_->possible_action(x, y)) {
	case Poverty::NONE:
	  break;
	case Poverty::SHIFT_CARDS:
	  new_cursor_type = POVERTY_SHIFT;
	  break;
	case Poverty::ACCEPT_CARDS:
	  new_cursor_type = POVERTY_ACCEPT;
	  break;
	case Poverty::TAKE_CARD:
	  new_cursor_type = POVERTY_GET_CARD;
	  break;
	case Poverty::PUT_CARD:
	  new_cursor_type = POVERTY_PUT_CARD;
	  break;
	case Poverty::RETURN_CARDS:
	  new_cursor_type = POVERTY_SHIFT_BACK;
	  break;
	case Poverty::GET_CARDS_BACK:
	  new_cursor_type = POVERTY_GETTING_BACK;
	  break;

	} // switch (this->poverty_->possible_action(x, y))
      } else if (::game_status == GAMESTATUS::GAME_PLAY) {
	// the human player has to play a card
	Player& player = ::party.game().player_current();
	if (this->get_card()) {
	DEBUG_ASSERTION((player.type() == Player::HUMAN),
			"UI_GTKMM::Table::mouse_cursor_update():\n"
			"  a card should be get, but the player '"
			<< player.no() << "' is not human but '"
			<< player.type() << "'");
	  Card const card = this->hand(player).card_at_position(x, y);
	  if (card) {
	    if (::setting(Setting::SHOW_IF_VALID)
		&& player.game().rule()(Rule::SHOW_IF_VALID)) {
	      if (player.game().trick_current().isvalid(card,
							player.hand()))
		new_cursor_type = CARD_VALID;
	      else
		new_cursor_type = CARD_INVALID;
	    } else { // if !(SHOW_IF_VALID)
	      new_cursor_type = PLAY_CARD;
	    } // if !(SHOW_IF_VALID)

	  } // if (card)
	} // if (player.type() == Player::HUMAN)

      } else if (this->full_trick_
		 && this->full_trick_->is_visible()) {
	new_cursor_type = CLOSE_FULL_TRICK;
      } // if (::game_status == ...)

      if (::in_running_game()) {
	Game const& game = ::party.game();
	for (vector<Player*>::const_iterator p = game.player_begin();
	     p != game.player_end();
	     ++p)
	  if (this->trickpile(**p).over_trick(x, y))
	    new_cursor_type = SHOW_LAST_TRICK;
      }	// if (::game_status in game)

      if (this->in_game_review())
	new_cursor_type = GAME_REVIEW;

      if (this->cursor_type != new_cursor_type) {
	this->cursor_type = new_cursor_type;
	this->get_window()->set_cursor(Table::cursor(this->cursor_type));
      } // if (this->cursor_type != new_cursor_type)

      return ;
    } // void Table::mouse_cursor_update()

  /**
   **
   ** event: redraw part of the window
   **
   ** @param	event	the event
   **
   ** @return	true
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  bool
    Table::on_expose_event(GdkEventExpose* event)
    {
      this->update(Gdk::Rectangle(event->area.x, event->area.y,
				  event->area.width, event->area.height));

      return true;
    } // bool Table::on_expose_event(GdkEventExpose* event)

  /**
   **
   ** event: the size has changed
   **
   ** @param	event	the event
   **
   ** @return	true
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  bool
    Table::on_configure_event(GdkEventConfigure* event)
    {
      this->pixmap = Gdk::Pixmap::create(this->get_window(),
					 this->width(), this->height(),
					 this->depth());

      if (!this->gc) {
	this->gc = Gdk::GC::create(this->pixmap);
	this->gc->set_fill(Gdk::TILED);
	this->load_background();

	this->poverty_shift_arrow_gc = Gdk::GC::create(this->pixmap);
	this->new_color(::setting(Setting::POVERTY_SHIFT_ARROW_COLOR),
			Setting::POVERTY_SHIFT_ARROW_COLOR);

	this->name_layout = this->create_pango_layout("");
	this->new_font(::setting(Setting::NAME_FONT), Setting::NAME_FONT);

	this->name_gc = Gdk::GC::create(this->pixmap);
	this->new_color(::setting(Setting::NAME_FONT_COLOR),
			Setting::NAME_FONT_COLOR);

	this->name_active_gc = Gdk::GC::create(this->pixmap);
	this->new_color(::setting(Setting::NAME_ACTIVE_FONT_COLOR),
			Setting::NAME_ACTIVE_FONT_COLOR);

	this->trickpile_points_layout = this->create_pango_layout("");
	this->new_font(::setting(Setting::TRICKPILE_POINTS_FONT),
		       Setting::TRICKPILE_POINTS_FONT);

	this->trickpile_points_gc = Gdk::GC::create(this->pixmap);
	this->new_color(::setting(Setting::TRICKPILE_POINTS_FONT_COLOR),
			Setting::TRICKPILE_POINTS_FONT_COLOR);
      } // if (!this->gc)

      this->draw_all();

      return true;
    } // bool Table::on_configure_event(GdkEventConfigure* event)

  /**
   **
   ** event: mouse motion
   **
   ** @param	event	the event
   **
   ** @return	whether there was a reaction
   **
   ** @version	0.6.5
   **
   ** @author	Diether Knof
   **
   **/
  bool
    Table::on_motion_notify_event(GdkEventMotion* event)
    {
      this->mouse_cursor_update();

      return false;
    } // bool Table::on_motion_notify_event(GdkEventMotion* event)

  /**
   **
   ** event: mouse motion
   **
   ** @param	event	the event
   **
   ** @return	whether there was a reaction
   **
   ** @version	0.6.5
   **
   ** @author	Diether Knof
   **
   **/
  Gdk::Cursor
    Table::cursor(CursorType const type)
    {
      switch (type) {
      case NONE:
	DEBUG_ASSERTION(false,
			"Table::cursor(type):\n"
			"  'type' == NONE");
	break;
      case DEFAULT:
	return Gdk::Cursor(Gdk::X_CURSOR);
	break;
      case BUSY:
	{
#ifdef POSTPHONED
	  Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_file("ui/gtkmm/cursor/busy.png");
	  GdkCursor* cursor = gdk_cursor_new_from_pixbuf(Gdk::Cursor(Gdk::X_CURSOR).get_display()->gobj(), pixbuf->gobj(), 0, 0);
	  //Gdk::Cursor(Gdk::Pixbuf::create_from_file("ui/gtkmm/cursor/busy.png"),);
	  return Gdk::Cursor(cursor);
#endif
	}
	return Gdk::Cursor(Gdk::WATCH);
	//return Gdk::Cursor(Gdk::EXCHANGE);
	//return Gdk::Cursor(Gdk::CLOCK);
	break;
      case POVERTY_SHIFT:
	return Gdk::Cursor(Gdk::SB_LEFT_ARROW);
	break;
      case POVERTY_SHIFT_BACK:
	return Gdk::Cursor(Gdk::SB_RIGHT_ARROW);
	break;
      case POVERTY_GET_CARD:
      case POVERTY_PUT_CARD:
	return Gdk::Cursor(Gdk::HAND1);
	break;
      case POVERTY_ACCEPT:
      case POVERTY_GETTING_BACK:
	return Gdk::Cursor(Gdk::HAND2);
	break;
      case PLAY_CARD:
	return Gdk::Cursor(Gdk::HAND1);
	break;
      case CARD_VALID:
	return Gdk::Cursor(Gdk::HAND1);
	break;
      case CARD_INVALID:
	return Gdk::Cursor(Gdk::PIRATE);
	break;
      case CLOSE_FULL_TRICK:
	return Gdk::Cursor(Gdk::TOP_LEFT_ARROW);
	break;
      case SHOW_LAST_TRICK:
	return Gdk::Cursor(Gdk::HAND2);
	break;
      case GAME_REVIEW:
	return Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW);
	break;
      } // switch (type)

      return Gdk::Cursor();
    } // static Gdk::Cursor Table::cursor(CursorType const type)
} // namespace UI_GTKMM_NS

#endif // #ifdef USE_UI_GTKMM
