De-fuzzyed some msgs...
[midnight-commander.git] / gnome / gmc-chargrid.c
blobd63100c9ba7afe704e6f6a56e60f56dc866849a7
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 <gtk/gtksignal.h>
9 #include <string.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)
21 struct attr {
22 gulong fg;
23 gulong bg;
24 int fg_set : 1;
25 int bg_set : 1;
29 enum {
30 SIZE_CHANGED,
31 LAST_SIGNAL
34 typedef void (* GmcCharGridSignal1) (GtkObject *object,
35 guint arg1,
36 guint arg2,
37 gpointer data);
39 static void gmc_char_grid_marshal_signal_1 (GtkObject *object,
40 GtkSignalFunc func,
41 gpointer func_data,
42 GtkArg *args);
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,
54 GdkRectangle *area);
55 static gint gmc_char_grid_expose (GtkWidget *widget,
56 GdkEventExpose *event);
57 static void gmc_char_grid_real_size_changed (GmcCharGrid *cgrid,
58 guint width,
59 guint height);
62 static GtkWidgetClass *parent_class;
64 static guint cgrid_signals[LAST_SIGNAL] = { 0 };
67 guint
68 gmc_char_grid_get_type (void)
70 static guint cgrid_type = 0;
72 if (!cgrid_type) {
73 GtkTypeInfo cgrid_info = {
74 "GmcCharGrid",
75 sizeof (GmcCharGrid),
76 sizeof (GmcCharGridClass),
77 (GtkClassInitFunc) gmc_char_grid_class_init,
78 (GtkObjectInitFunc) gmc_char_grid_init,
79 (GtkArgSetFunc) NULL,
80 (GtkArgGetFunc) NULL
83 cgrid_type = gtk_type_unique (gtk_widget_get_type (), &cgrid_info);
86 return cgrid_type;
89 static void
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",
102 GTK_RUN_FIRST,
103 object_class->type,
104 GTK_SIGNAL_OFFSET (GmcCharGridClass, size_changed),
105 gmc_char_grid_marshal_signal_1,
106 GTK_TYPE_NONE, 2,
107 GTK_TYPE_UINT,
108 GTK_TYPE_UINT);
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;
123 static void
124 gmc_char_grid_init (GmcCharGrid *cgrid)
126 cgrid->width = 0;
127 cgrid->height = 0;
128 cgrid->chars = NULL;
129 cgrid->attrs = NULL;
130 cgrid->frozen = FALSE;
131 cgrid->font = NULL;
132 cgrid->gc = NULL;
133 cgrid->char_width = 0;
134 cgrid->char_height = 0;
135 cgrid->char_y = 0;
138 GtkWidget *
139 gmc_char_grid_new (void)
141 GmcCharGrid *cgrid;
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);
151 static void
152 gmc_char_grid_destroy (GtkObject *object)
154 GmcCharGrid *cgrid;
156 g_return_if_fail (object != NULL);
157 g_return_if_fail (GMC_IS_CHAR_GRID (object));
159 cgrid = GMC_CHAR_GRID (object);
161 if (cgrid->chars)
162 g_free (cgrid->chars);
164 if (cgrid->attrs)
165 g_free (cgrid->attrs);
167 if (cgrid->font)
168 gdk_font_unref (cgrid->font);
170 if (cgrid->gc)
171 gdk_gc_destroy (cgrid->gc);
173 if (GTK_OBJECT_CLASS (parent_class)->destroy)
174 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
177 static void
178 gmc_char_grid_realize (GtkWidget *widget)
180 GdkWindowAttr attributes;
181 gint attributes_mask;
182 GmcCharGrid *cgrid;
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);
213 static void
214 gmc_char_grid_size_request (GtkWidget *widget, GtkRequisition *requisition)
216 GmcCharGrid *cgrid;
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;
228 static void
229 gmc_char_grid_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
231 GmcCharGrid *cgrid;
232 int w, h;
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));
255 static void
256 update_strip (GmcCharGrid *cgrid, int x, int y, int width)
258 int i;
259 char *chars;
260 struct attr *attrs;
261 int first;
262 gulong color;
263 gulong ocolor;
264 GdkColor gcolor;
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.
273 i = 0;
275 while (i < width) {
276 first = i;
277 ocolor = attrs[i].bg_set ? attrs[i].bg : GTK_WIDGET (cgrid)->style->bg[GTK_STATE_NORMAL].pixel;
279 do {
280 i++;
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,
288 cgrid->gc,
289 TRUE,
290 (first + x) * cgrid->char_width,
291 y * cgrid->char_height,
292 cgrid->char_width * (i - first),
293 cgrid->char_height);
296 /* Now paint the text */
298 i = 0;
300 while (i < width) {
301 first = i;
302 ocolor = attrs[i].fg_set ? attrs[i].fg : GTK_WIDGET (cgrid)->style->fg[GTK_STATE_NORMAL].pixel;
304 do {
305 i++;
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,
313 cgrid->font,
314 cgrid->gc,
315 (first + x) * cgrid->char_width,
316 y * cgrid->char_height + cgrid->char_y,
317 &chars[first],
318 i - first);
322 static void
323 update_region (GmcCharGrid *cgrid, int x, int y, int width, int height)
325 int i;
327 if ((width == 0) || (height == 0))
328 return;
330 x = MAX (0, x);
331 y = MAX (0, y);
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);
339 static void
340 paint (GmcCharGrid *cgrid, GdkRectangle *area)
342 int x1, y1, x2, y2;
344 /* This logic is shamelessly ripped from gtkterm :-) - Federico */
346 if (area->width > 1)
347 area->width--;
348 else if (area->x > 0)
349 area->x--;
351 if (area->height > 1)
352 area->height--;
353 else if (area->y > 0)
354 area->y--;
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);
365 static void
366 gmc_char_grid_draw (GtkWidget *widget, GdkRectangle *area)
368 GmcCharGrid *cgrid;
369 GdkRectangle w_area;
370 GdkRectangle p_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;
384 w_area.x = 0;
385 w_area.y = 0;
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);
394 static gint
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);
404 return FALSE;
407 void
408 gmc_char_grid_clear (GmcCharGrid *cgrid, int x, int y, int width, int height, GdkColor *bg)
410 int x1, y1, x2, y2;
411 int xx, yy;
412 char *ch;
413 struct attr *attrs;
415 g_return_if_fail (cgrid != NULL);
416 g_return_if_fail (GMC_IS_CHAR_GRID (cgrid));
418 x1 = MAX (x, 0);
419 y1 = MAX (y, 0);
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++) {
428 ch[xx] = ' ';
430 attrs[xx].bg = bg ? bg->pixel : 0;
431 attrs[xx].bg_set = bg ? TRUE : FALSE;
434 ch += cgrid->width;
435 attrs += cgrid->width;
438 if (GTK_WIDGET_DRAWABLE (cgrid) && !cgrid->frozen)
439 update_region (cgrid, x, y, width, height);
442 void
443 gmc_char_grid_put_char (GmcCharGrid *cgrid, int x, int y, GdkColor *fg, GdkColor *bg, char ch)
445 char *chars;
446 struct attr *attrs;
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))
453 return;
455 chars = CHARS (cgrid) + (y * cgrid->width + x);
456 attrs = ATTRS (cgrid) + (y * cgrid->width + x);
458 *chars = ch;
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);
470 void
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));
478 void
479 gmc_char_grid_put_text (GmcCharGrid *cgrid, int x, int y, GdkColor *fg, GdkColor *bg, char *text, int length)
481 char *chars;
482 struct attr *attrs;
483 int i, pos;
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))
491 return;
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++) {
497 *chars++ = *text++;
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;
505 attrs++;
508 if (GTK_WIDGET_DRAWABLE (cgrid) && !cgrid->frozen)
509 update_region (cgrid, x, y, i, 1);
512 void
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));
518 if (cgrid->font)
519 gdk_font_unref (cgrid->font);
521 cgrid->font = gdk_fontset_load (font_name);
523 if (!cgrid->font)
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));
533 void
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);
539 void
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));
545 if (width)
546 *width = cgrid->width;
548 if (height)
549 *height = cgrid->height;
552 void
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;
561 void
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));
573 static void
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);
583 static void
584 gmc_char_grid_real_size_changed (GmcCharGrid *cgrid, guint width, guint height)
586 int i;
587 char *chars;
588 struct attr *attrs;
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))
595 return;
597 if (cgrid->chars)
598 g_free (cgrid->chars);
600 if (cgrid->attrs)
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++) {
613 chars[i] = ' ';
614 attrs[i].fg = 0;
615 attrs[i].bg = 0;
616 attrs[i].fg_set = FALSE;
617 attrs[i].bg_set = FALSE;
620 gtk_widget_queue_resize (GTK_WIDGET (cgrid));