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