1 /* GmcCharGrid Widget - Simple character grid for the gmc viewer
3 * Copyright (C) 1997 The Free Software Foundation
5 * Author: Federico Mena <federico@nuclecu.unam.mx>
8 #include <gtk/gtksignal.h>
10 #include "gmc-chargrid.h"
13 #define DEFAULT_WIDTH 80
14 #define DEFAULT_HEIGHT 25
15 #define DEFAULT_FONT "fixed,-*-*-medium-r-normal-*-12-*-*-*-*-*-*-*,*"
18 #define CHARS(cgrid) ((char *) cgrid->chars)
19 #define ATTRS(cgrid) ((struct attr *) cgrid->attrs)
34 typedef void (* GmcCharGridSignal1
) (GtkObject
*object
,
39 static void gmc_char_grid_marshal_signal_1 (GtkObject
*object
,
45 static void gmc_char_grid_class_init (GmcCharGridClass
*class);
46 static void gmc_char_grid_init (GmcCharGrid
*cgrid
);
47 static void gmc_char_grid_destroy (GtkObject
*object
);
48 static void gmc_char_grid_realize (GtkWidget
*widget
);
49 static void gmc_char_grid_size_request (GtkWidget
*widget
,
50 GtkRequisition
*requisition
);
51 static void gmc_char_grid_size_allocate (GtkWidget
*widget
,
52 GtkAllocation
*allocation
);
53 static void gmc_char_grid_draw (GtkWidget
*widget
,
55 static gint
gmc_char_grid_expose (GtkWidget
*widget
,
56 GdkEventExpose
*event
);
57 static void gmc_char_grid_real_size_changed (GmcCharGrid
*cgrid
,
62 static GtkWidgetClass
*parent_class
;
64 static guint cgrid_signals
[LAST_SIGNAL
] = { 0 };
68 gmc_char_grid_get_type (void)
70 static guint cgrid_type
= 0;
73 GtkTypeInfo cgrid_info
= {
76 sizeof (GmcCharGridClass
),
77 (GtkClassInitFunc
) gmc_char_grid_class_init
,
78 (GtkObjectInitFunc
) gmc_char_grid_init
,
83 cgrid_type
= gtk_type_unique (gtk_widget_get_type (), &cgrid_info
);
90 gmc_char_grid_class_init (GmcCharGridClass
*class)
92 GtkObjectClass
*object_class
;
93 GtkWidgetClass
*widget_class
;
95 object_class
= (GtkObjectClass
*) class;
96 widget_class
= (GtkWidgetClass
*) class;
98 parent_class
= gtk_type_class (gtk_widget_get_type ());
100 cgrid_signals
[SIZE_CHANGED
] =
101 gtk_signal_new ("size_changed",
104 GTK_SIGNAL_OFFSET (GmcCharGridClass
, size_changed
),
105 gmc_char_grid_marshal_signal_1
,
110 gtk_object_class_add_signals (object_class
, cgrid_signals
, LAST_SIGNAL
);
112 object_class
->destroy
= gmc_char_grid_destroy
;
114 widget_class
->realize
= gmc_char_grid_realize
;
115 widget_class
->size_request
= gmc_char_grid_size_request
;
116 widget_class
->size_allocate
= gmc_char_grid_size_allocate
;
117 widget_class
->draw
= gmc_char_grid_draw
;
118 widget_class
->expose_event
= gmc_char_grid_expose
;
120 class->size_changed
= gmc_char_grid_real_size_changed
;
124 gmc_char_grid_init (GmcCharGrid
*cgrid
)
130 cgrid
->frozen
= FALSE
;
133 cgrid
->char_width
= 0;
134 cgrid
->char_height
= 0;
139 gmc_char_grid_new (void)
143 cgrid
= gtk_type_new (gmc_char_grid_get_type ());
145 gmc_char_grid_set_font (cgrid
, DEFAULT_FONT
);
146 gmc_char_grid_set_size (cgrid
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
148 return GTK_WIDGET (cgrid
);
152 gmc_char_grid_destroy (GtkObject
*object
)
156 g_return_if_fail (object
!= NULL
);
157 g_return_if_fail (GMC_IS_CHAR_GRID (object
));
159 cgrid
= GMC_CHAR_GRID (object
);
162 g_free (cgrid
->chars
);
165 g_free (cgrid
->attrs
);
168 gdk_font_unref (cgrid
->font
);
171 gdk_gc_destroy (cgrid
->gc
);
173 if (GTK_OBJECT_CLASS (parent_class
)->destroy
)
174 (* GTK_OBJECT_CLASS (parent_class
)->destroy
) (object
);
178 gmc_char_grid_realize (GtkWidget
*widget
)
180 GdkWindowAttr attributes
;
181 gint attributes_mask
;
184 g_return_if_fail (widget
!= NULL
);
185 g_return_if_fail (GMC_IS_CHAR_GRID (widget
));
187 cgrid
= GMC_CHAR_GRID (widget
);
189 GTK_WIDGET_SET_FLAGS (widget
, GTK_REALIZED
);
191 attributes
.x
= widget
->allocation
.x
+ (widget
->allocation
.width
- cgrid
->char_width
* cgrid
->width
) / 2;
192 attributes
.y
= widget
->allocation
.y
+ (widget
->allocation
.height
- cgrid
->char_height
* cgrid
->height
) / 2;
193 attributes
.width
= cgrid
->width
* cgrid
->char_width
;
194 attributes
.height
= cgrid
->height
* cgrid
->char_height
;
195 attributes
.window_type
= GDK_WINDOW_CHILD
;
196 attributes
.wclass
= GDK_INPUT_OUTPUT
;
197 attributes
.visual
= gtk_widget_get_visual (widget
);
198 attributes
.colormap
= gtk_widget_get_colormap (widget
);
199 attributes
.event_mask
= (gtk_widget_get_events (widget
)
200 | GDK_EXPOSURE_MASK
);
202 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
204 widget
->window
= gdk_window_new (gtk_widget_get_parent_window (widget
), &attributes
, attributes_mask
);
205 gdk_window_set_user_data (widget
->window
, widget
);
207 cgrid
->gc
= gdk_gc_new (cgrid
->widget
.window
);
209 widget
->style
= gtk_style_attach (widget
->style
, widget
->window
);
210 gtk_style_set_background (widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
214 gmc_char_grid_size_request (GtkWidget
*widget
, GtkRequisition
*requisition
)
218 g_return_if_fail (widget
!= NULL
);
219 g_return_if_fail (GMC_IS_CHAR_GRID (widget
));
220 g_return_if_fail (requisition
!= NULL
);
222 cgrid
= GMC_CHAR_GRID (widget
);
224 requisition
->width
= cgrid
->width
* cgrid
->char_width
;
225 requisition
->height
= cgrid
->height
* cgrid
->char_height
;
229 gmc_char_grid_size_allocate (GtkWidget
*widget
, GtkAllocation
*allocation
)
234 g_return_if_fail (widget
!= NULL
);
235 g_return_if_fail (GMC_IS_CHAR_GRID (widget
));
236 g_return_if_fail (allocation
!= NULL
);
238 widget
->allocation
= *allocation
;
240 cgrid
= GMC_CHAR_GRID (widget
);
242 w
= allocation
->width
/ cgrid
->char_width
;
243 h
= allocation
->height
/ cgrid
->char_height
;
245 if (GTK_WIDGET_REALIZED (widget
))
246 gdk_window_move_resize (widget
->window
,
247 allocation
->x
+ (allocation
->width
- cgrid
->char_width
* cgrid
->width
) / 2,
248 allocation
->y
+ (allocation
->height
- cgrid
->char_height
* cgrid
->height
) / 2,
249 cgrid
->width
* cgrid
->char_width
,
250 cgrid
->height
* cgrid
->char_height
);
252 gmc_char_grid_set_size (cgrid
, MAX (w
, 1), MAX (h
, 1));
256 update_strip (GmcCharGrid
*cgrid
, int x
, int y
, int width
)
266 chars
= CHARS (cgrid
) + (cgrid
->width
* y
+ x
);
267 attrs
= ATTRS (cgrid
) + (cgrid
->width
* y
+ x
);
269 /* First paint the background and then paint the text. We do it in two passes
270 * so that we can paint runs of same-background and foreground more efficiently.
277 ocolor
= attrs
[i
].bg_set
? attrs
[i
].bg
: GTK_WIDGET (cgrid
)->style
->bg
[GTK_STATE_NORMAL
].pixel
;
281 color
= attrs
[i
].bg_set
? attrs
[i
].bg
: GTK_WIDGET (cgrid
)->style
->bg
[GTK_STATE_NORMAL
].pixel
;
282 } while ((i
< width
) && (color
== ocolor
));
284 gcolor
.pixel
= ocolor
;
285 gdk_gc_set_foreground (cgrid
->gc
, &gcolor
);
287 gdk_draw_rectangle (cgrid
->widget
.window
,
290 (first
+ x
) * cgrid
->char_width
,
291 y
* cgrid
->char_height
,
292 cgrid
->char_width
* (i
- first
),
296 /* Now paint the text */
302 ocolor
= attrs
[i
].fg_set
? attrs
[i
].fg
: GTK_WIDGET (cgrid
)->style
->fg
[GTK_STATE_NORMAL
].pixel
;
306 color
= attrs
[i
].fg_set
? attrs
[i
].fg
: GTK_WIDGET (cgrid
)->style
->fg
[GTK_STATE_NORMAL
].pixel
;
307 } while ((i
< width
) && (color
== ocolor
));
309 gcolor
.pixel
= ocolor
;
310 gdk_gc_set_foreground (cgrid
->gc
, &gcolor
);
312 gdk_draw_text (cgrid
->widget
.window
,
315 (first
+ x
) * cgrid
->char_width
,
316 y
* cgrid
->char_height
+ cgrid
->char_y
,
323 update_region (GmcCharGrid
*cgrid
, int x
, int y
, int width
, int height
)
327 if ((width
== 0) || (height
== 0))
332 width
= MIN (width
, cgrid
->width
- x
);
333 height
= MIN (height
, cgrid
->height
- y
);
335 for (i
= 0; i
< height
; i
++)
336 update_strip (cgrid
, x
, y
+ i
, width
);
340 paint (GmcCharGrid
*cgrid
, GdkRectangle
*area
)
344 /* This logic is shamelessly ripped from gtkterm :-) - Federico */
348 else if (area
->x
> 0)
351 if (area
->height
> 1)
353 else if (area
->y
> 0)
356 x1
= area
->x
/ cgrid
->char_width
;
357 y1
= area
->y
/ cgrid
->char_height
;
359 x2
= (area
->x
+ area
->width
) / cgrid
->char_width
;
360 y2
= (area
->y
+ area
->height
) / cgrid
->char_height
;
362 update_region (cgrid
, x1
, y1
, (x2
- x1
) + 1, (y2
- y1
) + 1);
366 gmc_char_grid_draw (GtkWidget
*widget
, GdkRectangle
*area
)
372 g_return_if_fail (widget
!= NULL
);
373 g_return_if_fail (GMC_IS_CHAR_GRID (widget
));
374 g_return_if_fail (area
!= NULL
);
376 if (GTK_WIDGET_DRAWABLE (widget
)) {
377 cgrid
= GMC_CHAR_GRID (widget
);
379 /* Offset the area because the window does not fill thea allocation */
381 area
->x
-= (widget
->allocation
.width
- cgrid
->char_width
* cgrid
->width
) / 2;
382 area
->y
-= (widget
->allocation
.height
- cgrid
->char_height
* cgrid
->height
) / 2;
386 w_area
.width
= cgrid
->char_width
* cgrid
->width
;
387 w_area
.height
= cgrid
->char_height
* cgrid
->height
;
389 if (gdk_rectangle_intersect (area
, &w_area
, &p_area
))
390 paint (cgrid
, &p_area
);
395 gmc_char_grid_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
397 g_return_val_if_fail (widget
!= NULL
, FALSE
);
398 g_return_val_if_fail (GMC_IS_CHAR_GRID (widget
), FALSE
);
399 g_return_val_if_fail (event
!= NULL
, FALSE
);
401 if (GTK_WIDGET_DRAWABLE (widget
))
402 paint (GMC_CHAR_GRID (widget
), &event
->area
);
408 gmc_char_grid_clear (GmcCharGrid
*cgrid
, int x
, int y
, int width
, int height
, GdkColor
*bg
)
415 g_return_if_fail (cgrid
!= NULL
);
416 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
420 x2
= MIN (x
+ width
, cgrid
->width
);
421 y2
= MIN (y
+ height
, cgrid
->height
);
423 ch
= CHARS (cgrid
) + (y1
* cgrid
->width
);
424 attrs
= ATTRS (cgrid
) + (y1
* cgrid
->width
);
426 for (yy
= y1
; yy
< y2
; yy
++) {
427 for (xx
= x1
; xx
< x2
; xx
++) {
430 attrs
[xx
].bg
= bg
? bg
->pixel
: 0;
431 attrs
[xx
].bg_set
= bg
? TRUE
: FALSE
;
435 attrs
+= cgrid
->width
;
438 if (GTK_WIDGET_DRAWABLE (cgrid
) && !cgrid
->frozen
)
439 update_region (cgrid
, x
, y
, width
, height
);
443 gmc_char_grid_put_char (GmcCharGrid
*cgrid
, int x
, int y
, GdkColor
*fg
, GdkColor
*bg
, char ch
)
448 g_return_if_fail (cgrid
!= NULL
);
449 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
451 if ((x
< 0) || (x
>= cgrid
->width
)
452 || (y
< 0) || (y
>= cgrid
->height
))
455 chars
= CHARS (cgrid
) + (y
* cgrid
->width
+ x
);
456 attrs
= ATTRS (cgrid
) + (y
* cgrid
->width
+ x
);
460 attrs
->fg
= fg
? fg
->pixel
: 0;
461 attrs
->fg_set
= fg
? TRUE
: FALSE
;
463 attrs
->bg
= bg
? bg
->pixel
: 0;
464 attrs
->bg_set
= bg
? TRUE
: FALSE
;
466 if (GTK_WIDGET_DRAWABLE (cgrid
) && !cgrid
->frozen
)
467 update_region (cgrid
, x
, y
, 1, 1);
471 gmc_char_grid_put_string (GmcCharGrid
*cgrid
, int x
, int y
, GdkColor
*fg
, GdkColor
*bg
, char *str
)
473 g_return_if_fail (str
!= NULL
);
475 gmc_char_grid_put_text (cgrid
, x
, y
, fg
, bg
, str
, strlen (str
));
479 gmc_char_grid_put_text (GmcCharGrid
*cgrid
, int x
, int y
, GdkColor
*fg
, GdkColor
*bg
, char *text
, int length
)
485 g_return_if_fail (cgrid
!= NULL
);
486 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
487 g_return_if_fail (text
!= NULL
);
489 if ((x
< 0) || (x
>= cgrid
->width
)
490 || (y
< 0) || (y
>= cgrid
->height
))
493 chars
= CHARS (cgrid
) + (cgrid
->width
* y
+ x
);
494 attrs
= ATTRS (cgrid
) + (cgrid
->width
* y
+ x
);
496 for (i
= 0, pos
= x
; (i
< length
) && (pos
< cgrid
->width
); i
++, pos
++) {
499 attrs
->fg
= fg
? fg
->pixel
: 0;
500 attrs
->fg_set
= fg
? TRUE
: FALSE
;
502 attrs
->bg
= bg
? bg
->pixel
: 0;
503 attrs
->bg_set
= bg
? TRUE
: FALSE
;
508 if (GTK_WIDGET_DRAWABLE (cgrid
) && !cgrid
->frozen
)
509 update_region (cgrid
, x
, y
, i
, 1);
513 gmc_char_grid_set_font (GmcCharGrid
*cgrid
, const char *font_name
)
515 g_return_if_fail (cgrid
!= NULL
);
516 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
519 gdk_font_unref (cgrid
->font
);
521 cgrid
->font
= gdk_fontset_load (font_name
);
524 cgrid
->font
= gdk_fontset_load (DEFAULT_FONT
);
526 cgrid
->char_width
= gdk_char_width (cgrid
->font
, ' '); /* assume monospaced font! */
527 cgrid
->char_height
= cgrid
->font
->ascent
+ cgrid
->font
->descent
;
528 cgrid
->char_y
= cgrid
->font
->ascent
;
530 gtk_widget_queue_resize (GTK_WIDGET (cgrid
));
534 gmc_char_grid_set_size (GmcCharGrid
*cgrid
, guint width
, guint height
)
536 gtk_signal_emit (GTK_OBJECT (cgrid
), cgrid_signals
[SIZE_CHANGED
], width
, height
);
540 gmc_char_grid_get_size (GmcCharGrid
*cgrid
, guint
*width
, guint
*height
)
542 g_return_if_fail (cgrid
!= NULL
);
543 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
546 *width
= cgrid
->width
;
549 *height
= cgrid
->height
;
553 gmc_char_grid_freeze (GmcCharGrid
*cgrid
)
555 g_return_if_fail (cgrid
!= NULL
);
556 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
558 cgrid
->frozen
= TRUE
;
562 gmc_char_grid_thaw (GmcCharGrid
*cgrid
)
564 g_return_if_fail (cgrid
!= NULL
);
565 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
567 cgrid
->frozen
= FALSE
;
569 if (GTK_WIDGET_DRAWABLE (cgrid
))
570 gtk_widget_queue_draw (GTK_WIDGET (cgrid
));
574 gmc_char_grid_marshal_signal_1 (GtkObject
*object
, GtkSignalFunc func
, gpointer func_data
, GtkArg
*args
)
576 GmcCharGridSignal1 rfunc
;
578 rfunc
= (GmcCharGridSignal1
) func
;
580 (*rfunc
) (object
, GTK_VALUE_UINT (args
[0]), GTK_VALUE_UINT (args
[1]), func_data
);
584 gmc_char_grid_real_size_changed (GmcCharGrid
*cgrid
, guint width
, guint height
)
590 g_return_if_fail (cgrid
!= NULL
);
591 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
592 g_return_if_fail ((width
> 0) && (height
> 0));
594 if ((width
== cgrid
->width
) && (height
== cgrid
->height
))
598 g_free (cgrid
->chars
);
601 g_free (cgrid
->attrs
);
603 cgrid
->width
= width
;
604 cgrid
->height
= height
;
606 chars
= g_new (char, width
* height
);
607 cgrid
->chars
= chars
;
609 attrs
= g_new (struct attr
, width
* height
);
610 cgrid
->attrs
= attrs
;
612 for (i
= 0; i
< (width
* height
); i
++) {
616 attrs
[i
].fg_set
= FALSE
;
617 attrs
[i
].bg_set
= FALSE
;
620 gtk_widget_queue_resize (GTK_WIDGET (cgrid
));