2 * Copyright 2008 Department of Mathematical Sciences, New Mexico State University
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
23 #include "glyphtest.h"
27 #define _(s) dgettext(GETTEXT_PACKAGE,s)
32 #define HMARGINS(fw) ((fw)->hmargin << 1)
33 #define VMARGINS(fw) ((fw)->vmargin << 1)
49 static GtkWidgetClass *parent_class = 0;
50 static guint glyphtest_signals[NUM_SIGNALS];
52 #define GTESTMAX(h,i) ((h) > (i) ? (h) : (i))
55 _glyphtest_set_line_size(Glyphtest *gw)
58 gboolean changed = FALSE;
67 (void) memset((char *) &bbx, 0, sizeof(bdf_bbx_t));
69 wwidth = w->allocation.width - (HMARGINS(gw) + 4);
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;
79 if (lp->glyphs_used == 0) {
81 * If no glyphs are present, then set the overall bounding box
82 * to some simple default.
91 * If the actual line width changed, set the indicator.
93 if (wd != lp->width) {
97 * If the line width overflows the window width, set the changed flag.
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.
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));
114 * Now set the line size.
116 lp->height = lp->bbx.ascent + lp->bbx.descent;
117 lp->cpoint.y = (VMARGINS(gw) >> 1) + 2 + lp->bbx.ascent;
122 /**************************************************************************
126 **************************************************************************/
129 glyphtest_set_property(GObject *obj, guint prop_id, const GValue *value,
138 glyphtest_show_baseline(gw, g_value_get_boolean(value));
141 glyphtest_change_direction(gw, g_value_get_int(value));
147 glyphtest_get_property(GObject *obj, guint prop_id, GValue *value,
156 g_value_set_boolean(value, gw->show_baseline);
159 g_value_set_int(value, gw->dir);
165 glyphtest_destroy(GtkObject *obj)
170 * Do some checks to make sure the incoming object exists and is the right
173 g_return_if_fail(obj != 0);
174 g_return_if_fail(IS_GLYPHTEST(obj));
179 * Delete the line structure if it was allocated.
181 if (gw->line.glyphs_size > 0)
182 g_free(gw->line.glyphs);
183 gw->line.glyphs_used = gw->line.glyphs_size = 0;
186 * Free up any points that have been allocated.
188 if (gw->image_size > 0)
190 gw->image_size = gw->image_used = 0;
193 * Follow the class chain back up to free up resources allocated in the
196 GTK_OBJECT_CLASS(parent_class)->destroy(obj);
200 glyphtest_finalize(GObject *obj)
203 * Do some checks to make sure the incoming object exists and is the right
206 g_return_if_fail(obj != 0);
207 g_return_if_fail(IS_GLYPHTEST(obj));
210 * Follow the class chain back up to free up resources allocated in the
213 G_OBJECT_CLASS(parent_class)->finalize(obj);
217 glyphtest_preferred_size(GtkWidget *widget, GtkRequisition *preferred)
221 gw = GLYPHTEST(widget);
223 preferred->width = gw->line.width + 4 + HMARGINS(gw);
224 preferred->height = gw->line.height + 4 + VMARGINS(gw);
228 glyphtest_actual_size(GtkWidget *widget, GtkAllocation *actual)
230 widget->allocation = *actual;
232 if (GTK_WIDGET_REALIZED(widget))
233 gdk_window_move_resize(widget->window, actual->x, actual->y,
234 actual->width, actual->height);
238 glyphtest_draw_focus(GtkWidget *widget, GdkRectangle *area)
241 gint x, y, wd, ht, fwidth, fpad;
244 * Do something with this later to make sure the focus line width
245 * is set in the GC's.
247 gtk_widget_style_get(widget,
248 "focus-line-width", &fwidth,
249 "focus-padding", &fpad, NULL);
251 gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)];
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));
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);
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);
269 _glyphtest_get_pixels(Glyphtest *gw, bdf_glyph_t *glyph, bdf_font_t *font,
273 guint16 i, j, bpr, si, di, nx;
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;
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;
292 byte = glyph->bitmap[(i * bpr) + (nx >> 3)] & masks[si];
294 byte >>= (di - si) * font->bpp;
297 if (gw->image_used == gw->image_size) {
298 if (gw->image_size == 0)
300 (GdkPoint *) g_malloc(sizeof(GdkPoint) * 64);
302 gw->image = (GdkPoint *)
305 (gw->image_size + 64));;
306 gw->image_size += 64;
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;
319 _glyphtest_draw_glyph(Glyphtest *gw, bdf_glyph_t *glyph, bdf_font_t *font)
326 if (!GTK_WIDGET_REALIZED(w))
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;
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);
341 _glyphtest_redraw_glyphs(Glyphtest *gw)
351 if (!GTK_WIDGET_REALIZED(w))
357 if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT)
358 lp->cpoint.x = (HMARGINS(gw) >> 1) + 2;
360 lp->cpoint.x = w->allocation.width - ((HMARGINS(gw) >> 1) + 2);
362 for (i = 0, gp = lp->glyphs; i < lp->glyphs_used; i++, gp++) {
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
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;
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;
377 _glyphtest_draw_glyph(gw, gp->glyph, gp->font);
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;
384 lp->cpoint.x -= dwidth;
390 glyphtest_draw(GtkWidget *widget, GdkRectangle *area)
396 if (!GTK_WIDGET_REALIZED(widget))
399 gw = GLYPHTEST(widget);
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);
413 _glyphtest_redraw_glyphs(gw);
416 * Draw the baseline if indicated.
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;
423 gdk_draw_line(widget->window,
424 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
430 glyphtest_expose(GtkWidget *widget, GdkEventExpose *event)
433 * Paint the shadow first.
435 if (GTK_WIDGET_DRAWABLE(widget))
436 gtk_paint_shadow(widget->style, widget->window,
437 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
441 widget->allocation.width,
442 widget->allocation.height);
444 glyphtest_draw(widget, 0);
446 glyphtest_draw_focus(widget, &event->area);
452 glyphtest_realize(GtkWidget *widget)
455 GdkWindowAttr attributes;
456 gint attributes_mask;
458 g_return_if_fail(widget != NULL);
459 g_return_if_fail(IS_GLYPHTEST(widget));
461 gw = GLYPHTEST(widget);
462 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
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);
476 attributes_mask = GDK_WA_X|GDK_WA_Y|GDK_WA_VISUAL|GDK_WA_COLORMAP;
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);
482 widget->style = gtk_style_attach(widget->style, widget->window);
483 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
487 glyphtest_unrealize(GtkWidget *widget)
491 gw = GLYPHTEST(widget);
495 glyphtest_focus_in(GtkWidget *widget, GdkEventFocus *event)
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);
501 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
502 (void) glyphtest_draw_focus(widget, NULL);
508 glyphtest_focus_out(GtkWidget *widget, GdkEventFocus *event)
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);
514 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
515 (void) glyphtest_draw_focus(widget, NULL);
520 /**************************************************************************
522 * Class and object initialization routines.
524 **************************************************************************/
527 glyphtest_class_init(gpointer g_class, gpointer class_data)
529 GObjectClass *gocp = G_OBJECT_CLASS(g_class);
530 GtkObjectClass *ocp = GTK_OBJECT_CLASS(g_class);
531 GtkWidgetClass *wcp = GTK_WIDGET_CLASS(g_class);
534 * Set the class global variables.
536 parent_class = g_type_class_peek_parent(g_class);
538 ocp->destroy = glyphtest_destroy;
539 gocp->set_property = glyphtest_set_property;
540 gocp->get_property = glyphtest_get_property;
541 gocp->finalize = glyphtest_finalize;
544 * Add argument (a.k.a. resource) types.
546 g_object_class_install_property(gocp, PROP_BASELINE,
547 g_param_spec_boolean("showBaseline",
549 _("Draw the baseline."),
553 g_object_class_install_property(gocp, PROP_DIRECTION,
554 g_param_spec_uint("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,
563 * Add the signals these objects emit.
565 glyphtest_signals[ADD_GLYPH] =
566 g_signal_new("add_glyph",
567 G_TYPE_FROM_CLASS(gocp),
569 G_STRUCT_OFFSET(GlyphtestClass, glyph_added),
571 g_cclosure_marshal_VOID__VOID,
575 * Set all the functions for handling events for objects of this class.
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;
587 glyphtest_init(GTypeInstance *obj, gpointer g_class)
589 Glyphtest *gw = GLYPHTEST(obj);
592 GTK_WIDGET_SET_FLAGS(gw, GTK_CAN_FOCUS);
594 (void) memset((char *) &gw->line, 0, sizeof(GlyphtestLine));
596 gw->dir = GLYPHTEST_LEFT_TO_RIGHT;
597 gw->show_baseline = TRUE;
599 gw->image_used = gw->image_size = 0;
602 * Determine the space that will be needed for drawing the shadow and the
605 gtk_widget_style_get(GTK_WIDGET(gw),
606 "focus-line-width", &fwidth,
610 * Padding that will appear before and after the focus rectangle.
611 * Hardcode this for now.
613 gw->focus_thickness = 3;
615 gw->widget.style->xthickness + fwidth + (gw->focus_thickness * 2);
617 gw->widget.style->ythickness + fwidth + (gw->focus_thickness * 2);
620 * Call the line size function to set the initial size.
622 (void) _glyphtest_set_line_size(gw);
625 /**************************************************************************
629 **************************************************************************/
631 static const GTypeInfo glyphtest_info = {
632 sizeof(GlyphtestClass),
635 glyphtest_class_init,
645 glyphtest_get_type(void)
647 static GType glyphtest_type = 0;
650 glyphtest_type = g_type_register_static(GTK_TYPE_WIDGET,
651 "Glyphtest", &glyphtest_info, 0);
653 return glyphtest_type;
659 return GTK_WIDGET(g_object_new(glyphtest_get_type(), NULL));
663 glyphtest_add_glyph(Glyphtest *gw, bdf_font_t *font, bdf_glyph_t *glyph)
668 g_return_if_fail(gw != 0);
669 g_return_if_fail(font != 0);
670 g_return_if_fail(glyph != 0);
674 if (lp->glyphs_used == lp->glyphs_size) {
675 if (lp->glyphs_size == 0)
676 lp->glyphs = (GlyphtestGlyph *)
677 g_malloc(sizeof(GlyphtestGlyph) << 3);
679 lp->glyphs = (GlyphtestGlyph *)
680 g_realloc(lp->glyphs,
681 sizeof(GlyphtestGlyph) * (lp->glyphs_size + 8));
682 lp->glyphs_size += 8;
684 gp = lp->glyphs + lp->glyphs_used++;
688 if (_glyphtest_set_line_size(gw))
689 gtk_widget_queue_resize(GTK_WIDGET(gw));
692 * Just draw the glyph.
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
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;
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;
708 _glyphtest_draw_glyph(gw, glyph, font);
710 if (gw->dir == GLYPHTEST_LEFT_TO_RIGHT)
711 lp->cpoint.x += (font->spacing == BDF_PROPORTIONAL) ?
712 glyph->dwidth : font->monowidth;
714 lp->cpoint.x -= (font->spacing == BDF_PROPORTIONAL) ?
715 glyph->dwidth : font->monowidth;
719 * Call the signal that indicates that a glyph has been added.
721 g_signal_emit(GTK_OBJECT(gw), glyphtest_signals[ADD_GLYPH], 0);
725 glyphtest_remove_font(Glyphtest *gw, bdf_font_t *font)
731 g_return_if_fail(gw != 0);
732 g_return_if_fail(font != 0);
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;
743 if (gw->line.glyphs_used != j) {
745 gw->line.glyphs_used = j;
749 if (_glyphtest_set_line_size(gw))
750 gtk_widget_queue_resize(w);
752 glyphtest_draw(w, 0);
757 glyphtest_update_device_width(Glyphtest *gw, bdf_font_t *font)
763 g_return_if_fail(gw != 0);
768 * Determine if the device width change will cause a redraw.
772 for (i = 0; redraw == FALSE && i < gw->line.glyphs_used; i++) {
773 if (gw->line.glyphs[i].font == font)
779 * Determine if a resize is in order or just a redraw.
781 if (_glyphtest_set_line_size(gw))
782 gtk_widget_queue_resize(w);
784 glyphtest_draw(w, 0);
789 glyphtest_change_direction(Glyphtest *gw, gint direction)
791 g_return_if_fail(gw != 0);
794 * Return if the direction is invalid or the same as the current
797 if (direction < GLYPHTEST_LEFT_TO_RIGHT ||
798 direction > GLYPHTEST_RIGHT_TO_LEFT ||
799 direction == gw->dir)
803 glyphtest_draw(GTK_WIDGET(gw), 0);
807 glyphtest_show_baseline(Glyphtest *gw, gboolean baseline)
809 g_return_if_fail(gw != 0);
811 if (gw->show_baseline == baseline)
814 gw->show_baseline = baseline;
815 glyphtest_draw(GTK_WIDGET(gw), 0);
819 glyphtest_erase(Glyphtest *gw)
821 g_return_if_fail(gw != 0);
824 * May change later to shrink the widget.
826 gw->line.glyphs_used = 0;
827 glyphtest_draw(GTK_WIDGET(gw), 0);