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