00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051 #include "sys.h"
00052 #if defined(__cplusplus) && defined(CWDEBUG)
00053 #include "debug.h"
00054 #else
00055 #undef Dout
00056 #undef DoutFatal
00057 #define Dout(channel, output)
00058 #define DoutFatal(channel, output) do { } while(0)
00059 #ifndef _GNU_SOURCE
00060 #define _GNU_SOURCE
00061 #endif
00062 #endif
00063
00064 #include <glib-object.h>
00065 #include <math.h>
00066 #include <stdlib.h>
00067 #include <string.h>
00068
00069 #ifdef __cplusplus
00070 #define CONST(name, expr) expr
00071 #else
00072 #include "CwChessboard-CONST.h"
00073 #define CONST(name, expr) CWCHESSBOARD_CONST_##name
00074 #define CWCHESSBOARD_DEFINE_INLINE 1
00075 #endif
00076
00077 #include "CwChessboard.h"
00078 #include "CwChessboardCodes.h"
00079
00080
00081 #define CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET 0
00082 #define CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER 1
00083 #define CW_CHESSBOARD_EXPOSE_ALWAYS_CLEAR_BACKGROUND 1 // Needed to erase things like menu's.
00084 #define CW_CHESSBOARD_EXPOSE_DEBUG 0
00085
00086 #define CW_CHESSBOARD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), CW_TYPE_CHESSBOARD, CwChessboardPrivate))
00087
00088 G_DEFINE_TYPE(CwChessboard, cw_chessboard, GTK_TYPE_DRAWING_AREA);
00089
00090 static void cw_chessboard_destroy(GtkObject* object);
00091 static void cw_chessboard_finalize(GObject* object);
00092 static void cw_chessboard_realize(GtkWidget* widget);
00093 static void cw_chessboard_unrealize(GtkWidget* widget);
00094 static void cw_chessboard_size_request(GtkWidget* widget, GtkRequisition* requisition);
00095 static void cw_chessboard_size_allocate(GtkWidget* widget, GtkAllocation* allocation);
00096 static gboolean cw_chessboard_expose(GtkWidget* chessboard, GdkEventExpose* event);
00097 static gboolean cw_chessboard_motion_notify(GtkWidget* chessboard, GdkEventMotion* event);
00098 static gboolean cw_chessboard_default_motion_notify(GtkWidget* chessboard, GdkEventMotion* event);
00099
00100 static void recreate_hud_layers(CwChessboard* chessboard);
00101 static void redraw_hud_layer(CwChessboard* chessboard, guint hud);
00102
00103
00104
00105
00106 typedef gint BoardIndex;
00107
00108
00109
00110 gint const squares = 8;
00111
00112
00113 gint const min_sside = 12;
00114
00115
00116 #ifdef __cplusplus
00117 guint const number_of_hud_layers = 2;
00118 #else
00119 #define number_of_hud_layers 2
00120 #endif
00121
00122
00123
00124
00125 inline static BoardIndex convert_colrow2index(gint col, gint row)
00126 {
00127 return col | (row << 3);
00128 }
00129
00130
00131
00132
00133
00134 inline static gint convert_index2column(BoardIndex index)
00135 {
00136
00137 return index & 0x7;
00138 }
00139
00140
00141
00142
00143 inline static gint convert_index2row(BoardIndex index)
00144 {
00145
00146 return index >> 3;
00147 }
00148
00149
00150
00151
00152 inline static void convert_index2xy(CwChessboard* chessboard, BoardIndex index, gint* x, gint* y)
00153 {
00154 cw_chessboard_colrow2xy(chessboard, convert_index2column(index), convert_index2row(index), x, y);
00155 }
00156
00157
00158
00159
00160 inline static BoardIndex convert_xy2index(CwChessboard* chessboard, gdouble x, gdouble y)
00161 {
00162 gint col = cw_chessboard_x2col(chessboard, x);
00163 if ((col & ~0x7))
00164 return -1;
00165 gint row = cw_chessboard_y2row(chessboard, y);
00166 if ((row & ~0x7))
00167 return -1;
00168 return convert_colrow2index(col, row);
00169 }
00170
00171
00172
00173
00174 struct _CairoColor {
00175 double red;
00176 double green;
00177 double blue;
00178 };
00179 typedef struct _CairoColor CairoColor;
00180
00181 static int const pawn = 0;
00182 static int const rook = 1;
00183 static int const knight = 2;
00184 static int const bishop = 3;
00185 static int const queen = 4;
00186 static int const king = 5;
00187
00188
00189 struct _FloatingPiece {
00190 gint pixmap_x;
00191 gint pixmap_y;
00192 CwChessboardCode code;
00193 gboolean moved;
00194 gboolean pointer_device;
00195 };
00196 typedef struct _FloatingPiece FloatingPiece;
00197
00198
00199 struct _SquareCache {
00200 cairo_surface_t* surface;
00201 };
00202 typedef struct _SquareCache SquareCache;
00203
00204
00205 struct _Arrow {
00206 gint begin_col;
00207 gint begin_row;
00208 gint end_col;
00209 gint end_row;
00210 CairoColor color;
00211 guint64 has_content[number_of_hud_layers];
00212 };
00213 typedef struct _Arrow Arrow;
00214
00215
00216
00217
00218 struct _CwChessboardPrivate
00219 {
00220
00221 CairoColor dark_square_color;
00222 guint32 dark_square_color_pixel;
00223 CairoColor light_square_color;
00224 guint32 light_square_color_pixel;
00225 CairoColor board_border_color;
00226 guint32 board_border_color_pixel;
00227 CairoColor white_piece_fill_color;
00228 guint32 white_piece_fill_color_pixel;
00229 CairoColor white_piece_line_color;
00230 guint32 white_piece_line_color_pixel;
00231 CairoColor black_piece_fill_color;
00232 guint32 black_piece_fill_color_pixel;
00233 CairoColor black_piece_line_color;
00234 guint32 black_piece_line_color_pixel;
00235 GdkColor widget_background_color;
00236 CairoColor color_palet[31];
00237 guint32 allocated_colors_mask;
00238 CairoColor cursor_color;
00239 guint32 cursor_color_pixel;
00240
00241
00242 gboolean draw_border;
00243 gboolean flip_board;
00244 gboolean draw_turn_indicators;
00245 gboolean active_turn_indicator;
00246 gboolean has_hud_layer[number_of_hud_layers];
00247 gdouble marker_thickness;
00248 gboolean marker_below;
00249 gdouble cursor_thickness;
00250 gboolean show_cursor;
00251
00252
00253 gint sside;
00254 gint border_width;
00255 gint top_left_pixmap_x;
00256 gint top_left_pixmap_y;
00257 gint bottom_right_pixmap_x;
00258 gint bottom_right_pixmap_y;
00259 gint pixmap_top_left_a8_x;
00260 gint pixmap_top_left_a8_y;
00261 GdkRegion* chessboard_region;
00262 gint marker_thickness_px;
00263 gint cursor_thickness_px;
00264 gint cursor_col;
00265 gint cursor_row;
00266
00267
00268 GdkPixmap* pixmap;
00269 cairo_t* cr;
00270 SquareCache piece_pixmap[12];
00271 cairo_surface_t* hud_layer_surface[number_of_hud_layers];
00272 gint64 hud_has_content[number_of_hud_layers];
00273 gint64 hud_need_redraw[number_of_hud_layers];
00274 SquareCache hatching_pixmap;
00275
00276 CwChessboardCode board_codes[64];
00277 gint64 need_redraw_invalidated;
00278 gint64 need_redraw;
00279
00280 gsize number_of_floating_pieces;
00281 FloatingPiece floating_piece[32];
00282 gint floating_piece_handle;
00283 gboolean redraw_background;
00284
00285 GPtrArray* arrows;
00286 };
00287
00288 static CwChessboardCode const color_mask = 0x0001;
00289 static CwChessboardCode const piece_mask = 0x000e;
00290 static CwChessboardCode const piece_color_mask = 0x000f;
00291 static CwChessboardCode const bghandle_mask = 0x01f0;
00292 static CwChessboardCode const mahandle_mask = 0x3e00;
00293
00294 static inline gboolean is_empty_square(CwChessboardCode code)
00295 {
00296 return (code & piece_mask) == 0;
00297 }
00298
00299
00300 static inline int convert_code2piece(CwChessboardCode code)
00301 {
00302 return ((code & piece_mask) >> 1) - 1;
00303 }
00304
00305
00306 static inline int convert_code2piece_pixmap_index(CwChessboardCode code)
00307 {
00308 return (code & piece_color_mask) - 2;
00309 }
00310
00311
00312 static inline gboolean is_white_piece(CwChessboardCode code)
00313 {
00314 return (code & color_mask) != 0;
00315 }
00316
00317 static inline CwChessboardCode convert_piece2code(int piece, gboolean white)
00318 {
00319 return (unsigned char)((piece << 1) | (white ? 3 : 2));
00320 }
00321
00322 static inline gint convert_code2bghandle(CwChessboardCode code)
00323 {
00324 return (code & bghandle_mask) >> 4;
00325 }
00326
00327
00328 static inline CwChessboardCode convert_bghandle2code(gint bghandle)
00329 {
00330 return bghandle << 4;
00331 }
00332
00333 static inline gint convert_code2mahandle(CwChessboardCode code)
00334 {
00335 return (code & mahandle_mask) >> 9;
00336 }
00337
00338
00339 static inline CwChessboardCode convert_mahandle2code(gint mahandle)
00340 {
00341 return mahandle << 9;
00342 }
00343
00344 static inline gboolean is_inside_board(gint col, gint row)
00345 {
00346 return !((col | row) & ~0x7);
00347 }
00348
00349
00350 static void cw_chessboard_class_init(CwChessboardClass* chessboard_class)
00351 {
00352 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(chessboard_class);
00353 GtkObjectClass* object_class = GTK_OBJECT_CLASS(widget_class);
00354 GObjectClass* gobject_class = G_OBJECT_CLASS(object_class);
00355
00356 g_type_class_add_private(object_class, sizeof(CwChessboardPrivate));
00357
00358 gobject_class->finalize = cw_chessboard_finalize;
00359
00360 object_class->destroy = cw_chessboard_destroy;
00361
00362 widget_class->expose_event = cw_chessboard_expose;
00363 widget_class->motion_notify_event = cw_chessboard_default_motion_notify;
00364
00365 widget_class->realize = cw_chessboard_realize;
00366 widget_class->size_request = cw_chessboard_size_request;
00367 widget_class->size_allocate = cw_chessboard_size_allocate;
00368 widget_class->unrealize = cw_chessboard_unrealize;
00369
00370 chessboard_class->calc_board_border_width = cw_chessboard_default_calc_board_border_width;
00371 chessboard_class->draw_border = cw_chessboard_default_draw_border;
00372 chessboard_class->draw_turn_indicator = cw_chessboard_default_draw_turn_indicator;
00373 chessboard_class->draw_piece[pawn] = cw_chessboard_draw_pawn;
00374 chessboard_class->draw_piece[rook] = cw_chessboard_draw_rook;
00375 chessboard_class->draw_piece[knight] = cw_chessboard_draw_knight;
00376 chessboard_class->draw_piece[bishop] = cw_chessboard_draw_bishop;
00377 chessboard_class->draw_piece[queen] = cw_chessboard_draw_queen;
00378 chessboard_class->draw_piece[king] = cw_chessboard_draw_king;
00379 chessboard_class->draw_hud_layer = cw_chessboard_default_draw_hud_layer;
00380 chessboard_class->draw_hud_square = cw_chessboard_default_draw_hud_square;
00381 chessboard_class->cursor_left_chessboard = NULL;
00382 chessboard_class->cursor_entered_square = NULL;
00383 }
00384
00385 static void cw_chessboard_destroy(GtkObject* object)
00386 {
00387 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_destroy(" << object << ")");
00388 GTK_OBJECT_CLASS(cw_chessboard_parent_class)->destroy(object);
00389 }
00390
00391
00392 static void cw_chessboard_init(CwChessboard* chessboard)
00393 {
00394 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_init(" << chessboard << ")");
00395
00396 CwChessboardPrivate* priv = CW_CHESSBOARD_GET_PRIVATE(chessboard);
00397 GdkColormap* colormap = gtk_widget_get_colormap(GTK_WIDGET(chessboard));
00398
00399
00400 chessboard->priv = priv;
00401
00402 priv->draw_border = TRUE;
00403 priv->flip_board = FALSE;
00404 priv->draw_turn_indicators = TRUE;
00405 priv->active_turn_indicator = TRUE;
00406 memset(priv->piece_pixmap, 0, sizeof(priv->piece_pixmap));
00407 memset(priv->has_hud_layer, 0, sizeof(priv->has_hud_layer));
00408 priv->hatching_pixmap.surface = NULL;
00409 priv->sside = -1;
00410 priv->marker_thickness = 0.08;
00411 priv->marker_below = FALSE;
00412 priv->cursor_thickness = 0.04;
00413 priv->show_cursor = FALSE;
00414 priv->cursor_col = -1;
00415 priv->cursor_row = -1;
00416 * (gint*)(&chessboard->sside) = 0;
00417 memset(priv->board_codes, 0, sizeof(priv->board_codes));
00418 priv->number_of_floating_pieces = 0;
00419 priv->floating_piece_handle = -1;
00420 memset(priv->floating_piece, 0, sizeof(priv->floating_piece));
00421 priv->allocated_colors_mask = 0;
00422 priv->pixmap = NULL;
00423 priv->top_left_pixmap_x = 0;
00424 priv->top_left_pixmap_y = 0;
00425 priv->bottom_right_pixmap_x = 0;
00426 priv->bottom_right_pixmap_y = 0;
00427 priv->cr = NULL;
00428 priv->chessboard_region = NULL;
00429 memset(priv->hud_layer_surface, 0, sizeof(priv->hud_layer_surface));
00430 priv->arrows = g_ptr_array_new();
00431
00432 priv->board_codes[0] = white_rook;
00433 priv->board_codes[1] = white_knight;
00434 priv->board_codes[2] = white_bishop;
00435 priv->board_codes[3] = white_queen;
00436 priv->board_codes[4] = white_king;
00437 priv->board_codes[5] = white_bishop;
00438 priv->board_codes[6] = white_knight;
00439 priv->board_codes[7] = white_rook;
00440 priv->board_codes[8] = white_pawn;
00441 priv->board_codes[9] = white_pawn;
00442 priv->board_codes[10] = white_pawn;
00443 priv->board_codes[11] = white_pawn;
00444 priv->board_codes[12] = white_pawn;
00445 priv->board_codes[13] = white_pawn;
00446 priv->board_codes[14] = white_pawn;
00447 priv->board_codes[15] = white_pawn;
00448 priv->board_codes[63] = black_rook;
00449 priv->board_codes[62] = black_knight;
00450 priv->board_codes[61] = black_bishop;
00451 priv->board_codes[60] = black_king;
00452 priv->board_codes[59] = black_queen;
00453 priv->board_codes[58] = black_bishop;
00454 priv->board_codes[57] = black_knight;
00455 priv->board_codes[56] = black_rook;
00456 priv->board_codes[55] = black_pawn;
00457 priv->board_codes[54] = black_pawn;
00458 priv->board_codes[53] = black_pawn;
00459 priv->board_codes[52] = black_pawn;
00460 priv->board_codes[51] = black_pawn;
00461 priv->board_codes[50] = black_pawn;
00462 priv->board_codes[49] = black_pawn;
00463 priv->board_codes[48] = black_pawn;
00464
00465
00466 gushort const dark_square_red = 45875;
00467 gushort const dark_square_green = 58981;
00468 gushort const dark_square_blue = 45875;
00469
00470
00471 gushort const light_square_red = 65535;
00472 gushort const light_square_green = 65535;
00473 gushort const light_square_blue = 58981;
00474
00475
00476
00477
00478
00479 GdkColor dark_square_color;
00480 GdkColor light_square_color;
00481
00482
00483 for (double x = 0.0; x <= 1.0; x += 0.01)
00484 {
00485 dark_square_color.red = (gushort)((1.0 - x) * dark_square_red);
00486 dark_square_color.green = (gushort)((1.0 - x) * dark_square_green);
00487 dark_square_color.blue = (gushort)((1.0 - x) * dark_square_blue);
00488 if (!gdk_colormap_alloc_color(colormap,& dark_square_color, FALSE, TRUE))
00489 DoutFatal(dc::fatal, "gdk_colormap_alloc_color failed to allocate dark_square_color (" <<
00490 dark_square_color.red << ", " <<
00491 dark_square_color.green << ", " <<
00492 dark_square_color.blue << ")");
00493
00494 light_square_color.red = light_square_red + (gushort)(x * (65535 - light_square_red));
00495 light_square_color.green = light_square_green + (gushort)(x * (65535 - light_square_green));
00496 light_square_color.blue = light_square_blue + (gushort)(x * (65535 - light_square_blue));
00497 if (!gdk_colormap_alloc_color(colormap,& light_square_color, FALSE, TRUE))
00498 DoutFatal(dc::fatal, "gdk_colormap_alloc_color failed to allocate light_square_color (" <<
00499 light_square_color.red << ", " <<
00500 light_square_color.green << ", " <<
00501 light_square_color.blue << ")");
00502
00503 if (dark_square_color.red != light_square_color.red ||
00504 dark_square_color.green != light_square_color.green ||
00505 dark_square_color.blue != light_square_color.blue)
00506 break;
00507 }
00508
00509
00510 cw_chessboard_set_dark_square_color(chessboard,& dark_square_color);
00511 cw_chessboard_set_light_square_color(chessboard,& light_square_color);
00512
00513
00514 priv->widget_background_color = light_square_color;
00515
00516
00517
00518 priv->white_piece_fill_color.red = 1.0;
00519 priv->white_piece_fill_color.green = 1.0;
00520 priv->white_piece_fill_color.blue = 1.0;
00521
00522 priv->white_piece_line_color.red = 0.0;
00523 priv->white_piece_line_color.green = 0.0;
00524 priv->white_piece_line_color.blue = 0.0;
00525
00526 priv->black_piece_fill_color.red = 0.0;
00527 priv->black_piece_fill_color.green = 0.0;
00528 priv->black_piece_fill_color.blue = 0.0;
00529
00530 priv->black_piece_line_color.red = 1.0;
00531 priv->black_piece_line_color.green = 1.0;
00532 priv->black_piece_line_color.blue = 1.0;
00533
00534 priv->cursor_color.red = 1.0;
00535 priv->cursor_color.green = 0.0;
00536 priv->cursor_color.blue = 0.0;
00537
00538
00539 gtk_widget_set_double_buffered(GTK_WIDGET(chessboard), FALSE);
00540
00541 gtk_widget_add_events(GTK_WIDGET(chessboard),
00542 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
00543 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
00544
00545
00546 g_signal_connect(G_OBJECT(chessboard), "motion-notify-event", G_CALLBACK(cw_chessboard_motion_notify), NULL);
00547 }
00548
00549 static void cw_chessboard_finalize(GObject* object)
00550 {
00551 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_finalize(" << object << ")");
00552 CwChessboard* chessboard = CW_CHESSBOARD(object);
00553 g_ptr_array_free(chessboard->priv->arrows, TRUE);
00554 G_OBJECT_CLASS(cw_chessboard_parent_class)->finalize(object);
00555 }
00556
00557 static void invalidate_border(CwChessboard* chessboard)
00558 {
00559 if (GTK_WIDGET_REALIZED(chessboard))
00560 {
00561 CwChessboardPrivate* priv = chessboard->priv;
00562 GdkWindow* window = GTK_WIDGET(chessboard)->window;
00563 GdkRectangle pixmap_rect;
00564 pixmap_rect.x = priv->top_left_pixmap_x;
00565 pixmap_rect.y = priv->top_left_pixmap_y;
00566 pixmap_rect.width = priv->bottom_right_pixmap_x - priv->top_left_pixmap_x;
00567 pixmap_rect.height = priv->bottom_right_pixmap_y - priv->top_left_pixmap_y;
00568 GdkRegion* border_region = gdk_region_rectangle(&pixmap_rect);
00569 gdk_region_subtract(border_region, priv->chessboard_region);
00570 gdk_window_invalidate_region(window, border_region, FALSE);
00571 gdk_region_destroy(border_region);
00572 }
00573 }
00574
00575 static void invalidate_turn_indicators(CwChessboard* chessboard)
00576 {
00577 if (GTK_WIDGET_REALIZED(chessboard))
00578 {
00579 CwChessboardPrivate* priv = chessboard->priv;
00580
00581 gint const border_width = priv->border_width;
00582 gint const border_shadow_width = 2;
00583 gint const edge_width = border_width - border_shadow_width - 1;
00584 gint const side = squares * priv->sside;
00585
00586 double const factor = 0.085786;
00587 int const dx = (int)ceil((edge_width + 1) * factor);
00588
00589 GdkRectangle top_indicator_rect, bottom_indicator_rect;
00590 top_indicator_rect.x = priv->top_left_pixmap_x + priv->pixmap_top_left_a8_x + side + 1 - dx;
00591 top_indicator_rect.y = priv->top_left_pixmap_y + priv->pixmap_top_left_a8_y - 1 - edge_width;
00592 top_indicator_rect.width = edge_width + dx;
00593 top_indicator_rect.height = edge_width;
00594 GdkRegion* indicator_region = gdk_region_rectangle(&top_indicator_rect);
00595 bottom_indicator_rect.x = top_indicator_rect.x;
00596 bottom_indicator_rect.y = top_indicator_rect.y + edge_width + side + 2;
00597 bottom_indicator_rect.width = edge_width + dx;
00598 bottom_indicator_rect.height = edge_width;
00599 gdk_region_union_with_rect(indicator_region,& bottom_indicator_rect);
00600 top_indicator_rect.x += dx;
00601 top_indicator_rect.y += edge_width;
00602 top_indicator_rect.width = edge_width;
00603 top_indicator_rect.height = dx;
00604 gdk_region_union_with_rect(indicator_region,& top_indicator_rect);
00605 bottom_indicator_rect.x += dx;
00606 bottom_indicator_rect.y -= dx;
00607 bottom_indicator_rect.width = edge_width;
00608 bottom_indicator_rect.height = dx;
00609 gdk_region_union_with_rect(indicator_region,& bottom_indicator_rect);
00610 gdk_window_invalidate_region(GTK_WIDGET(chessboard)->window, indicator_region, FALSE);
00611 gdk_region_destroy(indicator_region);
00612 }
00613 }
00614
00615 #if 0 // Not currently used anywhere.
00616 static void invalidate_background(CwChessboard* chessboard)
00617 {
00618 if (GTK_WIDGET_REALIZED(chessboard))
00619 {
00620 CwChessboardPrivate* priv = chessboard->priv;
00621 GdkWindow* window = GTK_WIDGET(chessboard)->window;
00622 GdkRegion* background_region = gdk_drawable_get_clip_region(window);
00623 gdk_region_subtract(background_region, priv->chessboard_region);
00624 gdk_window_invalidate_region(window, background_region, FALSE);
00625 gdk_region_destroy(background_region);
00626 priv->redraw_background = TRUE;
00627 }
00628 }
00629 #endif
00630
00631 static void invalidate_square(CwChessboard* chessboard, gint col, gint row)
00632 {
00633 CwChessboardPrivate* priv = chessboard->priv;
00634 guint64 redraw_mask = 1;
00635 redraw_mask <<= convert_colrow2index(col, row);
00636
00637 if (!(priv->need_redraw_invalidated & redraw_mask))
00638 {
00639 if (GTK_WIDGET_REALIZED(chessboard))
00640 {
00641 GdkRectangle rect;
00642 rect.width = priv->sside;
00643 rect.height = priv->sside;
00644 cw_chessboard_colrow2xy(chessboard, col, row,& rect.x,& rect.y);
00645 gdk_window_invalidate_rect(GTK_WIDGET(chessboard)->window,& rect, FALSE);
00646 priv->need_redraw_invalidated |= redraw_mask;
00647 }
00648 else
00649 priv->need_redraw |= redraw_mask;
00650 }
00651 }
00652
00653 static void invalidate_board(CwChessboard* chessboard)
00654 {
00655 CwChessboardPrivate* priv = chessboard->priv;
00656 if (GTK_WIDGET_REALIZED(chessboard))
00657 {
00658 gdk_window_invalidate_region(GTK_WIDGET(chessboard)->window, priv->chessboard_region, FALSE);
00659 priv->need_redraw_invalidated = (guint64)-1;
00660 }
00661 else
00662 priv->need_redraw = (guint64)-1;
00663 }
00664
00665 static void invalidate_markers(CwChessboard* chessboard)
00666 {
00667 CwChessboardPrivate* priv = chessboard->priv;
00668 for (gint row = 0; row < squares; ++row)
00669 for (gint col = 0; col < squares; ++col)
00670 if (convert_code2mahandle(priv->board_codes[convert_colrow2index(col, row)]) != 0)
00671 invalidate_square(chessboard, col, row);
00672 }
00673
00674 static void invalidate_cursor(CwChessboard* chessboard)
00675 {
00676 CwChessboardPrivate* priv = chessboard->priv;
00677 if (priv->show_cursor && is_inside_board(priv->cursor_col, priv->cursor_row))
00678 invalidate_square(chessboard, priv->cursor_col, priv->cursor_row);
00679 }
00680
00681 static void update_cursor_position(CwChessboard* chessboard, gdouble x, gdouble y, gboolean forced)
00682 {
00683 CwChessboardPrivate* priv = chessboard->priv;
00684 gint col = cw_chessboard_x2col(chessboard, x);
00685 gint row = cw_chessboard_y2row(chessboard, y);
00686 gboolean cursor_moved = (col != priv->cursor_col || row != priv->cursor_row);
00687 gboolean was_inside_board = is_inside_board(priv->cursor_col, priv->cursor_row);
00688 gboolean inside_board = is_inside_board(col, row);
00689 if (cursor_moved || forced)
00690 {
00691 if (inside_board)
00692 {
00693 if (CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_entered_square)
00694 {
00695 if (was_inside_board)
00696 CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_entered_square(chessboard, priv->cursor_col, priv->cursor_row, col, row);
00697 else
00698 CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_entered_square(chessboard, -1, -1, col, row);
00699 }
00700 if (priv->show_cursor)
00701 invalidate_square(chessboard, col, row);
00702 }
00703 else if (was_inside_board && CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_left_chessboard)
00704 CW_CHESSBOARD_GET_CLASS(chessboard)->cursor_left_chessboard(chessboard, priv->cursor_col, priv->cursor_row);
00705 if (priv->show_cursor && was_inside_board)
00706 invalidate_square(chessboard, priv->cursor_col, priv->cursor_row);
00707 priv->cursor_col = col;
00708 priv->cursor_row = row;
00709 }
00710 if (priv->show_cursor && !priv->need_redraw_invalidated)
00711 {
00712
00713 Dout(dc::cwchessboardwidget, "Calling gdk_window_get_pointer()");
00714 gdk_window_get_pointer(GTK_WIDGET(chessboard)->window, NULL, NULL, NULL);
00715 }
00716 }
00717
00718 static void redraw_border(CwChessboard* chessboard)
00719 {
00720 CwChessboardPrivate* priv = chessboard->priv;
00721 cairo_t* cr = priv->cr;
00722
00723 gint const pixmap_width = priv->bottom_right_pixmap_x - priv->top_left_pixmap_x;
00724 gint const pixmap_height = priv->bottom_right_pixmap_y - priv->top_left_pixmap_y;
00725 gint const side = squares * priv->sside;
00726
00727
00728 if (priv->pixmap_top_left_a8_x != 0)
00729 cairo_rectangle(cr, 0, 0, priv->pixmap_top_left_a8_x, pixmap_height);
00730 if (pixmap_width - priv->pixmap_top_left_a8_x - side != 0)
00731 cairo_rectangle(cr, priv->pixmap_top_left_a8_x + side, 0,
00732 pixmap_width - priv->pixmap_top_left_a8_x - side, pixmap_height);
00733 if (priv->pixmap_top_left_a8_y != 0)
00734 cairo_rectangle(cr, 0, 0, pixmap_width, priv->pixmap_top_left_a8_y);
00735 if (pixmap_height - priv->pixmap_top_left_a8_y - side != 0)
00736 cairo_rectangle(cr, 0, priv->pixmap_top_left_a8_y + side,
00737 pixmap_width, pixmap_height - priv->pixmap_top_left_a8_y - side);
00738 cairo_set_source_rgb(cr, priv->widget_background_color.red / 65535.0,
00739 priv->widget_background_color.green / 65535.0, priv->widget_background_color.blue / 65535.0);
00740 cairo_fill(cr);
00741
00742
00743 if (priv->border_width)
00744 CW_CHESSBOARD_GET_CLASS(chessboard)->draw_border(chessboard);
00745
00746
00747 invalidate_border(chessboard);
00748 }
00749
00750 void cw_chessboard_default_draw_border(CwChessboard* chessboard)
00751 {
00752 CwChessboardPrivate* priv = chessboard->priv;
00753 cairo_t* cr = priv->cr;
00754
00755 gint const border_width = priv->border_width;
00756 gint const border_shadow_width = 2;
00757 gint const edge_width = border_width - border_shadow_width - 1;
00758 gint const side = squares * priv->sside;
00759
00760 cairo_save(cr);
00761
00762 cairo_translate(cr, priv->pixmap_top_left_a8_x - border_width, priv->pixmap_top_left_a8_y - border_width);
00763
00764 cairo_set_source_rgb(cr, 0, 0, 0);
00765 cairo_set_line_width(cr, 1.0);
00766 cairo_set_source_rgb(cr, priv->board_border_color.red * 0.5, priv->board_border_color.green * 0.5, priv->board_border_color.blue * 0.5);
00767 cairo_move_to(cr, side + border_width + 0.5, border_width - 0.5);
00768 cairo_line_to(cr, border_width - 0.5, border_width - 0.5);
00769 cairo_line_to(cr, border_width - 0.5, side + border_width + 0.5);
00770 cairo_stroke(cr);
00771 cairo_set_source_rgb(cr, (1.0 + priv->board_border_color.red) * 0.5, (1.0 + priv->board_border_color.green) * 0.5, (1.0 + priv->board_border_color.blue) * 0.5);
00772 cairo_move_to(cr, border_width - 0.5, side + border_width + 0.5);
00773 cairo_line_to(cr, side + border_width + 0.5, side + border_width + 0.5);
00774 cairo_line_to(cr, side + border_width + 0.5, border_width - 0.5);
00775 cairo_stroke(cr);
00776
00777 cairo_set_source_rgb(cr, priv->board_border_color.red, priv->board_border_color.green, priv->board_border_color.blue);
00778 cairo_set_line_width(cr, edge_width);
00779 cairo_rectangle(cr, border_shadow_width + edge_width * 0.5, border_shadow_width + edge_width * 0.5, side + edge_width + 2, side + edge_width + 2);
00780 cairo_stroke(cr);
00781
00782 cairo_set_source_rgb(cr, (1.0 + priv->board_border_color.red) * 0.5, (1.0 + priv->board_border_color.green) * 0.5, (1.0 + priv->board_border_color.blue) * 0.5);
00783 cairo_move_to(cr, 0, 0);
00784 cairo_line_to(cr, 0, side + 2 * border_width);
00785 cairo_rel_line_to(cr, border_shadow_width, -border_shadow_width);
00786 cairo_rel_line_to(cr, 0, -(side + 2 + 2 * edge_width));
00787 cairo_rel_line_to(cr, side + 2 + 2 * edge_width, 0);
00788 cairo_rel_line_to(cr, border_shadow_width, -border_shadow_width);
00789 cairo_close_path(cr);
00790 cairo_fill(cr);
00791 cairo_set_source_rgb(cr, priv->board_border_color.red * 0.5, priv->board_border_color.green * 0.5, priv->board_border_color.blue * 0.5);
00792 cairo_move_to(cr, side + 2 * border_width, side + 2 * border_width);
00793 cairo_line_to(cr, side + 2 * border_width, 0);
00794 cairo_rel_line_to(cr, -border_shadow_width, border_shadow_width);
00795 cairo_rel_line_to(cr, 0, side + 2 + 2 * edge_width);
00796 cairo_rel_line_to(cr, -(side + 2 + 2 * edge_width), 0);
00797 cairo_rel_line_to(cr, -border_shadow_width, border_shadow_width);
00798 cairo_close_path(cr);
00799 cairo_fill(cr);
00800
00801 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
00802 PangoLayout* layout = pango_cairo_create_layout(cr);
00803 PangoFontDescription* desc = pango_font_description_from_string("Sans Bold 14");
00804 gint font_pixels = (edge_width > 14) ? MAX(14, edge_width * 0.66) : edge_width;
00805 pango_font_description_set_absolute_size(desc, MAX(font_pixels, 7) * PANGO_SCALE);
00806 pango_layout_set_font_description(layout, desc);
00807 pango_font_description_free(desc);
00808 char c[2] = { 0, 0 };
00809 for (int col = 0; col < 8; ++col)
00810 {
00811 c[0] = 'A' + col;
00812 pango_layout_set_text(layout, c, -1);
00813 int width, height;
00814 pango_layout_get_size(layout,& width,& height);
00815 cairo_move_to(cr, border_width + ((priv->flip_board ? 7 - col : col) + 0.5) * priv->sside - ((double)width / PANGO_SCALE) / 2,
00816 side + 1.5 * border_width - ((double)height / PANGO_SCALE) / 2);
00817 pango_cairo_show_layout(cr, layout);
00818 }
00819 for (int row = 0; row < 8; ++row)
00820 {
00821 c[0] = '1' + row;
00822 pango_layout_set_text(layout, c, -1);
00823 int width, height;
00824 pango_layout_get_size(layout,& width,& height);
00825 cairo_move_to(cr, border_width / 2 - ((double)width / PANGO_SCALE) / 2,
00826 border_width + ((priv->flip_board ? row : 7 - row) + 0.5) * priv->sside - ((double)height / PANGO_SCALE) / 2);
00827 pango_cairo_show_layout(cr, layout);
00828 }
00829 g_object_unref(layout);
00830
00831 cairo_restore(cr);
00832
00833
00834 if (priv->draw_turn_indicators)
00835 CW_CHESSBOARD_GET_CLASS(chessboard)->draw_turn_indicator(chessboard, priv->active_turn_indicator, TRUE);
00836 }
00837
00838 void cw_chessboard_default_draw_turn_indicator(CwChessboard* chessboard, gboolean white, gboolean on)
00839 {
00840 CwChessboardPrivate* priv = chessboard->priv;
00841 cairo_t* cr = priv->cr;
00842
00843 gint const border_width = priv->border_width;
00844 gint const border_shadow_width = 2;
00845 gint const edge_width = border_width - border_shadow_width - 1;
00846 gint const side = squares * priv->sside;
00847
00848 cairo_save(cr);
00849
00850 cairo_translate(cr, priv->pixmap_top_left_a8_x + side + 1,
00851 priv->pixmap_top_left_a8_y - border_width + border_shadow_width + ((white != priv->flip_board) ? side + edge_width + 2 : 0));
00852
00853 double const factor = 0.085786;
00854 if (on)
00855 {
00856 if (white)
00857 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
00858 else
00859 cairo_set_source_rgb(cr, 0, 0, 0);
00860 cairo_arc(cr, edge_width * 0.5 - MAX((edge_width + 1) * factor - 1, 0),
00861 edge_width * 0.5 - (edge_width + 1) * ((white != priv->flip_board) ? factor : -factor), edge_width * 0.5, 0, 2 * M_PI);
00862 cairo_fill(cr);
00863 }
00864 else
00865 {
00866 gboolean top = (white == priv->flip_board);
00867 double dir = top ? 1.0 : -1.0;
00868 cairo_set_source_rgb(cr, priv->board_border_color.red, priv->board_border_color.green, priv->board_border_color.blue);
00869 cairo_move_to(cr, 0, top ? edge_width : 0);
00870 cairo_rel_line_to(cr, 0, dir * ((edge_width + 1) * factor + 1));
00871 cairo_rel_line_to(cr, edge_width, 0);
00872 cairo_line_to(cr, edge_width, top ? 0 : edge_width);
00873 cairo_rel_line_to(cr, -(edge_width + (edge_width + 1) * factor + 1), 0);
00874 cairo_rel_line_to(cr, 0, dir * edge_width);
00875 cairo_close_path(cr);
00876 cairo_fill(cr);
00877 }
00878
00879 cairo_restore(cr);
00880 }
00881
00882
00883
00884 static void redraw_square(CwChessboard* chessboard, gint index)
00885 {
00886 CwChessboardPrivate* priv = chessboard->priv;
00887 cairo_t* cr = priv->cr;
00888
00889 CwChessboardCode code = priv->board_codes[index];
00890 CwChessboardColorHandle bghandle = convert_code2bghandle(code);
00891 CwChessboardColorHandle mahandle = convert_code2mahandle(code);
00892 gint col = convert_index2column(index);
00893 gint row = convert_index2row(index);
00894 gint sx = priv->pixmap_top_left_a8_x + (priv->flip_board ? 7 - col : col) * priv->sside;
00895 gint sy = priv->pixmap_top_left_a8_y + (squares - 1 - (priv->flip_board ? 7 - row : row)) * priv->sside;
00896
00897 Dout(dc::cwchessboardwidget, "Calling redraw_square(" << chessboard << ", " << index << ")" <<
00898 " with Board Code: " << (int)code);
00899
00900
00901 cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
00902 if (bghandle != 0)
00903 cairo_set_source_rgb(cr,
00904 priv->color_palet[bghandle - 1].red,
00905 priv->color_palet[bghandle - 1].green,
00906 priv->color_palet[bghandle - 1].blue);
00907 else if (((col + row) & 1))
00908 cairo_set_source_rgb(cr,
00909 priv->light_square_color.red,
00910 priv->light_square_color.green,
00911 priv->light_square_color.blue);
00912 else
00913 cairo_set_source_rgb(cr,
00914 priv->dark_square_color.red,
00915 priv->dark_square_color.green,
00916 priv->dark_square_color.blue);
00917 if (!mahandle || !priv->marker_below)
00918 cairo_fill(cr);
00919 else
00920 {
00921 cairo_fill_preserve(cr);
00922
00923 cairo_rectangle(cr, sx + priv->marker_thickness_px, sy + priv->marker_thickness_px,
00924 priv->sside - 2 * priv->marker_thickness_px, priv->sside - 2 * priv->marker_thickness_px);
00925 cairo_fill_rule_t prev_fill_rule = cairo_get_fill_rule(cr);
00926 cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
00927 cairo_set_source_rgb(cr,
00928 priv->color_palet[mahandle - 1].red,
00929 priv->color_palet[mahandle - 1].green,
00930 priv->color_palet[mahandle - 1].blue);
00931 cairo_fill(cr);
00932 cairo_set_fill_rule(cr, prev_fill_rule);
00933 }
00934
00935 guint64 bit = 1;
00936 bit <<= index;
00937
00938
00939 if ((priv->hud_has_content[0] & bit))
00940 {
00941 cairo_set_source_surface(cr, priv->hud_layer_surface[0], priv->pixmap_top_left_a8_x, priv->pixmap_top_left_a8_y);
00942 cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
00943 cairo_fill(cr);
00944 }
00945
00946
00947 if (mahandle && !priv->marker_below)
00948 {
00949 cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
00950 cairo_rectangle(cr, sx + priv->marker_thickness_px, sy + priv->marker_thickness_px,
00951 priv->sside - 2 * priv->marker_thickness_px, priv->sside - 2 * priv->marker_thickness_px);
00952 cairo_fill_rule_t prev_fill_rule = cairo_get_fill_rule(cr);
00953 cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
00954 cairo_set_source_rgb(cr,
00955 priv->color_palet[mahandle - 1].red,
00956 priv->color_palet[mahandle - 1].green,
00957 priv->color_palet[mahandle - 1].blue);
00958 cairo_fill(cr);
00959 cairo_set_fill_rule(cr, prev_fill_rule);
00960 }
00961
00962 if (priv->show_cursor && priv->cursor_col == col && priv->cursor_row == row)
00963 {
00964 cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
00965 cairo_rectangle(cr, sx + priv->cursor_thickness_px, sy + priv->cursor_thickness_px,
00966 priv->sside - 2 * priv->cursor_thickness_px, priv->sside - 2 * priv->cursor_thickness_px);
00967 cairo_fill_rule_t prev_fill_rule = cairo_get_fill_rule(cr);
00968 cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
00969 cairo_set_source_rgb(cr,
00970 priv->cursor_color.red,
00971 priv->cursor_color.green,
00972 priv->cursor_color.blue);
00973 cairo_fill(cr);
00974 cairo_set_fill_rule(cr, prev_fill_rule);
00975 }
00976
00977
00978 if (!is_empty_square(code))
00979 {
00980 cairo_set_source_surface(cr, priv->piece_pixmap[convert_code2piece_pixmap_index(code)].surface, sx, sy);
00981 cairo_paint(cr);
00982 }
00983
00984
00985 if ((priv->hud_has_content[1] & bit))
00986 {
00987 cairo_set_source_surface(cr, priv->hud_layer_surface[1], priv->pixmap_top_left_a8_x, priv->pixmap_top_left_a8_y);
00988 cairo_rectangle(cr, sx, sy, priv->sside, priv->sside);
00989 cairo_fill(cr);
00990 }
00991 }
00992
00993 static void redraw_pieces(CwChessboard* chessboard)
00994 {
00995 CwChessboardPrivate* priv = chessboard->priv;
00996 for (int i = 0; i < 12; ++i)
00997 {
00998 if (priv->piece_pixmap[i].surface)
00999 cairo_surface_destroy(priv->piece_pixmap[i].surface);
01000 priv->piece_pixmap[i].surface =
01001 cairo_surface_create_similar(cairo_get_target(priv->cr),
01002 CAIRO_CONTENT_COLOR_ALPHA, priv->sside, priv->sside);
01003 Dout(dc::cwchessboardwidget|continued_cf, "(Re)drawing piece cache " << i << "... ");
01004 cairo_t* cr = cairo_create(priv->piece_pixmap[i].surface);
01005 unsigned char code = (unsigned char)(i + 2);
01006 cairo_rectangle(cr, 0, 0, priv->sside, priv->sside);
01007 cairo_clip(cr);
01008 CW_CHESSBOARD_GET_CLASS(chessboard)->
01009 draw_piece[convert_code2piece(code)] (chessboard,
01010 cr,
01011 0.5 * priv->sside,
01012 0.5 * priv->sside,
01013 priv->sside,
01014 is_white_piece(code));
01015 cairo_destroy(cr);
01016 Dout(dc::finish, "done");
01017 }
01018 invalidate_board(chessboard);
01019 }
01020
01021
01022
01023 static void redraw_pixmap(GtkWidget* widget)
01024 {
01025 CwChessboard* chessboard = CW_CHESSBOARD(widget);
01026 CwChessboardPrivate* priv = chessboard->priv;
01027
01028
01029 gint const max_total_side = MIN(widget->allocation.width, widget->allocation.height);
01030
01031
01032 gint sside = MAX(min_sside, max_total_side / squares);
01033 priv->border_width = 0;
01034
01035 if (priv->draw_border)
01036 {
01037
01038
01039
01040 ++sside;
01041 do
01042 {
01043 --sside;
01044
01045 priv->border_width = CW_CHESSBOARD_GET_CLASS(chessboard)->calc_board_border_width(chessboard, sside);
01046 }
01047 while (sside > min_sside && squares * sside + 2 * priv->border_width > max_total_side);
01048 }
01049
01050
01051
01052 gint const side = squares * sside;
01053
01054 gint const total_side = side + 2 * priv->border_width;
01055
01056
01057 gint pixmap_width = total_side;
01058 gint pixmap_height = total_side;
01059 if (widget->allocation.width < widget->allocation.height)
01060 pixmap_width = widget->allocation.width;
01061 else
01062 pixmap_height = widget->allocation.height;
01063
01064
01065 gint old_pixmap_width = priv->bottom_right_pixmap_x - priv->top_left_pixmap_x;
01066 gint old_pixmap_height = priv->bottom_right_pixmap_y - priv->top_left_pixmap_y;
01067
01068 priv->top_left_pixmap_x = ((widget->allocation.width - pixmap_width) & ~1) / 2;
01069 priv->top_left_pixmap_y = ((widget->allocation.height - pixmap_height) & ~1) / 2;
01070 priv->bottom_right_pixmap_x = priv->top_left_pixmap_x + pixmap_width;
01071 priv->bottom_right_pixmap_y = priv->top_left_pixmap_y + pixmap_height;
01072 g_assert(priv->top_left_pixmap_x == 0 || priv->top_left_pixmap_y == 0);
01073
01074
01075 * (gint*)(&chessboard->top_left_a1_x) = ((widget->allocation.width - side) & ~1) / 2;
01076 * (gint*)(&chessboard->top_left_a1_y) = ((widget->allocation.height - side) & ~1) / 2 + side - sside;
01077
01078
01079 priv->pixmap_top_left_a8_x = chessboard->top_left_a1_x - priv->top_left_pixmap_x;
01080 priv->pixmap_top_left_a8_y = chessboard->top_left_a1_y + sside - side - priv->top_left_pixmap_y;
01081
01082
01083 GdkRectangle rect;
01084 rect.x = priv->top_left_pixmap_x + priv->pixmap_top_left_a8_x;
01085 rect.y = priv->top_left_pixmap_y + priv->pixmap_top_left_a8_y;
01086 rect.width = squares * sside;
01087 rect.height = squares * sside;
01088 if (priv->chessboard_region)
01089 gdk_region_destroy(priv->chessboard_region);
01090 priv->chessboard_region = gdk_region_rectangle(&rect);
01091
01092 Dout(dc::cwchessboardwidget, "widget size is (" << widget->allocation.width << ", " << widget->allocation.height << ")");
01093 Dout(dc::cwchessboardwidget, "border width is " << priv->border_width <<
01094 "; " << squares << 'x' << squares << " squares with side " << sside);
01095 Dout(dc::cwchessboardwidget, "pixmap at (" << priv->top_left_pixmap_x << ", " << priv->top_left_pixmap_y << ") with size (" <<
01096 pixmap_width << ", " << pixmap_height << ")");
01097 Dout(dc::cwchessboardwidget, "a8 offset within pixmap is (" <<
01098 priv->pixmap_top_left_a8_x << ", " << priv->pixmap_top_left_a8_y << ")");
01099 Dout(dc::cwchessboardwidget, " a1 at (" << chessboard->top_left_a1_x << ", " << chessboard->top_left_a1_y << ")");
01100
01101
01102 gdk_window_invalidate_rect(widget->window,& widget->allocation, FALSE);
01103 priv->need_redraw_invalidated = (guint64)-1;
01104
01105 if (old_pixmap_width == pixmap_width && old_pixmap_height == pixmap_height)
01106 return;
01107
01108 if (priv->cr)
01109 cairo_destroy(priv->cr);
01110 if (priv->pixmap)
01111 g_object_unref(priv->pixmap);
01112
01113
01114 priv->pixmap = gdk_pixmap_new(widget->window, pixmap_width, pixmap_height, -1);
01115
01116
01117 priv->cr = gdk_cairo_create(priv->pixmap);
01118
01119
01120
01121
01122 if (priv->sside != sside)
01123 {
01124
01125 * (gint*)(&chessboard->sside) = priv->sside = sside;
01126
01127
01128 if (priv->hatching_pixmap.surface)
01129 cairo_surface_destroy(priv->hatching_pixmap.surface);
01130 priv->hatching_pixmap.surface = NULL;
01131
01132
01133 redraw_pieces(chessboard);
01134
01135
01136 priv->marker_thickness_px = MAX(1, MIN((gint)round(priv->marker_thickness * sside), sside / 2));
01137 Dout(dc::cwchessboardwidget, "Marker thickness set to " << priv->marker_thickness_px);
01138
01139
01140 priv->cursor_thickness_px = MAX(1, MIN((gint)round(priv->cursor_thickness * sside), sside / 2));
01141 Dout(dc::cwchessboardwidget, "Cursor thickness set to " << priv->cursor_thickness_px);
01142
01143
01144 Dout(dc::cwchessboardwidget|continued_cf, "(Re)creating HUD layers... ");
01145 recreate_hud_layers(chessboard);
01146 Dout(dc::finish, "done");
01147 }
01148
01149 redraw_border(chessboard);
01150 }
01151
01152 static void cw_chessboard_size_request(GtkWidget* widget, GtkRequisition* requisition)
01153 {
01154 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_size_request(" << ")");
01155
01156 CwChessboard* chessboard = CW_CHESSBOARD(widget);
01157 CwChessboardPrivate* priv = chessboard->priv;
01158
01159
01160 gint min_border_width = 0;
01161 if (priv->draw_border)
01162 min_border_width = 2 * CW_CHESSBOARD_GET_CLASS(chessboard)->calc_board_border_width(chessboard, min_sside);
01163 requisition->width = requisition->height = squares * min_sside + min_border_width;
01164 }
01165
01166 static void cw_chessboard_size_allocate(GtkWidget* widget, GtkAllocation* allocation)
01167 {
01168 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_size_allocate(" << widget << ", " << allocation << ")");
01169 widget->allocation =* allocation;
01170 if (GTK_WIDGET_REALIZED(widget))
01171 {
01172 gdk_window_move_resize(widget->window,
01173 allocation->x, allocation->y,
01174 allocation->width, allocation->height);
01175 redraw_pixmap(widget);
01176 }
01177 CwChessboard* chessboard = CW_CHESSBOARD(widget);
01178 chessboard->priv->redraw_background = TRUE;
01179 }
01180
01181 static void cw_chessboard_realize(GtkWidget* widget)
01182 {
01183 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_realize(" << widget << ")");
01184 CwChessboard* chessboard = CW_CHESSBOARD(widget);
01185 GTK_WIDGET_CLASS(cw_chessboard_parent_class)->realize(widget);
01186 redraw_pixmap(widget);
01187
01188 gdk_window_set_background(widget->window,& chessboard->priv->widget_background_color);
01189 }
01190
01191 static void cw_chessboard_unrealize(GtkWidget* widget)
01192 {
01193 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_unrealize(" << widget << ")");
01194 CwChessboard* chessboard = CW_CHESSBOARD(widget);
01195 CwChessboardPrivate* priv = chessboard->priv;
01196 for (int i = 0; i < 12; ++i)
01197 {
01198 if (priv->piece_pixmap[i].surface)
01199 cairo_surface_destroy(priv->piece_pixmap[i].surface);
01200 priv->piece_pixmap[i].surface = NULL;
01201 }
01202 if (priv->hatching_pixmap.surface)
01203 cairo_surface_destroy(priv->hatching_pixmap.surface);
01204 priv->hatching_pixmap.surface = NULL;
01205 gdk_region_destroy(priv->chessboard_region);
01206 priv->chessboard_region = NULL;
01207 for (guint hud = 0; hud < G_N_ELEMENTS(priv->hud_layer_surface); ++hud)
01208 cairo_surface_destroy(priv->hud_layer_surface[hud]);
01209 memset(priv->hud_layer_surface, 0, sizeof(priv->hud_layer_surface));
01210 cairo_destroy(priv->cr);
01211 priv->cr = NULL;
01212 g_object_unref(priv->pixmap);
01213 priv->pixmap = NULL;
01214 priv->sside = -1;
01215 priv->top_left_pixmap_x = 0;
01216 priv->top_left_pixmap_y = 0;
01217 priv->bottom_right_pixmap_x = 0;
01218 priv->bottom_right_pixmap_y = 0;
01219 GTK_WIDGET_CLASS(cw_chessboard_parent_class)->unrealize(widget);
01220 }
01221
01222
01223
01224 static gboolean cw_chessboard_expose(GtkWidget* widget, GdkEventExpose* event)
01225 {
01226 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_expose(" << widget << ", " << event << ")");
01227
01228 CwChessboard* chessboard = CW_CHESSBOARD(widget);
01229 CwChessboardPrivate* priv = chessboard->priv;
01230 GdkRegion* region = event->region;
01231
01232 #if CW_CHESSBOARD_EXPOSE_DEBUG
01233 gdk_window_clear(widget->window);
01234 #endif
01235
01236
01237 for (guint hud = 0; hud < G_N_ELEMENTS(priv->hud_need_redraw); ++hud)
01238 if (priv->hud_need_redraw[hud])
01239 redraw_hud_layer(chessboard, hud);
01240
01241
01242 guint64 redraw_mask = 1;
01243 for (gint i = 0; i < 64; ++i, redraw_mask <<= 1)
01244 if (((priv->need_redraw | priv->need_redraw_invalidated) & redraw_mask))
01245 redraw_square(chessboard, i);
01246 priv->need_redraw_invalidated = 0;
01247 priv->need_redraw = 0;
01248
01249 #if CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
01250
01251 if (priv->number_of_floating_pieces)
01252 {
01253 cairo_save(priv->cr);
01254 cairo_rectangle(priv->cr, priv->pixmap_top_left_a8_x, priv->pixmap_top_left_a8_y,
01255 squares * priv->sside, squares * priv->sside);
01256 cairo_clip(priv->cr);
01257 for (gsize i = 0; i < priv->number_of_floating_pieces; ++i)
01258 if (priv->floating_piece[i].moved)
01259 {
01260 cairo_set_source_surface(priv->cr,
01261 priv->piece_pixmap[convert_code2piece_pixmap_index(priv->floating_piece[i].code)].surface,
01262 priv->floating_piece[i].pixmap_x, priv->floating_piece[i].pixmap_y);
01263 cairo_paint(priv->cr);
01264 }
01265 cairo_restore(priv->cr);
01266 }
01267 #endif
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284 gboolean vertical = (priv->top_left_pixmap_x == 0);
01285
01286
01287 gboolean region_extends_outside_pixmap = FALSE;
01288
01289 GdkRectangle clipbox;
01290 gdk_region_get_clipbox(region,& clipbox);
01291 if (G_UNLIKELY(clipbox.y < priv->top_left_pixmap_y))
01292 region_extends_outside_pixmap = vertical;
01293 if (G_UNLIKELY(clipbox.y + clipbox.height > priv->bottom_right_pixmap_y))
01294 region_extends_outside_pixmap = vertical;
01295 if (G_UNLIKELY(clipbox.x < priv->top_left_pixmap_x))
01296 region_extends_outside_pixmap = !vertical;
01297 if (G_UNLIKELY(clipbox.x + clipbox.width > priv->bottom_right_pixmap_x))
01298 region_extends_outside_pixmap = !vertical;
01299 if (G_UNLIKELY(region_extends_outside_pixmap))
01300 {
01301 GdkRectangle pixmap_rect;
01302 pixmap_rect.x = priv->top_left_pixmap_x;
01303 pixmap_rect.y = priv->top_left_pixmap_y;
01304 pixmap_rect.width = priv->bottom_right_pixmap_x - priv->top_left_pixmap_x;
01305 pixmap_rect.height = priv->bottom_right_pixmap_y - priv->top_left_pixmap_y;
01306 GdkRegion* pixmap_region = gdk_region_rectangle(&pixmap_rect);
01307 if (CW_CHESSBOARD_EXPOSE_ALWAYS_CLEAR_BACKGROUND || priv->redraw_background)
01308 {
01309
01310 GdkRegion* outside_pixmap_region = gdk_region_copy(region);
01311 gdk_region_subtract(outside_pixmap_region, pixmap_region);
01312 GdkRectangle* outside_areas;
01313 gint n_outside_areas;
01314 gdk_region_get_rectangles(outside_pixmap_region,& outside_areas,& n_outside_areas);
01315 GdkRectangle const* outside_rect = outside_areas;
01316 for (int i = 0; i < n_outside_areas; ++i, ++outside_rect)
01317 gdk_window_clear_area(widget->window, outside_rect->x, outside_rect->y, outside_rect->width, outside_rect->height);
01318 #if CW_CHESSBOARD_EXPOSE_DEBUG
01319
01320 GdkGC* debug_gc = gdk_gc_new(priv->pixmap);
01321 GdkColor debug_green = { 0, 0, 65535, 0 };
01322 gdk_colormap_alloc_color(gtk_widget_get_colormap(widget),& debug_green, FALSE, TRUE);
01323 gdk_gc_set_foreground(debug_gc,& debug_green);
01324 outside_rect = outside_areas;
01325 for (int i = 0; i < n_outside_areas; ++i, ++outside_rect)
01326 gdk_draw_rectangle(widget->window, debug_gc, FALSE,
01327 outside_rect->x, outside_rect->y, outside_rect->width - 1, outside_rect->height - 1);
01328 g_object_unref(debug_gc);
01329 #endif
01330 gdk_region_destroy(outside_pixmap_region);
01331 priv->redraw_background = FALSE;
01332 }
01333 gdk_region_intersect(region, pixmap_region);
01334 gdk_region_destroy(pixmap_region);
01335 }
01336
01337 cairo_t* dest = gdk_cairo_create(widget->window);
01338 #if !CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER && !CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET
01339 if (priv->number_of_floating_pieces)
01340 cairo_save(dest);
01341 #endif
01342 cairo_surface_t* source = cairo_get_target(priv->cr);
01343 #if CW_CHESSBOARD_EXPOSE_DEBUG
01344 cairo_set_source_surface(dest, source, priv->top_left_pixmap_x, priv->top_left_pixmap_y);
01345 cairo_paint_with_alpha(dest, 0.35);
01346 #endif
01347 cairo_set_source_surface(dest, source, priv->top_left_pixmap_x, priv->top_left_pixmap_y);
01348 gdk_cairo_region(dest, region);
01349 #if CW_CHESSBOARD_EXPOSE_DEBUG
01350 cairo_clip_preserve(dest);
01351 #else
01352 cairo_clip(dest);
01353 #endif
01354 cairo_paint(dest);
01355 #if CW_CHESSBOARD_EXPOSE_DEBUG
01356 cairo_set_line_width(dest, 2);
01357 cairo_set_source_rgb(dest, 1, 0, 0);
01358 cairo_stroke(dest);
01359 #endif
01360
01361 #if !CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
01362
01363 if (priv->number_of_floating_pieces)
01364 {
01365 #if !CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET
01366 cairo_restore(dest);
01367 #endif
01368 for (gsize i = 0; i < priv->number_of_floating_pieces; ++i)
01369 if (priv->floating_piece[i].moved)
01370 {
01371 cairo_set_source_surface(dest,
01372 priv->piece_pixmap[convert_code2piece_pixmap_index(priv->floating_piece[i].code)].surface,
01373 priv->floating_piece[i].pixmap_x + priv->top_left_pixmap_x,
01374 priv->floating_piece[i].pixmap_y + priv->top_left_pixmap_y);
01375 cairo_paint(dest);
01376 }
01377 }
01378 #endif
01379
01380 cairo_destroy(dest);
01381
01382 if (priv->show_cursor || priv->floating_piece_handle != -1)
01383 {
01384
01385 Dout(dc::cwchessboardwidget, "Calling gdk_window_get_pointer()");
01386 gdk_window_get_pointer(GTK_WIDGET(chessboard)->window, NULL, NULL, NULL);
01387 }
01388 #if 0
01389 static int benchmark = 0;
01390 if (benchmark & 1)
01391 cw_chessboard_enable_hud_layer(chessboard, 0);
01392 else
01393 cw_chessboard_disable_hud_layer(chessboard, 0);
01394 if (++benchmark == 100)
01395 exit(0);
01396 #endif
01397
01398 return TRUE;
01399 }
01400
01401 gint cw_chessboard_default_calc_board_border_width(CwChessboard const* chessboard, gint sside)
01402 {
01403
01404
01405 return (gint)MAX(8.0, round(1.0 + (sside - 12) / 25.0) + sside / 3.0);
01406 }
01407
01408 gboolean cw_chessboard_default_draw_hud_square(G_GNUC_UNUSED CwChessboard* chessboard,
01409 cairo_t* cr, gint col, gint row, gint sside, guint hud)
01410 {
01411 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_default_draw_hud_square(" << chessboard << ", " <<
01412 cr << ", " << col << ", " << row << ", " << sside << ", " << hud << ")");
01413
01414 g_return_val_if_fail(hud < number_of_hud_layers, FALSE);
01415 g_return_val_if_fail(is_inside_board(col, row), FALSE);
01416
01417 int const number_of_lines = 21;
01418 double const line_width = 0.25;
01419
01420
01421 if (hud == 1 || ((col + row) & 1) == 1)
01422 return FALSE;
01423
01424 CwChessboardPrivate* priv = chessboard->priv;
01425 if (!priv->hatching_pixmap.surface)
01426 {
01427 priv->hatching_pixmap.surface = cairo_surface_create_similar(cairo_get_target(priv->cr),
01428 CAIRO_CONTENT_COLOR_ALPHA, sside, sside);
01429 cairo_t* cr = cairo_create(priv->hatching_pixmap.surface);
01430 cairo_set_line_width(cr, line_width);
01431 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
01432 cairo_set_source_rgb(cr, 0, 0, 0);
01433 cairo_scale(cr, (double)sside / number_of_lines, (double)sside / number_of_lines);
01434 for (int h = 0; h < number_of_lines; ++h)
01435 {
01436 double s = h + line_width;
01437 cairo_move_to(cr, s - 0.5 * line_width, 0.5 * line_width);
01438 cairo_line_to(cr, 0.5 * line_width, s - 0.5 * line_width);
01439 cairo_move_to(cr, s + 0.5 * line_width, number_of_lines - 0.5 * line_width);
01440 cairo_line_to(cr, number_of_lines - 0.5 * line_width, s + 0.5 * line_width);
01441 }
01442 cairo_stroke(cr);
01443 cairo_destroy(cr);
01444 }
01445
01446 cairo_set_source_surface(cr, priv->hatching_pixmap.surface, 0, 0);
01447 cairo_paint(cr);
01448
01449 return TRUE;
01450 }
01451
01452 void cw_chessboard_default_draw_hud_layer(CwChessboard* chessboard, cairo_t* cr, gint sside, guint hud)
01453 {
01454 g_return_if_fail(hud < number_of_hud_layers);
01455
01456 CwChessboardPrivate* priv = chessboard->priv;
01457 guint64 bit = 1;
01458 for (gint row = 0; row < 8; ++row)
01459 for (gint col = 0; col < 8; ++col, bit <<= 1)
01460 {
01461 if ((priv->hud_need_redraw[hud] & bit))
01462 {
01463 cairo_save(cr);
01464
01465 cairo_translate(cr, (priv->flip_board ? 7 - col : col) * sside, (priv->flip_board ? row : 7 - row) * sside);
01466 cairo_rectangle(cr, 0, 0, sside, sside);
01467 cairo_clip(cr);
01468 if (CW_CHESSBOARD_GET_CLASS(chessboard)->draw_hud_square(chessboard, cr, col, row, sside, hud))
01469 {
01470 priv->hud_has_content[hud] |= bit;
01471 invalidate_square(chessboard, col, row);
01472 }
01473 cairo_restore(cr);
01474 }
01475 }
01476 }
01477
01478 static GdkRegion* convert_mask2region(guint64 mask, gint x, gint y, gint sside, gboolean flip_board)
01479 {
01480 GdkRegion* region = gdk_region_new();
01481 guint64 row_mask = (guint64)0xff << 56;
01482 for (gint row = 7; row >= 0; --row, row_mask >>= 8)
01483 {
01484 if ((mask & row_mask))
01485 {
01486 guint64 col_mask = (guint64)1 << 8 * row;
01487 gint col_start = 0;
01488 gint col_end;
01489 do
01490 {
01491 while (col_start != 8 && !(mask & col_mask))
01492 {
01493 ++col_start;
01494 col_mask <<= 1;
01495 }
01496 if (col_start == 8)
01497 break;
01498 col_end = col_start;
01499 while (col_end != 8 && (mask & col_mask))
01500 {
01501 ++col_end;
01502 col_mask <<= 1;
01503 }
01504 GdkRectangle rect;
01505 rect.x = x + (flip_board ? 8 - col_end : col_start) * sside;
01506 rect.y = y + (flip_board ? row : 7 - row) * sside;
01507 rect.width = (col_end - col_start) * sside;
01508 rect.height = sside;
01509 gdk_region_union_with_rect(region,& rect);
01510 col_start = col_end;
01511 }
01512 while(col_end !=8);
01513 }
01514 }
01515 return region;
01516 }
01517
01518 static void redraw_hud_layer(CwChessboard* chessboard, guint hud)
01519 {
01520 Dout(dc::cwchessboardwidget, "Calling redraw_hud_layer(" << chessboard << ", " << hud << ")");
01521
01522 CwChessboardPrivate* priv = chessboard->priv;
01523 gint sside = priv->sside;
01524
01525 cairo_t* cr = cairo_create(priv->hud_layer_surface[hud]);
01526 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
01527 guint64 need_clear = priv->hud_has_content[hud] & priv->hud_need_redraw[hud];
01528 for (gint row = 0; row < 8; ++row)
01529 if ((need_clear & ((guint64)0xff << (8 * row))))
01530 {
01531 guint64 bit = (guint64)1 << (8 * row);
01532 for (gint col = 0; col < 8; ++col, bit <<= 1)
01533 if ((need_clear & bit))
01534 {
01535 cairo_rectangle(cr, (priv->flip_board ? 7 - col : col) * sside, (priv->flip_board ? row : 7 - row) * sside, sside, sside);
01536 invalidate_square(chessboard, col, row);
01537 }
01538 }
01539 cairo_fill(cr);
01540 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
01541 priv->hud_has_content[hud]& = ~priv->hud_need_redraw[hud];
01542
01543 if (priv->has_hud_layer[hud])
01544 {
01545 if (CW_CHESSBOARD_GET_CLASS(chessboard)->draw_hud_layer != cw_chessboard_default_draw_hud_layer)
01546 {
01547 priv->hud_has_content[hud] = (guint64)-1;
01548 invalidate_board(chessboard);
01549 }
01550 CW_CHESSBOARD_GET_CLASS(chessboard)->draw_hud_layer(chessboard, cr, sside, hud);
01551 }
01552
01553 GdkRegion* need_redraw_region = convert_mask2region(priv->hud_need_redraw[hud], 0, 0, priv->sside, priv->flip_board);
01554 gdk_cairo_region(cr, need_redraw_region);
01555 gdk_region_destroy(need_redraw_region);
01556 cairo_clip(cr);
01557
01558 for (guint i = 0; i < priv->arrows->len; ++i)
01559 {
01560 Arrow* arrow = (Arrow*)priv->arrows->pdata[i];
01561 if ((priv->hud_need_redraw[hud] & arrow->has_content[hud]))
01562 {
01563 guint64 other_content = 0;
01564 for (guint h = 0; h < number_of_hud_layers; ++h)
01565 if (h != hud)
01566 other_content |= arrow->has_content[h];
01567 gboolean has_colliding_content = (priv->hud_need_redraw[hud] & other_content) != 0;
01568 if (has_colliding_content)
01569 {
01570 cairo_save(cr);
01571 GdkRegion* clip_region = convert_mask2region(arrow->has_content[hud], 0, 0, priv->sside, priv->flip_board);
01572 gdk_cairo_region(cr, clip_region);
01573 gdk_region_destroy(clip_region);
01574 cairo_clip(cr);
01575 }
01576 gdouble length = sqrt((arrow->end_col - arrow->begin_col) * (arrow->end_col - arrow->begin_col) +
01577 (arrow->end_row - arrow->begin_row) * (arrow->end_row - arrow->begin_row));
01578 gdouble begin_x = (0.5 + (priv->flip_board ? 7 - arrow->begin_col : arrow->begin_col)) * priv->sside;
01579 gdouble begin_y = (0.5 + (priv->flip_board ? arrow->begin_row : 7 - arrow->begin_row)) * priv->sside;
01580
01581 gdouble vx = priv->sside * (arrow->end_col - arrow->begin_col) / length;
01582 gdouble vy = priv->sside * (arrow->begin_row - arrow->end_row) / length;
01583 if (priv->flip_board)
01584 {
01585 vx = -vx;
01586 vy = -vy;
01587 }
01588
01589 gdouble tx = -vy;
01590 gdouble ty = vx;
01591
01592 cairo_move_to(cr, begin_x + 0.125 * tx, begin_y + 0.125 * ty);
01593 cairo_rel_line_to(cr, (length - 0.25) * vx, (length - 0.25) * vy);
01594 cairo_rel_line_to(cr, 0.125 * tx, 0.125 * ty);
01595 cairo_line_to(cr, begin_x + length * vx, begin_y + length * vy);
01596 cairo_line_to(cr, begin_x + (length - 0.25) * vx - 0.25 * tx,
01597 begin_y + (length - 0.25) * vy - 0.25 * ty);
01598 cairo_rel_line_to(cr, 0.125 * tx, 0.125 * ty);
01599 cairo_rel_line_to(cr, (0.25 - length) * vx, (0.25 - length) * vy);
01600 cairo_close_path(cr);
01601 cairo_set_source_rgba(cr, arrow->color.red, arrow->color.green, arrow->color.blue, 0.5);
01602 cairo_fill(cr);
01603 if (has_colliding_content)
01604 cairo_restore(cr);
01605 priv->hud_has_content[hud] |= arrow->has_content[hud];
01606 }
01607 }
01608
01609 cairo_destroy(cr);
01610
01611 priv->hud_need_redraw[hud] = 0;
01612 }
01613
01614 static void recreate_hud_layers(CwChessboard* chessboard)
01615 {
01616 Dout(dc::cwchessboardwidget, "Calling recreate_hud_layers(" << chessboard << ")");
01617
01618 CwChessboardPrivate* priv = chessboard->priv;
01619
01620 for (guint hud = 0; hud < number_of_hud_layers; ++hud)
01621 {
01622 if (priv->hud_layer_surface[hud])
01623 cairo_surface_destroy(priv->hud_layer_surface[hud]);
01624
01625
01626 priv->hud_layer_surface[hud] = cairo_surface_create_similar(cairo_get_target(priv->cr),
01627 CAIRO_CONTENT_COLOR_ALPHA, squares * priv->sside, squares * priv->sside);
01628
01629 priv->hud_has_content[hud] = 0;
01630 priv->hud_need_redraw[hud] = (guint64)-1;
01631 }
01632
01633 invalidate_board(chessboard);
01634 }
01635
01636
01637
01638
01639 GtkWidget* cw_chessboard_new(void)
01640 {
01641 return (GtkWidget*)g_object_new(CW_TYPE_CHESSBOARD, NULL);
01642 }
01643
01644 CwChessboardColorHandle
01645 cw_chessboard_allocate_color_handle_rgb(CwChessboard* chessboard, gdouble red, gdouble green, gdouble blue)
01646 {
01647 CwChessboardPrivate* priv = chessboard->priv;
01648 guint32 bit = 1;
01649 guint color_index = 0;
01650 while ((priv->allocated_colors_mask & bit))
01651 {
01652 bit <<= 1;
01653 ++color_index;
01654 }
01655 g_assert(color_index < G_N_ELEMENTS(priv->color_palet));
01656 priv->allocated_colors_mask |= bit;
01657 priv->color_palet[color_index].red = red;
01658 priv->color_palet[color_index].green = green;
01659 priv->color_palet[color_index].blue = blue;
01660 return color_index + 1;
01661 }
01662
01663 void cw_chessboard_free_color_handle(CwChessboard* chessboard, CwChessboardColorHandle handle)
01664 {
01665 CwChessboardPrivate* priv = chessboard->priv;
01666 g_assert(handle > 0);
01667 guint color_index = handle - 1;
01668 g_assert(color_index < G_N_ELEMENTS(priv->color_palet));
01669 guint32 bit = 1 << color_index;
01670 g_assert((priv->allocated_colors_mask & bit) != 0);
01671 priv->allocated_colors_mask& = ~bit;
01672 }
01673
01674 void cw_chessboard_set_marker_color(CwChessboard* chessboard,
01675 gint col, gint row, CwChessboardColorHandle mahandle)
01676 {
01677 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_set_marker_color(" <<
01678 chessboard << ", " << col << ", " << row << ", " << (int)mahandle << ")");
01679 CwChessboardPrivate* priv = chessboard->priv;
01680 BoardIndex index = convert_colrow2index(col, row);
01681 CwChessboardCode old_code = priv->board_codes[index];
01682 priv->board_codes[index] = convert_mahandle2code(mahandle) | (old_code & ~mahandle_mask);
01683 invalidate_square(chessboard, col, row);
01684 }
01685
01686 CwChessboardColorHandle cw_chessboard_get_marker_color(CwChessboard* chessboard, gint col, gint row)
01687 {
01688 return convert_code2mahandle(chessboard->priv->board_codes[convert_colrow2index(col, row)]);
01689 }
01690
01691 void cw_chessboard_set_marker_thickness(CwChessboard* chessboard, gdouble thickness)
01692 {
01693 CwChessboardPrivate* priv = chessboard->priv;
01694 priv->marker_thickness = MIN(MAX(0, thickness), 0.5);
01695 priv->marker_thickness_px = MAX(1, MIN((gint)round(priv->marker_thickness * priv->sside), priv->sside / 2));
01696 invalidate_markers(chessboard);
01697 }
01698
01699 gdouble cw_chessboard_get_marker_thickness(CwChessboard* chessboard)
01700 {
01701 return chessboard->priv->marker_thickness;
01702 }
01703
01704 void cw_chessboard_set_marker_level(CwChessboard* chessboard, gboolean below)
01705 {
01706 chessboard->priv->marker_below = below;
01707 }
01708
01709 void cw_chessboard_set_background_color(CwChessboard* chessboard, gint col, gint row, CwChessboardColorHandle bghandle)
01710 {
01711 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_set_background_color(" <<
01712 chessboard << ", " << col << ", " << row << ", " << (int)bghandle << ")");
01713 CwChessboardPrivate* priv = chessboard->priv;
01714 BoardIndex index = convert_colrow2index(col, row);
01715 CwChessboardCode old_code = priv->board_codes[index];
01716 priv->board_codes[index] = convert_bghandle2code(bghandle) | (old_code & ~bghandle_mask);
01717 invalidate_square(chessboard, col, row);
01718 }
01719
01720 CwChessboardColorHandle cw_chessboard_get_background_color(CwChessboard* chessboard, gint col, gint row)
01721 {
01722 return convert_code2bghandle(chessboard->priv->board_codes[convert_colrow2index(col, row)]);
01723 }
01724
01725 void cw_chessboard_set_background_colors(CwChessboard* chessboard, CwChessboardColorHandle const* handles)
01726 {
01727
01728 CwChessboardCode* board_codes = chessboard->priv->board_codes;
01729 for (int i = 0; i < 64; ++i)
01730 if (convert_code2bghandle(board_codes[i]) != handles[i])
01731 cw_chessboard_set_background_color(chessboard, convert_index2column(i), convert_index2row(i), handles[i]);
01732 }
01733
01734 void cw_chessboard_get_background_colors(CwChessboard* chessboard, CwChessboardColorHandle* handles)
01735 {
01736 CwChessboardCode* board_codes = chessboard->priv->board_codes;
01737 for (int i = 0; i < 64; ++i)
01738 handles[i] = convert_code2bghandle(board_codes[i]);
01739 }
01740
01741 void cw_chessboard_set_square(CwChessboard* chessboard, gint col, gint row, CwChessboardCode code)
01742 {
01743 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_set_square(" <<
01744 chessboard << ", " << col << ", " << row << ", " << (int)code << ")");
01745 gint index = convert_colrow2index(col, row);
01746 CwChessboardCode* board_codes = chessboard->priv->board_codes;
01747 CwChessboardCode old_code = board_codes[index];
01748 if (old_code != code)
01749 {
01750 board_codes[index] = (old_code & ~piece_color_mask) | (code & piece_color_mask);
01751 invalidate_square(chessboard, col, row);
01752 }
01753 }
01754
01755 CwChessboardCode cw_chessboard_get_square(CwChessboard* chessboard, gint col, gint row)
01756 {
01757 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_get_square(" << chessboard << ", " << col << ", " << row << ")");
01758 gint index = convert_colrow2index(col, row);
01759 CwChessboardCode* board_codes = chessboard->priv->board_codes;
01760 return board_codes[index] & piece_color_mask;
01761 }
01762
01763 void cw_chessboard_set_draw_border(CwChessboard* chessboard, gboolean draw)
01764 {
01765 if (chessboard->priv->draw_border != draw)
01766 {
01767 chessboard->priv->draw_border = draw;
01768 if (GTK_WIDGET_REALIZED(chessboard))
01769 redraw_pixmap(GTK_WIDGET(chessboard));
01770 }
01771 }
01772
01773 gboolean cw_chessboard_get_draw_border(CwChessboard* chessboard)
01774 {
01775 return chessboard->priv->draw_border;
01776 }
01777
01778 void cw_chessboard_set_flip_board(CwChessboard* chessboard, gboolean flip)
01779 {
01780 if (chessboard->priv->flip_board != flip)
01781 {
01782 * (gboolean*)(&chessboard->flip_board) = chessboard->priv->flip_board = flip;
01783 if (GTK_WIDGET_REALIZED(chessboard))
01784 {
01785 redraw_border(chessboard);
01786 for (guint hud = 0; hud < G_N_ELEMENTS(chessboard->priv->hud_need_redraw); ++hud)
01787 {
01788 chessboard->priv->hud_need_redraw[hud] = (guint64)-1;
01789 chessboard->priv->hud_has_content[hud] = (guint64)-1;
01790 }
01791 invalidate_board(chessboard);
01792 }
01793 }
01794 }
01795
01796 gboolean cw_chessboard_get_flip_board(CwChessboard* chessboard)
01797 {
01798 return chessboard->priv->flip_board;
01799 }
01800
01801 void cw_chessboard_set_draw_turn_indicators(CwChessboard* chessboard, gboolean draw)
01802 {
01803 CwChessboardPrivate* priv = chessboard->priv;
01804 if (priv->draw_turn_indicators != draw)
01805 {
01806 priv->draw_turn_indicators = draw;
01807 if (GTK_WIDGET_REALIZED(chessboard) && priv->border_width)
01808 {
01809 CW_CHESSBOARD_GET_CLASS(chessboard)->draw_turn_indicator(chessboard, priv->active_turn_indicator, priv->draw_turn_indicators);
01810 invalidate_turn_indicators(chessboard);
01811 }
01812 }
01813 }
01814
01815 gboolean cw_chessboard_get_draw_turn_indicators(CwChessboard* chessboard)
01816 {
01817 return chessboard->priv->draw_turn_indicators;
01818 }
01819
01820 void cw_chessboard_set_active_turn_indicator(CwChessboard* chessboard, gboolean white)
01821 {
01822 CwChessboardPrivate* priv = chessboard->priv;
01823 if (priv->active_turn_indicator != white)
01824 {
01825 priv->active_turn_indicator = white;
01826 if (GTK_WIDGET_REALIZED(chessboard) && priv->border_width && priv->draw_turn_indicators)
01827 {
01828 CW_CHESSBOARD_GET_CLASS(chessboard)->draw_turn_indicator(chessboard, TRUE, white);
01829 CW_CHESSBOARD_GET_CLASS(chessboard)->draw_turn_indicator(chessboard, FALSE, !white);
01830 invalidate_turn_indicators(chessboard);
01831 }
01832 }
01833 }
01834
01835 gboolean cw_chessboard_get_active_turn_indicator(CwChessboard* chessboard)
01836 {
01837 return chessboard->priv->active_turn_indicator;
01838 }
01839
01840 void cw_chessboard_set_dark_square_color(CwChessboard* chessboard, GdkColor const* color)
01841 {
01842 chessboard->priv->dark_square_color_pixel = color->pixel;
01843 chessboard->priv->dark_square_color.red = color->red / 65535.0;
01844 chessboard->priv->dark_square_color.green = color->green / 65535.0;
01845 chessboard->priv->dark_square_color.blue = color->blue / 65535.0;
01846 invalidate_board(chessboard);
01847 }
01848
01849 void cw_chessboard_get_dark_square_color(CwChessboard* chessboard, GdkColor* color)
01850 {
01851 color->pixel = chessboard->priv->dark_square_color_pixel;
01852 color->red = (guint16)round(65535.0 * chessboard->priv->dark_square_color.red);
01853 color->green = (guint16)round(65535.0 * chessboard->priv->dark_square_color.green);
01854 color->blue = (guint16)round(65535.0 * chessboard->priv->dark_square_color.blue);
01855 }
01856
01857 void cw_chessboard_set_light_square_color(CwChessboard* chessboard, GdkColor const* color)
01858 {
01859 chessboard->priv->light_square_color_pixel = color->pixel;
01860 chessboard->priv->light_square_color.red = color->red / 65535.0;
01861 chessboard->priv->light_square_color.green = color->green / 65535.0;
01862 chessboard->priv->light_square_color.blue = color->blue / 65535.0;
01863 invalidate_board(chessboard);
01864 }
01865
01866 void cw_chessboard_get_light_square_color(CwChessboard* chessboard, GdkColor* color)
01867 {
01868 color->pixel = chessboard->priv->light_square_color_pixel;
01869 color->red = (guint16)round(65535.0 * chessboard->priv->light_square_color.red);
01870 color->green = (guint16)round(65535.0 * chessboard->priv->light_square_color.green);
01871 color->blue = (guint16)round(65535.0 * chessboard->priv->light_square_color.blue);
01872 }
01873
01874 void cw_chessboard_set_border_color(CwChessboard* chessboard, GdkColor const* color)
01875 {
01876 chessboard->priv->board_border_color_pixel = color->pixel;
01877 chessboard->priv->board_border_color.red = color->red / 65535.0;
01878 chessboard->priv->board_border_color.green = color->green / 65535.0;
01879 chessboard->priv->board_border_color.blue = color->blue / 65535.0;
01880 if (GTK_WIDGET_REALIZED(chessboard))
01881 redraw_border(chessboard);
01882 }
01883
01884 void cw_chessboard_get_border_color(CwChessboard* chessboard, GdkColor* color)
01885 {
01886 color->pixel = chessboard->priv->board_border_color_pixel;
01887 color->red = (guint16)round(65535.0 * chessboard->priv->board_border_color.red);
01888 color->green = (guint16)round(65535.0 * chessboard->priv->board_border_color.green);
01889 color->blue = (guint16)round(65535.0 * chessboard->priv->board_border_color.blue);
01890 }
01891
01892 void cw_chessboard_set_white_fill_color(CwChessboard* chessboard, GdkColor const* color)
01893 {
01894 chessboard->priv->white_piece_fill_color_pixel = color->pixel;
01895 chessboard->priv->white_piece_fill_color.red = color->red / 65535.0;
01896 chessboard->priv->white_piece_fill_color.green = color->green / 65535.0;
01897 chessboard->priv->white_piece_fill_color.blue = color->blue / 65535.0;
01898 if (GTK_WIDGET_REALIZED(chessboard))
01899 redraw_pieces(chessboard);
01900 }
01901
01902 void cw_chessboard_get_white_fill_color(CwChessboard* chessboard, GdkColor* color)
01903 {
01904 color->pixel = chessboard->priv->white_piece_fill_color_pixel;
01905 color->red = (guint16)round(65535.0 * chessboard->priv->white_piece_fill_color.red);
01906 color->green = (guint16)round(65535.0 * chessboard->priv->white_piece_fill_color.green);
01907 color->blue = (guint16)round(65535.0 * chessboard->priv->white_piece_fill_color.blue);
01908 }
01909
01910 void cw_chessboard_set_white_line_color(CwChessboard* chessboard, GdkColor const* color)
01911 {
01912 chessboard->priv->white_piece_line_color_pixel = color->pixel;
01913 chessboard->priv->white_piece_line_color.red = color->red / 65535.0;
01914 chessboard->priv->white_piece_line_color.green = color->green / 65535.0;
01915 chessboard->priv->white_piece_line_color.blue = color->blue / 65535.0;
01916 if (GTK_WIDGET_REALIZED(chessboard))
01917 redraw_pieces(chessboard);
01918 }
01919
01920 void cw_chessboard_get_white_line_color(CwChessboard* chessboard, GdkColor* color)
01921 {
01922 color->pixel = chessboard->priv->white_piece_line_color_pixel;
01923 color->red = (guint16)round(65535.0 * chessboard->priv->white_piece_line_color.red);
01924 color->green = (guint16)round(65535.0 * chessboard->priv->white_piece_line_color.green);
01925 color->blue = (guint16)round(65535.0 * chessboard->priv->white_piece_line_color.blue);
01926 }
01927
01928 void cw_chessboard_set_black_fill_color(CwChessboard* chessboard, GdkColor const* color)
01929 {
01930 chessboard->priv->black_piece_fill_color_pixel = color->pixel;
01931 chessboard->priv->black_piece_fill_color.red = color->red / 65535.0;
01932 chessboard->priv->black_piece_fill_color.green = color->green / 65535.0;
01933 chessboard->priv->black_piece_fill_color.blue = color->blue / 65535.0;
01934 if (GTK_WIDGET_REALIZED(chessboard))
01935 redraw_pieces(chessboard);
01936 }
01937
01938 void cw_chessboard_get_black_fill_color(CwChessboard* chessboard, GdkColor* color)
01939 {
01940 color->pixel = chessboard->priv->black_piece_fill_color_pixel;
01941 color->red = (guint16)round(65535.0 * chessboard->priv->black_piece_fill_color.red);
01942 color->green = (guint16)round(65535.0 * chessboard->priv->black_piece_fill_color.green);
01943 color->blue = (guint16)round(65535.0 * chessboard->priv->black_piece_fill_color.blue);
01944 }
01945
01946 void cw_chessboard_set_black_line_color(CwChessboard* chessboard, GdkColor const* color)
01947 {
01948 chessboard->priv->black_piece_line_color_pixel = color->pixel;
01949 chessboard->priv->black_piece_line_color.red = color->red / 65535.0;
01950 chessboard->priv->black_piece_line_color.green = color->green / 65535.0;
01951 chessboard->priv->black_piece_line_color.blue = color->blue / 65535.0;
01952 if (GTK_WIDGET_REALIZED(chessboard))
01953 redraw_pieces(chessboard);
01954 }
01955
01956 void cw_chessboard_get_black_line_color(CwChessboard* chessboard, GdkColor* color)
01957 {
01958 color->pixel = chessboard->priv->black_piece_line_color_pixel;
01959 color->red = (guint16)round(65535.0 * chessboard->priv->black_piece_line_color.red);
01960 color->green = (guint16)round(65535.0 * chessboard->priv->black_piece_line_color.green);
01961 color->blue = (guint16)round(65535.0 * chessboard->priv->black_piece_line_color.blue);
01962 }
01963
01964 void cw_chessboard_set_cursor_color(CwChessboard* chessboard, GdkColor const* color)
01965 {
01966 chessboard->priv->cursor_color_pixel = color->pixel;
01967 chessboard->priv->cursor_color.red = color->red / 65535.0;
01968 chessboard->priv->cursor_color.green = color->green / 65535.0;
01969 chessboard->priv->cursor_color.blue = color->blue / 65535.0;
01970 invalidate_cursor(chessboard);
01971 }
01972
01973 void cw_chessboard_get_cursor_color(CwChessboard* chessboard, GdkColor* color)
01974 {
01975 color->pixel = chessboard->priv->cursor_color_pixel;
01976 color->red = (guint16)round(65535.0 * chessboard->priv->cursor_color.red);
01977 color->green = (guint16)round(65535.0 * chessboard->priv->cursor_color.green);
01978 color->blue = (guint16)round(65535.0 * chessboard->priv->cursor_color.blue);
01979 }
01980
01981 void cw_chessboard_set_cursor_thickness(CwChessboard* chessboard, gdouble thickness)
01982 {
01983 CwChessboardPrivate* priv = chessboard->priv;
01984 priv->cursor_thickness = MIN(MAX(0, thickness), 0.5);
01985 priv->cursor_thickness_px = MAX(1, MIN((gint)round(priv->cursor_thickness * priv->sside), priv->sside / 2));
01986 invalidate_cursor(chessboard);
01987 }
01988
01989 gdouble cw_chessboard_get_cursor_thickness(CwChessboard* chessboard)
01990 {
01991 return chessboard->priv->cursor_thickness;
01992 }
01993
01994 void cw_chessboard_show_cursor(CwChessboard* chessboard)
01995 {
01996 GtkWidget* widget = GTK_WIDGET(chessboard);
01997 CwChessboardPrivate* priv = chessboard->priv;
01998 if (priv->show_cursor)
01999 return;
02000 priv->show_cursor = TRUE;
02001 gint x;
02002 gint y;
02003 Dout(dc::cwchessboardwidget, "Calling gdk_window_get_pointer()");
02004 gdk_window_get_pointer(widget->window,& x,& y, NULL);
02005 update_cursor_position(chessboard, x, y, TRUE);
02006 }
02007
02008 void cw_chessboard_hide_cursor(CwChessboard* chessboard)
02009 {
02010 CwChessboardPrivate* priv = chessboard->priv;
02011 invalidate_cursor(chessboard);
02012 priv->show_cursor = FALSE;
02013 }
02014
02015 void cw_chessboard_move_floating_piece(CwChessboard* chessboard, gint handle, gdouble x, gdouble y)
02016 {
02017 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_move_floating_piece(" <<
02018 chessboard << ", " << (int)handle << ", " << x << ", " << y << ")");
02019
02020 GtkWidget* widget = GTK_WIDGET(chessboard);
02021 CwChessboardPrivate* priv = chessboard->priv;
02022 GdkRectangle rect;
02023
02024 g_assert(handle >= 0 && (gsize)handle < G_N_ELEMENTS(priv->floating_piece));
02025 g_assert(!is_empty_square(priv->floating_piece[handle].code));
02026
02027
02028
02029
02030
02031 rect.width = priv->sside;
02032 rect.height = priv->sside;
02033 rect.x = priv->floating_piece[handle].pixmap_x + priv->top_left_pixmap_x;
02034 rect.y = priv->floating_piece[handle].pixmap_y + priv->top_left_pixmap_y;
02035 gdk_window_invalidate_rect(widget->window,& rect, FALSE);
02036 #if CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
02037 gint col = cw_chessboard_x2col(chessboard, rect.x);
02038 gint row = cw_chessboard_y2row(chessboard, rect.y);
02039 if (is_inside_board(col, row))
02040 {
02041 BoardIndex index = convert_colrow2index(col, row);
02042 guint64 redraw_mask = 1;
02043 priv->need_redraw |= (redraw_mask << index);
02044 }
02045 col += priv->flip_board ? -1 : 1;
02046 if (is_inside_board(col, row))
02047 {
02048 BoardIndex index = convert_colrow2index(col, row);
02049 guint64 redraw_mask = 1;
02050 priv->need_redraw |= (redraw_mask << index);
02051 }
02052 row += priv->flip_board ? 1 : -1;
02053 if (is_inside_board(col, row))
02054 {
02055 BoardIndex index = convert_colrow2index(col, row);
02056 guint64 redraw_mask = 1;
02057 priv->need_redraw |= (redraw_mask << index);
02058 }
02059 col += priv->flip_board ? 1 : -1;
02060 if (is_inside_board(col, row))
02061 {
02062 BoardIndex index = convert_colrow2index(col, row);
02063 guint64 redraw_mask = 1;
02064 priv->need_redraw |= (redraw_mask << index);
02065 }
02066 #endif
02067 gboolean outside_window =
02068 rect.x + rect.width < 0 ||
02069 rect.x > widget->allocation.x ||
02070 rect.y + rect.height < 0 ||
02071 rect.y > widget->allocation.y;
02072
02073 priv->redraw_background = priv->redraw_background ||
02074 rect.x < priv->top_left_pixmap_x ||
02075 rect.x + rect.width > priv->bottom_right_pixmap_x ||
02076 rect.y < priv->top_left_pixmap_y ||
02077 rect.y + rect.height > priv->bottom_right_pixmap_y;
02078 rect.x = (gint)trunc(x - 0.5 * priv->sside);
02079 rect.y = (gint)trunc(y - 0.5 * priv->sside);
02080 #if CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET || CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
02081 gdk_window_invalidate_rect(widget->window,& rect, FALSE);
02082 outside_window = outside_window&&
02083 (rect.x + rect.width < 0 ||
02084 rect.x > widget->allocation.x ||
02085 rect.y + rect.height < 0 ||
02086 rect.y > widget->allocation.y);
02087 #endif
02088 if (outside_window && priv->floating_piece[handle].pointer_device)
02089 {
02090 Dout(dc::cwchessboardwidget, "Calling gdk_window_get_pointer()");
02091 gdk_window_get_pointer(widget->window, NULL, NULL, NULL);
02092 }
02093 priv->floating_piece[handle].pixmap_x = rect.x - priv->top_left_pixmap_x;
02094 priv->floating_piece[handle].pixmap_y = rect.y - priv->top_left_pixmap_y;
02095 priv->floating_piece[handle].moved = TRUE;
02096 }
02097
02098 gint cw_chessboard_add_floating_piece(CwChessboard* chessboard, CwChessboardCode code,
02099 gdouble x, gdouble y, gboolean pointer_device)
02100 {
02101 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_add_floating_piece(" << chessboard << ", code:" << code <<
02102 ", x:" << x << ", y:" << y << ", " << pointer_device << ")");
02103
02104 CwChessboardPrivate* priv = chessboard->priv;
02105
02106
02107 g_assert(priv->number_of_floating_pieces < G_N_ELEMENTS(priv->floating_piece));
02108
02109 priv->number_of_floating_pieces++;
02110 gint handle = 0;
02111 while (priv->floating_piece[handle].code != empty_square)
02112 ++handle;
02113 GdkRectangle rect;
02114 rect.x = (gint)trunc(x - 0.5 * priv->sside);
02115 rect.y = (gint)trunc(y - 0.5 * priv->sside);
02116 priv->floating_piece[handle].code = code & piece_color_mask;
02117 priv->floating_piece[handle].pixmap_x = rect.x - priv->top_left_pixmap_x;
02118 priv->floating_piece[handle].pixmap_y = rect.y - priv->top_left_pixmap_y;
02119 priv->floating_piece[handle].moved = TRUE;
02120 if (priv->floating_piece_handle != -1)
02121 pointer_device = FALSE;
02122 priv->floating_piece[handle].pointer_device = pointer_device;
02123 if (pointer_device)
02124 priv->floating_piece_handle = handle;
02125 #if CW_CHESSBOARD_FLOATING_PIECE_INVALIDATE_TARGET || CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
02126
02127 rect.width = priv->sside;
02128 rect.height = priv->sside;
02129 gdk_window_invalidate_rect(GTK_WIDGET(chessboard)->window,& rect, FALSE);
02130 #else
02131
02132 rect.width = priv->sside;
02133 rect.height = priv->sside;
02134 gdk_window_invalidate_rect(GTK_WIDGET(chessboard)->window,& rect, FALSE);
02135 #endif
02136
02137 Dout(dc::cwchessboardwidget, "number_of_floating_pieces = " << priv->number_of_floating_pieces);
02138 Dout(dc::cwchessboardwidget, "Allocated handle " << handle);
02139
02140 return handle;
02141 }
02142
02143 void cw_chessboard_remove_floating_piece(CwChessboard* chessboard, gint handle)
02144 {
02145 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_remove_floating_piece(" << chessboard << ", handle:" << handle << ")");
02146
02147 CwChessboardPrivate* priv = chessboard->priv;
02148 GdkRectangle rect;
02149
02150 g_assert(handle >= 0 && (gsize)handle < G_N_ELEMENTS(priv->floating_piece));
02151 g_assert(!is_empty_square(priv->floating_piece[handle].code));
02152
02153
02154 rect.width = priv->sside;
02155 rect.height = priv->sside;
02156 rect.x = priv->floating_piece[handle].pixmap_x + priv->top_left_pixmap_x;
02157 rect.y = priv->floating_piece[handle].pixmap_y + priv->top_left_pixmap_y;
02158 gdk_window_invalidate_rect(GTK_WIDGET(chessboard)->window,& rect, FALSE);
02159 #if CW_CHESSBOARD_FLOATING_PIECE_DOUBLE_BUFFER
02160 gint col = cw_chessboard_x2col(chessboard, rect.x);
02161 gint row = cw_chessboard_y2row(chessboard, rect.y);
02162 if (is_inside_board(col, row))
02163 {
02164 BoardIndex index = convert_colrow2index(col, row);
02165 guint64 redraw_mask = 1;
02166 priv->need_redraw |= (redraw_mask << index);
02167 }
02168 col += priv->flip_board ? -1 : 1;
02169 if (is_inside_board(col, row))
02170 {
02171 BoardIndex index = convert_colrow2index(col, row);
02172 guint64 redraw_mask = 1;
02173 priv->need_redraw |= (redraw_mask << index);
02174 }
02175 row += priv->flip_board ? 1 : -1;
02176 if (is_inside_board(col, row))
02177 {
02178 BoardIndex index = convert_colrow2index(col, row);
02179 guint64 redraw_mask = 1;
02180 priv->need_redraw |= (redraw_mask << index);
02181 }
02182 col += priv->flip_board ? 1 : -1;
02183 if (is_inside_board(col, row))
02184 {
02185 BoardIndex index = convert_colrow2index(col, row);
02186 guint64 redraw_mask = 1;
02187 priv->need_redraw |= (redraw_mask << index);
02188 }
02189 #endif
02190
02191 priv->redraw_background = priv->redraw_background ||
02192 rect.x < priv->top_left_pixmap_x ||
02193 rect.x + rect.width > priv->bottom_right_pixmap_x ||
02194 rect.y < priv->top_left_pixmap_y ||
02195 rect.y + rect.height > priv->bottom_right_pixmap_y;
02196 if (priv->floating_piece[handle].pointer_device)
02197 priv->floating_piece_handle = -1;
02198 priv->number_of_floating_pieces--;
02199 priv->floating_piece[handle].code = empty_square;
02200 Dout(dc::cwchessboardwidget, "number_of_floating_pieces = " << priv->number_of_floating_pieces);
02201 }
02202
02203 CwChessboardCode cw_chessboard_get_floating_piece(CwChessboard* chessboard, gint handle)
02204 {
02205 g_assert(handle >= 0 && (gsize)handle < G_N_ELEMENTS(chessboard->priv->floating_piece));
02206 return chessboard->priv->floating_piece[handle].code;
02207 }
02208
02209 void cw_chessboard_enable_hud_layer(CwChessboard* chessboard, guint hud)
02210 {
02211 g_return_if_fail(hud < number_of_hud_layers);
02212 chessboard->priv->has_hud_layer[hud] = TRUE;
02213 chessboard->priv->hud_need_redraw[hud] = (guint64)-1;
02214 }
02215
02216 void cw_chessboard_disable_hud_layer(CwChessboard* chessboard, guint hud)
02217 {
02218 g_return_if_fail(hud < number_of_hud_layers);
02219 chessboard->priv->has_hud_layer[hud] = FALSE;
02220 chessboard->priv->hud_need_redraw[hud] = (guint64)-1;
02221 }
02222
02223 static guint64 invalidate_arrow(CwChessboard* chessboard, gint col1, gint row1, gint col2, gint row2)
02224 {
02225 Dout(dc::cwchessboardwidget|continued_cf, "Calling invalidate_arrow(" << chessboard << ", " <<
02226 col1 << ", " << row1 << ", " << col2 << ", " << row2 << ") = ");
02227 guint64 result = 0;
02228 if (col1 == col2)
02229 {
02230 if (row1 > row2)
02231 {
02232 gint tmp = row1;
02233 row1 = row2;
02234 row2 = tmp;
02235 }
02236 guint64 bit = (guint64)1 << convert_colrow2index(col1, row1);
02237 for (gint row = row1; row <= row2; ++row, bit <<= 8)
02238 {
02239 result |= bit;
02240 invalidate_square(chessboard, col1, row);
02241 }
02242 Dout(dc::finish, std::hex << result);
02243 return result;
02244 }
02245 else if (row1 == row2)
02246 {
02247 if (col1 > col2)
02248 {
02249 gint tmp = col1;
02250 col1 = col2;
02251 col2 = tmp;
02252 }
02253 guint64 bit = (guint64)1 << convert_colrow2index(col1, row1);
02254 for (gint col = col1; col <= col2; ++col, bit <<= 1)
02255 {
02256 result |= bit;
02257 invalidate_square(chessboard, col, row1);
02258 }
02259 Dout(dc::finish, std::hex << result);
02260 return result;
02261 }
02262 if (row1 > row2)
02263 {
02264
02265 gint tmp;
02266 tmp = col1; col1 = col2; col2 = tmp;
02267 tmp = row1; row1 = row2; row2 = tmp;
02268 }
02269 double const arrow_width = 0.125;
02270 double delta = arrow_width*
02271 sqrt((row2 - row1) * (row2 - row1) + (col2 - col1) * (col2 - col1)) / (row2 - row1);
02272 gint col_start = col1;
02273 gint row = row1;
02274 result = 0;
02275 guint64 sign = 1;
02276 if (col1 > col2)
02277 {
02278 sign = 2;
02279 delta = -delta;
02280 }
02281 for (double r = row1 + 0.5; r < row2; r += 1.0)
02282 {
02283 double c = col1 + (r - row1) * (col2 - col1) / (row2 - row1);
02284 gint col_end = (gint)round(c + delta);
02285 guint64 mask1 = (sign << convert_colrow2index(col_start, row)) - 1;
02286 guint64 mask2 = ((3 - sign) << convert_colrow2index(col_end, row)) - 1;
02287 result |= mask1 ^ mask2;
02288 col_start = (gint)round(c - delta);
02289 ++row;
02290 }
02291 guint64 mask1 = (sign << convert_colrow2index(col_start, row)) - 1;
02292 guint64 mask2 = ((3 - sign) << convert_colrow2index(col2, row)) - 1;
02293 result |= mask1 ^ mask2;
02294 Dout(dc::finish, std::hex << result);
02295 guint64 bit = 1;
02296 for (gint row = 0; row < squares; ++row)
02297 for (gint col = 0; col < squares; ++col, bit <<= 1)
02298 if ((result & bit))
02299 invalidate_square(chessboard, col, row);
02300 return result;
02301 }
02302
02303 gpointer cw_chessboard_add_arrow(CwChessboard* chessboard,
02304 gint begin_col, gint begin_row, gint end_col, gint end_row, GdkColor const* color)
02305 {
02306 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_add_arrow(" << chessboard << ", " <<
02307 begin_col << ", " << begin_row << ", " << end_col << ", " << end_row << ", " << color << ")");
02308
02309 g_return_val_if_fail(begin_col != end_col || begin_row != end_row, NULL);
02310 g_return_val_if_fail(is_inside_board(begin_col, begin_row) && is_inside_board(end_col, end_row), NULL);
02311
02312 Arrow* arrow = (Arrow*)g_malloc(sizeof(Arrow));
02313 g_ptr_array_add(chessboard->priv->arrows, arrow);
02314 arrow->begin_col = begin_col;
02315 arrow->begin_row = begin_row;
02316 arrow->end_col = end_col;
02317 arrow->end_row = end_row;
02318 arrow->color.red = color->red / 65535.0;
02319 arrow->color.green = color->green / 65535.0;
02320 arrow->color.blue = color->blue / 65535.0;
02321 guint64 content = invalidate_arrow(chessboard, begin_col, begin_row, end_col, end_row);
02322 guint64 start_square = (guint64)1 << convert_colrow2index(begin_col, begin_row);
02323 chessboard->priv->hud_need_redraw[0] |= (arrow->has_content[0] = start_square);
02324 chessboard->priv->hud_need_redraw[1] |= (arrow->has_content[1] = content ^ start_square);
02325 return arrow;
02326 }
02327
02328 void cw_chessboard_remove_arrow(CwChessboard* chessboard, gpointer ptr)
02329 {
02330 Dout(dc::cwchessboardwidget, "Calling cw_chessboard_remove_arrow(" << chessboard << ", " << ptr << ")");
02331 if (g_ptr_array_remove_fast(chessboard->priv->arrows, ptr))
02332 {
02333 Arrow* arrow = (Arrow*)ptr;
02334 chessboard->priv->hud_need_redraw[0] |= arrow->has_content[0];
02335 chessboard->priv->hud_need_redraw[1] |= arrow->has_content[1];
02336 g_free(ptr);
02337 }
02338 }
02339
02340
02341
02342
02343 static double const black_line_width = CONST(black_line_width, 0.026);
02344 static double const white_line_width = CONST(white_line_width, 1.5 * black_line_width);
02345
02346 static double snap_bottom(double y, double translate, double scale, double line_width)
02347 {
02348 if (scale < 27)
02349 return y;
02350 return (round((y + 0.5 * line_width) * scale - translate) + translate) / scale - 0.5 * line_width;
02351 }
02352
02353 static double snap_top(double y, double translate, double scale, double line_width)
02354 {
02355 if (scale < 27)
02356 return y;
02357 return (round((y - 0.5 * line_width) * scale - translate) + translate) / scale + 0.5 * line_width;
02358 }
02359
02360 static double snap_line_width(double line_width, double scale)
02361 {
02362 if (line_width * scale < 1.0)
02363 return line_width;
02364 return trunc(line_width * scale + 0.3) / scale;
02365 }
02366
02367
02368 static void set_fill_color(cairo_t* cr, CwChessboardPrivate* priv, gboolean white)
02369 {
02370 if (white)
02371 cairo_set_source_rgb(cr, priv->white_piece_fill_color.red,
02372 priv->white_piece_fill_color.green,
02373 priv->white_piece_fill_color.blue);
02374 else
02375 cairo_set_source_rgb(cr, priv->black_piece_fill_color.red,
02376 priv->black_piece_fill_color.green,
02377 priv->black_piece_fill_color.blue);
02378 }
02379
02380
02381 static void set_line_color(cairo_t* cr, CwChessboardPrivate* priv, gboolean white)
02382 {
02383 if (white)
02384 cairo_set_source_rgb(cr, priv->white_piece_line_color.red,
02385 priv->white_piece_line_color.green,
02386 priv->white_piece_line_color.blue);
02387 else
02388 cairo_set_source_rgb(cr, priv->black_piece_line_color.red,
02389 priv->black_piece_line_color.green,
02390 priv->black_piece_line_color.blue);
02391 }
02392
02393
02394 void cw_chessboard_draw_pawn(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
02395 {
02396 static double const base_outside_diameter_cm = CONST(base_outside_diameter_cm, 3.265);
02397 static double const width_pawn_cm = CONST(width_pawn_cm, 5.31);
02398 static double const base_radius = CONST(base_radius, 0.5 * (base_outside_diameter_cm / width_pawn_cm - black_line_width));
02399 static double const mid_outside_diameter_cm = CONST(mid_outside_diameter_cm, 1.98);
02400 static double const mid_radius = CONST(mid_radius, 0.5 * (mid_outside_diameter_cm / width_pawn_cm - black_line_width));
02401 static double const head_outside_diameter_cm = CONST(head_outside_diameter_cm, 1.12);
02402 static double const head_radius = CONST(head_radius, 0.5 * (head_outside_diameter_cm / width_pawn_cm - black_line_width));
02403 static double const height_pawn_cm = CONST(height_pawn_cm, 5.43);
02404 static double const bottom_pawn_cm = CONST(bottom_pawn_cm, 0.58);
02405 static double const foot_height = CONST(foot_height, 0.0387);
02406 static double const base_y = CONST(base_y, 0.5 - bottom_pawn_cm / height_pawn_cm - 0.5 * black_line_width);
02407 static double const base_scale = CONST(base_scale, 0.931);
02408 static double const mid_y = CONST(mid_y, -0.0545);
02409 static double const top_offset_cm = CONST(top_offset_cm, 0.62);
02410 static double const head_y = CONST(head_y, -0.5 + top_offset_cm / height_pawn_cm + 0.5 * black_line_width + head_radius);
02411
02412 static double const base_angle = CONST(base_angle, 1.148);
02413 static double const mid_angle1 = CONST(mid_angle1, 0.992);
02414 static double const inner_neck_width_cm = CONST(inner_neck_width_cm, 0.41);
02415 static double const neck_right = CONST(neck_right, 0.5 * (inner_neck_width_cm / width_pawn_cm + black_line_width));
02416 static double const head_angle = CONST(head_angle, asin(neck_right / head_radius));
02417 static double const mid_scale = CONST(mid_scale, (mid_y - (head_y + head_radius * cos(head_angle)) -
02418 0.1 * black_line_width) / sqrt(mid_radius * mid_radius - neck_right * neck_right));
02419 static double const mid_angle2 = CONST(mid_angle2, asin(head_radius * sin(head_angle) / mid_radius));
02420
02421 double const base_y_sn = snap_bottom(base_y, y, scale, black_line_width);
02422
02423 CwChessboardPrivate* priv = chessboard->priv;
02424
02425 cairo_save(cr);
02426 cairo_translate(cr, x, y);
02427 cairo_scale(cr, scale, scale);
02428 cairo_set_line_width(cr, black_line_width);
02429
02430
02431 cairo_move_to(cr, -base_radius, base_y_sn);
02432 cairo_save(cr);
02433 cairo_translate(cr, 0.0, base_y_sn - foot_height);
02434 cairo_scale(cr, 1.0, base_scale);
02435 cairo_arc(cr, 0.0, 0.0, base_radius, -M_PI, -M_PI + base_angle);
02436 cairo_restore(cr);
02437
02438
02439 cairo_save(cr);
02440 cairo_translate(cr, 0.0, mid_y);
02441 cairo_scale(cr, 1.0, mid_scale);
02442 cairo_arc(cr, 0.0, 0.0, mid_radius, -M_PI - mid_angle1, -0.5 * M_PI - mid_angle2);
02443 cairo_restore(cr);
02444
02445
02446 cairo_arc(cr, 0.0, head_y, head_radius, -1.5 * M_PI + head_angle, 0.5 * M_PI - head_angle);
02447
02448
02449 cairo_save(cr);
02450 cairo_translate(cr, 0.0, mid_y);
02451 cairo_scale(cr, 1.0, mid_scale);
02452 cairo_arc(cr, 0.0, 0.0, mid_radius, -0.5 * M_PI + mid_angle2, mid_angle1);
02453 cairo_restore(cr);
02454
02455
02456 cairo_save(cr);
02457 cairo_translate(cr, 0.0, base_y_sn - foot_height);
02458 cairo_scale(cr, 1.0, base_scale);
02459 cairo_arc(cr, 0.0, 0.0, base_radius, -base_angle, 0.0);
02460 cairo_restore(cr);
02461 cairo_line_to(cr, base_radius, base_y_sn);
02462
02463
02464 cairo_close_path(cr);
02465
02466 set_fill_color(cr, priv, white);
02467 cairo_fill_preserve(cr);
02468 if (white)
02469 set_line_color(cr, priv, white);
02470 cairo_stroke(cr);
02471
02472 cairo_restore(cr);
02473 }
02474
02475 void cw_chessboard_draw_king(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
02476 {
02477
02478 static double const blob_left_cm = CONST(blob_left_cm, 1.22);
02479 static double const band_edge_left_cm = CONST(band_edge_left_cm, 2.55);
02480 static double const band_left_cm = CONST(band_left_cm, 2.67);
02481 static double const inside_left_cm = CONST(inside_left_cm, 3.06);
02482 static double const center_blob_left_cm = CONST(center_blob_left_cm, 4.525);
02483 static double const cross_left_cm = CONST(cross_left_cm, 4.71);
02484 static double const width_king_cm = CONST(width_king_cm, 10.67);
02485 static double const bottom_king_cm = CONST(bottom_king_cm, 1.155);
02486 static double const band_line_top_cm = CONST(band_line_top_cm, 2.95);
02487 static double const band_top_king_cm = CONST(band_top_king_cm, 4.04);
02488 static double const center_y_cm = CONST(center_y_cm, 5.02);
02489 static double const blob_top_cm = CONST(blob_top_cm, 7.4);
02490 static double const center_blob_top_cm = CONST(center_blob_top_cm, 8.18);
02491 static double const cross_y_king_cm = CONST(cross_y_king_cm, 9.17);
02492 static double const cross_top_cm = CONST(cross_top_cm, 9.86);
02493 static double const height_king_cm = CONST(height_king_cm, 10.86);
02494
02495 static double const mid_x_king_cm = CONST(mid_x_king_cm, width_king_cm / 2);
02496 static double const mid_y_king_cm = CONST(mid_y_king_cm, height_king_cm / 2);
02497
02498
02499 static double const blob_left = CONST(blob_left, (blob_left_cm - mid_x_king_cm) / width_king_cm);
02500 static double const band_edge_left = CONST(band_edge_left, (band_edge_left_cm - mid_x_king_cm) / width_king_cm);
02501 static double const band_left = CONST(band_left, (band_left_cm - mid_x_king_cm) / width_king_cm);
02502 static double const inside_left = CONST(inside_left, (inside_left_cm - mid_x_king_cm) / width_king_cm);
02503 static double const center_blob_left = CONST(center_blob_left, (center_blob_left_cm - mid_x_king_cm) / width_king_cm);
02504 static double const cross_left = CONST(cross_left, (cross_left_cm - mid_x_king_cm) / width_king_cm);
02505 static double const bottom_king = CONST(bottom_king, (mid_y_king_cm - bottom_king_cm) / height_king_cm);
02506 static double const band_line_top = CONST(band_line_top, (mid_y_king_cm - band_line_top_cm) / height_king_cm);
02507 static double const band_top_king = CONST(band_top_king, (mid_y_king_cm - band_top_king_cm) / height_king_cm);
02508 static double const center_y = CONST(center_y, (mid_y_king_cm - center_y_cm) / height_king_cm);
02509 static double const blob_top = CONST(blob_top, (mid_y_king_cm - blob_top_cm) / height_king_cm);
02510 static double const center_blob_top = CONST(center_blob_top, (mid_y_king_cm - center_blob_top_cm) / height_king_cm);
02511 static double const cross_y_king = CONST(cross_y_king, (mid_y_king_cm - cross_y_king_cm) / height_king_cm);
02512 static double const cross_top = CONST(cross_top, (mid_y_king_cm - cross_top_cm) / height_king_cm);
02513
02514
02515 static double const inside_radius_king = CONST(inside_radius_king, -inside_left);
02516 static double const inside_scale_king = CONST(inside_scale_king, 0.180132);
02517 static double const band_top_radius = CONST(band_top_radius, -band_edge_left);
02518 static double const band_top_scale = CONST(band_top_scale, inside_scale_king);
02519 static double const band_top_y = CONST(band_top_y, band_top_king + band_top_radius * band_top_scale);
02520 static double const cos_alpha = CONST(cos_alpha, band_left / band_edge_left);
02521 static double const alpha = CONST(alpha, acos(cos_alpha));
02522 static double const band_bottom_scale = CONST(band_bottom_scale, inside_scale_king);
02523 static double const band_bottom_radius = CONST(band_bottom_radius, band_top_radius);
02524 static double const band_bottom_y = CONST(band_bottom_y, bottom_king - band_bottom_radius * band_bottom_scale);
02525 static double const dx = CONST(dx, band_top_radius * (1.0 - cos_alpha));
02526 static double const band_line_scale = CONST(band_line_scale, band_top_scale);
02527 static double const band_line_radius = CONST(band_line_radius, band_top_radius - dx);
02528 static double const band_line_y = CONST(band_line_y, band_line_top + band_line_radius * band_line_scale);
02529 static double const blob_radius = CONST(blob_radius, 0.7071067 * (blob_left + band_top_y - band_left - blob_top));
02530 static double const blob_x = CONST(blob_x, blob_left + blob_radius);
02531 static double const blob_y = CONST(blob_y, blob_top + blob_radius);
02532 static double const center_blob_radius = CONST(center_blob_radius, -center_blob_left);
02533 static double const center_blob_y = CONST(center_blob_y, center_blob_top + center_blob_radius);
02534
02535 static double const adjusted_center_blob_radius = CONST(adjusted_center_blob_radius, center_blob_radius + 0.01);
02536 static double const beta_king = CONST(beta_king, asin(adjusted_center_blob_radius / (center_y - center_blob_y)));
02537 static double const center2_y = CONST(center2_y, blob_y - blob_x - 1.4142136 * blob_radius);
02538
02539 CwChessboardPrivate* priv = chessboard->priv;
02540
02541 cairo_save(cr);
02542 cairo_translate(cr, x, y);
02543 cairo_scale(cr, scale, scale);
02544 cairo_set_line_width(cr, black_line_width);
02545
02546
02547 cairo_move_to(cr, band_left, band_top_y);
02548 cairo_arc(cr, blob_x, blob_y, blob_radius, 0.75 * M_PI, 1.75 * M_PI);
02549 cairo_line_to(cr, 0.0, center2_y);
02550
02551
02552 cairo_arc(cr, -blob_x, blob_y, blob_radius, -0.75 * M_PI, 0.25 * M_PI);
02553 cairo_line_to(cr, -band_left, band_top_y);
02554
02555 set_fill_color(cr, priv, white);
02556 cairo_fill_preserve(cr);
02557
02558
02559 cairo_move_to(cr, 0.0, band_top_y);
02560 cairo_line_to(cr, 0.0, center_y);
02561
02562 if (white)
02563 set_line_color(cr, priv, white);
02564 cairo_stroke(cr);
02565
02566
02567 cairo_move_to(cr, 0.0, center_y);
02568 cairo_arc(cr, 0.0, center_blob_y, adjusted_center_blob_radius, M_PI - beta_king, beta_king);
02569 cairo_close_path(cr);
02570
02571 if (white)
02572 set_fill_color(cr, priv, white);
02573 cairo_fill_preserve(cr);
02574 if (white)
02575 set_line_color(cr, priv, white);
02576 cairo_stroke(cr);
02577
02578
02579 cairo_move_to(cr, 0.0, center_blob_y - adjusted_center_blob_radius);
02580 cairo_line_to(cr, 0.0, cross_top);
02581 cairo_move_to(cr, cross_left, cross_y_king);
02582 cairo_line_to(cr, -cross_left, cross_y_king);
02583 cairo_stroke(cr);
02584
02585
02586 cairo_save(cr);
02587 cairo_translate(cr, 0.0, band_top_y);
02588 cairo_scale(cr, 1.0, band_top_scale);
02589 cairo_arc(cr, 0.0, 0.0, band_top_radius, M_PI - alpha, 2 * M_PI + alpha);
02590 cairo_restore(cr);
02591
02592
02593 cairo_line_to(cr, -band_left, band_line_y);
02594
02595
02596 cairo_save(cr);
02597 cairo_translate(cr, 0.0, band_bottom_y);
02598 cairo_scale(cr, 1.0, band_bottom_scale);
02599 cairo_arc(cr, 0.0, 0.0, band_bottom_radius, 0.0, M_PI);
02600 cairo_restore(cr);
02601
02602
02603 cairo_line_to(cr, band_left, band_line_y);
02604
02605
02606 cairo_close_path(cr);
02607
02608 cairo_path_t* path = cairo_copy_path(cr);
02609
02610 if (white)
02611 set_fill_color(cr, priv, white);
02612 cairo_fill(cr);
02613
02614
02615 cairo_save(cr);
02616 cairo_translate(cr, 0.0, band_line_y);
02617 cairo_scale(cr, 1.0, band_line_scale);
02618 cairo_arc(cr, 0.0, 0.0, band_line_radius, -M_PI, 0.0);
02619 cairo_restore(cr);
02620
02621 cairo_new_sub_path(cr);
02622
02623
02624 cairo_save(cr);
02625 cairo_translate(cr, 0.0, band_bottom_y + band_bottom_radius * band_bottom_scale - inside_radius_king * inside_scale_king);
02626 cairo_scale(cr, 1.0, inside_scale_king);
02627 if (white)
02628 cairo_arc(cr, 0.0, 0.0, inside_radius_king, -M_PI, M_PI);
02629 else
02630 cairo_arc(cr, 0.0, 0.0, inside_radius_king, -M_PI - alpha, alpha);
02631 cairo_restore(cr);
02632
02633 set_line_color(cr, priv, white);
02634 if (white)
02635 cairo_stroke(cr);
02636 else
02637 {
02638 cairo_set_line_width(cr, white_line_width);
02639 cairo_stroke(cr);
02640 cairo_set_line_width(cr, black_line_width);
02641 }
02642
02643 cairo_append_path(cr, path);
02644 if (!white)
02645 set_fill_color(cr, priv, white);
02646 cairo_stroke(cr);
02647
02648 cairo_path_destroy(path);
02649
02650 if (!white)
02651 {
02652
02653
02654 static double const av_line_width = CONST(av_line_width, 0.5 * (black_line_width + white_line_width));
02655 static double const da = CONST(da, av_line_width / band_top_radius);
02656 static double const dy = CONST(dy, av_line_width * tan(0.5 * beta_king));
02657
02658 cairo_save(cr);
02659 cairo_translate(cr, 0.0, band_top_y);
02660 cairo_scale(cr, 1.0, band_top_scale);
02661 cairo_arc_negative(cr, 0.0, 0.0, band_top_radius, -0.5 * M_PI - da, M_PI + alpha + da);
02662 cairo_restore(cr);
02663
02664 cairo_arc(cr, blob_x, blob_y, blob_radius - av_line_width, 0.75 * M_PI, 1.75 * M_PI);
02665
02666 static double const center2b_y = CONST(center2b_y, center2_y + av_line_width * 1.4142136);
02667 static double const sin_beta = CONST(sin_beta, adjusted_center_blob_radius / (center_y - center_blob_y));
02668 static double const x_king = CONST(x_king, sin_beta * (center_y + av_line_width / sin_beta - center2b_y) / sin(0.25 * M_PI - beta_king));
02669 static double const y_king = CONST(y_king, center2b_y - x_king);
02670
02671 cairo_line_to(cr, -x_king, y_king);
02672 cairo_line_to(cr, -av_line_width, center_y + dy);
02673
02674 cairo_close_path(cr);
02675
02676 cairo_new_sub_path(cr);
02677
02678 cairo_save(cr);
02679 cairo_translate(cr, 0.0, band_top_y);
02680 cairo_scale(cr, 1.0, band_top_scale);
02681 cairo_arc_negative(cr, 0.0, 0.0, band_top_radius, -alpha - da, -0.5 * M_PI + da);
02682 cairo_restore(cr);
02683
02684 cairo_line_to(cr, av_line_width, center_y + dy);
02685 cairo_line_to(cr, x_king, y_king);
02686
02687 cairo_arc(cr, -blob_x, blob_y, blob_radius - av_line_width, -0.75 * M_PI, 0.25 * M_PI);
02688
02689 cairo_close_path(cr);
02690
02691 cairo_new_sub_path(cr);
02692
02693 cairo_move_to(cr, 0.0, center_y - av_line_width / sin_beta);
02694 cairo_arc(cr, 0.0, center_blob_y, adjusted_center_blob_radius - av_line_width, M_PI - beta_king, beta_king);
02695
02696 cairo_close_path(cr);
02697
02698 set_line_color(cr, priv, white);
02699 cairo_set_line_width(cr, white_line_width);
02700 cairo_stroke(cr);
02701 }
02702
02703 cairo_restore(cr);
02704 }
02705
02706 void cw_chessboard_draw_queen(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
02707 {
02708
02709 static double const width_queen_cm = CONST(width_queen_cm, 5.34);
02710 static double const inside_width_cm = CONST(inside_width_cm, 2.97);
02711 static double const band1_width_cm = CONST(band1_width_cm, 2.59);
02712 static double const crown_bottom_width_cm = CONST(crown_bottom_width_cm, 3.31);
02713 static double const height_queen_cm = CONST(height_queen_cm, 5.39);
02714 static double const bottom_queen_cm = CONST(bottom_queen_cm, 0.5);
02715 static double const inside_height_cm = CONST(inside_height_cm, 0.54);
02716 static double const band1_height_cm = CONST(band1_height_cm, 0.47);
02717 static double const band2_height_cm = CONST(band2_height_cm, 0.43);
02718 static double const tooth_outside_cm = CONST(tooth_outside_cm, 1.83);
02719 static double const tooth_inside_cm = CONST(tooth_inside_cm, 2.20);
02720 static double const tooth_inside2_cm = CONST(tooth_inside2_cm, 2.36);
02721 static double const ball_outside_diameter_cm = CONST(ball_outside_diameter_cm, 0.6);
02722 static double const ball_top1_cm = CONST(ball_top1_cm, 4.31);
02723 static double const ball_right1_cm = CONST(ball_right1_cm, 0.90);
02724 static double const ball_top2_cm = CONST(ball_top2_cm, 4.80);
02725 static double const ball_right2_cm = CONST(ball_right2_cm, 1.88);
02726 static double const tooth3_x_cm = CONST(tooth3_x_cm, 2.25);
02727
02728 static double const mid_x_queen_cm = CONST(mid_x_queen_cm, width_queen_cm / 2);
02729 static double const mid_y_queen_cm = CONST(mid_y_queen_cm, height_queen_cm / 2);
02730
02731
02732 static double const inside_width = CONST(inside_width, inside_width_cm / width_queen_cm);
02733 static double const band1_width = CONST(band1_width, band1_width_cm / width_queen_cm);
02734 static double const crown_bottom_width = CONST(crown_bottom_width, crown_bottom_width_cm / width_queen_cm);
02735 static double const bottom_queen = CONST(bottom_queen, (mid_y_queen_cm - bottom_queen_cm) / height_queen_cm);
02736 static double const inside_height = CONST(inside_height, inside_height_cm / height_queen_cm);
02737 static double const band1_height = CONST(band1_height, band1_height_cm / height_queen_cm);
02738 static double const band2_height = CONST(band2_height, band2_height_cm / height_queen_cm);
02739 static double const tooth_outside = CONST(tooth_outside, (mid_y_queen_cm - tooth_outside_cm) / height_queen_cm);
02740 static double const tooth_inside = CONST(tooth_inside, (mid_y_queen_cm - tooth_inside_cm) / height_queen_cm);
02741 static double const tooth_inside2 = CONST(tooth_inside2, (mid_y_queen_cm - tooth_inside2_cm) / height_queen_cm);
02742 static double const ball_outside_diameter = CONST(ball_outside_diameter, ball_outside_diameter_cm / height_queen_cm);
02743 static double const ball_top1 = CONST(ball_top1, (mid_y_queen_cm - ball_top1_cm) / height_queen_cm);
02744 static double const ball_right1 = CONST(ball_right1, (ball_right1_cm - mid_x_queen_cm) / width_queen_cm);
02745 static double const ball_top2 = CONST(ball_top2, (mid_y_queen_cm - ball_top2_cm) / height_queen_cm);
02746 static double const ball_right2 = CONST(ball_right2, (ball_right2_cm - mid_x_queen_cm) / width_queen_cm);
02747 static double const tooth3_x = CONST(tooth3_x, (tooth3_x_cm - mid_x_queen_cm) / width_queen_cm);
02748
02749
02750 static double const inside_radius_queen = CONST(inside_radius_queen, inside_width / 2);
02751 static double const inside_scale_queen = CONST(inside_scale_queen, inside_height / inside_width);
02752 static double const inside_y_queen = CONST(inside_y_queen, bottom_queen - inside_radius_queen * inside_scale_queen);
02753 static double const band1_radius = CONST(band1_radius, band1_width / 2);
02754 static double const band1_scale = CONST(band1_scale, inside_scale_queen);
02755 static double const band1_y = CONST(band1_y, bottom_queen - inside_height - band1_height + band1_radius * band1_scale);
02756 static double const crown_bottom_left = CONST(crown_bottom_left, -crown_bottom_width / 2);
02757 static double const band2_radius = CONST(band2_radius ,band1_radius +
02758 (-band1_radius - crown_bottom_left) * band2_height / (band1_y - tooth_outside));
02759 static double const band2_scale = CONST(band2_scale, band1_scale);
02760 static double const band2_y = CONST(band2_y, bottom_queen - inside_height - band1_height - band2_height + band2_radius * band2_scale);
02761 static double const ball1_x = CONST(ball1_x, ball_right1 - ball_outside_diameter / 2);
02762 static double const ball2_x = CONST(ball2_x, ball_right2 - ball_outside_diameter / 2);
02763 static double const ball1_y = CONST(ball1_y, ball_top1 + ball_outside_diameter / 2);
02764 static double const ball2_y = CONST(ball2_y, ball_top2 + ball_outside_diameter / 2);
02765 static double const ball_radius_queen = CONST(ball_radius_queen, (ball_outside_diameter - black_line_width) / 2);
02766
02767
02768
02769
02770
02771 static double const ball_center_y = CONST(ball_center_y,
02772 0.5 * (ball2_x * ball2_x + ball2_y * ball2_y - ball1_x * ball1_x - ball1_y * ball1_y) / (ball2_y - ball1_y));
02773 static double const ball3_y = CONST(ball3_y,
02774 ball_center_y - sqrt(ball1_x * ball1_x + (ball1_y - ball_center_y) * (ball1_y - ball_center_y)));
02775
02776 static double const ball1_angle = CONST(ball1_angle, atan((0.5 * (crown_bottom_left + ball2_x) - ball1_x) / (tooth_outside - ball1_y)));
02777 static double const tooth1_x = CONST(tooth1_x, ball1_x + ball_radius_queen * sin(ball1_angle));
02778 static double const tooth2_x = CONST(tooth2_x, ball2_x);
02779 static double const tooth1_top = CONST(tooth1_top, ball1_y + ball_radius_queen * cos(ball1_angle));
02780 static double const tooth2_top = CONST(tooth2_top, ball2_y + ball_radius_queen);
02781 static double const tooth3_top = CONST(tooth3_top, ball3_y + ball_radius_queen);
02782
02783 CwChessboardPrivate* priv = chessboard->priv;
02784
02785 cairo_save(cr);
02786 cairo_translate(cr, x, y);
02787 cairo_scale(cr, scale, scale);
02788 cairo_set_line_width(cr, black_line_width);
02789
02790
02791
02792 for (int stroke = 0; stroke < 2; ++stroke)
02793 {
02794
02795 cairo_move_to(cr, -tooth1_x, tooth1_top);
02796 cairo_line_to(cr, -crown_bottom_left, tooth_outside);
02797 cairo_line_to(cr, band1_radius, band1_y);
02798
02799
02800
02801 cairo_save(cr);
02802 cairo_translate(cr, 0.0, inside_y_queen);
02803 cairo_scale(cr, 1.0, inside_scale_queen);
02804 cairo_arc(cr, 0.0, 0.0, inside_radius_queen, 0.0, M_PI);
02805 cairo_restore(cr);
02806
02807
02808 cairo_line_to(cr, -band1_radius, band1_y);
02809 cairo_line_to(cr, crown_bottom_left, tooth_outside);
02810 cairo_line_to(cr, tooth1_x, tooth1_top);
02811
02812
02813 if (stroke)
02814 {
02815 cairo_new_sub_path(cr);
02816 cairo_move_to(cr, tooth1_x, tooth1_top);
02817 }
02818
02819
02820 cairo_line_to(cr, tooth2_x, tooth_inside);
02821
02822
02823 cairo_line_to(cr, tooth2_x, tooth2_top);
02824
02825 if (stroke)
02826 {
02827 cairo_new_sub_path(cr);
02828 cairo_move_to(cr, tooth2_x, tooth2_top);
02829 }
02830
02831
02832 cairo_line_to(cr, tooth3_x, tooth_inside2);
02833
02834
02835 cairo_line_to(cr, 0.0, tooth3_top);
02836
02837 if (stroke)
02838 {
02839 cairo_new_sub_path(cr);
02840 cairo_move_to(cr, 0.0, tooth3_top);
02841 }
02842
02843
02844 cairo_line_to(cr, -tooth3_x, tooth_inside2);
02845
02846
02847 cairo_line_to(cr, -tooth2_x, tooth2_top);
02848
02849 if (stroke)
02850 {
02851 cairo_new_sub_path(cr);
02852 cairo_move_to(cr, -tooth2_x, tooth2_top);
02853 }
02854
02855
02856 cairo_line_to(cr, -tooth2_x, tooth_inside);
02857
02858
02859 cairo_line_to(cr, -tooth1_x, tooth1_top);
02860
02861 if (stroke)
02862 {
02863 if (white)
02864 set_line_color(cr, priv, white);
02865 else
02866 set_fill_color(cr, priv, white);
02867 cairo_stroke(cr);
02868 }
02869 else
02870 {
02871 set_fill_color(cr, priv, white);
02872 cairo_fill(cr);
02873
02874
02875 cairo_save(cr);
02876 cairo_translate(cr, 0.0, inside_y_queen);
02877 cairo_scale(cr, 1.0, inside_scale_queen);
02878 cairo_arc(cr, 0.0, 0.0, inside_radius_queen, -M_PI, 0.0);
02879 cairo_restore(cr);
02880
02881 cairo_new_sub_path(cr);
02882
02883
02884 cairo_save(cr);
02885 cairo_translate(cr, 0.0, band1_y);
02886 cairo_scale(cr, 1.0, band1_scale);
02887 cairo_arc(cr, 0.0, 0.0, band1_radius, -M_PI, 0.0);
02888 cairo_restore(cr);
02889
02890 set_line_color(cr, priv, white);
02891 if (white)
02892 cairo_stroke(cr);
02893 else
02894 {
02895 cairo_set_line_width(cr, white_line_width);
02896 cairo_stroke(cr);
02897 cairo_set_line_width(cr, black_line_width);
02898 }
02899 }
02900 }
02901
02902
02903
02904 cairo_arc(cr, ball1_x, ball1_y, ball_radius_queen, -M_PI, M_PI);
02905
02906 if (white)
02907 set_fill_color(cr, priv, white);
02908 cairo_fill_preserve(cr);
02909 if (white)
02910 set_line_color(cr, priv, white);
02911 cairo_stroke(cr);
02912
02913 cairo_arc(cr, ball2_x, ball2_y, ball_radius_queen, -M_PI, M_PI);
02914
02915 if (white)
02916 set_fill_color(cr, priv, white);
02917 cairo_fill_preserve(cr);
02918 if (white)
02919 set_line_color(cr, priv, white);
02920 cairo_stroke(cr);
02921
02922 cairo_arc(cr, 0.0, ball3_y, ball_radius_queen, -M_PI, M_PI);
02923
02924 if (white)
02925 set_fill_color(cr, priv, white);
02926 cairo_fill_preserve(cr);
02927 if (white)
02928 set_line_color(cr, priv, white);
02929 cairo_stroke(cr);
02930
02931 cairo_arc(cr, -ball2_x, ball2_y, ball_radius_queen, -M_PI, M_PI);
02932
02933 if (white)
02934 set_fill_color(cr, priv, white);
02935 cairo_fill_preserve(cr);
02936 if (white)
02937 set_line_color(cr, priv, white);
02938 cairo_stroke(cr);
02939
02940 cairo_arc(cr, -ball1_x, ball1_y, ball_radius_queen, -M_PI, M_PI);
02941
02942 if (white)
02943 set_fill_color(cr, priv, white);
02944 cairo_fill_preserve(cr);
02945 if (white)
02946 set_line_color(cr, priv, white);
02947 cairo_stroke(cr);
02948
02949 if (white)
02950 {
02951
02952
02953 static double const y0_queen = CONST(y0_queen, 0.0952);
02954
02955 static double const ym = CONST(ym, 0.0331);
02956
02957
02958 static double const x0_queen = CONST(x0_queen, tooth1_x + (y0_queen - tooth1_top) * (crown_bottom_left - tooth1_x) / (tooth_outside - tooth1_top));
02959
02960 static double const tilt_angle = CONST(tilt_angle, atan((ym - y0_queen) / x0_queen));
02961
02962
02963
02964 static double const beta_queen = CONST(beta_queen, 1.202);
02965
02966 static double const len = CONST(len, 0.1728);
02967
02968 static double const py = CONST(py, len * cos(beta_queen));
02969 static double const y0_plus_py_cos_tilt_angle = CONST(y0_plus_py_cos_tilt_angle, y0_queen + py * cos(tilt_angle));
02970 static double const sin_tilt_angle = CONST(sin_tilt_angle, sin(tilt_angle));
02971
02972 static double px_offset = CONST(px_offset, len * sin(beta_queen));
02973
02974 cairo_move_to(cr, crown_bottom_left, tooth_outside);
02975 cairo_line_to(cr, x0_queen, y0_queen);
02976
02977
02978 int const N = 4;
02979 for (int i = 0; i < N; ++i)
02980 {
02981 double const alpha = i * M_PI / N;
02982
02983 double px2 = x0_queen * cos(alpha + px_offset);
02984 double pz2 = - x0_queen * sin(alpha + px_offset);
02985 double px3 = x0_queen * cos(alpha + M_PI / N - px_offset);
02986 double pz3 = - x0_queen * sin(alpha + M_PI / N - px_offset);
02987 double px4 = x0_queen * cos(alpha + M_PI / N);
02988 double pz4 = - x0_queen * sin(alpha + M_PI / N);
02989
02990
02991 double tpy2 = y0_plus_py_cos_tilt_angle - pz2 * sin_tilt_angle;
02992 double tpy3 = y0_plus_py_cos_tilt_angle - pz3 * sin_tilt_angle;
02993 double tpy4 = y0_queen - pz4 * sin_tilt_angle;
02994 cairo_curve_to(cr, px2, tpy2, px3, tpy3, px4, tpy4);
02995 }
02996
02997 cairo_line_to(cr, -crown_bottom_left, tooth_outside);
02998 }
02999
03000
03001 cairo_save(cr);
03002 cairo_translate(cr, 0.0, band2_y);
03003 cairo_scale(cr, 1.0, band2_scale);
03004 cairo_arc_negative(cr, 0.0, 0.0, band2_radius, -0.15, -M_PI + 0.15);
03005 cairo_restore(cr);
03006
03007 if (white)
03008 {
03009 cairo_close_path(cr);
03010 set_fill_color(cr, priv, white);
03011 cairo_fill_preserve(cr);
03012 }
03013 else
03014 cairo_set_line_width(cr, white_line_width);
03015 set_line_color(cr, priv, white);
03016 cairo_stroke(cr);
03017
03018 cairo_restore(cr);
03019 }
03020
03021 void cw_chessboard_draw_rook(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
03022 {
03023
03024 static double const width_rook_cm = CONST(width_rook_cm, 5.33);
03025 static double const foot_left_cm = CONST(foot_left_cm, 0.90);
03026 static double const base_left_cm = CONST(base_left_cm, 1.26);
03027 static double const tower_left_cm = CONST(tower_left_cm, 1.64);
03028 static double const opening_left_cm = CONST(opening_left_cm, 1.795);
03029 static double const opening_right_cm = CONST(opening_right_cm, 2.315);
03030 static double const height_rook_cm = CONST(height_rook_cm, 5.30);
03031 static double const bottom_rook_cm = CONST(bottom_rook_cm, 0.58);
03032 static double const foot_top_cm = CONST(foot_top_cm, 0.95);
03033 static double const base_top_cm = CONST(base_top_cm, 1.41);
03034 static double const tower_bottom_cm = CONST(tower_bottom_cm, 1.76);
03035 static double const tower_top_cm = CONST(tower_top_cm, 3.43);
03036 static double const top_bottom_cm = CONST(top_bottom_cm, 3.81);
03037 static double const opening_bottom_cm = CONST(opening_bottom_cm, 4.25);
03038
03039
03040
03041 static double const foot_left = CONST(foot_left, -0.5 + foot_left_cm / width_rook_cm + 0.5 * black_line_width);
03042 static double const base_left = CONST(base_left, -0.5 + base_left_cm / width_rook_cm + 0.5 * black_line_width);
03043 static double const tower_left = CONST(tower_left, -0.5 + tower_left_cm / width_rook_cm + 0.5 * black_line_width);
03044 static double const opening_left = CONST(opening_left, -0.5 + opening_left_cm / width_rook_cm + 0.5 * black_line_width);
03045 static double const opening_right = CONST(opening_right, -0.5 + opening_right_cm / width_rook_cm + 0.5 * black_line_width);
03046 static double const bottom_rook = CONST(bottom_rook, 0.5 - bottom_rook_cm / height_rook_cm - 0.5 * black_line_width);
03047 static double const foot_top = CONST(foot_top, 0.5 - foot_top_cm / height_rook_cm - 0.5 * black_line_width);
03048 static double const base_top = CONST(base_top, 0.5 - base_top_cm / height_rook_cm - 0.5 * black_line_width);
03049 static double const tower_bottom = CONST(tower_bottom, 0.5 - tower_bottom_cm / height_rook_cm - 0.5 * black_line_width);
03050 static double const tower_top = CONST(tower_top, 0.5 - tower_top_cm / height_rook_cm - 0.5 * black_line_width);
03051 static double const top_bottom = CONST(top_bottom, 0.5 - top_bottom_cm / height_rook_cm - 0.5 * black_line_width);
03052 static double const opening_bottom = CONST(opening_bottom, 0.5 - opening_bottom_cm / height_rook_cm - 0.5 * black_line_width);
03053
03054
03055 static double const top_top = CONST(top_top, -bottom_rook);
03056
03057
03058 double const inner_line_width = white ? black_line_width : snap_line_width(white_line_width, scale);
03059 double const bottom_sn = snap_bottom(bottom_rook, y, scale, black_line_width);
03060 double const foot_top_sn = snap_bottom(foot_top, y, scale, inner_line_width);
03061 double const base_top_sn = snap_bottom(base_top, y, scale, inner_line_width);
03062 double const tower_bottom_sn = snap_bottom(tower_bottom, y, scale, inner_line_width);
03063 double const tower_top_sn = snap_top(tower_top, y, scale, inner_line_width);
03064 double const top_bottom_sn = snap_top(top_bottom, y, scale, inner_line_width);
03065 double const opening_bottom_sn = snap_top(opening_bottom, y, scale, black_line_width);
03066 double const top_top_sn = snap_top(top_top, y, scale, black_line_width);
03067
03068 CwChessboardPrivate* priv = chessboard->priv;
03069
03070 cairo_save(cr);
03071 cairo_translate(cr, x, y);
03072 cairo_scale(cr, scale, scale);
03073 cairo_set_line_width(cr, black_line_width);
03074
03075
03076 cairo_move_to(cr, foot_left, bottom_sn);
03077 cairo_line_to(cr, foot_left, foot_top_sn);
03078 cairo_line_to(cr, base_left, foot_top_sn);
03079 cairo_line_to(cr, base_left, base_top_sn);
03080 cairo_line_to(cr, tower_left, tower_bottom_sn);
03081 cairo_line_to(cr, tower_left, tower_top_sn);
03082 cairo_line_to(cr, base_left, top_bottom_sn);
03083 cairo_line_to(cr, base_left, top_top_sn);
03084
03085
03086 cairo_line_to(cr, opening_left, top_top_sn);
03087 cairo_line_to(cr, opening_left, opening_bottom_sn);
03088 cairo_line_to(cr, opening_right, opening_bottom_sn);
03089 cairo_line_to(cr, opening_right, top_top_sn);
03090 cairo_line_to(cr, -opening_right, top_top_sn);
03091 cairo_line_to(cr, -opening_right, opening_bottom_sn);
03092 cairo_line_to(cr, -opening_left, opening_bottom_sn);
03093 cairo_line_to(cr, -opening_left, top_top_sn);
03094 cairo_line_to(cr, -base_left, top_top_sn);
03095
03096
03097 cairo_line_to(cr, -base_left, top_bottom_sn);
03098 cairo_line_to(cr, -tower_left, tower_top_sn);
03099 cairo_line_to(cr, -tower_left, tower_bottom_sn);
03100 cairo_line_to(cr, -base_left, base_top_sn);
03101 cairo_line_to(cr, -base_left, foot_top_sn);
03102 cairo_line_to(cr, -foot_left, foot_top_sn);
03103 cairo_line_to(cr, -foot_left, bottom_sn);
03104
03105
03106 cairo_close_path(cr);
03107 cairo_path_t* path = cairo_copy_path(cr);
03108
03109 set_fill_color(cr, priv, white);
03110 cairo_fill(cr);
03111
03112
03113 cairo_move_to(cr, base_left + 0.5 * black_line_width, foot_top_sn);
03114 cairo_line_to(cr, -base_left - 0.5 * black_line_width, foot_top_sn);
03115 cairo_new_sub_path(cr);
03116 cairo_move_to(cr, base_left, base_top_sn);
03117 cairo_line_to(cr, -base_left, base_top_sn);
03118 cairo_new_sub_path(cr);
03119 cairo_move_to(cr, tower_left + (white ? 0.0 : (0.5 * black_line_width)), tower_bottom_sn);
03120 cairo_line_to(cr, -tower_left - (white ? 0.0 : (0.5 * black_line_width)), tower_bottom_sn);
03121 cairo_new_sub_path(cr);
03122 cairo_move_to(cr, tower_left + (white ? 0.0 : (0.5 * black_line_width)), tower_top_sn);
03123 cairo_line_to(cr, -tower_left - (white ? 0.0 : (0.5 * black_line_width)), tower_top_sn);
03124 cairo_new_sub_path(cr);
03125 cairo_move_to(cr, base_left + black_line_width * 0.5, top_bottom_sn);
03126 cairo_line_to(cr, -base_left - black_line_width * 0.5, top_bottom_sn);
03127
03128 set_line_color(cr, priv, white);
03129 if (white)
03130 cairo_stroke(cr);
03131 else
03132 {
03133 cairo_set_line_width(cr, inner_line_width);
03134 cairo_stroke(cr);
03135 cairo_set_line_width(cr, black_line_width);
03136 }
03137
03138 cairo_append_path(cr, path);
03139 if (!white)
03140 set_fill_color(cr, priv, white);
03141 cairo_stroke(cr);
03142
03143 cairo_path_destroy(path);
03144
03145 cairo_restore(cr);
03146 }
03147
03148 void cw_chessboard_draw_bishop(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
03149 {
03150
03151 static double const width_bishop_cm = CONST(width_bishop_cm, 5.34);
03152 static double const ribbon_width_cm = CONST(ribbon_width_cm, 0.49);
03153 static double const ribbon_bottom_left_cm = CONST(ribbon_bottom_left_cm, 0.72);
03154 static double const ribbon_top_left_cm = CONST(ribbon_top_left_cm, 2.28);
03155 static double const inside_outer_diameter_cm = CONST(inside_outer_diameter_cm, 2.0);
03156 static double const circle_diameter_cm = CONST(circle_diameter_cm, 2.44);
03157 static double const cross_width_cm = CONST(cross_width_cm, 0.93);
03158 static double const ball_outer_diameter_cm = CONST(ball_outer_diameter_cm, 0.81);
03159 static double const ball_inner_diameter_cm = CONST(ball_inner_diameter_cm, 0.41);
03160 static double const circle_start_angle = CONST(circle_start_angle, 0.767);
03161 static double const ribbon_end_angle = CONST(ribbon_end_angle, 1.097);
03162 static double const height_bishop_cm = CONST(height_bishop_cm, 5.44);
03163 static double const ribbon_bottom_y1_cm = CONST(ribbon_bottom_y1_cm, 0.52);
03164 static double const ribbon_bottom_y2_cm = CONST(ribbon_bottom_y2_cm, 0.76);
03165 static double const ribbon_bottom_y3_cm = CONST(ribbon_bottom_y3_cm, 0.55);
03166 static double const ribbon_top_y1_cm = CONST(ribbon_top_y1_cm, 0.99);
03167 static double const ribbon_top_y2_cm = CONST(ribbon_top_y2_cm, 1.25);
03168 static double const ribbon_inside_y_cm = CONST(ribbon_inside_y_cm, 0.93);
03169 static double const inside_bottom_cm = CONST(inside_bottom_cm, 1.34);
03170 static double const inside_top_cm = CONST(inside_top_cm, 1.86);
03171 static double const band_top_bishop_cm = CONST(band_top_bishop_cm, 2.34);
03172 static double const circle_y_cm = CONST(circle_y_cm, 3.11);
03173 static double const cross_y_bishop_cm = CONST(cross_y_bishop_cm, 3.24);
03174 static double const point_y_cm = CONST(point_y_cm, 4.47);
03175 static double const ball_y_cm = CONST(ball_y_cm, 4.675);
03176 static double const sp1_x_cm = CONST(sp1_x_cm, 2.1);
03177 static double const sp1_y_cm = CONST(sp1_y_cm, 3.95);
03178 static double const ribbon_bottom_x1_cm = CONST(ribbon_bottom_x1_cm, 3.34);
03179 static double const ribbon_bottom_x2_cm = CONST(ribbon_bottom_x2_cm, 4.1);
03180 static double const ribbon_top_x1_cm = CONST(ribbon_top_x1_cm, 3.54);
03181 static double const ribbon_top_x2_cm = CONST(ribbon_top_x2_cm, 4.24);
03182
03183
03184 static double const ribbon_width = CONST(ribbon_width, ribbon_width_cm / height_bishop_cm);
03185 static double const ribbon_bottom_left = CONST(ribbon_bottom_left, -0.5 + ribbon_bottom_left_cm / width_bishop_cm);
03186 static double const ribbon_bottom_x1 = CONST(ribbon_bottom_x1, -0.5 + ribbon_bottom_x1_cm / width_bishop_cm);
03187 static double const ribbon_bottom_x2 = CONST(ribbon_bottom_x2, -0.5 + ribbon_bottom_x2_cm / width_bishop_cm);
03188 static double const ribbon_top_x1 = CONST(ribbon_top_x1, -0.5 + ribbon_top_x1_cm / width_bishop_cm);
03189 static double const ribbon_top_x2 = CONST(ribbon_top_x2, -0.5 + ribbon_top_x2_cm / width_bishop_cm);
03190 static double const ribbon_top_left = CONST(ribbon_top_left, -0.5 + ribbon_top_left_cm / width_bishop_cm);
03191 static double const inside_radius_bishop = CONST(inside_radius_bishop, 0.5 * (inside_outer_diameter_cm / width_bishop_cm - black_line_width));
03192 static double const circle_radius = CONST(circle_radius, 0.5 * circle_diameter_cm / width_bishop_cm);
03193 static double const cross_leg = CONST(cross_leg, 0.5 * cross_width_cm / width_bishop_cm);
03194 static double const ball_radius_bishop = CONST(ball_radius_bishop, 0.25 * (ball_outer_diameter_cm + ball_inner_diameter_cm) / width_bishop_cm);
03195 static double const ball_line_width = CONST(ball_line_width, black_line_width);
03196 static double const ribbon_bottom_y1 = CONST(ribbon_bottom_y1, 0.5 - ribbon_bottom_y1_cm / height_bishop_cm - 0.5 * black_line_width);
03197 static double const ribbon_bottom_y2 = CONST(ribbon_bottom_y2, 0.5 - ribbon_bottom_y2_cm / height_bishop_cm + 0.5 * black_line_width);
03198 static double const ribbon_bottom_y3 = CONST(ribbon_bottom_y3, 0.5 - ribbon_bottom_y3_cm / height_bishop_cm);
03199 static double const ribbon_inside_y = CONST(ribbon_inside_y, 0.5 - ribbon_inside_y_cm / height_bishop_cm);
03200 static double const ribbon_top_y1 = CONST(ribbon_top_y1, 0.5 - ribbon_top_y1_cm / height_bishop_cm - 0.5 * black_line_width);
03201 static double const ribbon_top_y2 = CONST(ribbon_top_y2, 0.5 - ribbon_top_y2_cm / height_bishop_cm + 0.5 * black_line_width);
03202 static double const inside_scale_bishop = CONST(inside_scale_bishop, ((inside_top_cm - inside_bottom_cm) / height_bishop_cm - black_line_width) / (2 * inside_radius_bishop));
03203 static double const inside_y_bishop = CONST(inside_y_bishop, 0.5 - 0.5 * (inside_top_cm + inside_bottom_cm) / height_bishop_cm);
03204 static double const inside_bottom = CONST(inside_bottom, 0.5 - inside_bottom_cm / height_bishop_cm - 0.5 * black_line_width);
03205 static double const band_top_bishop = CONST(band_top_bishop, 0.5 - band_top_bishop_cm / height_bishop_cm + 0.5 * black_line_width);
03206 static double const circle_y = CONST(circle_y, 0.5 - circle_y_cm / height_bishop_cm);
03207 static double const cross_y_bishop = CONST(cross_y_bishop, 0.5 - cross_y_bishop_cm / height_bishop_cm);
03208 static double const point_y = CONST(point_y, 0.5 - point_y_cm / height_bishop_cm);
03209 static double const ball_y = CONST(ball_y, 0.5 - ball_y_cm / height_bishop_cm);
03210 static double const inside_angle = CONST(inside_angle, acos(-ribbon_top_left / inside_radius_bishop));
03211 static double const sp1_x = CONST(sp1_x, -0.5 + sp1_x_cm / width_bishop_cm);
03212 static double const sp1_y = CONST(sp1_y, 0.5 - sp1_y_cm / height_bishop_cm);
03213
03214
03215 static double const spline_magic = CONST(spline_magic, 0.551784);
03216 static double const cp2_x = CONST(cp2_x, ribbon_bottom_y1 - ribbon_inside_y);
03217 static double const sp2_x = CONST(sp2_x, spline_magic * cp2_x);
03218 static double const sp2_y = CONST(sp2_y, ribbon_inside_y + spline_magic * (ribbon_bottom_y1 - ribbon_inside_y));
03219 static double const sp3_x = CONST(sp3_x, ribbon_bottom_x1 - spline_magic * (ribbon_bottom_x1 - cp2_x));
03220 static double const sp3_y = CONST(sp3_y, ribbon_bottom_y1);
03221 static double const sp4_x = CONST(sp4_x, ribbon_bottom_x1 + spline_magic * (ribbon_bottom_x2 - ribbon_bottom_x1));
03222 static double const sp4_y = CONST(sp4_y, ribbon_bottom_y1);
03223 static double const sp5_x = CONST(sp5_x, ribbon_bottom_x2 - spline_magic * (ribbon_bottom_x2 - ribbon_bottom_x1));
03224 static double const sp5_y = CONST(sp5_y, ribbon_bottom_y2);
03225 static double const cp6_x = CONST(cp6_x, -ribbon_bottom_left - (ribbon_bottom_y3 - ribbon_bottom_y2) * tan(ribbon_end_angle));
03226 static double const sp6_x = CONST(sp6_x, ribbon_bottom_x2 + spline_magic * (cp6_x - ribbon_bottom_x2));
03227 static double const sp6_y = CONST(sp6_y, ribbon_bottom_y2);
03228 static double const sp7_x = CONST(sp7_x, -ribbon_bottom_left - spline_magic * (-ribbon_bottom_left - cp6_x));
03229 static double const sp7_y = CONST(sp7_y, ribbon_bottom_y3 - spline_magic * (ribbon_bottom_y3 - ribbon_bottom_y2));
03230 static double const ribbon_end_top_x = CONST(ribbon_end_top_x, -ribbon_bottom_left + ribbon_width * cos(ribbon_end_angle));
03231 static double const ribbon_end_top_y = CONST(ribbon_end_top_y, ribbon_bottom_y3 - ribbon_width * sin(ribbon_end_angle));
03232 static double const cp8_x = CONST(cp8_x, ribbon_end_top_x - (ribbon_end_top_y - ribbon_top_y2) * tan(ribbon_end_angle));
03233 static double const sp8_x = CONST(sp8_x, ribbon_end_top_x - spline_magic * (ribbon_end_top_x - cp8_x));
03234 static double const sp8_y = CONST(sp8_y, ribbon_end_top_y - spline_magic * (ribbon_end_top_y - ribbon_top_y2));
03235 static double const sp9_x = CONST(sp9_x, ribbon_top_x2 + spline_magic * (cp8_x - ribbon_top_x2));
03236 static double const sp9_y = CONST(sp9_y, ribbon_top_y2);
03237 static double const sp10_x = CONST(sp10_x, ribbon_top_x2 - spline_magic * (ribbon_top_x2 - ribbon_top_x1));
03238 static double const sp10_y = CONST(sp10_y, ribbon_top_y2);
03239 static double const sp11_x = CONST(sp11_x, ribbon_top_x1 + spline_magic * (ribbon_top_x2 - ribbon_top_x1));
03240 static double const sp11_y = CONST(sp11_y, ribbon_top_y1);
03241 static double const ribbon_top_y3 = CONST(ribbon_top_y3, 0.2695);
03242 static double const sp12_x = CONST(sp12_x, ribbon_top_x1 - spline_magic * (ribbon_top_x1 + ribbon_top_left));
03243 static double const sp12_y = CONST(sp12_y, ribbon_top_y1);
03244 static double const sp13_x = CONST(sp13_x, -ribbon_top_left);
03245 static double const sp13_y = CONST(sp13_y, ribbon_top_y3 + 0.509 * spline_magic * (ribbon_top_y1 - ribbon_top_y3));
03246
03247 CwChessboardPrivate* priv = chessboard->priv;
03248
03249 cairo_save(cr);
03250 cairo_translate(cr, x, y);
03251 cairo_scale(cr, scale, scale);
03252 cairo_set_line_width(cr, black_line_width);
03253
03254
03255
03256
03257 cairo_move_to(cr, -ribbon_top_x1, ribbon_top_y1);
03258 cairo_curve_to(cr, -sp11_x, sp11_y, -sp10_x, sp10_y, -ribbon_top_x2, ribbon_top_y2);
03259
03260
03261 cairo_curve_to(cr, -sp9_x, sp9_y, -sp8_x, sp8_y, -ribbon_end_top_x, ribbon_end_top_y);
03262
03263
03264 cairo_line_to(cr, ribbon_bottom_left, ribbon_bottom_y3);
03265
03266
03267 cairo_curve_to(cr, -sp7_x, sp7_y, -sp6_x, sp6_y, -ribbon_bottom_x2, ribbon_bottom_y2);
03268
03269
03270 cairo_curve_to(cr, -sp5_x, sp5_y, -sp4_x, sp4_y, -ribbon_bottom_x1, ribbon_bottom_y1);
03271
03272
03273 cairo_curve_to(cr, -sp3_x, sp3_y, -sp2_x, sp2_y, 0.0, ribbon_inside_y);
03274
03275
03276 cairo_curve_to(cr, sp2_x, sp2_y, sp3_x, sp3_y, ribbon_bottom_x1, ribbon_bottom_y1);
03277
03278
03279 cairo_curve_to(cr, sp4_x, sp4_y, sp5_x, sp5_y, ribbon_bottom_x2, ribbon_bottom_y2);
03280
03281
03282 cairo_curve_to(cr, sp6_x, sp6_y, sp7_x, sp7_y, -ribbon_bottom_left, ribbon_bottom_y3);
03283
03284
03285 cairo_line_to(cr, ribbon_end_top_x, ribbon_end_top_y);
03286
03287
03288 cairo_curve_to(cr, sp8_x, sp8_y, sp9_x, sp9_y, ribbon_top_x2, ribbon_top_y2);
03289
03290
03291 cairo_curve_to(cr, sp10_x, sp10_y, sp11_x, sp11_y, ribbon_top_x1, ribbon_top_y1);
03292
03293 if (!white)
03294 {
03295 set_fill_color(cr, priv, white);
03296 cairo_fill_preserve(cr);
03297 cairo_stroke(cr);
03298 cairo_move_to(cr, ribbon_top_x1, ribbon_top_y1);
03299 }
03300
03301
03302 cairo_curve_to(cr, sp12_x, sp12_y, sp13_x, sp13_y, -ribbon_top_left, ribbon_top_y3);
03303
03304
03305 cairo_save(cr);
03306 cairo_translate(cr, 0.0, inside_y_bishop);
03307 cairo_scale(cr, 1.0, inside_scale_bishop);
03308 cairo_arc(cr, 0.0, 0.0, inside_radius_bishop, inside_angle, M_PI - inside_angle);
03309 cairo_restore(cr);
03310
03311
03312 cairo_line_to(cr, ribbon_top_left, ribbon_top_y3);
03313
03314
03315 cairo_curve_to(cr, -sp13_x, sp13_y, -sp12_x, sp12_y, -ribbon_top_x1 + 0.01 * black_line_width, ribbon_top_y1);
03316 cairo_close_path(cr);
03317
03318 if (white)
03319 set_fill_color(cr, priv, white);
03320 else
03321 set_line_color(cr, priv, white);
03322 cairo_fill_preserve(cr);
03323 if (white)
03324 set_line_color(cr, priv, white);
03325 else
03326 set_fill_color(cr, priv, white);
03327 cairo_stroke(cr);
03328
03329
03330 cairo_move_to(cr, 0.0, inside_bottom);
03331 cairo_line_to(cr, 0.0, ribbon_inside_y);
03332 cairo_stroke(cr);
03333
03334
03335
03336 cairo_save(cr);
03337 cairo_translate(cr, 0.0, inside_y_bishop);
03338 cairo_scale(cr, 1.0, inside_scale_bishop);
03339 cairo_arc(cr, 0.0, 0.0, inside_radius_bishop, 0.0, -M_PI);
03340 cairo_restore(cr);
03341
03342 cairo_arc(cr, 0.0, circle_y, circle_radius, -M_PI - circle_start_angle, -M_PI);
03343
03344 cairo_curve_to(cr, -circle_radius, circle_y - 0.0848, sp1_x - 0.02657, sp1_y + 0.01722, sp1_x, sp1_y);
03345 cairo_curve_to(cr, sp1_x + 0.08845, sp1_y - 0.05733, -0.000333, point_y + 0.000265, 0.0, point_y);
03346 cairo_curve_to(cr, 0.000333, point_y + 0.000265, -sp1_x - 0.08845, sp1_y - 0.05733, -sp1_x, sp1_y);
03347 cairo_curve_to(cr, -sp1_x + 0.02657, sp1_y + 0.01722, circle_radius, circle_y - 0.0848, circle_radius, circle_y);
03348
03349 cairo_arc(cr, 0.0, circle_y, circle_radius, 0.0, circle_start_angle);
03350
03351 cairo_close_path(cr);
03352
03353 if (white)
03354 set_fill_color(cr, priv, white);
03355 cairo_fill_preserve(cr);
03356 if (white)
03357 set_line_color(cr, priv, white);
03358 cairo_stroke(cr);
03359
03360
03361 if (!white)
03362 set_line_color(cr, priv, white);
03363 cairo_save(cr);
03364 if (!white)
03365 {
03366 static double const x2_bishop = CONST(x2_bishop, -circle_radius * cos(circle_start_angle));
03367 static double const y2_bishop = CONST(y2_bishop, (circle_y + circle_radius * sin(circle_start_angle)));
03368 cairo_move_to(cr, -inside_radius_bishop, inside_y_bishop);
03369 cairo_line_to(cr, x2_bishop, y2_bishop);
03370 cairo_line_to(cr, -x2_bishop, y2_bishop);
03371 cairo_line_to(cr, inside_radius_bishop, inside_y_bishop);
03372 cairo_close_path(cr);
03373 cairo_clip(cr);
03374 }
03375 cairo_save(cr);
03376 cairo_translate(cr, 0.0, inside_y_bishop);
03377 cairo_scale(cr, 1.0, inside_scale_bishop);
03378 cairo_arc(cr, 0.0, 0.0, inside_radius_bishop, -M_PI, 0.0);
03379 cairo_restore(cr);
03380 if (white)
03381 cairo_stroke(cr);
03382 else
03383 {
03384 cairo_set_line_width(cr, white_line_width);
03385 cairo_stroke(cr);
03386 cairo_set_line_width(cr, black_line_width);
03387 }
03388 cairo_restore(cr);
03389
03390
03391
03392
03393
03394
03395
03396
03397
03398 static double const x1 = CONST(x1, -inside_radius_bishop);
03399 static double const y1 = CONST(y1, inside_y_bishop / -inside_scale_bishop);
03400 static double const x2 = CONST(x2, -circle_radius * cos(circle_start_angle));
03401 static double const y2 = CONST(y2, (circle_y + circle_radius * sin(circle_start_angle)) / -inside_scale_bishop);
03402 static double const d = CONST(d, sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)));
03403 static double const u1 = CONST(u1, (x2 - x1) / d);
03404 static double const u2 = CONST(u2, (y2 - y1) / d);
03405 static double const x0 = CONST(x0, x1 + (x2 - x1) * (0 - y1) / (y2 - y1));
03406
03407
03408
03409
03410
03411
03412
03413
03414
03415
03416
03417 static double const y0 = CONST(y0, (band_top_bishop / -inside_scale_bishop + x0 * u2) / (1 - u1));
03418 static double const band_radius = CONST(band_radius, band_top_bishop / -inside_scale_bishop - y0);
03419 static double const angle = CONST(angle, atan(u1 / u2));
03420 cairo_save(cr);
03421 cairo_scale(cr, 1.0, -inside_scale_bishop);
03422 if (!white)
03423 {
03424 static double const t2 = CONST(t2, x0 * u2 + u1 * y0);
03425 static double const t1 = CONST(t1, (y0 - u1 * t2) / u2);
03426 static double const x = CONST(x, x0 + u1 * t1);
03427 cairo_move_to(cr, x, y0);
03428 cairo_line_to(cr, x + d * u1, y0 + d * u2);
03429 cairo_line_to(cr, -x - d * u1, y0 + d * u2);
03430 cairo_line_to(cr, -x, y0);
03431 cairo_close_path(cr);
03432 cairo_clip(cr);
03433 }
03434 cairo_arc(cr, 0.0, y0, band_radius, angle, M_PI - angle);
03435
03436 cairo_scale(cr, 1.0, -1.0 / inside_scale_bishop);
03437 if (white)
03438 cairo_stroke(cr);
03439 else
03440 {
03441 cairo_set_line_width(cr, white_line_width);
03442 cairo_stroke(cr);
03443 cairo_set_line_width(cr, black_line_width);
03444 }
03445 cairo_restore(cr);
03446
03447
03448 cairo_move_to(cr, -cross_leg, cross_y_bishop);
03449 cairo_line_to(cr, cross_leg, cross_y_bishop);
03450 cairo_move_to(cr, 0.0, cross_y_bishop - cross_leg);
03451 cairo_line_to(cr, 0.0, cross_y_bishop + cross_leg);
03452 if (white)
03453 cairo_stroke(cr);
03454 else
03455 {
03456 cairo_set_line_width(cr, white_line_width);
03457 cairo_stroke(cr);
03458 cairo_set_line_width(cr, black_line_width);
03459 }
03460
03461 if (!white)
03462 {
03463 cairo_move_to(cr, -inside_radius_bishop, inside_y_bishop);
03464 cairo_arc(cr, 0.0, circle_y, circle_radius, -M_PI - circle_start_angle, -M_PI);
03465 cairo_move_to(cr, inside_radius_bishop, inside_y_bishop);
03466 cairo_arc_negative(cr, 0.0, circle_y, circle_radius, circle_start_angle, 0.0);
03467 set_fill_color(cr, priv, white);
03468 cairo_stroke(cr);
03469 }
03470
03471
03472 cairo_set_line_width(cr, ball_line_width);
03473 cairo_arc(cr, 0.0, ball_y, ball_radius_bishop, -M_PI, M_PI);
03474 if (white)
03475 set_fill_color(cr, priv, white);
03476 cairo_fill_preserve(cr);
03477 if (white)
03478 set_line_color(cr, priv, white);
03479 cairo_stroke(cr);
03480
03481 cairo_restore(cr);
03482 }
03483
03484 void cw_chessboard_draw_knight(CwChessboard* chessboard, cairo_t* cr, gdouble x, gdouble y, gdouble scale, gboolean white)
03485 {
03486
03487 static double const height_knight_cm = CONST(height_knight_cm, 21.9);
03488 static double const pixels_per_cm = CONST(pixels_per_cm, 32.467);
03489 static double const min_nose_x_px = CONST(min_nose_x_px, 8.0);
03490 static double const right_ear_y_px = CONST(right_ear_y_px, 15.0);
03491 static double const bottom_right_x_px = CONST(bottom_right_x_px, 582.82);
03492 static double const bottom_right_y_px = CONST(bottom_right_y_px, 580.82);
03493 static double const bottom_left_x_px = CONST(bottom_left_x_px, 190.00);
03494
03495 static double const pixel_scale = CONST(pixel_scale, 1.0 / (pixels_per_cm * height_knight_cm));
03496 static double const knight_black_line_width = CONST(knight_black_line_width, 0.95 * black_line_width / pixel_scale);
03497 static double const knight_white_line_width = CONST(knight_white_line_width, 1.3 * knight_black_line_width);
03498 static double const knight_white_glyp_line_width = CONST(knight_white_glyp_line_width, knight_white_line_width - knight_black_line_width);
03499
03500
03501 static double const max_y = CONST(max_y, bottom_right_y_px * pixel_scale);
03502 static double const min_y = CONST(min_y, right_ear_y_px * pixel_scale);
03503 static double const max_x = CONST(max_x, bottom_right_x_px * pixel_scale);
03504 static double const min_x = CONST(min_x, min_nose_x_px * pixel_scale);
03505
03506
03507 static double const pixel_translate_x = CONST(pixel_translate_x, -(max_x + min_x) / 2);
03508 static double const pixel_translate_y = CONST(pixel_translate_y, -(max_y + min_y) / 2);
03509
03510 CwChessboardPrivate* priv = chessboard->priv;
03511
03512 cairo_save(cr);
03513 cairo_translate(cr, x, y);
03514 cairo_scale(cr, scale, scale);
03515
03516
03517
03518
03519 cairo_translate(cr, pixel_translate_x, pixel_translate_y);
03520
03521 cairo_scale(cr, pixel_scale, pixel_scale);
03522
03523
03524 cairo_move_to(cr, 319.00, 299.00);
03525 cairo_curve_to(cr, 322.00, 449.00, 165.00, 445.00, 192.00, 570.00);
03526 cairo_curve_to(cr, 192.00, 570.00, 568.50, 571.00, 568.50, 571.00);
03527 cairo_curve_to(cr, 577.00, 426.00, 533.00, 99.00, 340.50, 88.50);
03528 cairo_curve_to(cr, 245.50, 87.50, 206.00, 86.00, 195.00, 102.00);
03529 set_fill_color(cr, priv, white);
03530 cairo_fill(cr);
03531
03532
03533 cairo_move_to(cr, 315.00, 300.33);
03534 cairo_curve_to(cr, 301.43, 300.80, 291.75, 314.52, 282.00, 325.00);
03535 cairo_curve_to(cr, 298.67, 317.33, 316.33, 325.00, 317.33, 344.33);
03536 cairo_curve_to(cr, 321.33, 337.33, 326.00, 326.00, 315.00, 300.33);
03537 if (white)
03538 set_line_color(cr, priv, white);
03539 cairo_fill(cr);
03540
03541
03542
03543 static double const back_top_offset = CONST(back_top_offset, (93.00 - knight_black_line_width) - 82.00);
03544 cairo_move_to(cr, 582.82, 580.82);
03545 cairo_curve_to(cr, 589.00, 359.00, 530.00,85.00, 332.00, 82.00 + back_top_offset);
03546 cairo_curve_to(cr, 320.87, 82.04 + back_top_offset, 314.25, 82.12 + back_top_offset, 302.50, 82.38 + back_top_offset);
03547 cairo_curve_to(cr, 302.75, 95.38, 296.22, 93.73, 319.50, 94.00);
03548 cairo_curve_to(cr, 510.50, 93.00, 556.12, 359.00, 556.12, 563.00);
03549 cairo_close_path(cr);
03550 cairo_fill(cr);
03551
03552
03553 cairo_move_to(cr, 190.00, 570.00);
03554 cairo_curve_to(cr, 190.00, 550.75, 190.00, 549.00, 190.00, 540.00);
03555 cairo_curve_to(cr, 190.00, 493.25, 210.50, 482.50, 285.00, 409.50);
03556 cairo_curve_to(cr, 298.25, 391.75, 313.00, 357.50, 317.75, 344.75);
03557 cairo_curve_to(cr, 320.25, 340.00, 320.25, 330.00, 320.00, 280.00);
03558 cairo_set_line_width(cr, knight_black_line_width);
03559 cairo_stroke(cr);
03560
03561
03562 cairo_move_to(cr, 144.00, 31.50);
03563 cairo_curve_to(cr, 122.50, 67.00, 147.50, 57.50, 146.00, 105.00);
03564 cairo_curve_to(cr, 112.00, 125.50, 123.00, 140.50, 102.50, 170.00);
03565 cairo_curve_to(cr, 84.00, 199.50, 128.00, 181.50, 33.50, 313.50);
03566 cairo_curve_to(cr, -23.00, 414.00, 81.50, 468.00, 130.00, 447.50);
03567 cairo_curve_to(cr, 182.50, 398.00, 142.50, 427.00, 179.50, 390.00);
03568 cairo_curve_to(cr, 194.50, 376.50, 212.50, 349.50, 237.50, 347.00);
03569 cairo_curve_to(cr, 268.00, 344.00, 283.50, 323.50, 306.00, 301.00);
03570 cairo_curve_to(cr, 327.50, 276.50, 330.00, 264.50, 330.00, 228.50);
03571 if (white)
03572 set_fill_color(cr, priv, white);
03573 cairo_fill_preserve(cr);
03574 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
03575 if (white)
03576 set_line_color(cr, priv, white);
03577 cairo_stroke(cr);
03578 cairo_move_to(cr, 201.00, 94.50);
03579 cairo_curve_to(cr, 184.50, 54.50, 152.00, 43.50, 144.00, 31.50);
03580 cairo_stroke(cr);
03581
03582
03583 cairo_move_to(cr, 170.50, 136.50);
03584 cairo_curve_to(cr, 170.00, 129.50, 175.50, 125.00, 183.50, 116.00);
03585 cairo_curve_to(cr, 204.50, 91.00, 216.00, 94.00, 238.00, 91.00);
03586 cairo_stroke(cr);
03587
03588 if (!white)
03589 {
03590
03591 cairo_move_to(cr, 529.00, 570.00);
03592 cairo_curve_to(cr, 530.50, 352.00, 476.50, 128.50, 334.00, 121.00);
03593 cairo_curve_to(cr, 310.50, 118.50, 310.00, 117.50, 296.50, 117.50);
03594 cairo_curve_to(cr, 291.50, 100.00, 252.50, 95.50, 242.20, 119.35);
03595 cairo_curve_to(cr, 227.55, 120.95, 212.22, 124.23, 198.50, 130.50);
03596 cairo_curve_to(cr, 178.00, 137.50, 158.50, 147.50, 154.00, 137.00);
03597 cairo_curve_to(cr, 149.50, 127.00, 145.50, 121.00, 204.00, 100.00);
03598 cairo_curve_to(cr, 226.50, 90.00, 276.50, 92.00, 319.50, 94.00);
03599 cairo_curve_to(cr, 510.50, 93.00, 556.00, 354.00, 556.00, 570.00);
03600 cairo_curve_to(cr, 548.06, 571.00, 537.73, 569.45, 529.00, 570.00);
03601 set_line_color(cr, priv, white);
03602 cairo_fill(cr);
03603 }
03604
03605
03606 double dummy = bottom_left_x_px;
03607 double bottom_right_y_px_sn = bottom_right_y_px;
03608 if (scale >= 27)
03609 {
03610
03611 cairo_user_to_device(cr,& dummy,& bottom_right_y_px_sn);
03612 bottom_right_y_px_sn = round(bottom_right_y_px_sn);
03613 cairo_device_to_user(cr,& dummy,& bottom_right_y_px_sn);
03614 }
03615 cairo_rectangle(cr, bottom_left_x_px - 0.5 * knight_black_line_width,
03616 bottom_right_y_px_sn - knight_black_line_width,
03617 bottom_right_x_px - (bottom_left_x_px - 0.5 * knight_black_line_width),
03618 knight_black_line_width);
03619 if (!white)
03620 set_fill_color(cr, priv, white);
03621 cairo_fill(cr);
03622
03623
03624 cairo_move_to(cr, 113.67, 389.33);
03625 cairo_curve_to(cr, 121.00, 388.00, 129.33, 406.67, 120.67, 414.67);
03626 cairo_curve_to(cr, 114.33, 419.33, 104.33, 431.00, 112.67, 444.00);
03627
03628 cairo_line_to(cr, 93.00, 446.67);
03629 cairo_curve_to(cr, 89.00, 418.67, 94.67, 417.33, 100.00, 412.67);
03630 cairo_curve_to(cr, 112.67, 402.00, 100.67, 394.67, 113.67, 389.33);
03631 if (!white)
03632 set_line_color(cr, priv, white);
03633 if (white)
03634 cairo_fill(cr);
03635 else
03636 {
03637 cairo_fill_preserve(cr);
03638 cairo_set_line_width(cr, knight_white_glyp_line_width);
03639 cairo_stroke(cr);
03640 cairo_set_line_width(cr, knight_black_line_width);
03641 }
03642
03643
03644 cairo_move_to(cr, 33.50, 313.50);
03645 cairo_curve_to(cr, -23.00, 414.00, 81.50, 468.00, 130.00, 447.50);
03646 if (!white)
03647 set_fill_color(cr, priv, white);
03648 cairo_stroke(cr);
03649
03650 if (!white)
03651 {
03652
03653 cairo_move_to(cr, 312.32, 293.46);
03654 cairo_curve_to(cr, 328.01, 273.63, 330.00, 260.62, 330.00, 228.50);
03655 cairo_set_line_width(cr, knight_white_line_width);
03656 set_line_color(cr, priv, white);
03657 cairo_stroke(cr);
03658 cairo_set_line_width(cr, knight_black_line_width);
03659 }
03660
03661
03662 for (int stroke = 0; stroke < 2; ++stroke)
03663 {
03664 cairo_move_to(cr, 242.00, 114.00);
03665 cairo_curve_to(cr, 235.00, 76.00, 235.50, 92.50, 267.00, 15.00);
03666 if (stroke)
03667 cairo_move_to(cr, 267.00, 15.00);
03668 cairo_curve_to(cr, 309.50, 85.50, 312.00, 88.00, 295.00, 117.00);
03669 if (stroke)
03670 {
03671 if (white)
03672 set_line_color(cr, priv, white);
03673 cairo_stroke(cr);
03674 }
03675 else
03676 {
03677 if (white)
03678 set_fill_color(cr, priv, white);
03679 else
03680 set_fill_color(cr, priv, white);
03681 cairo_fill(cr);
03682 }
03683 }
03684
03685 if (!white)
03686 set_line_color(cr, priv, white);
03687
03688
03689 cairo_move_to(cr, 76.00, 363.00);
03690 cairo_curve_to(cr, 66.00, 372.33, 78.33, 379.00, 66.00, 384.00);
03691 cairo_curve_to(cr, 21.00, 399.00, 61.67, 331.00, 79.67, 341.67);
03692 cairo_curve_to(cr, 81.00, 342.00, 84.67, 353.33, 76.00, 363.00);
03693 if (white)
03694 cairo_fill(cr);
03695 else
03696 {
03697 cairo_fill_preserve(cr);
03698 cairo_set_line_width(cr, knight_white_glyp_line_width);
03699 cairo_stroke(cr);
03700 cairo_set_line_width(cr, knight_black_line_width);
03701 }
03702
03703
03704 cairo_move_to(cr, 173.33, 208.00);
03705 cairo_curve_to(cr, 180.67, 207.00, 182.00, 197.67, 182.00, 197.67);
03706 cairo_curve_to(cr, 184.59, 176.98, 182.28, 177.30, 190.67, 173.00);
03707 cairo_curve_to(cr, 201.00, 169.33, 198.33, 146.00, 173.33, 161.67);
03708 cairo_curve_to(cr, 146.00, 181.33, 130.67, 192.00, 128.33, 202.67);
03709 cairo_curve_to(cr, 124.00, 233.33, 131.00, 227.33, 144.67, 207.00);
03710 cairo_curve_to(cr, 150.67, 201.00, 158.67, 193.67, 162.33, 203.33);
03711 cairo_curve_to(cr, 164.67, 206.00, 165.63, 209.29, 173.33, 208.00);
03712 if (white)
03713 cairo_fill(cr);
03714 else
03715 {
03716 cairo_fill_preserve(cr);
03717 cairo_set_line_width(cr, knight_white_glyp_line_width);
03718 cairo_stroke(cr);
03719 }
03720
03721 cairo_restore(cr);
03722 }
03723
03724
03725
03726
03727 static gboolean cw_chessboard_motion_notify(GtkWidget* widget, GdkEventMotion* event)
03728 {
03729 Dout(dc::motion_event, "Calling cw_chessboard_motion_notify(" << widget << ", " << event << ")");
03730 update_cursor_position(CW_CHESSBOARD(widget), event->x, event->y, FALSE);
03731
03732 return FALSE;
03733 }
03734
03735 static gboolean cw_chessboard_default_motion_notify(GtkWidget* widget, GdkEventMotion* event)
03736 {
03737 Dout(dc::motion_event, "Calling cw_chessboard_default_motion_notify(" << widget << ", " << event << ")");
03738
03739 CwChessboard* chessboard = CW_CHESSBOARD(widget);
03740 CwChessboardPrivate* priv = chessboard->priv;
03741
03742 if (priv->floating_piece_handle != -1)
03743 {
03744 double hsside = 0.5 * chessboard->sside;
03745 double fraction = hsside - (gint)hsside;
03746 cw_chessboard_move_floating_piece(chessboard, priv->floating_piece_handle, event->x - fraction, event->y - fraction);
03747 return TRUE;
03748 }
03749
03750 return FALSE;
03751 }
03752