/* * Copyright 2008 Department of Mathematical Sciences, New Mexico State University * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * DEPARTMENT OF MATHEMATICAL SCIENCES OR NEW MEXICO STATE UNIVERSITY BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "glyphedit.h" #include #include #include #ifdef HAVE_XLIB #include #endif #ifdef ENABLE_NLS #include #define _(s) dgettext(GETTEXT_PACKAGE,s) #else #define _(s) (s) #endif /* * Each pixel will be displayed by a square with this number of pixels * on each side. */ #define MIN_PIXEL_SIZE 2 #define MAX_PIXEL_SIZE 20 #define DEFAULT_PIXEL_SIZE 10 #define HMARGINS(gw) ((gw)->hmargin << 1) #define VMARGINS(gw) ((gw)->vmargin << 1) /* * Crosshair cursor. */ static const gchar *cross_xpm[] = { "13 13 2 1", " c None", ". c #000000", " . ", " . ", " . ", " . ", " . ", " ", "..... .....", " ", " . ", " . ", " . ", " . ", " . " }; /* * Macros that represent the properties used by this type of object. */ #define GLYPHEDIT_CLIPBOARD gdk_atom_intern("GLYPHEDIT_CLIPBOARD", FALSE) #define GLYPHEDIT_BDF_CHAR gdk_atom_intern("GLYPHEDIT_BDF_CHAR", FALSE) #define GLYPHEDIT_BITMAP gdk_atom_intern("GLYPHEDIT_BITMAP", FALSE) #define GLYPHEDIT_GLYPH gdk_atom_intern("GLYPHEDIT_GLYPH", FALSE) /* * Set default values. */ /* * Enums used for identifying properties. */ enum { PROP_0 = 0, GLYPH_GRID, BASELINE_COLOR, SELECTION_COLOR, CURSOR_COLOR, PIXEL_SIZE, SHOW_X_HEIGHT, SHOW_CAP_HEIGHT, COLOR_LIST, OPERATION }; /* * The list of signals emitted by these objects. */ enum { GLYPH_MODIFIED = 0, POINTER_MOVED, OPERATION_CHANGE, COLOR_CHANGE }; /************************************************************************** * * Local variables. * **************************************************************************/ static GtkWidgetClass *parent_class = 0; static guint glyphedit_signals[COLOR_CHANGE + 1]; /************************************************************************** * * Class member functions. * **************************************************************************/ static void glyphedit_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { GtkWidget *widget; Glyphedit *gw; widget = GTK_WIDGET(obj); gw = GLYPHEDIT(obj); switch (prop_id) { case GLYPH_GRID: glyphedit_set_grid(gw, (bdf_glyph_grid_t *) g_value_get_pointer(value)); break; case BASELINE_COLOR: break; case SELECTION_COLOR: break; case CURSOR_COLOR: break; case PIXEL_SIZE: glyphedit_set_pixel_size(gw, g_value_get_uint(value)); break; case SHOW_X_HEIGHT: glyphedit_set_show_x_height(gw, g_value_get_boolean(value)); break; case SHOW_CAP_HEIGHT: glyphedit_set_show_cap_height(gw, g_value_get_boolean(value)); break; case COLOR_LIST: gw->colors = (guint16 *) g_value_get_pointer(value); gtk_widget_queue_draw(widget); break; case OPERATION: glyphedit_set_operation(gw, (GlypheditOperation) g_value_get_uint(value)); break; } } static void glyphedit_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { GtkWidget *widget; Glyphedit *gw; widget = GTK_WIDGET(obj); gw = GLYPHEDIT(obj); switch (prop_id) { case GLYPH_GRID: g_value_set_pointer(value, gw->grid); break; case BASELINE_COLOR: break; case SELECTION_COLOR: break; case CURSOR_COLOR: break; case PIXEL_SIZE: g_value_set_uint(value, gw->pixel_size); break; case SHOW_X_HEIGHT: g_value_set_boolean(value, gw->show_x_height); break; case SHOW_CAP_HEIGHT: g_value_set_boolean(value, gw->show_cap_height); break; case COLOR_LIST: g_value_set_pointer(value, gw->colors); break; case OPERATION: g_value_set_uint(value, (guint) gw->op); break; } } static void glyphedit_destroy(GtkObject *obj) { Glyphedit *gw; GlypheditClass *gwc; /* * Do some checks to make sure the incoming object exists and is the right * kind. */ g_return_if_fail(obj != 0); g_return_if_fail(IS_GLYPHEDIT(obj)); gw = GLYPHEDIT(obj); gwc = GLYPHEDIT_GET_CLASS(obj); /* * Unreference objects used class-wide so they get deallocated properly * when no longer used. The unreference only needs to happen the first * time since the objects are created at class initialization time. */ if (gwc->cursor != 0) gdk_cursor_unref(gwc->cursor); if (gwc->gridgc != 0) g_object_unref(G_OBJECT(gwc->gridgc)); if (gwc->bbxgc != 0) g_object_unref(G_OBJECT(gwc->bbxgc)); if (gwc->pixgc != 0) g_object_unref(G_OBJECT(gwc->pixgc)); if (gwc->selgc != 0) g_object_unref(G_OBJECT(gwc->selgc)); /* * Free up any colors allocated. */ if (gw->baselineColor.pixel != 0) gdk_colormap_free_colors(gw->widget.style->colormap, &gw->baselineColor, 1); gwc->cursor = 0; gwc->gridgc = gwc->bbxgc = gwc->pixgc = gwc->selgc = 0; /* * Free up the grid info. */ if (gw->grid != 0) { bdf_free_glyph_grid(gw->grid); gw->grid = 0; } if (gw->spot_size > 0) { g_free(gw->spot); gw->spot_size = gw->spot_used = 0; } GTK_OBJECT_CLASS(parent_class)->destroy(obj); } static void glyphedit_finalize(GObject *obj) { /* * Do some checks to make sure the incoming object exists and is the right * kind. */ g_return_if_fail(obj != 0); g_return_if_fail(IS_GLYPHEDIT(obj)); /* * Follow the class chain back up to free up resources allocated in the * parent classes. */ G_OBJECT_CLASS(parent_class)->finalize(obj); } static void glyphedit_preferred_size(GtkWidget *widget, GtkRequisition *preferred) { Glyphedit *gw; GdkScreen *screen; guint16 dht, margin; gw = GLYPHEDIT(widget); screen = gdk_display_get_default_screen(gdk_display_get_default()); dht = gdk_screen_get_height(screen); /* * This little bit of code quietly forces the glyph grid to be * at most 1/2 the height of the screen being used to help avoid taking * up too much space on the desktop. */ margin = VMARGINS(gw); preferred->height = margin + ((gw->pixel_size + 4) * gw->grid->grid_height); if (preferred->height > (dht >> 1)) { while (gw->pixel_size > 2) { preferred->height = margin + ((gw->pixel_size + 4) * gw->grid->grid_height); if (preferred->height < (dht >> 1)) break; gw->pixel_size--; } } preferred->width = HMARGINS(gw) + ((gw->pixel_size + 4) * gw->grid->grid_width); } static void glyphedit_actual_size(GtkWidget *widget, GtkAllocation *actual) { widget->allocation = *actual; if (GTK_WIDGET_REALIZED(widget)) gdk_window_move_resize(widget->window, actual->x, actual->y, actual->width, actual->height); } static void glyphedit_draw_focus(GtkWidget *widget, GdkRectangle *area) { GdkGC *gc; gint x, y, wd, ht, fwidth, fpad; /* * Do something with this later to make sure the focus line width * is set in the GC's. */ gtk_widget_style_get(widget, "focus-line-width", &fwidth, "focus-padding", &fpad, NULL); gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)]; x = (widget->style->xthickness + fwidth + fpad) - 1; y = (widget->style->ythickness + fwidth + fpad) - 1; wd = (widget->allocation.width - (x * 2)); ht = (widget->allocation.height - (y * 2)); if (GTK_WIDGET_HAS_FOCUS(widget)) gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(widget), area, widget, "glyphedit", x, y, wd, ht); else { gdk_gc_set_clip_rectangle(gc, area); gdk_draw_rectangle(widget->window, gc, FALSE, x, y, wd - 1, ht - 1); gdk_gc_set_clip_rectangle(gc, 0); } } static void glyphedit_draw_pixel(Glyphedit *gw, gint16 x, gint16 y, gboolean sel) { GtkWidget *w = GTK_WIDGET(gw); GlypheditClass *gwc; gint16 bpr, set, dx, dy, di, si; guchar *masks, *bmap; GdkRectangle pix; if (!GTK_WIDGET_REALIZED(w) || gw->grid == 0) return; gwc = GLYPHEDIT_GET_CLASS(gw); di = 0; masks = 0; switch (gw->grid->bpp) { case 1: masks = bdf_onebpp; di = 7; break; case 2: masks = bdf_twobpp; di = 3; break; case 4: masks = bdf_fourbpp; di = 1; break; case 8: masks = bdf_eightbpp; di = 0; break; } dx = (gw->pixel_size + 4) * gw->grid->grid_width; dy = (gw->pixel_size + 4) * gw->grid->grid_height; pix.x = (gw->widget.allocation.width >> 1) - (dx >> 1) + ((gw->pixel_size + 4) * x) + 2; pix.y = (gw->widget.allocation.height >> 1) - (dy >> 1) + ((gw->pixel_size + 4) * y) + 2; pix.width = pix.height = gw->pixel_size + 1; if (sel == TRUE && gw->grid->sel.width != 0) { bpr = ((gw->grid->sel.width * gw->grid->bpp) + 7) >> 3; dy = y - gw->grid->sel.y; dx = (x - gw->grid->sel.x) * gw->grid->bpp; bmap = gw->grid->sel.bitmap; } else { bpr = ((gw->grid->grid_width * gw->grid->bpp) + 7) >> 3; dy = y; dx = x * gw->grid->bpp; bmap = gw->grid->bitmap; } si = (dx & 7) / gw->grid->bpp; set = bmap[(dy * bpr) + (dx >> 3)] & masks[si]; if (di > si) set >>= (di - si) * gw->grid->bpp; if (set) { if (gw->grid->bpp > 1) { switch (gw->grid->bpp) { case 2: memset(gw->spot, gw->colors[set-1], gw->spot_used); break; case 4: memset(gw->spot, gw->colors[set-1+4], gw->spot_used); break; case 8: memset(gw->spot, set, gw->spot_used); break; } gdk_draw_gray_image(GTK_WIDGET(gw)->window, gwc->pixgc, pix.x, pix.y, pix.width, pix.height, GDK_RGB_DITHER_NONE, gw->spot, pix.width); } else gdk_draw_rectangle(GTK_WIDGET(gw)->window, gwc->pixgc, TRUE, pix.x, pix.y, pix.width, pix.height); } else gdk_window_clear_area(GTK_WIDGET(gw)->window, pix.x, pix.y, pix.width, pix.height); if (sel == TRUE) gdk_draw_rectangle(GTK_WIDGET(gw)->window, gwc->selgc, TRUE, pix.x + 1, pix.y + 1, pix.width - 2, pix.height - 2); } static void glyphedit_draw_glyph(Glyphedit *gw) { GtkWidget *w = GTK_WIDGET(gw); gint16 x, y; gboolean sel; if (!GTK_WIDGET_REALIZED(w) || gw->grid == 0) return; for (y = 0; y < gw->grid->grid_height; y++) { for (x = 0; x < gw->grid->grid_width; x++) { sel = (bdf_in_selection(gw->grid, x, y, 0) ? TRUE : FALSE); glyphedit_draw_pixel(gw, x, y, sel); } } } static void glyphedit_draw_font_bbx(Glyphedit *gw) { GtkWidget *w = GTK_WIDGET(gw); GlypheditClass *gwc; gint16 xoff, yoff, fxoff, fyoff, psize; GdkRectangle frame; if (!GTK_WIDGET_REALIZED(w)) return; gwc = GLYPHEDIT_GET_CLASS(gw); psize = gw->pixel_size + 4; frame.width = psize * gw->grid->font_bbx.width; frame.height = psize * (gw->grid->font_bbx.ascent + gw->grid->font_bbx.descent); fxoff = psize * gw->grid->grid_width; fyoff = psize * gw->grid->grid_height; frame.x = (gw->widget.allocation.width >> 1) - (fxoff >> 1); frame.y = (gw->widget.allocation.height >> 1) - (fyoff >> 1); if (gw->grid->font_bbx.x_offset < 0) fxoff = psize * (gw->grid->base_x + gw->grid->font_bbx.x_offset); else fxoff = psize * gw->grid->base_x; fyoff = psize * (gw->grid->base_y - gw->grid->font_bbx.ascent); /* * Due to some odd behavior, the box has to be drawn with the y point off * by one because the top of the rectangle does not get drawn otherwise. * Even calling gdk_draw_line() specifically doesn't work. * * This may have been fixed in later versions of GDK. */ gdk_draw_rectangle(GTK_WIDGET(gw)->window, gwc->bbxgc, FALSE, frame.x + fxoff, frame.y + fyoff + 1, frame.width, frame.height); /* * Draw vertical baseline. */ xoff = (gw->pixel_size + 4) * gw->grid->base_x; yoff = (gw->pixel_size + 4) * gw->grid->base_y; gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc, frame.x + xoff, frame.y + fyoff, frame.x + xoff, frame.y + fyoff + frame.height); /* * Draw horizontal baseline. */ gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc, frame.x + fxoff, frame.y + yoff, frame.x + fxoff + frame.width, frame.y + yoff); /* * Draw the CAP_HEIGHT if indicated and exists. */ if (gw->grid && gw->grid->cap_height != 0) { yoff = (gw->pixel_size + 4) * (gw->grid->base_y - gw->grid->cap_height); if (gw->show_cap_height == TRUE) gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc, frame.x + fxoff, frame.y + yoff, frame.x + fxoff + frame.width, frame.y + yoff); else { gdk_window_clear_area(GTK_WIDGET(gw)->window, frame.x + fxoff, frame.y + yoff, frame.width, 1); gdk_draw_line(GTK_WIDGET(gw)->window, gwc->gridgc, frame.x + fxoff, frame.y + yoff, frame.x + fxoff + frame.width, frame.y + yoff); } } /* * Draw the X_HEIGHT if indicated and exists. */ if (gw->grid && gw->grid->x_height != 0) { yoff = (gw->pixel_size + 4) * (gw->grid->base_y - gw->grid->x_height); if (gw->show_x_height == TRUE) gdk_draw_line(GTK_WIDGET(gw)->window, gwc->bbxgc, frame.x + fxoff, frame.y + yoff, frame.x + fxoff + frame.width, frame.y + yoff); else { gdk_window_clear_area(GTK_WIDGET(gw)->window, frame.x + fxoff, frame.y + yoff, frame.width, 1); gdk_draw_line(GTK_WIDGET(gw)->window, gwc->gridgc, frame.x + fxoff, frame.y + yoff, frame.x + fxoff + frame.width, frame.y + yoff); } } } static void glyphedit_draw(GtkWidget *widget, GdkRegion *region) { Glyphedit *gw; gint x, y, limit, unit, wd, ht; GlypheditClass *gwc; GdkRectangle frame; g_return_if_fail(widget != NULL); g_return_if_fail(IS_GLYPHEDIT(widget)); gw = GLYPHEDIT(widget); gwc = GLYPHEDIT_GET_CLASS(widget); wd = gw->grid->grid_width; ht = gw->grid->grid_height; frame.width = (gw->pixel_size + 4) * wd; frame.height = (gw->pixel_size + 4) * ht; /* * Adjust the frame horizontal and vertical positions so it * always appears centered on the window. */ frame.x = (widget->allocation.width >> 1) - (frame.width >> 1); frame.y = (widget->allocation.height >> 1) - (frame.height >> 1); /* * Limit the drawing area to the clip region. */ if (region != 0) gdk_gc_set_clip_region(gwc->gridgc, region); /* * Draw the outside frame. */ gdk_draw_rectangle(widget->window, gwc->gridgc, FALSE, frame.x, frame.y, frame.width, frame.height); /* * Draw the vertical grid lines. */ limit = frame.x + frame.width; unit = gw->pixel_size + 4; for (x = frame.x + unit, y = frame.y; x < limit; x += unit) gdk_draw_line(widget->window, gwc->gridgc, x, y, x, y + frame.height); /* * Draw the horizontal grid lines. */ limit = frame.y + frame.height; for (x = frame.x, y = frame.y + unit; y < limit; y += unit) gdk_draw_line(widget->window, gwc->gridgc, x, y, x + frame.width, y); if (region != 0) gdk_gc_set_clip_region(gwc->gridgc, 0); glyphedit_draw_font_bbx(gw); glyphedit_draw_glyph(gw); } static void glyphedit_create_gcs(GtkWidget *widget, gboolean force) { Glyphedit *gw; GlypheditClass *gwc; GdkGCValuesMask gcm; GdkGCValues gcv; gint8 dashes[2] = {1, 1}; gw = GLYPHEDIT(widget); gwc = GLYPHEDIT_GET_CLASS(G_OBJECT(widget)); gcm = GDK_GC_FOREGROUND|GDK_GC_BACKGROUND|GDK_GC_FUNCTION; if (gwc->gridgc == 0 || force == TRUE) { if (gwc->gridgc != 0) g_object_unref(G_OBJECT(gwc->gridgc)); gcv.foreground.pixel = widget->style->fg[GTK_WIDGET_STATE(widget)].pixel; gcv.background.pixel = widget->style->bg[GTK_WIDGET_STATE(widget)].pixel; gcv.function = GDK_COPY; gcv.line_style = GDK_LINE_ON_OFF_DASH; gwc->gridgc = gdk_gc_new_with_values(widget->window, &gcv, gcm|GDK_GC_LINE_STYLE); /* * Now set the dash lengths since they can't be set in the GC values. */ gdk_gc_set_dashes(gwc->gridgc, 0, dashes, 2); } if (gwc->bbxgc == 0 || force == TRUE) { if (gwc->bbxgc != 0) g_object_unref(G_OBJECT(gwc->bbxgc)); if (gw->baselineColor.pixel == 0) /* * Default to red. */ gdk_colormap_alloc_color(gw->widget.style->colormap, &gw->baselineColor, FALSE, TRUE); gcv.foreground.pixel = gw->baselineColor.pixel; gcv.function = GDK_COPY; gwc->bbxgc = gdk_gc_new_with_values(widget->window, &gcv, GDK_GC_FOREGROUND|GDK_GC_FUNCTION); } if (gwc->selgc == 0 || force == TRUE) { if (gwc->selgc != 0) g_object_unref(G_OBJECT(gwc->selgc)); gcv.foreground.pixel = widget->style->fg[GTK_WIDGET_STATE(widget)].pixel; gcv.background.pixel = widget->style->bg[GTK_WIDGET_STATE(widget)].pixel; gcv.foreground.pixel ^= gcv.background.pixel; gcv.function = GDK_XOR; gwc->selgc = gdk_gc_new_with_values(widget->window, &gcv, gcm); } if (gwc->pixgc == 0 || force == TRUE) { if (gwc->pixgc != 0) g_object_unref(G_OBJECT(gwc->pixgc)); gcv.foreground.pixel = widget->style->fg[GTK_WIDGET_STATE(widget)].pixel; gcv.background.pixel = widget->style->bg[GTK_WIDGET_STATE(widget)].pixel; gcv.function = GDK_COPY; gwc->pixgc = gdk_gc_new_with_values(widget->window, &gcv, gcm); } } static void glyphedit_realize(GtkWidget *widget) { Glyphedit *gw; GlypheditClass *gwc; GdkWindowAttr attributes; gint attributes_mask; GdkPixbuf *cb; g_return_if_fail(widget != NULL); g_return_if_fail(IS_GLYPHEDIT(widget)); gwc = GLYPHEDIT_GET_CLASS(widget); gw = GLYPHEDIT(widget); GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); attributes.window_type = GDK_WINDOW_CHILD; attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual(widget); attributes.colormap = gtk_widget_get_colormap(widget); attributes.event_mask = gtk_widget_get_events(widget); attributes.event_mask |= (GDK_EXPOSURE_MASK|GDK_BUTTON_PRESS_MASK| GDK_BUTTON_RELEASE_MASK|GDK_ENTER_NOTIFY_MASK| GDK_POINTER_MOTION_MASK| GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK| GDK_LEAVE_NOTIFY_MASK|GDK_FOCUS_CHANGE_MASK| GDK_PROPERTY_CHANGE_MASK); attributes_mask = GDK_WA_X|GDK_WA_Y|GDK_WA_VISUAL|GDK_WA_COLORMAP; widget->window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask); gdk_window_set_user_data(widget->window, widget); widget->style = gtk_style_attach(widget->style, widget->window); gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL); /* * Create the crosshair cursor. */ if (gwc->cursor == 0) { gwc = GLYPHEDIT_GET_CLASS(widget); cb = gdk_pixbuf_new_from_xpm_data(cross_xpm); gwc->cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), cb, 7, 7); g_object_unref(G_OBJECT(cb)); } glyphedit_create_gcs(widget, FALSE); gdk_window_set_cursor(widget->window, gwc->cursor); } static gboolean glyphedit_expose(GtkWidget *widget, GdkEventExpose *event) { /* * Paint the shadow first. */ if (GTK_WIDGET_DRAWABLE(widget)) gtk_paint_shadow(widget->style, widget->window, GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT, &event->area, widget, "glyphedit", 0, 0, widget->allocation.width, widget->allocation.height); glyphedit_draw(widget, event->region); glyphedit_draw_focus(widget, &event->area); return FALSE; } static gint glyphedit_focus_in(GtkWidget *widget, GdkEventFocus *event) { g_return_val_if_fail(widget != NULL, FALSE); g_return_val_if_fail(IS_GLYPHEDIT(widget), FALSE); g_return_val_if_fail(event != NULL, FALSE); GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); glyphedit_draw_focus(widget, 0); return FALSE; } static gint glyphedit_focus_out(GtkWidget *widget, GdkEventFocus *event) { g_return_val_if_fail(widget != NULL, FALSE); g_return_val_if_fail(IS_GLYPHEDIT(widget), FALSE); g_return_val_if_fail(event != NULL, FALSE); GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); glyphedit_draw_focus(widget, 0); return FALSE; } /************************************************************************** * * Class and object initialization routines. * **************************************************************************/ static GType glyphedit_get_operation_type(void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { {GLYPHEDIT_NONE, "GLYPHEDIT_NONE", "none"}, {GLYPHEDIT_SELECT, "GLYPHEDIT_SELECT", "select"}, {GLYPHEDIT_DRAW, "GLYPHEDIT_DRAW", "draw"}, {GLYPHEDIT_MOVE, "GLYPHEDIT_MOVE", "move"}, {GLYPHEDIT_COPY, "GLYPHEDIT_COPY", "copy"}, {GLYPHEDIT_FLIP_HORIZONTAL, "GLYPHEDIT_FLIP_HORIZONTAL", "flip-horizontal"}, {GLYPHEDIT_FLIP_VERTICAL, "GLYPHEDIT_FLIP_VERTICAL", "flip-verticalal"}, {GLYPHEDIT_SHEAR, "GLYPHEDIT_SHEAR", "shear"}, {GLYPHEDIT_ROTATE_LEFT, "GLYPHEDIT_ROTATE_LEFT", "rotate-left"}, {GLYPHEDIT_ROTATE_RIGHT, "GLYPHEDIT_ROTATE_RIGHT", "rotate-right"}, {GLYPHEDIT_ROTATE, "GLYPHEDIT_ROTATE", "rotate"}, {GLYPHEDIT_SHIFT_UP_LEFT, "GLYPHEDIT_SHIFT_UP_LEFT", "shift-up-left"}, {GLYPHEDIT_SHIFT_UP, "GLYPHEDIT_SHIFT_UP", "shift-up"}, {GLYPHEDIT_SHIFT_UP_RIGHT, "GLYPHEDIT_SHIFT_UP_RIGHT", "shift-up-right"}, {GLYPHEDIT_SHIFT_LEFT, "GLYPHEDIT_SHIFT_LEFT", "shift-left"}, {GLYPHEDIT_SHIFT_RIGHT, "GLYPHEDIT_SHIFT_RIGHT", "shift-right"}, {GLYPHEDIT_SHIFT_DOWN_LEFT, "GLYPHEDIT_SHIFT_DOWN_LEFT", "shift-down-left"}, {GLYPHEDIT_SHIFT_DOWN, "GLYPHEDIT_SHIFT_DOWN", "shift-down"}, {GLYPHEDIT_SHIFT_DOWN_RIGHT, "GLYPHEDIT_SHIFT_DOWN_RIGHT", "shift-down-right"}, {0, 0, 0} }; etype = g_enum_register_static("GlypheditOperation", values); } return etype; } static void glyphedit_init(GTypeInstance *obj, gpointer g_class) { Glyphedit *gw = GLYPHEDIT(obj); GlypheditClass *gwc = GLYPHEDIT_CLASS(g_class); gint fwidth, fpad; GTK_WIDGET_SET_FLAGS(gw, GTK_CAN_FOCUS); gwc->gridgc = gwc->bbxgc = gwc->pixgc = gwc->selgc = 0; gw->default_pixel_size = gw->pixel_size = DEFAULT_PIXEL_SIZE; /* * Make sure the spot is the right size. */ fpad = (gw->pixel_size + 1) * (gw->pixel_size + 1); if (gw->spot_size < fpad) { if (gw->spot_size == 0) gw->spot = g_malloc(fpad); else gw->spot = g_realloc(gw->spot, fpad); gw->spot_size = fpad; } gw->spot_used = fpad; gw->owns_clipboard = FALSE; gw->grid = 0; gw->last_x = gw->last_y = 0; memset((char *) &gw->sel_start, 0, sizeof(GdkPoint)); memset((char *) &gw->sel_end, 0, sizeof(GdkPoint)); /* * Always initialize to the first color. */ gw->cidx = 1; /* * Initialize the last color seen. */ gw->lcolor = 0; gtk_widget_style_get(GTK_WIDGET(gw), "focus-line-width", &fwidth, "focus-padding", &fpad, NULL); /* * Padding that will appear before and after the focus rectangle. * Hardcode this for now. */ gw->border = 4; gw->hmargin = gw->widget.style->xthickness + fwidth + fpad + gw->border; gw->vmargin = gw->widget.style->ythickness + fwidth + fpad + gw->border; gw->baselineColor.pixel = gw->selectionColor.pixel = gw->cursorColor.pixel = 0; gw->baselineColor.red = 0xffff; gw->baselineColor.green = gw->baselineColor.blue = 0; gw->op = GLYPHEDIT_DRAW; } /* * A convenience function for calling the GLYPH_MODIFIED signal because * so many functions depend on it. */ static void glyphedit_signal_glyph_change(Glyphedit *gw) { bdf_bitmap_t image; bdf_metrics_t metrics; GlypheditSignalInfo si; if (gw->grid == 0) return; glyphedit_get_glyph_metrics(gw, &metrics); bdf_grid_image(gw->grid, &image); si.reason = GLYPHEDIT_GLYPH_MODIFIED; si.metrics = &metrics; si.image = ℑ si.color = gw->cidx; g_signal_emit(G_OBJECT(gw), glyphedit_signals[GLYPH_MODIFIED], 0, &si); if (image.bytes > 0) free(image.bitmap); } /************************************************************************** * * API functions. * **************************************************************************/ GtkWidget * glyphedit_new(const gchar *prop1, ...) { GtkWidget *w; va_list var_args; va_start(var_args, prop1); w = GTK_WIDGET(g_object_new_valist(glyphedit_get_type(), prop1, var_args)); va_end(var_args); return w; } GtkWidget * glyphedit_newv(bdf_glyph_grid_t *grid, guint16 default_pixel_size, gboolean show_x_height, gboolean show_cap_height, guint16 *colors) { Glyphedit *ge = g_object_new(glyphedit_get_type(), "glyphGrid", grid, "pixelSize", default_pixel_size, "showXHeight", show_x_height, "showCapHeight", show_cap_height, "colorList", colors, NULL); return GTK_WIDGET(ge); } gint32 glyphedit_get_encoding(Glyphedit *gw) { g_return_val_if_fail(gw != NULL, -1); g_return_val_if_fail(IS_GLYPHEDIT(gw), -1); return (gw->grid) ? gw->grid->encoding : -1; } void glyphedit_get_glyph_metrics(Glyphedit *gw, bdf_metrics_t *metrics) { g_return_if_fail(gw != NULL); g_return_if_fail(metrics != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (!gw->grid) memset(metrics, 0, sizeof(bdf_metrics_t)); else { metrics->font_spacing = gw->grid->spacing; metrics->swidth = gw->grid->swidth; metrics->dwidth = gw->grid->dwidth; metrics->width = gw->grid->glyph_bbx.width; metrics->height = gw->grid->glyph_bbx.height; metrics->x_offset = gw->grid->glyph_bbx.x_offset; metrics->y_offset = gw->grid->glyph_bbx.y_offset; metrics->ascent = gw->grid->glyph_bbx.ascent; metrics->descent = gw->grid->glyph_bbx.descent; } } void glyphedit_get_font_metrics(Glyphedit *gw, bdf_metrics_t *metrics) { g_return_if_fail(gw != NULL); g_return_if_fail(metrics != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (!gw->grid) memset(metrics, 0, sizeof(bdf_metrics_t)); else { metrics->font_spacing = gw->grid->spacing; metrics->swidth = gw->grid->swidth; metrics->dwidth = gw->grid->dwidth; metrics->width = gw->grid->font_bbx.width; metrics->height = gw->grid->font_bbx.height; metrics->x_offset = gw->grid->font_bbx.x_offset; metrics->y_offset = gw->grid->font_bbx.y_offset; metrics->ascent = gw->grid->font_bbx.ascent; metrics->descent = gw->grid->font_bbx.descent; } } bdf_psf_unimap_t * glyphedit_get_psf_mappings(Glyphedit *gw) { g_return_val_if_fail(gw != NULL, 0); g_return_val_if_fail(IS_GLYPHEDIT(gw), 0); return (gw->grid) ? &gw->grid->unicode : 0; } /* * Can set both font and glyph metrics. */ void glyphedit_set_metrics(Glyphedit *gw, bdf_metrics_t *metrics) { GtkWidget *w = GTK_WIDGET(gw); g_return_if_fail(gw != NULL); g_return_if_fail(metrics != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (gw->grid == 0) return; if (bdf_grid_resize(gw->grid, metrics)) { glyphedit_signal_glyph_change(gw); gtk_widget_queue_resize(GTK_WIDGET(gw)); } else if (GTK_WIDGET_REALIZED(w)) /* * The size didn't change, but we need to redraw if the widget * has been realized. */ gtk_widget_queue_draw(GTK_WIDGET(gw)); } gint glyphedit_get_spacing(Glyphedit *gw) { g_return_val_if_fail(gw != NULL, -1); g_return_val_if_fail(IS_GLYPHEDIT(gw), -1); g_return_val_if_fail(gw->grid != NULL, -1); return gw->grid->spacing; } void glyphedit_set_spacing(Glyphedit *gw, gint spacing, guint16 monowidth) { bdf_metrics_t metrics; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); g_return_if_fail(gw->grid != NULL); gw->grid->spacing = spacing; if (spacing != BDF_PROPORTIONAL) { glyphedit_get_font_metrics(gw, &metrics); metrics.dwidth = metrics.width = monowidth; glyphedit_set_metrics(gw, &metrics); } } void glyphedit_set_grid(Glyphedit *gw, bdf_glyph_grid_t *grid) { g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); bdf_free_glyph_grid(gw->grid); gw->grid = grid; if (grid) { gw->last_x = grid->base_x; gw->last_y = grid->base_y; } else gw->last_x = gw->last_y = 0; /* * If the widget is in Move or Copy mode, change back to Select mode. */ if (gw->op == GLYPHEDIT_MOVE || gw->op == GLYPHEDIT_COPY) { gw->pending_op = gw->op; gw->op = GLYPHEDIT_SELECT; } gw->cidx = 1; gw->lcolor = 0; gtk_widget_queue_resize(GTK_WIDGET(gw)); } gboolean glyphedit_get_modified(Glyphedit *gw) { g_return_val_if_fail(gw != NULL, FALSE); g_return_val_if_fail(IS_GLYPHEDIT(gw), FALSE); return (gw->grid) ? gw->grid->modified : FALSE; } void glyphedit_set_modified(Glyphedit *gw, gboolean modified) { g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (gw->grid) gw->grid->modified = ((modified == TRUE) ? 1 : 0); } void glyphedit_signal_modified(Glyphedit *gw) { g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); glyphedit_signal_glyph_change(gw); } gboolean glyphedit_get_selecting(Glyphedit *gw) { g_return_val_if_fail(gw != NULL, FALSE); g_return_val_if_fail(IS_GLYPHEDIT(gw), FALSE); g_return_val_if_fail(gw->grid != NULL, FALSE); return bdf_has_selection(gw->grid, 0, 0, 0, 0) ? TRUE : FALSE; } gboolean glyphedit_clipboard_empty(Glyphedit *gw) { GdkWindow *owner; gboolean empty = TRUE; GdkAtom atype; gint aformat, nitems; guchar *data; g_return_val_if_fail(gw != NULL, empty); g_return_val_if_fail(IS_GLYPHEDIT(gw), empty); if ((owner = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD)) == 0) return empty; /* * Check to see if the clipboard contents are empty or not. * * This is handled specially to allow determination of this without * using up what might be a lot of memory to get the whole contents. It * will have to be changed for Windows. */ if (gdk_property_get(owner, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP, 0, 16, FALSE, &atype, &aformat, &nitems, &data)) { if (nitems > 0) { empty = FALSE; free((char *) data); } } return empty; } void glyphedit_get_image(Glyphedit *gw, bdf_bitmap_t *image) { g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); g_return_if_fail(image != NULL); if (gw->grid) bdf_grid_image(gw->grid, image); else memset(image, 0, sizeof(bdf_bitmap_t)); } bdf_glyph_grid_t * glyphedit_get_grid(Glyphedit *gw) { g_return_val_if_fail(gw != NULL, 0); g_return_val_if_fail(IS_GLYPHEDIT(gw), 0); return gw->grid; } bdf_glyph_t * glyphedit_get_glyph(Glyphedit *gw, gboolean *unencoded) { g_return_val_if_fail(gw != NULL, 0); g_return_val_if_fail(IS_GLYPHEDIT(gw), 0); if (gw->grid) { if (unencoded) *unencoded = (gw->grid->unencoded == 0) ? FALSE : TRUE; return bdf_grid_glyph(gw->grid); } if (unencoded) *unencoded = FALSE; return 0; } void glyphedit_set_show_cap_height(Glyphedit *gw, gboolean show) { g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); gw->show_cap_height = show; /* * Redraw the bounding box. */ glyphedit_draw_font_bbx(gw); } void glyphedit_set_show_x_height(Glyphedit *gw, gboolean show) { g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); gw->show_x_height = show; /* * Redraw the bounding box. */ glyphedit_draw_font_bbx(gw); } void glyphedit_crop_glyph(Glyphedit *gw) { g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (gw->grid && bdf_grid_crop(gw->grid, 1)) glyphedit_signal_glyph_change(gw); glyphedit_draw_glyph(gw); } void glyphedit_shift_glyph(Glyphedit *gw, gint16 xcount, gint16 ycount) { g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (gw->grid && bdf_grid_shift(gw->grid, xcount, ycount)) glyphedit_signal_glyph_change(gw); glyphedit_draw_glyph(gw); } void glyphedit_rotate_glyph(Glyphedit *gw, gint16 degrees) { gint resize = 0; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (gw->grid && bdf_grid_rotate(gw->grid, degrees, &resize)) { glyphedit_signal_glyph_change(gw); if (resize) gtk_widget_queue_resize(GTK_WIDGET(gw)); else glyphedit_draw_glyph(gw); } } void glyphedit_shear_glyph(Glyphedit *gw, gint16 degrees) { gint resize = 0; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (gw->grid && bdf_grid_shear(gw->grid, degrees, &resize)) { glyphedit_signal_glyph_change(gw); if (resize) gtk_widget_queue_resize(GTK_WIDGET(gw)); else glyphedit_draw_glyph(gw); } } void glyphedit_embolden_glyph(Glyphedit *gw) { g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (gw->grid && bdf_grid_embolden(gw->grid)) { glyphedit_signal_glyph_change(gw); /* * Simply redraw the glyph because the size didn't change, * only the bitmap. */ glyphedit_draw_glyph(gw); } } void glyphedit_flip_glyph(Glyphedit *gw, GtkOrientation direction) { gint flipped; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); g_return_if_fail(gw->grid != NULL); flipped = (direction == GTK_ORIENTATION_HORIZONTAL) ? bdf_grid_flip(gw->grid, -1) : bdf_grid_flip(gw->grid, 1); if (flipped) { glyphedit_signal_glyph_change(gw); /* * Simply redraw the glyph because the size didn't change, * only the bitmap. */ glyphedit_draw_glyph(gw); } } void glyphedit_set_pixel_size(Glyphedit *gw, guint pixel_size) { gint bytes; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if (pixel_size < MIN_PIXEL_SIZE || pixel_size > MAX_PIXEL_SIZE) return; /* * Queue up a resize to force the resize and redraw. */ gw->pixel_size = pixel_size; /* * Make sure the spot is the right size. */ bytes = (pixel_size + 1) * (pixel_size + 1); if (gw->spot_size < bytes) { if (gw->spot_size == 0) gw->spot = g_malloc(bytes); else gw->spot = g_realloc(gw->spot, bytes); gw->spot_size = bytes; } gw->spot_used = bytes; gtk_widget_queue_resize(GTK_WIDGET(gw)); } guint glyphedit_get_pixel_size(Glyphedit *gw) { g_return_val_if_fail(gw != NULL, GLYPHEDIT_NONE); g_return_val_if_fail(IS_GLYPHEDIT(gw), GLYPHEDIT_NONE); g_return_val_if_fail(gw->grid != NULL, GLYPHEDIT_NONE); return gw->pixel_size; } GlypheditOperation glyphedit_get_operation(Glyphedit *gw) { g_return_val_if_fail(gw != NULL, GLYPHEDIT_NONE); g_return_val_if_fail(IS_GLYPHEDIT(gw), GLYPHEDIT_NONE); g_return_val_if_fail(gw->grid != NULL, GLYPHEDIT_NONE); return gw->op; } void glyphedit_set_operation(Glyphedit *gw, GlypheditOperation op) { gint16 sx, sy, x, y, wd, ht; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); g_return_if_fail(gw->grid != NULL); if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) { if (op == GLYPHEDIT_MOVE) bdf_detach_selection(gw->grid); else if (op == GLYPHEDIT_COPY) bdf_attach_selection(gw->grid); else { if (op == GLYPHEDIT_DRAW) { /* * Attach the selected part of the bitmap. */ bdf_attach_selection(gw->grid); /* * Erase the selected rectangle. */ for (sy = y; sy < y + ht; sy++) { for (sx = x; sx < x + wd; sx++) glyphedit_draw_pixel(gw, sx, sy, FALSE); } bdf_lose_selection(gw->grid); } gw->op = op; } gw->pending_op = GLYPHEDIT_NONE; glyphedit_signal_glyph_change(gw); } else { if (op == GLYPHEDIT_MOVE || op == GLYPHEDIT_COPY) { gw->op = GLYPHEDIT_SELECT; gw->pending_op = op; } else { gw->op = op; gw->pending_op = GLYPHEDIT_NONE; } } } void glyphedit_insert_bitmap(Glyphedit *gw, bdf_bitmap_t *bitmap) { GtkWidget *w = GTK_WIDGET(gw); GdkWindow *win; gint16 sx, sy, x, y, wd, ht; bdf_metrics_t metrics; GlypheditSignalInfo si; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); if ((win = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD)) == 0) { gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD, GDK_CURRENT_TIME, FALSE); win = w->window; } else if (win != w->window) gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD, GDK_CURRENT_TIME, FALSE); if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) { /* * This widget already has a selection, so release it. */ if (gw->op != GLYPHEDIT_SELECT) bdf_attach_selection(gw->grid); for (sy = y; sy < y + ht; sy++) { for (sx = x; sx < x + wd; sx++) glyphedit_draw_pixel(gw, sx, sy, FALSE); } bdf_lose_selection(gw->grid); } bitmap->x = gw->last_x; bitmap->y = gw->last_y; glyphedit_get_font_metrics(gw, &metrics); if (bitmap->width > metrics.width || bitmap->height > metrics.height) { /* * Adjust the insert position on the X axis if necessary. */ if (bitmap->width > metrics.width) bitmap->x = gw->grid->base_x + gw->grid->font_bbx.x_offset; /* * Adjust the insert position on the Y axis and the ascent if * necessary. */ if (bitmap->height > metrics.height) { bitmap->y = 0; metrics.ascent = bitmap->height - gw->grid->font_bbx.descent; } metrics.width = bitmap->width; metrics.height = bitmap->height; glyphedit_set_metrics(gw, &metrics); } /* * Set the selection in the grid. */ bdf_add_selection(gw->grid, bitmap); /* * Now update the grid. */ if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) { for (sy = y; sy < y + ht; sy++) { for (sx = x; sx < x + wd; sx++) glyphedit_draw_pixel(gw, sx, sy, TRUE); } } /* * Set up and call the operation change signal. */ si.reason = GLYPHEDIT_OPERATION_CHANGE; si.operation = GLYPHEDIT_MOVE; g_signal_emit(G_OBJECT(gw), glyphedit_signals[OPERATION_CHANGE], 0, &si); /* * Set up and call the modified signal. */ glyphedit_signal_glyph_change(gw); /* * Make sure the widget goes into MOVE mode at this point. * This allows the user to position what was pasted without * destroying the glyph bitmap that was already there. */ if (gw->op != GLYPHEDIT_MOVE) { gw->op = GLYPHEDIT_MOVE; gw->pending_op = GLYPHEDIT_NONE; } glyphedit_copy_selection(gw); } static void glyphedit_own_clipboard(Glyphedit *gw) { GtkWidget *w; GdkWindow *win; w = GTK_WIDGET(gw); if (!GTK_WIDGET_REALIZED(w) || gw->owns_clipboard == TRUE) return; win = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD); gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD, GDK_CURRENT_TIME, FALSE); gw->owns_clipboard = (gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD) == w->window) ? TRUE : FALSE; /* * The Intrinsics may need to have a SelectionClear notice sent. Probably * won't be necessary on Windows. */ } static guchar * glyphedit_encode_selection(Glyphedit *gw, gint *bytes) { gint bcount, size; gint16 wd, ht; guchar *bmap, *bp; *bytes = 0; if (!bdf_has_selection(gw->grid, 0, 0, &wd, &ht)) return 0; size = bcount = (gint) gw->grid->sel.bytes >> 1; size += sizeof(guint16) * 3; bp = bmap = (guchar *) g_malloc(size); /* * Encode the width and height in Most Significant Byte order assuming * the width and height types are 16-bit values. */ if (!bdf_little_endian()) { *bp++ = (gw->grid->bpp >> 8) & 0xff; *bp++ = gw->grid->bpp & 0xff; *bp++ = (wd >> 8) & 0xff; *bp++ = wd & 0xff; *bp++ = (ht >> 8) & 0xff; *bp++ = ht & 0xff; } else { *bp++ = gw->grid->bpp & 0xff; *bp++ = (gw->grid->bpp >> 8) & 0xff; *bp++ = wd & 0xff; *bp++ = (wd >> 8) & 0xff; *bp++ = ht & 0xff; *bp++ = (ht >> 8) & 0xff; } (void) memcpy((char *) bp, (char *) gw->grid->sel.bitmap, bcount); *bytes = size; return bmap; } void glyphedit_copy_selection(Glyphedit *gw) { GtkWidget *w = GTK_WIDGET(gw); guchar *sel; gint bytes; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); /* * If the widget has no selection, then this routine will return 0. */ if ((sel = glyphedit_encode_selection(gw, &bytes)) == 0) return; /* * Go ahead and actually write the data to the clipboard and then free the * buffer. */ gdk_property_change(w->window, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP, 8, GDK_PROP_MODE_REPLACE, sel, (gint) bytes); g_free(sel); } void glyphedit_cut_selection(Glyphedit *gw) { GtkWidget *w = GTK_WIDGET(gw); guchar *sel; gint bytes; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); /* * If the widget has no selection, then this routine will return 0. */ if ((sel = glyphedit_encode_selection(gw, &bytes)) == 0) return; /* * Go ahead and actually write the data to the clipboard and then free the * buffer. */ gdk_property_change(w->window, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP, 8, GDK_PROP_MODE_REPLACE, sel, (gint) bytes); g_free(sel); /* * Now actually delete the selection and update the glyph. */ bdf_delete_selection(gw->grid); bdf_lose_selection(gw->grid); if (gw->op != GLYPHEDIT_DRAW) { gw->pending_op = gw->op; gw->op = GLYPHEDIT_SELECT; } glyphedit_draw_glyph(gw); glyphedit_signal_glyph_change(gw); } void glyphedit_change_operation(Glyphedit *gw, GlypheditOperation op) { gboolean call_modify; gint16 sx, sy, x, y, wd, ht; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); call_modify = TRUE; /* * Special handling is needed for move and copy operations. If a * selection does not exist yet, then make the move/copy a pending * operation and set the operation to select. Once the selection is made, * the operation will be changed to the pending move/copy operation. If a * selection exists, then set the move/copy operation and detach/attach * the selection accordingly. */ if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) { if (op == GLYPHEDIT_MOVE) bdf_detach_selection(gw->grid); else if (op == GLYPHEDIT_COPY) bdf_attach_selection(gw->grid); else { if (op == GLYPHEDIT_DRAW) { /* * Attach the selected part of the bitmap. */ bdf_attach_selection(gw->grid); /* * Erase the selected rectangle. */ for (sy = y; sy < y + ht; sy++) { for (sx = x; sx < x + wd; sx++) glyphedit_draw_pixel(gw, sx, sy, FALSE); } bdf_lose_selection(gw->grid); } else call_modify = FALSE; gw->op = op; } gw->pending_op = GLYPHEDIT_NONE; glyphedit_signal_glyph_change(gw); } else { if (op == GLYPHEDIT_MOVE || op == GLYPHEDIT_COPY) { gw->op = GLYPHEDIT_SELECT; gw->pending_op = op; } else { gw->op = op; gw->pending_op = GLYPHEDIT_NONE; } } } void glyphedit_set_color(Glyphedit *gw, gint idx) { GlypheditSignalInfo si; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); g_return_if_fail(gw->grid != 0); if (gw->grid) { if (idx <= 0) idx = (1 << gw->grid->bpp); else if (idx > (1 << gw->grid->bpp)) idx = 1; } else idx = 1; if (idx != gw->cidx) { si.reason = GLYPHEDIT_COLOR_CHANGE; si.color = idx; g_signal_emit(G_OBJECT(gw), glyphedit_signals[COLOR_CHANGE], 0, &si); } gw->cidx = idx; } void glyphedit_paste_selection(Glyphedit *gw) { GtkWidget *w = GTK_WIDGET(gw); GdkWindow *win; GdkAtom atype; gint aformat, nitems; guchar *data, *bp; gint16 sx, sy, x, y, wd, ht; bdf_metrics_t metrics; bdf_bitmap_t image; GlypheditSignalInfo si; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); g_return_if_fail(gw->grid != NULL); if ((win = gdk_selection_owner_get(GLYPHEDIT_CLIPBOARD)) == 0) { gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD, GDK_CURRENT_TIME, FALSE); win = w->window; } nitems = 0; gdk_property_get(win, GLYPHEDIT_CLIPBOARD, GLYPHEDIT_BITMAP, 0, 10240, FALSE, &atype, &aformat, &nitems, &data); if (win != w->window) gdk_selection_owner_set(w->window, GLYPHEDIT_CLIPBOARD, GDK_CURRENT_TIME, FALSE); if (nitems > 0) { /* * Got a bitmap. */ if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) { /* * This widget already has a selection, so release it. */ if (gw->op != GLYPHEDIT_SELECT) bdf_attach_selection(gw->grid); for (sy = y; sy < y + ht; sy++) { for (sx = x; sx < x + wd; sx++) glyphedit_draw_pixel(gw, sx, sy, FALSE); } bdf_lose_selection(gw->grid); } bp = data; if (!bdf_little_endian()) { image.bpp = (*bp++ << 8) & 0xff00; image.bpp |= *bp++; image.width = (*bp++ << 8) & 0xff00; image.width |= *bp++; image.height = (*bp++ << 8) & 0xff00; image.height |= *bp++; } else { image.bpp = *bp++ & 0xff; image.bpp |= (*bp++ << 8) & 0xff00; image.width = *bp++ & 0xff; image.width |= (*bp++ << 8) & 0xff00; image.height = *bp++ & 0xff; image.height |= (*bp++ << 8) & 0xff00; } image.bytes = (((image.width * image.bpp) + 7) >> 3) * image.height; image.bitmap = bp; image.x = gw->last_x; image.y = gw->last_y; /* * If the bitmap being pasted is larger than the current grid, then * resize the grid before doing anything else. */ glyphedit_get_font_metrics(gw, &metrics); if (image.width > metrics.width || image.height > metrics.height) { /* * Adjust the insert position on the X axis if necessary. */ if (image.width > metrics.width) image.x = gw->grid->base_x + gw->grid->font_bbx.x_offset; /* * Adjust the insert position on the Y axis and the ascent if * necessary. */ if (image.height > metrics.height) { image.y = 0; metrics.ascent = image.height - gw->grid->font_bbx.descent; } metrics.width = image.width; metrics.height = image.height; glyphedit_set_metrics(gw, &metrics); } /* * Set the selection in the grid. */ bdf_add_selection(gw->grid, &image); /* * Now update the grid. */ if (bdf_has_selection(gw->grid, &x, &y, &wd, &ht)) { for (sy = y; sy < y + ht; sy++) { for (sx = x; sx < x + wd; sx++) glyphedit_draw_pixel(gw, sx, sy, TRUE); } } /* * Set up and call the image update. */ glyphedit_signal_glyph_change(gw); /* * Free up the original value passed. */ g_free(data); /* * Alert the client that the widget is changing to the MOVE * operation. */ si.reason = GLYPHEDIT_OPERATION_CHANGE; si.operation = GLYPHEDIT_MOVE; g_signal_emit(G_OBJECT(gw), glyphedit_signals[OPERATION_CHANGE], 0, &si); /* * Make sure the widget goes into MOVE mode at this point. * This allows the user to position what was pasted without * destroying the glyph bitmap that was already there. */ if (gw->op != GLYPHEDIT_MOVE) { gw->op = GLYPHEDIT_MOVE; gw->pending_op = GLYPHEDIT_NONE; } /* * Last, recopy the selection to the clipboard because changing owners * causes the data to be lost. */ glyphedit_copy_selection(gw); } } void glyphedit_select_all(Glyphedit *gw) { gint16 tx, ty, sx, sy, wd, ht; GlypheditSignalInfo si; g_return_if_fail(gw != NULL); g_return_if_fail(IS_GLYPHEDIT(gw)); g_return_if_fail(gw->grid != NULL); /* * If a selection already exists, clear it. */ if (bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht)) { if (gw->op != GLYPHEDIT_SELECT) bdf_attach_selection(gw->grid); for (ty = sy; ty < sy + ht; ty++) { for (tx = sx; tx < sx + wd; tx++) glyphedit_draw_pixel(gw, tx, ty, FALSE); } bdf_lose_selection(gw->grid); } wd = gw->grid->glyph_bbx.width; ht = gw->grid->glyph_bbx.height; sx = gw->sel_start.x = gw->grid->glyph_x; sy = gw->sel_start.y = gw->grid->glyph_y; gw->sel_end.x = gw->grid->glyph_x + wd; gw->sel_end.y = gw->grid->glyph_y + ht; /* * Gain control of the GLYPHEDIT_CLIPBOARD atom. */ glyphedit_own_clipboard(gw); bdf_set_selection(gw->grid, sx, sy, wd, ht); bdf_detach_selection(gw->grid); for (ty = sy; ty < sy + ht; ty++) { for (tx = sx; tx < sx + wd; tx++) glyphedit_draw_pixel(gw, tx, ty, TRUE); } /* * Alert the client that the widget is changing to the MOVE * operation. */ si.reason = GLYPHEDIT_OPERATION_CHANGE; si.operation = GLYPHEDIT_MOVE; g_signal_emit(G_OBJECT(gw), glyphedit_signals[OPERATION_CHANGE], 0, &si); /* * Make sure the widget goes into MOVE mode at this point. * This allows the user to position what was pasted without * destroying the glyph bitmap that was already there. */ if (gw->op != GLYPHEDIT_MOVE) { gw->op = GLYPHEDIT_MOVE; gw->pending_op = GLYPHEDIT_NONE; } } gint32 glyphedit_encoding(Glyphedit *gw) { g_return_val_if_fail(gw != NULL, -1); g_return_val_if_fail(IS_GLYPHEDIT(gw), -1); g_return_val_if_fail(gw->grid != NULL, -1); return (gw->grid->unencoded) ? -1 : gw->grid->encoding; } static void glyphedit_get_pointer_coord(Glyphedit *gw, gint16 ex, gint16 ey, gint16 *px, gint16 *py) { GtkWidget *w = GTK_WIDGET(gw); gint16 x, y, wd, ht; wd = (gw->pixel_size + 4) * gw->grid->grid_width; ht = (gw->pixel_size + 4) * gw->grid->grid_height; /* * Need the plus 1 to account for the outer rectangle. */ x = (w->allocation.width >> 1) - (wd >> 1) + 1; y = (w->allocation.height >> 1) - (ht >> 1) + 1; if (ex < x || ex > x + wd) *px = -1; else *px = (ex - x) / (gw->pixel_size + 4); if (ey < y || ey > y + ht) *py = -1; else *py = (ey - y) / (gw->pixel_size + 4); /* * Adjust for a possible overrun off the edges of the grid. */ if (*px >= gw->grid->grid_width) *px = gw->grid->grid_width - 1; if (*py >= gw->grid->grid_height) *py = gw->grid->grid_height - 1; } static gboolean glyphedit_in_selection(Glyphedit *gw, gint16 x, gint16 y) { return (((gw->sel_start.y <= y && y <= gw->sel_end.y) || (gw->sel_end.y <= y && y <= gw->sel_start.y)) && ((gw->sel_start.x <= x && x <= gw->sel_end.x) || (gw->sel_end.x <= x && x <= gw->sel_start.x))) ? TRUE : FALSE; } static gboolean glyphedit_in_intersection(Glyphedit *gw, gint16 ix, gint16 iy, gint16 x, gint16 y) { return (((gw->sel_start.y <= y && y <= iy) || (iy <= y && y <= gw->sel_start.y)) && ((gw->sel_start.x <= x && x <= ix) || (ix <= x && x <= gw->sel_start.x))) ? TRUE : FALSE; } static void glyphedit_update_selection(Glyphedit *gw, gint16 x, gint16 y, gboolean set) { gint16 wd, ht; for (ht = 0; ht < gw->grid->grid_height; ht++) { for (wd = 0; wd < gw->grid->grid_width; wd++) { if (glyphedit_in_intersection(gw, x, y, wd, ht) == FALSE && glyphedit_in_selection(gw, wd, ht) == TRUE) /* * Clear or set the pixel. */ glyphedit_draw_pixel(gw, wd, ht, set); } } } static gboolean glyphedit_button_press(GtkWidget *w, GdkEventButton *event) { Glyphedit *gw; gint16 x, y, sx, sy, tx, ty, wd, ht; gboolean changed; gw = GLYPHEDIT(w); glyphedit_get_pointer_coord(gw, (gint16) event->x, (gint16) event->y, &x, &y); if (event->button == 2 && (event->state & GDK_SHIFT_MASK)) { /* * Paste. */ glyphedit_paste_selection(gw); return FALSE; } changed = FALSE; if (gw->op == GLYPHEDIT_DRAW) { switch (event->button) { case 1: if ((changed = bdf_grid_set_pixel(gw->grid, x, y, gw->cidx))) glyphedit_draw_pixel(gw, x, y, FALSE); break; case 2: if ((changed = bdf_grid_invert_pixel(gw->grid, x, y, gw->cidx))) glyphedit_draw_pixel(gw, x, y, FALSE); break; case 3: if ((changed = bdf_grid_clear_pixel(gw->grid, x, y))) glyphedit_draw_pixel(gw, x, y, FALSE); break; } if (changed == TRUE) glyphedit_signal_glyph_change(gw); } else if (gw->op == GLYPHEDIT_SELECT) { /* * If a selection already exists, clear it. */ if (bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht)) { if (gw->pending_op != GLYPHEDIT_NONE) bdf_attach_selection(gw->grid); for (ty = sy; ty < sy + ht; ty++) { for (tx = sx; tx < sx + wd; tx++) glyphedit_draw_pixel(gw, tx, ty, FALSE); } bdf_lose_selection(gw->grid); } /* * Select the pixel at the point and initialize the selection * rectangle. */ glyphedit_draw_pixel(gw, x, y, TRUE); gw->sel_start.x = gw->sel_end.x = x; gw->sel_start.y = gw->sel_end.y = y; } else { /* * Check to see if this is Button3 and a selection exists. If so, * then copy the selection to the clipboard and return. */ if (event->button == 3 && bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht)) { glyphedit_copy_selection(gw); gw->last_x = x; gw->last_y = y; return FALSE; } /* * The operation is one of move or copy. If the button is clicked * outside the selection, remove the selection and start over. */ if (bdf_has_selection(gw->grid, &sx, &sy, &wd, &ht) && !bdf_in_selection(gw->grid, x, y, 0)) { if (gw->op != GLYPHEDIT_SELECT) bdf_attach_selection(gw->grid); for (ty = sy; ty < sy + ht; ty++) { for (tx = sx; tx < sx + wd; tx++) glyphedit_draw_pixel(gw, tx, ty, FALSE); } bdf_lose_selection(gw->grid); gw->pending_op = gw->op; gw->op = GLYPHEDIT_SELECT; /* * Select the pixel at the point and initialize the selection * rectangle. */ glyphedit_draw_pixel(gw, x, y, TRUE); gw->sel_start.x = gw->sel_end.x = x; gw->sel_start.y = gw->sel_end.y = y; } } /* * Set the last coordinate to the point just handled. */ gw->last_x = x; gw->last_y = y; return FALSE; } static gboolean glyphedit_button_release(GtkWidget *w, GdkEventButton *event) { Glyphedit *gw; gint16 sx, sy, ex, ey; /* * Button releases on a widget without the focus is ignored. */ if (!GTK_WIDGET_HAS_FOCUS(w)) return FALSE; gw = GLYPHEDIT(w); sx = MIN(gw->sel_start.x, gw->sel_end.x); ex = MAX(gw->sel_start.x, gw->sel_end.x); sy = MIN(gw->sel_start.y, gw->sel_end.y); ey = MAX(gw->sel_start.y, gw->sel_end.y); if (gw->op == GLYPHEDIT_SELECT) { if (sx == ex && sy == ey) glyphedit_draw_pixel(gw, gw->sel_start.x, gw->sel_start.y, FALSE); else { /* * Gain control of the GLYPHEDIT_CLIPBOARD atom. */ glyphedit_own_clipboard(gw); bdf_set_selection(gw->grid, sx, sy, (ex - sx) + 1, (ey - sy) + 1); /* * Switch to a move/copy operations if necessary. */ if (gw->pending_op != GLYPHEDIT_NONE) { gw->op = gw->pending_op; gw->pending_op = GLYPHEDIT_NONE; /* * If the pending operation is a move, then make sure the * selection is detached. */ if (gw->op == GLYPHEDIT_MOVE) bdf_detach_selection(gw->grid); } } } return FALSE; } static gboolean glyphedit_motion_notify(GtkWidget *w, GdkEventMotion *event) { Glyphedit *gw; gboolean changed; gint16 x, y, ix, iy; GlypheditSignalInfo si; gw = GLYPHEDIT(w); glyphedit_get_pointer_coord(gw, (gint16) event->x, (gint16) event->y, &x, &y); /* * Return if the mouse is off the edges of the grid or the mouse is still * on the same point as the last one. */ if (x < 0 || y < 0 || (x == gw->last_x && y == gw->last_y)) return FALSE; si.reason = GLYPHEDIT_POINTER_MOVED; si.x = x - gw->grid->base_x; si.y = -(y - gw->grid->base_y) - 1; si.color = bdf_grid_color_at(gw->grid, x, y); g_signal_emit(G_OBJECT(gw), glyphedit_signals[POINTER_MOVED], 0, &si); ix = gw->last_x; iy = gw->last_y; gw->last_x = x; gw->last_y = y; /* * If the event is a simple motion event with no button being pressed, * then simply return at this point. */ if (!GTK_WIDGET_HAS_FOCUS(w) || !(event->state & (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK))) return FALSE; changed = FALSE; if (gw->op == GLYPHEDIT_DRAW) { /* * Drawing. */ if (event->state & GDK_BUTTON1_MASK) { if ((changed = bdf_grid_set_pixel(gw->grid, x, y, gw->cidx))) glyphedit_draw_pixel(gw, x, y, FALSE); } else if (event->state & GDK_BUTTON2_MASK) { if ((changed = bdf_grid_invert_pixel(gw->grid, x, y, gw->cidx))) glyphedit_draw_pixel(gw, x, y, FALSE); } else if (event->state & GDK_BUTTON3_MASK) { if ((changed = bdf_grid_clear_pixel(gw->grid, x, y))) glyphedit_draw_pixel(gw, x, y, FALSE); } /* * If one of the pixels changed, then call the callback. */ if (changed) glyphedit_signal_glyph_change(gw); } else if (gw->op == GLYPHEDIT_SELECT) { /* * Determine the other point on the intersection rectangle. */ ix = gw->sel_start.x; iy = gw->sel_start.y; if (x > ix) ix = MIN(gw->sel_end.x, x); else if (x < ix) ix = MAX(gw->sel_end.x, x); if (y > iy) iy = MIN(gw->sel_end.y, y); else if (y < iy) iy = MAX(gw->sel_end.y, y); /* * Clear the pixels outside the intersection of the old selection * rectangle and the new selection rectangle. */ glyphedit_update_selection(gw, ix, iy, FALSE); /* * Set the new endpoint of the selection rectangle. */ gw->sel_end.x = x; gw->sel_end.y = y; /* * Set all pixels outside the intersection of the old selection * rectangle and the new selection rectangle, but inside the new * selection rectangle. */ glyphedit_update_selection(gw, ix, iy, TRUE); } else { /* * A move or copy is in progress. */ if (bdf_has_selection(gw->grid, 0, 0, 0, 0) && bdf_grid_shift(gw->grid, x - ix, y - iy)) { glyphedit_draw_glyph(gw); glyphedit_signal_glyph_change(gw); } } return FALSE; } static gboolean glyphedit_key_press(GtkWidget *w, GdkEventKey *event) { gboolean ret = FALSE; switch (event->keyval) { case GDK_Left: case GDK_KP_Left: glyphedit_shift_glyph(GLYPHEDIT(w), -1, 0); break; case GDK_Right: case GDK_KP_Right: glyphedit_shift_glyph(GLYPHEDIT(w), 1, 0); break; case GDK_Up: case GDK_KP_Up: /* * For some reason, the Up arrow causes the focus to change to * other widgets. Returning TRUE insures that the up arrow works * as expected. */ glyphedit_shift_glyph(GLYPHEDIT(w), 0, -1); ret = TRUE; break; case GDK_Down: case GDK_KP_Down: glyphedit_shift_glyph(GLYPHEDIT(w), 0, 1); break; case GDK_Delete: case GDK_BackSpace: glyphedit_cut_selection(GLYPHEDIT(w)); break; case GDK_9: case GDK_KP_9: glyphedit_rotate_glyph(GLYPHEDIT(w), -90); case GDK_0: case GDK_KP_0: glyphedit_rotate_glyph(GLYPHEDIT(w), 90); break; case GDK_minus: case GDK_KP_Subtract: glyphedit_flip_glyph(GLYPHEDIT(w), GTK_ORIENTATION_HORIZONTAL); break; case GDK_equal: case GDK_KP_Equal: glyphedit_flip_glyph(GLYPHEDIT(w), GTK_ORIENTATION_VERTICAL); break; case GDK_comma: case GDK_Z: case GDK_z: /* Change to a lighter color. */ glyphedit_set_color(GLYPHEDIT(w), GLYPHEDIT(w)->cidx - 1); break; case GDK_period: case GDK_X: case GDK_x: /* Change to a darker color. */ glyphedit_set_color(GLYPHEDIT(w), GLYPHEDIT(w)->cidx + 1); break; } return ret; } static gboolean glyphedit_key_release(GtkWidget *w, GdkEventKey *event) { return FALSE; } static void glyphedit_class_init(gpointer g_class, gpointer class_data) { GObjectClass *gocp = G_OBJECT_CLASS(g_class); GtkObjectClass *ocp = GTK_OBJECT_CLASS(g_class); GtkWidgetClass *wcp = GTK_WIDGET_CLASS(g_class); /* * Set the class global variables. */ parent_class = g_type_class_peek_parent(g_class); ocp->destroy = glyphedit_destroy; gocp->set_property = glyphedit_set_property; gocp->get_property = glyphedit_get_property; gocp->finalize = glyphedit_finalize; /* * Add argument (a.k.a. resource) types. */ g_object_class_install_property(gocp, GLYPH_GRID, g_param_spec_pointer("glyphGrid", _("Glyph Grid"), _("The glyph in a grid structure."), G_PARAM_READWRITE)); g_object_class_install_property(gocp, PIXEL_SIZE, g_param_spec_uint("pixelSize", _("Pixel Size"), _("The number of pixels to use to draw one grid pixel."), 1, 20, 10, G_PARAM_READWRITE)); g_object_class_install_property(gocp, SHOW_X_HEIGHT, g_param_spec_boolean("showXHeight", _("Show X Height"), _("Draw a line at the x height."), FALSE, G_PARAM_READWRITE)); g_object_class_install_property(gocp, SHOW_CAP_HEIGHT, g_param_spec_boolean("showCapHeight", _("Show Cap Height"), _("Draw a line at the cap height."), FALSE, G_PARAM_READWRITE)); g_object_class_install_property(gocp, OPERATION, g_param_spec_enum("operation", _("Edit Operation"), _("Glyph edit operation."), glyphedit_get_operation_type(), GLYPHEDIT_DRAW, G_PARAM_READWRITE)); g_object_class_install_property(gocp, COLOR_LIST, g_param_spec_pointer("colorList", _("Color list"), _("Colors to be used for glyphs having bits-per-pixel > 1."), G_PARAM_READWRITE)); /* * Add the signals these objects emit. */ glyphedit_signals[GLYPH_MODIFIED] = g_signal_new("glyph-modified", G_TYPE_FROM_CLASS(gocp), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GlypheditClass, glyph_modified), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); glyphedit_signals[POINTER_MOVED] = g_signal_new("pointer-moved", G_TYPE_FROM_CLASS(gocp), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GlypheditClass, pointer_moved), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); glyphedit_signals[OPERATION_CHANGE] = g_signal_new("operation-change", G_TYPE_FROM_CLASS(gocp), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GlypheditClass, operation_change), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); glyphedit_signals[COLOR_CHANGE] = g_signal_new("color-change", G_TYPE_FROM_CLASS(gocp), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(GlypheditClass, color_change), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); /* * Set all the functions for handling events for objects of this class. */ wcp->size_request = glyphedit_preferred_size; wcp->size_allocate = glyphedit_actual_size; wcp->realize = glyphedit_realize; wcp->expose_event = glyphedit_expose; wcp->focus_in_event = glyphedit_focus_in; wcp->focus_out_event = glyphedit_focus_out; wcp->button_press_event = glyphedit_button_press; wcp->button_release_event = glyphedit_button_release; wcp->motion_notify_event = glyphedit_motion_notify; wcp->key_press_event = glyphedit_key_press; wcp->key_release_event = glyphedit_key_release; } GType glyphedit_get_type(void) { static GType glyphedit_type = 0; if (!glyphedit_type) { static const GTypeInfo glyphedit_info = { sizeof(GlypheditClass), 0, 0, glyphedit_class_init, 0, 0, sizeof(Glyphedit), 0, glyphedit_init, 0, }; glyphedit_type = g_type_register_static(GTK_TYPE_WIDGET, "Glyphedit", &glyphedit_info, 0); } return glyphedit_type; }