1 /**********************************************************************
2  *
3  *   FreeDoko a Doppelkopf-Game
4  *
5  *   Copyright (C) 2001 – 2018 by Diether Knof and Borg Enders
6  *
7  *   This program is free software; you can redistribute it and/or
8  *   modify it under the terms of the GNU General Public License as
9  *   published by the Free Software Foundation; either version 2 of
10  *   the License, or (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *   You can find this license in the file 'gpl.txt'.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program; if not, write to the Free Software
20  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  *   MA  02111-1307  USA
22  *
23  *  Contact:
24  *    Diether Knof dknof@posteo.de
25  *
26  *********************************************************************/
27 
28 #include "constants.h"
29 
30 #ifdef USE_UI_GTKMM
31 
32 #include "table.h"
33 #include "poverty.h"
34 #include "party_points.h"
35 #include "party_finished.h"
36 #include "last_trick.h"
37 #include "cards_distribution.h"
38 #include "trick.h"
39 #include "hand.h"
40 #include "trickpile.h"
41 #include "icongroup.h"
42 #include "name.h"
43 #include "reservation.h"
44 
45 #include "ui.h"
46 #include "cards.h"
47 #include "gameinfo_dialog.h"
48 #include "full_trick.h"
49 #include "game_finished.h"
50 
51 #include "../../party/party.h"
52 #include "../../party/rule.h"
53 #include "../../game/game.h"
54 #include "../../player/player.h"
55 #include "../../card/trick.h"
56 #include "../../misc/preferences.h"
57 #include "../../utils/string.h"
58 
59 #include <gtkmm/drawingarea.h>
60 #include <gdkmm/general.h>
61 #include <cmath>
62 using std::pow;
63 
64 namespace UI_GTKMM_NS {
65 
66 /** updates the table from the buffer (the surface)
67  **/
68 void
update_from_buffer()69 Table::update_from_buffer()
70 {
71   auto cr = this->get_window()->create_cairo_context();
72   cr->set_source(this->surface_, 0, 0);
73   cr->paint();
74 } // void Table::update_from_buffer()
75 
76 /** draw all
77  **/
78 void
draw_all()79 Table::draw_all()
80 {
81   if (!this->get_realized())
82     return ;
83 
84   auto cr = this->create_cairo_context();
85 
86   cr->push_group();
87 
88 
89   this->draw_background(cr);
90 
91   if (::game_status & GameStatus::game) {
92     for (auto& p : this->part_) {
93       auto const outline = p->outline();
94       cr->set_source(p->surface(), outline.x(), outline.y());
95       cr->paint();
96     }
97   } else { // if (::game_status)
98     this->draw_logo(cr);
99   } // if (::game_status)
100   cr->pop_group_to_source();
101   cr->paint();
102 
103   this->update_from_buffer();
104 
105 #ifdef WINDOWS
106 #ifdef WORKAROUND
107   // Sometimes the window is not updated
108   this->queue_draw();
109 #endif
110 #endif
111 
112   this->mouse_cursor_update();
113 } // void Table::draw_all()
114 
115 /** Force the redrawing of all elements
116  **/
117 void
force_redraw_all()118 Table::force_redraw_all()
119 {
120   for (auto p : this->part_)
121     p->force_redraw();
122   this->draw_all();
123 } // void Table::force_redraw_all()
124 
125 /** draw the background
126  ** According to the size of the background as pattern or as image
127  **/
128 void
draw_background(Cairo::RefPtr<::Cairo::Context> cr)129 Table::draw_background(Cairo::RefPtr<::Cairo::Context> cr)
130 {
131   cr->save();
132   if (   this->width() < 2 * this->background_pixbuf->get_width()
133       && this->height() < 2  * this->background_pixbuf->get_height()) {
134     cr->scale(static_cast<double>(this->width()) / this->background_pixbuf->get_width(),
135               static_cast<double>(this->height()) / this->background_pixbuf->get_height());
136     Gdk::Cairo::set_source_pixbuf(cr, this->background_pixbuf, 0, 0);
137     cr->paint();
138   } else {
139     cr->set_source(this->background_pattern);
140     cr->rectangle(0, 0, this->width(), this->height());
141     cr->fill();
142   }
143   cr->restore();
144 }
145 
146 /** draw the logo
147  **
148  ** @param    cr     drawing context
149  **
150  ** @bug   clip mask does not work (GTK-bug?)
151  **/
152 void
draw_logo(Cairo::RefPtr<::Cairo::Context> cr)153 Table::draw_logo(Cairo::RefPtr<::Cairo::Context> cr)
154 {
155   if (!this->get_realized())
156     return ;
157   if (!this->ui->logo)
158     return ;
159 
160   cr->push_group();
161   draw_pixbuf(cr, this->ui->logo,
162               (this->width() - this->ui->logo->get_width()) / 2,
163               (this->height() - this->ui->logo->get_height()) / 2);
164   { // draw a border with the cards
165     auto const card_width = this->ui->cards->width();
166     auto const card_height = this->ui->cards->height();
167 
168     auto surface_top = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
169                                                    this->width(),
170                                                    this->height());
171     auto surface_right = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
172                                                      this->width(),
173                                                      this->height());
174     auto surface_bottom = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
175                                                       this->width(),
176                                                       this->height());
177     auto surface_left = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
178                                                     this->width(),
179                                                     this->height());
180 
181     { // top
182       auto cr = Cairo::Context::create(surface_top);
183       vector<Card> const border_card =  {
184         {Card::heart_ten},
185         {Card::club_queen},
186         {Card::spade_queen},
187         {Card::heart_queen},
188         {Card::diamond_queen},
189         {Card::club_jack},
190         {Card::spade_jack}};
191 
192       auto const border_top_x
193         = (card_width
194            + ( (static_cast<int>(border_card.size()) * card_height
195                 + card_width >= this->width())
196               ? 0
197               : ((this->width() - (border_card.size() * card_height
198                                    + card_width)
199                  ) / border_card.size())
200              )
201           );
202 
203       for (size_t n = 0; n < border_card.size(); n++) {
204         draw_pixbuf(cr,
205                     this->ui->cards->card(border_card[n], Rotation::left),
206                     border_top_x +
207                     n * (this->width() - 2 * border_top_x
208                          - card_height)
209                     / (border_card.size() - 1),
210                     card_width / 2);
211       } // for (n < border_card.size())
212     } // top
213     { // right
214       auto cr = Cairo::Context::create(surface_right);
215       vector<Card> const border_card = {
216         {Card::heart_jack},
217         {Card::diamond_jack},
218         {Card::diamond_ace},
219         {Card::diamond_ten},
220         {Card::diamond_king}};
221 
222       auto const border_right_y
223         = (card_width
224            + ( (static_cast<int>(border_card.size()) * card_height
225                 + card_width >= this->height())
226               ? 0
227               : ((this->height() - (border_card.size() * card_height
228                                     + card_width)
229                  ) / border_card.size())
230              )
231           );
232 
233       for (size_t n = 0; n < border_card.size(); n++) {
234         draw_pixbuf(cr,
235                     this->ui->cards->card(border_card[n], Rotation::up),
236                     this->width() - 3 * card_width / 2,
237                     border_right_y +
238                     n * (this->height() - 2 * border_right_y
239                          - card_height)
240                     / (border_card.size() - 1)
241                    );
242         cr->paint();
243       }
244     } // right
245     { // bottom
246       auto cr = Cairo::Context::create(surface_bottom);
247       vector<Card> const border_card = {
248         {Card::diamond_nine},
249         {Card::club_ace},
250         {Card::club_ten},
251         {Card::club_king},
252         {Card::club_nine},
253         {Card::heart_ace},
254         {Card::heart_king}};
255 
256       auto const border_bottom_x
257         = (card_width
258            + ( (static_cast<int>(border_card.size()) * card_height
259                 + card_width >= this->width())
260               ? 0
261               : ((this->width() - (border_card.size() * card_height
262                                    + card_width)
263                  ) / border_card.size())
264              )
265           );
266 
267       for (size_t n = 0; n < border_card.size(); n++) {
268         draw_pixbuf(cr,
269                     this->ui->cards->card(border_card[n], Rotation::right),
270                     border_bottom_x +
271                     (border_card.size() - 1 - n)
272                     * (this->width() - 2 * border_bottom_x
273                        - card_height)
274                     / (border_card.size() - 1),
275                     this->height()
276                     - 3 * card_width / 2
277                    );
278       }
279     } // bottom
280     int border_left_y = 0;
281     { // left
282       auto cr = Cairo::Context::create(surface_left);
283       vector<Card> const border_card = {
284         {Card::heart_nine},
285         {Card::spade_ace},
286         {Card::spade_ten},
287         {Card::spade_king},
288         {Card::spade_nine}};
289 
290       border_left_y
291         = (card_width
292            + ( (static_cast<int>(border_card.size()) * card_height
293                 + card_width >= this->height())
294               ? 0
295               : ((this->height() - (border_card.size() * card_height
296                                     + card_width)
297                  ) / border_card.size())
298              )
299           );
300 
301       for (size_t n = 0; n < border_card.size(); n++) {
302         int const pos_x = card_width / 2;
303         int const pos_y = (border_left_y +
304                            (border_card.size() - 1 - n)
305                            * (this->height() - 2 * border_left_y
306                               - card_height)
307                            / (border_card.size() - 1));
308 
309         draw_pixbuf(cr,
310                     this->ui->cards->card(border_card[n], Rotation::down),
311                     pos_x, pos_y);
312       }
313     } // left
314 
315     cr->set_source(surface_top, 0, 0);
316     cr->paint();
317     cr->set_source(surface_right, 0, 0);
318     cr->paint();
319     cr->set_source(surface_bottom, 0, 0);
320     cr->paint();
321     cr->set_source(surface_left, 0, 0);
322     cr->paint();
323     {
324       cr->save();
325       cr->rectangle(0, 0,
326                     border_left_y + max(this->ui->cards->width(), this->ui->cards->height()),
327                     this->height());
328       cr->clip();
329       cr->set_source(surface_top, 0, 0);
330       cr->paint();
331       cr->restore();
332     }
333   } // draw a border with the cards
334   cr->pop_group_to_source();
335   cr->paint();
336 
337   this->update_from_buffer();
338 } // void Table::draw_logo(Cairo::RefPtr<::Cairo::Context> cr)
339 
340 /** update the cards distribution
341  **/
342 void
update_cards_distribution()343 Table::update_cards_distribution()
344 {
345   this->cards_distribution_->force_redraw();
346 }
347 
348 /** update all hands
349  **/
350 void
update_hands()351 Table::update_hands()
352 {
353   for (auto& h : this->hand_)
354     h.second->force_redraw();
355 }
356 
357 /** update all cards
358  **/
359 void
update_cards()360 Table::update_cards()
361 {
362   for (auto& h : this->hand_)
363     h.second->force_redraw();
364   for (auto& t : this->trickpile_)
365     t.second->force_redraw();
366   this->trick_->force_redraw();
367   this->poverty_->force_redraw();
368 }
369 
370 /** update all cards back
371  **/
372 void
update_cards_back()373 Table::update_cards_back()
374 {
375   for (auto& h : this->hand_)
376     h.second->force_redraw();
377   for (auto& t : this->trickpile_)
378     t.second->force_redraw();
379 } // void Table::update_cards_back()
380 
381 /** update all icons
382  **/
383 void
update_icons()384 Table::update_icons()
385 {
386   for (auto& i : this->icongroup_)
387     i.second->force_redraw();
388 } // void Table::update_icons()
389 
390 /** loads the background
391  **/
392 void
load_background()393 Table::load_background()
394 {
395   try {
396     auto back_new = Gdk::Pixbuf::create_from_file(::preferences.path(::Preferences::Type::background));
397     if (!back_new)
398       throw Glib::FileError(Glib::FileError::FAILED,
399                             "error loading pixmap from '"
400                             + ::preferences.path(::Preferences::Type::background) + "'");
401     this->background_pixbuf = back_new;
402 
403     auto surface = Cairo::ImageSurface::create(Cairo::FORMAT_RGB24,
404                                                back_new->get_width(),
405                                                back_new->get_height());
406     auto cr = Cairo::Context::create(surface);
407     Gdk::Cairo::set_source_pixbuf(cr, back_new, 0, 0);
408     cr->paint();
409     this->background_pattern = Cairo::SurfacePattern::create(surface);
410     this->background_pattern->set_extend(Cairo::EXTEND_REPEAT);
411 
412     this->draw_all();
413   } catch (Glib::FileError const& file_error) {
414     this->ui->error(_("Error::loading the background %s",
415                       ::preferences.value(::Preferences::Type::background)
416                      ));
417   } // try
418 } // void Table::load_background()
419 
420 /** update the cards
421  **/
422 void
cards_update()423 Table::cards_update()
424 { this->force_redraw_all(); }
425 
426 /** update the cards back
427  **/
428 void
cards_back_update()429 Table::cards_back_update()
430 { this->force_redraw_all(); }
431 
432 /** the name of 'player' has changed
433  **
434  ** @param    player   the player with the changed name
435  **/
436 void
name_changed(Player const & player)437 Table::name_changed(Player const& player)
438 {
439   if (this->party_points_)
440     this->party_points_->name_changed(player);
441 
442   if (this->party_finished_)
443     this->party_finished_->name_changed(player);
444 
445   if (::game_status < GameStatus::game_init)
446     return ;
447   this->force_redraw_all();
448   this->force_redraw_all(); // two times, for widescreen layout to get all positions right
449 
450   this->gameinfo_->name_changed(player);
451 
452   if (this->last_trick_)
453     this->last_trick_->name_changed(player);
454 
455   if (this->game_finished_)
456     this->game_finished_->name_changed(player);
457 
458   if (!this->name_.empty())
459     this->name(player).force_redraw();
460 } // void Table::name_changed(Player const& player)
461 
462 /** updates the font
463  **
464  ** @param    fontname   the name of the new font
465  ** @param    type        the type of the font
466  **/
467 void
new_font(string const & fontname,::Preferences::Type::String const type)468 Table::new_font(string const& fontname, ::Preferences::Type::String const type)
469 {
470   switch (type) {
471   case ::Preferences::Type::name_font:
472     for (auto& n : this->name_)
473       n.second->force_redraw();
474     this->draw_all();
475     break;
476 
477   case ::Preferences::Type::trickpile_points_font:
478     for (auto& t : this->trickpile_)
479       t.second->force_redraw();
480     this->draw_all();
481     break;
482   default:
483     break;
484   } // switch (type)
485 } // void Table::new_font(string fontname, ::Preferences::Type::String type)
486 
487 /** updates the  color
488  **
489  ** @param    colorname   the name of the new color
490  ** @param    type      the type of the font
491  **
492  ** @todo   using 'set_foreground'
493  **/
494 void
new_color(string const & colorname,::Preferences::Type::String const type)495 Table::new_color(string const& colorname, ::Preferences::Type::String const type)
496 {
497   auto cr = this->create_cairo_context();
498   cr->push_group();
499   switch (type) {
500   case ::Preferences::Type::name_font_color:
501   case ::Preferences::Type::name_active_font_color:
502   case ::Preferences::Type::name_reservation_font_color: {
503 
504     for (auto& n : this->name_)
505       n.second->force_redraw();
506     this->draw_all();
507 
508   } break;
509 
510   case ::Preferences::Type::trickpile_points_font_color:
511     for (auto& t : this->trickpile_)
512       t.second->force_redraw();
513     this->draw_all();
514     break;
515 
516   case ::Preferences::Type::poverty_shift_arrow_color:
517     if ((::game_status == GameStatus::game_poverty_shift)
518         && this->poverty_)
519       this->poverty().force_redraw();
520     this->draw_all();
521 
522     break;
523 
524   default:
525     break;
526   } // switch(type)
527 } // void Table::new_color(string colorname, ::Preferences::Type::String type)
528 
529 /** the setting has changed
530  **
531  ** @param    type   the type that has changed
532  **/
533 void
preference_update(int const type)534 Table::preference_update(int const type)
535 {
536   switch(type) {
537   case ::Preferences::Type::show_all_hands:
538     this->force_redraw_all();
539     break;
540   case ::Preferences::Type::emphasize_valid_cards:
541     if ( (::game_status == GameStatus::game_play)
542         && (this->ui->game().players().current_player().type() == Player::Type::human) ) {
543       this->force_redraw_all();
544     }
545     break ;
546   case ::Preferences::Type::automatic_card_suggestion:
547     if (::preferences(::Preferences::Type::automatic_card_suggestion))
548       if (   (::game_status == GameStatus::game_play)
549           && !this->ui->game().tricks().current().isfull()
550           && (this->ui->game().players().current_player().type() == Player::Type::human))
551         this->show_card_suggestion(false);
552     break;
553   case ::Preferences::Type::show_trickpiles_points:
554     for (auto& t : this->trickpile_)
555       t.second->force_redraw();
556     this->draw_all();
557     break;
558   case ::Preferences::Type::announce_swines_automatically:
559     if (::game_status == GameStatus::game_reservation) {
560       for (auto& r : this->reservation_)
561         r.second->sensitivity_update();
562     } // if (::game_status == GameStatus::game_reservation)
563   case ::Preferences::Type::background:
564     this->load_background();
565     break;
566   case ::Preferences::Type::rotate_trick_cards:
567     this->trick_->force_redraw();
568     this->draw_all();
569     break;
570   case ::Preferences::Type::name_font:
571   case ::Preferences::Type::trickpile_points_font:
572     this->new_font(::preferences(static_cast<::Preferences::Type::String const>(type)),
573                    static_cast<::Preferences::Type::String const>(type));
574     break;
575   case ::Preferences::Type::name_font_color:
576   case ::Preferences::Type::name_active_font_color:
577   case ::Preferences::Type::trickpile_points_font_color:
578   case ::Preferences::Type::poverty_shift_arrow_color:
579     this->new_color(::preferences(static_cast<::Preferences::Type::String const>(type)),
580                     static_cast<::Preferences::Type::String const>(type));
581     break;
582   case ::Preferences::Type::own_hand_on_table_bottom:
583   case ::Preferences::Type::table_rotation:
584     this->draw_all();
585     break;
586 
587   default:
588     break;
589   } // switch(type)
590 } // void Table::preference_update(int type)
591 
592 /** update the mouse cursor
593  **/
594 void
mouse_cursor_update()595 Table::mouse_cursor_update()
596 {
597   if (!this->get_realized())
598     return ;
599   int x, y;
600   this->get_pointer(x, y);
601 
602   CursorType new_cursor_type = CursorType::standard;
603   if (this->ui->busy())
604     new_cursor_type = CursorType::busy;
605 
606   switch (::game_status) {
607   case GameStatus::game_poverty_shift:
608     DEBUG_ASSERTION(this->poverty_,
609                     "Table::mouse_cursor_update():\n"
610                     "  'this->poverty_' == nullptr");
611     switch (this->poverty().possible_action(x, y)) {
612     case Poverty::Action::none:
613       break;
614     case Poverty::Action::shift_cards:
615       new_cursor_type = CursorType::poverty_shift;
616       break;
617     case Poverty::Action::accept_cards:
618       new_cursor_type = CursorType::poverty_accept;
619       break;
620     case Poverty::Action::take_card:
621       new_cursor_type = CursorType::poverty_get_card;
622       break;
623     case Poverty::Action::put_card:
624       new_cursor_type = CursorType::poverty_put_card;
625       break;
626     case Poverty::Action::fill_up:
627       new_cursor_type = CursorType::poverty_fill_up;
628       break;
629     case Poverty::Action::return_cards:
630       new_cursor_type = CursorType::poverty_shift_back;
631       break;
632     case Poverty::Action::get_cards_back:
633       new_cursor_type = CursorType::poverty_getting_back;
634       break;
635 
636     } // switch (this->poverty().possible_action(x, y))
637     break;
638 
639   case GameStatus::game_play: {
640     // the human player has to play a card
641     Player& player = this->ui->game().players().current_player();
642     if (this->get_card()) {
643       DEBUG_ASSERTION((player.type() == Player::Type::human),
644                       "UI_GTKMM::Table::mouse_cursor_update():\n"
645                       "  a card should be get, but the player '"
646                       << player.no() << "' is not human but '"
647                       << player.type() << "'");
648       auto const card = this->hand(player).card_at_position(x, y);
649       if (!card.is_empty()) {
650         if (::preferences(::Preferences::Type::show_if_valid)) {
651           if (player.game().tricks().current().isvalid(card, player.hand()))
652             new_cursor_type = CursorType::card_valid;
653           else
654             new_cursor_type = CursorType::card_invalid;
655         } else { // if !(show_if_valid)
656           new_cursor_type = CursorType::play_card;
657         } // if !(show_if_valid)
658 
659       } // if (card)
660     } // if (player.type() == Player::Type::human)
661     break;
662   } // case GameStatus::game_play:
663   default:
664     if (   ::in_running_game()
665         && this->ui->game().tricks().current().isfull()
666         && this->full_trick_
667         && this->trick().mouse_over_cards()) {
668       new_cursor_type = CursorType::close_full_trick;
669     } // if (::game_status == ...)
670     break;
671   } // switch(::game_type)
672 
673   if (::in_running_game()) {
674     auto const& game = this->ui->game();
675     for (auto const& p : game.players()) {
676       if (this->trickpile(p).over_trick(x, y))
677         new_cursor_type = CursorType::show_last_trick;
678       if (p.type() == Player::Type::human) {
679         if (this->icongroup(p).mouse_over_next_announcement())
680           new_cursor_type = CursorType::next_announcement;
681       } // if (p->type() == Player::Type::human)
682     } // for (p : game.player)
683   } // if (::game_status in game)
684 
685 
686   if (this->in_game_review())
687     new_cursor_type = CursorType::game_review;
688 
689   if (this->cursor_type != new_cursor_type) {
690     if (   (this->cursor_type == CursorType::next_announcement)
691         || (new_cursor_type == CursorType::next_announcement) ) {
692       this->cursor_type = new_cursor_type;
693       this->draw_all();
694     }
695     this->cursor_type = new_cursor_type;
696     this->get_window()->set_cursor(Table::cursor(this->cursor_type));
697   } // if (this->cursor_type != new_cursor_type)
698 } // void Table::mouse_cursor_update()
699 
700 /** event: draw the table
701  **
702  ** @param    c   the graphic context
703  **
704  ** @return   true
705  **/
706 bool
on_draw(::Cairo::RefPtr<::Cairo::Context> const & c)707 Table::on_draw(::Cairo::RefPtr<::Cairo::Context> const& c)
708 {
709   c->set_source(this->surface_, 0, 0);
710   c->paint();
711   return true;
712 } // bool Table::on_draw(::Cairo::RefPtr<::Cairo::Context> c)
713 
714 /** event: the size has changed
715  **
716  ** @param    event   the event
717  **
718  ** @return   true
719  **/
720 bool
on_configure_event(GdkEventConfigure * const event)721 Table::on_configure_event(GdkEventConfigure* const event)
722 {
723   if (   (this->get_width() != this->surface_->get_width())
724       || (this->get_height() != this->surface_->get_height()) ) {
725     this->surface_ = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
726                                                  this->get_width(),
727                                                  this->get_height());
728     // update the layout
729     if (this->width() / 3 < this->height() / 2)
730       this->layout_ = Layout::standard;
731     else
732       this->layout_ = Layout::widescreen;
733 
734     this->force_redraw_all();
735   }
736   //this->update_from_buffer() wird von on_draw() aufgerufen
737 
738   return true;
739 } // bool Table::on_configure_event(GdkEventConfigure* event)
740 
741 /** event: mouse motion
742  **
743  ** @param    event   the event
744  **
745  ** @return   whether there was a reaction
746  **/
747 bool
on_motion_notify_event(GdkEventMotion * const event)748 Table::on_motion_notify_event(GdkEventMotion* const event)
749 {
750   this->mouse_cursor_update();
751 
752   return false;
753 } // bool Table::on_motion_notify_event(GdkEventMotion* event)
754 
755 /** -> result
756  **
757  ** @param    type   the cursor type
758  **
759  ** @return   the cursor for 'type'
760  **/
761 Glib::RefPtr<Gdk::Cursor>
cursor(CursorType const type)762 Table::cursor(CursorType const type)
763 {
764   switch (type) {
765   case CursorType::none:
766     DEBUG_ASSERTION(false,
767                     "Table::cursor(type):\n"
768                     "  'type' == NONE");
769     break;
770   case CursorType::standard: {
771     static auto const cursor = Gdk::Cursor::create(this->get_display(), "default");
772     return cursor;
773   }
774     break;
775   case CursorType::busy: {
776 #ifdef POSTPONED
777     Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_file("ui/gtkmm/cursor/busy.png");
778     {
779       GdkCursor* cursor = gdk_cursor_new_from_pixbuf(Gdk::Cursor(Gdk::X_CURSOR).get_display()->gobj(), pixbuf->gobj(), 0, 0);
780       //Gdk::Cursor(Gdk::Pixbuf::create_from_file("ui/gtkmm/cursor/busy.png"),);
781       return cursor;
782     }
783 #endif
784   }
785     {
786       static auto const cursor = Gdk::Cursor::create(Gdk::WATCH);
787       return cursor;
788     }
789     //return Gdk::Cursor(Gdk::EXCHANGE);
790     //return Gdk::Cursor(Gdk::CLOCK);
791   case CursorType::poverty_shift: {
792     static auto const cursor_left = Gdk::Cursor::create(Gdk::SB_LEFT_ARROW);
793     static auto const cursor_right = Gdk::Cursor::create(Gdk::SB_RIGHT_ARROW);
794     static auto const cursor_up = Gdk::Cursor::create(Gdk::SB_UP_ARROW);
795     static auto const cursor_down = Gdk::Cursor::create(Gdk::SB_DOWN_ARROW);
796     switch (this->position(this->ui->game().players().current_player())) {
797     case Position::south:
798       return cursor_left;
799     case Position::north:
800       return cursor_right;
801     case Position::west:
802       return cursor_up;
803     case Position::east:
804       return cursor_down;
805     default:
806       return cursor_left;
807     }
808   }
809   case CursorType::poverty_shift_back: {
810     static auto const cursor_left = Gdk::Cursor::create(Gdk::SB_LEFT_ARROW);
811     static auto const cursor_right = Gdk::Cursor::create(Gdk::SB_RIGHT_ARROW);
812     static auto const cursor_up = Gdk::Cursor::create(Gdk::SB_UP_ARROW);
813     static auto const cursor_down = Gdk::Cursor::create(Gdk::SB_DOWN_ARROW);
814     switch (this->position(this->ui->game().players().current_player())) {
815     case Position::south:
816       return cursor_right;
817     case Position::north:
818       return cursor_left;
819     case Position::west:
820       return cursor_down;
821     case Position::east:
822       return cursor_up;
823     default:
824       return cursor_right;
825     }
826   }
827   case CursorType::poverty_get_card:
828   case CursorType::poverty_put_card:
829   case CursorType::poverty_fill_up: {
830     static auto const cursor = Gdk::Cursor::create(Gdk::HAND1);
831     return cursor;
832   }
833   case CursorType::poverty_accept:
834   case CursorType::poverty_getting_back: {
835     static auto const cursor = Gdk::Cursor::create(Gdk::HAND2);
836     return cursor;
837   }
838   case CursorType::next_announcement: {
839     static auto const cursor = Gdk::Cursor::create(Gdk::HAND2);
840     return cursor;
841   }
842   case CursorType::play_card: {
843     static auto const cursor = Gdk::Cursor::create(Gdk::HAND1);
844     return cursor;
845   }
846   case CursorType::card_valid: {
847     static auto const cursor = Gdk::Cursor::create(Gdk::HAND1);
848     return cursor;
849   }
850   case CursorType::card_invalid: {
851     static auto const cursor = Gdk::Cursor::create(Gdk::PIRATE);
852     return cursor;
853   }
854   case CursorType::close_full_trick: {
855     static auto const cursor = Gdk::Cursor::create(Gdk::HAND1);
856     return cursor;
857   }
858   case CursorType::show_last_trick: {
859     static auto const cursor = Gdk::Cursor::create(Gdk::HAND2);
860     return cursor;
861   }
862   case CursorType::game_review: {
863     static auto const cursor = Gdk::Cursor::create(Gdk::SB_H_DOUBLE_ARROW);
864     return cursor;
865   }
866   } // switch (type)
867 
868   return {};
869 } // static Gdk::Cursor const& Table::cursor(CursorType type)
870 
871 /** the progress has changed -- redraw the name of the active player
872  **
873  ** @param    progress   new progress
874  **/
875 void
progress_changed(double const progress)876 Table::progress_changed(double const progress)
877 {
878   this->draw_all();
879 } // void Table::progress_changed(double progress)
880 
881 /** the progress has finished -- redraw the name of the active player
882  **/
883 void
progress_finished()884 Table::progress_finished()
885 {
886   this->draw_all();
887 } // void Table::progress_finished()
888 
889 /** draw a pixbuf
890  **
891  ** @param    cr       drawing context
892  ** @param    pixbuf   pixbuf to draw
893  ** @param    x        x position
894  ** @param    y        y position
895  **/
896 void
draw_pixbuf(Cairo::RefPtr<::Cairo::Context> cr,Glib::RefPtr<Gdk::Pixbuf> pixbuf,int const x,int const y)897 draw_pixbuf(Cairo::RefPtr<::Cairo::Context> cr,
898             Glib::RefPtr<Gdk::Pixbuf> pixbuf,
899             int const x, int const y)
900 {
901   if (!pixbuf)
902     return ;
903   Gdk::Cairo::set_source_pixbuf(cr, pixbuf, x, y);
904   cr->paint();
905 } // void draw_pixbuf(Cairo::RefPtr<::Cairo::Context> cr, Glib::RefPtr<Gdk::Pixbuf> pixbuf, int x, int y)
906 
907 /** draw a pixbuf
908  **
909  ** @param    cr       drawing context
910  ** @param    pixbuf   pixbuf to draw
911  ** @param    pos      position
912  **/
913 void
draw_pixbuf(Cairo::RefPtr<::Cairo::Context> cr,Glib::RefPtr<Gdk::Pixbuf> pixbuf,Gdk::Point const & pos)914 draw_pixbuf(Cairo::RefPtr<::Cairo::Context> cr,
915             Glib::RefPtr<Gdk::Pixbuf> pixbuf,
916             Gdk::Point const& pos)
917 {
918   if (!pixbuf)
919     return ;
920   Gdk::Cairo::set_source_pixbuf(cr, pixbuf, pos.get_x(), pos.get_y());
921   cr->paint();
922 } // void draw_pixbuf(Cairo::RefPtr<::Cairo::Context> cr, Glib::RefPtr<Gdk::Pixbuf> pixbuf, Gdk::Point pos)
923 
924 } // namespace UI_GTKMM_NS
925 
926 #endif // #ifdef USE_UI_GTKMM
927