]> git.kernelconcepts.de Git - gbdfed.git/blob - guigedit.c
Fixup several compile faults due to changes in recent distributions,
[gbdfed.git] / guigedit.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 "gbdfed.h"
24 #include "glyphedit.h"
25 #include "labcon.h"
26 #include "gectrl.h"
27
28 #define UPMSG "Glyph Edit: The glyph has been modified.\nDo you want to save?"
29
30 static const gchar *lb_xpm[] = {
31 /* width height num_colors chars_per_pixel */
32 "    32    32        3            1",
33 /* colors */
34 ". c None",
35 "# c #000000",
36 "r c #ff0000",
37 /* pixels */
38 "...........##...................",
39 "...........##...................",
40 "................................",
41 "................................",
42 "...........##...................",
43 "...........##...................",
44 "................................",
45 "..r....r........................",
46 ".rr....rr..##...................",
47 "rrrrrrrrrr.##...................",
48 ".rr....rr.......................",
49 "..r....r........................",
50 "...........##...................",
51 "...........##...................",
52 "................................",
53 "................................",
54 "...........##...................",
55 "...........##...................",
56 "................................",
57 "##..##..##.##.##..##..##..##..##",
58 "##..##..##.##.##..##..##..##..##",
59 "................................",
60 "...........##...................",
61 "...........##...................",
62 "................................",
63 "................................",
64 "...........##...................",
65 "...........##...................",
66 "................................",
67 "................................",
68 "...........##...................",
69 "...........##..................."
70 };
71
72 static const gchar *rb_xpm[] = {
73 /* width height num_colors chars_per_pixel */
74 "    32    32        3            1",
75 /* colors */
76 ". c None",
77 "# c #000000",
78 "r c #ff0000",
79 /* pixels */
80 "...........##...................",
81 "...........##...................",
82 "................................",
83 "................................",
84 "...........##...................",
85 "...........##...................",
86 "................................",
87 "................r............r..",
88 "...........##..rr............rr.",
89 "...........##.rrrrrrrrrrrrrrrrrr",
90 "...............rr............rr.",
91 "................r............r..",
92 "...........##...................",
93 "...........##...................",
94 "................................",
95 "................................",
96 "...........##...................",
97 "...........##...................",
98 "................................",
99 "##..##..##.##.##..##..##..##..##",
100 "##..##..##.##.##..##..##..##..##",
101 "................................",
102 "...........##...................",
103 "...........##...................",
104 "................................",
105 "................................",
106 "...........##...................",
107 "...........##...................",
108 "................................",
109 "................................",
110 "...........##...................",
111 "...........##..................."
112 };
113
114 static const gchar *as_xpm[] = {
115 /* width height num_colors chars_per_pixel */
116 "    32    32        3            1",
117 /* colors */
118 ". c None",
119 "# c #000000",
120 "r c #ff0000",
121 /* pixels */
122 "...........##.........r.........",
123 "...........##........rrr........",
124 "....................rrrrr.......",
125 "......................r.........",
126 "...........##.........r.........",
127 "...........##.........r.........",
128 "......................r.........",
129 "......................r.........",
130 "...........##.........r.........",
131 "...........##.........r.........",
132 "......................r.........",
133 "......................r.........",
134 "...........##.........r.........",
135 "...........##.........r.........",
136 "......................r.........",
137 "....................rrrrr.......",
138 "...........##........rrr........",
139 "...........##.........r.........",
140 "................................",
141 "##..##..##.##.##..##..##..##..##",
142 "##..##..##.##.##..##..##..##..##",
143 "................................",
144 "...........##...................",
145 "...........##...................",
146 "................................",
147 "................................",
148 "...........##...................",
149 "...........##...................",
150 "................................",
151 "................................",
152 "...........##...................",
153 "...........##..................."
154 };
155
156 static const char *ds_xpm[] = {
157 /* width height num_colors chars_per_pixel */
158 "    32    32        3            1",
159 /* colors */
160 ". c None",
161 "# c #000000",
162 "r c #ff0000",
163 /* pixels */
164 "...........##...................",
165 "...........##...................",
166 "................................",
167 "................................",
168 "...........##...................",
169 "...........##...................",
170 "................................",
171 "................................",
172 "...........##...................",
173 "...........##...................",
174 "................................",
175 "................................",
176 "...........##...................",
177 "...........##...................",
178 "................................",
179 "................................",
180 "...........##...................",
181 "...........##...................",
182 "................................",
183 "##..##..##.##.##..##..##..##..##",
184 "##..##..##.##.##..##..##..##..##",
185 "................................",
186 "...........##.........r.........",
187 "...........##........rrr........",
188 "....................rrrrr.......",
189 "......................r.........",
190 "...........##.........r.........",
191 "...........##.........r.........",
192 "......................r.........",
193 "....................rrrrr.......",
194 "...........##........rrr........",
195 "...........##.........r........."
196 };
197
198 /*
199  * Global pixbufs.
200  */
201 static GdkPixbuf *lb_image = 0;
202 static GdkPixbuf *rb_image = 0;
203 static GdkPixbuf *as_image = 0;
204 static GdkPixbuf *ds_image = 0;
205
206 typedef struct {
207     GtkWidget *dialog;
208     GtkWidget *notebook;
209     GtkWidget *apply;
210
211     /*
212      * The rotate/shear tab.
213      */
214     GtkWidget *rotate;
215     GtkWidget *shear;
216     GtkAdjustment *rotate_adj;
217     GtkAdjustment *shear_adj;
218     GtkWidget *degrees;
219     gboolean degrees_modified;
220
221     /*
222      * The bounding box resize tab.
223      */
224     GtkWidget *lbearing;
225     GtkWidget *rbearing;
226     GtkWidget *ascent;
227     GtkWidget *descent;
228     gboolean resize_modified;
229
230     /*
231      * The PSF mappings tab.
232      */
233     GtkWidget *psf_add;
234     GtkWidget *psf_delete;
235     GtkWidget *psf_mappings;
236     GtkWidget *psf_input;
237     gboolean psf_modified;
238 } GlypheditNotebookRec;
239
240 typedef struct {
241     gulong id;
242     gulong owner;
243     gulong handler;
244
245     GtkAccelGroup *ag;
246
247     GtkWidget *shell;
248     GtkWidget *gedit;
249     GtkWidget *gectrl;
250     GtkWidget *name;
251     GtkWidget *encoding;
252     GtkWidget *dwidth;
253     GtkWidget *metrics;
254     GtkWidget *coords;
255     GtkWidget *gectltips;
256
257     GtkWidget *file_menu;
258     GtkWidget *update;
259     GtkWidget *update_prev;
260     GtkWidget *update_next;
261
262     GtkWidget *button_update;
263     GtkWidget *button_prev;
264     GtkWidget *button_next;
265
266     GtkWidget *edit_menu;
267     GtkWidget *reload;
268     GtkWidget *resize;
269     GtkWidget *paste;
270     GtkWidget *copy;
271     GtkWidget *cut;
272     GtkWidget *select_all;
273     GtkWidget *next;
274     GtkWidget *prev;
275     GtkWidget *unimap;
276     GtkWidget *unimap_page;
277
278     GtkWidget *ops_menu;
279     GlypheditNotebookRec ops;
280 } GlypheditRec;
281
282 static GlypheditRec *glyph_editors;
283 static gulong num_glyph_editors;
284
285 static GlypheditRec *
286 _guigedit_get_glyph_editor(gulong owner)
287 {
288     gulong i;
289     GlypheditRec *ge;
290
291     if (num_glyph_editors == 0) {
292         glyph_editors = ge =
293             (GlypheditRec *) g_malloc0(sizeof(GlypheditRec));
294         ge->id = num_glyph_editors++;
295     } else {
296         for (i = 0; i < num_glyph_editors; i++) {
297             if (glyph_editors[i].owner == ~0) {
298                 ge = &glyph_editors[i];
299                 ge->owner = owner;
300                 return ge;
301             }
302         }
303         glyph_editors = (GlypheditRec *)
304             g_realloc(glyph_editors,
305                       sizeof(GlypheditRec) * (num_glyph_editors + 1));
306
307         ge = glyph_editors + num_glyph_editors;
308         (void) memset((char *) ge, 0, sizeof(GlypheditRec));
309         ge->id = num_glyph_editors++;
310     }
311     ge->owner = owner;
312     return ge;
313 }
314
315 /**************************************************************************
316  *
317  * Menu construction.
318  *
319  **************************************************************************/
320
321 static GtkWidget *
322 make_accel_menu_item(GtkWidget *menu, const gchar *text, const gchar *accel,
323                      GtkAccelGroup *ag)
324 {
325     GtkWidget *mi;
326     guint key;
327     GdkModifierType mods;
328
329     mi = gtk_menu_item_new_with_mnemonic(text);
330
331     gtk_accelerator_parse(accel, &key, &mods);
332     gtk_widget_add_accelerator(mi, "activate", ag, key, mods,
333                                GTK_ACCEL_VISIBLE|GTK_ACCEL_LOCKED);
334     gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
335
336     return mi;
337 }
338
339 static void
340 update_font(GtkWidget *w, gpointer data)
341 {
342     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
343     gbdfed_editor_t *ed = editors + ge->owner;
344     const gchar *s;
345     gchar *prgname = g_get_prgname();
346     gboolean unencoded;
347     bdf_glyph_t *glyph;
348     GlypheditOperation op;
349
350     if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) {
351         if (glyphedit_get_selecting(GLYPHEDIT(ge->gedit)) == TRUE) {
352           /*
353            * A selection operation is in progress. Need to switch back to
354            * the Draw operation to finalize the selection and then switch
355            * back.
356            */
357             op = glyphedit_get_operation(GLYPHEDIT(ge->gedit));
358             glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_DRAW);
359             glyphedit_change_operation(GLYPHEDIT(ge->gedit), op);
360         }
361
362         glyph = glyphedit_get_glyph(GLYPHEDIT(ge->gedit), &unencoded);
363
364         /*
365          * Set the new name and device width for the glyph. These may not
366          * have actually changed, but this is simplest for the moment.
367          */
368         if (glyph->name != 0)
369           free(glyph->name);
370         glyph->name = (gchar *) gtk_entry_get_text(GTK_ENTRY(ge->name));
371         s = gtk_entry_get_text(GTK_ENTRY(ge->dwidth));
372         glyph->dwidth = (guint16) _bdf_atos((char *) s, 0, 10);
373
374         /*
375          * Now update the font itself.
376          */
377         fontgrid_update_glyph(FONTGRID(ed->fgrid), glyph, unencoded);
378
379         /*
380          * Free the glyph structure. The name has already been deallocated
381          * and replaced with a possibly new name.
382          */
383         if (glyph->bytes > 0)
384           free(glyph->bitmap);
385         free(glyph);
386
387         glyphedit_set_modified(GLYPHEDIT(ge->gedit), FALSE);
388     }
389
390     /*
391      * Just modified the PSF mappings.
392      */
393     fontgrid_update_psf_mappings(FONTGRID(ed->fgrid),
394                                  glyphedit_get_encoding(GLYPHEDIT(ge->gedit)),
395                                  glyphedit_get_psf_mappings(GLYPHEDIT(ge->gedit)));
396
397     /*
398      * Unset the modified flag and update the title.
399      */
400     glyphedit_set_modified(GLYPHEDIT(ge->gedit), FALSE);
401     if (ed->file == 0)
402       sprintf(buffer1, "%s - Glyph Edit: (unnamed%d)",
403               prgname, ed->id);
404     else
405       sprintf(buffer1, "%s - Glyph Edit: %s", prgname, ed->file);
406
407     gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
408
409
410     gtk_widget_set_sensitive(ge->update, FALSE);
411     gtk_widget_set_sensitive(ge->update_next, FALSE);
412     gtk_widget_set_sensitive(ge->update_prev, FALSE);
413     gtk_widget_set_sensitive(ge->button_update, FALSE);
414
415     /*
416      * Force the focus to be on the glyph grid
417      */
418     gtk_widget_grab_focus(ge->gedit);
419 }
420
421 /*
422  * Code common to both next_glyph() and previous_glyph().
423  */
424 static void
425 update_glyphedit(gbdfed_editor_t *ed, GlypheditRec *ge, bdf_glyph_grid_t *grid)
426 {
427     Glyphedit *gw;
428
429     gw = GLYPHEDIT(ge->gedit);
430     gtk_entry_set_text(GTK_ENTRY(ge->name), grid->name);
431
432     if (grid->unencoded)
433       sprintf(buffer1, "-1");
434     else {
435         switch (fontgrid_get_code_base(FONTGRID(ed->fgrid))) {
436           case 8: sprintf(buffer1, "%o", grid->encoding); break;
437           case 10: sprintf(buffer1, "%d", grid->encoding); break;
438           case 16: sprintf(buffer1, "%04X", grid->encoding); break;
439         }
440     }
441     gtk_label_set_text(GTK_LABEL(ge->encoding), buffer1);
442
443     sprintf(buffer1, "%hd", grid->dwidth);
444     gtk_widget_set_sensitive(ge->dwidth, TRUE);
445     g_signal_handler_block(G_OBJECT(ge->dwidth), ge->handler);
446     gtk_entry_set_text(GTK_ENTRY(ge->dwidth), buffer1);
447     g_signal_handler_unblock(G_OBJECT(ge->dwidth), ge->handler);
448
449     if (grid->spacing != BDF_PROPORTIONAL) {
450         gtk_widget_set_sensitive(ge->dwidth, FALSE);
451         if (ge->unimap_page != 0)
452           gtk_widget_set_sensitive(ge->unimap_page, TRUE);
453     } else if (ge->unimap_page != 0)
454       gtk_widget_set_sensitive(ge->unimap_page, FALSE);
455
456     sprintf(buffer1, "width %hd height %hd\r\nascent %hd descent %hd",
457             grid->glyph_bbx.width, grid->glyph_bbx.height,
458             grid->glyph_bbx.ascent, grid->glyph_bbx.descent);
459     gtk_label_set_text(GTK_LABEL(ge->metrics), buffer1);
460
461     /*
462      * Set the new grid in the glyph editor.
463      */
464     glyphedit_set_grid(gw, grid);
465
466     /*
467      * Set the sensitivity of the update menu items appropriately.
468      */
469     if (grid->modified) {
470         gtk_widget_set_sensitive(ge->update, TRUE);
471         gtk_widget_set_sensitive(ge->update_next, TRUE);
472         gtk_widget_set_sensitive(ge->update_prev, TRUE);
473         gtk_widget_set_sensitive(ge->button_update, TRUE);
474     } else {
475         gtk_widget_set_sensitive(ge->update, FALSE);
476         gtk_widget_set_sensitive(ge->update_next, FALSE);
477         gtk_widget_set_sensitive(ge->update_prev, FALSE);
478         gtk_widget_set_sensitive(ge->button_update, FALSE);
479     }
480
481     if (glyphedit_get_encoding(gw) == 0)
482       gtk_widget_set_sensitive(ge->button_prev, FALSE);
483     else
484       gtk_widget_set_sensitive(ge->button_prev, TRUE);
485
486     if (glyphedit_get_encoding(gw) == 0xffff)
487       gtk_widget_set_sensitive(ge->button_next, FALSE);
488     else
489       gtk_widget_set_sensitive(ge->button_next, TRUE);
490
491     /*
492      * Force the focus to be on the glyph grid.
493      */
494     gtk_widget_grab_focus(ge->gedit);
495 }
496
497 static void
498 next_glyph(GtkWidget *w, gpointer data)
499 {
500     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
501     gbdfed_editor_t *ed = editors + ge->owner;
502     bdf_font_t *font;
503     bdf_glyph_grid_t *grid;
504     bdf_bitmap_t image;
505
506     if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) {
507         if (guiutil_yes_or_no(ge->shell, UPMSG, TRUE))
508           update_font(w, GUINT_TO_POINTER(ge->id));
509     }
510
511     grid = glyphedit_get_grid(GLYPHEDIT(ge->gedit));
512
513     if (fontgrid_select_next_glyph(FONTGRID(ed->fgrid), grid->encoding) == FALSE)
514       return;
515
516     font = fontgrid_get_font(FONTGRID(ed->fgrid));
517
518     if (grid->unencoded)
519       grid = bdf_make_glyph_grid(font, grid->encoding + 1, 1);
520     else
521       grid = bdf_make_glyph_grid(font, grid->encoding + 1, 0);
522
523     update_glyphedit(ed, ge, grid);
524
525     glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
526     gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
527     if (image.bytes > 0)
528       free(image.bitmap);
529 }
530
531 static void
532 previous_glyph(GtkWidget *w, gpointer data)
533 {
534     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
535     gbdfed_editor_t *ed = editors + ge->owner;
536     bdf_font_t *font;
537     bdf_glyph_grid_t *grid;
538     bdf_bitmap_t image;
539
540     if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) {
541         if (guiutil_yes_or_no(ge->shell, UPMSG, TRUE))
542           update_font(w, GUINT_TO_POINTER(ge->id));
543     }
544
545     grid = glyphedit_get_grid(GLYPHEDIT(ge->gedit));
546
547     if (fontgrid_select_previous_glyph(FONTGRID(ed->fgrid), grid->encoding) == FALSE)
548       return;
549
550     font = fontgrid_get_font(FONTGRID(ed->fgrid));
551
552     if (grid->unencoded)
553       grid = bdf_make_glyph_grid(font, grid->encoding - 1, 1);
554     else
555       grid = bdf_make_glyph_grid(font, grid->encoding - 1, 0);
556
557     update_glyphedit(ed, ge, grid);
558
559     glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
560     gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
561     if (image.bytes > 0)
562       free(image.bitmap);
563 }
564
565 static void
566 update_and_next_glyph(GtkWidget *w, gpointer data)
567 {
568     update_font(w, data);
569     next_glyph(w, data);
570 }
571
572 static void
573 update_and_previous_glyph(GtkWidget *w, gpointer data)
574 {
575     update_font(w, data);
576     previous_glyph(w, data);
577 }
578
579 static gboolean
580 close_glyph_editor(GtkWidget *w, GdkEvent *ev, gpointer data)
581 {
582     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
583
584     /*
585      * Glyph editors with no owners are ignored. This lets us call this
586      * routine at application shutdown time to update all modified
587      * glyph editors.
588      */
589     if (ge->owner == ~0)
590       return TRUE;
591
592     /*
593      * We don't check to see if the grid has been modified, because
594      * certain operations cause the modify flag to be set, but they
595      * don't really represent a modification.
596      */
597     if (GTK_WIDGET_IS_SENSITIVE(ge->update) == TRUE) {
598         if (guiutil_yes_or_no(ge->shell, UPMSG, TRUE))
599           update_font(w, GUINT_TO_POINTER(ge->id));
600     }
601
602     /*
603      * Release this editor back into the pool to be reused.
604      */
605     ge->owner = ~0;
606
607     /*
608      * Hide the shell.
609      */
610     gtk_widget_hide(ge->shell);
611
612     return TRUE;
613 }
614
615 static void
616 activate_close_glyph_editor(GtkWidget *w, gpointer data)
617 {
618     (void) close_glyph_editor(w, 0, data);
619 }
620
621 static GtkWidget *
622 make_file_menu(GlypheditRec *ge, GtkWidget *menubar)
623 {
624     GtkWidget *file, *menu, *mitem, *sep;
625
626     /*
627      * Create the File menu.
628      */
629     file = gtk_menu_item_new_with_mnemonic("_File");
630
631     ge->file_menu = menu = gtk_menu_new();
632
633     ge->update = make_accel_menu_item(menu, "_Update",
634                                       "<Control>S", ge->ag);
635     g_signal_connect(G_OBJECT(ge->update), "activate",
636                      G_CALLBACK(update_font), GUINT_TO_POINTER(ge->id));
637
638     ge->update_next = make_accel_menu_item(menu, "Update and _Next",
639                                            "<Control>U", ge->ag);
640     g_signal_connect(G_OBJECT(ge->update_next), "activate",
641                      G_CALLBACK(update_and_next_glyph),
642                      GUINT_TO_POINTER(ge->id));
643
644     ge->update_prev = make_accel_menu_item(menu, "Update and _Previous",
645                                            "<Control>B", ge->ag);
646     g_signal_connect(G_OBJECT(ge->update_prev), "activate",
647                      G_CALLBACK(update_and_previous_glyph),
648                      GUINT_TO_POINTER(ge->id));
649
650     /*
651      * Separator.
652      */
653     sep = gtk_menu_item_new();
654     gtk_widget_set_sensitive(sep, FALSE);
655     gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
656
657     mitem = make_accel_menu_item(menu, "_Close", "<Control>F4", ge->ag);
658     (void) g_signal_connect(G_OBJECT(mitem), "activate",
659                             G_CALLBACK(activate_close_glyph_editor),
660                             GUINT_TO_POINTER(ge->id));
661
662     gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), menu);
663
664     return file;
665 }
666
667 static gboolean
668 edit_menu_up(GtkWidget *w, GdkEvent *event, gpointer data)
669 {
670     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
671     Glyphedit *gw;
672
673     gw = GLYPHEDIT(ge->gedit);
674
675     if (glyphedit_clipboard_empty(gw))
676       gtk_widget_set_sensitive(ge->paste, FALSE);
677     else
678       gtk_widget_set_sensitive(ge->paste, TRUE);
679
680     gtk_widget_set_sensitive(ge->copy,
681                              glyphedit_get_selecting(gw));
682     gtk_widget_set_sensitive(ge->cut,
683                              glyphedit_get_selecting(gw));
684
685     if (glyphedit_get_encoding(gw) == 0)
686       gtk_widget_set_sensitive(ge->prev, FALSE);
687     else
688       gtk_widget_set_sensitive(ge->prev, TRUE);
689
690     if (glyphedit_get_encoding(gw) == 0xffff)
691       gtk_widget_set_sensitive(ge->next, FALSE);
692     else
693       gtk_widget_set_sensitive(ge->next, TRUE);
694
695     gtk_widget_set_sensitive(ge->reload, glyphedit_get_modified(gw));
696
697     if (glyphedit_get_spacing(gw) != BDF_PROPORTIONAL)
698       gtk_widget_set_sensitive(ge->unimap, TRUE);
699     else
700       gtk_widget_set_sensitive(ge->unimap, FALSE);
701
702     return FALSE;
703 }
704
705 static gboolean
706 edit_menu_down(GtkWidget *w, GdkEvent *event, gpointer data)
707 {
708     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
709
710     gtk_widget_set_sensitive(ge->paste, TRUE);
711     gtk_widget_set_sensitive(ge->copy, TRUE);
712     gtk_widget_set_sensitive(ge->cut, TRUE);
713     gtk_widget_set_sensitive(ge->prev, TRUE);
714     gtk_widget_set_sensitive(ge->next, TRUE);
715     gtk_widget_set_sensitive(ge->reload, TRUE);
716     gtk_widget_set_sensitive(ge->unimap, TRUE);
717
718     return FALSE;
719 }
720
721 static void
722 reload_glyph(GtkWidget *w, gpointer data)
723 {
724     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
725     gbdfed_editor_t *ed = editors + ge->owner;
726     bdf_font_t *font;
727     bdf_glyph_grid_t *grid;
728     bdf_bitmap_t image;
729
730     font = fontgrid_get_font(FONTGRID(ed->fgrid));
731     grid = glyphedit_get_grid(GLYPHEDIT(ge->gedit));
732
733     grid = bdf_make_glyph_grid(font, grid->encoding, grid->unencoded);
734
735     update_glyphedit(ed, ge, grid);
736
737     glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
738     gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
739     if (image.bytes > 0)
740       free(image.bitmap);
741 }
742
743 static void
744 copy_selection(GtkWidget *w, gpointer data)
745 {
746     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
747
748     glyphedit_copy_selection(GLYPHEDIT(ge->gedit));
749 }
750
751 static void
752 cut_selection(GtkWidget *w, gpointer data)
753 {
754     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
755     bdf_bitmap_t image;
756
757     glyphedit_cut_selection(GLYPHEDIT(ge->gedit));
758     glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
759     gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
760     if (image.bytes > 0)
761       free(image.bitmap);
762 }
763
764 static void
765 paste_selection(GtkWidget *w, gpointer data)
766 {
767     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
768     bdf_bitmap_t image;
769
770     glyphedit_paste_selection(GLYPHEDIT(ge->gedit));
771     glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
772     gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
773     if (image.bytes > 0)
774       free(image.bitmap);
775 }
776
777 static void
778 select_all(GtkWidget *w, gpointer data)
779 {
780     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
781
782     glyphedit_select_all(GLYPHEDIT(ge->gedit));
783 }
784
785 static GtkWidget *
786 make_edit_menu(GlypheditRec *ge, GtkWidget *menubar)
787 {
788     GtkWidget *edit, *menu, *sep;
789
790     /*
791      * Create the Edit menu.
792      */
793     edit = gtk_menu_item_new_with_mnemonic("_Edit");
794
795     ge->edit_menu = menu = gtk_menu_new();
796     g_signal_connect(G_OBJECT(menu), "map_event", G_CALLBACK(edit_menu_up),
797                      GUINT_TO_POINTER(ge->id));
798     g_signal_connect(G_OBJECT(menu), "unmap_event", G_CALLBACK(edit_menu_down),
799                      GUINT_TO_POINTER(ge->id));
800
801     ge->reload = make_accel_menu_item(menu, "Re_load",
802                                       "<Control>L", ge->ag);
803     g_signal_connect(G_OBJECT(ge->reload), "activate",
804                      G_CALLBACK(reload_glyph), GUINT_TO_POINTER(ge->id));
805
806     /*
807      * Separator.
808      */
809     sep = gtk_menu_item_new();
810     gtk_widget_set_sensitive(sep, FALSE);
811     gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
812
813     ge->copy = make_accel_menu_item(menu, "_Copy",
814                                     "<Control>C", ge->ag);
815     g_signal_connect(G_OBJECT(ge->copy), "activate",
816                      G_CALLBACK(copy_selection),
817                      GUINT_TO_POINTER(ge->id));
818
819     ge->cut = make_accel_menu_item(menu, "C_ut",
820                                    "<Control>X", ge->ag);
821     g_signal_connect(G_OBJECT(ge->cut), "activate",
822                      G_CALLBACK(cut_selection),
823                      GUINT_TO_POINTER(ge->id));
824
825     ge->paste = make_accel_menu_item(menu, "_Paste",
826                                      "<Control>V", ge->ag);
827     g_signal_connect(G_OBJECT(ge->paste), "activate",
828                      G_CALLBACK(paste_selection),
829                      GUINT_TO_POINTER(ge->id));
830
831     /*
832      * Separator.
833      */
834     sep = gtk_menu_item_new();
835     gtk_widget_set_sensitive(sep, FALSE);
836     gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
837
838     ge->select_all = make_accel_menu_item(menu, "Select _All",
839                                           "<Control>A", ge->ag);
840     g_signal_connect(G_OBJECT(ge->select_all), "activate",
841                      G_CALLBACK(select_all),
842                      GUINT_TO_POINTER(ge->id));
843
844     /*
845      * Separator.
846      */
847     sep = gtk_menu_item_new();
848     gtk_widget_set_sensitive(sep, FALSE);
849     gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
850
851     ge->next = make_accel_menu_item(menu, "_Next Glyph",
852                                     "<Control>N", ge->ag);
853     g_signal_connect(G_OBJECT(ge->next), "activate",
854                      G_CALLBACK(next_glyph), GUINT_TO_POINTER(ge->id));
855
856     ge->prev = make_accel_menu_item(menu, "Pre_vious Glyph",
857                                     "<Control>P", ge->ag);
858     g_signal_connect(G_OBJECT(ge->prev), "activate",
859                      G_CALLBACK(previous_glyph), GUINT_TO_POINTER(ge->id));
860
861     gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit), menu);
862
863     return edit;
864 }
865
866 static gboolean
867 operations_menu_up(GtkWidget *w, GdkEvent *event, gpointer data)
868 {
869     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
870     Glyphedit *gw;
871
872     gw = GLYPHEDIT(ge->gedit);
873
874     if (glyphedit_get_spacing(gw) != BDF_PROPORTIONAL)
875       gtk_widget_set_sensitive(ge->unimap, TRUE);
876     else
877       gtk_widget_set_sensitive(ge->unimap, FALSE);
878
879     return FALSE;
880 }
881
882 static gboolean
883 operations_menu_down(GtkWidget *w, GdkEvent *event, gpointer data)
884 {
885     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
886     Glyphedit *gw;
887
888     gw = GLYPHEDIT(ge->gedit);
889     gtk_widget_set_sensitive(ge->unimap, TRUE);
890
891     return FALSE;
892 }
893
894 static void
895 draw_operation(GtkWidget *w, gpointer data)
896 {
897     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
898     glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_DRAW);
899     gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_DRAW);
900 }
901
902 static void
903 move_operation(GtkWidget *w, gpointer data)
904 {
905     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
906     glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_MOVE);
907     gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_MOVE);
908 }
909
910 static void
911 copy_operation(GtkWidget *w, gpointer data)
912 {
913     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
914     glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_COPY);
915     gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_COPY);
916 }
917
918 static void
919 set_rotate_limits(GtkWidget *w, gpointer data)
920 {
921     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
922
923     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ge->ops.rotate)))
924       gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(ge->ops.degrees),
925                                      ge->ops.rotate_adj);
926 }
927
928 static void
929 set_shear_limits(GtkWidget *w, gpointer data)
930 {
931     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
932     const gchar *s;
933     gint16 v = -1000;
934
935     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ge->ops.shear))) {
936         /*
937          * This little tap dance is to avoid a minor, but ugly GUI
938          * situation where the value in the spin button may be obscured
939          * when the adjustment is changed back. The shear value can have
940          * at most 2 digits where the rotate value can have 3. Changing
941          * back to the shear adjustment can cause a resize of the spin
942          * button, sometimes obscuring the value left over from the rotate
943          * adjustment.
944          */
945         s = gtk_entry_get_text(GTK_ENTRY(ge->ops.degrees));
946         v = (gint16) _bdf_atos((char *) s, 0, 10);
947         if (v < -20)
948           v = -20;
949         else if (v > 20)
950           v = 20;
951         if (v != -1000)
952           gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.degrees),
953                                     (gdouble) v);
954         gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(ge->ops.degrees),
955                                        ge->ops.shear_adj);
956     }
957 }
958
959 /*
960  * Called when the value for rotating or shearing a glyph has changed.
961  */
962 static void
963 degrees_changed(GtkWidget *w, gpointer data)
964 {
965     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
966
967     gtk_widget_set_sensitive(ge->ops.apply, TRUE);
968     ge->ops.degrees_modified = TRUE;
969 }
970
971 /*
972  * Called when any of the fields in the resize tab are changed.
973  */
974 static void
975 resize_changed(GtkWidget *w, gpointer data)
976 {
977     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
978
979     gtk_widget_set_sensitive(ge->ops.apply, TRUE);
980     ge->ops.resize_modified = TRUE;
981 }
982
983 static gboolean
984 count_list_items(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *i,
985                  gpointer data)
986 {
987     gint *n = (gint *) data;
988     *n = *n + 1;
989
990     return FALSE;
991 }
992
993 static gboolean
994 collect_list_items(GtkTreeModel *m, GtkTreePath *p, GtkTreeIter *i,
995                    gpointer data)
996 {
997     gchar **mappings = (gchar **) data;
998
999     gtk_tree_model_get(m, i, 0,
1000                        &mappings[gtk_tree_path_get_indices(p)[0]], -1);
1001     return FALSE;
1002 }
1003
1004 /*
1005  * Called when the Apply button is pressed.
1006  */
1007 static void
1008 apply_operations(GtkWidget *w, gint response, gpointer data)
1009 {
1010     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1011     const gchar *s;
1012     gint16 lb, rb, as, ds, degrees;
1013     gint i, n;
1014     GtkTreeModel *model;
1015     gchar **mappings;
1016     bdf_psf_unimap_t *mp;
1017     bdf_metrics_t metrics;
1018
1019     if (ge->ops.degrees_modified) {
1020         /*
1021          * The degrees of rotatation or shearing have been modified.
1022          */
1023         s = gtk_entry_get_text(GTK_ENTRY(ge->ops.degrees));
1024         degrees = (gint16) _bdf_atos((char *) s, 0, 10);
1025         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ge->ops.rotate)))
1026           glyphedit_rotate_glyph(GLYPHEDIT(ge->gedit), degrees);
1027         else
1028           glyphedit_shear_glyph(GLYPHEDIT(ge->gedit), degrees);
1029         ge->ops.degrees_modified = FALSE;
1030     }
1031
1032     if (ge->ops.resize_modified) {
1033         /*
1034          * The bounding box has been modified.
1035          */
1036         s = gtk_entry_get_text(GTK_ENTRY(ge->ops.lbearing));
1037         lb = (gint16) _bdf_atos((char *) s, 0, 10);
1038         s = gtk_entry_get_text(GTK_ENTRY(ge->ops.rbearing));
1039         rb = (gint16) _bdf_atos((char *) s, 0, 10);
1040         s = gtk_entry_get_text(GTK_ENTRY(ge->ops.ascent));
1041         as = (gint16) _bdf_atos((char *) s, 0, 10);
1042         s = gtk_entry_get_text(GTK_ENTRY(ge->ops.descent));
1043         ds = (gint16) _bdf_atos((char *) s, 0, 10);
1044
1045         metrics.width = rb - lb;
1046         metrics.x_offset = lb;
1047         metrics.ascent = as;
1048         metrics.descent = ds;
1049         metrics.height = as + ds;
1050         metrics.y_offset = -ds;
1051
1052         glyphedit_set_metrics(GLYPHEDIT(ge->gedit), &metrics);
1053         ge->ops.degrees_modified = FALSE;
1054     }
1055
1056     if (ge->ops.psf_modified) {
1057         model = gtk_tree_view_get_model(GTK_TREE_VIEW(ge->ops.psf_mappings));
1058         n = 0;
1059         gtk_tree_model_foreach(model, count_list_items, (gpointer) &n);
1060         mappings = (gchar **) g_malloc(sizeof(gchar *) * n);
1061         gtk_tree_model_foreach(model, collect_list_items, (gpointer) mappings);
1062         mp = glyphedit_get_psf_mappings(GLYPHEDIT(ge->gedit));
1063         _bdf_psf_pack_mapping(mappings, n,
1064                               glyphedit_get_encoding(GLYPHEDIT(ge->gedit)),
1065                               mp);
1066         for (i = 0; i < n; i++)
1067           g_free(mappings[i]);
1068         if (n > 0)
1069           g_free(mappings);
1070         glyphedit_set_modified(GLYPHEDIT(ge->gedit), TRUE);
1071         glyphedit_signal_modified(GLYPHEDIT(ge->gedit));
1072         ge->ops.psf_modified = FALSE;
1073     }
1074
1075     /*
1076      * Only disable the Apply button if everything has been updated.
1077      */
1078     if (ge->ops.degrees_modified == FALSE &&
1079         ge->ops.resize_modified == FALSE &&
1080         ge->ops.psf_modified == FALSE)
1081       gtk_widget_set_sensitive(ge->ops.apply, FALSE);
1082
1083     gtk_widget_hide(ge->ops.dialog);
1084 }
1085
1086 static void
1087 change_unimap(GtkTreeModel *m, const gchar *path,
1088               const gchar *ntext, gpointer data)
1089 {
1090     gchar *ot;
1091     GtkTreePath *p = gtk_tree_path_new_from_string(path);
1092     GtkTreeIter iter;
1093
1094     gtk_tree_model_get_iter(m, &iter, p);
1095     gtk_tree_model_get(m, &iter, 0, &ot, -1);
1096     g_free(ot);
1097
1098     gtk_list_store_set(GTK_LIST_STORE(m), &iter, 0, ntext, -1);
1099 }
1100
1101 static void
1102 add_mapping(GtkWidget *w, gpointer data)
1103 {
1104     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1105     const gchar *v;
1106     gchar *i;
1107     gulong n;
1108     GtkTreeModel *model;
1109     GtkTreePath *path;
1110     GtkTreeIter iter;
1111
1112     v = gtk_entry_get_text(GTK_ENTRY(ge->ops.psf_input));
1113
1114     /*
1115      * Insure that the value is in the form expected.
1116      */
1117     n = (gulong) _bdf_atol((char *) v, 0, 16);
1118     if (n <= 0xffff)
1119       sprintf(buffer1, "U+%04lX", n);
1120     else
1121       sprintf(buffer1, "U+%06lX", n);
1122     v = (const gchar *) buffer1;
1123
1124     model = gtk_tree_view_get_model(GTK_TREE_VIEW(ge->ops.psf_mappings));
1125     gtk_list_store_append(GTK_LIST_STORE(model), &iter);
1126     gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, v, -1);
1127
1128     i = gtk_tree_model_get_string_from_iter(model, &iter);
1129     path = gtk_tree_path_new_from_string(i);
1130     g_free(i);
1131     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(ge->ops.psf_mappings),
1132                                  path, 0, TRUE, 0.5, 0.0);
1133     gtk_tree_path_free(path);
1134
1135     ge->ops.psf_modified = TRUE;
1136
1137     gtk_entry_set_text(GTK_ENTRY(ge->ops.psf_input), "");
1138     gtk_widget_set_sensitive(ge->ops.psf_add, FALSE);
1139     gtk_widget_set_sensitive(ge->ops.apply, TRUE);
1140 }
1141
1142 static void
1143 enable_add(GtkWidget *w, gpointer data)
1144 {
1145     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1146     const gchar *v = gtk_entry_get_text(GTK_ENTRY(ge->ops.psf_input));
1147
1148     if (strlen(v) == 0)
1149       gtk_widget_set_sensitive(ge->ops.psf_add, FALSE);
1150     else
1151       gtk_widget_set_sensitive(ge->ops.psf_add, TRUE);
1152 }
1153
1154 static void
1155 delete_unimap(GtkWidget *w, gpointer data)
1156 {
1157     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1158     GtkTreeModel *model;
1159     GtkTreeSelection *sel;
1160     GtkTreeIter iter;
1161
1162     model = gtk_tree_view_get_model(GTK_TREE_VIEW(ge->ops.psf_mappings));
1163     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(ge->ops.psf_mappings));
1164
1165     if (gtk_tree_selection_get_selected(sel, 0, &iter)) {
1166         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1167         ge->ops.psf_modified = TRUE;
1168         gtk_widget_set_sensitive(ge->ops.psf_add, FALSE);
1169         gtk_widget_set_sensitive(ge->ops.apply, TRUE);
1170     }
1171 }
1172
1173 static void
1174 operations_dialog_populate(GlypheditRec *ge)
1175 {
1176     bdf_psf_unimap_t *psf;
1177     char **mappings;
1178     int i, nmappings;
1179     GtkTreeModel *model;
1180     GtkTreeIter iter;
1181     bdf_metrics_t metrics;
1182
1183     /*
1184      * Populate the fields of the dialog with initial values.
1185      */
1186     glyphedit_get_font_metrics(GLYPHEDIT(ge->gedit), &metrics);
1187
1188     /*
1189      * The left bearing cannot be set when the font has character cell
1190      * spacing. But make sure it is enabled so the value from the font
1191      * can be set.
1192      */
1193     gtk_widget_set_sensitive(ge->ops.lbearing, TRUE);
1194
1195     /*
1196      * Set the values.
1197      */
1198     gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.lbearing),
1199                               (gdouble) (-metrics.x_offset));
1200     gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.rbearing),
1201                               (gdouble) (metrics.width + metrics.x_offset));
1202     gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.ascent),
1203                               (gdouble) metrics.ascent);
1204     gtk_spin_button_set_value(GTK_SPIN_BUTTON(ge->ops.descent),
1205                               (gdouble) metrics.descent);
1206     if (metrics.font_spacing == BDF_CHARCELL)
1207       gtk_widget_set_sensitive(ge->ops.lbearing, FALSE);
1208
1209     /*
1210      * Add the PSF mappings to the list.
1211      */
1212     if ((psf = glyphedit_get_psf_mappings(GLYPHEDIT(ge->gedit)))) {
1213         /*
1214          * Erase the list store.
1215          */
1216         model = gtk_tree_view_get_model(GTK_TREE_VIEW(ge->ops.psf_mappings));
1217         gtk_list_store_clear(GTK_LIST_STORE(model));
1218
1219         mappings = _bdf_psf_unpack_mapping(psf, &nmappings);
1220
1221         /*
1222          * Add the mappings to the list.
1223          */
1224         for (i = 0; i < nmappings; i++) {
1225             gtk_list_store_append(GTK_LIST_STORE(model), &iter);
1226             gtk_list_store_set(GTK_LIST_STORE(model), &iter,
1227                                0, mappings[i], -1);
1228         }
1229         free((char *) mappings);
1230     }
1231
1232     gtk_entry_set_text(GTK_ENTRY(ge->ops.psf_input), "");
1233     gtk_widget_set_sensitive(ge->ops.psf_add, FALSE);
1234
1235     /*
1236      * Make the "Apply" button insensitive until the user has modified
1237      * something.
1238      */
1239     gtk_widget_set_sensitive(ge->ops.apply, FALSE);
1240
1241     ge->ops.degrees_modified = ge->ops.resize_modified =
1242         ge->ops.psf_modified = FALSE;
1243 }
1244
1245 static void
1246 operations_dialog_setup(GlypheditRec *ge)
1247 {
1248     GtkWidget *nb, *label, *button, *hbox, *vbox, *frame, *swin;
1249     GtkListStore *store;
1250     GtkTreeViewColumn *column;
1251     GtkCellRenderer *cell_renderer;
1252     GtkTreeSelection *sel;
1253
1254     if (ge->ops.dialog != 0)
1255       return;
1256
1257     /*
1258      * Create the pixbufs if necessary.
1259      */
1260     if (lb_image == 0)
1261       lb_image = gdk_pixbuf_new_from_xpm_data(lb_xpm);
1262     if (rb_image == 0)
1263       rb_image = gdk_pixbuf_new_from_xpm_data(rb_xpm);
1264     if (as_image == 0)
1265       as_image = gdk_pixbuf_new_from_xpm_data(as_xpm);
1266     if (ds_image == 0)
1267       ds_image = gdk_pixbuf_new_from_xpm_data(ds_xpm);
1268
1269     ge->ops.dialog = gtk_dialog_new();
1270     g_signal_connect(G_OBJECT(ge->ops.dialog), "response",
1271                      G_CALLBACK(apply_operations),
1272                      GUINT_TO_POINTER(ge->id));
1273     /*
1274      * The "delete_event" handling in the dialog doesn't seem to be
1275      * working with GTK+ version 2.7.4.
1276      */
1277     g_signal_connect(G_OBJECT(ge->ops.dialog), "delete_event",
1278                      G_CALLBACK(gtk_widget_hide), 0);
1279     ge->ops.apply = gtk_dialog_add_button(GTK_DIALOG(ge->ops.dialog),
1280                                           GTK_STOCK_APPLY,
1281                                           GTK_RESPONSE_APPLY);
1282     gtk_widget_set_sensitive(ge->ops.apply, FALSE);
1283     button = gtk_dialog_add_button(GTK_DIALOG(ge->ops.dialog),
1284                                    GTK_STOCK_CLOSE,
1285                                    GTK_RESPONSE_CLOSE);
1286
1287     nb = ge->ops.notebook = gtk_notebook_new();
1288
1289     /*
1290      * 1. Create the rotate/shear tab.
1291      */
1292     frame = gtk_frame_new(0);
1293     gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
1294
1295     vbox = gtk_vbox_new(FALSE, 10);
1296
1297     hbox = gtk_hbox_new(FALSE, 0);
1298     ge->ops.rotate = gtk_radio_button_new_with_label(0, "Rotate");
1299     g_signal_connect(G_OBJECT(ge->ops.rotate), "toggled",
1300                      G_CALLBACK(set_rotate_limits), GUINT_TO_POINTER(ge->id));
1301     ge->ops.shear =
1302         gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ge->ops.rotate),
1303                                         "Shear");
1304     g_signal_connect(G_OBJECT(ge->ops.shear), "toggled",
1305                      G_CALLBACK(set_shear_limits), GUINT_TO_POINTER(ge->id));
1306
1307     gtk_box_pack_start(GTK_BOX(hbox), ge->ops.rotate, FALSE, FALSE, 2);
1308     gtk_box_pack_start(GTK_BOX(hbox), ge->ops.shear, FALSE, FALSE, 2);
1309
1310     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1311
1312     ge->ops.rotate_adj =
1313         (GtkAdjustment *) gtk_adjustment_new(0.0, -359.0, 359.0, 1.0,
1314                                              10.0, 0.0);
1315     /*
1316      * Do this so the adjustment doesn't get unref'ed out of existence
1317      * until we explicitly get rid of it later.
1318      */
1319     g_object_ref(G_OBJECT(ge->ops.rotate_adj));
1320     /*gtk_object_sink(GTK_OBJECT(ge->ops.rotate_adj));*/
1321
1322     ge->ops.shear_adj =
1323         (GtkAdjustment *) gtk_adjustment_new(0.0, -20.0, 20.0, 1.0,
1324                                              5.0, 0.0);
1325     /*
1326      * Do this so the adjustment doesn't get unref'ed out of existence
1327      * until we explicitly get rid of it later.
1328      */
1329     g_object_ref(G_OBJECT(ge->ops.shear_adj));
1330     /*gtk_object_sink(GTK_OBJECT(ge->ops.shear_adj));*/
1331
1332     hbox = gtk_hbox_new(FALSE, 0);
1333     label = gtk_label_new("Degrees:");
1334     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
1335
1336     ge->ops.degrees = gtk_widget_new(gtk_spin_button_get_type(),
1337                                      "max_length", 6,
1338                                      "adjustment", ge->ops.rotate_adj,
1339                                      "climb_rate", 1.0,
1340                                      "digits", 0,
1341                                      "value", 0.0,
1342                                      "numeric", TRUE,
1343                                      NULL);
1344     g_signal_connect(G_OBJECT(ge->ops.degrees), "changed",
1345                      G_CALLBACK(degrees_changed), GUINT_TO_POINTER(ge->id));
1346
1347     gtk_box_pack_start(GTK_BOX(hbox), ge->ops.degrees, FALSE, FALSE, 0);
1348
1349     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
1350
1351     gtk_container_add(GTK_CONTAINER(frame), vbox);
1352
1353     /*
1354      * Add the frame to a notebook page.
1355      */
1356     gtk_notebook_append_page(GTK_NOTEBOOK(nb), frame,
1357                              gtk_label_new("Rotate/Shear"));
1358
1359     /*
1360      * 2. Create the resize font bounding box tab.
1361      */
1362     vbox = gtk_vbox_new(TRUE, 0);
1363
1364     frame = gtk_frame_new("Left and Right Bearing");
1365
1366     hbox = gtk_hbox_new(TRUE, 0);
1367     gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
1368
1369     ge->ops.lbearing = gtk_spin_button_new_with_range(0.0, 1000.0, 1.0);
1370     g_signal_connect(G_OBJECT(ge->ops.lbearing), "changed",
1371                      G_CALLBACK(resize_changed), GUINT_TO_POINTER(ge->id));
1372     label = labcon_new_pixbuf_defaults(lb_image, ge->ops.lbearing, 0);
1373     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
1374
1375     ge->ops.rbearing = gtk_spin_button_new_with_range(0.0, 1000.0, 1.0);
1376     g_signal_connect(G_OBJECT(ge->ops.rbearing), "changed",
1377                      G_CALLBACK(resize_changed), GUINT_TO_POINTER(ge->id));
1378     label = labcon_new_pixbuf_defaults(rb_image, ge->ops.rbearing, label);
1379     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
1380
1381     gtk_container_add(GTK_CONTAINER(frame), hbox);
1382
1383     gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1384
1385     frame = gtk_frame_new("Ascent and Descent");
1386
1387     hbox = gtk_hbox_new(TRUE, 0);
1388     gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
1389
1390     ge->ops.ascent = gtk_spin_button_new_with_range(0.0, 1000.0, 1.0);
1391     g_signal_connect(G_OBJECT(ge->ops.ascent), "changed",
1392                      G_CALLBACK(resize_changed), GUINT_TO_POINTER(ge->id));
1393     label = labcon_new_pixbuf_defaults(as_image, ge->ops.ascent, label);
1394     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
1395
1396     ge->ops.descent = gtk_spin_button_new_with_range(0.0, 1000.0, 1.0);
1397     g_signal_connect(G_OBJECT(ge->ops.descent), "changed",
1398                      G_CALLBACK(resize_changed), GUINT_TO_POINTER(ge->id));
1399     label = labcon_new_pixbuf_defaults(ds_image, ge->ops.descent, label);
1400     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
1401
1402     gtk_container_add(GTK_CONTAINER(frame), hbox);
1403
1404     gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
1405
1406     gtk_notebook_append_page(GTK_NOTEBOOK(nb), vbox,
1407                              gtk_label_new("Resize BBX"));
1408
1409     /*
1410      * 3. Create the PSF Unicode mapping tab.
1411      */
1412     vbox = gtk_vbox_new(FALSE, 0);
1413
1414     swin = gtk_scrolled_window_new(0, 0);
1415     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
1416                                    GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1417     gtk_container_set_border_width(GTK_CONTAINER(swin), 3);
1418
1419     store = gtk_list_store_new(1, G_TYPE_STRING);
1420     ge->ops.psf_mappings =
1421         gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1422     g_object_unref(store);
1423
1424     gtk_widget_set_size_request(ge->ops.psf_mappings, 150, 150);
1425     gtk_container_add(GTK_CONTAINER(swin), ge->ops.psf_mappings);
1426
1427
1428     cell_renderer = gtk_cell_renderer_text_new();
1429     g_object_set(G_OBJECT(cell_renderer), "editable", TRUE, NULL);
1430     g_signal_connect_object(G_OBJECT(cell_renderer), "edited",
1431                             G_CALLBACK(change_unimap), (gpointer) store,
1432                             G_CONNECT_SWAPPED);
1433     column = gtk_tree_view_column_new_with_attributes("Unicode Mappings",
1434                                                       cell_renderer,
1435                                                       "text", 0,
1436                                                       NULL);
1437
1438     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1439     gtk_tree_view_append_column(GTK_TREE_VIEW(ge->ops.psf_mappings), column);
1440     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(ge->ops.psf_mappings));
1441
1442     gtk_box_pack_start(GTK_BOX(vbox), swin, FALSE, FALSE, 0);
1443
1444     hbox = gtk_hbox_new(FALSE, 0);
1445
1446     ge->ops.psf_delete = gtk_button_new_from_stock(GTK_STOCK_DELETE);
1447     gtk_box_pack_end(GTK_BOX(hbox), ge->ops.psf_delete, FALSE, FALSE, 5);
1448     g_signal_connect(G_OBJECT(ge->ops.psf_delete), "clicked",
1449                      G_CALLBACK(delete_unimap), GUINT_TO_POINTER(ge->id));
1450
1451     ge->ops.psf_add = gtk_button_new_from_stock(GTK_STOCK_ADD);
1452     gtk_box_pack_end(GTK_BOX(hbox), ge->ops.psf_add, FALSE, FALSE, 0);
1453     g_signal_connect(G_OBJECT(ge->ops.psf_add), "clicked",
1454                      G_CALLBACK(add_mapping), GUINT_TO_POINTER(ge->id));
1455
1456     ge->ops.psf_input = gtk_widget_new(gtk_entry_get_type(),
1457                                        "max_length", 8, NULL);
1458     g_signal_connect(G_OBJECT(ge->ops.psf_input), "activate",
1459                      G_CALLBACK(add_mapping), GUINT_TO_POINTER(ge->id));
1460     g_signal_connect(G_OBJECT(ge->ops.psf_input), "changed",
1461                      G_CALLBACK(enable_add), GUINT_TO_POINTER(ge->id));
1462     gtk_box_pack_end(GTK_BOX(hbox), ge->ops.psf_input, FALSE, FALSE, 0);
1463
1464     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1465
1466     gtk_notebook_append_page(GTK_NOTEBOOK(nb), vbox,
1467                              gtk_label_new("PSF Unicode Mappings"));
1468
1469     ge->unimap_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb), 2);
1470     if (glyphedit_get_spacing(GLYPHEDIT(ge->gedit)) != BDF_PROPORTIONAL)
1471       gtk_widget_set_sensitive(ge->unimap_page, TRUE);
1472     else
1473       gtk_widget_set_sensitive(ge->unimap_page, FALSE);
1474
1475     /*
1476      * 4. Add the notebook to the dialog.
1477      */
1478     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(ge->ops.dialog)->vbox), nb);
1479
1480     gtk_window_set_transient_for(GTK_WINDOW(ge->ops.dialog),
1481                                  GTK_WINDOW(ge->shell));
1482     gtk_widget_show_all(GTK_DIALOG(ge->ops.dialog)->vbox);
1483     gtk_widget_show_all(GTK_DIALOG(ge->ops.dialog)->action_area);
1484 }
1485
1486 static void
1487 show_rotate_dialog(GtkWidget *w, gpointer data)
1488 {
1489     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1490
1491     operations_dialog_setup(ge);
1492     operations_dialog_populate(ge);
1493
1494     /*
1495      * Make sure we turn to the first notebook page.
1496      */
1497     gtk_notebook_set_current_page(GTK_NOTEBOOK(ge->ops.notebook), 0);
1498
1499     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ge->ops.rotate), TRUE);
1500
1501     /*
1502      * Move the focus to the spin box.
1503      */
1504     gtk_widget_grab_focus(ge->ops.degrees);
1505
1506     gtk_widget_show(ge->ops.dialog);
1507 }
1508
1509 static void
1510 show_shear_dialog(GtkWidget *w, gpointer data)
1511 {
1512     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1513
1514     operations_dialog_setup(ge);
1515     operations_dialog_populate(ge);
1516
1517     /*
1518      * Make sure we turn to the first notebook page.
1519      */
1520     gtk_notebook_set_current_page(GTK_NOTEBOOK(ge->ops.notebook), 0);
1521
1522     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ge->ops.shear), TRUE);
1523
1524     /*
1525      * Move the focus to the spin box.
1526      */
1527     gtk_widget_grab_focus(ge->ops.degrees);
1528
1529     gtk_widget_show(ge->ops.dialog);
1530 }
1531
1532 static void
1533 show_resize_dialog(GtkWidget *w, gpointer data)
1534 {
1535     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1536
1537     operations_dialog_setup(ge);
1538     operations_dialog_populate(ge);
1539
1540     /*
1541      * Make sure we turn to the first notebook page.
1542      */
1543     gtk_notebook_set_current_page(GTK_NOTEBOOK(ge->ops.notebook), 1);
1544
1545     /*
1546      * Move the focus to the first sensitive spin box.
1547      */
1548     if (GTK_WIDGET_SENSITIVE(ge->ops.lbearing))
1549       gtk_widget_grab_focus(ge->ops.lbearing);
1550     else
1551       gtk_widget_grab_focus(ge->ops.rbearing);
1552
1553     gtk_widget_show(ge->ops.dialog);
1554 }
1555
1556 static void
1557 embolden_glyph(GtkWidget *w, gpointer data)
1558 {
1559     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1560
1561     glyphedit_embolden_glyph(GLYPHEDIT(ge->gedit));
1562 }
1563
1564 static void
1565 show_unimap_dialog(GtkWidget *w, gpointer data)
1566 {
1567     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1568
1569     operations_dialog_setup(ge);
1570     operations_dialog_populate(ge);
1571
1572     gtk_notebook_set_current_page(GTK_NOTEBOOK(ge->ops.notebook), 2);
1573
1574     /*
1575      * Move the focus to the input field.
1576      */
1577     gtk_widget_grab_focus(ge->ops.psf_input);
1578
1579     gtk_widget_show(ge->ops.dialog);
1580 }
1581
1582 static GtkWidget *
1583 make_ops_menu(GlypheditRec *ge, GtkWidget *menubar)
1584 {
1585     GtkWidget *ops, *menu, *mitem, *sep;
1586
1587     /*
1588      * Create the Operations menu.
1589      */
1590     ops = gtk_menu_item_new_with_mnemonic("_Operations");
1591
1592     ge->ops_menu = menu = gtk_menu_new();
1593     g_signal_connect(G_OBJECT(menu), "map_event",
1594                      G_CALLBACK(operations_menu_up), GUINT_TO_POINTER(ge->id));
1595     g_signal_connect(G_OBJECT(menu), "unmap_event",
1596                      G_CALLBACK(operations_menu_down),
1597                      GUINT_TO_POINTER(ge->id));
1598
1599     mitem = make_accel_menu_item(menu, "_Draw",
1600                                  "<Control>D", ge->ag);
1601     g_signal_connect(G_OBJECT(mitem), "activate",
1602                      G_CALLBACK(draw_operation),
1603                      GUINT_TO_POINTER(ge->id));
1604
1605     mitem = make_accel_menu_item(menu, "_Move",
1606                                  "<Control>M", ge->ag);
1607     g_signal_connect(G_OBJECT(mitem), "activate",
1608                      G_CALLBACK(move_operation),
1609                      GUINT_TO_POINTER(ge->id));
1610
1611     mitem = make_accel_menu_item(menu, "_Copy",
1612                                  "<Control>Y", ge->ag);
1613     g_signal_connect(G_OBJECT(mitem), "activate",
1614                      G_CALLBACK(copy_operation),
1615                      GUINT_TO_POINTER(ge->id));
1616
1617     /*
1618      * Separator.
1619      */
1620     sep = gtk_menu_item_new();
1621     gtk_widget_set_sensitive(sep, FALSE);
1622     gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
1623
1624     mitem = make_accel_menu_item(menu, "_Rotate",
1625                                  "<Control>T", ge->ag);
1626     g_signal_connect(G_OBJECT(mitem), "activate",
1627                      G_CALLBACK(show_rotate_dialog),
1628                      GUINT_TO_POINTER(ge->id));
1629
1630     mitem = make_accel_menu_item(menu, "_Shear",
1631                                  "<Control>E", ge->ag);
1632     g_signal_connect(G_OBJECT(mitem), "activate",
1633                      G_CALLBACK(show_shear_dialog),
1634                      GUINT_TO_POINTER(ge->id));
1635
1636     mitem = make_accel_menu_item(menu, "_Embolden",
1637                                  "<Control>H", ge->ag);
1638     g_signal_connect(G_OBJECT(mitem), "activate",
1639                      G_CALLBACK(embolden_glyph),
1640                      GUINT_TO_POINTER(ge->id));
1641
1642     gtk_menu_item_set_submenu(GTK_MENU_ITEM(ops), menu);
1643
1644     /*
1645      * Separator.
1646      */
1647     sep = gtk_menu_item_new();
1648     gtk_widget_set_sensitive(sep, FALSE);
1649     gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep);
1650
1651     ge->resize = make_accel_menu_item(menu, "_Resize BBX",
1652                                            "<Control>R", ge->ag);
1653     g_signal_connect(G_OBJECT(ge->resize), "activate",
1654                      G_CALLBACK(show_resize_dialog),
1655                      GUINT_TO_POINTER(ge->id));
1656
1657     ge->unimap = make_accel_menu_item(menu, "Edit PSF Unicode _Mappings",
1658                                       "<Control>F", ge->ag);
1659     g_signal_connect(G_OBJECT(ge->unimap), "activate",
1660                      G_CALLBACK(show_unimap_dialog),
1661                      GUINT_TO_POINTER(ge->id));
1662
1663     return ops;
1664 }
1665
1666 static void
1667 pointer_moved(GtkWidget *w, gpointer cb, gpointer ged)
1668 {
1669     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged);
1670     GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb;
1671     bdf_glyph_grid_t *g;
1672
1673     g = glyphedit_get_grid(GLYPHEDIT(ge->gedit));
1674     if (g->bpp == 1 || si->color == 0)
1675       sprintf(buffer1, "(%d,%d)", si->x, si->y);
1676     else {
1677         switch (g->bpp) {
1678           case 2:
1679             sprintf(buffer1, "(%d,%d,%d)", si->x, si->y,
1680                     options.colors[si->color-1]);
1681             break;
1682           case 4:
1683             sprintf(buffer1, "(%d,%d,%d)", si->x, si->y,
1684                     options.colors[si->color+4-1]);
1685             break;
1686           case 8:
1687             sprintf(buffer1, "(%d,%d,%d)", si->x, si->y, si->color);
1688             break;
1689         }
1690     }
1691
1692     gtk_label_set_text(GTK_LABEL(ge->coords), buffer1);
1693 }
1694
1695 /*
1696  * Under certain circumstances, the glyphedit widget causes the operation to
1697  * change. Basically, when a bitmap is pasted, the widget goes into a MOVE
1698  * operation. All the operations are handled here just in case of future
1699  * changes.
1700  */
1701 static void
1702 operation_changed(GtkWidget *w, gpointer cb, gpointer ged)
1703 {
1704     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged);
1705     GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb;
1706
1707     if (si->operation == GLYPHEDIT_DRAW)
1708       gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_DRAW);
1709     else if (si->operation == GLYPHEDIT_MOVE)
1710       gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_MOVE);
1711     else if (si->operation == GLYPHEDIT_COPY)
1712       gecontrol_change_operation(GECONTROL(ge->gectrl), GECONTROL_COPY);
1713 }
1714
1715 static void
1716 color_changed(GtkWidget *w, gpointer cb, gpointer ged)
1717 {
1718     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged);
1719     GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb;
1720
1721     gecontrol_change_color(GECONTROL(ge->gectrl), si->color);
1722 }
1723
1724 static void
1725 glyph_modified(GtkWidget *w, gpointer cb, gpointer ged)
1726 {
1727     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(ged);
1728     gbdfed_editor_t *ed = editors + ge->owner;
1729     GlypheditSignalInfo *si = (GlypheditSignalInfo *) cb;
1730     gchar *prgname = g_get_prgname();
1731
1732     if (si->metrics == 0)
1733       return;
1734
1735     if (ed->file == 0)
1736       sprintf(buffer1, "%s - Glyph Edit: (unnamed%d) [modified]",
1737               prgname, ed->id);
1738     else
1739       sprintf(buffer1, "%s - Glyph Edit: %s [modified]", prgname, ed->file);
1740
1741     gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
1742
1743     sprintf(buffer1, "width %hd height %hd\nascent %hd descent %hd",
1744             si->metrics->width, si->metrics->height,
1745             si->metrics->ascent, si->metrics->descent);
1746     gtk_label_set_text(GTK_LABEL(ge->metrics), buffer1);
1747
1748     if (si->metrics->font_spacing == BDF_PROPORTIONAL) {
1749         sprintf(buffer1, "%hd", si->metrics->dwidth);
1750         g_signal_handler_block(G_OBJECT(ge->dwidth), ge->handler);
1751         gtk_entry_set_text(GTK_ENTRY(ge->dwidth), buffer1);
1752         g_signal_handler_unblock(G_OBJECT(ge->dwidth), ge->handler);
1753     }
1754
1755     /*
1756      * Update the glyph image on the Glyphedit control widget.
1757      */
1758     gecontrol_update_glyph_image(GECONTROL(ge->gectrl), si->image);
1759
1760     gtk_widget_set_sensitive(ge->update, TRUE);
1761     gtk_widget_set_sensitive(ge->update_next, TRUE);
1762     gtk_widget_set_sensitive(ge->update_prev, TRUE);
1763     gtk_widget_set_sensitive(ge->button_update, TRUE);
1764     gtk_widget_set_sensitive(ge->button_next, TRUE);
1765     gtk_widget_set_sensitive(ge->button_prev, TRUE);
1766 }
1767
1768 /*
1769  * This function will be a bit screwy until I can figure out how to make a
1770  * signal pass an enum as the first param after the widget.
1771  */
1772 static void
1773 gectrl_activate(GtkWidget *w, gpointer info, gpointer data)
1774 {
1775     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1776     GEControlActivateInfo *ai = (GEControlActivateInfo *) info;
1777
1778     switch (ai->operation) {
1779       case GECONTROL_DRAW:
1780         glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_DRAW);
1781         break;
1782       case GECONTROL_MOVE:
1783         glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_MOVE);
1784         break;
1785       case GECONTROL_COPY:
1786         glyphedit_change_operation(GLYPHEDIT(ge->gedit), GLYPHEDIT_COPY);
1787         break;
1788       case GECONTROL_FLIP_HORIZONTAL:
1789         glyphedit_flip_glyph(GLYPHEDIT(ge->gedit), GTK_ORIENTATION_HORIZONTAL);
1790         break;
1791       case GECONTROL_FLIP_VERTICAL:
1792         glyphedit_flip_glyph(GLYPHEDIT(ge->gedit), GTK_ORIENTATION_VERTICAL);
1793         break;
1794       case GECONTROL_SHEAR:
1795         show_shear_dialog(w, data);
1796         break;
1797       case GECONTROL_ROTATE_LEFT_90:
1798         glyphedit_rotate_glyph(GLYPHEDIT(ge->gedit), -90);
1799         break;
1800       case GECONTROL_ROTATE_RIGHT_90:
1801         glyphedit_rotate_glyph(GLYPHEDIT(ge->gedit), 90);
1802         break;
1803       case GECONTROL_ROTATE:
1804         show_rotate_dialog(w, data);
1805         break;
1806       case GECONTROL_SHIFT_UP_LEFT:
1807         glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), -1, -1);
1808         break;
1809       case GECONTROL_SHIFT_UP:
1810         glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 0, -1);
1811         break;
1812       case GECONTROL_SHIFT_UP_RIGHT:
1813         glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 1, -1);
1814         break;
1815       case GECONTROL_SHIFT_LEFT:
1816         glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), -1, 0);
1817         break;
1818       case GECONTROL_SHIFT_RIGHT:
1819         glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 1, 0);
1820         break;
1821       case GECONTROL_SHIFT_DOWN_LEFT:
1822         glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), -1, 1);
1823         break;
1824       case GECONTROL_SHIFT_DOWN:
1825         glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 0, 1);
1826         break;
1827       case GECONTROL_SHIFT_DOWN_RIGHT:
1828         glyphedit_shift_glyph(GLYPHEDIT(ge->gedit), 1, 1);
1829         break;
1830       case GECONTROL_COLOR_CHANGE:
1831         glyphedit_set_color(GLYPHEDIT(ge->gedit), ai->color);
1832         break;
1833     }
1834 }
1835
1836 /*
1837  * This is called when the device width field is changed in any way.
1838  */
1839 static void
1840 enable_update(GtkWidget *w, gpointer data)
1841 {
1842     GlypheditRec *ge = glyph_editors + GPOINTER_TO_UINT(data);
1843     gbdfed_editor_t *ed = editors + ge->owner;
1844
1845     if (ed->file == 0)
1846       sprintf(buffer1, "%s - Glyph Edit: (unnamed%d) [modified]",
1847               g_get_prgname(), ed->id);
1848     else
1849       sprintf(buffer1, "%s - Glyph Edit: %s [modified]",
1850               g_get_prgname(), ed->file);
1851
1852     gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
1853     gtk_widget_set_sensitive(ge->update, TRUE);
1854     gtk_widget_set_sensitive(ge->update_next, TRUE);
1855     gtk_widget_set_sensitive(ge->update_prev, TRUE);
1856     gtk_widget_set_sensitive(ge->button_update, TRUE);
1857 }
1858
1859 static void
1860 _guigedit_build_editor(GlypheditRec *ge, bdf_glyph_grid_t *grid, guint base,
1861                        gbdfed_editor_t *ed)
1862 {
1863     GtkWidget *mb, *mitem, *frame, *vbox, *vbox1, *hbox, *img;
1864     bdf_bitmap_t image;
1865
1866     if (ed->file == 0)
1867       sprintf(buffer1, "%s - Glyph Edit: (unnamed%d)", g_get_prgname(),
1868               ed->id);
1869     else
1870       sprintf(buffer1, "%s - Glyph Edit: %s", g_get_prgname(), ed->file);
1871
1872     ge->shell = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1873
1874     gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
1875
1876     gtk_window_set_resizable(GTK_WINDOW(ge->shell), TRUE);
1877
1878     (void) g_signal_connect(G_OBJECT(ge->shell), "destroy_event",
1879                             G_CALLBACK(close_glyph_editor),
1880                             GUINT_TO_POINTER(ge->id));
1881     (void) g_signal_connect(G_OBJECT(ge->shell), "delete_event",
1882                             G_CALLBACK(close_glyph_editor),
1883                             GUINT_TO_POINTER(ge->id));
1884
1885     vbox = gtk_vbox_new(FALSE, 0);
1886     gtk_container_add(GTK_CONTAINER(ge->shell), vbox);
1887
1888     ge->ag = gtk_accel_group_new();
1889
1890     mb = gtk_menu_bar_new();
1891     mitem = make_file_menu(ge, mb);
1892     gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
1893
1894     mitem = make_edit_menu(ge, mb);
1895     gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
1896
1897     mitem = make_ops_menu(ge, mb);
1898     gtk_menu_shell_append(GTK_MENU_SHELL(mb), mitem);
1899
1900     gtk_box_pack_start(GTK_BOX(vbox), mb, FALSE, TRUE, 0);
1901
1902     /*
1903      * Attach the accelerators to the editor.
1904      */
1905     gtk_window_add_accel_group(GTK_WINDOW(ge->shell), ge->ag);
1906
1907     /*
1908      * 1. Add the glyph name, next/previous buttons and encoding widgets.
1909      */
1910     frame = gtk_frame_new(0);
1911     gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1912     gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0);
1913
1914     vbox1 = gtk_vbox_new(TRUE, 0);
1915     gtk_container_add(GTK_CONTAINER(frame), vbox1);
1916
1917     hbox = gtk_hbox_new(FALSE, 0);
1918     gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
1919
1920     ge->name = gtk_widget_new(gtk_entry_get_type(), "max_length", 128, NULL);
1921     mitem = labcon_new_label_defaults("Glyph Name:", ge->name, 0);
1922     gtk_box_pack_start(GTK_BOX(hbox), mitem, TRUE, TRUE, 0);
1923
1924     /* Update button */
1925     ge->button_update = gtk_button_new();
1926     guiutil_util_set_tooltip(ge->button_update, "Update Font");
1927     img = gtk_image_new_from_stock(GTK_STOCK_APPLY, GTK_ICON_SIZE_SMALL_TOOLBAR);
1928     gtk_button_set_image(GTK_BUTTON(ge->button_update), img);
1929     g_signal_connect(G_OBJECT(ge->button_update), "clicked", 
1930                      G_CALLBACK(update_font), NULL);
1931     gtk_box_pack_start(GTK_BOX(hbox), ge->button_update, FALSE, FALSE, 0);
1932
1933     /* Previous button */
1934     ge->button_prev = gtk_button_new();
1935     guiutil_util_set_tooltip(ge->button_prev, "Previous Glyph");
1936     img = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, 
1937                                    GTK_ICON_SIZE_SMALL_TOOLBAR);
1938     gtk_button_set_image(GTK_BUTTON(ge->button_prev), img);
1939     g_signal_connect(G_OBJECT(ge->button_prev), "clicked",
1940                      G_CALLBACK(previous_glyph), NULL);
1941     gtk_box_pack_start(GTK_BOX(hbox), ge->button_prev, FALSE, FALSE, 0);
1942
1943     /* Next button */
1944     ge->button_next = gtk_button_new();
1945     guiutil_util_set_tooltip(ge->button_next, "Next Glyph");
1946     img = gtk_image_new_from_stock(GTK_STOCK_MEDIA_NEXT, 
1947                                    GTK_ICON_SIZE_SMALL_TOOLBAR);
1948     gtk_button_set_image(GTK_BUTTON(ge->button_next), img);
1949     g_signal_connect(G_OBJECT(ge->button_next), "clicked", 
1950                      G_CALLBACK(next_glyph), NULL);
1951     gtk_box_pack_start(GTK_BOX(hbox), ge->button_next, FALSE, FALSE, 0);
1952
1953     /* Encoding */
1954     ge->encoding = gtk_label_new("0000");
1955     gtk_misc_set_alignment(GTK_MISC(ge->encoding), 0.0, 0.5);
1956     mitem = labcon_new_label_defaults("Encoding:", ge->encoding, mitem);
1957     gtk_box_pack_start(GTK_BOX(vbox1), mitem, TRUE, TRUE, 0);
1958
1959     /*
1960      * 2. Add the device width and metrics widgets.
1961      */
1962     frame = gtk_frame_new(0);
1963     gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1964     gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0);
1965
1966     vbox1 = gtk_vbox_new(FALSE, 5);
1967     gtk_container_add(GTK_CONTAINER(frame), vbox1);
1968
1969     ge->dwidth = gtk_widget_new(gtk_entry_get_type(),
1970                                 "max_length", 6,
1971                                 NULL);
1972     ge->handler = g_signal_connect(G_OBJECT(ge->dwidth), "changed",
1973                                    G_CALLBACK(enable_update),
1974                                    GUINT_TO_POINTER(ge->id));
1975     mitem = labcon_new_label_defaults("Device Width:", ge->dwidth, mitem);
1976     gtk_box_pack_start(GTK_BOX(vbox1), mitem, TRUE, TRUE, 0);
1977
1978     ge->metrics = gtk_label_new("width 0 height 0\r\nascent 0 descent 0");
1979     gtk_misc_set_alignment(GTK_MISC(ge->metrics), 0.0, 0.5);
1980     mitem = labcon_new_label_defaults("Metrics:", ge->metrics, mitem);
1981     gtk_box_pack_start(GTK_BOX(vbox1), mitem, TRUE, TRUE, 0);
1982
1983     hbox = gtk_hbox_new(FALSE, 0);
1984
1985     vbox1 = gtk_vbox_new(FALSE, 0);
1986
1987     /*
1988      * Create the coordinates label.
1989      */
1990     ge->coords = gtk_label_new("(0,0)");
1991     gtk_misc_set_alignment(GTK_MISC(ge->coords), 0.5, 0.5);
1992     gtk_box_pack_start(GTK_BOX(vbox1), ge->coords, FALSE, TRUE, 0);
1993
1994     /*
1995      * Create the glyph editor.
1996      */
1997     ge->gedit = glyphedit_newv(grid, options.pixel_size, options.show_x_height,
1998                                options.show_cap_height, options.colors);
1999     g_signal_connect(G_OBJECT(ge->gedit), "glyph-modified",
2000                      G_CALLBACK(glyph_modified), GUINT_TO_POINTER(ge->id));
2001     g_signal_connect(G_OBJECT(ge->gedit), "pointer-moved",
2002                      G_CALLBACK(pointer_moved), GUINT_TO_POINTER(ge->id));
2003     g_signal_connect(G_OBJECT(ge->gedit), "operation-change",
2004                      G_CALLBACK(operation_changed), GUINT_TO_POINTER(ge->id));
2005     g_signal_connect(G_OBJECT(ge->gedit), "color-change",
2006                      G_CALLBACK(color_changed), GUINT_TO_POINTER(ge->id));
2007     gtk_box_pack_start(GTK_BOX(vbox1), ge->gedit, TRUE, TRUE, 0);
2008
2009     gtk_box_pack_start(GTK_BOX(hbox), vbox1, TRUE, TRUE, 0);
2010
2011     vbox1 = gtk_vbox_new(FALSE, 0);
2012
2013     ge->gectltips = gtk_label_new("");
2014     gtk_misc_set_alignment(GTK_MISC(ge->gectltips), 0.5, 0.5);
2015     gtk_box_pack_start(GTK_BOX(vbox1), ge->gectltips, FALSE, TRUE, 0);
2016
2017     /*
2018      * Get the initial glyph image.
2019      */
2020     glyphedit_get_image(GLYPHEDIT(ge->gedit), &image);
2021     ge->gectrl = gecontrol_newv(ge->gectltips, &image, options.colors);
2022     if (image.bytes > 0)
2023       free(image.bitmap);
2024     g_signal_connect(G_OBJECT(ge->gectrl), "activate",
2025                      G_CALLBACK(gectrl_activate), GUINT_TO_POINTER(ge->id));
2026
2027     gtk_box_pack_start(GTK_BOX(vbox1), ge->gectrl, TRUE, TRUE, 0);
2028
2029     gtk_box_pack_start(GTK_BOX(hbox), vbox1, FALSE, TRUE, 0);
2030
2031     gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
2032 }
2033
2034 void
2035 guigedit_edit_glyph(gbdfed_editor_t *ed, FontgridSelectionInfo *si)
2036 {
2037     GlypheditRec *ge;
2038     bdf_font_t *font;
2039     guint base;
2040     bdf_glyph_grid_t *grid;
2041     bdf_bitmap_t image;
2042
2043     font = fontgrid_get_font(FONTGRID(ed->fgrid));
2044     base = fontgrid_get_code_base(FONTGRID(ed->fgrid));
2045
2046     if (si->unencoded)
2047       grid = bdf_make_glyph_grid(font, si->start, 1);
2048     else
2049       grid = bdf_make_glyph_grid(font, si->start, 0);
2050
2051     ge = _guigedit_get_glyph_editor(ed->id);
2052
2053     if (ge->name == 0) {
2054         _guigedit_build_editor(ge, grid, base, ed);
2055     } else {
2056         if (ed->file == 0)
2057           sprintf(buffer1, "%s - Glyph Edit: (unnamed%d)", g_get_prgname(),
2058                   ed->id);
2059         else
2060           sprintf(buffer1, "%s - Glyph Edit: %s", g_get_prgname(), ed->file);
2061
2062         gtk_window_set_title(GTK_WINDOW(ge->shell), buffer1);
2063         glyphedit_set_grid(GLYPHEDIT(ge->gedit), grid);
2064
2065         /*
2066          * Update the image in the glypheditor control panel.
2067          */
2068         if (grid) {
2069             bdf_grid_image(grid, &image);
2070             gecontrol_set_glyph_image(GECONTROL(ge->gectrl), &image);
2071             if (image.bytes > 0)
2072               free(image.bitmap);
2073         } else
2074           gecontrol_set_glyph_image(GECONTROL(ge->gectrl), 0);
2075
2076         /*
2077          * Make sure the control has the most current list of colors.
2078          */
2079         gecontrol_set_color_list(GECONTROL(ge->gectrl), options.colors);
2080     }
2081
2082     /*
2083      * Update the text fields and labels with the glyph info.
2084      */
2085     gtk_entry_set_text(GTK_ENTRY(ge->name), grid->name);
2086     if (grid->unencoded)
2087       sprintf(buffer1, "-1");
2088     else {
2089         switch (base) {
2090           case 8: sprintf(buffer1, "%o", grid->encoding); break;
2091           case 10: sprintf(buffer1, "%d", grid->encoding); break;
2092           case 16: sprintf(buffer1, "%04X", grid->encoding); break;
2093         }
2094     }
2095     gtk_label_set_text(GTK_LABEL(ge->encoding), buffer1);
2096
2097     gtk_widget_set_sensitive(ge->dwidth, TRUE);
2098     sprintf(buffer1, "%hd", grid->dwidth);
2099
2100     g_signal_handler_block(G_OBJECT(ge->dwidth), ge->handler);
2101     gtk_entry_set_text(GTK_ENTRY(ge->dwidth), buffer1);
2102     g_signal_handler_unblock(G_OBJECT(ge->dwidth), ge->handler);
2103
2104     if (grid->spacing != BDF_PROPORTIONAL)
2105       gtk_widget_set_sensitive(ge->dwidth, FALSE);
2106
2107     sprintf(buffer1, "width %hd height %hd\r\nascent %hd descent %hd",
2108             grid->glyph_bbx.width, grid->glyph_bbx.height,
2109             grid->glyph_bbx.ascent, grid->glyph_bbx.descent);
2110     gtk_label_set_text(GTK_LABEL(ge->metrics), buffer1);
2111
2112     if (grid->modified) {
2113         gtk_widget_set_sensitive(ge->update, TRUE);
2114         gtk_widget_set_sensitive(ge->button_update, TRUE);
2115         gtk_widget_set_sensitive(ge->update_next, TRUE);
2116         gtk_widget_set_sensitive(ge->update_prev, TRUE);
2117     } else {
2118         gtk_widget_set_sensitive(ge->update, FALSE);
2119         gtk_widget_set_sensitive(ge->button_update, FALSE);
2120         gtk_widget_set_sensitive(ge->update_next, FALSE);
2121         gtk_widget_set_sensitive(ge->update_prev, FALSE);
2122     }
2123
2124     /*
2125      * Set the sensitivity of the next and previous buttons.
2126      */
2127     if (glyphedit_get_encoding(GLYPHEDIT(ge->gedit)) == 0)
2128       gtk_widget_set_sensitive(ge->button_prev, FALSE);
2129     else
2130       gtk_widget_set_sensitive(ge->button_prev, TRUE);
2131
2132     if (glyphedit_get_encoding(GLYPHEDIT(ge->gedit)) == 0xffff)
2133       gtk_widget_set_sensitive(ge->button_next, FALSE);
2134     else
2135       gtk_widget_set_sensitive(ge->button_next, TRUE);
2136
2137     gtk_widget_show_all(ge->shell);
2138
2139     /*
2140      * Force the focus to be on the glyph grid.
2141      */
2142     gtk_widget_grab_focus(ge->gedit);
2143 }
2144
2145 /*
2146  * Routine to set the show_cap_height value for all the glyph editors.
2147  */
2148 void
2149 guigedit_show_cap_height(gboolean show)
2150 {
2151     gulong i;
2152
2153     for (i = 0; i < num_glyph_editors; i++) {
2154         if (glyph_editors[i].owner != ~0)
2155           glyphedit_set_show_cap_height(GLYPHEDIT(glyph_editors[i].gedit),
2156                                         show);
2157     }
2158 }
2159
2160 /*
2161  * Routine to set the show_cap_height value for all the glyph editors.
2162  */
2163 void
2164 guigedit_show_x_height(gboolean show)
2165 {
2166     gulong i;
2167
2168     for (i = 0; i < num_glyph_editors; i++) {
2169         if (glyph_editors[i].owner != ~0)
2170           glyphedit_set_show_x_height(GLYPHEDIT(glyph_editors[i].gedit), show);
2171     }
2172 }
2173
2174 /*
2175  * Routine to set the pixel size on all of the visible editors.
2176  */
2177 void
2178 guigedit_set_pixel_size(guint pixel_size)
2179 {
2180     gulong i;
2181
2182     for (i = 0; i < num_glyph_editors; i++) {
2183         if (glyph_editors[i].owner != ~0)
2184           glyphedit_set_pixel_size(GLYPHEDIT(glyph_editors[i].gedit),
2185                                    pixel_size);
2186     }
2187 }
2188
2189 /*
2190  * Routine to set the spacing and device width on all of the visible editors.
2191  */
2192 void
2193 guigedit_set_font_spacing(gint spacing, guint16 monowidth)
2194 {
2195     gulong i;
2196
2197     for (i = 0; i < num_glyph_editors; i++) {
2198         if (glyph_editors[i].owner != ~0)
2199           glyphedit_set_spacing(GLYPHEDIT(glyph_editors[i].gedit), spacing,
2200                                 monowidth);
2201     }
2202 }
2203
2204 /*
2205  * Routine to set the code base on all the glyph editors that are active.
2206  */
2207 void
2208 guigedit_set_code_base(gint base)
2209 {
2210     guint i, enc;
2211
2212     for (i = 0; i < num_glyph_editors; i++) {
2213         if (glyph_editors[i].owner != ~0) {
2214             enc = (guint) glyphedit_get_encoding(GLYPHEDIT(glyph_editors[i].gedit));
2215             switch (base) {
2216               case 8:
2217                 sprintf(buffer1, "%o", enc);
2218                 break;
2219               case 10:
2220                 sprintf(buffer1, "%d", enc);
2221                 break;
2222               case 16:
2223                 sprintf(buffer1, "%04X", enc);
2224                 break;
2225             }
2226             gtk_label_set_text(GTK_LABEL(glyph_editors[i].encoding), buffer1);
2227         }
2228     }
2229 }
2230
2231 /*
2232  * Routine to clean up everything that was allocated here.
2233  */
2234 void
2235 guigedit_cleanup(void)
2236 {
2237     gulong i;
2238
2239     /*
2240      * Cycle through all the glyph editors and check if they have been
2241      * modified and if they need to be saved.
2242      */
2243     for (i = 0; i < num_glyph_editors; i++) {
2244         if (glyph_editors[i].id != ~0)
2245           close_glyph_editor(0, 0, GUINT_TO_POINTER(i));
2246     }
2247
2248     /*
2249      * Unreference all the pixbufs so they go away.
2250      */
2251     if (lb_image != 0)
2252       g_object_unref(G_OBJECT(lb_image));
2253     if (rb_image != 0)
2254       g_object_unref(G_OBJECT(rb_image));
2255     if (as_image != 0)
2256       g_object_unref(G_OBJECT(as_image));
2257     if (ds_image != 0)
2258       g_object_unref(G_OBJECT(ds_image));
2259
2260     lb_image = rb_image = as_image = ds_image = 0;
2261
2262     /*
2263      * GTK will take care of the widgets in the glyph editors.
2264      */
2265     if (num_glyph_editors > 0)
2266       g_free(glyph_editors);
2267 }