~ubuntu-branches/ubuntu/quantal/sgt-puzzles/quantal

« back to all changes in this revision

Viewing changes to gtk.c

  • Committer: Bazaar Package Importer
  • Author(s): Ben Hutchings
  • Date: 2011-03-01 04:16:54 UTC
  • mfrom: (1.2.9 upstream)
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20110301041654-949qy9qrroziy7vq
* New upstream version:
  - Add Range and Signpost puzzles
  - Use stock icons and conventional order for dialog buttons
  - Use Cairo for screen rendering
* Update German translation, thanks to Helge Kreutzmann
* Remove or update patches applied or partially applied upstream
* Use Debian source format 3.0 (quilt)

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 * gtk.c: GTK front end for my puzzle collection.
3
3
 */
4
4
 
 
5
#define _GNU_SOURCE
 
6
 
5
7
#include <stdio.h>
6
8
#include <assert.h>
7
9
#include <stdlib.h>
9
11
#include <stdarg.h>
10
12
#include <string.h>
11
13
#include <errno.h>
 
14
#include <math.h>
 
15
#include <limits.h>
 
16
#include <unistd.h>
 
17
#include <locale.h>
12
18
 
13
19
#include <sys/time.h>
14
20
 
35
41
#if !GTK_CHECK_VERSION(2,4,0)
36
42
# define OLD_FILESEL
37
43
#endif
 
44
#if GTK_CHECK_VERSION(2,8,0)
 
45
# define USE_CAIRO
 
46
#endif
 
47
 
 
48
/* #undef USE_CAIRO */
 
49
/* #define NO_THICK_LINE */
 
50
 
 
51
#ifdef COMBINED
 
52
static const gameindex *thegameindex;
 
53
#define thegame (*thegameindex->game)
 
54
#define xpm_icons (thegameindex->xpm_icons)
 
55
#define n_xpm_icons (*thegameindex->n_xpm_icons)
 
56
#endif
38
57
 
39
58
#ifdef DEBUGGING
40
59
static FILE *debug_fp = NULL;
112
131
    GtkWidget *area;
113
132
    GtkWidget *statusbar;
114
133
    guint statusctx;
115
 
    GdkPixmap *pixmap;
 
134
    int w, h;
 
135
    midend *me;
 
136
#ifdef USE_CAIRO
 
137
    const float *colours;
 
138
    cairo_t *cr;
 
139
    cairo_surface_t *image;
 
140
    GdkPixmap *pixmap;
 
141
    GdkColor background;               /* for painting outside puzzle area */
 
142
#else
 
143
    GdkPixmap *pixmap;
 
144
    GdkGC *gc;
116
145
    GdkColor *colours;
 
146
    GdkColormap *colmap;
 
147
    int backgroundindex;               /* which of colours[] is background */
 
148
#endif
117
149
    int ncolours;
118
 
    GdkColormap *colmap;
119
 
    int w, h;
120
 
    midend *me;
121
 
    GdkGC *gc;
122
150
    int bbox_l, bbox_r, bbox_u, bbox_d;
123
151
    int timer_active, timer_id;
124
152
    struct timeval last_time;
141
169
    GtkWidget *copy_menu_item;
142
170
};
143
171
 
 
172
struct blitter {
 
173
#ifdef USE_CAIRO
 
174
    cairo_surface_t *image;
 
175
#else
 
176
    GdkPixmap *pixmap;
 
177
#endif
 
178
    int w, h, x, y;
 
179
};
 
180
 
144
181
void get_random_seed(void **randseed, int *randseedsize)
145
182
{
146
183
    struct timeval *tvp = snew(struct timeval);
167
204
    gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, text);
168
205
}
169
206
 
170
 
void gtk_start_draw(void *handle)
171
 
{
172
 
    frontend *fe = (frontend *)handle;
 
207
/* ----------------------------------------------------------------------
 
208
 * Cairo drawing functions.
 
209
 */
 
210
 
 
211
#ifdef USE_CAIRO
 
212
 
 
213
static void setup_drawing(frontend *fe)
 
214
{
 
215
    fe->cr = cairo_create(fe->image);
 
216
    cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_GRAY);
 
217
    cairo_set_line_width(fe->cr, 1.0);
 
218
    cairo_set_line_cap(fe->cr, CAIRO_LINE_CAP_SQUARE);
 
219
    cairo_set_line_join(fe->cr, CAIRO_LINE_JOIN_ROUND);
 
220
}
 
221
 
 
222
static void teardown_drawing(frontend *fe)
 
223
{
 
224
    cairo_t *cr;
 
225
 
 
226
    cairo_destroy(fe->cr);
 
227
    fe->cr = NULL;
 
228
 
 
229
    cr = gdk_cairo_create(fe->pixmap);
 
230
    cairo_set_source_surface(cr, fe->image, 0, 0);
 
231
    cairo_rectangle(cr,
 
232
                    fe->bbox_l - 1,
 
233
                    fe->bbox_u - 1,
 
234
                    fe->bbox_r - fe->bbox_l + 2,
 
235
                    fe->bbox_d - fe->bbox_u + 2);
 
236
    cairo_fill(cr);
 
237
    cairo_destroy(cr);
 
238
}
 
239
 
 
240
static void snaffle_colours(frontend *fe)
 
241
{
 
242
    fe->colours = midend_colours(fe->me, &fe->ncolours);
 
243
}
 
244
 
 
245
static void set_colour(frontend *fe, int colour)
 
246
{
 
247
    cairo_set_source_rgb(fe->cr,
 
248
                         fe->colours[3*colour + 0],
 
249
                         fe->colours[3*colour + 1],
 
250
                         fe->colours[3*colour + 2]);
 
251
}
 
252
 
 
253
static void set_window_background(frontend *fe, int colour)
 
254
{
 
255
    GdkColormap *colmap;
 
256
 
 
257
    colmap = gdk_colormap_get_system();
 
258
    fe->background.red = fe->colours[3*colour + 0] * 65535;
 
259
    fe->background.green = fe->colours[3*colour + 1] * 65535;
 
260
    fe->background.blue = fe->colours[3*colour + 2] * 65535;
 
261
    if (!gdk_colormap_alloc_color(colmap, &fe->background, FALSE, FALSE)) {
 
262
        g_error("couldn't allocate background (#%02x%02x%02x)\n",
 
263
                fe->background.red >> 8, fe->background.green >> 8,
 
264
                fe->background.blue >> 8);
 
265
    }
 
266
    gdk_window_set_background(fe->area->window, &fe->background);
 
267
    gdk_window_set_background(fe->window->window, &fe->background);
 
268
}
 
269
 
 
270
static PangoLayout *make_pango_layout(frontend *fe)
 
271
{
 
272
    return (pango_cairo_create_layout(fe->cr));
 
273
}
 
274
 
 
275
static void draw_pango_layout(frontend *fe, PangoLayout *layout,
 
276
                              int x, int y)
 
277
{
 
278
    cairo_move_to(fe->cr, x, y);
 
279
    pango_cairo_show_layout(fe->cr, layout);
 
280
}
 
281
 
 
282
static void save_screenshot_png(frontend *fe, const char *screenshot_file)
 
283
{
 
284
    cairo_surface_write_to_png(fe->image, screenshot_file);
 
285
}
 
286
 
 
287
static void do_clip(frontend *fe, int x, int y, int w, int h)
 
288
{
 
289
    cairo_new_path(fe->cr);
 
290
    cairo_rectangle(fe->cr, x, y, w, h);
 
291
    cairo_clip(fe->cr);
 
292
}
 
293
 
 
294
static void do_unclip(frontend *fe)
 
295
{
 
296
    cairo_reset_clip(fe->cr);
 
297
}
 
298
 
 
299
static void do_draw_rect(frontend *fe, int x, int y, int w, int h)
 
300
{
 
301
    cairo_save(fe->cr);
 
302
    cairo_new_path(fe->cr);
 
303
    cairo_set_antialias(fe->cr, CAIRO_ANTIALIAS_NONE);
 
304
    cairo_rectangle(fe->cr, x, y, w, h);
 
305
    cairo_fill(fe->cr);
 
306
    cairo_restore(fe->cr);
 
307
}
 
