diff options
author | Franklin Wei <franklin@rockbox.org> | 2020-06-25 14:44:33 -0400 |
---|---|---|
committer | Franklin Wei <franklin@rockbox.org> | 2020-06-25 18:45:58 +0000 |
commit | 48b0ef1cf22ec37927116ac83ea7c7cfc1f9083e (patch) | |
tree | 148ced6ae04e578abc38a38e92879fa13b97a604 /apps/plugins/puzzles/src/gtk.c | |
parent | dd3a8e08988308cf88c10a44176d83a8a152ec4a (diff) | |
download | rockbox-48b0ef1cf22ec37927116ac83ea7c7cfc1f9083e.tar.gz rockbox-48b0ef1cf22ec37927116ac83ea7c7cfc1f9083e.zip |
puzzles: resync with upstream
This brings the upstream version to 9aa7b7c (with some of my changes as
well).
Change-Id: I5bf8a3e0b8672d82cb1bf34afc07adbe12a3ac53
Diffstat (limited to 'apps/plugins/puzzles/src/gtk.c')
-rw-r--r-- | apps/plugins/puzzles/src/gtk.c | 753 |
1 files changed, 697 insertions, 56 deletions
diff --git a/apps/plugins/puzzles/src/gtk.c b/apps/plugins/puzzles/src/gtk.c index d41f8677b9..7588ee0dc6 100644 --- a/apps/plugins/puzzles/src/gtk.c +++ b/apps/plugins/puzzles/src/gtk.c | |||
@@ -44,6 +44,17 @@ | |||
44 | # endif | 44 | # endif |
45 | #endif | 45 | #endif |
46 | 46 | ||
47 | #if defined USE_CAIRO && GTK_CHECK_VERSION(2,10,0) | ||
48 | /* We can only use printing if we are using Cairo for drawing and we | ||
49 | have a GTK version >= 2.10 (when GtkPrintOperation was added). */ | ||
50 | # define USE_PRINTING | ||
51 | # if GTK_CHECK_VERSION(2,18,0) | ||
52 | /* We can embed the page setup. Before 2.18, we needed to have a | ||
53 | separate page setup. */ | ||
54 | # define USE_EMBED_PAGE_SETUP | ||
55 | # endif | ||
56 | #endif | ||
57 | |||
47 | #if GTK_CHECK_VERSION(3,0,0) | 58 | #if GTK_CHECK_VERSION(3,0,0) |
48 | /* The old names are still more concise! */ | 59 | /* The old names are still more concise! */ |
49 | #define gtk_hbox_new(x,y) gtk_box_new(GTK_ORIENTATION_HORIZONTAL,y) | 60 | #define gtk_hbox_new(x,y) gtk_box_new(GTK_ORIENTATION_HORIZONTAL,y) |
@@ -132,6 +143,18 @@ struct font { | |||
132 | }; | 143 | }; |
133 | 144 | ||
134 | /* | 145 | /* |
146 | * An internal API for functions which need to be different for | ||
147 | * printing and drawing. | ||
148 | */ | ||
149 | struct internal_drawing_api { | ||
150 | void (*set_colour)(frontend *fe, int colour); | ||
151 | #ifdef USE_CAIRO | ||
152 | void (*fill)(frontend *fe); | ||
153 | void (*fill_preserve)(frontend *fe); | ||
154 | #endif | ||
155 | }; | ||
156 | |||
157 | /* | ||
135 | * This structure holds all the data relevant to a single window. | 158 | * This structure holds all the data relevant to a single window. |
136 | * In principle this would allow us to open multiple independent | 159 | * In principle this would allow us to open multiple independent |
137 | * puzzle windows, although I can't currently see any real point in | 160 | * puzzle windows, although I can't currently see any real point in |
@@ -180,7 +203,7 @@ struct frontend { | |||
180 | GtkWidget *cfgbox; | 203 | GtkWidget *cfgbox; |
181 | void *paste_data; | 204 | void *paste_data; |
182 | int paste_data_len; | 205 | int paste_data_len; |
183 | int pw, ph; /* pixmap size (w, h are area size) */ | 206 | int pw, ph, ps; /* pixmap size (w, h are area size, s is GDK scale) */ |
184 | int ox, oy; /* offset of pixmap in drawing area */ | 207 | int ox, oy; /* offset of pixmap in drawing area */ |
185 | #ifdef OLD_FILESEL | 208 | #ifdef OLD_FILESEL |
186 | char *filesel_name; | 209 | char *filesel_name; |
@@ -225,6 +248,23 @@ struct frontend { | |||
225 | */ | 248 | */ |
226 | bool awaiting_resize_ack; | 249 | bool awaiting_resize_ack; |
227 | #endif | 250 | #endif |
251 | #ifdef USE_CAIRO | ||
252 | int printcount, printw, printh; | ||
253 | float printscale; | ||
254 | bool printsolns, printcolour; | ||
255 | int hatch; | ||
256 | float hatchthick, hatchspace; | ||
257 | drawing *print_dr; | ||
258 | document *doc; | ||
259 | #endif | ||
260 | #ifdef USE_PRINTING | ||
261 | GtkPrintOperation *printop; | ||
262 | GtkPrintContext *printcontext; | ||
263 | GtkSpinButton *printcount_spin_button, *printw_spin_button, | ||
264 | *printh_spin_button, *printscale_spin_button; | ||
265 | GtkCheckButton *soln_check_button, *colour_check_button; | ||
266 | #endif | ||
267 | const struct internal_drawing_api *dr_api; | ||
228 | }; | 268 | }; |
229 | 269 | ||
230 | struct blitter { | 270 | struct blitter { |
@@ -247,15 +287,19 @@ void get_random_seed(void **randseed, int *randseedsize) | |||
247 | void frontend_default_colour(frontend *fe, float *output) | 287 | void frontend_default_colour(frontend *fe, float *output) |
248 | { | 288 | { |
249 | #if !GTK_CHECK_VERSION(3,0,0) | 289 | #if !GTK_CHECK_VERSION(3,0,0) |
250 | /* | 290 | if (!fe->headless) { |
251 | * Use the widget style's default background colour as the | 291 | /* |
252 | * background for the puzzle drawing area. | 292 | * If we have a widget and it has a style that specifies a |
253 | */ | 293 | * default background colour, use that as the background for |
254 | GdkColor col = gtk_widget_get_style(fe->window)->bg[GTK_STATE_NORMAL]; | 294 | * the puzzle drawing area. |
255 | output[0] = col.red / 65535.0; | 295 | */ |
256 | output[1] = col.green / 65535.0; | 296 | GdkColor col = gtk_widget_get_style(fe->window)->bg[GTK_STATE_NORMAL]; |
257 | output[2] = col.blue / 65535.0; | 297 | output[0] = col.red / 65535.0; |
258 | #else | 298 | output[1] = col.green / 65535.0; |
299 | output[2] = col.blue / 65535.0; | ||
300 | } | ||
301 | #endif | ||
302 | |||
259 | /* | 303 | /* |
260 | * GTK 3 has decided that there's no such thing as a 'default | 304 | * GTK 3 has decided that there's no such thing as a 'default |
261 | * background colour' any more, because widget styles might set | 305 | * background colour' any more, because widget styles might set |
@@ -263,9 +307,11 @@ void frontend_default_colour(frontend *fe, float *output) | |||
263 | * image. We don't want to get into overlaying our entire puzzle | 307 | * image. We don't want to get into overlaying our entire puzzle |
264 | * on an arbitrary background image, so we'll just make up a | 308 | * on an arbitrary background image, so we'll just make up a |
265 | * reasonable shade of grey. | 309 | * reasonable shade of grey. |
310 | * | ||
311 | * This is also what we do on GTK 2 in headless mode, where we | ||
312 | * don't have a widget style to query. | ||
266 | */ | 313 | */ |
267 | output[0] = output[1] = output[2] = 0.9F; | 314 | output[0] = output[1] = output[2] = 0.9F; |
268 | #endif | ||
269 | } | 315 | } |
270 | 316 | ||
271 | void gtk_status_bar(void *handle, const char *text) | 317 | void gtk_status_bar(void *handle, const char *text) |
@@ -290,6 +336,7 @@ void gtk_status_bar(void *handle, const char *text) | |||
290 | static void setup_drawing(frontend *fe) | 336 | static void setup_drawing(frontend *fe) |
291 | { | 337 | { |
292 | fe->cr = cairo_create(fe->image); | 338 | fe->cr = cairo_create(fe->image); |
339 | cairo_scale(fe->cr, fe->ps, fe->ps); | ||
293 | cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_GRAY); | 340 | cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_GRAY); |
294 | cairo_set_line_width(fe->cr, 1.0); | 341 | cairo_set_line_width(fe->cr, 1.0); |
295 | cairo_set_line_cap(fe->cr, CAIRO_LINE_CAP_SQUARE); | 342 | cairo_set_line_cap(fe->cr, CAIRO_LINE_CAP_SQUARE); |
@@ -321,12 +368,23 @@ static void snaffle_colours(frontend *fe) | |||
321 | fe->colours = midend_colours(fe->me, &fe->ncolours); | 368 | fe->colours = midend_colours(fe->me, &fe->ncolours); |
322 | } | 369 | } |
323 | 370 | ||
324 | static void set_colour(frontend *fe, int colour) | 371 | static void draw_set_colour(frontend *fe, int colour) |
325 | { | 372 | { |
326 | cairo_set_source_rgb(fe->cr, | 373 | cairo_set_source_rgb(fe->cr, |
327 | fe->colours[3*colour + 0], | 374 | fe->colours[3*colour + 0], |
328 | fe->colours[3*colour + 1], | 375 | fe->colours[3*colour + 1], |
329 | fe->colours[3*colour + 2]); | 376 | fe->colours[3*colour + 2]); |
377 | } | ||
378 | |||
379 | static void print_set_colour(frontend *fe, int colour) | ||
380 | { | ||
381 | float r, g, b; | ||
382 | |||
383 | print_get_colour(fe->print_dr, colour, fe->printcolour, | ||
384 | &(fe->hatch), &r, &g, &b); | ||
385 | |||
386 | if (fe->hatch < 0) | ||
387 | cairo_set_source_rgb(fe->cr, r, g, b); | ||
330 | } | 388 | } |
331 | 389 | ||
332 | static void set_window_background(frontend *fe, int colour) | 390 | static void set_window_background(frontend *fe, int colour) |
@@ -395,6 +453,82 @@ static void save_screenshot_png(frontend *fe, const char *screenshot_file) | |||
395 | cairo_surface_write_to_png(fe->image, screenshot_file); | 453 | cairo_surface_write_to_png(fe->image, screenshot_file); |
396 | } | 454 | } |
397 | 455 | ||
456 | static void do_hatch(frontend *fe) | ||
457 | { | ||
458 | double i, x, y, width, height, maxdim; | ||
459 | |||
460 | /* Get the dimensions of the region to be hatched. */ | ||
461 | cairo_path_extents(fe->cr, &x, &y, &width, &height); | ||
462 | |||
463 | maxdim = max(width, height); | ||
464 | |||
465 | cairo_save(fe->cr); | ||
466 | |||
467 | /* Set the line color and width. */ | ||
468 | cairo_set_source_rgb(fe->cr, 0, 0, 0); | ||
469 | cairo_set_line_width(fe->cr, fe->hatchthick); | ||
470 | /* Clip to the region. */ | ||
471 | cairo_clip(fe->cr); | ||
472 | /* Hatch the bounding area of the fill region. */ | ||
473 | if (fe->hatch == HATCH_VERT || fe->hatch == HATCH_PLUS) { | ||
474 | for (i = 0.0; i <= width; i += fe->hatchspace) { | ||
475 | cairo_move_to(fe->cr, i, 0); | ||
476 | cairo_rel_line_to(fe->cr, 0, height); | ||
477 | } | ||
478 | } | ||
479 | if (fe->hatch == HATCH_HORIZ || fe->hatch == HATCH_PLUS) { | ||
480 | for (i = 0.0; i <= height; i += fe->hatchspace) { | ||
481 | cairo_move_to(fe->cr, 0, i); | ||
482 | cairo_rel_line_to(fe->cr, width, 0); | ||
483 | } | ||
484 | } | ||
485 | if (fe->hatch == HATCH_SLASH || fe->hatch == HATCH_X) { | ||
486 | for (i = -height; i <= width; i += fe->hatchspace * ROOT2) { | ||
487 | cairo_move_to(fe->cr, i, 0); | ||
488 | cairo_rel_line_to(fe->cr, maxdim, maxdim); | ||
489 | } | ||
490 | } | ||
491 | if (fe->hatch == HATCH_BACKSLASH || fe->hatch == HATCH_X) { | ||
492 | for (i = 0.0; i <= width + height; i += fe->hatchspace * ROOT2) { | ||
493 | cairo_move_to(fe->cr, i, 0); | ||
494 | cairo_rel_line_to(fe->cr, -maxdim, maxdim); | ||
495 | } | ||
496 | } | ||
497 | cairo_stroke(fe->cr); | ||
498 | |||
499 | cairo_restore(fe->cr); | ||
500 | } | ||
501 | |||
502 | static void do_draw_fill(frontend *fe) | ||
503 | { | ||
504 | cairo_fill(fe->cr); | ||
505 | } | ||
506 | |||
507 | static void do_draw_fill_preserve(frontend *fe) | ||
508 | { | ||
509 | cairo_fill_preserve(fe->cr); | ||
510 | } | ||
511 | |||
512 | static void do_print_fill(frontend *fe) | ||
513 | { | ||
514 | if (fe->hatch < 0) | ||
515 | cairo_fill(fe->cr); | ||
516 | else | ||
517 | do_hatch(fe); | ||
518 | } | ||
519 | |||
520 | static void do_print_fill_preserve(frontend *fe) | ||
521 | { | ||
522 | if (fe->hatch < 0) { | ||
523 | cairo_fill_preserve(fe->cr); | ||
524 | } else { | ||
525 | cairo_path_t *oldpath; | ||
526 | oldpath = cairo_copy_path(fe->cr); | ||
527 | do_hatch(fe); | ||
528 | cairo_append_path(fe->cr, oldpath); | ||
529 | } | ||
530 | } | ||
531 | |||
398 | static void do_clip(frontend *fe, int x, int y, int w, int h) | 532 | static void do_clip(frontend *fe, int x, int y, int w, int h) |
399 | { | 533 | { |
400 | cairo_new_path(fe->cr); | 534 | cairo_new_path(fe->cr); |
@@ -413,7 +547,7 @@ static void do_draw_rect(frontend *fe, int x, int y, int w, int h) | |||
413 | cairo_new_path(fe->cr); | 547 | cairo_new_path(fe->cr); |
414 | cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_NONE); | 548 | cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_NONE); |
415 | cairo_rectangle(fe->cr, x, y, w, h); | 549 | cairo_rectangle(fe->cr, x, y, w, h); |
416 | cairo_fill(fe->cr); | 550 | fe->dr_api->fill(fe); |
417 | cairo_restore(fe->cr); | 551 | cairo_restore(fe->cr); |
418 | } | 552 | } |
419 | 553 | ||
@@ -447,11 +581,11 @@ static void do_draw_poly(frontend *fe, int *coords, int npoints, | |||
447 | cairo_line_to(fe->cr, coords[i*2] + 0.5, coords[i*2 + 1] + 0.5); | 581 | cairo_line_to(fe->cr, coords[i*2] + 0.5, coords[i*2 + 1] + 0.5); |
448 | cairo_close_path(fe->cr); | 582 | cairo_close_path(fe->cr); |
449 | if (fillcolour >= 0) { | 583 | if (fillcolour >= 0) { |
450 | set_colour(fe, fillcolour); | 584 | fe->dr_api->set_colour(fe, fillcolour); |
451 | cairo_fill_preserve(fe->cr); | 585 | fe->dr_api->fill_preserve(fe); |
452 | } | 586 | } |
453 | assert(outlinecolour >= 0); | 587 | assert(outlinecolour >= 0); |
454 | set_colour(fe, outlinecolour); | 588 | fe->dr_api->set_colour(fe, outlinecolour); |
455 | cairo_stroke(fe->cr); | 589 | cairo_stroke(fe->cr); |
456 | } | 590 | } |
457 | 591 | ||
@@ -462,11 +596,11 @@ static void do_draw_circle(frontend *fe, int cx, int cy, int radius, | |||
462 | cairo_arc(fe->cr, cx + 0.5, cy + 0.5, radius, 0, 2*PI); | 596 | cairo_arc(fe->cr, cx + 0.5, cy + 0.5, radius, 0, 2*PI); |
463 | cairo_close_path(fe->cr); /* Just in case... */ | 597 | cairo_close_path(fe->cr); /* Just in case... */ |
464 | if (fillcolour >= 0) { | 598 | if (fillcolour >= 0) { |
465 | set_colour(fe, fillcolour); | 599 | fe->dr_api->set_colour(fe, fillcolour); |
466 | cairo_fill_preserve(fe->cr); | 600 | fe->dr_api->fill_preserve(fe); |
467 | } | 601 | } |
468 | assert(outlinecolour >= 0); | 602 | assert(outlinecolour >= 0); |
469 | set_colour(fe, outlinecolour); | 603 | fe->dr_api->set_colour(fe, outlinecolour); |
470 | cairo_stroke(fe->cr); | 604 | cairo_stroke(fe->cr); |
471 | } | 605 | } |
472 | 606 | ||
@@ -512,23 +646,24 @@ static void wipe_and_maybe_destroy_cairo(frontend *fe, cairo_t *cr, | |||
512 | static void setup_backing_store(frontend *fe) | 646 | static void setup_backing_store(frontend *fe) |
513 | { | 647 | { |
514 | #ifndef USE_CAIRO_WITHOUT_PIXMAP | 648 | #ifndef USE_CAIRO_WITHOUT_PIXMAP |
515 | if (fe->headless) { | 649 | if (!fe->headless) { |
516 | fprintf(stderr, "headless mode does not work with GDK pixmaps\n"); | 650 | fe->pixmap = gdk_pixmap_new(gtk_widget_get_window(fe->area), |
517 | exit(1); | 651 | fe->pw*fe->ps, fe->ph*fe->ps, -1); |
652 | } else { | ||
653 | fe->pixmap = NULL; | ||
518 | } | 654 | } |
519 | |||
520 | fe->pixmap = gdk_pixmap_new(gtk_widget_get_window(fe->area), | ||
521 | fe->pw, fe->ph, -1); | ||
522 | #endif | 655 | #endif |
656 | |||
523 | fe->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, | 657 | fe->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, |
524 | fe->pw, fe->ph); | 658 | fe->pw*fe->ps, fe->ph*fe->ps); |
525 | 659 | ||
526 | wipe_and_maybe_destroy_cairo(fe, cairo_create(fe->image), true); | 660 | wipe_and_maybe_destroy_cairo(fe, cairo_create(fe->image), true); |
527 | #ifndef USE_CAIRO_WITHOUT_PIXMAP | 661 | #ifndef USE_CAIRO_WITHOUT_PIXMAP |
528 | wipe_and_maybe_destroy_cairo(fe, gdk_cairo_create(fe->pixmap), true); | 662 | if (!fe->headless) |
663 | wipe_and_maybe_destroy_cairo(fe, gdk_cairo_create(fe->pixmap), true); | ||
529 | #endif | 664 | #endif |
530 | #if GTK_CHECK_VERSION(3,22,0) | ||
531 | if (!fe->headless) { | 665 | if (!fe->headless) { |
666 | #if GTK_CHECK_VERSION(3,22,0) | ||
532 | GdkWindow *gdkwin; | 667 | GdkWindow *gdkwin; |
533 | cairo_region_t *region; | 668 | cairo_region_t *region; |
534 | GdkDrawingContext *drawctx; | 669 | GdkDrawingContext *drawctx; |
@@ -541,11 +676,11 @@ static void setup_backing_store(frontend *fe) | |||
541 | wipe_and_maybe_destroy_cairo(fe, cr, false); | 676 | wipe_and_maybe_destroy_cairo(fe, cr, false); |
542 | gdk_window_end_draw_frame(gdkwin, drawctx); | 677 | gdk_window_end_draw_frame(gdkwin, drawctx); |
543 | cairo_region_destroy(region); | 678 | cairo_region_destroy(region); |
544 | } | ||
545 | #else | 679 | #else |
546 | wipe_and_maybe_destroy_cairo( | 680 | wipe_and_maybe_destroy_cairo( |
547 | fe, gdk_cairo_create(gtk_widget_get_window(fe->area)), true); | 681 | fe, gdk_cairo_create(gtk_widget_get_window(fe->area)), true); |
548 | #endif | 682 | #endif |
683 | } | ||
549 | } | 684 | } |
550 | 685 | ||
551 | static bool backing_store_ok(frontend *fe) | 686 | static bool backing_store_ok(frontend *fe) |
@@ -616,7 +751,7 @@ static void set_window_background(frontend *fe, int colour) | |||
616 | gdk_window_set_background(fe->window->window, &fe->colours[colour]); | 751 | gdk_window_set_background(fe->window->window, &fe->colours[colour]); |
617 | } | 752 | } |
618 | 753 | ||
619 | static void set_colour(frontend *fe, int colour) | 754 | static void draw_set_colour(frontend *fe, int colour) |
620 | { | 755 | { |
621 | gdk_gc_set_foreground(fe->gc, &fe->colours[colour]); | 756 | gdk_gc_set_foreground(fe->gc, &fe->colours[colour]); |
622 | } | 757 | } |
@@ -709,11 +844,11 @@ static void do_draw_poly(frontend *fe, int *coords, int npoints, | |||
709 | } | 844 | } |
710 | 845 | ||
711 | if (fillcolour >= 0) { | 846 | if (fillcolour >= 0) { |
712 | set_colour(fe, fillcolour); | 847 | fe->dr_api->set_colour(fe, fillcolour); |
713 | gdk_draw_polygon(fe->pixmap, fe->gc, true, points, npoints); | 848 | gdk_draw_polygon(fe->pixmap, fe->gc, true, points, npoints); |
714 | } | 849 | } |
715 | assert(outlinecolour >= 0); | 850 | assert(outlinecolour >= 0); |
716 | set_colour(fe, outlinecolour); | 851 | fe->dr_api->set_colour(fe, outlinecolour); |
717 | 852 | ||
718 | /* | 853 | /* |
719 | * In principle we ought to be able to use gdk_draw_polygon for | 854 | * In principle we ought to be able to use gdk_draw_polygon for |
@@ -733,14 +868,14 @@ static void do_draw_circle(frontend *fe, int cx, int cy, int radius, | |||
733 | int fillcolour, int outlinecolour) | 868 | int fillcolour, int outlinecolour) |
734 | { | 869 | { |
735 | if (fillcolour >= 0) { | 870 | if (fillcolour >= 0) { |
736 | set_colour(fe, fillcolour); | 871 | fe->dr_api->set_colour(fe, fillcolour); |
737 | gdk_draw_arc(fe->pixmap, fe->gc, true, | 872 | gdk_draw_arc(fe->pixmap, fe->gc, true, |
738 | cx - radius, cy - radius, | 873 | cx - radius, cy - radius, |
739 | 2 * radius, 2 * radius, 0, 360 * 64); | 874 | 2 * radius, 2 * radius, 0, 360 * 64); |
740 | } | 875 | } |
741 | 876 | ||
742 | assert(outlinecolour >= 0); | 877 | assert(outlinecolour >= 0); |
743 | set_colour(fe, outlinecolour); | 878 | fe->dr_api->set_colour(fe, outlinecolour); |
744 | gdk_draw_arc(fe->pixmap, fe->gc, false, | 879 | gdk_draw_arc(fe->pixmap, fe->gc, false, |
745 | cx - radius, cy - radius, | 880 | cx - radius, cy - radius, |
746 | 2 * radius, 2 * radius, 0, 360 * 64); | 881 | 2 * radius, 2 * radius, 0, 360 * 64); |
@@ -1045,21 +1180,21 @@ void gtk_draw_text(void *handle, int x, int y, int fonttype, int fontsize, | |||
1045 | /* | 1180 | /* |
1046 | * Do the job. | 1181 | * Do the job. |
1047 | */ | 1182 | */ |
1048 | set_colour(fe, colour); | 1183 | fe->dr_api->set_colour(fe, colour); |
1049 | align_and_draw_text(fe, i, align, x, y, text); | 1184 | align_and_draw_text(fe, i, align, x, y, text); |
1050 | } | 1185 | } |
1051 | 1186 | ||
1052 | void gtk_draw_rect(void *handle, int x, int y, int w, int h, int colour) | 1187 | void gtk_draw_rect(void *handle, int x, int y, int w, int h, int colour) |
1053 | { | 1188 | { |
1054 | frontend *fe = (frontend *)handle; | 1189 | frontend *fe = (frontend *)handle; |
1055 | set_colour(fe, colour); | 1190 | fe->dr_api->set_colour(fe, colour); |
1056 | do_draw_rect(fe, x, y, w, h); | 1191 | do_draw_rect(fe, x, y, w, h); |
1057 | } | 1192 | } |
1058 | 1193 | ||
1059 | void gtk_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) | 1194 | void gtk_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) |
1060 | { | 1195 | { |
1061 | frontend *fe = (frontend *)handle; | 1196 | frontend *fe = (frontend *)handle; |
1062 | set_colour(fe, colour); | 1197 | fe->dr_api->set_colour(fe, colour); |
1063 | do_draw_line(fe, x1, y1, x2, y2); | 1198 | do_draw_line(fe, x1, y1, x2, y2); |
1064 | } | 1199 | } |
1065 | 1200 | ||
@@ -1067,7 +1202,7 @@ void gtk_draw_thick_line(void *handle, float thickness, | |||
1067 | float x1, float y1, float x2, float y2, int colour) | 1202 | float x1, float y1, float x2, float y2, int colour) |
1068 | { | 1203 | { |
1069 | frontend *fe = (frontend *)handle; | 1204 | frontend *fe = (frontend *)handle; |
1070 | set_colour(fe, colour); | 1205 | fe->dr_api->set_colour(fe, colour); |
1071 | do_draw_thick_line(fe, thickness, x1, y1, x2, y2); | 1206 | do_draw_thick_line(fe, thickness, x1, y1, x2, y2); |
1072 | } | 1207 | } |
1073 | 1208 | ||
@@ -1161,6 +1296,105 @@ char *gtk_text_fallback(void *handle, const char *const *strings, int nstrings) | |||
1161 | } | 1296 | } |
1162 | #endif | 1297 | #endif |
1163 | 1298 | ||
1299 | #ifdef USE_PRINTING | ||
1300 | void gtk_begin_doc(void *handle, int pages) | ||
1301 | { | ||
1302 | frontend *fe = (frontend *)handle; | ||
1303 | gtk_print_operation_set_n_pages(fe->printop, pages); | ||
1304 | } | ||
1305 | |||
1306 | void gtk_begin_page(void *handle, int number) | ||
1307 | { | ||
1308 | } | ||
1309 | |||
1310 | void gtk_begin_puzzle(void *handle, float xm, float xc, | ||
1311 | float ym, float yc, int pw, int ph, float wmm) | ||
1312 | { | ||
1313 | frontend *fe = (frontend *)handle; | ||
1314 | double ppw, pph, pox, poy, dpmmx, dpmmy; | ||
1315 | double scale; | ||
1316 | |||
1317 | ppw = gtk_print_context_get_width(fe->printcontext); | ||
1318 | pph = gtk_print_context_get_height(fe->printcontext); | ||
1319 | dpmmx = gtk_print_context_get_dpi_x(fe->printcontext) / 25.4; | ||
1320 | dpmmy = gtk_print_context_get_dpi_y(fe->printcontext) / 25.4; | ||
1321 | |||
1322 | /* | ||
1323 | * Compute the puzzle's position in pixels on the logical page. | ||
1324 | */ | ||
1325 | pox = xm * ppw + xc * dpmmx; | ||
1326 | poy = ym * pph + yc * dpmmy; | ||
1327 | |||
1328 | /* | ||
1329 | * And determine the scale. | ||
1330 | * | ||
1331 | * I need a scale such that the maximum puzzle-coordinate | ||
1332 | * extent of the rectangle (pw * scale) is equal to the pixel | ||
1333 | * equivalent of the puzzle's millimetre width (wmm * dpmmx). | ||
1334 | */ | ||
1335 | scale = wmm * dpmmx / pw; | ||
1336 | |||
1337 | /* | ||
1338 | * Now instruct Cairo to transform points based on our calculated | ||
1339 | * values (order here *is* important). | ||
1340 | */ | ||
1341 | cairo_save(fe->cr); | ||
1342 | cairo_translate(fe->cr, pox, poy); | ||
1343 | cairo_scale(fe->cr, scale, scale); | ||
1344 | |||
1345 | fe->hatchthick = 0.2 * pw / wmm; | ||
1346 | fe->hatchspace = 1.0 * pw / wmm; | ||
1347 | } | ||
1348 | |||
1349 | void gtk_end_puzzle(void *handle) | ||
1350 | { | ||
1351 | frontend *fe = (frontend *)handle; | ||
1352 | cairo_restore(fe->cr); | ||
1353 | } | ||
1354 | |||
1355 | void gtk_end_page(void *handle, int number) | ||
1356 | { | ||
1357 | } | ||
1358 | |||
1359 | void gtk_end_doc(void *handle) | ||
1360 | { | ||
1361 | } | ||
1362 | |||
1363 | void gtk_line_width(void *handle, float width) | ||
1364 | { | ||
1365 | frontend *fe = (frontend *)handle; | ||
1366 | cairo_set_line_width(fe->cr, width); | ||
1367 | } | ||
1368 | |||
1369 | void gtk_line_dotted(void *handle, bool dotted) | ||
1370 | { | ||
1371 | frontend *fe = (frontend *)handle; | ||
1372 | |||
1373 | if (dotted) { | ||
1374 | const double dash = 35.0; | ||
1375 | cairo_set_dash(fe->cr, &dash, 1, 0); | ||
1376 | } else { | ||
1377 | cairo_set_dash(fe->cr, NULL, 0, 0); | ||
1378 | } | ||
1379 | } | ||
1380 | #endif /* USE_PRINTING */ | ||
1381 | |||
1382 | const struct internal_drawing_api internal_drawing = { | ||
1383 | draw_set_colour, | ||
1384 | #ifdef USE_CAIRO | ||
1385 | do_draw_fill, | ||
1386 | do_draw_fill_preserve, | ||
1387 | #endif | ||
1388 | }; | ||
1389 | |||
1390 | #ifdef USE_CAIRO | ||
1391 | const struct internal_drawing_api internal_printing = { | ||
1392 | print_set_colour, | ||
1393 | do_print_fill, | ||
1394 | do_print_fill_preserve, | ||
1395 | }; | ||
1396 | #endif | ||
1397 | |||
1164 | const struct drawing_api gtk_drawing = { | 1398 | const struct drawing_api gtk_drawing = { |
1165 | gtk_draw_text, | 1399 | gtk_draw_text, |
1166 | gtk_draw_rect, | 1400 | gtk_draw_rect, |
@@ -1177,8 +1411,19 @@ const struct drawing_api gtk_drawing = { | |||
1177 | gtk_blitter_free, | 1411 | gtk_blitter_free, |
1178 | gtk_blitter_save, | 1412 | gtk_blitter_save, |
1179 | gtk_blitter_load, | 1413 | gtk_blitter_load, |
1414 | #ifdef USE_PRINTING | ||
1415 | gtk_begin_doc, | ||
1416 | gtk_begin_page, | ||
1417 | gtk_begin_puzzle, | ||
1418 | gtk_end_puzzle, | ||
1419 | gtk_end_page, | ||
1420 | gtk_end_doc, | ||
1421 | gtk_line_width, | ||
1422 | gtk_line_dotted, | ||
1423 | #else | ||
1180 | NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */ | 1424 | NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */ |
1181 | NULL, NULL, /* line_width, line_dotted */ | 1425 | NULL, NULL, /* line_width, line_dotted */ |
1426 | #endif | ||
1182 | #ifdef USE_PANGO | 1427 | #ifdef USE_PANGO |
1183 | gtk_text_fallback, | 1428 | gtk_text_fallback, |
1184 | #else | 1429 | #else |
@@ -1340,12 +1585,22 @@ static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data) | |||
1340 | frontend *fe = (frontend *)data; | 1585 | frontend *fe = (frontend *)data; |
1341 | GdkRectangle dirtyrect; | 1586 | GdkRectangle dirtyrect; |
1342 | 1587 | ||
1588 | cairo_surface_t *target_surface = cairo_get_target(cr); | ||
1589 | cairo_matrix_t m; | ||
1590 | cairo_get_matrix(cr, &m); | ||
1591 | double orig_sx, orig_sy; | ||
1592 | cairo_surface_get_device_scale(target_surface, &orig_sx, &orig_sy); | ||
1593 | cairo_surface_set_device_scale(target_surface, 1.0, 1.0); | ||
1594 | cairo_translate(cr, m.x0 * (orig_sx - 1.0), m.y0 * (orig_sy - 1.0)); | ||
1595 | |||
1343 | gdk_cairo_get_clip_rectangle(cr, &dirtyrect); | 1596 | gdk_cairo_get_clip_rectangle(cr, &dirtyrect); |
1344 | cairo_set_source_surface(cr, fe->image, fe->ox, fe->oy); | 1597 | cairo_set_source_surface(cr, fe->image, fe->ox, fe->oy); |
1345 | cairo_rectangle(cr, dirtyrect.x, dirtyrect.y, | 1598 | cairo_rectangle(cr, dirtyrect.x, dirtyrect.y, |
1346 | dirtyrect.width, dirtyrect.height); | 1599 | dirtyrect.width, dirtyrect.height); |
1347 | cairo_fill(cr); | 1600 | cairo_fill(cr); |
1348 | 1601 | ||
1602 | cairo_surface_set_device_scale(target_surface, orig_sx, orig_sy); | ||
1603 | |||
1349 | return true; | 1604 | return true; |
1350 | } | 1605 | } |
1351 | #else | 1606 | #else |
@@ -1390,16 +1645,22 @@ static gint map_window(GtkWidget *widget, GdkEvent *event, | |||
1390 | static void resize_puzzle_to_area(frontend *fe, int x, int y) | 1645 | static void resize_puzzle_to_area(frontend *fe, int x, int y) |
1391 | { | 1646 | { |
1392 | int oldw = fe->w, oldpw = fe->pw, oldh = fe->h, oldph = fe->ph; | 1647 | int oldw = fe->w, oldpw = fe->pw, oldh = fe->h, oldph = fe->ph; |
1648 | int oldps = fe->ps; | ||
1393 | 1649 | ||
1394 | fe->w = x; | 1650 | fe->w = x; |
1395 | fe->h = y; | 1651 | fe->h = y; |
1396 | midend_size(fe->me, &x, &y, true); | 1652 | midend_size(fe->me, &x, &y, true); |
1397 | fe->pw = x; | 1653 | fe->pw = x; |
1398 | fe->ph = y; | 1654 | fe->ph = y; |
1655 | #if GTK_CHECK_VERSION(3,10,0) | ||
1656 | fe->ps = gtk_widget_get_scale_factor(fe->area); | ||
1657 | #else | ||
1658 | fe->ps = 1; | ||
1659 | #endif | ||
1399 | fe->ox = (fe->w - fe->pw) / 2; | 1660 | fe->ox = (fe->w - fe->pw) / 2; |
1400 | fe->oy = (fe->h - fe->ph) / 2; | 1661 | fe->oy = (fe->h - fe->ph) / 2; |
1401 | 1662 | ||
1402 | if (oldw != fe->w || oldpw != fe->pw || | 1663 | if (oldw != fe->w || oldpw != fe->pw || oldps != fe->ps || |
1403 | oldh != fe->h || oldph != fe->ph || !backing_store_ok(fe)) { | 1664 | oldh != fe->h || oldph != fe->ph || !backing_store_ok(fe)) { |
1404 | if (backing_store_ok(fe)) | 1665 | if (backing_store_ok(fe)) |
1405 | teardown_backing_store(fe); | 1666 | teardown_backing_store(fe); |
@@ -1413,6 +1674,7 @@ static gint configure_area(GtkWidget *widget, | |||
1413 | GdkEventConfigure *event, gpointer data) | 1674 | GdkEventConfigure *event, gpointer data) |
1414 | { | 1675 | { |
1415 | frontend *fe = (frontend *)data; | 1676 | frontend *fe = (frontend *)data; |
1677 | |||
1416 | resize_puzzle_to_area(fe, event->width, event->height); | 1678 | resize_puzzle_to_area(fe, event->width, event->height); |
1417 | #if GTK_CHECK_VERSION(3,0,0) | 1679 | #if GTK_CHECK_VERSION(3,0,0) |
1418 | fe->awaiting_resize_ack = false; | 1680 | fe->awaiting_resize_ack = false; |
@@ -2245,6 +2507,317 @@ static char *file_selector(frontend *fe, const char *title, bool save) | |||
2245 | 2507 | ||
2246 | #endif | 2508 | #endif |
2247 | 2509 | ||
2510 | #ifdef USE_PRINTING | ||
2511 | GObject *create_print_widget(GtkPrintOperation *print, gpointer data) | ||
2512 | { | ||
2513 | GtkLabel *count_label, *width_label, *height_label, | ||
2514 | *scale_llabel, *scale_rlabel; | ||
2515 | GtkBox *scale_hbox; | ||
2516 | GtkWidget *grid; | ||
2517 | frontend *fe = (frontend *)data; | ||
2518 | |||
2519 | fe->printcount_spin_button = | ||
2520 | GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1, 999, 1)); | ||
2521 | gtk_spin_button_set_numeric(fe->printcount_spin_button, true); | ||
2522 | gtk_spin_button_set_snap_to_ticks(fe->printcount_spin_button, true); | ||
2523 | fe->printw_spin_button = | ||
2524 | GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1, 99, 1)); | ||
2525 | gtk_spin_button_set_numeric(fe->printw_spin_button, true); | ||
2526 | gtk_spin_button_set_snap_to_ticks(fe->printw_spin_button, true); | ||
2527 | fe->printh_spin_button = | ||
2528 | GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1, 99, 1)); | ||
2529 | gtk_spin_button_set_numeric(fe->printh_spin_button, true); | ||
2530 | gtk_spin_button_set_snap_to_ticks(fe->printh_spin_button, true); | ||
2531 | fe->printscale_spin_button = | ||
2532 | GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1, 1000, 1)); | ||
2533 | gtk_spin_button_set_digits(fe->printscale_spin_button, 1); | ||
2534 | gtk_spin_button_set_numeric(fe->printscale_spin_button, true); | ||
2535 | if (thegame.can_solve) { | ||
2536 | fe->soln_check_button = | ||
2537 | GTK_CHECK_BUTTON( | ||
2538 | gtk_check_button_new_with_label("Print solutions")); | ||
2539 | } | ||
2540 | if (thegame.can_print_in_colour) { | ||
2541 | fe->colour_check_button = | ||
2542 | GTK_CHECK_BUTTON( | ||
2543 | gtk_check_button_new_with_label("Print in color")); | ||
2544 | } | ||
2545 | |||
2546 | /* Set defaults to what was selected last time. */ | ||
2547 | gtk_spin_button_set_value(fe->printcount_spin_button, | ||
2548 | (gdouble)fe->printcount); | ||
2549 | gtk_spin_button_set_value(fe->printw_spin_button, | ||
2550 | (gdouble)fe->printw); | ||
2551 | gtk_spin_button_set_value(fe->printh_spin_button, | ||
2552 | (gdouble)fe->printh); | ||
2553 | gtk_spin_button_set_value(fe->printscale_spin_button, | ||
2554 | (gdouble)fe->printscale); | ||
2555 | if (thegame.can_solve) { | ||
2556 | gtk_toggle_button_set_active( | ||
2557 | GTK_TOGGLE_BUTTON(fe->soln_check_button), fe->printsolns); | ||
2558 | } | ||
2559 | if (thegame.can_print_in_colour) { | ||
2560 | gtk_toggle_button_set_active( | ||
2561 | GTK_TOGGLE_BUTTON(fe->colour_check_button), fe->printcolour); | ||
2562 | } | ||
2563 | |||
2564 | count_label = GTK_LABEL(gtk_label_new("Puzzles to print:")); | ||
2565 | width_label = GTK_LABEL(gtk_label_new("Puzzles across:")); | ||
2566 | height_label = GTK_LABEL(gtk_label_new("Puzzles down:")); | ||
2567 | scale_llabel = GTK_LABEL(gtk_label_new("Puzzle scale:")); | ||
2568 | scale_rlabel = GTK_LABEL(gtk_label_new("%")); | ||
2569 | #if GTK_CHECK_VERSION(3,0,0) | ||
2570 | gtk_widget_set_halign(GTK_WIDGET(count_label), GTK_ALIGN_START); | ||
2571 | gtk_widget_set_halign(GTK_WIDGET(width_label), GTK_ALIGN_START); | ||
2572 | gtk_widget_set_halign(GTK_WIDGET(height_label), GTK_ALIGN_START); | ||
2573 | gtk_widget_set_halign(GTK_WIDGET(scale_llabel), GTK_ALIGN_START); | ||
2574 | #else | ||
2575 | gtk_misc_set_alignment(GTK_MISC(count_label), 0, 0); | ||
2576 | gtk_misc_set_alignment(GTK_MISC(width_label), 0, 0); | ||
2577 | gtk_misc_set_alignment(GTK_MISC(height_label), 0, 0); | ||
2578 | gtk_misc_set_alignment(GTK_MISC(scale_llabel), 0, 0); | ||
2579 | #endif | ||
2580 | |||
2581 | scale_hbox = GTK_BOX(gtk_hbox_new(false, 6)); | ||
2582 | gtk_box_pack_start(scale_hbox, GTK_WIDGET(fe->printscale_spin_button), | ||
2583 | false, false, 0); | ||
2584 | gtk_box_pack_start(scale_hbox, GTK_WIDGET(scale_rlabel), | ||
2585 | false, false, 0); | ||
2586 | |||
2587 | #if GTK_CHECK_VERSION(3,0,0) | ||
2588 | grid = gtk_grid_new(); | ||
2589 | gtk_grid_set_column_spacing(GTK_GRID(grid), 18); | ||
2590 | gtk_grid_set_row_spacing(GTK_GRID(grid), 18); | ||
2591 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(count_label), 0, 0, 1, 1); | ||
2592 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(width_label), 0, 1, 1, 1); | ||
2593 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(height_label), 0, 2, 1, 1); | ||
2594 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(scale_llabel), 0, 3, 1, 1); | ||
2595 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->printcount_spin_button), | ||
2596 | 1, 0, 1, 1); | ||
2597 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->printw_spin_button), | ||
2598 | 1, 1, 1, 1); | ||
2599 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->printh_spin_button), | ||
2600 | 1, 2, 1, 1); | ||
2601 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(scale_hbox), 1, 3, 1, 1); | ||
2602 | if (thegame.can_solve) { | ||
2603 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->soln_check_button), | ||
2604 | 0, 4, 1, 1); | ||
2605 | } | ||
2606 | if (thegame.can_print_in_colour) { | ||
2607 | gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(fe->colour_check_button), | ||
2608 | thegame.can_solve, 4, 1, 1); | ||
2609 | } | ||
2610 | #else | ||
2611 | grid = gtk_table_new((thegame.can_solve || thegame.can_print_in_colour) ? | ||
2612 | 5 : 4, 2, false); | ||
2613 | gtk_table_set_col_spacings(GTK_TABLE(grid), 18); | ||
2614 | gtk_table_set_row_spacings(GTK_TABLE(grid), 18); | ||
2615 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(count_label), 0, 1, 0, 1, | ||
2616 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2617 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(width_label), 0, 1, 1, 2, | ||
2618 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2619 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(height_label), 0, 1, 2, 3, | ||
2620 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2621 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(scale_llabel), 0, 1, 3, 4, | ||
2622 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2623 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->printcount_spin_button), | ||
2624 | 1, 2, 0, 1, | ||
2625 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2626 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->printw_spin_button), | ||
2627 | 1, 2, 1, 2, | ||
2628 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2629 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->printh_spin_button), | ||
2630 | 1, 2, 2, 3, | ||
2631 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2632 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(scale_hbox), 1, 2, 3, 4, | ||
2633 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2634 | if (thegame.can_solve) { | ||
2635 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->soln_check_button), | ||
2636 | 0, 1, 4, 5, | ||
2637 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2638 | } | ||
2639 | if (thegame.can_print_in_colour) { | ||
2640 | gtk_table_attach(GTK_TABLE(grid), GTK_WIDGET(fe->colour_check_button), | ||
2641 | thegame.can_solve, thegame.can_solve + 1, 4, 5, | ||
2642 | GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); | ||
2643 | } | ||
2644 | #endif | ||
2645 | gtk_container_set_border_width(GTK_CONTAINER(grid), 12); | ||
2646 | |||
2647 | gtk_widget_show_all(grid); | ||
2648 | |||
2649 | return G_OBJECT(grid); | ||
2650 | } | ||
2651 | |||
2652 | void apply_print_widget(GtkPrintOperation *print, | ||
2653 | GtkWidget *widget, gpointer data) | ||
2654 | { | ||
2655 | frontend *fe = (frontend *)data; | ||
2656 | |||
2657 | /* We ignore `widget' because it is easier and faster to store the | ||
2658 | widgets we need in `fe' then to get the children of `widget'. */ | ||
2659 | fe->printcount = | ||
2660 | gtk_spin_button_get_value_as_int(fe->printcount_spin_button); | ||
2661 | fe->printw = gtk_spin_button_get_value_as_int(fe->printw_spin_button); | ||
2662 | fe->printh = gtk_spin_button_get_value_as_int(fe->printh_spin_button); | ||
2663 | fe->printscale = gtk_spin_button_get_value(fe->printscale_spin_button); | ||
2664 | if (thegame.can_solve) { | ||
2665 | fe->printsolns = | ||
2666 | gtk_toggle_button_get_active( | ||
2667 | GTK_TOGGLE_BUTTON(fe->soln_check_button)); | ||
2668 | } | ||
2669 | if (thegame.can_print_in_colour) { | ||
2670 | fe->printcolour = | ||
2671 | gtk_toggle_button_get_active( | ||
2672 | GTK_TOGGLE_BUTTON(fe->colour_check_button)); | ||
2673 | } | ||
2674 | } | ||
2675 | |||
2676 | void print_begin(GtkPrintOperation *printop, | ||
2677 | GtkPrintContext *context, gpointer data) | ||
2678 | { | ||
2679 | frontend *fe = (frontend *)data; | ||
2680 | midend *nme = NULL; /* non-interactive midend for bulk puzzle generation */ | ||
2681 | int i; | ||
2682 | |||
2683 | fe->printcontext = context; | ||
2684 | fe->cr = gtk_print_context_get_cairo_context(context); | ||
2685 | |||
2686 | /* | ||
2687 | * Create our document structure and fill it up with puzzles. | ||
2688 | */ | ||
2689 | fe->doc = document_new(fe->printw, fe->printh, fe->printscale / 100.0F); | ||
2690 | |||
2691 | for (i = 0; i < fe->printcount; i++) { | ||
2692 | const char *err; | ||
2693 | |||
2694 | if (i == 0) { | ||
2695 | err = midend_print_puzzle(fe->me, fe->doc, fe->printsolns); | ||
2696 | } else { | ||
2697 | if (!nme) { | ||
2698 | game_params *params; | ||
2699 | |||
2700 | nme = midend_new(NULL, &thegame, NULL, NULL); | ||
2701 | |||
2702 | /* | ||
2703 | * Set the non-interactive mid-end to have the same | ||
2704 | * parameters as the standard one. | ||
2705 | */ | ||
2706 | params = midend_get_params(fe->me); | ||
2707 | midend_set_params(nme, params); | ||
2708 | thegame.free_params(params); | ||
2709 | } | ||
2710 | |||
2711 | midend_new_game(nme); | ||
2712 | err = midend_print_puzzle(nme, fe->doc, fe->printsolns); | ||
2713 | } | ||
2714 | |||
2715 | if (err) { | ||
2716 | error_box(fe->window, err); | ||
2717 | return; | ||
2718 | } | ||
2719 | } | ||
2720 | |||
2721 | if (nme) | ||
2722 | midend_free(nme); | ||
2723 | |||
2724 | /* Begin the document. */ | ||
2725 | document_begin(fe->doc, fe->print_dr); | ||
2726 | } | ||
2727 | |||
2728 | void draw_page(GtkPrintOperation *printop, | ||
2729 | GtkPrintContext *context, | ||
2730 | gint page_nr, gpointer data) | ||
2731 | { | ||
2732 | frontend *fe = (frontend *)data; | ||
2733 | document_print_page(fe->doc, fe->print_dr, page_nr); | ||
2734 | } | ||
2735 | |||
2736 | void print_end(GtkPrintOperation *printop, | ||
2737 | GtkPrintContext *context, gpointer data) | ||
2738 | { | ||
2739 | frontend *fe = (frontend *)data; | ||
2740 | |||
2741 | /* End and free the document. */ | ||
2742 | document_end(fe->doc, fe->print_dr); | ||
2743 | document_free(fe->doc); | ||
2744 | fe->doc = NULL; | ||
2745 | } | ||
2746 | |||
2747 | static void print_dialog(frontend *fe) | ||
2748 | { | ||
2749 | GError *error; | ||
2750 | static GtkPrintSettings *settings = NULL; | ||
2751 | static GtkPageSetup *page_setup = NULL; | ||
2752 | #ifndef USE_EMBED_PAGE_SETUP | ||
2753 | GtkPageSetup *new_page_setup; | ||
2754 | #endif | ||
2755 | |||
2756 | fe->printop = gtk_print_operation_new(); | ||
2757 | gtk_print_operation_set_use_full_page(fe->printop, true); | ||
2758 | gtk_print_operation_set_custom_tab_label(fe->printop, "Puzzle Settings"); | ||
2759 | g_signal_connect(fe->printop, "create-custom-widget", | ||
2760 | G_CALLBACK(create_print_widget), fe); | ||
2761 | g_signal_connect(fe->printop, "custom-widget-apply", | ||
2762 | G_CALLBACK(apply_print_widget), fe); | ||
2763 | g_signal_connect(fe->printop, "begin-print", G_CALLBACK(print_begin), fe); | ||
2764 | g_signal_connect(fe->printop, "draw-page", G_CALLBACK(draw_page), fe); | ||
2765 | g_signal_connect(fe->printop, "end-print", G_CALLBACK(print_end), fe); | ||
2766 | #ifdef USE_EMBED_PAGE_SETUP | ||
2767 | gtk_print_operation_set_embed_page_setup(fe->printop, true); | ||
2768 | #else | ||
2769 | if (page_setup == NULL) { | ||
2770 | page_setup = | ||
2771 | g_object_ref( | ||
2772 | gtk_print_operation_get_default_page_setup(fe->printop)); | ||
2773 | } | ||
2774 | if (settings == NULL) { | ||
2775 | settings = | ||
2776 | g_object_ref(gtk_print_operation_get_print_settings(fe->printop)); | ||
2777 | } | ||
2778 | new_page_setup = gtk_print_run_page_setup_dialog(GTK_WINDOW(fe->window), | ||
2779 | page_setup, settings); | ||
2780 | g_object_unref(page_setup); | ||
2781 | page_setup = new_page_setup; | ||
2782 | gtk_print_operation_set_default_page_setup(fe->printop, page_setup); | ||
2783 | #endif | ||
2784 | |||
2785 | if (settings != NULL) | ||
2786 | gtk_print_operation_set_print_settings(fe->printop, settings); | ||
2787 | if (page_setup != NULL) | ||
2788 | gtk_print_operation_set_default_page_setup(fe->printop, page_setup); | ||
2789 | |||
2790 | switch (gtk_print_operation_run(fe->printop, | ||
2791 | GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, | ||
2792 | GTK_WINDOW(fe->window), &error)) { | ||
2793 | case GTK_PRINT_OPERATION_RESULT_ERROR: | ||
2794 | error_box(fe->window, error->message); | ||
2795 | g_error_free(error); | ||
2796 | break; | ||
2797 | case GTK_PRINT_OPERATION_RESULT_APPLY: | ||
2798 | if (settings != NULL) | ||
2799 | g_object_unref(settings); | ||
2800 | settings = | ||
2801 | g_object_ref(gtk_print_operation_get_print_settings(fe->printop)); | ||
2802 | #ifdef USE_EMBED_PAGE_SETUP | ||
2803 | if (page_setup != NULL) | ||
2804 | g_object_unref(page_setup); | ||
2805 | page_setup = | ||
2806 | g_object_ref( | ||
2807 | gtk_print_operation_get_default_page_setup(fe->printop)); | ||
2808 | #endif | ||
2809 | break; | ||
2810 | default: | ||
2811 | /* Don't error out on -Werror=switch. */ | ||
2812 | break; | ||
2813 | } | ||
2814 | |||
2815 | g_object_unref(fe->printop); | ||
2816 | fe->printop = NULL; | ||
2817 | fe->printcontext = NULL; | ||
2818 | } | ||
2819 | #endif /* USE_PRINTING */ | ||
2820 | |||
2248 | struct savefile_write_ctx { | 2821 | struct savefile_write_ctx { |
2249 | FILE *fp; | 2822 | FILE *fp; |
2250 | int error; | 2823 | int error; |
@@ -2346,6 +2919,15 @@ static void menu_load_event(GtkMenuItem *menuitem, gpointer data) | |||
2346 | } | 2919 | } |
2347 | } | 2920 | } |
2348 | 2921 | ||
2922 | #ifdef USE_PRINTING | ||
2923 | static void menu_print_event(GtkMenuItem *menuitem, gpointer data) | ||
2924 | { | ||
2925 | frontend *fe = (frontend *)data; | ||
2926 | |||
2927 | print_dialog(fe); | ||
2928 | } | ||
2929 | #endif | ||
2930 | |||
2349 | static void menu_solve_event(GtkMenuItem *menuitem, gpointer data) | 2931 | static void menu_solve_event(GtkMenuItem *menuitem, gpointer data) |
2350 | { | 2932 | { |
2351 | frontend *fe = (frontend *)data; | 2933 | frontend *fe = (frontend *)data; |
@@ -2388,18 +2970,31 @@ static void menu_about_event(GtkMenuItem *menuitem, gpointer data) | |||
2388 | frontend *fe = (frontend *)data; | 2970 | frontend *fe = (frontend *)data; |
2389 | 2971 | ||
2390 | #if GTK_CHECK_VERSION(3,0,0) | 2972 | #if GTK_CHECK_VERSION(3,0,0) |
2973 | # define ABOUT_PARAMS \ | ||
2974 | "program-name", thegame.name, \ | ||
2975 | "version", ver, \ | ||
2976 | "comments", "Part of Simon Tatham's Portable Puzzle Collection" | ||
2977 | |||
2391 | extern char *const *const xpm_icons[]; | 2978 | extern char *const *const xpm_icons[]; |
2392 | extern const int n_xpm_icons; | 2979 | extern const int n_xpm_icons; |
2393 | GdkPixbuf *icon = gdk_pixbuf_new_from_xpm_data | 2980 | |
2394 | ((const gchar **)xpm_icons[n_xpm_icons-1]); | 2981 | if (n_xpm_icons) { |
2395 | gtk_show_about_dialog | 2982 | GdkPixbuf *icon = gdk_pixbuf_new_from_xpm_data |
2396 | (GTK_WINDOW(fe->window), | 2983 | ((const gchar **)xpm_icons[n_xpm_icons-1]); |
2397 | "program-name", thegame.name, | 2984 | |
2398 | "version", ver, | 2985 | gtk_show_about_dialog |
2399 | "comments", "Part of Simon Tatham's Portable Puzzle Collection", | 2986 | (GTK_WINDOW(fe->window), |
2400 | "logo", icon, | 2987 | ABOUT_PARAMS, |
2401 | (const gchar *)NULL); | 2988 | "logo", icon, |
2402 | g_object_unref(G_OBJECT(icon)); | 2989 | (const gchar *)NULL); |
2990 | g_object_unref(G_OBJECT(icon)); | ||
2991 | } | ||
2992 | else { | ||
2993 | gtk_show_about_dialog | ||
2994 | (GTK_WINDOW(fe->window), | ||
2995 | ABOUT_PARAMS, | ||
2996 | (const gchar *)NULL); | ||
2997 | } | ||
2403 | #else | 2998 | #else |
2404 | char titlebuf[256]; | 2999 | char titlebuf[256]; |
2405 | char textbuf[1024]; | 3000 | char textbuf[1024]; |
@@ -2489,6 +3084,9 @@ static frontend *new_window( | |||
2489 | char *arg, int argtype, char **error, bool headless) | 3084 | char *arg, int argtype, char **error, bool headless) |
2490 | { | 3085 | { |
2491 | frontend *fe; | 3086 | frontend *fe; |
3087 | #ifdef USE_PRINTING | ||
3088 | frontend *print_fe = NULL; | ||
3089 | #endif | ||
2492 | GtkBox *vbox, *hbox; | 3090 | GtkBox *vbox, *hbox; |
2493 | GtkWidget *menu, *menuitem; | 3091 | GtkWidget *menu, *menuitem; |
2494 | GList *iconlist; | 3092 | GList *iconlist; |
@@ -2501,13 +3099,14 @@ static frontend *new_window( | |||
2501 | fe = snew(frontend); | 3099 | fe = snew(frontend); |
2502 | memset(fe, 0, sizeof(frontend)); | 3100 | memset(fe, 0, sizeof(frontend)); |
2503 | 3101 | ||
2504 | #if !GTK_CHECK_VERSION(3,0,0) | 3102 | #ifndef USE_CAIRO |
2505 | if (headless) { | 3103 | if (headless) { |
2506 | fprintf(stderr, "headless mode not supported below GTK 3\n"); | 3104 | fprintf(stderr, "headless mode not supported for non-Cairo drawing\n"); |
2507 | exit(1); | 3105 | exit(1); |
2508 | } | 3106 | } |
2509 | #else | 3107 | #else |
2510 | fe->headless = headless; | 3108 | fe->headless = headless; |
3109 | fe->ps = 1; /* in headless mode, configure_area won't have set this */ | ||
2511 | #endif | 3110 | #endif |
2512 | 3111 | ||
2513 | fe->timer_active = false; | 3112 | fe->timer_active = false; |
@@ -2515,6 +3114,32 @@ static frontend *new_window( | |||
2515 | 3114 | ||
2516 | fe->me = midend_new(fe, &thegame, >k_drawing, fe); | 3115 | fe->me = midend_new(fe, &thegame, >k_drawing, fe); |
2517 | 3116 | ||
3117 | fe->dr_api = &internal_drawing; | ||
3118 | |||
3119 | #ifdef USE_PRINTING | ||
3120 | if (thegame.can_print) { | ||
3121 | print_fe = snew(frontend); | ||
3122 | memset(print_fe, 0, sizeof(frontend)); | ||
3123 | |||
3124 | /* Defaults */ | ||
3125 | print_fe->printcount = print_fe->printw = print_fe->printh = 1; | ||
3126 | print_fe->printscale = 100; | ||
3127 | print_fe->printsolns = false; | ||
3128 | print_fe->printcolour = thegame.can_print_in_colour; | ||
3129 | |||
3130 | /* | ||
3131 | * We need to use the same midend as the main frontend because | ||
3132 | * we need midend_print_puzzle() to be able to print the | ||
3133 | * current puzzle. | ||
3134 | */ | ||
3135 | print_fe->me = fe->me; | ||
3136 | |||
3137 | print_fe->print_dr = drawing_new(>k_drawing, print_fe->me, print_fe); | ||
3138 | |||
3139 | print_fe->dr_api = &internal_printing; | ||
3140 | } | ||
3141 | #endif | ||
3142 | |||
2518 | if (arg) { | 3143 | if (arg) { |
2519 | const char *err; | 3144 | const char *err; |
2520 | FILE *fp; | 3145 | FILE *fp; |
@@ -2568,6 +3193,12 @@ static frontend *new_window( | |||
2568 | *error = dupstr(errbuf); | 3193 | *error = dupstr(errbuf); |
2569 | midend_free(fe->me); | 3194 | midend_free(fe->me); |
2570 | sfree(fe); | 3195 | sfree(fe); |
3196 | #ifdef USE_PRINTING | ||
3197 | if (thegame.can_print) { | ||
3198 | drawing_free(print_fe->print_dr); | ||
3199 | sfree(print_fe); | ||
3200 | } | ||
3201 | #endif | ||
2571 | return NULL; | 3202 | return NULL; |
2572 | } | 3203 | } |
2573 | 3204 | ||
@@ -2711,6 +3342,16 @@ static frontend *new_window( | |||
2711 | g_signal_connect(G_OBJECT(menuitem), "activate", | 3342 | g_signal_connect(G_OBJECT(menuitem), "activate", |
2712 | G_CALLBACK(menu_save_event), fe); | 3343 | G_CALLBACK(menu_save_event), fe); |
2713 | gtk_widget_show(menuitem); | 3344 | gtk_widget_show(menuitem); |
3345 | #ifdef USE_PRINTING | ||
3346 | if (thegame.can_print) { | ||
3347 | add_menu_separator(GTK_CONTAINER(menu)); | ||
3348 | menuitem = gtk_menu_item_new_with_label("Print..."); | ||
3349 | gtk_container_add(GTK_CONTAINER(menu), menuitem); | ||
3350 | g_signal_connect(G_OBJECT(menuitem), "activate", | ||
3351 | G_CALLBACK(menu_print_event), print_fe); | ||
3352 | gtk_widget_show(menuitem); | ||
3353 | } | ||
3354 | #endif | ||
2714 | #ifndef STYLUS_BASED | 3355 | #ifndef STYLUS_BASED |
2715 | add_menu_separator(GTK_CONTAINER(menu)); | 3356 | add_menu_separator(GTK_CONTAINER(menu)); |
2716 | add_menu_ui_item(fe, GTK_CONTAINER(menu), "Undo", UI_UNDO, 'u', 0); | 3357 | add_menu_ui_item(fe, GTK_CONTAINER(menu), "Undo", UI_UNDO, 'u', 0); |