]> git.kernelconcepts.de Git - gbdfed.git/blob - glyphtest.c
Fixup several compile faults due to changes in recent distributions,
[gbdfed.git] / glyphtest.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 "glyphtest.h"
24
25 #ifdef ENABLE_NLS
26 #include <libintl.h>
27 #define _(s) dgettext(GETTEXT_PACKAGE,s)
28 #else
29 #define _(s) (s)
30 #endif
31
32 #define HMARGINS(fw) ((fw)->hmargin << 1)
33 #define VMARGINS(fw) ((fw)->vmargin << 1)
34
35 /*
36  * Argument types.
37  */
38 enum {
39     PROP_0 = 0,
40     PROP_BASELINE,
41     PROP_DIRECTION
42 };
43
44 enum {
45     ADD_GLYPH = 0,
46     NUM_SIGNALS
47 };
48
49 static GtkWidgetClass *parent_class = 0;
50 static guint glyphtest_signals[NUM_SIGNALS];
51
52 #define GTESTMAX(h,i) ((h) > (i) ? (h) : (i))
53
54 static gboolean
55 _glyphtest_set_line_size(Glyphtest *gw)
56 {
57     GtkWidget *w;
58     gboolean changed = FALSE;
59     guint32 i;
60     guint16 wd, wwidth;
61     GlyphtestLine *lp;
62     bdf_bbx_t bbx;
63
64     w = GTK_WIDGET(gw);
65
66     lp = &gw->line;
67     (void) memset((char *) &bbx, 0, sizeof(bdf_bbx_t));
68
69     wwidth = w->allocation.width - (HMARGINS(gw) + 4);
70
71     for (wd = 0, i = 0; i < lp->glyphs_used; i++) {
72         bbx.ascent = GTESTMAX(bbx.ascent, lp->glyphs[i].font->bbx.ascent);
73         bbx.descent = GTESTMAX(bbx.descent, lp->glyphs[i].font->bbx.descent);
74         bbx.width = GTESTMAX(bbx.width, lp->glyphs[i].font->bbx.width);
75         wd += (lp->glyphs[i].font->spacing == BDF_PROPORTIONAL) ?
76             lp->glyphs[i].glyph->dwidth : lp->glyphs[i].font->monowidth;
77     }
78
79     if (lp->glyphs_used == 0) {
80         /*
81          * If no glyphs are present, then set the overall bounding box
82          * to some simple default.
83          */
84         bbx.ascent = 12;
85         bbx.descent = 3;
86         bbx.width = 10;
87         wd = bbx.width << 3;
88     }
89
90     /*
91      * If the actual line width changed, set the indicator.
92      */
93     if (wd != lp->width) {
94         lp->width = wd;
95
96         /*
97          * If the line width overflows the window width, set the changed flag.
98          */
99         if (wd > wwidth)
100           changed = TRUE;
101     }
102
103     /*
104      * If the new bounding box is not the same as the current line bounding
105      * box, then make the new one the current line bounding box.
106      */
107     if (bbx.ascent != lp->bbx.ascent || bbx.descent != lp->bbx.descent ||
108         bbx.width != lp->bbx.width) {
109         (void) memcpy((char *) &lp->bbx, (char *) &bbx, sizeof(bdf_bbx_t));
110         changed = TRUE;
111     }
112
113     /*
114      * Now set the line size.
115      */
116     lp->height = lp->bbx.ascent + lp->bbx.descent;
117     lp->cpoint.y = (VMARGINS(gw) >> 1) + 2 + lp->bbx.ascent;
118
119     return changed;
120 }
121
122 /**************************************************************************
123  *
124  * Class functions.
125  *
126  **************************************************************************/
127
128 static void
129 glyphtest_set_property(GObject *obj, guint prop_id, const GValue *value,
130                        GParamSpec *pspec)
131 {
132     Glyphtest *gw;
133
134     gw = GLYPHTEST(obj);
135
136     switch (prop_id) {
137       case PROP_BASELINE:
138         glyphtest_show_baseline(gw, g_value_get_boolean(value));
139         break;
140       case PROP_DIRECTION:
141         glyphtest_change_direction(gw, g_value_get_int(value));
142         break;
143     }
144 }
145
146 static void
147 glyphtest_get_property(GObject *obj, guint prop_id, GValue *value,
148                        GParamSpec *pspec)
149 {
150     Glyphtest *gw;
151
152     gw = GLYPHTEST(obj);
153
154     switch (prop_id) {
155       case PROP_BASELINE:
156         g_value_set_boolean(value, gw->show_baseline);
157         break;
158       case PROP_DIRECTION:
159         g_value_set_int(value, gw->dir);
160         break;
161     }
162 }
163
164 static void
165 glyphtest_destroy(GtkObject *obj)
166 {
167     Glyphtest *gw;
168
169     /*
170      * Do some checks to make sure the incoming object exists and is the right
171      * kind.
172      */
173     g_return_if_fail(obj != 0);
174     g_return_if_fail(IS_GLYPHTEST(obj));
175
176     gw = GLYPHTEST(obj);
177
178     /*
179      * Delete the line structure if it was allocated.
180      */
181     if (gw->line.glyphs_size > 0)
182       g_free(gw->line.glyphs);
183     gw->line.glyphs_used = gw->line.glyphs_size = 0;
184
185     /*
186      * Free up any points that have been allocated.
187      */
188     if (gw->image_size > 0)
189       g_free(gw->image);
190     gw->image_size = gw->image_used = 0;
191
192     /*
193      * Follow the class chain back up to free up resources allocated in the
194      * parent classes.
195      */
196     GTK_OBJECT_CLASS(parent_class)->destroy(obj);
197 }
198
199 static void
200 glyphtest_finalize(GObject *obj)
201 {
202     /*
203      * Do some checks to make sure the incoming object exists and is the right
204      * kind.
205      */
206     g_return_if_fail(obj != 0);
207     g_return_if_fail(IS_GLYPHTEST(obj));
208
209     /*
210      * Follow the class chain back up to free up resources allocated in the
211      * parent classes.
212      */
213     G_OBJECT_CLASS(parent_class)->finalize(obj);
214 }
215
216 static void
217 glyphtest_preferred_size(GtkWidget *widget, GtkRequisition *preferred)
218 {
219     Glyphtest *gw;
220
221     gw = GLYPHTEST(widget);
222
223     preferred->width = gw->line.width + 4 + HMARGINS(gw);
224     preferred->height = gw->line.height + 4 + VMARGINS(gw);
225 }
226
227 static void
228 glyphtest_actual_size(GtkWidget *widget, GtkAllocation *actual)
229 {
230     widget->allocation = *actual;
231
232     if (GTK_WIDGET_REALIZED(widget))
233       gdk_window_move_resize(widget->window, actual->x, actual->y,
234                              actual->width, actual->height);
235 }
236
237 static void
238 glyphtest_draw_focus(GtkWidget *widget, GdkRectangle *area)
239 {
240     GdkGC *gc;
241     gint x, y, wd, ht, fwidth, fpad;
242
243     /*
244      * Do something with this later to make sure the focus line width
245      * is set in the GC's.
246      */
247     gtk_widget_style_get(widget,
248                          "focus-line-width", &fwidth,
249                          "focus-padding", &fpad, NULL);
250
251     gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)];
252
253     x = (widget->style->xthickness + fwidth + fpad) - 1;
254     y = (widget->style->ythickness + fwidth + fpad) - 1;
255     wd = (widget->allocation.width - (x * 2));
256     ht = (widget->allocation.height - (y * 2));
257
258     if (GTK_WIDGET_HAS_FOCUS(widget))
259       gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(widget),
260                       area, widget, "glyphtest", x, y, wd, ht);
261     else {
262         gdk_gc_set_clip_rectangle(gc, area);
263         gdk_draw_rectangle(widget->window, gc, FALSE, x, y, wd - 1, ht - 1);
264         gdk_gc_set_clip_rectangle(gc, 0);
265     }
266 }
267
268 static void
269 _glyphtest_get_pixels(Glyphtest *gw, bdf_glyph_t *glyph, bdf_font_t *font,
270                       gint16 x, gint y)
271 {
272     gint byte;
273     guint16 i, j, bpr, si, di, nx;
274     guchar *masks;
275
276     di = 0;
277     masks = 0;
278     gw->image_used = 0;
279
280     switch (font->bpp) {
281       case 1: masks = bdf_onebpp; di = 7; break;
282       case 2: masks = bdf_twobpp; di = 3; break;
283       case 4: masks = bdf_fourbpp; di = 1; break;
284       case 8: masks = bdf_eightbpp; di = 0; break;
285     }
286
287     bpr = ((glyph->bbx.width * font->bpp) + 7) >> 3;
288     for (i = 0; i < glyph->bbx.height; i++) {
289         for (nx = j = 0; j < glyph->bbx.width; j++, nx += font->bpp) {
290             si = (nx & 7) / font->bpp;
291
292             byte = glyph->bitmap[(i * bpr) + (nx >> 3)] & masks[si];
293             if (di > si)
294               byte >>= (di - si) * font->bpp;
295
296             if (byte) {
297                 if (gw->image_used == gw->image_size) {
298                     if (gw->image_size == 0)
299                       gw->image =
300                           (GdkPoint *) g_malloc(sizeof(GdkPoint) * 64);
301                     else
302                       gw->image = (GdkPoint *)
303                           g_realloc(gw->image,
304                                     sizeof(GdkPoint) *
305                                     (gw->image_size + 64));;
306                     gw->image_size += 64;
307                 }
308                 gw->image[gw->image_used].x =
309                     x + glyph->bbx.x_offset + j;
310                 gw->image[gw->image_used].y =
311                     (y - glyph->bbx.ascent) + i;
312                 gw->image_used++;
313             }
314         }
315     }
316 }
317
318 static void
319 _glyphtest_draw_glyph(Glyphtest *gw, bdf_glyph_t *glyph, bdf_font_t *font)
320 {
321     GtkWidget *w;
322     gint16 rx, ry;
323
324     w = GTK_WIDGET(gw);
325
326     if (!GTK_WIDGET_REALIZED(w))
327       return;
328
329     ry = gw->line.cpoint.y;
330     rx = gw->line.cpoint.x;
331     if (gw->dir != GLYPHTEST_LEFT_TO_RIGHT)
332       rx -= glyph->bbx.width;
333
334     _glyphtest_get_pixels(gw, glyph, font, rx, ry);
335     if (gw->image_used > 0)
336       gdk_draw_points(w->window, w->style->fg_gc[GTK_STATE_NORMAL],
337                       gw->image, gw->image_used);
338 }
339
340 static void
341 _glyphtest_redraw_glyphs(Glyphtest *gw)
342 {
343     GtkWidget *w;
344     guint32 i;
345     guint16 dwidth;
346     GlyphtestLine *lp;
347     GlyphtestGlyph *gp;
348
349     w = GTK_WIDGET(gw);
350
351     if (!GTK_WIDGET_REALIZED(w))
352       return;
353
354     lp = &gw->line;
355
356     lp->width = 0;
357     if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT)
358       lp->cpoint.x = (HMARGINS(gw) >> 1) + 2;
359     else
360       lp->cpoint.x = w->allocation.width - ((HMARGINS(gw) >> 1) + 2);
361
362     for (i = 0, gp = lp->glyphs; i < lp->glyphs_used; i++, gp++) {
363
364         /*
365          * Handle the special cases of the first glyph in case the normal
366          * drawing position is going to put part of the glyph off the edge of
367          * the window.
368          */
369         if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT) {
370             if (i == 0 && gp->glyph->bbx.x_offset < 0)
371               lp->cpoint.x += -gp->glyph->bbx.x_offset;
372         } else {
373             if (i == 0 && gp->glyph->bbx.x_offset > 0 &&
374                 gp->glyph->bbx.x_offset > gp->glyph->bbx.width)
375               lp->cpoint.x -= gp->glyph->bbx.width - gp->glyph->bbx.x_offset;
376         }
377         _glyphtest_draw_glyph(gw, gp->glyph, gp->font);
378
379         dwidth = (gp->font->spacing == BDF_PROPORTIONAL) ?
380             gp->glyph->dwidth : gp->font->monowidth;
381         if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT)
382           lp->cpoint.x += dwidth;
383         else
384           lp->cpoint.x -= dwidth;
385         lp->width += dwidth;
386     }
387 }
388
389 static void
390 glyphtest_draw(GtkWidget *widget, GdkRectangle *area)
391 {
392     Glyphtest *gw;
393     GdkPoint s, e;
394     GdkRectangle clear;
395
396     if (!GTK_WIDGET_REALIZED(widget))
397       return;
398
399     gw = GLYPHTEST(widget);
400
401     /*
402      * Erase the window.
403      */
404     clear.x = clear.y = (HMARGINS(gw) >> 1);
405     clear.width = widget->allocation.width - (clear.x << 1);
406     clear.height = widget->allocation.height - (clear.y << 1);
407     gdk_window_clear_area(widget->window, clear.x, clear.y,
408                           clear.width, clear.height);
409
410     /*
411      * Redraw the glyphs.
412      */
413     _glyphtest_redraw_glyphs(gw);
414
415     /*
416      * Draw the baseline if indicated.
417      */
418     if (gw->show_baseline == TRUE) {
419         s.x = (HMARGINS(gw) >> 1) + 2;
420         e.x = widget->allocation.width - s.x;
421         s.y = e.y = gw->line.cpoint.y;
422
423         gdk_draw_line(widget->window,
424                       widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
425                       s.x, s.y, e.x, e.y);
426     }
427 }
428
429 static gboolean
430 glyphtest_expose(GtkWidget *widget, GdkEventExpose *event)
431 {
432     /*
433      * Paint the shadow first.
434      */
435     if (GTK_WIDGET_DRAWABLE(widget))
436       gtk_paint_shadow(widget->style, widget->window,
437                        GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
438                        &event->area,
439                        widget, "glyphtest",
440                        0, 0,
441                        widget->allocation.width,
442                        widget->allocation.height);
443
444     glyphtest_draw(widget, 0);
445
446     glyphtest_draw_focus(widget, &event->area);
447
448     return FALSE;
449 }
450
451 static void
452 glyphtest_realize(GtkWidget *widget)
453 {
454     Glyphtest *gw;
455     GdkWindowAttr attributes;
456     gint attributes_mask;
457
458     g_return_if_fail(widget != NULL);
459     g_return_if_fail(IS_GLYPHTEST(widget));
460
461     gw = GLYPHTEST(widget);
462     GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
463
464     attributes.window_type = GDK_WINDOW_CHILD;
465     attributes.x = widget->allocation.x;
466     attributes.y = widget->allocation.y;
467     attributes.width = widget->allocation.width;
468     attributes.height = widget->allocation.height;
469     attributes.wclass = GDK_INPUT_OUTPUT;
470     attributes.visual = gtk_widget_get_visual(widget);
471     attributes.colormap = gtk_widget_get_colormap(widget);
472     attributes.event_mask = gtk_widget_get_events(widget);
473     attributes.event_mask |= (GDK_EXPOSURE_MASK|GDK_ENTER_NOTIFY_MASK|
474                               GDK_LEAVE_NOTIFY_MASK|GDK_FOCUS_CHANGE_MASK);
475
476     attributes_mask = GDK_WA_X|GDK_WA_Y|GDK_WA_VISUAL|GDK_WA_COLORMAP;
477
478     widget->window = gdk_window_new(gtk_widget_get_parent_window(widget),
479                                     &attributes, attributes_mask);
480     gdk_window_set_user_data(widget->window, widget);
481
482     widget->style = gtk_style_attach(widget->style, widget->window);
483     gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
484 }
485
486 static void
487 glyphtest_unrealize(GtkWidget *widget)
488 {
489     Glyphtest *gw;
490
491     gw = GLYPHTEST(widget);
492 }
493
494 static gint
495 glyphtest_focus_in(GtkWidget *widget, GdkEventFocus *event)
496 {
497     g_return_val_if_fail(widget != NULL, FALSE);
498     g_return_val_if_fail(IS_GLYPHTEST(widget), FALSE);
499     g_return_val_if_fail(event != NULL, FALSE);
500
501     GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
502     (void) glyphtest_draw_focus(widget, NULL);
503
504     return FALSE;
505 }
506
507 static gint
508 glyphtest_focus_out(GtkWidget *widget, GdkEventFocus *event)
509 {
510     g_return_val_if_fail(widget != NULL, FALSE);
511     g_return_val_if_fail(IS_GLYPHTEST(widget), FALSE);
512     g_return_val_if_fail(event != NULL, FALSE);
513
514     GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
515     (void) glyphtest_draw_focus(widget, NULL);
516
517     return FALSE;
518 }
519
520 /**************************************************************************
521  *
522  * Class and object initialization routines.
523  *
524  **************************************************************************/
525
526 static void
527 glyphtest_class_init(gpointer g_class, gpointer class_data)
528 {
529     GObjectClass *gocp = G_OBJECT_CLASS(g_class);
530     GtkObjectClass *ocp = GTK_OBJECT_CLASS(g_class);
531     GtkWidgetClass *wcp = GTK_WIDGET_CLASS(g_class);
532
533     /*
534      * Set the class global variables.
535      */
536     parent_class = g_type_class_peek_parent(g_class);
537
538     ocp->destroy = glyphtest_destroy;
539     gocp->set_property = glyphtest_set_property;
540     gocp->get_property = glyphtest_get_property;
541     gocp->finalize = glyphtest_finalize;
542
543     /*
544      * Add argument (a.k.a. resource) types.
545      */
546     g_object_class_install_property(gocp, PROP_BASELINE,
547                                     g_param_spec_boolean("showBaseline",
548                                                          _("Show baseline"),
549                                                          _("Draw the baseline."),
550                                                          FALSE,
551                                                          G_PARAM_READWRITE));
552
553     g_object_class_install_property(gocp, PROP_DIRECTION,
554                                     g_param_spec_uint("direction",
555                                                       _("Direction"),
556                                                       _("Override for the drawing direction of the glyphs."),
557                                                       GLYPHTEST_LEFT_TO_RIGHT,
558                                                       GLYPHTEST_RIGHT_TO_LEFT,
559                                                       GLYPHTEST_LEFT_TO_RIGHT,
560                                                       G_PARAM_READWRITE));
561
562     /*
563      * Add the signals these objects emit.
564      */
565     glyphtest_signals[ADD_GLYPH] =
566         g_signal_new("add_glyph",
567                      G_TYPE_FROM_CLASS(gocp),
568                      G_SIGNAL_RUN_FIRST,
569                      G_STRUCT_OFFSET(GlyphtestClass, glyph_added),
570                      NULL, NULL,
571                      g_cclosure_marshal_VOID__VOID,
572                      G_TYPE_NONE, 0);
573
574     /*
575      * Set all the functions for handling events for objects of this class.
576      */
577     wcp->size_request = glyphtest_preferred_size;
578     wcp->size_allocate = glyphtest_actual_size;
579     wcp->realize = glyphtest_realize;
580     wcp->unrealize = glyphtest_unrealize;
581     wcp->expose_event = glyphtest_expose;
582     wcp->focus_in_event = glyphtest_focus_in;
583     wcp->focus_out_event = glyphtest_focus_out;
584 }
585
586 static void
587 glyphtest_init(GTypeInstance *obj, gpointer g_class)
588 {
589     Glyphtest *gw = GLYPHTEST(obj);
590     gint fwidth;
591
592     GTK_WIDGET_SET_FLAGS(gw, GTK_CAN_FOCUS);
593
594     (void) memset((char *) &gw->line, 0, sizeof(GlyphtestLine));
595
596     gw->dir = GLYPHTEST_LEFT_TO_RIGHT;
597     gw->show_baseline = TRUE;
598
599     gw->image_used = gw->image_size = 0;
600
601     /*
602      * Determine the space that will be needed for drawing the shadow and the
603      * focus rectangle.
604      */
605     gtk_widget_style_get(GTK_WIDGET(gw),
606                          "focus-line-width", &fwidth,
607                          NULL);
608
609     /*
610      * Padding that will appear before and after the focus rectangle.
611      * Hardcode this for now.
612      */
613     gw->focus_thickness = 3;
614     gw->hmargin =
615         gw->widget.style->xthickness + fwidth + (gw->focus_thickness * 2);
616     gw->vmargin = 
617         gw->widget.style->ythickness + fwidth + (gw->focus_thickness * 2);
618
619     /*
620      * Call the line size function to set the initial size.
621      */
622     (void) _glyphtest_set_line_size(gw);
623 }
624
625 /**************************************************************************
626  *
627  * API functions.
628  *
629  **************************************************************************/
630
631 static const GTypeInfo glyphtest_info = {
632     sizeof(GlyphtestClass),
633     NULL,
634     NULL,
635     glyphtest_class_init,
636     NULL,
637     NULL,
638     sizeof(Glyphtest),
639     0,
640     glyphtest_init,
641     NULL,
642 };
643
644 GType
645 glyphtest_get_type(void)
646 {
647     static GType glyphtest_type = 0;
648
649     if (!glyphtest_type)
650       glyphtest_type = g_type_register_static(GTK_TYPE_WIDGET,
651                                               "Glyphtest", &glyphtest_info, 0);
652
653     return glyphtest_type;
654 }
655
656 GtkWidget *
657 glyphtest_new(void)
658 {
659     return GTK_WIDGET(g_object_new(glyphtest_get_type(), NULL));
660 }
661
662 void
663 glyphtest_add_glyph(Glyphtest *gw, bdf_font_t *font, bdf_glyph_t *glyph)
664 {
665     GlyphtestLine *lp;
666     GlyphtestGlyph *gp;
667
668     g_return_if_fail(gw != 0);
669     g_return_if_fail(font != 0);
670     g_return_if_fail(glyph != 0);
671
672     lp = &gw->line;
673
674     if (lp->glyphs_used == lp->glyphs_size) {
675         if (lp->glyphs_size == 0)
676           lp->glyphs = (GlyphtestGlyph *)
677               g_malloc(sizeof(GlyphtestGlyph) << 3);
678         else
679           lp->glyphs = (GlyphtestGlyph *)
680               g_realloc(lp->glyphs,
681                         sizeof(GlyphtestGlyph) * (lp->glyphs_size + 8));
682         lp->glyphs_size += 8;
683     }
684     gp = lp->glyphs + lp->glyphs_used++;
685     gp->font = font;
686     gp->glyph = glyph;
687
688     if (_glyphtest_set_line_size(gw))
689       gtk_widget_queue_resize(GTK_WIDGET(gw));
690     else {
691         /*
692          * Just draw the glyph.
693          */
694         /*
695          * If the first glyph would be drawn off the edge of the window, make
696          * sure the initial position is adjusted to display the first glyph at
697          * the edge.
698          */
699         if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT) {
700             if (gw->line.glyphs_used == 1 && glyph->bbx.x_offset < 0)
701               lp->cpoint.x += -glyph->bbx.x_offset;
702         } else {
703             if (gw->line.glyphs_used == 1 && glyph->bbx.x_offset > 0 &&
704                 glyph->bbx.x_offset > glyph->bbx.width)
705               lp->cpoint.x -= glyph->bbx.width - glyph->bbx.x_offset;
706         }
707
708         _glyphtest_draw_glyph(gw, glyph, font);
709
710         if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT)
711           lp->cpoint.x += (font->spacing == BDF_PROPORTIONAL) ?
712               glyph->dwidth : font->monowidth;
713         else
714           lp->cpoint.x -= (font->spacing == BDF_PROPORTIONAL) ?
715               glyph->dwidth : font->monowidth;
716     }
717
718     /*
719      * Call the signal that indicates that a glyph has been added.
720      */
721     g_signal_emit(GTK_OBJECT(gw), glyphtest_signals[ADD_GLYPH], 0);
722 }
723
724 void
725 glyphtest_remove_font(Glyphtest *gw, bdf_font_t *font)
726 {
727     GtkWidget *w;
728     guint32 i, j;
729     gboolean redo;
730
731     g_return_if_fail(gw != 0);
732     g_return_if_fail(font != 0);
733
734     w = GTK_WIDGET(gw);
735
736     for (redo = FALSE, i = j = 0; i < gw->line.glyphs_used; i++) {
737         if (gw->line.glyphs[i].font != font) {
738             gw->line.glyphs[j].font = gw->line.glyphs[i].font;
739             gw->line.glyphs[j].glyph = gw->line.glyphs[i].glyph;
740             j++;
741         }
742     }
743     if (gw->line.glyphs_used != j) {
744         redo = TRUE;
745         gw->line.glyphs_used = j;
746     }
747
748     if (redo) {
749         if (_glyphtest_set_line_size(gw))
750           gtk_widget_queue_resize(w);
751         else
752           glyphtest_draw(w, 0);
753     }
754 }
755
756 void
757 glyphtest_update_device_width(Glyphtest *gw, bdf_font_t *font)
758 {
759     GtkWidget *w;
760     guint32 i;
761     gboolean redraw;
762
763     g_return_if_fail(gw != 0);
764
765     w = GTK_WIDGET(gw);
766
767     /*
768      * Determine if the device width change will cause a redraw.
769      */
770     redraw = FALSE;
771
772     for (i = 0; redraw == FALSE && i < gw->line.glyphs_used; i++) {
773         if (gw->line.glyphs[i].font == font)
774           redraw = TRUE;
775     }
776
777     if (redraw) {
778         /*
779          * Determine if a resize is in order or just a redraw.
780          */
781         if (_glyphtest_set_line_size(gw))
782           gtk_widget_queue_resize(w);
783         else
784           glyphtest_draw(w, 0);
785     }
786 }
787
788 void
789 glyphtest_change_direction(Glyphtest *gw, gint direction)
790 {
791     g_return_if_fail(gw != 0);
792
793     /*
794      * Return if the direction is invalid or the same as the current
795      * direction.
796      */
797     if (direction < GLYPHTEST_LEFT_TO_RIGHT ||
798         direction > GLYPHTEST_RIGHT_TO_LEFT ||
799         direction == gw->dir)
800       return;
801
802     gw->dir = direction;
803     glyphtest_draw(GTK_WIDGET(gw), 0);
804 }
805
806 void
807 glyphtest_show_baseline(Glyphtest *gw, gboolean baseline)
808 {
809     g_return_if_fail(gw != 0);
810
811     if (gw->show_baseline == baseline)
812       return;
813
814     gw->show_baseline = baseline;
815     glyphtest_draw(GTK_WIDGET(gw), 0);
816 }
817
818 void
819 glyphtest_erase(Glyphtest *gw)
820 {
821     g_return_if_fail(gw != 0);
822
823     /*
824      * May change later to shrink the widget.
825      */
826     gw->line.glyphs_used = 0;
827     glyphtest_draw(GTK_WIDGET(gw), 0);
828 }