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>
9 #include <gtk/gtksignal.h>
11 #include "gmc-chargrid.h"
14 #define DEFAULT_WIDTH 80
15 #define DEFAULT_HEIGHT 25
16 #define DEFAULT_FONT "-*-*-medium-r-normal-*-14-*-*-*-c-*-*-*," \
17 "-*-*-medium-r-normal-*-14-*-*-*-m-*-*-*,fixed,*"
20 #define CHARS(cgrid) ((char *) cgrid->chars)
21 #define ATTRS(cgrid) ((struct attr *) cgrid->attrs)
36 typedef void (* GmcCharGridSignal1
) (GtkObject
*object
,
41 static void gmc_char_grid_marshal_signal_1 (GtkObject
*object
,
47 static void gmc_char_grid_class_init (GmcCharGridClass
*class);
48 static void gmc_char_grid_init (GmcCharGrid
*cgrid
);
49 static void gmc_char_grid_destroy (GtkObject
*object
);
50 static void gmc_char_grid_realize (GtkWidget
*widget
);
51 static void gmc_char_grid_size_request (GtkWidget
*widget
,
52 GtkRequisition
*requisition
);
53 static void gmc_char_grid_size_allocate (GtkWidget
*widget
,
54 GtkAllocation
*allocation
);
55 static void gmc_char_grid_draw (GtkWidget
*widget
,
57 static gint
gmc_char_grid_expose (GtkWidget
*widget
,
58 GdkEventExpose
*event
);
59 static void gmc_char_grid_real_size_changed (GmcCharGrid
*cgrid
,
64 static GtkWidgetClass
*parent_class
;
66 static guint cgrid_signals
[LAST_SIGNAL
] = { 0 };
70 gmc_char_grid_get_type (void)
72 static guint cgrid_type
= 0;
75 GtkTypeInfo cgrid_info
= {
78 sizeof (GmcCharGridClass
),
79 (GtkClassInitFunc
) gmc_char_grid_class_init
,
80 (GtkObjectInitFunc
) gmc_char_grid_init
,
85 cgrid_type
= gtk_type_unique (gtk_widget_get_type (), &cgrid_info
);
92 gmc_char_grid_class_init (GmcCharGridClass
*class)
94 GtkObjectClass
*object_class
;
95 GtkWidgetClass
*widget_class
;
97 object_class
= (GtkObjectClass
*) class;
98 widget_class
= (GtkWidgetClass
*) class;
100 parent_class
= gtk_type_class (gtk_widget_get_type ());
102 cgrid_signals
[SIZE_CHANGED
] =
103 gtk_signal_new ("size_changed",
106 GTK_SIGNAL_OFFSET (GmcCharGridClass
, size_changed
),
107 gmc_char_grid_marshal_signal_1
,
112 gtk_object_class_add_signals (object_class
, cgrid_signals
, LAST_SIGNAL
);
114 object_class
->destroy
= gmc_char_grid_destroy
;
116 widget_class
->realize
= gmc_char_grid_realize
;
117 widget_class
->size_request
= gmc_char_grid_size_request
;
118 widget_class
->size_allocate
= gmc_char_grid_size_allocate
;
119 widget_class
->draw
= gmc_char_grid_draw
;
120 widget_class
->expose_event
= gmc_char_grid_expose
;
122 class->size_changed
= gmc_char_grid_real_size_changed
;
126 gmc_char_grid_init (GmcCharGrid
*cgrid
)
132 cgrid
->frozen
= FALSE
;
135 cgrid
->char_width
= 0;
136 cgrid
->char_height
= 0;
141 gmc_char_grid_new (void)
145 cgrid
= gtk_type_new (gmc_char_grid_get_type ());
147 gmc_char_grid_set_font (cgrid
, DEFAULT_FONT
);
148 gmc_char_grid_set_size (cgrid
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
150 return GTK_WIDGET (cgrid
);
154 gmc_char_grid_destroy (GtkObject
*object
)
158 g_return_if_fail (object
!= NULL
);
159 g_return_if_fail (GMC_IS_CHAR_GRID (object
));
161 cgrid
= GMC_CHAR_GRID (object
);
164 g_free (cgrid
->chars
);
167 g_free (cgrid
->attrs
);
170 gdk_font_unref (cgrid
->font
);
173 gdk_gc_destroy (cgrid
->gc
);
175 if (GTK_OBJECT_CLASS (parent_class
)->destroy
)
176 (* GTK_OBJECT_CLASS (parent_class
)->destroy
) (object
);
180 gmc_char_grid_realize (GtkWidget
*widget
)
182 GdkWindowAttr attributes
;
183 gint attributes_mask
;
186 g_return_if_fail (widget
!= NULL
);
187 g_return_if_fail (GMC_IS_CHAR_GRID (widget
));
189 cgrid
= GMC_CHAR_GRID (widget
);
191 GTK_WIDGET_SET_FLAGS (widget
, GTK_REALIZED
);
193 attributes
.x
= widget
->allocation
.x
+ (widget
->allocation
.width
- cgrid
->char_width
* cgrid
->width
) / 2;
194 attributes
.y
= widget
->allocation
.y
+ (widget
->allocation
.height
- cgrid
->char_height
* cgrid
->height
) / 2;
195 attributes
.width
= cgrid
->width
* cgrid
->char_width
;
196 attributes
.height
= cgrid
->height
* cgrid
->char_height
;
197 attributes
.window_type
= GDK_WINDOW_CHILD
;
198 attributes
.wclass
= GDK_INPUT_OUTPUT
;
199 attributes
.visual
= gtk_widget_get_visual (widget
);
200 attributes
.colormap
= gtk_widget_get_colormap (widget
);
201 attributes
.event_mask
= (gtk_widget_get_events (widget
)
202 | GDK_EXPOSURE_MASK
);
204 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
206 widget
->window
= gdk_window_new (gtk_widget_get_parent_window (widget
), &attributes
, attributes_mask
);
207 gdk_window_set_user_data (widget
->window
, widget
);
209 cgrid
->gc
= gdk_gc_new (cgrid
->widget
.window
);
211 widget
->style
= gtk_style_attach (widget
->style
, widget
->window
);
212 gtk_style_set_background (widget
->style
, widget
->window
, GTK_STATE_NORMAL
);
216 gmc_char_grid_size_request (GtkWidget
*widget
, GtkRequisition
*requisition
)
220 g_return_if_fail (widget
!= NULL
);
221 g_return_if_fail (GMC_IS_CHAR_GRID (widget
));
222 g_return_if_fail (requisition
!= NULL
);
224 cgrid
= GMC_CHAR_GRID (widget
);
226 requisition
->width
= cgrid
->width
* cgrid
->char_width
;
227 requisition
->height
= cgrid
->height
* cgrid
->char_height
;
231 gmc_char_grid_size_allocate (GtkWidget
*widget
, GtkAllocation
*allocation
)
236 g_return_if_fail (widget
!= NULL
);
237 g_return_if_fail (GMC_IS_CHAR_GRID (widget
));
238 g_return_if_fail (allocation
!= NULL
);
240 widget
->allocation
= *allocation
;
242 cgrid
= GMC_CHAR_GRID (widget
);
244 w
= allocation
->width
/ cgrid
->char_width
;
245 h
= allocation
->height
/ cgrid
->char_height
;
247 if (GTK_WIDGET_REALIZED (widget
))
248 gdk_window_move_resize (widget
->window
,
249 allocation
->x
+ (allocation
->width
- cgrid
->char_width
* cgrid
->width
) / 2,
250 allocation
->y
+ (allocation
->height
- cgrid
->char_height
* cgrid
->height
) / 2,
251 cgrid
->width
* cgrid
->char_width
,
252 cgrid
->height
* cgrid
->char_height
);
254 gmc_char_grid_set_size (cgrid
, MAX (w
, 1), MAX (h
, 1));
258 update_strip (GmcCharGrid
*cgrid
, int x
, int y
, int width
)
268 chars
= CHARS (cgrid
) + (cgrid
->width
* y
+ x
);
269 attrs
= ATTRS (cgrid
) + (cgrid
->width
* y
+ x
);
271 /* First paint the background and then paint the text. We do it in two passes
272 * so that we can paint runs of same-background and foreground more efficiently.
279 ocolor
= attrs
[i
].bg_set
? attrs
[i
].bg
: GTK_WIDGET (cgrid
)->style
->bg
[GTK_STATE_NORMAL
].pixel
;
283 color
= attrs
[i
].bg_set
? attrs
[i
].bg
: GTK_WIDGET (cgrid
)->style
->bg
[GTK_STATE_NORMAL
].pixel
;
284 } while ((i
< width
) && (color
== ocolor
));
286 gcolor
.pixel
= ocolor
;
287 gdk_gc_set_foreground (cgrid
->gc
, &gcolor
);
289 gdk_draw_rectangle (cgrid
->widget
.window
,
292 (first
+ x
) * cgrid
->char_width
,
293 y
* cgrid
->char_height
,
294 cgrid
->char_width
* (i
- first
),
298 /* Now paint the text */
304 ocolor
= attrs
[i
].fg_set
? attrs
[i
].fg
: GTK_WIDGET (cgrid
)->style
->fg
[GTK_STATE_NORMAL
].pixel
;
308 color
= attrs
[i
].fg_set
? attrs
[i
].fg
: GTK_WIDGET (cgrid
)->style
->fg
[GTK_STATE_NORMAL
].pixel
;
309 } while ((i
< width
) && (color
== ocolor
));
311 gcolor
.pixel
= ocolor
;
312 gdk_gc_set_foreground (cgrid
->gc
, &gcolor
);
314 gdk_draw_text (cgrid
->widget
.window
,
317 (first
+ x
) * cgrid
->char_width
,
318 y
* cgrid
->char_height
+ cgrid
->char_y
,
325 update_region (GmcCharGrid
*cgrid
, int x
, int y
, int width
, int height
)
329 if ((width
== 0) || (height
== 0))
334 width
= MIN (width
, cgrid
->width
- x
);
335 height
= MIN (height
, cgrid
->height
- y
);
337 for (i
= 0; i
< height
; i
++)
338 update_strip (cgrid
, x
, y
+ i
, width
);
342 paint (GmcCharGrid
*cgrid
, GdkRectangle
*area
)
346 /* This logic is shamelessly ripped from gtkterm :-) - Federico */
350 else if (area
->x
> 0)
353 if (area
->height
> 1)
355 else if (area
->y
> 0)
358 x1
= area
->x
/ cgrid
->char_width
;
359 y1
= area
->y
/ cgrid
->char_height
;
361 x2
= (area
->x
+ area
->width
) / cgrid
->char_width
;
362 y2
= (area
->y
+ area
->height
) / cgrid
->char_height
;
364 update_region (cgrid
, x1
, y1
, (x2
- x1
) + 1, (y2
- y1
) + 1);
368 gmc_char_grid_draw (GtkWidget
*widget
, GdkRectangle
*area
)
374 g_return_if_fail (widget
!= NULL
);
375 g_return_if_fail (GMC_IS_CHAR_GRID (widget
));
376 g_return_if_fail (area
!= NULL
);
378 if (GTK_WIDGET_DRAWABLE (widget
)) {
379 cgrid
= GMC_CHAR_GRID (widget
);
381 /* Offset the area because the window does not fill thea allocation */
383 area
->x
-= (widget
->allocation
.width
- cgrid
->char_width
* cgrid
->width
) / 2;
384 area
->y
-= (widget
->allocation
.height
- cgrid
->char_height
* cgrid
->height
) / 2;
388 w_area
.width
= cgrid
->char_width
* cgrid
->width
;
389 w_area
.height
= cgrid
->char_height
* cgrid
->height
;
391 if (gdk_rectangle_intersect (area
, &w_area
, &p_area
))
392 paint (cgrid
, &p_area
);
397 gmc_char_grid_expose (GtkWidget
*widget
, GdkEventExpose
*event
)
399 g_return_val_if_fail (widget
!= NULL
, FALSE
);
400 g_return_val_if_fail (GMC_IS_CHAR_GRID (widget
), FALSE
);
401 g_return_val_if_fail (event
!= NULL
, FALSE
);
403 if (GTK_WIDGET_DRAWABLE (widget
))
404 paint (GMC_CHAR_GRID (widget
), &event
->area
);
410 gmc_char_grid_clear (GmcCharGrid
*cgrid
, int x
, int y
, int width
, int height
, GdkColor
*bg
)
417 g_return_if_fail (cgrid
!= NULL
);
418 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
422 x2
= MIN (x
+ width
, cgrid
->width
);
423 y2
= MIN (y
+ height
, cgrid
->height
);
425 ch
= CHARS (cgrid
) + (y1
* cgrid
->width
);
426 attrs
= ATTRS (cgrid
) + (y1
* cgrid
->width
);
428 for (yy
= y1
; yy
< y2
; yy
++) {
429 for (xx
= x1
; xx
< x2
; xx
++) {
432 attrs
[xx
].bg
= bg
? bg
->pixel
: 0;
433 attrs
[xx
].bg_set
= bg
? TRUE
: FALSE
;
437 attrs
+= cgrid
->width
;
440 if (GTK_WIDGET_DRAWABLE (cgrid
) && !cgrid
->frozen
)
441 update_region (cgrid
, x
, y
, width
, height
);
445 gmc_char_grid_put_char (GmcCharGrid
*cgrid
, int x
, int y
, GdkColor
*fg
, GdkColor
*bg
, char ch
)
450 g_return_if_fail (cgrid
!= NULL
);
451 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
453 if ((x
< 0) || (x
>= cgrid
->width
)
454 || (y
< 0) || (y
>= cgrid
->height
))
457 chars
= CHARS (cgrid
) + (y
* cgrid
->width
+ x
);
458 attrs
= ATTRS (cgrid
) + (y
* cgrid
->width
+ x
);
462 attrs
->fg
= fg
? fg
->pixel
: 0;
463 attrs
->fg_set
= fg
? TRUE
: FALSE
;
465 attrs
->bg
= bg
? bg
->pixel
: 0;
466 attrs
->bg_set
= bg
? TRUE
: FALSE
;
468 if (GTK_WIDGET_DRAWABLE (cgrid
) && !cgrid
->frozen
)
469 update_region (cgrid
, x
, y
, 1, 1);
473 gmc_char_grid_put_string (GmcCharGrid
*cgrid
, int x
, int y
, GdkColor
*fg
, GdkColor
*bg
, char *str
)
475 g_return_if_fail (str
!= NULL
);
477 gmc_char_grid_put_text (cgrid
, x
, y
, fg
, bg
, str
, strlen (str
));
481 gmc_char_grid_put_text (GmcCharGrid
*cgrid
, int x
, int y
, GdkColor
*fg
, GdkColor
*bg
, char *text
, int length
)
487 g_return_if_fail (cgrid
!= NULL
);
488 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
489 g_return_if_fail (text
!= NULL
);
491 if ((x
< 0) || (x
>= cgrid
->width
)
492 || (y
< 0) || (y
>= cgrid
->height
))
495 chars
= CHARS (cgrid
) + (cgrid
->width
* y
+ x
);
496 attrs
= ATTRS (cgrid
) + (cgrid
->width
* y
+ x
);
498 for (i
= 0, pos
= x
; (i
< length
) && (pos
< cgrid
->width
); i
++, pos
++) {
501 attrs
->fg
= fg
? fg
->pixel
: 0;
502 attrs
->fg_set
= fg
? TRUE
: FALSE
;
504 attrs
->bg
= bg
? bg
->pixel
: 0;
505 attrs
->bg_set
= bg
? TRUE
: FALSE
;
510 if (GTK_WIDGET_DRAWABLE (cgrid
) && !cgrid
->frozen
)
511 update_region (cgrid
, x
, y
, i
, 1);
515 gmc_char_grid_set_font (GmcCharGrid
*cgrid
, const char *font_name
)
517 g_return_if_fail (cgrid
!= NULL
);
518 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
521 gdk_font_unref (cgrid
->font
);
523 cgrid
->font
= gdk_fontset_load (font_name
);
526 cgrid
->font
= gdk_fontset_load (DEFAULT_FONT
);
528 cgrid
->char_width
= gdk_char_width (cgrid
->font
, ' '); /* assume monospaced font! */
529 cgrid
->char_height
= cgrid
->font
->ascent
+ cgrid
->font
->descent
;
530 cgrid
->char_y
= cgrid
->font
->ascent
;
532 gtk_widget_queue_resize (GTK_WIDGET (cgrid
));
536 gmc_char_grid_set_size (GmcCharGrid
*cgrid
, guint width
, guint height
)
538 gtk_signal_emit (GTK_OBJECT (cgrid
), cgrid_signals
[SIZE_CHANGED
], width
, height
);
542 gmc_char_grid_get_size (GmcCharGrid
*cgrid
, guint
*width
, guint
*height
)
544 g_return_if_fail (cgrid
!= NULL
);
545 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
548 *width
= cgrid
->width
;
551 *height
= cgrid
->height
;
555 gmc_char_grid_freeze (GmcCharGrid
*cgrid
)
557 g_return_if_fail (cgrid
!= NULL
);
558 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
560 cgrid
->frozen
= TRUE
;
564 gmc_char_grid_thaw (GmcCharGrid
*cgrid
)
566 g_return_if_fail (cgrid
!= NULL
);
567 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
569 cgrid
->frozen
= FALSE
;
571 if (GTK_WIDGET_DRAWABLE (cgrid
))
572 gtk_widget_queue_draw (GTK_WIDGET (cgrid
));
576 gmc_char_grid_marshal_signal_1 (GtkObject
*object
, GtkSignalFunc func
, gpointer func_data
, GtkArg
*args
)
578 GmcCharGridSignal1 rfunc
;
580 rfunc
= (GmcCharGridSignal1
) func
;
582 (*rfunc
) (object
, GTK_VALUE_UINT (args
[0]), GTK_VALUE_UINT (args
[1]), func_data
);
586 gmc_char_grid_real_size_changed (GmcCharGrid
*cgrid
, guint width
, guint height
)
592 g_return_if_fail (cgrid
!= NULL
);
593 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid
));
594 g_return_if_fail ((width
> 0) && (height
> 0));
596 if ((width
== cgrid
->width
) && (height
== cgrid
->height
))
600 g_free (cgrid
->chars
);
603 g_free (cgrid
->attrs
);
605 cgrid
->width
= width
;
606 cgrid
->height
= height
;
608 chars
= g_new (char, width
* height
);
609 cgrid
->chars
= chars
;
611 attrs
= g_new (struct attr
, width
* height
);
612 cgrid
->attrs
= attrs
;
614 for (i
= 0; i
< (width
* height
); i
++) {
618 attrs
[i
].fg_set
= FALSE
;
619 attrs
[i
].bg_set
= FALSE
;
622 gtk_widget_queue_resize (GTK_WIDGET (cgrid
));