static void create_hand_widgets (window_board_t *win) { static const char *alignment_a[] = {"alignment_w", "alignment_n", "alignment_e", "alignment_s"}; static int dir[] = { 1, 2, 3, 4 }; int h; GtkSizeGroup *sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_BOTH); for (h = 0; h < 4; h++) { GtkWidget *alignment = get_widget (alignment_a[h]); GtkWidget *hand = hand_display_new(HAND_DISPLAY_MODE_HAND); gtk_container_add(GTK_CONTAINER(alignment), hand); gtk_widget_show(hand); g_signal_connect (hand, "card-clicked", G_CALLBACK (card_clicked), dir + h); g_signal_connect (hand, "card-enter", G_CALLBACK (card_enter), dir + h); g_signal_connect (hand, "card-leave", G_CALLBACK (card_leave), dir + h); g_signal_connect (hand, "card-drag-drop", G_CALLBACK (card_drag_drop), dir + h); win->handdisp[h] = HAND_DISPLAY(hand); hand_display_set_drag (HAND_DISPLAY (hand), 1); gtk_size_group_add_widget (sizegroup, hand); } g_object_unref (sizegroup); /* trick display */ GtkWidget *grid = get_widget ("table1"); GtkWidget *table = hand_display_new (HAND_DISPLAY_MODE_TABLE); gtk_table_attach_defaults (GTK_TABLE (grid), table, 1, 2, 1, 2); gtk_widget_show (table); win->table = HAND_DISPLAY (table); }
static void hand_display_size_request (GtkWidget *hand, GtkRequisition *requisition) { HandDisplay *handdisp = HAND_DISPLAY(hand); requisition->width = handdisp->want_width; requisition->height = handdisp->mode != HAND_DISPLAY_MODE_CARD ? 90 : 10; }
static gboolean hand_display_button_press (GtkWidget *hand, GdkEventButton *event) { HandDisplay *handdisp = HAND_DISPLAY(hand); int card = which_card(handdisp, event->x, event->y); if (event->type != GDK_BUTTON_RELEASE) /* don't trigger after dnd */ handdisp->cur_focus = card; if (event->type == GDK_BUTTON_PRESS) { handdisp->cur_click = card; if (handdisp->drag) { handdisp->drag_x = event->x; handdisp->drag_y = event->y; } } if (handdisp->cur_focus == -1) return FALSE; redraw_card (hand, card); if (event->type == GDK_BUTTON_RELEASE) { if (handdisp->cur_click == card) g_signal_emit_by_name (handdisp, "card-clicked", card); handdisp->cur_click = -1; } return FALSE; }
static gboolean hand_display_motion (GtkWidget *hand, GdkEventMotion *event) { HandDisplay *handdisp = HAND_DISPLAY(hand); int card = which_card(handdisp, event->x, event->y); if (handdisp->drag && handdisp->cur_click >= 0 && handdisp->cur_click < 52 && gtk_drag_check_threshold (hand, handdisp->drag_x, handdisp->drag_y, event->x, event->y)) { if (! target_list) target_list = gtk_target_list_new (target_entry, 1); handdisp->cur_drag = handdisp->cur_click; gtk_drag_begin (hand, target_list, GDK_ACTION_COPY, 1, (GdkEvent *) event); } if (handdisp->cur_focus != card) { if (handdisp->cur_focus != -1) { redraw_card (hand, handdisp->cur_focus); g_signal_emit_by_name (handdisp, "card-leave", handdisp->cur_focus); } handdisp->cur_focus = card; if (card != -1) { redraw_card (hand, card); g_signal_emit_by_name (handdisp, "card-enter", card); } } gdk_window_get_pointer(gtk_widget_get_window (hand), NULL, NULL, NULL); /* request more pointer hints */ return FALSE; }
static void hand_display_drag_leave (GtkWidget *hand, GdkDragContext *dc, gpointer data) { HandDisplay *handdisp = HAND_DISPLAY(hand); if (handdisp->cur_focus != -1) { redraw_card (hand, handdisp->cur_focus); g_signal_emit_by_name (handdisp, "card-drag-leave", handdisp->cur_focus); handdisp->cur_focus = -1; } }
static void hand_display_drag_data_get (GtkWidget *hand, GdkDragContext *dc, GtkSelectionData *selection_data, guint targettype, guint t, gpointer data) { HandDisplay *handdisp = HAND_DISPLAY (hand); if (handdisp->cur_drag < 0) return; assert (targettype == 0); gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 32, (guchar *) &(handdisp->cur_drag), sizeof (int)); }
static gboolean hand_display_leave (GtkWidget *hand, GdkEventCrossing *event) { HandDisplay *handdisp = HAND_DISPLAY(hand); if (handdisp->cur_focus != -1) { redraw_card (hand, handdisp->cur_focus); g_signal_emit_by_name (handdisp, "card-leave", handdisp->cur_focus); handdisp->cur_focus = -1; } return FALSE; }
static void redraw_card (GtkWidget *hand, int card) { HandDisplay *handdisp = HAND_DISPLAY(hand); GdkRectangle rect; rect.x = handdisp->l[card] - 2; rect.y = handdisp->t[card] - 7; rect.width = handdisp->r[card] - handdisp->l[card] + 4; rect.height = handdisp->b[card] - handdisp->t[card] + 8; gdk_window_invalidate_rect (gtk_widget_get_window (hand), &rect, FALSE); }
static void hand_display_drag_end (GtkWidget *hand, GdkDragContext *dc, gpointer data) { HandDisplay *handdisp = HAND_DISPLAY (hand); if (handdisp->cur_drag >= 0) { handdisp->cards[handdisp->cur_drag] &= ~HAND_DISPLAY_INVISIBLE_CARD; redraw_card (hand, handdisp->cur_drag); } handdisp->cur_drag = -1; gtk_widget_destroy (drag_win); drag_win = NULL; }
static void hand_display_drag_begin (GtkWidget *hand, GdkDragContext *dc, gpointer data /* unused */) { HandDisplay *handdisp = HAND_DISPLAY (hand); assert (handdisp->cur_drag >= 0 && handdisp->cur_drag < 52); handdisp->cards[handdisp->cur_drag] |= HAND_DISPLAY_INVISIBLE_CARD; handdisp->cur_click = -1; redraw_card (hand, handdisp->cur_drag); /* create drag widget */ if (drag_win) { /* should be NULL, but happens sometimes */ gtk_widget_destroy (drag_win); drag_win = NULL; } drag_win = gtk_window_new (GTK_WINDOW_POPUP); GtkWidget *card = hand_display_new (HAND_DISPLAY_MODE_CARD); hand_display_set_style (HAND_DISPLAY (card), handdisp->style); hand_display_card_set_card (HAND_DISPLAY (card), handdisp->cur_drag); gtk_container_add (GTK_CONTAINER (drag_win), card); gtk_drag_set_icon_widget (dc, drag_win, 0, 0); gtk_widget_show_all (drag_win); }
static void hand_display_drag_data_received (GtkWidget *hand, GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint targettype, guint t, gpointer data) { HandDisplay *handdisp = HAND_DISPLAY (hand); int *card = (int *) gtk_selection_data_get_data (selection_data); int on_card = which_card(handdisp, x, y); /* if (*card == on_card) { gtk_drag_finish (dc, FALSE, FALSE, t); return; } */ g_signal_emit_by_name (handdisp, "card-drag-drop", *card, on_card); gtk_drag_finish (dc, TRUE, FALSE, t); }
static gboolean hand_display_drag_motion (GtkWidget *hand, GdkDragContext *dc, gint x, gint y, guint t, gpointer data) { HandDisplay *handdisp = HAND_DISPLAY(hand); int card = which_card(handdisp, x, y); if (handdisp->cur_focus != card) { if (handdisp->cur_focus != -1) { redraw_card (hand, handdisp->cur_focus); g_signal_emit_by_name (handdisp, "card-drag-leave", handdisp->cur_focus); } handdisp->cur_focus = card; if (card != -1) { redraw_card (hand, card); g_signal_emit_by_name (handdisp, "card-drag-enter", card); } } return FALSE; }
static void draw (GtkWidget *hand, cairo_t *cr) { int x, y; HandDisplay *handdisp = HAND_DISPLAY(hand); cairo_text_extents_t extents; cairo_font_extents_t fextents; GtkAllocation allocation; gtk_widget_get_allocation (hand, &allocation); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); /* "card" mode for drap&drop icon */ if (handdisp->mode == HAND_DISPLAY_MODE_CARD) { if (handdisp->style == HAND_DISPLAY_STYLE_CARDS) { if (card_width != handdisp->want_width) { /* adjust window */ handdisp->want_width = card_width; gdk_window_resize (gtk_widget_get_parent_window (hand), card_width, card_height); /* shaped drag icon * credits to Mirco "MacSlow" Mueller <*****@*****.**> * http://macslow.thepimp.net/?p=26 */ GdkBitmap *pShapeBitmap = (GdkBitmap*) gdk_pixmap_new (NULL, card_width, card_height, 1); assert (pShapeBitmap); cairo_t *pCairoContext = gdk_cairo_create (pShapeBitmap); assert (cairo_status (pCairoContext) == CAIRO_STATUS_SUCCESS); render_card (pCairoContext, 0, 0, handdisp->table_card[0], HAND_DISPLAY_CARD); cairo_destroy (pCairoContext); gdk_window_shape_combine_mask (gtk_widget_get_parent_window (hand), pShapeBitmap, 0, 0); g_object_unref ((gpointer) pShapeBitmap); } render_card (cr, 0, 0, handdisp->table_card[0], HAND_DISPLAY_CARD); return; } char cs[6]; int c = handdisp->table_card[0]; int suit = c / 13; int rank = c % 13; snprintf (cs, 6, "%s%s", suit_str[suit], rank_str[rank]); cairo_set_font_size (cr, 20); cairo_text_extents (cr, cs, &extents); #define XPAD 4 #define YPAD 2 int w = extents.width + 2 * XPAD + 5; int h = extents.height + 2 * YPAD; cairo_set_source_rgb (cr, HAND_DISPLAY_FOCUS_BG); cairo_rectangle (cr, 1, 1, w, h); cairo_fill (cr); cairo_set_source_rgb (cr, HAND_DISPLAY_FONT); cairo_rectangle (cr, 1, 1, w, h); cairo_stroke (cr); cairo_move_to (cr, XPAD - extents.x_bearing + 1, YPAD - extents.y_bearing + 2); cairo_set_source_rgb (cr, suit_color[suit][0], suit_color[suit][1], suit_color[suit][2]); cairo_show_text (cr, suit_str[suit]); cairo_set_source_rgb (cr, HAND_DISPLAY_FONT); cairo_show_text (cr, rank_str[rank]); if (w + 2 != handdisp->want_width) { /* adjust window */ handdisp->want_width = w + 2; gdk_window_resize (gtk_widget_get_parent_window (hand), w + 2, h + 2); } return; } cairo_set_source_rgb (cr, HAND_DISPLAY_TABLE_BG); cairo_rectangle (cr, 0, 0, allocation.width, allocation.height); cairo_fill (cr); /* "table" mode for displaying the already played cards in the middle of the screen */ if (handdisp->mode == HAND_DISPLAY_MODE_TABLE && handdisp->style == HAND_DISPLAY_STYLE_CARDS) { int i; for (i = 0; i < 4; i++) { switch (handdisp->table_seat[i]) { case 1: x = allocation.width / 2 - card_width + 5; /*W*/ y = (allocation.height - card_height) / 2 + 5; break; case 2: x = (allocation.width - card_width) / 2 - 2; /*N*/ y = MAX (allocation.height / 2 - card_height + 10, 0); break; case 3: x = allocation.width / 2 - 5; /*E*/ y = (allocation.height - card_height) / 2 - 5; break; case 4: x = (allocation.width - card_width) / 2 + 2; /*S*/ y = MIN (allocation.height / 2 - 10, allocation.height - card_height); break; default: return; /* stop here */ } render_card (cr, x, y, handdisp->table_card[i], HAND_DISPLAY_CARD); } return; } if (handdisp->mode == HAND_DISPLAY_MODE_TABLE) { /* text */ cairo_set_font_size (cr, 20); int i; for (i = 0; i < 4; i++) { char cs[6]; if (!handdisp->table_seat[i]) return; int c = handdisp->table_card[i]; int suit = c / 13; int rank = c % 13; snprintf (cs, 6, "%s%s", suit_str[suit], rank_str[rank]); cairo_text_extents (cr, cs, &extents); #define XOFF 38 #define YOFF 23 #define WIDTH 52 #define HEIGHT 28 #define RAD 5 switch (handdisp->table_seat[i]) { /* middle point */ case 1: x = allocation.width / 2 - XOFF; /*W*/ y = allocation.height / 2; break; case 2: x = allocation.width / 2; /*N*/ y = allocation.height / 2 - YOFF; break; case 3: x = allocation.width / 2 + XOFF; /*E*/ y = allocation.height / 2; break; case 4: x = allocation.width / 2; /*S*/ y = allocation.height / 2 + YOFF; break; default: return; /* stop here */ } cairo_new_path (cr); cairo_arc_negative (cr, x-WIDTH/2+RAD, y+HEIGHT/2 - RAD, RAD, M_PI, M_PI_2); cairo_arc_negative (cr, x+WIDTH/2-RAD, y+HEIGHT/2 - RAD, RAD, M_PI_2, 0.0); cairo_arc_negative (cr, x+WIDTH/2-RAD, y-HEIGHT/2 + RAD, RAD, 0.0, -M_PI_2); cairo_arc_negative (cr, x-WIDTH/2+RAD, y-HEIGHT/2 + RAD, RAD, -M_PI_2, M_PI); cairo_close_path (cr); cairo_set_source_rgb (cr, HAND_DISPLAY_FOCUS_BG); cairo_fill_preserve (cr); cairo_set_source_rgb (cr, HAND_DISPLAY_FONT); cairo_stroke (cr); cairo_move_to (cr, x - extents.width / 2 - 3, y + extents.height / 2 - 3); cairo_set_source_rgb (cr, suit_color[suit][0], suit_color[suit][1], suit_color[suit][2]); cairo_show_text (cr, suit_str[suit]); cairo_set_source_rgb (cr, HAND_DISPLAY_FONT); cairo_show_text (cr, rank_str[rank]); } return; } /* normal hands display */ /* compute cached best card score for this hand */ if (handdisp->best_card_score == HAND_DISPLAY_NO_SCORE) { int c; handdisp->best_card_score = handdisp->card_score_neg ? 14 : -14; for (c = 0; c < 52; c++) { if (handdisp->card_score_neg ? (handdisp->card_score[c] < handdisp->best_card_score) : (handdisp->card_score[c] > handdisp->best_card_score)) handdisp->best_card_score = handdisp->card_score[c]; } } /* "cards" style */ if (handdisp->style == HAND_DISPLAY_STYLE_CARDS && handdisp->mode != HAND_DISPLAY_MODE_HAND_X) { /* we do not support MODE_X here, yet we still allow setting * STYLE_CARDS there to have the right drag icon */ y = MAX (allocation.height - card_height - 5, 15); int n = 0; int suit; for (suit = 0; suit < 4; suit++) { int c; for (c = 13 * (handdisp->suits[suit] + 1) - 1; c >= 13 * handdisp->suits[suit]; c--) { if (handdisp->cards[c]) { x = floor (n++ * (allocation.width - card_width) / 12.0); int sc = handdisp->card_score[c]; int yy = c == handdisp->cur_focus ? y - 15 : (sc != HAND_DISPLAY_NO_SCORE && handdisp->best_card_score == sc ? y - 5 : y); yy = MAX (yy, 0); handdisp->l[c] = x; handdisp->r[c] = x + card_width; handdisp->t[c] = y - 15; handdisp->b[c] = y + card_height; if (handdisp->cards[c] & HAND_DISPLAY_INVISIBLE_CARD) continue; render_card (cr, x, yy, c, handdisp->cards[c]); if (handdisp->cards[c] == HAND_DISPLAY_HILIGHT_CARD) { /* draw triangular arrow */ cairo_set_source_rgb (cr, HAND_DISPLAY_HILIGHT_FONT); cairo_move_to (cr, x + 3 + (allocation.width - card_width - 10) / 24.0, yy); cairo_rel_line_to (cr, -5, -5); cairo_rel_line_to (cr, 10, 0); cairo_fill (cr); } /* show card score */ if (handdisp->card_score[c] == HAND_DISPLAY_NO_SCORE) continue; char *buf = overtricks (handdisp->card_score[c]); static int text_h = 0; if (!text_h) { cairo_font_extents (cr, &fextents); text_h = fextents.ascent; } cairo_text_extents (cr, buf, &extents); cairo_move_to (cr, x + 1 + text_h, yy + 30 + extents.width); cairo_set_source_rgb (cr, HAND_DISPLAY_DD_FONT); cairo_save (cr); cairo_rotate (cr, -G_PI_2); cairo_show_text (cr, buf); cairo_restore (cr); } } } return; } /* "text" style */ /* draw suit symbols */ cairo_set_font_size (cr, 20); int suit_width = 0; int suit; for (suit = 0; suit < 4; suit++) { x = 0; y = floor ((double) allocation.height * (3.8 - suit) / 4.0); cairo_move_to (cr, x, y); cairo_text_extents (cr, suit_str[suit], &extents); if (extents.x_advance > suit_width) suit_width = extents.x_advance; cairo_set_source_rgb (cr, suit_color[suit][0], suit_color[suit][1], suit_color[suit][2]); cairo_show_text (cr, suit_str[suit]); } /* draw cards */ for (suit = 0; suit < 4; suit++) { x = 4 + suit_width; y = floor ((double) allocation.height * (3.8 - suit) / 4.0); cairo_move_to (cr, x, y); int c; for (c = 13 * suit + 12; c >= 13 * suit; c--) { if (handdisp->cards[c]) { cairo_text_extents (cr, rank_str[c % 13], &extents); handdisp->l[c] = x + extents.x_bearing; handdisp->r[c] = x + extents.x_bearing + extents.width; handdisp->t[c] = y + extents.y_bearing; handdisp->b[c] = y + extents.y_bearing + extents.height; if (!(handdisp->cards[c] & HAND_DISPLAY_INVISIBLE_CARD)) { if (c == handdisp->cur_focus) { cairo_set_source_rgb (cr, HAND_DISPLAY_FOCUS_BG); cairo_rectangle (cr, handdisp->l[c] - 1, handdisp->t[c] - 1, extents.width + 2, extents.height + 2); cairo_fill (cr); } if (handdisp->card_score[c] == HAND_DISPLAY_NO_SCORE) { if (handdisp->cards[c] == HAND_DISPLAY_CARD) cairo_set_source_rgb (cr, HAND_DISPLAY_FONT); else if (handdisp->cards[c] == HAND_DISPLAY_GREY_CARD) cairo_set_source_rgb (cr, HAND_DISPLAY_GREY_FONT); else if (handdisp->cards[c] == HAND_DISPLAY_OLD_CARD) cairo_set_source_rgb (cr, HAND_DISPLAY_OLD_FONT); else if (handdisp->cards[c] == HAND_DISPLAY_HILIGHT_CARD) cairo_set_source_rgb (cr, HAND_DISPLAY_HILIGHT_FONT); } else { /* invert colors if the score is for the opps */ if (handdisp->card_score_neg ^ (handdisp->card_score[c] >= 0)) if (handdisp->best_card_score == handdisp->card_score[c]) cairo_set_source_rgb (cr, HAND_DISPLAY_BEST_POS_FONT); else cairo_set_source_rgb (cr, HAND_DISPLAY_POS_FONT); else if (handdisp->best_card_score == handdisp->card_score[c]) cairo_set_source_rgb (cr, HAND_DISPLAY_BEST_NEG_FONT); else cairo_set_source_rgb (cr, HAND_DISPLAY_NEG_FONT); } cairo_move_to (cr, x, y); cairo_show_text (cr, rank_str[c % 13]); } x += extents.x_advance; y += extents.y_advance; } else { handdisp->l[c] = handdisp->r[c] = handdisp->t[c] = handdisp->b[c] = 0; } } /* MODE_X */ if (handdisp->mode == HAND_DISPLAY_MODE_HAND_X) { c = 52 + suit; x += 3; cairo_text_extents (cr, "x", &extents); handdisp->l[c] = x + extents.x_bearing; handdisp->r[c] = x + extents.x_bearing + extents.width; handdisp->t[c] = y + extents.y_bearing; handdisp->b[c] = y + extents.y_bearing + extents.height; if (c == handdisp->cur_focus) { cairo_set_source_rgb (cr, HAND_DISPLAY_FOCUS_BG); cairo_rectangle (cr, handdisp->l[c] - 1, handdisp->t[c] - 1, extents.width + 2, extents.height + 2); cairo_fill (cr); } cairo_set_source_rgb (cr, HAND_DISPLAY_FONT); cairo_move_to (cr, x, y); cairo_show_text (cr, "x"); x += extents.x_advance + 4; } if (x > handdisp->want_width) { /* grow window */ handdisp->want_width = x; gtk_widget_queue_resize (hand); } } /* show card scores */ cairo_set_font_size (cr, 10); int c; for (c = 51; c >= 0; c--) { char *buf; if (handdisp->card_score[c] != HAND_DISPLAY_NO_SCORE) { buf = overtricks (handdisp->card_score[c]); cairo_text_extents (cr, buf, &extents); cairo_move_to (cr, handdisp->r[c] - extents.x_advance, handdisp->b[c]); cairo_set_source_rgb (cr, HAND_DISPLAY_DD_FONT); cairo_show_text (cr, buf); } } }