*** empty log message ***
[nvi.git] / gtk / gtkviscreen.c
blob73398d7e2b3020ae42a3d5e06502817740e0a76c
1 #include <stdio.h>
2 #include <string.h>
4 #include <gtk/gtkmain.h>
5 #include <gtk/gtksignal.h>
6 #include "gtkviscreen.h"
8 #define DEFAULT_VI_SCREEN_WIDTH_CHARS 80
9 #define DEFAULT_VI_SCREEN_HEIGHT_LINES 25
10 #define VI_SCREEN_BORDER_ROOM 1
12 enum {
13 ARG_0,
14 ARG_VADJUSTMENT,
17 enum {
18 RESIZED,
19 LAST_SIGNAL
22 static void gtk_vi_screen_class_init (GtkViScreenClass *klass);
23 static void gtk_vi_screen_set_arg (GtkObject *object,
24 GtkArg *arg,
25 guint arg_id);
26 static void gtk_vi_screen_get_arg (GtkObject *object,
27 GtkArg *arg,
28 guint arg_id);
29 static void gtk_vi_screen_init (GtkViScreen *vi);
30 static void gtk_vi_screen_destroy (GtkObject *object);
31 static void gtk_vi_screen_realize (GtkWidget *widget);
33 static void gtk_vi_screen_map (GtkWidget *widget);
34 static void gtk_vi_screen_unmap (GtkWidget *widget);
36 static void gtk_vi_screen_size_request (GtkWidget *widget,
37 GtkRequisition *requisition);
38 static void gtk_vi_screen_size_allocate (GtkWidget *widget,
39 GtkAllocation *allocation);
41 static void gtk_vi_screen_adjustment (GtkAdjustment *adjustment,
42 GtkViScreen *text);
45 static void gtk_vi_screen_draw (GtkWidget *widget,
46 GdkRectangle *area);
47 static gint gtk_vi_screen_expose (GtkWidget *widget,
48 GdkEventExpose *event);
50 static void recompute_geometry (GtkViScreen* vi);
51 static void expose_text (GtkViScreen* vi, GdkRectangle *area, gboolean cursor);
52 static void draw_lines(GtkViScreen *vi, gint y, gint x, gint ymax, gint xmax);
53 static void mark_lines(GtkViScreen *vi, gint ymin, gint xmin, gint ymax, gint xmax);
55 static GtkWidgetClass *parent_class = NULL;
56 static guint vi_screen_signals[LAST_SIGNAL] = { 0 };
58 #define CharAt(scr,y,x) scr->chars + (y) * scr->cols + x
59 #define FlagAt(scr,y,x) (scr->reverse + (y) * scr->cols + x)
61 #define COLOR_STANDARD 0x00
62 #define COLOR_STANDOUT 0x01
64 /* XXX */
65 enum { SA_ALTERNATE, SA_INVERSE };
67 void
68 gtk_vi_screen_attribute(GtkViScreen *vi, gint attribute, gint on)
70 switch (attribute) {
71 case SA_ALTERNATE:
73 fprintf(stderr, "alternate: %d\n", on);
75 break;
76 case SA_INVERSE:
77 vi->color = on ? COLOR_STANDOUT : COLOR_STANDARD;
78 break;
82 void
83 gtk_vi_screen_move(GtkViScreen *vi, gint row, gint col)
85 mark_lines(vi, vi->cury, vi->curx, vi->cury+1, vi->curx+1);
86 vi->curx = col;
87 vi->cury = row;
88 mark_lines(vi, vi->cury, vi->curx, vi->cury+1, vi->curx+1);
91 void
92 gtk_vi_screen_clrtoel (GtkViScreen *vi)
94 memset(CharAt(vi,vi->cury,vi->curx), ' ', vi->cols - vi->curx);
95 memset(FlagAt(vi,vi->cury,vi->curx), COLOR_STANDARD, vi->cols - vi->curx);
96 mark_lines(vi, vi->cury, vi->curx, vi->cury+1, vi->cols);
99 void
100 gtk_vi_screen_addstr(GtkViScreen *vi, const char *str, int len)
103 fprintf(stderr, "%d --%.*s--\n", len, len, str);
105 memcpy(CharAt(vi,vi->cury,vi->curx), str, len);
106 memset(FlagAt(vi,vi->cury,vi->curx), vi->color, len);
107 mark_lines(vi, vi->cury, vi->curx, vi->cury+1, vi->curx + len);
108 if ((vi->curx += len) >= vi->cols) {
109 if (++vi->cury >= vi->rows) {
110 vi->cury = vi->rows-1;
111 vi->curx = vi->cols-1;
112 } else {
113 vi->curx = 0;
118 void
119 gtk_vi_screen_deleteln(GtkViScreen *vi)
121 gint y = vi->cury;
122 gint rows = vi->rows - (y+1);
124 memmove(CharAt(vi,y,0), CharAt(vi,y+1,0), rows * vi->cols);
125 memset(CharAt(vi,vi->rows-1,0), ' ', vi->cols);
126 memmove(FlagAt(vi,y,0), FlagAt(vi,y+1,0), rows * vi->cols);
127 memset(FlagAt(vi,vi->rows-1,0), COLOR_STANDARD, vi->cols);
128 mark_lines(vi, y, 0, vi->rows, vi->cols);
131 void
132 gtk_vi_screen_insertln(GtkViScreen *vi)
134 gint y = vi->cury;
135 gint rows = vi->rows - (y+1);
137 memmove(CharAt(vi,y+1,0), CharAt(vi,y,0), rows * vi->cols);
138 memset(CharAt(vi,y,0), ' ', vi->cols);
139 memmove(FlagAt(vi,y+1,0), FlagAt(vi,y,0), rows * vi->cols);
140 memset(FlagAt(vi,y,0), COLOR_STANDARD, vi->cols);
141 mark_lines(vi, y, 0, vi->rows, vi->cols);
144 void
145 gtk_vi_screen_refresh(GtkViScreen *vi)
147 if (vi->marked_maxy == 0)
148 return;
149 draw_lines(vi, vi->marked_y, vi->marked_x, vi->marked_maxy, vi->marked_maxx);
150 vi->marked_x = vi->cols;
151 vi->marked_y = vi->rows;
152 vi->marked_maxx = 0;
153 vi->marked_maxy = 0;
156 void
157 gtk_vi_screen_rewrite(GtkViScreen *vi, gint row)
159 memset(FlagAt(vi,row,0), COLOR_STANDARD, vi->cols);
160 mark_lines(vi, row, 0, row+1, vi->cols);
163 GtkType
164 gtk_vi_screen_get_type (void)
166 static GtkType vi_screen_type = 0;
168 if (!vi_screen_type)
170 static const GtkTypeInfo vi_screen_info =
172 "GtkViScreen",
173 sizeof (GtkViScreen),
174 sizeof (GtkViScreenClass),
175 (GtkClassInitFunc) gtk_vi_screen_class_init,
176 (GtkObjectInitFunc) gtk_vi_screen_init,
177 /* reserved_1 */ NULL,
178 /* reserved_2 */ NULL,
179 (GtkClassInitFunc) NULL,
182 vi_screen_type = gtk_type_unique (GTK_TYPE_WIDGET, &vi_screen_info);
185 return vi_screen_type;
188 static void
189 gtk_vi_screen_class_init (GtkViScreenClass *class)
191 GtkObjectClass *object_class;
192 GtkWidgetClass *widget_class;
194 object_class = (GtkObjectClass*) class;
195 widget_class = (GtkWidgetClass*) class;
196 parent_class = gtk_type_class (GTK_TYPE_WIDGET);
198 vi_screen_signals[RESIZED] =
199 gtk_signal_new ("resized",
200 GTK_RUN_FIRST,
201 object_class->type,
202 GTK_SIGNAL_OFFSET (GtkViScreenClass, resized),
203 gtk_marshal_NONE__INT_INT,
204 GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT, 0);
206 gtk_object_class_add_signals(object_class, vi_screen_signals, LAST_SIGNAL);
208 gtk_object_add_arg_type ("GtkViScreen::vadjustment",
209 GTK_TYPE_ADJUSTMENT,
210 GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
211 ARG_VADJUSTMENT);
213 object_class->set_arg = gtk_vi_screen_set_arg;
214 object_class->get_arg = gtk_vi_screen_get_arg;
215 object_class->destroy = gtk_vi_screen_destroy;
217 widget_class->realize = gtk_vi_screen_realize;
219 widget_class->map = gtk_vi_screen_map;
220 widget_class->unmap = gtk_vi_screen_unmap;
222 widget_class->size_request = gtk_vi_screen_size_request;
223 widget_class->size_allocate = gtk_vi_screen_size_allocate;
224 widget_class->draw = gtk_vi_screen_draw;
225 widget_class->expose_event = gtk_vi_screen_expose;
227 class->rename = NULL;
228 class->resized = NULL;
231 static void
232 gtk_vi_screen_set_arg (GtkObject *object,
233 GtkArg *arg,
234 guint arg_id)
236 GtkViScreen *vi_screen;
238 vi_screen = GTK_VI_SCREEN (object);
240 switch (arg_id)
242 case ARG_VADJUSTMENT:
243 gtk_vi_screen_set_adjustment (vi_screen, GTK_VALUE_POINTER (*arg));
244 break;
245 default:
246 break;
250 static void
251 gtk_vi_screen_get_arg (GtkObject *object,
252 GtkArg *arg,
253 guint arg_id)
255 GtkViScreen *vi_screen;
257 vi_screen = GTK_VI_SCREEN (object);
259 switch (arg_id)
261 case ARG_VADJUSTMENT:
262 GTK_VALUE_POINTER (*arg) = vi_screen->vadj;
263 break;
264 default:
265 arg->type = GTK_TYPE_INVALID;
266 break;
270 static void
271 gtk_vi_screen_init (GtkViScreen *vi)
273 GtkStyle *style;
275 GTK_WIDGET_SET_FLAGS (vi, GTK_CAN_FOCUS);
277 vi->text_area = NULL;
278 vi->chars = 0;
279 vi->reverse = 0;
280 vi->color = COLOR_STANDARD;
281 vi->cols = 0;
282 vi->rows = 0;
284 style = gtk_style_copy(GTK_WIDGET(vi)->style);
285 style->font = gdk_font_load("fixed");
286 GTK_WIDGET(vi)->style = style;
289 static void
290 gtk_vi_screen_destroy (GtkObject *object)
292 GtkViScreen *vi_screen;
294 g_return_if_fail (object != NULL);
295 g_return_if_fail (GTK_IS_VI_SCREEN (object));
297 vi_screen = (GtkViScreen*) object;
300 gtk_signal_disconnect_by_data (GTK_OBJECT (vi_screen->vadj), vi_screen);
303 GTK_OBJECT_CLASS(parent_class)->destroy (object);
306 GtkWidget*
307 gtk_vi_screen_new (GtkAdjustment *vadj)
309 GtkWidget *vi;
311 vi = gtk_widget_new (GTK_TYPE_VI_SCREEN,
312 "vadjustment", vadj,
313 NULL);
316 return vi;
319 void
320 gtk_vi_screen_set_adjustment (GtkViScreen *vi_screen,
321 GtkAdjustment *vadj)
323 g_return_if_fail (vi_screen != NULL);
324 g_return_if_fail (GTK_IS_VI_SCREEN (vi_screen));
325 if (vadj)
326 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
327 else
328 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 1.0, 0.0, 1.0, 0.0, 0.0));
330 if (vi_screen->vadj && (vi_screen->vadj != vadj))
332 gtk_signal_disconnect_by_data (GTK_OBJECT (vi_screen->vadj), vi_screen);
333 gtk_object_unref (GTK_OBJECT (vi_screen->vadj));
336 if (vi_screen->vadj != vadj)
338 vi_screen->vadj = vadj;
339 gtk_object_ref (GTK_OBJECT (vi_screen->vadj));
340 gtk_object_sink (GTK_OBJECT (vi_screen->vadj));
343 gtk_signal_connect (GTK_OBJECT (vi_screen->vadj), "changed",
344 (GtkSignalFunc) gtk_vi_screen_adjustment,
345 vi_screen);
346 gtk_signal_connect (GTK_OBJECT (vi_screen->vadj), "value_changed",
347 (GtkSignalFunc) gtk_vi_screen_adjustment,
348 vi_screen);
349 gtk_signal_connect (GTK_OBJECT (vi_screen->vadj), "disconnect",
350 (GtkSignalFunc) gtk_vi_screen_disconnect,
351 vi_screen);
352 gtk_vi_screen_adjustment (vadj, vi_screen);
357 static void
358 gtk_vi_screen_realize (GtkWidget *widget)
360 GtkViScreen *vi;
361 GdkWindowAttr attributes;
362 gint attributes_mask;
364 g_return_if_fail (widget != NULL);
365 g_return_if_fail (GTK_IS_VI_SCREEN (widget));
367 vi = GTK_VI_SCREEN (widget);
368 GTK_WIDGET_SET_FLAGS (vi, GTK_REALIZED);
370 attributes.window_type = GDK_WINDOW_CHILD;
371 attributes.x = widget->allocation.x;
372 attributes.y = widget->allocation.y;
373 attributes.width = widget->allocation.width;
374 attributes.height = widget->allocation.height;
375 attributes.wclass = GDK_INPUT_OUTPUT;
376 attributes.visual = gtk_widget_get_visual (widget);
377 attributes.colormap = gtk_widget_get_colormap (widget);
378 attributes.event_mask = gtk_widget_get_events (widget);
379 attributes.event_mask |= (GDK_EXPOSURE_MASK |
380 GDK_BUTTON_PRESS_MASK |
381 GDK_BUTTON_RELEASE_MASK |
382 GDK_BUTTON_MOTION_MASK |
383 GDK_ENTER_NOTIFY_MASK |
384 GDK_LEAVE_NOTIFY_MASK |
385 GDK_KEY_PRESS_MASK);
386 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
388 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
389 gdk_window_set_user_data (widget->window, vi);
391 attributes.x = (widget->style->klass->xthickness + VI_SCREEN_BORDER_ROOM);
392 attributes.y = (widget->style->klass->ythickness + VI_SCREEN_BORDER_ROOM);
393 attributes.width = MAX (1, (gint)widget->allocation.width - (gint)attributes.x * 2);
394 attributes.height = MAX (1, (gint)widget->allocation.height - (gint)attributes.y * 2);
396 vi->text_area = gdk_window_new (widget->window, &attributes, attributes_mask);
397 gdk_window_set_user_data (vi->text_area, vi);
399 widget->style = gtk_style_attach (widget->style, widget->window);
401 /* Can't call gtk_style_set_background here because it's handled specially */
402 gdk_window_set_background (widget->window, &widget->style->base[GTK_STATE_NORMAL]);
403 gdk_window_set_background (vi->text_area, &widget->style->base[GTK_STATE_NORMAL]);
405 vi->gc = gdk_gc_new (vi->text_area);
406 /* What's this ? */
407 gdk_gc_set_exposures (vi->gc, TRUE);
408 gdk_gc_set_foreground (vi->gc, &widget->style->text[GTK_STATE_NORMAL]);
410 vi->reverse_gc = gdk_gc_new (vi->text_area);
411 gdk_gc_set_foreground (vi->reverse_gc, &widget->style->base[GTK_STATE_NORMAL]);
413 gdk_window_show (vi->text_area);
415 recompute_geometry (vi);
418 static void
419 gtk_vi_screen_size_request (GtkWidget *widget,
420 GtkRequisition *requisition)
422 gint xthickness;
423 gint ythickness;
424 gint char_height;
425 gint char_width;
426 GtkViScreen *vi;
428 g_return_if_fail (widget != NULL);
429 g_return_if_fail (GTK_IS_VI_SCREEN (widget));
430 g_return_if_fail (requisition != NULL);
432 vi = GTK_VI_SCREEN (widget);
434 xthickness = widget->style->klass->xthickness + VI_SCREEN_BORDER_ROOM;
435 ythickness = widget->style->klass->ythickness + VI_SCREEN_BORDER_ROOM;
437 vi->ch_ascent = widget->style->font->ascent;
438 vi->ch_height = (widget->style->font->ascent + widget->style->font->descent);
439 vi->ch_width = gdk_text_width (widget->style->font, "A", 1);
440 char_height = DEFAULT_VI_SCREEN_HEIGHT_LINES * vi->ch_height;
441 char_width = DEFAULT_VI_SCREEN_WIDTH_CHARS * vi->ch_width;
443 requisition->width = char_width + xthickness * 2;
444 requisition->height = char_height + ythickness * 2;
447 static void
448 gtk_vi_screen_size_allocate (GtkWidget *widget,
449 GtkAllocation *allocation)
451 GtkViScreen *vi;
453 g_return_if_fail (widget != NULL);
454 g_return_if_fail (GTK_IS_VI_SCREEN (widget));
455 g_return_if_fail (allocation != NULL);
457 vi = GTK_VI_SCREEN (widget);
459 widget->allocation = *allocation;
460 if (GTK_WIDGET_REALIZED (widget))
462 gdk_window_move_resize (widget->window,
463 allocation->x, allocation->y,
464 allocation->width, allocation->height);
466 gdk_window_move_resize (vi->text_area,
467 widget->style->klass->xthickness + VI_SCREEN_BORDER_ROOM,
468 widget->style->klass->ythickness + VI_SCREEN_BORDER_ROOM,
469 MAX (1, (gint)widget->allocation.width - (gint)(widget->style->klass->xthickness +
470 (gint)VI_SCREEN_BORDER_ROOM) * 2),
471 MAX (1, (gint)widget->allocation.height - (gint)(widget->style->klass->ythickness +
472 (gint)VI_SCREEN_BORDER_ROOM) * 2));
474 recompute_geometry (vi);
479 static void
480 gtk_vi_screen_adjustment (GtkAdjustment *adjustment,
481 GtkViScreen *vi_screen)
483 g_return_if_fail (adjustment != NULL);
484 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
485 g_return_if_fail (vi_screen != NULL);
486 g_return_if_fail (GTK_IS_VI_SCREEN (vi_screen));
491 static void
492 gtk_vi_screen_draw (GtkWidget *widget,
493 GdkRectangle *area)
495 g_return_if_fail (widget != NULL);
496 g_return_if_fail (GTK_IS_VI_SCREEN (widget));
497 g_return_if_fail (area != NULL);
499 if (GTK_WIDGET_DRAWABLE (widget))
501 expose_text (GTK_VI_SCREEN (widget), area, TRUE);
502 gtk_widget_draw_focus (widget);
506 static gint
507 gtk_vi_screen_expose (GtkWidget *widget,
508 GdkEventExpose *event)
510 g_return_val_if_fail (widget != NULL, FALSE);
511 g_return_val_if_fail (GTK_IS_VI_SCREEN (widget), FALSE);
512 g_return_val_if_fail (event != NULL, FALSE);
514 if (event->window == GTK_VI_SCREEN (widget)->text_area)
516 expose_text (GTK_VI_SCREEN (widget), &event->area, TRUE);
518 else if (event->count == 0)
520 gtk_widget_draw_focus (widget);
523 return FALSE;
526 static void
527 recompute_geometry (GtkViScreen* vi)
529 //gint xthickness;
530 //gint ythickness;
531 gint height;
532 gint width;
533 gint rows, cols;
535 //xthickness = widget->style->klass->xthickness + VI_SCREEN_BORDER_ROOM;
536 //ythickness = widget->style->klass->ythickness + VI_SCREEN_BORDER_ROOM;
538 gdk_window_get_size (vi->text_area, &width, &height);
540 rows = height / vi->ch_height;
541 cols = width / vi->ch_width;
543 if (rows == vi->rows && cols == vi->cols)
544 return;
546 vi->marked_x = vi->cols = cols;
547 vi->marked_y = vi->rows = rows;
548 vi->marked_maxx = 0;
549 vi->marked_maxy = 0;
551 g_free(vi->chars);
552 vi->chars = g_new(gchar, vi->rows*vi->cols);
553 memset(vi->chars, ' ', vi->rows*vi->cols);
554 g_free(vi->reverse);
555 vi->reverse = g_new(gchar, vi->rows*vi->cols);
556 memset(vi->reverse, 0, vi->rows*vi->cols);
558 gtk_signal_emit(GTK_OBJECT(vi), vi_screen_signals[RESIZED], vi->rows, vi->cols);
561 static void
562 expose_text (GtkViScreen* vi, GdkRectangle *area, gboolean cursor)
564 gint ymax;
565 gint xmax, xmin;
567 gdk_window_clear_area (vi->text_area, area->x, area->y,
568 area->width, area->height);
569 ymax = MIN((area->y + area->height + vi->ch_height - 1) / vi->ch_height,
570 vi->rows);
571 xmin = area->x / vi->ch_width;
572 xmax = MIN((area->x + area->width + vi->ch_width - 1) / vi->ch_width,
573 vi->cols);
574 draw_lines(vi, area->y / vi->ch_height, xmin, ymax, xmax);
577 #define Inverse(screen,y,x) \
578 ((*FlagAt(screen,y,x) == COLOR_STANDOUT) ^ \
579 (screen->cury == y && screen->curx == x))
581 static void
582 draw_lines(GtkViScreen *vi, gint ymin, gint xmin, gint ymax, gint xmax)
584 gint y, x, len;
585 gchar *line;
586 GdkGC *fg, *bg;
588 for (y = ymin, line = vi->chars + y*vi->cols;
589 y < ymax; ++y, line += vi->cols) {
590 for (x = xmin; x < xmax; x+=len) {
591 gchar inverse;
592 for (inverse = Inverse(vi,y,x), len = 1;
593 x+len < xmax && Inverse(vi,y,x+len) == inverse; ++len);
594 if (inverse) {
595 fg = vi->reverse_gc;
596 bg = vi->gc;
597 } else {
598 bg = vi->reverse_gc;
599 fg = vi->gc;
601 gdk_draw_rectangle(vi->text_area, bg, 1, x * vi->ch_width,
602 y * vi->ch_height, len * vi->ch_width,
603 vi->ch_height);
604 gdk_draw_text (vi->text_area, GTK_WIDGET(vi)->style->font, fg,
605 x * vi->ch_width,
606 y * vi->ch_height + vi->ch_ascent,
607 line+x, len);
612 static void
613 mark_lines(GtkViScreen *vi, gint ymin, gint xmin, gint ymax, gint xmax)
615 if (ymin < vi->marked_y) vi->marked_y = ymin;
616 if (xmin < vi->marked_x) vi->marked_x = xmin;
617 if (ymax > vi->marked_maxy) vi->marked_maxy = ymax;
618 if (xmax > vi->marked_maxx) vi->marked_maxx = xmax;