308
 
 
309
static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2)
 
310
{
 
311
    cairo_new_path(fe->cr);
 
312
    cairo_move_to(fe->cr, x1 + 0.5, y1 + 0.5);
 
313
    cairo_line_to(fe->cr, x2 + 0.5, y2 + 0.5);
 
314
    cairo_stroke(fe->cr);
 
315
}
 
316
 
 
317
static void do_draw_thick_line(frontend *fe, float thickness,
 
318
                               float x1, float y1, float x2, float y2)
 
319
{
 
320
    cairo_save(fe->cr);
 
321
    cairo_set_line_width(fe->cr, thickness);
 
322
    cairo_new_path(fe->cr);
 
323
    cairo_move_to(fe->cr, x1, y1);
 
324
    cairo_line_to(fe->cr, x2, y2);
 
325
    cairo_stroke(fe->cr);
 
326
    cairo_restore(fe->cr);
 
327
}
 
328
 
 
329
static void do_draw_poly(frontend *fe, int *coords, int npoints,
 
330
                         int fillcolour, int outlinecolour)
 
331
{
 
332
    int i;
 
333
 
 
334
    cairo_new_path(fe->cr);
 
335
    for (i = 0; i < npoints; i++)
 
336
        cairo_line_to(fe->cr, coords[i*2] + 0.5, coords[i*2 + 1] + 0.5);
 
337
    cairo_close_path(fe->cr);
 
338
    if (fillcolour >= 0) {
 
339
        set_colour(fe, fillcolour);
 
340
        cairo_fill_preserve(fe->cr);
 
341
    }
 
342
    assert(outlinecolour >= 0);
 
343
    set_colour(fe, outlinecolour);
 
344
    cairo_stroke(fe->cr);
 
345
}
 
346
 
 
347
static void do_draw_circle(frontend *fe, int cx, int cy, int radius,
 
348
                           int fillcolour, int outlinecolour)
 
349
{
 
350
    cairo_new_path(fe->cr);
 
351
    cairo_arc(fe->cr, cx + 0.5, cy + 0.5, radius, 0, 2*PI);
 
352
    cairo_close_path(fe->cr);           /* Just in case... */
 
353
    if (fillcolour >= 0) {
 
354
        set_colour(fe, fillcolour);
 
355
        cairo_fill_preserve(fe->cr);
 
356
    }
 
357
    assert(outlinecolour >= 0);
 
358
    set_colour(fe, outlinecolour);
 
359
    cairo_stroke(fe->cr);
 
360
}
 
361
 
 
362
static void setup_blitter(blitter *bl, int w, int h)
 
363
{
 
364
    bl->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
 
365
}
 
366
 
 
367
static void teardown_blitter(blitter *bl)
 
368
{
 
369
    cairo_surface_destroy(bl->image);
 
370
}
 
371
 
 
372
static void do_blitter_save(frontend *fe, blitter *bl, int x, int y)
 
373
{
 
374
    cairo_t *cr = cairo_create(bl->image);
 
375
 
 
376
    cairo_set_source_surface(cr, fe->image, -x, -y);
 
377
    cairo_paint(cr);
 
378
    cairo_destroy(cr);
 
379
}
 
380
 
 
381
static void do_blitter_load(frontend *fe, blitter *bl, int x, int y)
 
382
{
 
383
    cairo_set_source_surface(fe->cr, bl->image, x, y);
 
384
    cairo_paint(fe->cr);
 
385
}
 
386
 
 
387
static void clear_backing_store(frontend *fe)
 
388
{
 
389
    fe->image = NULL;
 
390
}
 
391
 
 
392
static void setup_backing_store(frontend *fe)
 
393
{
 
394
    cairo_t *cr;
 
395
    int i;
 
396
 
 
397
    fe->pixmap = gdk_pixmap_new(fe->area->window, fe->pw, fe->ph, -1);
 
398
    fe->image = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
 
399
                                           fe->pw, fe->ph);
 
400
 
 
401
    for (i = 0; i < 3; i++) {
 
402
        switch (i) {
 
403
            case 0: cr = cairo_create(fe->image); break;
 
404
            case 1: cr = gdk_cairo_create(fe->pixmap); break;
 
405
            case 2: cr = gdk_cairo_create(fe->area->window); break;
 
406
        }
 
407
        cairo_set_source_rgb(cr,
 
408
                             fe->colours[0], fe->colours[1], fe->colours[2]);
 
409
        cairo_paint(cr);
 
410
        cairo_destroy(cr);
 
411
    }
 
412
}
 
413
 
 
414
static int backing_store_ok(frontend *fe)
 
415
{
 
416
    return (!!fe->image);
 
417
}
 
418
 
 
419
static void teardown_backing_store(frontend *fe)
 
420
{
 
421
    cairo_surface_destroy(fe->image);
 
422
    gdk_pixmap_unref(fe->pixmap);
 
423
    fe->image = NULL;
 
424
}
 
425
 
 
426
#endif
 
427
 
 
428
/* ----------------------------------------------------------------------
 
429
 * GDK drawing functions.
 
430
 */
 
431
 
 
432
#ifndef USE_CAIRO
 
433
 
 
434
static void setup_drawing(frontend *fe)
 
435
{
173
436
    fe->gc = gdk_gc_new(fe->area->window);
174
 
    fe->bbox_l = fe->w;
175
 
    fe->bbox_r = 0;
176
 
    fe->bbox_u = fe->h;
177
 
    fe->bbox_d = 0;
178
 
}
179
 
 
180
 
void gtk_clip(void *handle, int x, int y, int w, int h)
181
 
{
182
 
    frontend *fe = (frontend *)handle;
 
437
}
 
438
 
 
439
static void teardown_drawing(frontend *fe)
 
440
{
 
441
    gdk_gc_unref(fe->gc);
 
442
    fe->gc = NULL;
 
443
}
 
444
 
 
445
static void snaffle_colours(frontend *fe)
 
446
{
 
447
    int i, ncolours;
 
448
    float *colours;
 
449
    gboolean *success;
 
450
 
 
451
    fe->colmap = gdk_colormap_get_system();
 
452
    colours = midend_colours(fe->me, &ncolours);
 
453
    fe->ncolours = ncolours;
 
454
    fe->colours = snewn(ncolours, GdkColor);
 
455
    for (i = 0; i < ncolours; i++) {
 
456
        fe->colours[i].red = colours[i*3] * 0xFFFF;
 
457
        fe->colours[i].green = colours[i*3+1] * 0xFFFF;
 
458
        fe->colours[i].blue = colours[i*3+2] * 0xFFFF;
 
459
    }
 
460
    success = snewn(ncolours, gboolean);
 
461
    gdk_colormap_alloc_colors(fe->colmap, fe->colours, ncolours,
 
462
                              FALSE, FALSE, success);
 
463
    for (i = 0; i < ncolours; i++) {
 
464
        if (!success[i]) {
 
465
            g_error("couldn't allocate colour %d (#%02x%02x%02x)\n",
 
466
                    i, fe->colours[i].red >> 8,
 
467
                    fe->colours[i].green >> 8,
 
468
                    fe->colours[i].blue >> 8);
 
469
        }
 
470
    }
 
471
}
 
472
 
 
473
static void set_window_background(frontend *fe, int colour)
 
474
{
 
475
    fe->backgroundindex = colour;
 
476
    gdk_window_set_background(fe->area->window, &fe->colours[colour]);
 
477
    gdk_window_set_background(fe->window->window, &fe->colours[colour]);
 
478
}
 
479
 
 
480
static void set_colour(frontend *fe, int colour)
 
481
{
 
482
    gdk_gc_set_foreground(fe->gc, &fe->colours[colour]);
 
483
}
 
484
 
 
485
#ifdef USE_PANGO
 
486
static PangoLayout *make_pango_layout(frontend *fe)
 
487
{
 
488
    return (pango_layout_new(gtk_widget_get_pango_context(fe->area)));
 
489
}
 
490
 
 
491
static void draw_pango_layout(frontend *fe, PangoLayout *layout,
 
492
                              int x, int y)
 
493
{
 
494
    gdk_draw_layout(fe->pixmap, fe->gc, x, y, layout);
 
495
}
 
