]> git.kernelconcepts.de Git - gbdfed.git/blob - glyphedit.c
Fixup several compile faults due to changes in recent distributions,
[gbdfed.git] / glyphedit.c
1 /*
2  * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE
18  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
19  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22
23 #include "glyphedit.h"
24 #include <gdk/gdk.h>
25 #include <gdk/gdkkeysyms.h>
26 #include <gtk/gtkselection.h>
27
28 #ifdef HAVE_XLIB
29 #include <gdk/gdkx.h>
30 #endif
31
32 #ifdef ENABLE_NLS
33 #include <libintl.h>
34 #define _(s) dgettext(GETTEXT_PACKAGE,s)
35 #else
36 #define _(s) (s)
37 #endif
38
39 /*
40  * Each pixel will be displayed by a square with this number of pixels
41  * on each side.
42  */
43 #define MIN_PIXEL_SIZE     2
44 #define MAX_PIXEL_SIZE     20
45 #define DEFAULT_PIXEL_SIZE 10
46
47 #define HMARGINS(gw) ((gw)->hmargin << 1)
48 #define VMARGINS(gw) ((gw)->vmargin << 1)
49
50 /*
51  * Crosshair cursor.
52  */
53 static const gchar *cross_xpm[] = {
54 "13 13 2 1",
55 "       c None",
56 ".      c #000000",
57 "      .      ",
58 "      .      ",
59 "      .      ",
60 "      .      ",
61 "      .      ",
62 "             ",
63 ".....   .....",
64 "             ",
65 "      .      ",
66 "      .      ",
67 "      .      ",
68 "      .      ",
69 "      .      "
70 };
71
72 /*
73  * Macros that represent the properties used by this type of object.
74  */
75 #define GLYPHEDIT_CLIPBOARD gdk_atom_intern("GLYPHEDIT_CLIPBOARD", FALSE)
76 #define GLYPHEDIT_BDF_CHAR gdk_atom_intern("GLYPHEDIT_BDF_CHAR", FALSE)
77 #define GLYPHEDIT_BITMAP gdk_atom_intern("GLYPHEDIT_BITMAP", FALSE)
78 #define GLYPHEDIT_GLYPH gdk_atom_intern("GLYPHEDIT_GLYPH", FALSE)
79
80 /*
81  * Set default values.
82  */
83
84 /*
85  * Enums used for identifying properties.
86  */
87 enum {
88     PROP_0 = 0,
89     GLYPH_GRID,
90     BASELINE_COLOR,
91     SELECTION_COLOR,
92     CURSOR_COLOR,
93     PIXEL_SIZE,
94     SHOW_X_HEIGHT,
95     SHOW_CAP_HEIGHT,
96     COLOR_LIST,
97     OPERATION
98 };
99
100 /*
101  * The list of signals emitted by these objects.
102  */
103 enum {
104     GLYPH_MODIFIED = 0,
105     POINTER_MOVED,
106     OPERATION_CHANGE,
107     COLOR_CHANGE
108 };
109
110 /**************************************************************************
111  *
112  * Local variables.
113  *
114  **************************************************************************/
115
116 static GtkWidgetClass *parent_class = 0;
117 static guint glyphedit_signals[OPERATION_CHANGE + 2];
118
119 /**************************************************************************
120  *
121  * Class member functions.
122  *
123  **************************************************************************/
124
125 static void
126 glyphedit_set_property(GObject *obj, guint prop_id, const GValue *value,
127                        GParamSpec *pspec)
128 {
129     GtkWidget *widget;
130     Glyphedit *gw;
131
132     widget = GTK_WIDGET(obj);
133     gw = GLYPHEDIT(obj);
134
135     switch (prop_id) {
136       case GLYPH_GRID:
137         glyphedit_set_grid(gw,
138                            (bdf_glyph_grid_t *) g_value_get_pointer(value));
139         break;
140       case BASELINE_COLOR:
141         break;
142       case SELECTION_COLOR:
143         break;
144       case CURSOR_COLOR:
145         break;
146       case PIXEL_SIZE:
147         glyphedit_set_pixel_size(gw, g_value_get_uint(value));
148         break;
149       case SHOW_X_HEIGHT:
150         glyphedit_set_show_x_height(gw, g_value_get_boolean(value));
151         break;
152       case SHOW_CAP_HEIGHT:
153         glyphedit_set_show_cap_height(gw, g_value_get_boolean(value));
154         break;
155       case COLOR_LIST:
156         gw->colors = (guint16 *) g_value_get_pointer(value);
157         gtk_widget_queue_draw(widget);
158         break;
159       case OPERATION:
160         glyphedit_set_operation(gw,
161                                 (GlypheditOperation) g_value_get_uint(value));
162         break;
163     }
164 }
165
166 static void
167 glyphedit_get_property(GObject *obj, guint prop_id, GValue *value,
168                        GParamSpec *pspec)
169 {
170     GtkWidget *widget;
171     Glyphedit *gw;
172
173     widget = GTK_WIDGET(obj);
174     gw = GLYPHEDIT(obj);
175
176     switch (prop_id) {
177       case GLYPH_GRID:
178         g_value_set_pointer(value, gw->grid);
179         break;
180       case BASELINE_COLOR:
181         break;
182       case SELECTION_COLOR:
183         break;
184       case CURSOR_COLOR:
185         break;
186       case PIXEL_SIZE:
187         g_value_set_uint(value, gw->pixel_size);
188         break;
189       case SHOW_X_HEIGHT:
190         g_value_set_boolean(value, gw->show_x_height);
191         break;
192       case SHOW_CAP_HEIGHT:
193         g_value_set_boolean(value, gw->show_cap_height);
194         break;
195       case COLOR_LIST:
196         g_value_set_pointer(value, gw->colors);
197         break;
198       case OPERATION:
199         g_value_set_uint(value, (guint) gw->op);
200         break;
201     }
202 }
203
204 static void
205 glyphedit_destroy(GtkObject *obj)
206 {
207     Glyphedit *gw;
208     GlypheditClass *gwc;
209
210     /*
211      * Do some checks to make sure the incoming object exists and is the right
212      * kind.
213      */
214     g_return_if_fail(obj != 0);
215     g_return_if_fail(IS_GLYPHEDIT(obj));
216
217     gw = GLYPHEDIT(obj);
218     gwc = GLYPHEDIT_GET_CLASS(obj);
219
220     /*
221      * Unreference objects used class-wide so they get deallocated properly
222      * when no longer used. The unreference only needs to happen the first
223      * time since the objects are created at class initialization time.
224      */
225     if (gwc->cursor != 0)
226       gdk_cursor_unref(gwc->cursor);
227
228     if (gwc->gridgc != 0)
229       g_object_unref(G_OBJECT(gwc->gridgc));
230     if (gwc->bbxgc != 0)
231       g_object_unref(G_OBJECT(gwc->bbxgc));
232     if (gwc->pixgc != 0)
233       g_object_unref(G_OBJECT(gwc->pixgc));
234     if (gwc->selgc != 0)
235       g_object_unref(G_OBJECT(gwc->selgc));
236
237     /*
238      * Free up any colors allocated.
239      */
240     if (gw->baselineColor.pixel != 0)
241       gdk_colormap_free_colors(gw->widget.style->colormap,
242                                &gw->baselineColor, 1);
243
244     gwc->cursor = 0;
245     gwc->gridgc = gwc->bbxgc = gwc->pixgc = gwc->selgc = 0;
246
247     /*
248      * Free up the grid info.
249      */
250     if (gw->grid != 0) {
251         bdf_free_glyph_grid(gw->grid);
252         gw->grid = 0;
253     }
254
255     if (gw->spot_size > 0) {
256         g_free(gw->spot);
257         gw->spot_size = gw->spot_used = 0;
258     }
259
260     GTK_OBJECT_CLASS(parent_class)->destroy(obj);
261 }
262
263 static void
264 glyphedit_finalize(GObject *obj)
265 {
266     /*
267      * Do some checks to make sure the incoming object exists and is the right
268      * kind.
269      */
270     g_return_if_fail(obj != 0);
271     g_return_if_fail(IS_GLYPHEDIT(obj));
272
273     /*
274      * Follow the class chain back up to free up resources allocated in the
275      * parent classes.
276      */
277     G_OBJECT_CLASS(parent_class)->finalize(obj);
278 }
279
280 static void
281 glyphedit_preferred_size(GtkWidget *widget, GtkRequisition *preferred)
282 {
283     Glyphedit *gw;
284     GdkScreen *screen;
285     guint16 dht, margin;
286
287     gw = GLYPHEDIT(widget);
288
289     screen = gdk_display_get_default_screen(gdk_display_get_default());
290     dht = gdk_screen_get_height(screen);
291
292     /*
293      * This little bit of code quietly forces the glyph grid to be
294      * at most 1/2 the height of the screen being used to help avoid taking
295      * up too much space on the desktop.
296      */
297     margin = VMARGINS(gw);
298     preferred->height = margin +
299         ((gw->pixel_size + 4) * gw->grid->grid_height);
300     if (preferred->height > (dht >> 1)) {
301         while (gw->pixel_size > 2) {
302             preferred->height = margin +
303                 ((gw->pixel_size + 4) * gw->grid->grid_height);
304             if (preferred->height < (dht >> 1))
305               break;
306             gw->pixel_size--;
307         }
308     }
309
310     preferred->width = HMARGINS(gw) +
311         ((gw->pixel_size + 4) * gw->grid->grid_width);
312 }
313
314 static void
315 glyphedit_actual_size(GtkWidget *widget, GtkAllocation *actual)
316 {
317     widget->allocation = *actual;
318
319     if (GTK_WIDGET_REALIZED(widget))
320       gdk_window_move_resize(widget->window, actual->x, actual->y,
321                              actual->width, actual->height);
322 }
323
324 static void
325 glyphedit_draw_focus(GtkWidget *widget, GdkRectangle *area)
326 {
327     GdkGC *gc;
328     gint x, y, wd, ht, fwidth, fpad;
329
330     /*
331      * Do something with this later to make sure the focus line width
332      * is set in the GC's.
333      */
334     gtk_widget_style_get(widget,
335                          "focus-line-width", &fwidth,
336                          "focus-padding", &fpad, NULL);
337
338     gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)];
339
340     x = (widget->style->xthickness + fwidth + fpad) - 1;
341     y = (widget->style->ythickness + fwidth + fpad) - 1;
342     wd = (widget->allocation.width - (x * 2));
343     ht = (widget->allocation.height - (y * 2));
344
345     if (GTK_WIDGET_HAS_FOCUS(widget))
346       gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(widget),
347                       area, widget, "glyphedit", x, y, wd, ht);
348     else {
349         gdk_gc_set_clip_rectangle(gc, area);
350         gdk_draw_rectangle(widget->window, gc, FALSE, x, y, wd - 1, ht - 1);
351         gdk_gc_set_clip_rectangle(gc, 0);
352     }
353 }
354
355 static void
356 glyphedit_draw_pixel(Glyphedit *gw, gint16 x, gint16 y, gboolean sel)
357 {
358     GtkWidget *w = GTK_WIDGET(gw);
359     GlypheditClass *gwc;
360     gint16 bpr, set, dx, dy, di, si;
361     guchar *masks, *bmap;
362     GdkRectangle pix;
363
364     if (!GTK_WIDGET_REALIZED(w) || gw->grid == 0)
365       return;
366
367     gwc = GLYPHEDIT_GET_CLASS(gw);
368
369     di = 0;
370     masks = 0;
371     switch (gw->grid->bpp) {
372       case 1: masks = bdf_onebpp; di = 7; break;
373       case 2: masks = bdf_twobpp; di = 3; break;
374       case 4: masks = bdf_fourbpp; di = 1; break;
375       case 8: masks = bdf_eightbpp; di = 0; break;
376     }
377
378     dx = (gw->pixel_size + 4) * gw->grid->grid_width;
379     dy = (gw->pixel_size + 4) * gw->grid->grid_height;
380
381     pix.x = (gw->widget.allocation.width >> 1) - (dx >> 1) +
382         ((gw->pixel_size + 4) * x) + 2;
383     pix.y = (gw->widget.allocation.height >> 1) - (dy >> 1) +
384         ((gw->pixel_size + 4) * y) + 2;
385     pix.width = pix.height = gw->pixel_size + 1;
386
387     if (sel == TRUE && gw->grid->sel.width != 0) {
388         bpr = ((gw->grid->sel.width * gw->grid->bpp) + 7) >> 3;
389         dy = y - gw->grid->sel.y;
390         dx = (x - gw->grid->sel.x) * gw->grid->bpp;
391         bmap = gw->grid->sel.bitmap;
392     } else {
393         bpr = ((gw->grid->grid_width * gw->grid->bpp) + 7) >> 3;
394         dy = y;
395         dx = x * gw->grid->bpp;
396         bmap = gw->grid->bitmap;
397     }
398     si = (dx & 7) / gw->grid->bpp;
399     set = bmap[(dy * bpr) + (dx >> 3)] & masks[si];
400     if (di > si)
401       set >>= (di - si) * gw->grid->bpp;
402
403     if (set) {
404         if (gw->grid->bpp > 1) {
405             switch (gw->grid->bpp) {
406               case 2:
407                 memset(gw->spot, gw->colors[set-1], gw->spot_used);
408                 break;
409               case 4:
410                 memset(gw->spot, gw->colors[set-1+4], gw->spot_used);
411                 break;
412               case 8:
413                 memset(gw->spot, set, gw->spot_used);
414                 break;
415             }
416             gdk_draw_gray_image(GTK_WIDGET(gw)->window, gwc->pixgc,
417                                 pix.x, pix.y, pix.width, pix.height,
418                                 GDK_RGB_DITHER_NONE, gw->spot, pix.width);
419         } else
420           gdk_draw_rectangle(GTK_WIDGET(gw)->window, gwc->pixgc, TRUE,
421                              pix.x, pix.y, pix.width, pix.height);
422     } else
423       gdk_window_clear_area(GTK_WIDGET(gw)->window, pix.x, pix.y,
424                             pix.width, pix.height);
425     if (sel == TRUE)
426       gdk_draw_rectangle(GTK_WIDGET(gw)->window, gwc->selgc, TRUE,
427                          pix.x + 1, pix.y + 1,
428                          pix.width - 2, pix.height - 2);
429 }
430
431 static void
432 glyphedit_draw_glyph(Glyphedit *gw)
433 {
434     GtkWidget *w = GTK_WIDGET(gw);
435     gint16 x, y;
436     gboolean sel;
437
438     if (!GTK_WIDGET_REALIZED(w) || gw->grid == 0)
439       return;
440
441     for (y = 0; y < gw->grid->grid_height; y++) {
442         for (x = 0; x < gw->grid->grid_width; x++) {
443             sel = (bdf_in_selection(gw->grid, x, y, 0) ? TRUE : FALSE);
444             glyphedit_draw_pixel(gw, x, y, sel);
445         }
446     }
447 }
448
449 static void
450 glyphedit_draw_font_bbx(Glyphedit *gw)
451 {
452     GtkWidget *w = GTK_WIDGET(gw);
453     GlypheditClass *gwc;
454     gint16 xoff, yoff, fxoff, fyoff, psize;
455     GdkRectangle frame;
456
457     if (!GTK_WIDGET_REALIZED(w))
458       return;
459
460     gwc = GLYPHEDIT_GET_CLASS(gw);
461
462     psize = gw->pixel_size + 4;
463     frame.width = psize * gw->grid->font_bbx.width;
464     frame.height = psize *
465         (gw->grid->font_bbx.ascent + gw->grid->font_bbx.descent);
466
467     fxoff = psize * gw->grid->grid_width;
468     fyoff = psize * gw->grid->grid_height;
469     frame.x = (gw->widget.allocation.width >> 1) - (fxoff >> 1);
470     frame.y = (gw->widget.allocation.height >> 1) - (fyoff >> 1);
471
472     if (gw->grid->font_bbx.x_offset < 0)
473       fxoff = psize * (gw->grid->base_x + gw->grid->font_bbx.x_offset);
474     else
475       fxoff = psize * gw->grid->base_x;
476
477     fyoff = psize * (gw->grid->base_y - gw->grid->font_bbx.ascent);
478
479     /*
480      * Due to some odd behavior, the box has to be drawn with the y point off
481      * by one because the top of the rectangle does not get drawn otherwise.
482      * Even calling gdk_draw_line() specifically doesn't work.
483      *
484      * This may have been fixed in later versions of GDK.
485      */
486     gdk_draw_rectangle(GTK_WIDGET(gw)->window, gwc->bbxgc, FALSE,
487                        frame.x + fxoff, frame.y + fyoff + 1,
488                        frame.width, frame.height);
489
490     /*
491      * Draw vertical baseline.
492      */
493     xoff = (gw->pixel_size + 4) * gw->grid->base_x;
494     yoff = (gw->pixel_size + 4) * gw->grid->base_y;
495
496     gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc,
497                   frame.x + xoff, frame.y + fyoff,
498                   frame.x + xoff, frame.y + fyoff + frame.height);
499
500     /*
501      * Draw horizontal baseline.
502      */
503     gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc,
504                   frame.x + fxoff, frame.y + yoff,
505                   frame.x + fxoff + frame.width, frame.y + yoff);
506
507     /*
508      * Draw the CAP_HEIGHT if indicated and exists.
509      */
510     if (gw->grid && gw->grid->cap_height != 0) {
511         yoff = (gw->pixel_size + 4) *
512             (gw->grid->base_y - gw->grid->cap_height);
513         if (gw->show_cap_height == TRUE)
514           gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc,
515                         frame.x + fxoff, frame.y + yoff,
516                         frame.x + fxoff + frame.width, frame.y + yoff);
517         else {
518             gdk_window_clear_area(GTK_WIDGET(gw)->window, frame.x + fxoff,
519                                   frame.y + yoff, frame.width, 1);
520             gdk_draw_line(GTK_WIDGET(gw)->window, gwc->gridgc,
521                           frame.x + fxoff, frame.y + yoff,
522                           frame.x + fxoff + frame.width, frame.y + yoff);
523         }
524     }
525
526     /*
527      * Draw the X_HEIGHT if indicated and exists.
528      */
529     if (gw->grid && gw->grid->x_height != 0) {
530         yoff = (gw->pixel_size + 4) * (gw->grid->base_y - gw->grid->x_height);
531         if (gw->show_x_height == TRUE)
532           gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc,
533                         frame.x + fxoff, frame.y + yoff,
534                         frame.x + fxoff + frame.width, frame.y + yoff);
535         else {
536             gdk_window_clear_area(GTK_WIDGET(gw)->window, frame.x + fxoff,
537                                   frame.y + yoff, frame.width, 1);
538             gdk_draw_line(GTK_WIDGET(gw)->window, gwc->gridgc,
539                           frame.x + fxoff, frame.y + yoff,
540                           frame.x + fxoff + frame.width, frame.y + yoff);
541         }
542     }
543 }
544
545 static void
546 glyphedit_draw(GtkWidget *widget, GdkRegion *region)
547 {
548     Glyphedit *gw;
549     gint x, y, limit, unit, wd, ht;
550     GlypheditClass *gwc;
551     GdkRectangle frame;
552
553     g_return_if_fail(widget != NULL);
554     g_return_if_fail(IS_GLYPHEDIT(widget));
555
556     gw = GLYPHEDIT(widget);
557     gwc = GLYPHEDIT_GET_CLASS(widget);
558
559     wd = gw->grid->grid_width;
560     ht = gw->grid->grid_height;
561
562     frame.width = (gw->pixel_size + 4) * wd;
563     frame.height = (gw->pixel_size + 4) * ht;
564
565     /*
566      * Adjust the frame horizontal and vertical positions so it
567      * always appears centered on the window.
568      */
569     frame.x = (widget->allocation.width >> 1) - (frame.width >> 1);
570     frame.y = (widget->allocation.height >> 1) - (frame.height >> 1);
571
572     /*
573      * Limit the drawing area to the clip region.
574      */
575     if (region != 0)
576       gdk_gc_set_clip_region(gwc->gridgc, region);
577
578     /*
579      * Draw the outside frame.
580      */
581     gdk_draw_rectangle(widget->window, gwc->gridgc, FALSE,
582                        frame.x, frame.y, frame.width, frame.height);
583
584     /*
585      * Draw the vertical grid lines.
586      */
587     limit = frame.x + frame.width;
588     unit = gw->pixel_size + 4;
589     for (x = frame.x + unit, y = frame.y; x < limit; x += unit)
590       gdk_draw_line(widget->window, gwc->gridgc, x, y, x, y + frame.height);
591
592     /*
593      * Draw the horizontal grid lines.
594      */
595     limit = frame.y + frame.height;
596     for (x = frame.x, y = frame.y + unit; y < limit; y += unit)
597       gdk_draw_line(widget->window, gwc->gridgc, x, y, x + frame.width, y);
598
599     if (region != 0)
600       gdk_gc_set_clip_region(gwc->gridgc, 0);
601
602     glyphedit_draw_font_bbx(gw);
603     glyphedit_draw_glyph(gw);
604 }
605
606 static void
607 glyphedit_create_gcs(GtkWidget *widget, gboolean force)
608 {
609     Glyphedit *gw;
610     GlypheditClass *gwc;
611     GdkGCValuesMask gcm;
612     GdkGCValues gcv;
613     gint8 dashes[2] = {1, 1};
614
615     gw = GLYPHEDIT(widget);
616     gwc = GLYPHEDIT_GET_CLASS(G_OBJECT(widget));
617
618     gcm = GDK_GC_FOREGROUND|GDK_GC_BACKGROUND|GDK_GC_FUNCTION;
619
620     if (gwc->gridgc == 0 || force == TRUE) {
621         if (gwc->gridgc != 0)
622           g_object_unref(G_OBJECT(gwc->gridgc));
623         gcv.foreground.pixel =
624             widget->style->fg[GTK_WIDGET_STATE(widget)].pixel;
625         gcv.background.pixel =
626             widget->style->bg[GTK_WIDGET_STATE(widget)].pixel;
627         gcv.function = GDK_COPY;
628         gcv.line_style = GDK_LINE_ON_OFF_DASH;
629         gwc->gridgc = gdk_gc_new_with_values(widget->window, &gcv,
630                                              gcm|GDK_GC_LINE_STYLE);
631
632         /*
633          * Now set the dash lengths since they can't be set in the GC values.
634          */
635         gdk_gc_set_dashes(gwc->gridgc, 0, dashes, 2);
636     }
637
638     if (gwc->bbxgc == 0 || force == TRUE) {
639         if (gwc->bbxgc != 0)
640           g_object_unref(G_OBJECT(gwc->bbxgc));
641
642         if (gw->baselineColor.pixel == 0)
643           /*
644            * Default to red.
645            */
646           gdk_colormap_alloc_color(gw->widget.style->colormap,
647                                    &gw->baselineColor, FALSE, TRUE);
648
649         gcv.foreground.pixel = gw->baselineColor.pixel;
650         gcv.function = GDK_COPY;
651         gwc->bbxgc = gdk_gc_new_with_values(widget->window, &gcv,
652                                             GDK_GC_FOREGROUND|GDK_GC_FUNCTION);
653     }
654
655     if (gwc->selgc == 0 || force == TRUE) {
656         if (gwc->selgc != 0)
657           g_object_unref(G_OBJECT(gwc->selgc));
658
659         gcv.foreground.pixel =
660             widget->style->fg[GTK_WIDGET_STATE(widget)].pixel;
661         gcv.background.pixel =
662             widget->style->bg[GTK_WIDGET_STATE(widget)].pixel;
663         gcv.foreground.pixel ^= gcv.background.pixel;
664         gcv.function = GDK_XOR;
665         gwc->selgc = gdk_gc_new_with_values(widget->window, &gcv, gcm);
666     }
667
668     if (gwc->pixgc == 0 || force == TRUE) {
669         if (gwc->pixgc != 0)
670           g_object_unref(G_OBJECT(gwc->pixgc));
671
672         gcv.foreground.pixel =
673             widget->style->fg[GTK_WIDGET_STATE(widget)].pixel;
674         gcv.background.pixel =
675             widget->style->bg[GTK_WIDGET_STATE(widget)].pixel;
676         gcv.function = GDK_COPY;
677         gwc->pixgc = gdk_gc_new_with_values(widget->window, &gcv, gcm);
678     }
679 }
680
681 static void
682 glyphedit_realize(GtkWidget *widget)
683 {
684     Glyphedit *gw;
685     GlypheditClass *gwc;
686     GdkWindowAttr attributes;
687     gint attributes_mask;
688     GdkPixbuf *cb;
689
690     g_return_if_fail(widget != NULL);
691     g_return_if_fail(IS_GLYPHEDIT(widget));
692
693     gwc = GLYPHEDIT_GET_CLASS(widget);
694     gw = GLYPHEDIT(widget);
695     GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
696
697     attributes.window_type = GDK_WINDOW_CHILD;
698     attributes.x = widget->allocation.x;
699     attributes.y = widget->allocation.y;
700     attributes.width = widget->allocation.width;
701     attributes.height = widget->allocation.height;
702     attributes.wclass = GDK_INPUT_OUTPUT;
703     attributes.visual = gtk_widget_get_visual(widget);
704     attributes.colormap = gtk_widget_get_colormap(widget);
705     attributes.event_mask = gtk_widget_get_events(widget);
706     attributes.event_mask |= (GDK_EXPOSURE_MASK|GDK_BUTTON_PRESS_MASK|
707                               GDK_BUTTON_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK|
708                               GDK_POINTER_MOTION_MASK|
709                               GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK|
710                               GDK_LEAVE_NOTIFY_MASK|GDK_FOCUS_CHANGE_MASK|
711                               GDK_PROPERTY_CHANGE_MASK);
712
713     attributes_mask = GDK_WA_X|GDK_WA_Y|GDK_WA_VISUAL|GDK_WA_COLORMAP;
714
715     widget->window = gdk_window_new(gtk_widget_get_parent_window(widget),
716                                     &attributes, attributes_mask);
717     gdk_window_set_user_data(widget->window, widget);
718
719     widget->style = gtk_style_attach(widget->style, widget->window);
720     gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
721
722     /*
723      * Create the crosshair cursor.
724      */
725     if (gwc->cursor == 0) {
726         gwc = GLYPHEDIT_GET_CLASS(widget);
727         cb = gdk_pixbuf_new_from_xpm_data(cross_xpm);
728         gwc->cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
729                                                  cb, 7, 7);
730         g_object_unref(G_OBJECT(cb));
731     }
732
733     glyphedit_create_gcs(widget, FALSE);
734
735     gdk_window_set_cursor(widget->window, gwc->cursor);
736 }
737
738 static gboolean
739 glyphedit_expose(GtkWidget *widget, GdkEventExpose *event)
740 {
741     /*
742      * Paint the shadow first.
743      */
744     if (GTK_WIDGET_DRAWABLE(widget))
745       gtk_paint_shadow(widget->style, widget->window,
746                        GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
747                        &event->area, widget, "glyphedit",
748                        0, 0,
749                        widget->allocation.width,
750                        widget->allocation.height);
751
752     glyphedit_draw(widget, event->region);
753
754     glyphedit_draw_focus(widget, &event->area);
755
756     return FALSE;
757 }
758
759 static gint
760 glyphedit_focus_in(GtkWidget *widget, GdkEventFocus *event)
761 {
762     g_return_val_if_fail(widget != NULL, FALSE);
763     g_return_val_if_fail(IS_GLYPHEDIT(widget), FALSE);
764     g_return_val_if_fail(event != NULL, FALSE);
765
766     GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
767     glyphedit_draw_focus(widget, 0);
768
769     return FALSE;
770 }
771
772 static gint
773 glyphedit_focus_out(GtkWidget *widget, GdkEventFocus *event)
774 {
775     g_return_val_if_fail(widget != NULL, FALSE);
776     g_return_val_if_fail(IS_GLYPHEDIT(widget), FALSE);
777     g_return_val_if_fail(event != NULL, FALSE);
778
779     GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
780     glyphedit_draw_focus(widget, 0);
781
782     return FALSE;
783 }
784
785 /**************************************************************************
786  *
787  * Class and object initialization routines.
788  *
789  **************************************************************************/
790
791 static GType
792 glyphedit_get_operation_type(void)
793 {
794     static GType etype = 0;
795     if (etype == 0) {
796         static const GEnumValue values[] = {
797             {GLYPHEDIT_NONE, "GLYPHEDIT_NONE", "none"},
798             {GLYPHEDIT_SELECT, "GLYPHEDIT_SELECT", "select"},
799             {GLYPHEDIT_DRAW, "GLYPHEDIT_DRAW", "draw"},
800             {GLYPHEDIT_MOVE, "GLYPHEDIT_MOVE", "move"},
801             {GLYPHEDIT_COPY, "GLYPHEDIT_COPY", "copy"},
802             {GLYPHEDIT_FLIP_HORIZONTAL,
803              "GLYPHEDIT_FLIP_HORIZONTAL",
804              "flip-horizontal"},
805             {GLYPHEDIT_FLIP_VERTICAL,
806              "GLYPHEDIT_FLIP_VERTICAL",
807              "flip-verticalal"},
808             {GLYPHEDIT_SHEAR, "GLYPHEDIT_SHEAR", "shear"},
809             {GLYPHEDIT_ROTATE_LEFT,
810              "GLYPHEDIT_ROTATE_LEFT",
811              "rotate-left"},
812             {GLYPHEDIT_ROTATE_RIGHT,
813              "GLYPHEDIT_ROTATE_RIGHT",
814              "rotate-right"},
815             {GLYPHEDIT_ROTATE,
816              "GLYPHEDIT_ROTATE",
817              "rotate"},
818             {GLYPHEDIT_SHIFT_UP_LEFT,
819              "GLYPHEDIT_SHIFT_UP_LEFT",
820              "shift-up-left"},
821             {GLYPHEDIT_SHIFT_UP,
822              "GLYPHEDIT_SHIFT_UP",
823              "shift-up"},
824             {GLYPHEDIT_SHIFT_UP_RIGHT,
825              "GLYPHEDIT_SHIFT_UP_RIGHT",
826              "shift-up-right"},
827             {GLYPHEDIT_SHIFT_LEFT,
828              "GLYPHEDIT_SHIFT_LEFT",
829              "shift-left"},
830             {GLYPHEDIT_SHIFT_RIGHT,
831              "GLYPHEDIT_SHIFT_RIGHT",
832              "shift-right"},
833             {GLYPHEDIT_SHIFT_DOWN_LEFT,
834              "GLYPHEDIT_SHIFT_DOWN_LEFT",
835              "shift-down-left"},
836             {GLYPHEDIT_SHIFT_DOWN,
837              "GLYPHEDIT_SHIFT_DOWN",
838              "shift-down"},
839             {GLYPHEDIT_SHIFT_DOWN_RIGHT,
840              "GLYPHEDIT_SHIFT_DOWN_RIGHT",
841              "shift-down-right"},
842             {0, 0, 0}
843         };
844         etype = g_enum_register_static("GlypheditOperation", values);
845     }
846     return etype;
847 }
848
849 static void
850 glyphedit_init(GTypeInstance *obj, gpointer g_class)
851 {
852     Glyphedit *gw = GLYPHEDIT(obj);
853     GlypheditClass *gwc = GLYPHEDIT_CLASS(g_class);
854     gint fwidth, fpad;
855
856     GTK_WIDGET_SET_FLAGS(gw, GTK_CAN_FOCUS);
857
858     gwc->gridgc = gwc->bbxgc = gwc->pixgc = gwc->selgc = 0;
859
860     gw->default_pixel_size = gw->pixel_size = DEFAULT_PIXEL_SIZE;
861
862     /*
863      * Make sure the spot is the right size.
864      */
865     fpad = (gw->pixel_size + 1) * (gw->pixel_size + 1);
866     if (gw->spot_size < fpad) {
867         if (gw->spot_size == 0)
868           gw->spot = g_malloc(fpad);
869         else
870           gw->spot = g_realloc(gw->spot, fpad);
871         gw->spot_size = fpad;
872     }
873     gw->spot_used = fpad;
874
875     gw->owns_clipboard = FALSE;
876
877     gw->grid = 0;
878
879     gw->last_x = gw->last_y = 0;
880
881     memset((char *) &gw->sel_start, 0, sizeof(GdkPoint));
882     memset((char *) &gw->sel_end, 0, sizeof(GdkPoint));
883
884     /*
885      * Always initialize to the first color.
886      */
887     gw->cidx = 1;
888
889     /*
890      * Initialize the last color seen.
891      */
892     gw->lcolor = 0;
893
894     gtk_widget_style_get(GTK_WIDGET(gw),
895                          "focus-line-width", &fwidth,
896                          "focus-padding", &fpad,
897                          NULL);
898
899     /*
900      * Padding that will appear before and after the focus rectangle.
901      * Hardcode this for now.
902      */
903     gw->border = 4;
904
905     gw->hmargin = gw->widget.style->xthickness + fwidth + fpad + gw->border;
906     gw->vmargin = gw->widget.style->ythickness + fwidth + fpad + gw->border;
907
908     gw->baselineColor.pixel = gw->selectionColor.pixel =
909         gw->cursorColor.pixel = 0;
910
911     gw->baselineColor.red = 0xffff;
912     gw->baselineColor.green = gw->baselineColor.blue = 0;
913
914     gw->op = GLYPHEDIT_DRAW;
915 }
916
917 /*
918  * A convenience function for calling the GLYPH_MODIFIED signal because
919  * so many functions depend on it.
920  */
921 static void
922 glyphedit_signal_glyph_change(Glyphedit *gw)
923 {
924     bdf_bitmap_t image;
925     bdf_metrics_t metrics;
926     GlypheditSignalInfo si;
927
928     if (gw->grid == 0)
929       return;
930
931     glyphedit_get_glyph_metrics(gw, &metrics);
932     bdf_grid_image(gw->grid, &image);
933     si.reason = GLYPHEDIT_GLYPH_MODIFIED;
934     si.metrics = &metrics;
935     si.image = &image;
936     si.color = gw->cidx;
937
938     g_signal_emit(G_OBJECT(gw), glyphedit_signals[GLYPH_MODIFIED], 0, &si);
939     if (image.bytes > 0)
940       free(image.bitmap);
941 }
942
943 /**************************************************************************
944  *
945  * API functions.
946  *
947  **************************************************************************/
948
949 GtkWidget *
950 glyphedit_new(const gchar *prop1, ...)
951 {
952     GtkWidget *w;
953     va_list var_args;
954
955     va_start(var_args, prop1);
956     w = GTK_WIDGET(g_object_new_valist(glyphedit_get_type(), prop1, var_args));
957     va_end(var_args);
958
959     return w;
960 }
961
962 GtkWidget *
963 glyphedit_newv(bdf_glyph_grid_t *grid, guint16 default_pixel_size,
964                gboolean show_x_height, gboolean show_cap_height,
965                guint16 *colors)
966 {
967     Glyphedit *ge = g_object_new(glyphedit_get_type(),
968                                  "glyphGrid", grid,
969                                  "pixelSize", default_pixel_size,
970                                  "showXHeight", show_x_height,
971                                  "showCapHeight", show_cap_height,
972                                  "colorList", colors,
973                                  NULL);
974
975     return GTK_WIDGET(ge);
976 }
977
978 gint32
979 glyphedit_get_encoding(Glyphedit *gw)
980 {
981     g_return_val_if_fail(gw != NULL, -1);
982     g_return_val_if_fail(IS_GLYPHEDIT(gw), -1);
983
984     return (gw->grid) ? gw->grid->encoding : -1;
985 }
986
987 void
988 glyphedit_get_glyph_metrics(Glyphedit *gw, bdf_metrics_t *metrics)
989 {
990     g_return_if_fail(gw != NULL);
991     g_return_if_fail(metrics != NULL);
992     g_return_if_fail(IS_GLYPHEDIT(gw));
993
994     if (!gw->grid)
995       memset(metrics, 0, sizeof(bdf_metrics_t));
996     else {
997         metrics->font_spacing = gw->grid->spacing;
998         metrics->swidth = gw->grid->swidth;
999         metrics->dwidth = gw->grid->dwidth;
1000         metrics->width = gw->grid->glyph_bbx.width;
1001         metrics->height = gw->grid->glyph_bbx.height;
1002         metrics->x_offset = gw->grid->glyph_bbx.x_offset;
1003         metrics->y_offset = gw->grid->glyph_bbx.y_offset;
1004         metrics->ascent = gw->grid->glyph_bbx.ascent;
1005         metrics->descent = gw->grid->glyph_bbx.descent;
1006     }
1007 }
1008
1009 void
1010 glyphedit_get_font_metrics(Glyphedit *gw, bdf_metrics_t *metrics)
1011 {
1012     g_return_if_fail(gw != NULL);
1013     g_return_if_fail(metrics != NULL);
1014     g_return_if_fail(IS_GLYPHEDIT(gw));
1015
1016     if (!gw->grid)
1017       memset(metrics, 0, sizeof(bdf_metrics_t));
1018     else {
1019         metrics->font_spacing = gw->grid->spacing;
1020         metrics->swidth = gw->grid->swidth;
1021         metrics->dwidth = gw->grid->dwidth;
1022         metrics->width = gw->grid->font_bbx.width;
1023         metrics->height = gw->grid->font_bbx.height;
1024         metrics->x_offset = gw->grid->font_bbx.x_offset;
1025         metrics->y_offset = gw->grid->font_bbx.y_offset;
1026         metrics->ascent = gw->grid->font_bbx.ascent;
1027         metrics->descent = gw->grid->font_bbx.descent;
1028     }
1029 }
1030
1031 bdf_psf_unimap_t *
1032 glyphedit_get_psf_mappings(Glyphedit *gw)
1033 {
1034     g_return_val_if_fail(gw != NULL, 0);
1035     g_return_val_if_fail(IS_GLYPHEDIT(gw), 0);
1036
1037     return (gw->grid) ? &gw->grid->unicode : 0;
1038 }
1039
1040 /*
1041  * Can set both font and glyph metrics.
1042  */
1043 void
1044 glyphedit_set_metrics(Glyphedit *gw, bdf_metrics_t *metrics)
1045 {
1046     GtkWidget *w = GTK_WIDGET(gw);
1047
1048     g_return_if_fail(gw != NULL);
1049     g_return_if_fail(metrics != NULL);
1050     g_return_if_fail(IS_GLYPHEDIT(gw));
1051
1052     if (gw->grid == 0)
1053       return;
1054
1055     if (bdf_grid_resize(gw->grid, metrics)) {
1056         glyphedit_signal_glyph_change(gw);
1057         gtk_widget_queue_resize(GTK_WIDGET(gw));
1058     } else if (GTK_WIDGET_REALIZED(w))
1059       /*
1060        * The size didn't change, but we need to redraw if the widget
1061        * has been realized.
1062        */
1063       gtk_widget_queue_draw(GTK_WIDGET(gw));
1064 }
1065
1066 gint
1067 glyphedit_get_spacing(Glyphedit *gw)
1068 {
1069     g_return_val_if_fail(gw != NULL, -1);
1070     g_return_val_if_fail(IS_GLYPHEDIT(gw), -1);
1071     g_return_val_if_fail(gw->grid != NULL, -1);
1072
1073     return gw->grid->spacing;
1074 }
1075
1076 void
1077 glyphedit_set_spacing(Glyphedit *gw, gint spacing, guint16 monowidth)
1078 {
1079     bdf_metrics_t metrics;
1080
1081     g_return_if_fail(gw != NULL);
1082     g_return_if_fail(IS_GLYPHEDIT(gw));
1083     g_return_if_fail(gw->grid != NULL);
1084
1085     gw->grid->spacing = spacing;
1086     if (spacing != BDF_PROPORTIONAL) {
1087         glyphedit_get_font_metrics(gw, &metrics);
1088         metrics.dwidth = metrics.width = monowidth;
1089         glyphedit_set_metrics(gw, &metrics);
1090     }
1091 }
1092
1093 void
1094 glyphedit_set_grid(Glyphedit *gw, bdf_glyph_grid_t *grid)
1095 {
1096     g_return_if_fail(gw != NULL);
1097     g_return_if_fail(IS_GLYPHEDIT(gw));
1098
1099     bdf_free_glyph_grid(gw->grid);
1100     gw->grid = grid;
1101
1102     if (grid) {
1103       gw->last_x = grid->base_x;
1104       gw->last_y = grid->base_y;
1105     } else
1106       gw->last_x = gw->last_y = 0;
1107
1108     /*
1109      * If the widget is in Move or Copy mode, change back to Select mode.
1110      */
1111     if (gw->op == GLYPHEDIT_MOVE || gw->op == GLYPHEDIT_COPY) {
1112         gw->pending_op = gw->op;
1113         gw->op = GLYPHEDIT_SELECT;
1114     }
1115
1116     gw->cidx = 1;
1117     gw->lcolor = 0;
1118
1119     gtk_widget_queue_resize(GTK_WIDGET(gw));
1120 }
1121
1122 gboolean
1123 glyphedit_get_modified(Glyphedit *gw)
1124 {
1125     g_return_val_if_fail(gw != NULL, FALSE);
1126     g_return_val_if_fail(IS_GLYPHEDIT(gw), FALSE);
1127
1128     return (gw->grid) ? gw->grid->modified : FALSE;
1129 }
1130
1131 void
1132 glyphedit_set_modified(Glyphedit *gw, gboolean modified)
1133 {
1134     g_return_if_fail(gw != NULL);
1135     g_return_if_fail(IS_GLYPHEDIT(gw));
1136
1137     if (gw->grid)
1138       gw->grid->modified = ((modified == TRUE) ? 1 : 0);
1139 }
1140
1141 void
1142 glyphedit_signal_modified(Glyphedit *gw)
1143 {
1144     g_return_if_fail(gw != NULL);
1145     g_return_if_fail(IS_GLYPHEDIT(gw));
1146
1147     glyphedit_signal_glyph_change(gw);
1148 }
1149
1150 gboolean
1151 glyphedit_get_selecting(Glyphedit *gw)
1152 {
1153     g_return_val_if_fail(gw != NULL, FALSE);
1154     g_return_val_if_fail(IS_GLYPHEDIT(gw), FALSE);
1155     g_return_val_if_fail(gw->grid != NULL, FALSE);
1156
1157     return bdf_has_selection(gw->grid, 0, 0, 0, 0) ? TRUE : FALSE;
1158 }
1159
1160 gboolean
1161 glyphedit_clipboard_empty(Glyphedit *gw)
1162 {
1163     GdkWindow *owner;
1164     gboolean empty = TRUE;
1165     GdkAtom atype;
1166     gint aformat, nitems;
1167     guchar *data;
1168
1169     g_return_val_if_fail(gw != NULL, empty);
1170     g_return_val_if_fail(IS_GLYPHEDIT(gw), empty);
1171
1172     if ((owner = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD)) == 0)
1173       return empty;
1174
1175     /*
1176      * Check to see if the clipboard contents are empty or not.
1177      *
1178      * This is handled specially to allow determination of this without
1179      * using up what might be a lot of memory to get the whole contents.  It
1180      * will have to be changed for Windows.
1181      */
1182     if (gdk_property_get(owner, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP,
1183                          0, 16, FALSE, &atype, &aformat, &nitems, &data)) {
1184         if (nitems > 0) {
1185             empty = FALSE;
1186             free((char *) data);
1187         }
1188     }
1189
1190     return empty;
1191 }
1192
1193 void
1194 glyphedit_get_image(Glyphedit *gw, bdf_bitmap_t *image)
1195 {
1196     g_return_if_fail(gw != NULL);
1197     g_return_if_fail(IS_GLYPHEDIT(gw));
1198     g_return_if_fail(image != NULL);
1199
1200     if (gw->grid)
1201       bdf_grid_image(gw->grid, image);
1202     else
1203       memset(image, 0, sizeof(bdf_bitmap_t));
1204 }
1205
1206 bdf_glyph_grid_t *
1207 glyphedit_get_grid(Glyphedit *gw)
1208 {
1209     g_return_val_if_fail(gw != NULL, 0);
1210     g_return_val_if_fail(IS_GLYPHEDIT(gw), 0);
1211
1212     return gw->grid;
1213 }
1214
1215 bdf_glyph_t *
1216 glyphedit_get_glyph(Glyphedit *gw, gboolean *unencoded)
1217 {
1218     g_return_val_if_fail(gw != NULL, 0);
1219     g_return_val_if_fail(IS_GLYPHEDIT(gw), 0);
1220
1221     if (gw->grid) {
1222         if (unencoded)
1223           *unencoded = (gw->grid->unencoded == 0) ? FALSE : TRUE;
1224         return bdf_grid_glyph(gw->grid);
1225     }
1226     if (unencoded)
1227       *unencoded = FALSE;
1228     return 0;
1229 }
1230
1231 void
1232 glyphedit_set_show_cap_height(Glyphedit *gw, gboolean show)
1233 {
1234     g_return_if_fail(gw != NULL);
1235     g_return_if_fail(IS_GLYPHEDIT(gw));
1236
1237     gw->show_cap_height = show;
1238
1239     /*
1240      * Redraw the bounding box.
1241      */
1242     glyphedit_draw_font_bbx(gw);
1243 }
1244
1245 void
1246 glyphedit_set_show_x_height(Glyphedit *gw, gboolean show)
1247 {
1248     g_return_if_fail(gw != NULL);
1249     g_return_if_fail(IS_GLYPHEDIT(gw));
1250
1251     gw->show_x_height = show;
1252
1253     /*
1254      * Redraw the bounding box.
1255      */
1256     glyphedit_draw_font_bbx(gw);
1257 }
1258
1259 void
1260 glyphedit_crop_glyph(Glyphedit *gw)
1261 {
1262     g_return_if_fail(gw != NULL);
1263     g_return_if_fail(IS_GLYPHEDIT(gw));
1264
1265     if (gw->grid && bdf_grid_crop(gw->grid, 1))
1266       glyphedit_signal_glyph_change(gw);
1267
1268     glyphedit_draw_glyph(gw);
1269 }
1270
1271 void
1272 glyphedit_shift_glyph(Glyphedit *gw, gint16 xcount, gint16 ycount)
1273 {
1274     g_return_if_fail(gw != NULL);
1275     g_return_if_fail(IS_GLYPHEDIT(gw));
1276
1277     if (gw->grid && bdf_grid_shift(gw->grid, xcount, ycount))
1278       glyphedit_signal_glyph_change(gw);
1279
1280     glyphedit_draw_glyph(gw);
1281 }
1282
1283 void
1284 glyphedit_rotate_glyph(Glyphedit *gw, gint16 degrees)
1285 {
1286     gint resize = 0;
1287
1288     g_return_if_fail(gw != NULL);
1289     g_return_if_fail(IS_GLYPHEDIT(gw));
1290
1291     if (gw->grid && bdf_grid_rotate(gw->grid, degrees, &resize)) {
1292         glyphedit_signal_glyph_change(gw);
1293         if (resize)
1294           gtk_widget_queue_resize(GTK_WIDGET(gw));
1295         else
1296           glyphedit_draw_glyph(gw);
1297     }
1298 }
1299
1300 void
1301 glyphedit_shear_glyph(Glyphedit *gw, gint16 degrees)
1302 {
1303     gint resize = 0;
1304
1305     g_return_if_fail(gw != NULL);
1306     g_return_if_fail(IS_GLYPHEDIT(gw));
1307
1308     if (gw->grid && bdf_grid_shear(gw->grid, degrees, &resize)) {
1309         glyphedit_signal_glyph_change(gw);
1310         if (resize)
1311           gtk_widget_queue_resize(GTK_WIDGET(gw));
1312         else
1313           glyphedit_draw_glyph(gw);
1314     }
1315 }
1316
1317 void
1318 glyphedit_embolden_glyph(Glyphedit *gw)
1319 {
1320     g_return_if_fail(gw != NULL);
1321     g_return_if_fail(IS_GLYPHEDIT(gw));
1322
1323     if (gw->grid && bdf_grid_embolden(gw->grid)) {
1324         glyphedit_signal_glyph_change(gw);
1325
1326         /*
1327          * Simply redraw the glyph because the size didn't change,
1328          * only the bitmap.
1329          */
1330         glyphedit_draw_glyph(gw);
1331     }
1332 }
1333
1334 void
1335 glyphedit_flip_glyph(Glyphedit *gw, GtkOrientation direction)
1336 {
1337     gint flipped;
1338
1339     g_return_if_fail(gw != NULL);
1340     g_return_if_fail(IS_GLYPHEDIT(gw));
1341     g_return_if_fail(gw->grid != NULL);
1342
1343     flipped = (direction == GTK_ORIENTATION_HORIZONTAL) ?
1344         bdf_grid_flip(gw->grid, -1) : bdf_grid_flip(gw->grid, 1);
1345
1346     if (flipped) {
1347         glyphedit_signal_glyph_change(gw);
1348
1349         /*
1350          * Simply redraw the glyph because the size didn't change,
1351          * only the bitmap.
1352          */
1353         glyphedit_draw_glyph(gw);
1354     }
1355 }
1356
1357 void
1358 glyphedit_set_pixel_size(Glyphedit *gw, guint pixel_size)
1359 {
1360     gint bytes;
1361
1362     g_return_if_fail(gw != NULL);
1363     g_return_if_fail(IS_GLYPHEDIT(gw));
1364
1365     if (pixel_size < MIN_PIXEL_SIZE || pixel_size > MAX_PIXEL_SIZE)
1366       return;
1367
1368     /*
1369      * Queue up a resize to force the resize and redraw.
1370      */
1371     gw->pixel_size = pixel_size;
1372
1373     /*
1374      * Make sure the spot is the right size.
1375      */
1376     bytes = (pixel_size + 1) * (pixel_size + 1);
1377     if (gw->spot_size < bytes) {
1378         if (gw->spot_size == 0)
1379           gw->spot = g_malloc(bytes);
1380         else
1381           gw->spot = g_realloc(gw->spot, bytes);
1382         gw->spot_size = bytes;
1383     }
1384     gw->spot_used = bytes;
1385
1386     gtk_widget_queue_resize(GTK_WIDGET(gw));
1387 }
1388
1389 guint
1390 glyphedit_get_pixel_size(Glyphedit *gw)
1391 {
1392     g_return_val_if_fail(gw != NULL, GLYPHEDIT_NONE);
1393     g_return_val_if_fail(IS_GLYPHEDIT(gw), GLYPHEDIT_NONE);
1394     g_return_val_if_fail(gw->grid != NULL, GLYPHEDIT_NONE);
1395
1396     return gw->pixel_size;
1397 }
1398
1399 GlypheditOperation
1400 glyphedit_get_operation(Glyphedit *gw)
1401 {
1402     g_return_val_if_fail(gw != NULL, GLYPHEDIT_NONE);
1403     g_return_val_if_fail(IS_GLYPHEDIT(gw), GLYPHEDIT_NONE);
1404     g_return_val_if_fail(gw->grid != NULL, GLYPHEDIT_NONE);
1405
1406     return gw->op;
1407 }
1408
1409 void
1410 glyphedit_set_operation(Glyphedit *gw, GlypheditOperation op)
1411 {
1412     gint16 sx, sy, x, y, wd, ht;
1413
1414     g_return_if_fail(gw != NULL);
1415     g_return_if_fail(IS_GLYPHEDIT(gw));
1416     g_return_if_fail(gw->grid != NULL);
1417
1418     if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
1419         if (op == GLYPHEDIT_MOVE)
1420           bdf_detach_selection(gw->grid);
1421         else if (op == GLYPHEDIT_COPY)
1422           bdf_attach_selection(gw->grid);
1423         else {
1424             if (op == GLYPHEDIT_DRAW) {
1425                 /*
1426                  * Attach the selected part of the bitmap.
1427                  */
1428                 bdf_attach_selection(gw->grid);
1429
1430                 /*
1431                  * Erase the selected rectangle.
1432                  */
1433                 for (sy = y; sy < y + ht; sy++) {
1434                     for (sx = x; sx < x + wd; sx++)
1435                       glyphedit_draw_pixel(gw, sx, sy, FALSE);
1436                 }
1437                 bdf_lose_selection(gw->grid);
1438             }
1439
1440             gw->op = op;
1441         }
1442         gw->pending_op = GLYPHEDIT_NONE;
1443
1444         glyphedit_signal_glyph_change(gw);
1445     } else {
1446         if (op == GLYPHEDIT_MOVE || op == GLYPHEDIT_COPY) {
1447             gw->op = GLYPHEDIT_SELECT;
1448             gw->pending_op = op;
1449         } else {
1450             gw->op = op;
1451             gw->pending_op = GLYPHEDIT_NONE;
1452         }
1453     }
1454 }
1455
1456 void
1457 glyphedit_insert_bitmap(Glyphedit *gw, bdf_bitmap_t *bitmap)
1458 {
1459     GtkWidget *w = GTK_WIDGET(gw);
1460     GdkWindow *win;
1461     gint16 sx, sy, x, y, wd, ht;
1462     bdf_metrics_t metrics;
1463     GlypheditSignalInfo si;
1464
1465     g_return_if_fail(gw != NULL);
1466     g_return_if_fail(IS_GLYPHEDIT(gw));
1467
1468     if ((win = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD)) == 0) {
1469         gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
1470                                 GDK_CURRENT_TIME, FALSE);
1471         win = w->window;
1472     } else if (win != w->window)
1473       gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
1474                               GDK_CURRENT_TIME, FALSE);
1475
1476     if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
1477         /*
1478          * This widget already has a selection, so release it.
1479          */
1480         if (gw->op != GLYPHEDIT_SELECT)
1481           bdf_attach_selection(gw->grid);
1482
1483         for (sy = y; sy < y + ht; sy++) {
1484             for (sx = x; sx < x + wd; sx++)
1485               glyphedit_draw_pixel(gw, sx, sy, FALSE);
1486         }
1487         bdf_lose_selection(gw->grid);
1488     }
1489
1490     bitmap->x = gw->last_x;
1491     bitmap->y = gw->last_y;
1492
1493     glyphedit_get_font_metrics(gw, &metrics);
1494     if (bitmap->width > metrics.width || bitmap->height > metrics.height) {
1495         /*
1496          * Adjust the insert position on the X axis if necessary.
1497          */
1498         if (bitmap->width > metrics.width)
1499           bitmap->x = gw->grid->base_x + gw->grid->font_bbx.x_offset;
1500         /*
1501          * Adjust the insert position on the Y axis and the ascent if
1502          * necessary.
1503          */
1504         if (bitmap->height > metrics.height) {
1505             bitmap->y = 0;
1506             metrics.ascent = bitmap->height - gw->grid->font_bbx.descent;
1507         }
1508         metrics.width = bitmap->width;
1509         metrics.height = bitmap->height;
1510         glyphedit_set_metrics(gw, &metrics);
1511     }
1512
1513     /*
1514      * Set the selection in the grid.
1515      */
1516     bdf_add_selection(gw->grid, bitmap);
1517
1518     /*
1519      * Now update the grid.
1520      */
1521     if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
1522         for (sy = y; sy < y + ht; sy++) {
1523             for (sx = x; sx < x + wd; sx++)
1524               glyphedit_draw_pixel(gw, sx, sy, TRUE);
1525         }
1526     }
1527
1528     /*
1529      * Set up and call the operation change signal.
1530      */
1531     si.reason = GLYPHEDIT_OPERATION_CHANGE;
1532     si.operation = GLYPHEDIT_MOVE;
1533     g_signal_emit(G_OBJECT(gw), glyphedit_signals[OPERATION_CHANGE], 0,
1534                   &si);
1535
1536     /*
1537      * Set up and call the modified signal.
1538      */
1539     glyphedit_signal_glyph_change(gw);
1540
1541     /*
1542      * Make sure the widget goes into MOVE mode at this point.
1543      * This allows the user to position what was pasted without
1544      * destroying the glyph bitmap that was already there.
1545      */
1546     if (gw->op != GLYPHEDIT_MOVE) {
1547         gw->op = GLYPHEDIT_MOVE;
1548         gw->pending_op = GLYPHEDIT_NONE;
1549     }
1550
1551     glyphedit_copy_selection(gw);
1552 }
1553
1554 static void
1555 glyphedit_own_clipboard(Glyphedit *gw)
1556 {
1557     GtkWidget *w;
1558     GdkWindow *win;
1559
1560     w = GTK_WIDGET(gw);
1561     if (!GTK_WIDGET_REALIZED(w) || gw->owns_clipboard == TRUE)
1562       return;
1563
1564     win = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD);
1565     gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
1566                             GDK_CURRENT_TIME, FALSE);
1567
1568     gw->owns_clipboard =
1569         (gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD) == w->window) ? TRUE : FALSE;
1570
1571     /*
1572      * The Intrinsics may need to have a SelectionClear notice sent. Probably
1573      * won't be necessary on Windows.
1574      */
1575 }
1576
1577 static guchar *
1578 glyphedit_encode_selection(Glyphedit *gw, gint *bytes)
1579 {
1580     gint bcount, size;
1581     gint16 wd, ht;
1582     guchar *bmap, *bp;
1583
1584     *bytes = 0;
1585     if (!bdf_has_selection(gw->grid, 0, 0, &wd, &ht))
1586       return 0;
1587
1588     size = bcount = (gint) gw->grid->sel.bytes >> 1;
1589     size += sizeof(guint16) * 3;
1590     bp = bmap = (guchar *) g_malloc(size);
1591
1592     /*
1593      * Encode the width and height in Most Significant Byte order assuming
1594      * the width and height types are 16-bit values.
1595      */
1596     if (!bdf_little_endian()) {
1597         *bp++ = (gw->grid->bpp >> 8) & 0xff;
1598         *bp++ = gw->grid->bpp & 0xff;
1599         *bp++ = (wd >> 8) & 0xff;
1600         *bp++ = wd & 0xff;
1601         *bp++ = (ht >> 8) & 0xff;
1602         *bp++ = ht & 0xff;
1603     } else {
1604         *bp++ = gw->grid->bpp & 0xff;
1605         *bp++ = (gw->grid->bpp >> 8) & 0xff;
1606         *bp++ = wd & 0xff;
1607         *bp++ = (wd >> 8) & 0xff;
1608         *bp++ = ht & 0xff;
1609         *bp++ = (ht >> 8) & 0xff;
1610     }
1611
1612     (void) memcpy((char *) bp, (char *) gw->grid->sel.bitmap, bcount);
1613
1614     *bytes = size;
1615     return bmap;
1616 }
1617
1618 void
1619 glyphedit_copy_selection(Glyphedit *gw)
1620 {
1621     GtkWidget *w = GTK_WIDGET(gw);
1622     guchar *sel;
1623     gint bytes;
1624
1625     g_return_if_fail(gw != NULL);
1626     g_return_if_fail(IS_GLYPHEDIT(gw));
1627
1628     /*
1629      * If the widget has no selection, then this routine will return 0.
1630      */
1631     if ((sel = glyphedit_encode_selection(gw, &bytes)) == 0)
1632       return;
1633
1634     /*
1635      * Go ahead and actually write the data to the clipboard and then free the
1636      * buffer.
1637      */
1638     gdk_property_change(w->window, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP,
1639                         8, GDK_PROP_MODE_REPLACE, sel, (gint) bytes);
1640
1641     g_free(sel);
1642 }
1643
1644 void
1645 glyphedit_cut_selection(Glyphedit *gw)
1646 {
1647     GtkWidget *w = GTK_WIDGET(gw);
1648     guchar *sel;
1649     gint bytes;
1650
1651     g_return_if_fail(gw != NULL);
1652     g_return_if_fail(IS_GLYPHEDIT(gw));
1653
1654     /*
1655      * If the widget has no selection, then this routine will return 0.
1656      */
1657     if ((sel = glyphedit_encode_selection(gw, &bytes)) == 0)
1658       return;
1659
1660     /*
1661      * Go ahead and actually write the data to the clipboard and then free the
1662      * buffer.
1663      */
1664     gdk_property_change(w->window, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP,
1665                         8, GDK_PROP_MODE_REPLACE, sel, (gint) bytes);
1666
1667     g_free(sel);
1668
1669     /*
1670      * Now actually delete the selection and update the glyph.
1671      */
1672     bdf_delete_selection(gw->grid);
1673     bdf_lose_selection(gw->grid);
1674     if (gw->op != GLYPHEDIT_DRAW) {
1675         gw->pending_op = gw->op;
1676         gw->op = GLYPHEDIT_SELECT;
1677     }
1678     glyphedit_draw_glyph(gw);
1679     glyphedit_signal_glyph_change(gw);
1680 }
1681
1682 void
1683 glyphedit_change_operation(Glyphedit *gw, GlypheditOperation op)
1684 {
1685     gboolean call_modify;
1686     gint16 sx, sy, x, y, wd, ht;
1687
1688     g_return_if_fail(gw != NULL);
1689     g_return_if_fail(IS_GLYPHEDIT(gw));
1690
1691     call_modify = TRUE;
1692
1693     /*
1694      * Special handling is needed for move and copy operations.  If a
1695      * selection does not exist yet, then make the move/copy a pending
1696      * operation and set the operation to select.  Once the selection is made,
1697      * the operation will be changed to the pending move/copy operation.  If a
1698      * selection exists, then set the move/copy operation and detach/attach
1699      * the selection accordingly.
1700      */
1701     if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
1702         if (op == GLYPHEDIT_MOVE)
1703           bdf_detach_selection(gw->grid);
1704         else if (op == GLYPHEDIT_COPY)
1705           bdf_attach_selection(gw->grid);
1706         else {
1707             if (op == GLYPHEDIT_DRAW) {
1708                 /*
1709                  * Attach the selected part of the bitmap.
1710                  */
1711                 bdf_attach_selection(gw->grid);
1712
1713                 /*
1714                  * Erase the selected rectangle.
1715                  */
1716                 for (sy = y; sy < y + ht; sy++) {
1717                     for (sx = x; sx < x + wd; sx++)
1718                       glyphedit_draw_pixel(gw, sx, sy, FALSE);
1719                 }
1720                 bdf_lose_selection(gw->grid);
1721
1722             } else
1723               call_modify = FALSE;
1724             gw->op = op;
1725         }
1726         gw->pending_op = GLYPHEDIT_NONE;
1727         glyphedit_signal_glyph_change(gw);
1728     } else {
1729         if (op == GLYPHEDIT_MOVE || op == GLYPHEDIT_COPY) {
1730             gw->op = GLYPHEDIT_SELECT;
1731             gw->pending_op = op;
1732         } else {
1733             gw->op = op;
1734             gw->pending_op = GLYPHEDIT_NONE;
1735         }
1736     }
1737 }
1738
1739 void
1740 glyphedit_set_color(Glyphedit *gw, gint idx)
1741 {
1742     GlypheditSignalInfo si;
1743
1744     g_return_if_fail(gw != NULL);
1745     g_return_if_fail(IS_GLYPHEDIT(gw));
1746     g_return_if_fail(gw->grid != 0);
1747
1748     if (gw->grid) {
1749         if (idx <= 0)
1750           idx = (1 << gw->grid->bpp);
1751         else if (idx > (1 << gw->grid->bpp))
1752           idx = 1;
1753     } else
1754       idx = 1;
1755
1756     if (idx != gw->cidx) {
1757         si.reason = GLYPHEDIT_COLOR_CHANGE;
1758         si.color = idx;
1759         g_signal_emit(G_OBJECT(gw), glyphedit_signals[COLOR_CHANGE], 0, &si);
1760     }
1761
1762     gw->cidx = idx;
1763 }
1764
1765 void
1766 glyphedit_paste_selection(Glyphedit *gw)
1767 {
1768     GtkWidget *w = GTK_WIDGET(gw);
1769     GdkWindow *win;
1770     GdkAtom atype;
1771     gint aformat, nitems;
1772     guchar *data, *bp;
1773     gint16 sx, sy, x, y, wd, ht;
1774     bdf_metrics_t metrics;
1775     bdf_bitmap_t image;
1776     GlypheditSignalInfo si;
1777     
1778     g_return_if_fail(gw != NULL);
1779     g_return_if_fail(IS_GLYPHEDIT(gw));
1780     g_return_if_fail(gw->grid != NULL);
1781
1782     if ((win = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD)) == 0) {
1783         gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
1784                                 GDK_CURRENT_TIME, FALSE);
1785         win = w->window;
1786     }
1787
1788     nitems = 0;
1789     gdk_property_get(win, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP,
1790                      0, 10240, FALSE, &atype, &aformat, &nitems, &data);
1791
1792     if (win != w->window)
1793       gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD,
1794                               GDK_CURRENT_TIME, FALSE);
1795
1796     if (nitems > 0) {
1797         /*
1798          * Got a bitmap.
1799          */
1800
1801         if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
1802             /*
1803              * This widget already has a selection, so release it.
1804              */
1805             if (gw->op != GLYPHEDIT_SELECT)
1806               bdf_attach_selection(gw->grid);
1807
1808             for (sy = y; sy < y + ht; sy++) {
1809                 for (sx = x; sx < x + wd; sx++)
1810                   glyphedit_draw_pixel(gw, sx, sy, FALSE);
1811             }
1812             bdf_lose_selection(gw->grid);
1813         }
1814
1815         bp = data;
1816
1817         if (!bdf_little_endian()) {
1818             image.bpp = (*bp++ << 8) & 0xff00;
1819             image.bpp |= *bp++;
1820             image.width = (*bp++ << 8) & 0xff00;
1821             image.width |= *bp++;
1822             image.height = (*bp++ << 8) & 0xff00;
1823             image.height |= *bp++;
1824         } else {
1825             image.bpp = *bp++ & 0xff;
1826             image.bpp |= (*bp++ << 8) & 0xff00;
1827             image.width = *bp++ & 0xff;
1828             image.width |= (*bp++ << 8) & 0xff00;
1829             image.height = *bp++ & 0xff;
1830             image.height |= (*bp++ << 8) & 0xff00;
1831         }
1832
1833         image.bytes = (((image.width * image.bpp) + 7) >> 3) * image.height;
1834         image.bitmap = bp;
1835
1836         image.x = gw->last_x;
1837         image.y = gw->last_y;
1838
1839         /*
1840          * If the bitmap being pasted is larger than the current grid, then
1841          * resize the grid before doing anything else.
1842          */
1843         glyphedit_get_font_metrics(gw, &metrics);
1844         if (image.width > metrics.width || image.height > metrics.height) {
1845             /*
1846              * Adjust the insert position on the X axis if necessary.
1847              */
1848             if (image.width > metrics.width)
1849               image.x = gw->grid->base_x +
1850                   gw->grid->font_bbx.x_offset;
1851             /*
1852              * Adjust the insert position on the Y axis and the ascent if
1853              * necessary.
1854              */
1855             if (image.height > metrics.height) {
1856                 image.y = 0;
1857                 metrics.ascent = image.height - gw->grid->font_bbx.descent;
1858             }
1859             metrics.width = image.width;
1860             metrics.height = image.height;
1861             glyphedit_set_metrics(gw, &metrics);
1862         }
1863
1864         /*
1865          * Set the selection in the grid.
1866          */
1867         bdf_add_selection(gw->grid, &image);
1868
1869         /*
1870          * Now update the grid.
1871          */
1872         if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) {
1873             for (sy = y; sy < y + ht; sy++) {
1874                 for (sx = x; sx < x + wd; sx++)
1875                   glyphedit_draw_pixel(gw, sx, sy, TRUE);
1876             }
1877         }
1878
1879         /*
1880          * Set up and call the image update.
1881          */
1882         glyphedit_signal_glyph_change(gw);
1883
1884         /*
1885          * Free up the original value passed.
1886          */
1887         g_free(data);
1888
1889         /*
1890          * Alert the client that the widget is changing to the MOVE
1891          * operation.
1892          */
1893         si.reason = GLYPHEDIT_OPERATION_CHANGE;
1894         si.operation = GLYPHEDIT_MOVE;
1895         g_signal_emit(G_OBJECT(gw), glyphedit_signals[OPERATION_CHANGE],
1896                       0, &si);
1897
1898         /*
1899          * Make sure the widget goes into MOVE mode at this point.
1900          * This allows the user to position what was pasted without
1901          * destroying the glyph bitmap that was already there.
1902          */
1903         if (gw->op != GLYPHEDIT_MOVE) {
1904             gw->op = GLYPHEDIT_MOVE;
1905             gw->pending_op = GLYPHEDIT_NONE;
1906         }
1907
1908         /*
1909          * Last, recopy the selection to the clipboard because changing owners
1910          * causes the data to be lost.
1911          */
1912         glyphedit_copy_selection(gw);
1913     }
1914 }
1915
1916 void
1917 glyphedit_select_all(Glyphedit *gw)
1918 {
1919     gint16 tx, ty, sx, sy, wd, ht;
1920     GlypheditSignalInfo si;
1921
1922     g_return_if_fail(gw != NULL);
1923     g_return_if_fail(IS_GLYPHEDIT(gw));
1924     g_return_if_fail(gw->grid != NULL);
1925
1926     /*
1927      * If a selection already exists, clear it.
1928      */
1929     if (bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht)) {
1930         if (gw->op != GLYPHEDIT_SELECT)
1931           bdf_attach_selection(gw->grid);
1932
1933         for (ty = sy; ty < sy + ht; ty++) {
1934             for (tx = sx; tx < sx + wd; tx++)
1935               glyphedit_draw_pixel(gw, tx, ty, FALSE);
1936         }
1937         bdf_lose_selection(gw->grid);
1938     }
1939
1940     wd = gw->grid->glyph_bbx.width;
1941     ht = gw->grid->glyph_bbx.height;
1942
1943     sx = gw->sel_start.x = gw->grid->glyph_x;
1944     sy = gw->sel_start.y = gw->grid->glyph_y;
1945     gw->sel_end.x = gw->grid->glyph_x + wd;
1946     gw->sel_end.y = gw->grid->glyph_y + ht;
1947
1948     /*
1949      * Gain control of the GLYPHEDIT_CLIPBOARD atom.
1950      */
1951     glyphedit_own_clipboard(gw);
1952
1953     bdf_set_selection(gw->grid, sx, sy, wd, ht);
1954     bdf_detach_selection(gw->grid);
1955
1956     for (ty = sy; ty < sy + ht; ty++) {
1957         for (tx = sx; tx < sx + wd; tx++)
1958           glyphedit_draw_pixel(gw, tx, ty, TRUE);
1959     }
1960
1961     /*
1962      * Alert the client that the widget is changing to the MOVE
1963      * operation.
1964      */
1965     si.reason = GLYPHEDIT_OPERATION_CHANGE;
1966     si.operation = GLYPHEDIT_MOVE;
1967     g_signal_emit(G_OBJECT(gw), glyphedit_signals[OPERATION_CHANGE],
1968                   0, &si);
1969
1970     /*
1971      * Make sure the widget goes into MOVE mode at this point.
1972      * This allows the user to position what was pasted without
1973      * destroying the glyph bitmap that was already there.
1974      */
1975     if (gw->op != GLYPHEDIT_MOVE) {
1976         gw->op = GLYPHEDIT_MOVE;
1977         gw->pending_op = GLYPHEDIT_NONE;
1978     }
1979 }
1980
1981 gint32
1982 glyphedit_encoding(Glyphedit *gw)
1983 {
1984     g_return_val_if_fail(gw != NULL, -1);
1985     g_return_val_if_fail(IS_GLYPHEDIT(gw), -1);
1986     g_return_val_if_fail(gw->grid != NULL, -1);
1987
1988     return (gw->grid->unencoded) ? -1 : gw->grid->encoding;
1989 }
1990
1991 static void
1992 glyphedit_get_pointer_coord(Glyphedit *gw, gint16 ex, gint16 ey,
1993                             gint16 *px, gint16 *py)
1994 {
1995     GtkWidget *w = GTK_WIDGET(gw);
1996     gint16 x, y, wd, ht;
1997
1998     wd = (gw->pixel_size + 4) * gw->grid->grid_width;
1999     ht = (gw->pixel_size + 4) * gw->grid->grid_height;
2000
2001     /*
2002      * Need the plus 1 to account for the outer rectangle.
2003      */
2004     x = (w->allocation.width >> 1) - (wd >> 1) + 1;
2005     y = (w->allocation.height >> 1) - (ht >> 1) + 1;
2006
2007     if (ex < x || ex > x + wd)
2008       *px = -1;
2009     else
2010       *px = (ex - x) / (gw->pixel_size + 4);
2011
2012     if (ey < y || ey > y + ht)
2013       *py = -1;
2014     else
2015       *py = (ey - y) / (gw->pixel_size + 4);
2016
2017     /*
2018      * Adjust for a possible overrun off the edges of the grid.
2019      */
2020     if (*px >= gw->grid->grid_width)
2021       *px = gw->grid->grid_width - 1;
2022     if (*py >= gw->grid->grid_height)
2023       *py = gw->grid->grid_height - 1;
2024 }
2025
2026 static gboolean
2027 glyphedit_in_selection(Glyphedit *gw, gint16 x, gint16 y)
2028 {
2029     return (((gw->sel_start.y <= y && y <= gw->sel_end.y) ||
2030              (gw->sel_end.y <= y && y <= gw->sel_start.y)) &&
2031             ((gw->sel_start.x <= x && x <= gw->sel_end.x) ||
2032              (gw->sel_end.x <= x && x <= gw->sel_start.x)))
2033         ? TRUE : FALSE;
2034 }
2035
2036 static gboolean
2037 glyphedit_in_intersection(Glyphedit *gw, gint16 ix, gint16 iy,
2038                           gint16 x, gint16 y)
2039 {
2040     return (((gw->sel_start.y <= y && y <= iy) ||
2041              (iy <= y && y <= gw->sel_start.y)) &&
2042             ((gw->sel_start.x <= x && x <= ix) ||
2043              (ix <= x && x <= gw->sel_start.x))) ? TRUE : FALSE;
2044 }
2045
2046 static void
2047 glyphedit_update_selection(Glyphedit *gw, gint16 x, gint16 y, gboolean set)
2048 {
2049     gint16 wd, ht;
2050
2051     for (ht = 0; ht < gw->grid->grid_height; ht++) {
2052         for (wd = 0; wd < gw->grid->grid_width; wd++) {
2053             if (glyphedit_in_intersection(gw, x, y, wd, ht) == FALSE &&
2054                 glyphedit_in_selection(gw, wd, ht) == TRUE)
2055               /*
2056                * Clear or set the pixel.
2057                */
2058               glyphedit_draw_pixel(gw, wd, ht, set);
2059         }
2060     }
2061 }
2062
2063 static gboolean
2064 glyphedit_button_press(GtkWidget *w, GdkEventButton *event)
2065 {
2066     Glyphedit *gw;
2067     gint16 x, y, sx, sy, tx, ty, wd, ht;
2068     gboolean changed;
2069
2070     gw = GLYPHEDIT(w);
2071
2072     glyphedit_get_pointer_coord(gw, (gint16) event->x, (gint16) event->y,
2073                                 &x, &y);
2074
2075     if (event->button == 2 && (event->state & GDK_SHIFT_MASK)) {
2076         /*
2077          * Paste.
2078          */
2079         glyphedit_paste_selection(gw);
2080         return FALSE;
2081     }
2082
2083     changed = FALSE;
2084     if (gw->op == GLYPHEDIT_DRAW) {
2085         switch (event->button) {
2086           case 1:
2087             if ((changed = bdf_grid_set_pixel(gw->grid, x, y, gw->cidx)))
2088               glyphedit_draw_pixel(gw, x, y, FALSE);
2089             break;
2090           case 2:
2091             if ((changed = bdf_grid_invert_pixel(gw->grid, x, y, gw->cidx)))
2092               glyphedit_draw_pixel(gw, x, y, FALSE);
2093             break;
2094           case 3:
2095             if ((changed = bdf_grid_clear_pixel(gw->grid, x, y)))
2096               glyphedit_draw_pixel(gw, x, y, FALSE);
2097             break;
2098         }
2099         if (changed == TRUE)
2100           glyphedit_signal_glyph_change(gw);
2101     } else if (gw->op == GLYPHEDIT_SELECT) {
2102         /*
2103          * If a selection already exists, clear it.
2104          */
2105         if (bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht)) {
2106             if (gw->pending_op != GLYPHEDIT_NONE)
2107               bdf_attach_selection(gw->grid);
2108
2109             for (ty = sy; ty < sy + ht; ty++) {
2110                 for (tx = sx; tx < sx + wd; tx++)
2111                   glyphedit_draw_pixel(gw, tx, ty, FALSE);
2112             }
2113             bdf_lose_selection(gw->grid);
2114         }
2115
2116         /*
2117          * Select the pixel at the point and initialize the selection
2118          * rectangle.
2119          */
2120         glyphedit_draw_pixel(gw, x, y, TRUE);
2121
2122         gw->sel_start.x = gw->sel_end.x = x;
2123         gw->sel_start.y = gw->sel_end.y = y;
2124     } else {
2125         /*
2126          * Check to see if this is Button3 and a selection exists.  If so,
2127          * then copy the selection to the clipboard and return.
2128          */
2129         if (event->button == 3 &&
2130             bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht)) {
2131             glyphedit_copy_selection(gw);
2132             gw->last_x = x;
2133             gw->last_y = y;
2134             return FALSE;
2135         }
2136
2137         /*
2138          * The operation is one of move or copy.  If the button is clicked
2139          * outside the selection, remove the selection and start over.
2140          */
2141         if (bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht) &&
2142             !bdf_in_selection(gw->grid, x, y, 0)) {
2143
2144             if (gw->op != GLYPHEDIT_SELECT)
2145               bdf_attach_selection(gw->grid);
2146
2147             for (ty = sy; ty < sy + ht; ty++) {
2148                 for (tx = sx; tx < sx + wd; tx++)
2149                   glyphedit_draw_pixel(gw, tx, ty, FALSE);
2150             }
2151             bdf_lose_selection(gw->grid);
2152
2153             gw->pending_op = gw->op;
2154             gw->op = GLYPHEDIT_SELECT;
2155
2156             /*
2157              * Select the pixel at the point and initialize the selection
2158              * rectangle.
2159              */
2160             glyphedit_draw_pixel(gw, x, y, TRUE);
2161
2162             gw->sel_start.x = gw->sel_end.x = x;
2163             gw->sel_start.y = gw->sel_end.y = y;
2164         }
2165     }
2166
2167     /*
2168      * Set the last coordinate to the point just handled.
2169      */
2170     gw->last_x = x;
2171     gw->last_y = y;
2172
2173     return FALSE;
2174 }
2175
2176 static gboolean
2177 glyphedit_button_release(GtkWidget *w, GdkEventButton *event)
2178 {
2179     Glyphedit *gw;
2180     gint16 sx, sy, ex, ey;
2181
2182     /*
2183      * Button releases on a widget without the focus is ignored.
2184      */
2185     if (!GTK_WIDGET_HAS_FOCUS(w))
2186       return FALSE;
2187
2188     gw = GLYPHEDIT(w);
2189
2190     sx = MIN(gw->sel_start.x, gw->sel_end.x);
2191     ex = MAX(gw->sel_start.x, gw->sel_end.x);
2192     sy = MIN(gw->sel_start.y, gw->sel_end.y);
2193     ey = MAX(gw->sel_start.y, gw->sel_end.y);
2194
2195     if (gw->op == GLYPHEDIT_SELECT) {
2196         if (sx == ex && sy == ey)
2197           glyphedit_draw_pixel(gw, gw->sel_start.x, gw->sel_start.y, FALSE);
2198         else {
2199             /*
2200              * Gain control of the GLYPHEDIT_CLIPBOARD atom.
2201              */
2202             glyphedit_own_clipboard(gw);
2203
2204             bdf_set_selection(gw->grid, sx, sy, (ex - sx) + 1, (ey - sy) + 1);
2205
2206             /*
2207              * Switch to a move/copy operations if necessary.
2208              */
2209             if (gw->pending_op != GLYPHEDIT_NONE) {
2210                 gw->op = gw->pending_op;
2211                 gw->pending_op = GLYPHEDIT_NONE;
2212                 /*
2213                  * If the pending operation is a move, then make sure the
2214                  * selection is detached.
2215                  */
2216                 if (gw->op == GLYPHEDIT_MOVE)
2217                   bdf_detach_selection(gw->grid);
2218             }
2219         }
2220     }
2221     return FALSE;
2222 }
2223
2224 static gboolean
2225 glyphedit_motion_notify(GtkWidget *w, GdkEventMotion *event)
2226 {
2227     Glyphedit *gw;
2228     gboolean changed;
2229     gint16 x, y, ix, iy;
2230     GlypheditSignalInfo si;
2231
2232     gw = GLYPHEDIT(w);
2233
2234     glyphedit_get_pointer_coord(gw, (gint16) event->x, (gint16) event->y,
2235                                 &x, &y);
2236
2237     /*
2238      * Return if the mouse is off the edges of the grid or the mouse is still
2239      * on the same point as the last one.
2240      */
2241     if (x < 0 || y < 0 || (x == gw->last_x && y == gw->last_y))
2242       return FALSE;
2243
2244     si.reason = GLYPHEDIT_POINTER_MOVED;
2245     si.x = x - gw->grid->base_x;
2246     si.y = -(y - gw->grid->base_y) - 1;
2247     si.color = bdf_grid_color_at(gw->grid, x, y);
2248     g_signal_emit(G_OBJECT(gw), glyphedit_signals[POINTER_MOVED],
2249                   0, &si);
2250
2251     ix = gw->last_x;
2252     iy = gw->last_y;
2253
2254     gw->last_x = x;
2255     gw->last_y = y;
2256
2257     /*
2258      * If the event is a simple motion event with no button being pressed,
2259      * then simply return at this point.
2260      */
2261     if (!GTK_WIDGET_HAS_FOCUS(w) ||
2262         !(event->state & (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK)))
2263       return FALSE;
2264
2265     changed = FALSE;
2266     if (gw->op == GLYPHEDIT_DRAW) {
2267         /*
2268          * Drawing.
2269          */
2270         if (event->state & GDK_BUTTON1_MASK) {
2271             if ((changed = bdf_grid_set_pixel(gw->grid, x, y, gw->cidx)))
2272               glyphedit_draw_pixel(gw, x, y, FALSE);
2273         } else if (event->state & GDK_BUTTON2_MASK) {
2274             if ((changed = bdf_grid_invert_pixel(gw->grid, x, y, gw->cidx)))
2275               glyphedit_draw_pixel(gw, x, y, FALSE);
2276         } else if (event->state & GDK_BUTTON3_MASK) {
2277             if ((changed = bdf_grid_clear_pixel(gw->grid, x, y)))
2278               glyphedit_draw_pixel(gw, x, y, FALSE);
2279         }
2280
2281         /*
2282          * If one of the pixels changed, then call the callback.
2283          */
2284         if (changed)
2285           glyphedit_signal_glyph_change(gw);
2286     } else if (gw->op == GLYPHEDIT_SELECT) {
2287         /*
2288          * Determine the other point on the intersection rectangle.
2289          */
2290         ix = gw->sel_start.x;
2291         iy = gw->sel_start.y;
2292
2293         if (x > ix)
2294           ix = MIN(gw->sel_end.x, x);
2295         else if (x < ix)
2296           ix = MAX(gw->sel_end.x, x);
2297
2298         if (y > iy)
2299           iy = MIN(gw->sel_end.y, y);
2300         else if (y < iy)
2301           iy = MAX(gw->sel_end.y, y);
2302
2303         /*
2304          * Clear the pixels outside the intersection of the old selection
2305          * rectangle and the new selection rectangle.
2306          */
2307         glyphedit_update_selection(gw, ix, iy, FALSE);
2308
2309         /*
2310          * Set the new endpoint of the selection rectangle.
2311          */
2312         gw->sel_end.x = x;
2313         gw->sel_end.y = y;
2314
2315         /*
2316          * Set all pixels outside the intersection of the old selection
2317          * rectangle and the new selection rectangle, but inside the new
2318          * selection rectangle.
2319          */
2320         glyphedit_update_selection(gw, ix, iy, TRUE);
2321     } else {
2322         /*
2323          * A move or copy is in progress.
2324          */
2325         if (bdf_has_selection(gw->grid, 0, 0, 0, 0) &&
2326             bdf_grid_shift(gw->grid, x - ix, y - iy)) {
2327             glyphedit_draw_glyph(gw);
2328             glyphedit_signal_glyph_change(gw);
2329         }
2330     }
2331
2332     return FALSE;
2333 }
2334
2335 static gboolean
2336 glyphedit_key_press(GtkWidget *w, GdkEventKey *event)
2337 {
2338     gboolean ret = FALSE;
2339
2340     switch (event->keyval) {
2341       case GDK_Left:
2342       case GDK_KP_Left:
2343         glyphedit_shift_glyph(GLYPHEDIT(w), -1, 0);
2344         break;
2345       case GDK_Right:
2346       case GDK_KP_Right:
2347         glyphedit_shift_glyph(GLYPHEDIT(w), 1, 0);
2348         break;
2349       case GDK_Up:
2350       case GDK_KP_Up:
2351         /*
2352          * For some reason, the Up arrow causes the focus to change to
2353          * other widgets. Returning TRUE insures that the up arrow works
2354          * as expected.
2355          */
2356         glyphedit_shift_glyph(GLYPHEDIT(w), 0, -1);
2357         ret = TRUE;
2358         break;
2359       case GDK_Down:
2360       case GDK_KP_Down:
2361         glyphedit_shift_glyph(GLYPHEDIT(w), 0, 1);
2362         break;
2363       case GDK_Delete:
2364       case GDK_BackSpace:
2365         glyphedit_cut_selection(GLYPHEDIT(w));
2366         break;
2367       case GDK_9:
2368       case GDK_KP_9:
2369         glyphedit_rotate_glyph(GLYPHEDIT(w), -90);
2370       case GDK_0:
2371       case GDK_KP_0:
2372         glyphedit_rotate_glyph(GLYPHEDIT(w), 90);
2373         break;
2374       case GDK_minus:
2375       case GDK_KP_Subtract:
2376         glyphedit_flip_glyph(GLYPHEDIT(w), GTK_ORIENTATION_HORIZONTAL);
2377         break;
2378       case GDK_equal:
2379       case GDK_KP_Equal:
2380         glyphedit_flip_glyph(GLYPHEDIT(w), GTK_ORIENTATION_VERTICAL);
2381         break;
2382       case GDK_comma:
2383       case GDK_Z:
2384       case GDK_z:
2385         /* Change to a lighter color. */
2386         glyphedit_set_color(GLYPHEDIT(w), GLYPHEDIT(w)->cidx - 1);
2387         break;
2388       case GDK_period:
2389       case GDK_X:
2390       case GDK_x:
2391         /* Change to a darker color. */
2392         glyphedit_set_color(GLYPHEDIT(w), GLYPHEDIT(w)->cidx + 1);
2393         break;
2394     }
2395
2396     return ret;
2397 }
2398
2399 static gboolean
2400 glyphedit_key_release(GtkWidget *w, GdkEventKey *event)
2401 {
2402     return FALSE;
2403 }
2404
2405 static void
2406 glyphedit_class_init(gpointer g_class, gpointer class_data)
2407 {
2408     GObjectClass *gocp = G_OBJECT_CLASS(g_class);
2409     GtkObjectClass *ocp = GTK_OBJECT_CLASS(g_class);
2410     GtkWidgetClass *wcp = GTK_WIDGET_CLASS(g_class);
2411
2412     /*
2413      * Set the class global variables.
2414      */
2415     parent_class = g_type_class_peek_parent(g_class);
2416
2417     ocp->destroy = glyphedit_destroy;
2418
2419     gocp->set_property = glyphedit_set_property;
2420     gocp->get_property = glyphedit_get_property;
2421     gocp->finalize = glyphedit_finalize;
2422
2423     /*
2424      * Add argument (a.k.a. resource) types.
2425      */
2426     g_object_class_install_property(gocp, GLYPH_GRID,
2427                                     g_param_spec_pointer("glyphGrid",
2428                                                        _("Glyph Grid"),
2429                                                        _("The glyph in a grid structure."),
2430                                                        G_PARAM_READWRITE));
2431
2432     g_object_class_install_property(gocp, PIXEL_SIZE,
2433                                     g_param_spec_uint("pixelSize",
2434                                                       _("Pixel Size"),
2435                                                       _("The number of pixels to use to draw one grid pixel."),
2436                                                       1,
2437                                                       20,
2438                                                       10,
2439                                                       G_PARAM_READWRITE));
2440
2441     g_object_class_install_property(gocp, SHOW_X_HEIGHT,
2442                                     g_param_spec_boolean("showXHeight",
2443                                                          _("Show X Height"),
2444                                                          _("Draw a line at the x height."),
2445                                                          FALSE,
2446                                                          G_PARAM_READWRITE));
2447
2448     g_object_class_install_property(gocp, SHOW_CAP_HEIGHT,
2449                                     g_param_spec_boolean("showCapHeight",
2450                                                          _("Show Cap Height"),
2451                                                          _("Draw a line at the cap height."),
2452                                                          FALSE,
2453                                                          G_PARAM_READWRITE));
2454
2455     g_object_class_install_property(gocp, OPERATION,
2456                                     g_param_spec_enum("operation",
2457                                                       _("Edit Operation"),
2458                                                       _("Glyph edit operation."),
2459                                                       glyphedit_get_operation_type(),
2460                                                       GLYPHEDIT_DRAW,
2461                                                       G_PARAM_READWRITE));
2462
2463     g_object_class_install_property(gocp, COLOR_LIST,
2464                                     g_param_spec_pointer("colorList",
2465                                                          _("Color list"),
2466                                                          _("Colors to be used for glyphs having bits-per-pixel > 1."),
2467                                                          G_PARAM_READWRITE));
2468
2469
2470     /*
2471      * Add the signals these objects emit.
2472      */
2473     glyphedit_signals[GLYPH_MODIFIED] =
2474         g_signal_new("glyph-modified",
2475                      G_TYPE_FROM_CLASS(gocp),
2476                      G_SIGNAL_RUN_FIRST,
2477                      G_STRUCT_OFFSET(GlypheditClass, glyph_modified),
2478                      NULL, NULL,
2479                      g_cclosure_marshal_VOID__POINTER,
2480                      G_TYPE_NONE, 1, G_TYPE_POINTER);
2481
2482     glyphedit_signals[POINTER_MOVED] =
2483         g_signal_new("pointer-moved",
2484                      G_TYPE_FROM_CLASS(gocp),
2485                      G_SIGNAL_RUN_FIRST,
2486                      G_STRUCT_OFFSET(GlypheditClass, pointer_moved),
2487                      NULL, NULL,
2488                      g_cclosure_marshal_VOID__POINTER,
2489                      G_TYPE_NONE, 1, G_TYPE_POINTER);
2490
2491     glyphedit_signals[OPERATION_CHANGE] =
2492         g_signal_new("operation-change",
2493                      G_TYPE_FROM_CLASS(gocp),
2494                      G_SIGNAL_RUN_FIRST,
2495                      G_STRUCT_OFFSET(GlypheditClass, operation_change),
2496                      NULL, NULL,
2497                      g_cclosure_marshal_VOID__POINTER,
2498                      G_TYPE_NONE, 1, G_TYPE_POINTER);
2499
2500     glyphedit_signals[COLOR_CHANGE] =
2501         g_signal_new("color-change",
2502                      G_TYPE_FROM_CLASS(gocp),
2503                      G_SIGNAL_RUN_FIRST,
2504                      G_STRUCT_OFFSET(GlypheditClass, color_change),
2505                      NULL, NULL,
2506                      g_cclosure_marshal_VOID__POINTER,
2507                      G_TYPE_NONE, 1, G_TYPE_POINTER);
2508
2509     /*
2510      * Set all the functions for handling events for objects of this class.
2511      */
2512     wcp->size_request = glyphedit_preferred_size;
2513     wcp->size_allocate = glyphedit_actual_size;
2514     wcp->realize = glyphedit_realize;
2515     wcp->expose_event = glyphedit_expose;
2516     wcp->focus_in_event = glyphedit_focus_in;
2517     wcp->focus_out_event = glyphedit_focus_out;
2518     wcp->button_press_event = glyphedit_button_press;
2519     wcp->button_release_event = glyphedit_button_release;
2520     wcp->motion_notify_event = glyphedit_motion_notify;
2521     wcp->key_press_event = glyphedit_key_press;
2522     wcp->key_release_event = glyphedit_key_release;
2523 }
2524
2525 GType
2526 glyphedit_get_type(void)
2527 {
2528     static GType glyphedit_type = 0;
2529
2530     if (!glyphedit_type) {
2531         static const GTypeInfo glyphedit_info = {
2532             sizeof(GlypheditClass),
2533             0,
2534             0,
2535             glyphedit_class_init,
2536             0,
2537             0,
2538             sizeof(Glyphedit),
2539             0,
2540             glyphedit_init,
2541             0,
2542         };
2543
2544         glyphedit_type = g_type_register_static(GTK_TYPE_WIDGET, "Glyphedit",
2545                                                 &glyphedit_info, 0);
2546     }
2547
2548     return glyphedit_type;
2549 }