Defuzzied one entry
[midnight-commander.git] / gnome / gmc-chargrid.c
blobc1a63ee2e47491b79c93e01ca5ee135d559b56b6
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>
6 */
8 #include <config.h>
9 #include <gtk/gtksignal.h>
10 #include <string.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)
23 struct attr {
24 gulong fg;
25 gulong bg;
26 int fg_set : 1;
27 int bg_set : 1;
31 enum {
32 SIZE_CHANGED,
33 LAST_SIGNAL
36 typedef void (* GmcCharGridSignal1) (GtkObject *object,
37 guint arg1,
38 guint arg2,
39 gpointer data);
41 static void gmc_char_grid_marshal_signal_1 (GtkObject *object,
42 GtkSignalFunc func,
43 gpointer func_data,
44 GtkArg *args);
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,
56 GdkRectangle *area);
57 static gint gmc_char_grid_expose (GtkWidget *widget,
58 GdkEventExpose *event);
59 static void gmc_char_grid_real_size_changed (GmcCharGrid *cgrid,
60 guint width,
61 guint height);
64 static GtkWidgetClass *parent_class;
66 static guint cgrid_signals[LAST_SIGNAL] = { 0 };
69 guint
70 gmc_char_grid_get_type (void)
72 static guint cgrid_type = 0;
74 if (!cgrid_type) {
75 GtkTypeInfo cgrid_info = {
76 "GmcCharGrid",
77 sizeof (GmcCharGrid),
78 sizeof (GmcCharGridClass),
79 (GtkClassInitFunc) gmc_char_grid_class_init,
80 (GtkObjectInitFunc) gmc_char_grid_init,
81 (GtkArgSetFunc) NULL,
82 (GtkArgGetFunc) NULL
85 cgrid_type = gtk_type_unique (gtk_widget_get_type (), &cgrid_info);
88 return cgrid_type;
91 static void
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",
104 GTK_RUN_FIRST,
105 object_class->type,
106 GTK_SIGNAL_OFFSET (GmcCharGridClass, size_changed),
107 gmc_char_grid_marshal_signal_1,
108 GTK_TYPE_NONE, 2,
109 GTK_TYPE_UINT,
110 GTK_TYPE_UINT);
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;
125 static void
126 gmc_char_grid_init (GmcCharGrid *cgrid)
128 cgrid->width = 0;
129 cgrid->height = 0;
130 cgrid->chars = NULL;
131 cgrid->attrs = NULL;
132 cgrid->frozen = FALSE;
133 cgrid->font = NULL;
134 cgrid->gc = NULL;
135 cgrid->char_width = 0;
136 cgrid->char_height = 0;
137 cgrid->char_y = 0;
140 GtkWidget *
141 gmc_char_grid_new (void)
143 GmcCharGrid *cgrid;
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);
153 static void
154 gmc_char_grid_destroy (GtkObject *object)
156 GmcCharGrid *cgrid;
158 g_return_if_fail (object != NULL);
159 g_return_if_fail (GMC_IS_CHAR_GRID (object));
161 cgrid = GMC_CHAR_GRID (object);
163 if (cgrid->chars)
164 g_free (cgrid->chars);
166 if (cgrid->attrs)
167 g_free (cgrid->attrs);
169 if (cgrid->font)
170 gdk_font_unref (cgrid->font);
172 if (cgrid->gc)
173 gdk_gc_destroy (cgrid->gc);
175 if (GTK_OBJECT_CLASS (parent_class)->destroy)
176 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
179 static void
180 gmc_char_grid_realize (GtkWidget *widget)
182 GdkWindowAttr attributes;
183 gint attributes_mask;
184 GmcCharGrid *cgrid;
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);
215 static void
216 gmc_char_grid_size_request (GtkWidget *widget, GtkRequisition *requisition)
218 GmcCharGrid *cgrid;
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;
230 static void
231 gmc_char_grid_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
233 GmcCharGrid *cgrid;
234 int w, h;
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));
257 static void
258 update_strip (GmcCharGrid *cgrid, int x, int y, int width)
260 int i;
261 char *chars;
262 struct attr *attrs;
263 int first;
264 gulong color;
265 gulong ocolor;
266 GdkColor gcolor;
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.
275 i = 0;
277 while (i < width) {
278 first = i;
279 ocolor = attrs[i].bg_set ? attrs[i].bg : GTK_WIDGET (cgrid)->style->bg[GTK_STATE_NORMAL].pixel;
281 do {
282 i++;
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,
290 cgrid->gc,
291 TRUE,
292 (first + x) * cgrid->char_width,
293 y * cgrid->char_height,
294 cgrid->char_width * (i - first),
295 cgrid->char_height);
298 /* Now paint the text */
300 i = 0;
302 while (i < width) {
303 first = i;
304 ocolor = attrs[i].fg_set ? attrs[i].fg : GTK_WIDGET (cgrid)->style->fg[GTK_STATE_NORMAL].pixel;
306 do {
307 i++;
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,
315 cgrid->font,
316 cgrid->gc,
317 (first + x) * cgrid->char_width,
318 y * cgrid->char_height + cgrid->char_y,
319 &chars[first],
320 i - first);
324 static void
325 update_region (GmcCharGrid *cgrid, int x, int y, int width, int height)
327 int i;
329 if ((width == 0) || (height == 0))
330 return;
332 x = MAX (0, x);
333 y = MAX (0, y);
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);
341 static void
342 paint (GmcCharGrid *cgrid, GdkRectangle *area)
344 int x1, y1, x2, y2;
346 /* This logic is shamelessly ripped from gtkterm :-) - Federico */
348 if (area->width > 1)
349 area->width--;
350 else if (area->x > 0)
351 area->x--;
353 if (area->height > 1)
354 area->height--;
355 else if (area->y > 0)
356 area->y--;
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);
367 static void
368 gmc_char_grid_draw (GtkWidget *widget, GdkRectangle *area)
370 GmcCharGrid *cgrid;
371 GdkRectangle w_area;
372 GdkRectangle p_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;
386 w_area.x = 0;
387 w_area.y = 0;
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);
396 static gint
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);
406 return FALSE;
409 void
410 gmc_char_grid_clear (GmcCharGrid *cgrid, int x, int y, int width, int height, GdkColor *bg)
412 int x1, y1, x2, y2;
413 int xx, yy;
414 char *ch;
415 struct attr *attrs;
417 g_return_if_fail (cgrid != NULL);
418 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid));
420 x1 = MAX (x, 0);
421 y1 = MAX (y, 0);
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++) {
430 ch[xx] = ' ';
432 attrs[xx].bg = bg ? bg->pixel : 0;
433 attrs[xx].bg_set = bg ? TRUE : FALSE;
436 ch += cgrid->width;
437 attrs += cgrid->width;
440 if (GTK_WIDGET_DRAWABLE (cgrid) && !cgrid->frozen)
441 update_region (cgrid, x, y, width, height);
444 void
445 gmc_char_grid_put_char (GmcCharGrid *cgrid, int x, int y, GdkColor *fg, GdkColor *bg, char ch)
447 char *chars;
448 struct attr *attrs;
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))
455 return;
457 chars = CHARS (cgrid) + (y * cgrid->width + x);
458 attrs = ATTRS (cgrid) + (y * cgrid->width + x);
460 *chars = ch;
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);
472 void
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));
480 void
481 gmc_char_grid_put_text (GmcCharGrid *cgrid, int x, int y, GdkColor *fg, GdkColor *bg, char *text, int length)
483 char *chars;
484 struct attr *attrs;
485 int i, pos;
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))
493 return;
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++) {
499 *chars++ = *text++;
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;
507 attrs++;
510 if (GTK_WIDGET_DRAWABLE (cgrid) && !cgrid->frozen)
511 update_region (cgrid, x, y, i, 1);
514 void
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));
520 if (cgrid->font)
521 gdk_font_unref (cgrid->font);
523 cgrid->font = gdk_fontset_load (font_name);
525 if (!cgrid->font)
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));
535 void
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);
541 void
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));
547 if (width)
548 *width = cgrid->width;
550 if (height)
551 *height = cgrid->height;
554 void
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;
563 void
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));
575 static void
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);
585 static void
586 gmc_char_grid_real_size_changed (GmcCharGrid *cgrid, guint width, guint height)
588 int i;
589 char *chars;
590 struct attr *attrs;
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))
597 return;
599 if (cgrid->chars)
600 g_free (cgrid->chars);
602 if (cgrid->attrs)
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++) {
615 chars[i] = ' ';
616 attrs[i].fg = 0;
617 attrs[i].bg = 0;
618 attrs[i].fg_set = FALSE;
619 attrs[i].bg_set = FALSE;
622 gtk_widget_queue_resize (GTK_WIDGET (cgrid));