496
#endif
 
497
 
 
498
static void save_screenshot_png(frontend *fe, const char *screenshot_file)
 
499
{
 
500
    GdkPixbuf *pb;
 
501
    GError *gerror = NULL;
 
502
 
 
503
    midend_redraw(fe->me);
 
504
 
 
505
    pb = gdk_pixbuf_get_from_drawable(NULL, fe->pixmap,
 
506
                                      NULL, 0, 0, 0, 0, -1, -1);
 
507
    gdk_pixbuf_save(pb, screenshot_file, "png", &gerror, NULL);
 
508
}
 
509
 
 
510
static void do_clip(frontend *fe, int x, int y, int w, int h)
 
511
{
183
512
    GdkRectangle rect;
184
513
 
185
514
    rect.x = x;
186
515
    rect.y = y;
187
516
    rect.width = w;
188
517
    rect.height = h;
189
 
 
190
518
    gdk_gc_set_clip_rectangle(fe->gc, &rect);
191
519
}
192
520
 
193
 
void gtk_unclip(void *handle)
 
521
static void do_unclip(frontend *fe)
194
522
{
195
 
    frontend *fe = (frontend *)handle;
196
523
    GdkRectangle rect;
197
524
 
198
525
    rect.x = 0;
199
526
    rect.y = 0;
200
527
    rect.width = fe->w;
201
528
    rect.height = fe->h;
202
 
 
203
529
    gdk_gc_set_clip_rectangle(fe->gc, &rect);
204
530
}
205
531
 
206
 
void gtk_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
207
 
                   int align, int colour, char *text)
208
 
{
209
 
    frontend *fe = (frontend *)handle;
210
 
    int i;
211
 
 
212
 
    /*
213
 
     * Find or create the font.
214
 
     */
215
 
    for (i = 0; i < fe->nfonts; i++)
216
 
        if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize)
217
 
            break;
218
 
 
219
 
    if (i == fe->nfonts) {
220
 
        if (fe->fontsize <= fe->nfonts) {
221
 
            fe->fontsize = fe->nfonts + 10;
222
 
            fe->fonts = sresize(fe->fonts, fe->fontsize, struct font);
223
 
        }
224
 
 
225
 
        fe->nfonts++;
226
 
 
227
 
        fe->fonts[i].type = fonttype;
228
 
        fe->fonts[i].size = fontsize;
229
 
 
230
 
#ifdef USE_PANGO
231
 
        /*
232
 
         * Use Pango to find the closest match to the requested
233
 
         * font.
234
 
         */
235
 
        {
236
 
            PangoFontDescription *fd;
237
 
 
238
 
            fd = pango_font_description_new();
239
 
            /* `Monospace' and `Sans' are meta-families guaranteed to exist */
240
 
            pango_font_description_set_family(fd, fonttype == FONT_FIXED ?
241
 
                                              "Monospace" : "Sans");
242
 
            pango_font_description_set_weight(fd, PANGO_WEIGHT_BOLD);
243
 
            /*
244
 
             * I found some online Pango documentation which
245
 
             * described a function called
246
 
             * pango_font_description_set_absolute_size(), which is
247
 
             * _exactly_ what I want here. Unfortunately, none of
248
 
             * my local Pango installations have it (presumably
249
 
             * they're too old), so I'm going to have to hack round
250
 
             * it by figuring out the point size myself. This
251
 
             * limits me to X and probably also breaks in later
252
 
             * Pango installations, so ideally I should add another
253
 
             * CHECK_VERSION type ifdef and use set_absolute_size
254
 
             * where available. All very annoying.
255
 
             */
256
 
#ifdef HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
257
 
            pango_font_description_set_absolute_size(fd, PANGO_SCALE*fontsize);
258
 
#else
259
 
            {
260
 
                Display *d = GDK_DISPLAY();
261
 
                int s = DefaultScreen(d);
262
 
                double resolution =
263
 
                    (PANGO_SCALE * 72.27 / 25.4) * 
264
 
                    ((double) DisplayWidthMM(d, s) / DisplayWidth (d, s));
265
 
                pango_font_description_set_size(fd, resolution * fontsize);
266
 
            }
267
 
#endif
268
 
            fe->fonts[i].desc = fd;
269
 
        }
270
 
 
271
 
#else
272
 
        /*
273
 
         * In GTK 1.2, I don't know of any plausible way to
274
 
         * pick a suitable font, so I'm just going to be
275
 
         * tedious.
276
 
         */
277
 
        fe->fonts[i].font = gdk_font_load(fonttype == FONT_FIXED ?
278
 
                                          "fixed" : "variable");
279
 
#endif
280
 
 
281
 
    }
282
 
 
283
 
    /*
284
 
     * Set the colour.
285
 
     */
286
 
    gdk_gc_set_foreground(fe->gc, &fe->colours[colour]);
287
 
 
288
 
#ifdef USE_PANGO
289
 
 
290
 
    {
291
 
        PangoLayout *layout;
292
 
        PangoRectangle rect;
293
 
 
294
 
        /*
295
 
         * Create a layout.
296
 
         */
297
 
        layout = pango_layout_new(gtk_widget_get_pango_context(fe->area));
298
 
        pango_layout_set_font_description(layout, fe->fonts[i].desc);
299
 
        pango_layout_set_text(layout, text, strlen(text));
300
 
        pango_layout_get_pixel_extents(layout, NULL, &rect);
301
 
 
302
 
        if (align & ALIGN_VCENTRE)
303
 
            rect.y -= rect.height / 2;
304
 
        else
305
 
            rect.y -= rect.height;
306
 
 
307
 
        if (align & ALIGN_HCENTRE)
308
 
            rect.x -= rect.width / 2;
309
 
        else if (align & ALIGN_HRIGHT)
310
 
            rect.x -= rect.width;
311
 
 
312
 
        gdk_draw_layout(fe->pixmap, fe->gc, rect.x + x, rect.y + y, layout);
313
 
 
314
 
        g_object_unref(layout);
315
 
    }
316
 
 
317
 
#else
318
 
    /*
319
 
     * Find string dimensions and process alignment.
320
 
     */
321
 
    {
322
 
        int lb, rb, wid, asc, desc;
323
 
 
324
 
        /*
325
 
         * Measure vertical string extents with respect to the same
326
 
         * string always...
327
 
         */
328
 
        gdk_string_extents(fe->fonts[i].font,
329
 
                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
330
 
                           &lb, &rb, &wid, &asc, &desc);
331
 
        if (align & ALIGN_VCENTRE)
332
 
            y += asc - (asc+desc)/2;
333
 
        else
334
 
            y += asc;
335
 
 
336
 
        /*
337
 
         * ... but horizontal extents with respect to the provided
338
 
         * string. This means that multiple pieces of text centred
339
 
         * on the same y-coordinate don't have different baselines.
340
 
         */
341
 
        gdk_string_extents(fe->fonts[i].font, text,
342
 
                           &lb, &rb, &wid, &asc, &desc);
343
 
 
344
 
        if (align & ALIGN_HCENTRE)
345
 
            x -= wid / 2;
346
 
        else if (align & ALIGN_HRIGHT)
347
 
            x -= wid;
348
 
 
349
 
    }
350
 
 
351
 
    /*
352
 
     * Actually draw the text.
353
 
     */
354
 
    gdk_draw_string(fe->pixmap, fe->fonts[i].font, fe->gc, x, y, text);
355
 
#endif
356
 
 
357
 
}
358
 
 
359
 
void gtk_draw_rect(void *handle, int x, int y, int w, int h, int colour)
360
 
{
361
 
    frontend *fe = (frontend *)handle;
362
 
    gdk_gc_set_foreground(fe->gc, &fe->colours[colour]);
 
532
static void do_draw_rect(frontend *fe, int x, int y, int w, int h)
 
533
{
363
534
    gdk_draw_rectangle(fe->pixmap, fe->gc, 1, x, y, w, h);
364
535
}
365
536
 
366
 
void gtk_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
367
 
{
368
 
    frontend *fe = (frontend *)handle;
369
 
    gdk_gc_set_foreground(fe->gc, &fe->colours[colour]);
370
 
    gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2);
371
 
}
372
 
 
373
 
