98
98
static gint get_cell_index (GtkMineField * mfield, guint x, guint y);
99
99
static void setup_sign (sign * signp, const char *file, guint minesizepixels);
100
static void gtk_mine_draw (GtkMineField * mfield, guint x, guint y);
100
static void gtk_mine_queue_draw (GtkMineField * mfield, guint x, guint y);
101
101
static gint gtk_minefield_button_press (GtkWidget * widget,
102
102
GdkEventButton * event);
103
103
static gint gtk_minefield_button_release (GtkWidget * widget,
104
104
GdkEventButton * event);
105
105
static void gtk_minefield_check_field (GtkMineField * mfield, gint x, gint y);
106
106
static void gtk_minefield_class_init (GtkMineFieldClass * class);
107
static gboolean gtk_minefield_expose (GtkWidget * widget, GdkEventExpose * event);
107
static gboolean gtk_minefield_draw (GtkWidget * widget, cairo_t * cr);
108
108
static void gtk_minefield_init (GtkMineField * mfield);
109
109
static void gtk_minefield_lose (GtkMineField * mfield);
110
110
static gint gtk_minefield_motion_notify (GtkWidget * widget,
401
398
xofs = allocation->x + (allocation->width - width) / 2;
402
399
yofs = allocation->y + (allocation->height - height) / 2;
405
if (!mfield->thick_line)
406
mfield->thick_line = gdk_gc_new (window);
407
gdk_gc_copy (mfield->thick_line, gtk_widget_get_style (widget)->black_gc);
408
gdk_gc_set_line_attributes (mfield->thick_line,
409
MAX (1, 0.1 * minesizepixels),
411
GDK_CAP_ROUND, GDK_JOIN_ROUND);
413
401
gdk_window_move_resize (window, xofs, yofs, width, height);
418
gtk_minefield_size_request (GtkWidget * widget, GtkRequisition * requisition)
420
GtkMineField *mf = GTK_MINEFIELD (widget);
421
/* request the minimum size - to allow the widget window to be resized */
422
requisition->width = mf->xsize * MINESIZE_MIN;
423
requisition->height = mf->ysize * MINESIZE_MIN;
427
gtk_mine_draw (GtkMineField * mfield, guint x, guint y)
406
gtk_minefield_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural)
408
GtkMineField *mf = GTK_MINEFIELD (widget);
410
/* request the minimum size - to allow the widget window to be resized */
411
*minimum = *natural = mf->xsize * MINESIZE_MIN;
415
gtk_minefield_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural)
417
GtkMineField *mf = GTK_MINEFIELD (widget);
419
/* request the minimum size - to allow the widget window to be resized */
420
*minimum = *natural = mf->ysize * MINESIZE_MIN;
424
gtk_mine_queue_draw (GtkMineField * mfield, guint x, guint y)
426
guint minesizepixels = mfield->minesizepixels;
428
gtk_widget_queue_draw_area (GTK_WIDGET (mfield),
429
x * minesizepixels, y * minesizepixels,
430
minesizepixels, minesizepixels);
434
gtk_mine_draw (GtkMineField * mfield, cairo_t *cr, guint x, guint y)
429
436
int c = get_cell_index (mfield, x, y);
431
438
gboolean clicked;
433
440
guint minesizepixels;
435
static const char stipple_data[] = { 0x03, 0x03, 0x0c, 0x0c };
436
static GdkPixmap *stipple = NULL;
438
442
GtkWidget *widget = GTK_WIDGET (mfield);
442
444
g_return_if_fail (c != -1);
444
window = gtk_widget_get_window (widget);
445
446
style = gtk_widget_get_style (widget);
447
/* This gives us a dotted line to increase the contrast between
448
* buttons and the "sea". */
449
if (stipple == NULL) {
450
stipple = gdk_bitmap_create_from_data (NULL, stipple_data, 4, 4);
451
dots = gdk_gc_new (window);
452
gdk_gc_copy (dots, style->dark_gc[2]);
453
gdk_gc_set_stipple (dots, stipple);
454
g_object_unref (stipple);
455
gdk_gc_set_fill (dots, GDK_STIPPLED);
458
448
minesizepixels = mfield->minesizepixels;
460
450
noshadow = mfield->mines[c].shown;
462
452
clicked = mfield->mines[c].down;
464
/* gtk_paint_box needs a clipping rectangle. */
465
rect.x = x * minesizepixels;
466
rect.y = y * minesizepixels;
467
rect.width = minesizepixels;
468
rect.height = minesizepixels;
470
454
if (noshadow) { /* draw grid on ocean floor */
455
double dots[] = {2, 2};
471
457
gtk_paint_box (style,
473
459
clicked ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL,
477
462
"button", x * minesizepixels, y * minesizepixels, minesizepixels, minesizepixels);
478
463
if (y == 0) { /* top row only */
479
gdk_draw_line (window, /* top */
480
dots, x * minesizepixels, 0, x * minesizepixels + minesizepixels - 1, 0);
464
cairo_move_to (cr, x * minesizepixels, 0);
465
cairo_line_to (cr, x * minesizepixels + minesizepixels - 1, 0);
482
467
if (x == 0) { /* left column only */
483
gdk_draw_line (window, /* left */
484
dots, 0, y * minesizepixels, 0, y * minesizepixels + minesizepixels - 1);
468
cairo_move_to (cr, 0, y * minesizepixels);
469
cairo_line_to (cr, 0, y * minesizepixels + minesizepixels - 1);
486
gdk_draw_line (window, /* right */
488
x * minesizepixels + minesizepixels - 1,
490
x * minesizepixels + minesizepixels - 1, y * minesizepixels + minesizepixels - 1);
491
gdk_draw_line (window, /* bottom */
494
y * minesizepixels + minesizepixels - 1,
495
x * minesizepixels + minesizepixels - 1, y * minesizepixels + minesizepixels - 1);
471
cairo_move_to (cr, x * minesizepixels + minesizepixels - 1 + 0.5, y * minesizepixels + 0.5);
472
cairo_line_to (cr, x * minesizepixels + minesizepixels - 1 + 0.5, y * minesizepixels + minesizepixels - 1 + 0.5);
473
cairo_move_to (cr, x * minesizepixels + 0.5, y * minesizepixels + minesizepixels - 1 + 0.5);
474
cairo_line_to (cr, x * minesizepixels + minesizepixels - 1 + 0.5, y * minesizepixels + minesizepixels - 1 + 0.5);
477
gdk_cairo_set_source_color (cr, >k_widget_get_style (widget)->dark[gtk_widget_get_state (widget)]);
478
cairo_set_line_width (cr, 1);
479
cairo_set_dash (cr, dots, 2, 0);
497
482
} else { /* draw shadow around possible mine location */
498
483
gtk_paint_box (style,
500
485
clicked ? GTK_STATE_ACTIVE : GTK_STATE_SELECTED,
501
486
clicked ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
504
488
"button", x * minesizepixels, y * minesizepixels, minesizepixels, minesizepixels);
512
496
g_assert (nm >= 0 && nm <= 9);
514
498
if (mfield->use_overmine_warning && n < nm) {
515
gdk_draw_pixbuf (window, NULL,
516
mfield->warning.scaledpixbuf, 0, 0,
517
x * minesizepixels + (minesizepixels - mfield->warning.width) / 2,
518
y * minesizepixels + (minesizepixels - mfield->warning.height) / 2,
519
mfield->warning.width, mfield->warning.height,
520
GDK_RGB_DITHER_NORMAL, 0, 0);
499
gdk_cairo_set_source_pixbuf (cr, mfield->warning.scaledpixbuf,
500
x * minesizepixels + (minesizepixels - mfield->warning.width) / 2,
501
y * minesizepixels + (minesizepixels - mfield->warning.height) / 2);
503
x * minesizepixels + (minesizepixels - mfield->warning.width) / 2,
504
y * minesizepixels + (minesizepixels - mfield->warning.height) / 2,
505
mfield->warning.width, mfield->warning.height);
524
gdk_draw_layout (window,
526
x * minesizepixels + mfield->numstr[n].dx,
527
y * minesizepixels + mfield->numstr[n].dy,
528
PANGO_LAYOUT (mfield->numstr[n].layout));
511
x * minesizepixels + mfield->numstr[n].dx,
512
y * minesizepixels + mfield->numstr[n].dy);
513
pango_cairo_show_layout (cr, PANGO_LAYOUT (mfield->numstr[n].layout));
531
516
} else if (mfield->mines[c].marked == MINE_QUESTION) {
532
gdk_draw_pixbuf (window, NULL,
533
mfield->question.scaledpixbuf, 0, 0,
534
x * minesizepixels + (minesizepixels - mfield->flag.width) / 2,
535
y * minesizepixels + (minesizepixels - mfield->flag.height) / 2,
536
mfield->flag.width, mfield->flag.height,
537
GDK_RGB_DITHER_NORMAL, 0, 0);
517
gdk_cairo_set_source_pixbuf (cr, mfield->question.scaledpixbuf,
518
x * minesizepixels + (minesizepixels - mfield->flag.width) / 2,
519
y * minesizepixels + (minesizepixels - mfield->flag.height) / 2);
521
x * minesizepixels + (minesizepixels - mfield->flag.width) / 2,
522
y * minesizepixels + (minesizepixels - mfield->flag.height) / 2,
523
mfield->flag.width, mfield->flag.height);
538
525
} else if (mfield->mines[c].marked == MINE_MARKED) {
539
gdk_draw_pixbuf (window, NULL,
540
mfield->flag.scaledpixbuf, 0, 0,
541
x * minesizepixels + (minesizepixels - mfield->flag.width) / 2,
542
y * minesizepixels + (minesizepixels - mfield->flag.height) / 2,
543
mfield->flag.width, mfield->flag.height,
544
GDK_RGB_DITHER_NORMAL, 0, 0);
526
gdk_cairo_set_source_pixbuf (cr, mfield->flag.scaledpixbuf,
527
x * minesizepixels + (minesizepixels - mfield->flag.width) / 2,
528
y * minesizepixels + (minesizepixels - mfield->flag.height) / 2);
530
x * minesizepixels + (minesizepixels - mfield->flag.width) / 2,
531
y * minesizepixels + (minesizepixels - mfield->flag.height) / 2,
532
mfield->flag.width, mfield->flag.height);
546
535
if (mfield->lose && mfield->mines[c].mined != 1) {
547
536
int x1 = x * minesizepixels + 0.1 * minesizepixels;
549
538
int x2 = x * minesizepixels + 0.9 * minesizepixels;
550
539
int y2 = y * minesizepixels + 0.9 * minesizepixels;
552
gdk_draw_line (window, mfield->thick_line, x1, y1, x2, y2);
553
gdk_draw_line (window, mfield->thick_line, x1, y2, x2, y1);
541
cairo_move_to (cr, x1, y1);
542
cairo_line_to (cr, x2, y2);
543
cairo_move_to (cr, x1, y2);
544
cairo_line_to (cr, x2, y1);
547
gdk_cairo_set_source_color (cr, >k_widget_get_style (widget)->black);
548
cairo_set_line_width (cr, MAX (1, 0.1 * minesizepixels));
549
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
550
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
555
554
} else if (mfield->lose && mfield->mines[c].mined) {
556
gdk_draw_pixbuf (window, NULL,
557
mfield->mine.scaledpixbuf, 0, 0,
558
x * minesizepixels + (minesizepixels - mfield->flag.width) / 2,
559
y * minesizepixels + (minesizepixels - mfield->flag.height) / 2,
560
mfield->flag.width, mfield->flag.height,
561
GDK_RGB_DITHER_NORMAL, 0, 0);
555
gdk_cairo_set_source_pixbuf (cr, mfield->mine.scaledpixbuf,
556
x * minesizepixels + (minesizepixels - mfield->flag.width) / 2,
557
y * minesizepixels + (minesizepixels - mfield->flag.height) / 2);
559
x * minesizepixels + (minesizepixels - mfield->flag.width) / 2,
560
y * minesizepixels + (minesizepixels - mfield->flag.height) / 2,
561
mfield->flag.width, mfield->flag.height);
563
564
if (mfield->lose && mfield->mines[c].mined && mfield->mines[c].shown) {
564
gdk_draw_pixbuf (window, NULL,
565
mfield->bang.scaledpixbuf, 0, 0,
566
x * minesizepixels + (minesizepixels - mfield->bang.width) / 2,
567
y * minesizepixels + (minesizepixels - mfield->bang.height) / 2,
568
mfield->bang.width, mfield->bang.height,
569
GDK_RGB_DITHER_NORMAL, 0, 0);
565
gdk_cairo_set_source_pixbuf (cr, mfield->bang.scaledpixbuf,
566
x * minesizepixels + (minesizepixels - mfield->bang.width) / 2,
567
y * minesizepixels + (minesizepixels - mfield->bang.height) / 2);
569
x * minesizepixels + (minesizepixels - mfield->bang.width) / 2,
570
y * minesizepixels + (minesizepixels - mfield->bang.height) / 2,
571
mfield->bang.width, mfield->bang.height);
575
gtk_minefield_expose (GtkWidget * widget, GdkEventExpose * event)
577
gtk_minefield_draw (GtkWidget * widget, cairo_t * cr)
577
579
g_return_val_if_fail (widget != NULL, FALSE);
578
580
g_return_val_if_fail (GTK_IS_MINEFIELD (widget), FALSE);
579
g_return_val_if_fail (event != NULL, FALSE);
581
582
if (gtk_widget_is_drawable (widget)) {
582
guint x1, y1, x2, y2, x, y;
583
584
GtkMineField *mfield = GTK_MINEFIELD (widget);
584
GdkRectangle *area = &event->area;
586
586
/* mine square numbers must be resized to fit the mine size */
587
587
gtk_minefield_setup_signs (mfield);
588
588
gtk_minefield_setup_numbers (mfield);
591
x1 = area->x / mfield->minesizepixels;
592
y1 = area->y / mfield->minesizepixels;
593
x2 = (area->x + area->width - 1) / mfield->minesizepixels;
594
y2 = (area->y + area->height - 1) / mfield->minesizepixels;
598
x2 = mfield->xsize - 1;
599
y2 = mfield->ysize - 1;
602
/* These are necessary because we get an expose call before a
603
* resize at the old size, but after we have changed our data. */
604
if (x2 >= mfield->xsize)
605
x2 = mfield->xsize - 1;
606
if (y2 >= mfield->ysize)
607
y2 = mfield->ysize - 1;
609
for (x = x1; x <= x2; x++)
610
for (y = y1; y <= y2; y++)
611
gtk_mine_draw (mfield, x, y);
590
for (x = 0; x < mfield->xsize; x++)
591
for (y = 0; y < mfield->ysize; y++)
592
gtk_mine_draw (mfield, cr, x, y);