void gtk_draw_poly(void *handle, int *coords, int npoints,
374
 
                   int fillcolour, int outlinecolour)
375
 
{
376
 
    frontend *fe = (frontend *)handle;
 
537
static void do_draw_line(frontend *fe, int x1, int y1, int x2, int y2)
 
538
{
 
539
    gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2);
 
540
}
 
541
 
 
542
static void do_draw_thick_line(frontend *fe, float thickness,
 
543
                               float x1, float y1, float x2, float y2)
 
544
{
 
545
    GdkGCValues save;
 
546
 
 
547
    gdk_gc_get_values(fe->gc, &save);
 
548
    gdk_gc_set_line_attributes(fe->gc,
 
549
                               thickness,
 
550
                               GDK_LINE_SOLID,
 
551
                               GDK_CAP_BUTT,
 
552
                               GDK_JOIN_BEVEL);
 
553
    gdk_draw_line(fe->pixmap, fe->gc, x1, y1, x2, y2);
 
554
    gdk_gc_set_line_attributes(fe->gc,
 
555
                               save.line_width,
 
556
                               save.line_style,
 
557
                               save.cap_style,
 
558
                               save.join_style);
 
559
}
 
560
 
 
561
static void do_draw_poly(frontend *fe, int *coords, int npoints,
 
562
                         int fillcolour, int outlinecolour)
 
563
{
377
564
    GdkPoint *points = snewn(npoints, GdkPoint);
378
565
    int i;
379
566
 
383
570
    }
384
571
 
385
572
    if (fillcolour >= 0) {
386
 
        gdk_gc_set_foreground(fe->gc, &fe->colours[fillcolour]);
 
573
        set_colour(fe, fillcolour);
387
574
        gdk_draw_polygon(fe->pixmap, fe->gc, TRUE, points, npoints);
388
575
    }
389
576
    assert(outlinecolour >= 0);
390
 
    gdk_gc_set_foreground(fe->gc, &fe->colours[outlinecolour]);
 
577
    set_colour(fe, outlinecolour);
391
578
 
392
579
    /*
393
580
     * In principle we ought to be able to use gdk_draw_polygon for
403
590
    sfree(points);
404
591
}
405
592
 
406
 
void gtk_draw_circle(void *handle, int cx, int cy, int radius,
407
 
                     int fillcolour, int outlinecolour)
 
593
static void do_draw_circle(frontend *fe, int cx, int cy, int radius,
 
594
                           int fillcolour, int outlinecolour)
408
595
{
409
 
    frontend *fe = (frontend *)handle;
410
596
    if (fillcolour >= 0) {
411
 
        gdk_gc_set_foreground(fe->gc, &fe->colours[fillcolour]);
 
597
        set_colour(fe, fillcolour);
412
598
        gdk_draw_arc(fe->pixmap, fe->gc, TRUE,
413
599
                     cx - radius, cy - radius,
414
600
                     2 * radius, 2 * radius, 0, 360 * 64);
415
601
    }
416
602
 
417
603
    assert(outlinecolour >= 0);
418
 
    gdk_gc_set_foreground(fe->gc, &fe->colours[outlinecolour]);
 
604
    set_colour(fe, outlinecolour);
419
605
    gdk_draw_arc(fe->pixmap, fe->gc, FALSE,
420
606
                 cx - radius, cy - radius,
421
607
                 2 * radius, 2 * radius, 0, 360 * 64);
422
608
}
423
609
 
424
 
struct blitter {
425
 
    GdkPixmap *pixmap;
426
 
    int w, h, x, y;
427
 
};
428
 
 
429
 
blitter *gtk_blitter_new(void *handle, int w, int h)
 
610
static void setup_blitter(blitter *bl, int w, int h)
430
611
{
431
612
    /*
432
613
     * We can't create the pixmap right now, because fe->window
433
614
     * might not yet exist. So we just cache w and h and create it
434
615
     * during the firs call to blitter_save.
435
616
     */
 
617
    bl->pixmap = NULL;
 
618
}
 
619
 
 
620
static void teardown_blitter(blitter *bl)
 
621
{
 
622
    if (bl->pixmap)
 
623
        gdk_pixmap_unref(bl->pixmap);
 
624
}
 
625
 
 
626
static void do_blitter_save(frontend *fe, blitter *bl, int x, int y)
 
627
{
 
628
    if (!bl->pixmap)
 
629
        bl->pixmap = gdk_pixmap_new(fe->area->window, bl->w, bl->h, -1);
 
630
    gdk_draw_pixmap(bl->pixmap,
 
631
                    fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
 
632
                    fe->pixmap,
 
633
                    x, y, 0, 0, bl->w, bl->h);
 
634
}
 
635
 
 
636
static void do_blitter_load(frontend *fe, blitter *bl, int x, int y)
 
637
{
 
638
    assert(bl->pixmap);
 
639
    gdk_draw_pixmap(fe->pixmap,
 
640
                    fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
 
641
                    bl->pixmap,
 
642
                    0, 0, x, y, bl->w, bl->h);
 
643
}
 
644
 
 
645
static void clear_backing_store(frontend *fe)
 
646
{
 
647
    fe->pixmap = NULL;
 
648
}
 
649
 
 
650
static void setup_backing_store(frontend *fe)
 
651
{
 
652
    GdkGC *gc;
 
653
 
 
654
    fe->pixmap = gdk_pixmap_new(fe->area->window, fe->pw, fe->ph, -1);
 
655
 
 
656
    gc = gdk_gc_new(fe->area->window);
 
657
    gdk_gc_set_foreground(gc, &fe->colours[0]);
 
658
    gdk_draw_rectangle(fe->pixmap, gc, 1, 0, 0, fe->pw, fe->ph);
 
659
    gdk_draw_rectangle(fe->area->window, gc, 1, 0, 0, fe->w, fe->h);
 
660
    gdk_gc_unref(gc);
 
661
}
 
662
 
 
663
static int backing_store_ok(frontend *fe)
 
664
{
 
665
    return (!!fe->pixmap);
 
666
}
 
667
 
 
668
static void teardown_backing_store(frontend *fe)
 
669
{
 
670
    gdk_pixmap_unref(fe->pixmap);
 
671
    fe->pixmap = NULL;
 
672
}
 
673
 
 
674
#endif
 
675
 
 
676
static void repaint_rectangle(frontend *fe, GtkWidget *widget,
 
677
                              int x, int y, int w, int h)
 
678
{
 
679
    GdkGC *gc = gdk_gc_new(widget->window);
 
680
#ifdef USE_CAIRO
 
681
    gdk_gc_set_foreground(gc, &fe->background);
 
682
#else
 
683
    gdk_gc_set_foreground(gc, &fe->colours[fe->backgroundindex]);
 
684
#endif
 
685
    if (x < fe->ox) {
 
686
        gdk_draw_rectangle(widget->window, gc,
 
687
                           TRUE, x, y, fe->ox - x, h);
 
688
        w -= (fe->ox - x);
 
689
        x = fe->ox;
 
690
    }
 
691
    if (y < fe->oy) {
 
692
        gdk_draw_rectangle(widget->window, gc,
 
693
                           TRUE, x, y, w, fe->oy - y);
 
694
        h -= (fe->oy - y);
 
695
        y = fe->oy;
 
696
    }
 
697
    if (w > fe->pw) {
 
698
        gdk_draw_rectangle(widget->window, gc,
 
699
                           TRUE, x + fe->pw, y, w - fe->pw, h);
 
700
        w = fe->pw;
 
701
    }
 
702
    if (h > fe->ph) {
 
703
        gdk_draw_rectangle(widget->window, gc,
 
704
                           TRUE, x, y + fe->ph, w, h - fe->ph);
 
705
        h = fe->ph;
 
706
    }
 
707
    gdk_draw_pixmap(widget->window, gc, fe->pixmap,
 
708
                    x - fe->ox, y - fe->oy, x, y, w, h);
 
709
    gdk_gc_unref(gc);
 
710
}
 
711
 
 
712
/* ----------------------------------------------------------------------
 
713
 * Pango font functions.
 
714
 */
 
715
 
 
716
#ifdef USE_PANGO
 
717
 
 
718
static void add_font(frontend *fe, int index, int fonttype, int fontsize)
 
719
{
 
720
    /*
 
721
     * Use Pango to find the closest match to the requested
 
722
     * font.
 
723
     */
 
724
    PangoFontDescription *fd;
 
725
 
 
726
    fd = pango_font_description_new();
 
727
    /* `Monospace' and `Sans' are meta-families guaranteed to exist */
 
728
    pango_font_description_set_family(fd, fonttype == FONT_FIXED ?
 
729
                                      "Monospace" : "Sans");
 
730
    pango_font_description_set_weight(fd, PANGO_WEIGHT_BOLD);
 
731
    /*
 
732
     * I found some online Pango documentation which
 
733
     * described a function called
 
734
     * pango_font_description_set_absolute_size(), which is
 
735
     * _exactly_ what I want here. Unfortunately, none of
 
736
     * my local Pango installations have it (presumably
 
737
     * they're too old), so I'm going to have to hack round
 
738
     * it by figuring out the point size myself. This
 
739
     * limits me to X and probably also breaks in later
 
740
     * Pango installations, so ideally I should add another
 
741
     * CHECK_VERSION type ifdef and use set_absolute_size
 
742
     * where available. All very annoying.
 
743
     */
 
744
#ifdef HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
 
745
    pango_font_description_set_absolute_size(fd, PANGO_SCALE*fontsize);
 
746
#else
 
747
    {
 
748
        Display *d = GDK_DISPLAY();
 
749
        int s = DefaultScreen(d);
 
750
        double resolution =
 
751
            (PANGO_SCALE * 72.27 / 25.4) *
 
752
            ((double) DisplayWidthMM(d, s) / DisplayWidth (d, s));
 
753
        pango_font_description_set_size(fd, resolution * fontsize);
 
754
    }
 
755
#endif
 
756
    fe->fonts[index].desc = fd;
 
757
}
 
758
 
 
759
static void align_and_draw_text(frontend *fe,
 
760
                                int index, int align, int x, int y,
 
761
                                const char *text)
 
762
{
 
763
    PangoLayout *layout;
 
764
    PangoRectangle rect;
 
765
 
 
766
    layout = make_pango_layout(fe);
 
767
 
 
768
    /*
 
769
     * Create a layout.
 
770
     */
 
771
    pango_layout_set_font_description(layout, fe->fonts[index].desc);
 
772
    pango_layout_set_text(layout, text, strlen(text));
 
773
    pango_layout_get_pixel_extents(layout, NULL, &rect);
 
774
 
 
775
    if (align & ALIGN_VCENTRE)
 
776
        rect.y -= rect.height / 2;
 
777
    else
 
778
        rect.y -= rect.height;
 
779
 
 
780
    if (align & ALIGN_HCENTRE)
 
781
        rect.x -= rect.width / 2;
 
782
    else if (align & ALIGN_HRIGHT)
 
783
        rect.x -= rect.width;
 
784
 
 
785
    draw_pango_layout(fe, layout, rect.x + x, rect.y + y);
 
786
 
 
787
    g_object_unref(layout);
 
788
}
 
789
 
 
790
#endif
 
791
 
 
792
/* ----------------------------------------------------------------------
 
793
 * Old-fashioned font functions.
 
794
 */
 
795
 
 
796
#ifndef USE_PANGO
 
797
 
 
798
static void add_font(int index, int fonttype, int fontsize)
 
799
{
 
800
    /*
 
801
     * In GTK 1.2, I don't know of any plausible way to
 
802
     * pick a suitable font, so I'm just going to be
 
803
     * tedious.
 
804
     */
 
805
    fe->fonts[i].font = gdk_font_load(fonttype == FONT_FIXED ?
 
806
                                      "fixed" : "variable");
 
807
}
 
808
 
 
809
static void align_and_draw_text(int index, int align, int x, int y,
 
810
                                const char *text)
 
811
{
 
812
    int lb, rb, wid, asc, desc;
 
813
 
 
814
    /*
 
815
     * Measure vertical string extents with respect to the same
 
816
     * string always...
 
817
     */
 
818
    gdk_string_extents(fe->fonts[i].font,
 
819
                       "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
 
820
                       &lb, &rb, &wid, &asc, &desc);
 
821
    if (align & ALIGN_VCENTRE)
 
822
        y += asc - (asc+desc)/2;
 
823
    else
 
824
        y += asc;
 
825
 
 
826
    /*
 
827
     * ... but horizontal extents with respect to the provided
 
828
     * string. This means that multiple pieces of text centred
 
829
     * on the same y-coordinate don't have different baselines.
 
830
     */
 
831
    gdk_string_extents(fe->fonts[i].font, text,
 
832
                       &lb, &rb, &wid, &asc, &desc);
 
833
 
 
834
    if (align & ALIGN_HCENTRE)
 
835
        x -= wid / 2;
 
836
    else if (align & ALIGN_HRIGHT)
 
837
        x -= wid;
 
838
 
 
839
    /*
 
840
     * Actually draw the text.
 
841
     */
 
842
    gdk_draw_string(fe->pixmap, fe->fonts[i].font, fe->gc, x, y, text);
 
843
}
 
844
 
 
845
#endif
 
846
 
 
847
/* ----------------------------------------------------------------------
 
848
 * The exported drawing functions.
 
849
 */
 
850
 
 
851
void gtk_start_draw(void *handle)
 
852
{
 
853
    frontend *fe = (frontend *)handle;
 
854
    fe->bbox_l = fe->w;
 
855
    fe->bbox_r = 0;
 
856
    fe->bbox_u = fe->h;
 
857
    fe->bbox_d = 0;
 
858
    setup_drawing(fe);
 
859
}
 
860
 
 
861
void gtk_clip(void *handle, int x, int y, int w, int h)
 
862
{
 
863
    frontend *fe = (frontend *)handle;
 
864
    do_clip(fe, x, y, w, h);
 
865
}
 
866
 
 
867
void gtk_unclip(void *handle)
 
868
{
 
869
    frontend *fe = (frontend *)handle;
 
870
    do_unclip(fe);
 
871
}
 
872
 
 
873
void gtk_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
 
874
                   int align, int colour, char *text)
 
875
{
 
876
    frontend *fe = (frontend *)handle;
 
877
    int i;
 
878
 
 
879
    /*
 
880
     * Find or create the font.
 
881
     */
 
882
    for (i = 0; i < fe->nfonts; i++)
 
883
        if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize)
 
884
            break;
 
885
 
 
886
    if (i == fe->nfonts) {
 
887
        if (fe->fontsize <= fe->nfonts) {
 
888
            fe->fontsize = fe->nfonts + 10;
 
889
            fe->fonts = sresize(fe->fonts, fe->fontsize, struct font);
 
890
        }
 
891
 
 
892
        fe->nfonts++;
 
893
 
 
894
        fe->fonts[i].type = fonttype;
 
895
        fe->fonts[i].size = fontsize;
 
896
        add_font(fe, i, fonttype, fontsize);
 
897
    }
 
898
 
 
899
    /*
 
900
     * Do the job.
 
901
     */
 
902
    set_colour(fe, colour);
 
903
    align_and_draw_text(fe, i, align, x, y, text);
 
904
}
 
905
 
 
906
void gtk_draw_rect(void *handle, int x, int y, int w, int h, int colour)
 
907
{
 
908
    frontend *fe = (frontend *)handle;
 
909
    set_colour(fe, colour);
 
910
    do_draw_rect(fe, x, y, w, h);
 
911
}
 
912
 
 
913
void gtk_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
 
914
{
 
915
    frontend *fe = (frontend *)handle;
 
916
    set_colour(fe, colour);
 
917
    do_draw_line(fe, x1, y1, x2, y2);
 
918
}
 
919
 
 
920
void gtk_draw_thick_line(void *handle, float thickness,
 
921
                         float x1, float y1, float x2, float y2, int colour)
 
922
{
 
923
    frontend *fe = (frontend *)handle;
 
924
    set_colour(fe, colour);
 
925
    do_draw_thick_line(fe, thickness, x1, y1, x2, y2);
 
926
}
 
927
 
 
928
void gtk_draw_poly(void *handle, int *coords, int npoints,
 
929
                   int fillcolour, int outlinecolour)
 
930
{
 
931
    frontend *fe = (frontend *)handle;
 
932
    do_draw_poly(fe, coords, npoints, fillcolour, outlinecolour);
 
933
}
 
934
 
 
935
void gtk_draw_circle(void *handle, int cx, int cy, int radius,
 
936
                     int fillcolour, int outlinecolour)
 
937
{
 
938
    frontend *fe = (frontend *)handle;
 
939
    do_draw_circle(fe, cx, cy, radius, fillcolour, outlinecolour);
 
940
}
 
941
 
 
942
blitter *gtk_blitter_new(void *handle, int w, int h)
 
943
{
436
944
    blitter *bl = snew(blitter);
437
 
    bl->pixmap = NULL;
 
945
    setup_blitter(bl, w, h);
438
946
    bl->w = w;
439
947
    bl->h = h;
440
948
    return bl;
442
950
 
443
951
void gtk_blitter_free(void *handle, blitter *bl)
444
952
{
445
 
    if (bl->pixmap)
446
 
        gdk_pixmap_unref(bl->pixmap);
 
953
    teardown_blitter(bl);
447
954
    sfree(bl);
448
955
}
449
956
 
450
957
void gtk_blitter_save(void *handle, blitter *bl, int x, int y)
451
958
{
452
959
    frontend *fe = (frontend *)handle;
453
 
    if (!bl->pixmap)
454
 
        bl->pixmap = gdk_pixmap_new(fe->area->window, bl->w, bl->h, -1);
 
960
    do_blitter_save(fe, bl, x, y);
455
961
    bl->x = x;
456
962
    bl->y = y;
457
 
    gdk_draw_pixmap(bl->pixmap,
458
 
                    fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
459
 
                    fe->pixmap,
460
 
                    x, y, 0, 0, bl->w, bl->h);
461
963
}
462
964
 
463
965
void gtk_blitter_load(void *handle, blitter *bl, int x, int y)
464
966
{
465
967
    frontend *fe = (frontend *)handle;
466
 
    assert(bl->pixmap);
467
968
    if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
468
969
        x = bl->x;
469
970
        y = bl->y;
470
971
    }
471
 
    gdk_draw_pixmap(fe->pixmap,
472
 
                    fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
473
 
                    bl->pixmap,
474
 
                    0, 0, x, y, bl->w, bl->h);
 
972
    do_blitter_load(fe, bl, x, y);
475
973
}
476
974
 
477
975
void gtk_draw_update(void *handle, int x, int y, int w, int h)
486
984
void gtk_end_draw(void *handle)
487
985
{
488
986
    frontend *fe = (frontend *)handle;
489
 
    gdk_gc_unref(fe->gc);
490
 
    fe->gc = NULL;
 
987
 
 
988
    teardown_drawing(fe);
491
989
 
492
990
    if (fe->bbox_l < fe->bbox_r && fe->bbox_u < fe->bbox_d) {
493
 
        gdk_draw_pixmap(fe->area->window,
494
 
                        fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
495
 
                        fe->pixmap,
496
 
                        fe->bbox_l, fe->bbox_u,
497
 
                        fe->ox + fe->bbox_l, fe->oy + fe->bbox_u,
498
 
                        fe->bbox_r - fe->bbox_l, fe->bbox_d - fe->bbox_u);
 
991
        repaint_rectangle(fe, fe->area,
 
992
                          fe->bbox_l - 1 + fe->ox,
 
993
                          fe->bbox_u - 1 + fe->oy,
 
994
                          fe->bbox_r - fe->bbox_l + 2,
 
995
                          fe->bbox_d - fe->bbox_u + 2);
499
996
    }
500
997
}
501
998
 
533
1030
#else
534
1031
    NULL,
535
1032
#endif
 
1033
#ifdef NO_THICK_LINE
 
1034
    NULL,
 
1035
#else
 
1036
    gtk_draw_thick_line,
 
1037
#endif
536
1038
};
537
1039
 
538
1040
static void destroy(GtkWidget *widget, gpointer data)
550
1052
    int shift = (event->state & GDK_SHIFT_MASK) ? MOD_SHFT : 0;
551
1053
    int ctrl = (event->state & GDK_CONTROL_MASK) ? MOD_CTRL : 0;
552
1054
 
553
 
    if (!fe->pixmap)
 
1055
    if (!backing_store_ok(fe))
554
1056
        return TRUE;
555
1057
 
556
1058
#if !GTK_CHECK_VERSION(2,0,0)
617
1119
    frontend *fe = (frontend *)data;
618
1120
    int button;
619
1121
 
620
 
    if (!fe->pixmap)
 
1122
    if (!backing_store_ok(fe))
621
1123
        return TRUE;
622
1124
 
623
1125
    if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE)
648
1150
    frontend *fe = (frontend *)data;
649
1151
    int button;
650
1152
 
651
 
    if (!fe->pixmap)
 
1153
    if (!backing_store_ok(fe))
652
1154
        return TRUE;
653
1155
 
654
1156
    if (event->state & (GDK_BUTTON2_MASK | GDK_SHIFT_MASK))
677
1179
{
678
1180
    frontend *fe = (frontend *)data;
679
1181
 
680
 
    if (fe->pixmap) {
681
 
        gdk_draw_pixmap(widget->window,
682
 
                        widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
683
 
                        fe->pixmap,
684
 
                        event->area.x - fe->ox, event->area.y - fe->oy,
685
 
                        event->area.x, event->area.y,
686
 
                        event->area.width, event->area.height);
 
1182
    if (backing_store_ok(fe)) {
 
1183
        repaint_rectangle(fe, widget,
 
1184
                          event->area.x, event->area.y,
 
1185
                          event->area.width, event->area.height);
687
1186
    }
688
1187
    return TRUE;
689
1188
}
707
1206
                           GdkEventConfigure *event, gpointer data)
708
1207
{
709
1208
    frontend *fe = (frontend *)data;
710
 
    GdkGC *gc;
711
1209
    int x, y;
712
1210
 
713
 
    if (fe->pixmap)
714
 
        gdk_pixmap_unref(fe->pixmap);
 
1211
    if (backing_store_ok(fe))
 
1212
        teardown_backing_store(fe);
715
1213
 
716
1214
    x = fe->w = event->width;
717
1215
    y = fe->h = event->height;
721
1219
    fe->ox = (fe->w - fe->pw) / 2;
722
1220
    fe->oy = (fe->h - fe->ph) / 2;
723
1221
 
724
 
    fe->pixmap = gdk_pixmap_new(widget->window, fe->pw, fe->ph, -1);
725
 
 
726
 
    gc = gdk_gc_new(fe->area->window);
727
 
    gdk_gc_set_foreground(gc, &fe->colours[0]);
728
 
    gdk_draw_rectangle(fe->pixmap, gc, 1, 0, 0, fe->pw, fe->ph);
729
 
    gdk_draw_rectangle(widget->window, gc, 1, 0, 0,
730
 
                       event->width, event->height);
731
 
    gdk_gc_unref(gc);
732
 
 
 
1222
    setup_backing_store(fe);
733
1223
    midend_force_redraw(fe->me);
734
1224
 
735
1225
    return TRUE;
826
1316
    gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
827
1317
 
828
1318
    if (type == MB_OK) {
829
 
        titles = "OK\0";
 
1319
        titles = GTK_STOCK_OK "\0";
830
1320
        def = cancel = 0;
831
1321
    } else {
832
1322
        assert(type == MB_YESNO);
833
 
        titles = "Yes\0No\0";
834
 
        def = 0;
835
 
        cancel = 1;
 
1323
        titles = GTK_STOCK_NO "\0" GTK_STOCK_YES "\0";
 
1324
        def = 1;
 
1325
        cancel = 0;
836
1326
    }
837
1327
    i = 0;
838
1328
    
839
1329
    while (*titles) {
840
 
        button = gtk_button_new_with_label(titles);
 
1330
        button = gtk_button_new_from_stock(titles);
841
1331
        gtk_box_pack_end(GTK_BOX(GTK_DIALOG(window)->action_area),
842
1332
                         button, FALSE, FALSE, 0);
843
1333
        gtk_widget_show(button);
866
1356
    gtk_widget_show(window);
867
1357
    i = -1;
868
1358
    gtk_main();
869
 
    return (type == MB_YESNO ? i == 0 : TRUE);
 
1359
    return (type == MB_YESNO ? i == 1 : TRUE);
870
1360
}
871
1361
 
872
1362
void error_box(GtkWidget *parent, char *msg)
956
1446
    gtk_window_set_title(GTK_WINDOW(fe->cfgbox), title);
957
1447
    sfree(title);
958
1448
 
959
 
    w = gtk_button_new_with_label("OK");
 
1449
    w = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
 
1450
    gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->action_area),
 
1451
                     w, FALSE, FALSE, 0);
 
1452
    gtk_widget_show(w);
 
1453
    gtk_signal_connect(GTK_OBJECT(w), "clicked",
 
1454
                       GTK_SIGNAL_FUNC(config_cancel_button_clicked), fe);
 
1455
    cancel = w;
 
1456
 
 
1457
    w = gtk_button_new_from_stock(GTK_STOCK_OK);
960
1458
    gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->action_area),
961
1459
                     w, FALSE, FALSE, 0);
962
1460
    gtk_widget_show(w);
965
1463
    gtk_signal_connect(GTK_OBJECT(w), "clicked",
966
1464
                       GTK_SIGNAL_FUNC(config_ok_button_clicked), fe);
967
1465
 
968
 
    w = gtk_button_new_with_label("Cancel");
969
 
    gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->action_area),
970
 
                     w, FALSE, FALSE, 0);
971
 
    gtk_widget_show(w);
972
 
    gtk_signal_connect(GTK_OBJECT(w), "clicked",
973
 
                       GTK_SIGNAL_FUNC(config_cancel_button_clicked), fe);
974
 
    cancel = w;
975
 
 
976
1466
    table = gtk_table_new(1, 2, FALSE);
977
1467
    y = 0;
978
1468
    gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->vbox),
1224
1714
GdkAtom compound_text_atom, utf8_string_atom;
1225
1715
int paste_initialised = FALSE;
1226
1716
 
1227
 
void init_paste()
1228
 
{
1229
 
    unsigned char empty[] = { 0 };
1230
 
 
1231
 
    if (paste_initialised)
1232
 
        return;
1233
 
 
1234
 
    if (!compound_text_atom)
1235
 
        compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
1236
 
    if (!utf8_string_atom)
1237
 
        utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
1238
 
 
1239
 
    /*
1240
 
     * Ensure that all the cut buffers exist - according to the
1241
 
     * ICCCM, we must do this before we start using cut buffers.
1242
 
     */
1243
 
    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1244
 
                    XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, empty, 0);
1245
 
    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1246
 
                    XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, empty, 0);
1247
 
    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1248
 
                    XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, empty, 0);
1249
 
    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1250
 
                    XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, empty, 0);
1251
 
    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1252
 
                    XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, empty, 0);
1253
 
    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1254
 
                    XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, empty, 0);
1255
 
    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1256
 
                    XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, empty, 0);
1257
 
    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
1258
 
                    XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0);
1259
 
}
1260
 
 
1261
 
/* Store data in a cut-buffer. */
1262
 
void store_cutbuffer(char *ptr, int len)
1263
 
{
1264
 
    /* ICCCM says we must rotate the buffers before storing to buffer 0. */
1265
 
    XRotateBuffers(GDK_DISPLAY(), 1);
1266
 
    XStoreBytes(GDK_DISPLAY(), ptr, len);
1267
 
}
1268
 
 
1269
 
void write_clip(frontend *fe, char *data)
1270
 
{
1271
 
    init_paste();
1272
 
 
1273
 
    if (fe->paste_data)
1274
 
        sfree(fe->paste_data);
 
1717
static void set_selection(frontend *fe, GdkAtom selection)
 
1718
{
 
1719
    if (!paste_initialised) {
 
1720
        compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
 
1721
        utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
 
1722
        paste_initialised = TRUE;
 
1723
    }
1275
1724
 
1276
1725
    /*
1277
1726
     * For this simple application we can safely assume that the
1280
1729
     * COMPOUND_TEXT or UTF8_STRING.
1281
1730
     */
1282
1731
 
 
1732
    if (gtk_selection_owner_set(fe->area, selection, CurrentTime)) {
 
1733
        gtk_selection_clear_targets(fe->area, selection);
 
1734
        gtk_selection_add_target(fe->area, selection,
 
1735
                                 GDK_SELECTION_TYPE_STRING, 1);
 
1736
        gtk_selection_add_target(fe->area, selection, compound_text_atom, 1);
 
1737
        gtk_selection_add_target(fe->area, selection, utf8_string_atom, 1);
 
1738
    }
 
1739
}
 
1740
 
 
1741
void write_clip(frontend *fe, char *data)
 
1742
{
 
1743
    if (fe->paste_data)
 
1744
        sfree(fe->paste_data);
 
1745
 
1283
1746
    fe->paste_data = data;
1284
1747
    fe->paste_data_len = strlen(data);
1285
1748
 
1286
 
    store_cutbuffer(fe->paste_data, fe->paste_data_len);
1287
 
 
1288
 
    if (gtk_selection_owner_set(fe->area, GDK_SELECTION_PRIMARY,
1289
 
                                CurrentTime)) {
1290
 
        gtk_selection_clear_targets(fe->area, GDK_SELECTION_PRIMARY);
1291
 
        gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY,
1292
 
                                 GDK_SELECTION_TYPE_STRING, 1);
1293
 
        gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY,
1294
 
                                 compound_text_atom, 1);
1295
 
        gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY,
1296
 
                                 utf8_string_atom, 1);
1297
 
    }
 
1749
    set_selection(fe, GDK_SELECTION_PRIMARY);
 
1750
    set_selection(fe, GDK_SELECTION_CLIPBOARD);
1298
1751
}
1299
1752
 
1300
1753
void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
1537
1990
    resize_fe(fe);
1538
1991
}
1539
1992
 
 
1993
#ifndef HELP_BROWSER_PATH
 
1994
#define HELP_BROWSER_PATH "yelp:khelpcenter:$BROWSER"
 
1995
#endif
 
1996
 
 
1997
static void show_help(frontend *fe, const char *topic)
 
1998
{
 
1999
    const char *list = HELP_BROWSER_PATH;
 
2000
    char path[PATH_MAX + 1];
 
2001
    struct {
 
2002
        const char *s;
 
2003
        int len;
 
2004
    } lang[3];
 
2005
    int i;
 
2006
 
 
2007
    /*
 
2008
     * Search for help file, trying:
 
2009
     * 1. Version for this locale, ignoring encoding (HTML browsers
 
2010
     *    must handle multiple encodings)
 
2011
     * 2. Version for this locale, ignoring encoding and country
 
2012
     * 3. English version
 
2013
     */
 
2014
    lang[0].s = setlocale(LC_MESSAGES, NULL);
 
2015
    lang[0].len = strcspn(lang[0].s, ".@");
 
2016
    lang[1].s = lang[0].s;
 
2017
    lang[1].len = strcspn(lang[1].s, "_");
 
2018
    if (lang[1].len > lang[0].len)
 
2019
        lang[1].len = lang[0].len;
 
2020
    lang[2].s = "en";
 
2021
    lang[2].len = 2;
 
2022
    for (i = 0; i < lenof(lang); i++) {
 
2023
        sprintf(path, "%s/sgt-puzzles/help/%.*s/%s.html",
 
2024
                SHAREDIR, lang[i].len, lang[i].s, topic);
 
2025
        if (access(path, R_OK) == 0)
 
2026
            break;
 
2027
    }
 
2028
    if (i == lenof(lang)) {
 
2029
        error_box(fe->window, "Help file is not installed");
 
2030
        return;
 
2031
    }
 
2032
 
 
2033
    for (;;) {
 
2034
        size_t len;
 
2035
        char buf[PATH_MAX + 1];
 
2036
        const char *command;
 
2037
        const char *argv[3];
 
2038
 
 
2039
        len = strcspn(list, ":");
 
2040
        if (len <= PATH_MAX) {
 
2041
            memcpy(buf, list, len);
 
2042
            buf[len] = 0;
 
2043
            if (buf[0] == '$')
 
2044
                command = getenv(buf + 1);
 
2045
            else
 
2046
                command = buf;
 
2047
            if (command) {
 
2048
                argv[0] = command;
 
2049
                argv[1] = path;
 
2050
                argv[2] = NULL;
 
2051
                if (g_spawn_async(NULL, (char **)argv, NULL,
 
2052
                                  G_SPAWN_SEARCH_PATH,
 
2053
                                  NULL, NULL, NULL, NULL))
 
2054
                    return;
 
2055
            }
 
2056
        }
 
2057
 
 
2058
        if (!list[len])
 
2059
            break;
 
2060
        list += len + 1;
 
2061
    }
 
2062
 
 
2063
    error_box(fe->window, "Failed to start a help browser");
 
2064
}
 
2065
 
 
2066
static void menu_help_contents_event(GtkMenuItem *menuitem, gpointer data)
 
2067
{
 
2068
    show_help((frontend *)data, "index");
 
2069
}
 
2070
 
 
2071
static void menu_help_specific_event(GtkMenuItem *menuitem, gpointer data)
 
2072
{
 
2073
    show_help((frontend *)data, thegame.htmlhelp_topic);
 
2074
}
 
2075
 
1540
2076
static void menu_about_event(GtkMenuItem *menuitem, gpointer data)
1541
2077
{
1542
2078
    frontend *fe = (frontend *)data;
1601
2137
    GList *iconlist;
1602
2138
    int x, y, n;
1603
2139
    char errbuf[1024];
1604
 
    extern char *const *const xpm_icons[];
1605
 
    extern const int n_xpm_icons;
1606
2140
 
1607
2141
    fe = snew(frontend);
1608
2142
 
1814
2348
    menu = gtk_menu_new();
1815
2349
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
1816
2350
 
 
2351
    menuitem = gtk_menu_item_new_with_label("Contents");
 
2352
    gtk_container_add(GTK_CONTAINER(menu), menuitem);
 
2353
    gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
 
2354
                       GTK_SIGNAL_FUNC(menu_help_contents_event), fe);
 
2355
    gtk_widget_show(menuitem);
 
2356
 
 
2357
    if (thegame.htmlhelp_topic) {
 
2358
        char *item;
 
2359
        assert(thegame.name);
 
2360
        item = snewn(9+strlen(thegame.name), char); /*ick*/
 
2361
        sprintf(item, "Help on %s", thegame.name);
 
2362
        menuitem = gtk_menu_item_new_with_label(item);
 
2363
        sfree(item);
 
2364
        gtk_container_add(GTK_CONTAINER(menu), menuitem);
 
2365
        gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
 
2366
                           GTK_SIGNAL_FUNC(menu_help_specific_event), fe);
 
2367
        gtk_widget_show(menuitem);
 
2368
    }
 
2369
 
1817
2370
    menuitem = gtk_menu_item_new_with_label("About");
1818
2371
    gtk_container_add(GTK_CONTAINER(menu), menuitem);
1819
2372
    gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
1858
2411
 
1859
2412
    changed_preset(fe);
1860
2413
 
1861
 
    {
1862
 
        int i, ncolours;
1863
 
        float *colours;
1864
 
        gboolean *success;
1865
 
 
1866
 
        fe->colmap = gdk_colormap_get_system();
1867
 
        colours = midend_colours(fe->me, &ncolours);
1868
 
        fe->ncolours = ncolours;
1869
 
        fe->colours = snewn(ncolours, GdkColor);
1870
 
        for (i = 0; i < ncolours; i++) {
1871
 
            fe->colours[i].red = colours[i*3] * 0xFFFF;
1872
 
            fe->colours[i].green = colours[i*3+1] * 0xFFFF;
1873
 
            fe->colours[i].blue = colours[i*3+2] * 0xFFFF;
1874
 
        }
1875
 
        success = snewn(ncolours, gboolean);
1876
 
        gdk_colormap_alloc_colors(fe->colmap, fe->colours, ncolours,
1877
 
                                  FALSE, FALSE, success);
1878
 
        for (i = 0; i < ncolours; i++) {
1879
 
            if (!success[i]) {
1880
 
                g_error("couldn't allocate colour %d (#%02x%02x%02x)\n",
1881
 
                        i, fe->colours[i].red >> 8,
1882
 
                        fe->colours[i].green >> 8,
1883
 
                        fe->colours[i].blue >> 8);
1884
 
            }
1885
 
        }
1886
 
    }
 
2414
    snaffle_colours(fe);
1887
2415
 
1888
2416
    if (midend_wants_statusbar(fe->me)) {
1889
2417
        GtkWidget *viewport;
1909
2437
        fe->statusbar = NULL;
1910
2438
 
1911
2439
    fe->area = gtk_drawing_area_new();
 
2440
#if GTK_CHECK_VERSION(2,0,0)
 
2441
    GTK_WIDGET_UNSET_FLAGS(fe->area, GTK_DOUBLE_BUFFERED);
 
2442
#endif
1912
2443
    get_size(fe, &x, &y);
1913
2444
    gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
1914
2445
    fe->w = x;
1916
2447
 
1917
2448
    gtk_box_pack_end(vbox, fe->area, TRUE, TRUE, 0);
1918
2449
 
1919
 
    fe->pixmap = NULL;
 
2450
    clear_backing_store(fe);
1920
2451
    fe->fonts = NULL;
1921
2452
    fe->nfonts = fe->fontsize = 0;
1922
2453
 
1974
2505
     * the window.
1975
2506
     */
1976
2507
    gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), 1, 1);
1977
 
 
1978
 
    gdk_window_set_background(fe->area->window, &fe->colours[0]);
1979
 
    gdk_window_set_background(fe->window->window, &fe->colours[0]);
 
2508
    set_window_background(fe, 0);
1980
2509
 
1981
2510
    return fe;
1982
2511
}
2002
2531
 
2003
2532
int main(int argc, char **argv)
2004
2533
{
2005
 
    char *pname = argv[0];
 
2534
    char *pname;
 
2535
    int i;
2006
2536
    char *error;
2007
2537
    int ngenerate = 0, print = FALSE, px = 1, py = 1;
2008
2538
    int soln = FALSE, colour = FALSE;
2017
2547
    char **av = argv;
2018
2548
    char errbuf[500];
2019
2549
 
 
2550
#ifdef COMBINED
 
2551
    pname = strrchr(argv[0], '/');
 
2552
    if (pname)
 
2553
        pname++;
 
2554
    else
 
2555
        pname = argv[0];
 
2556
    for (i = 0; i < gamecount; i++) {
 
2557
        size_t len = strlen(gamelist[i].name);
 
2558
        if (!strncmp(pname, gamelist[i].name, len) &&
 
2559
            (pname[len] == 0 || !strcmp(pname + len, "game"))) {
 
2560
            thegameindex = &gamelist[i];
 
2561
            break;
 
2562
        }
 
2563
    }
 
2564
    if (!thegameindex) {
 
2565
        fprintf(stderr, "puzzles: unknown puzzle '%s'\n", pname);
 
2566
        exit(2);
 
2567
    }
 
2568
#endif
 
2569
 
2020
2570
    /*
2021
2571
     * Command line parsing in this function is rather fiddly,
2022
2572
     * because GTK wants to have a go at argc/argv _first_ - and
2350
2900
        }
2351
2901
 
2352
2902
        if (screenshot_file) {
2353
 
            GdkPixbuf *pb;
2354
 
            GError *gerror = NULL;
2355
 
 
2356
 
            midend_redraw(fe->me);
2357
 
 
2358
 
            pb = gdk_pixbuf_get_from_drawable(NULL, fe->pixmap,
2359
 
                                              NULL, 0, 0, 0, 0, -1, -1);
2360
 
            gdk_pixbuf_save(pb, screenshot_file, "png", &gerror, NULL);
2361
 
 
 
2903
            save_screenshot_png(fe, screenshot_file);
2362
2904
            exit(0);
2363
2905
        }
2364
2906