align CHAR_T string in log
[nvi.git] / motif_l / m_vi.c
blob7a1542675fbeb2fc5292ec2650ce719cc19be2e3
1 /*-
2 * Copyright (c) 1996
3 * Rob Zimmermann. All rights reserved.
4 * Copyright (c) 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
8 */
10 #include "config.h"
12 #ifndef lint
13 static const char sccsid[] = "$Id: m_vi.c,v 8.38 2000/07/05 11:33:19 skimo Exp $ (Berkeley) $Date: 2000/07/05 11:33:19 $";
14 #endif /* not lint */
16 #include <sys/types.h>
17 #include <sys/queue.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/StringDefs.h>
21 #include <X11/cursorfont.h>
22 #include <Xm/PanedW.h>
23 #include <Xm/DrawingA.h>
24 #include <Xm/Form.h>
25 #include <Xm/Frame.h>
26 #include <Xm/ScrollBar.h>
28 #include <bitstring.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
38 #include "../common/common.h"
39 #include "../ipc/ip.h"
40 #include "m_motif.h"
41 #include "vi_mextern.h"
42 #include "pathnames.h"
44 extern int vi_ofd;
46 static void f_copy();
47 static void f_paste();
48 static void f_clear();
52 * Globals and costants
55 #define BufferSize 1024
57 static XFontStruct *font;
58 static GC gc;
59 GC __vi_copy_gc;
60 static XtAppContext ctx;
62 xvi_screen *__vi_screen = NULL;
63 static Cursor std_cursor;
64 static Cursor busy_cursor;
65 static XtTranslations area_trans;
66 static int multi_click_length;
68 void (*__vi_exitp)(); /* Exit function. */
71 /* hack for drag scrolling...
72 * I'm not sure why, but the current protocol gets out of sync when
73 * a lot of drag messages get passed around. Likely, we need to wait
74 * for core to finish repainting the screen before sending more drag
75 * messages.
76 * To that end, we set scroll_block when we receive input from the scrollbar,
77 * and we clear it when we process the IPO_REFRESH message from core.
78 * A specific SCROLL_COMPLETED message would be better, but this seems to work.
81 static Boolean scroll_block = False;
84 * PUBLIC: void __vi_set_scroll_block __P((void));
86 void
87 __vi_set_scroll_block()
89 scroll_block = True;
93 * PUBLIC: void __vi_clear_scroll_block __P((void));
95 void
96 __vi_clear_scroll_block()
98 scroll_block = False;
102 #if defined(__STDC__)
103 static void set_gc_colors( xvi_screen *this_screen, int val )
104 #else
105 static void set_gc_colors( this_screen, val )
106 xvi_screen *this_screen;
107 int val;
108 #endif
110 static Pixel fg, bg, hi, shade;
111 static int prev = COLOR_INVALID;
113 /* no change? */
114 if ( prev == val ) return;
116 /* init? */
117 if ( gc == NULL ) {
119 /* what colors are selected for the drawing area? */
120 XtVaGetValues( this_screen->area,
121 XtNbackground, &bg,
122 XtNforeground, &fg,
123 XmNhighlightColor, &hi,
124 XmNtopShadowColor, &shade,
128 gc = XCreateGC( XtDisplay(this_screen->area),
129 DefaultRootWindow(XtDisplay(this_screen->area)),
134 XSetFont( XtDisplay(this_screen->area), gc, font->fid );
137 /* special colors? */
138 if ( val & COLOR_CARET ) {
139 XSetForeground( XtDisplay(this_screen->area), gc, fg );
140 XSetBackground( XtDisplay(this_screen->area), gc, hi );
142 else if ( val & COLOR_SELECT ) {
143 XSetForeground( XtDisplay(this_screen->area), gc, fg );
144 XSetBackground( XtDisplay(this_screen->area), gc, shade );
146 else switch (val) {
147 case COLOR_STANDARD:
148 XSetForeground( XtDisplay(this_screen->area), gc, fg );
149 XSetBackground( XtDisplay(this_screen->area), gc, bg );
150 break;
151 case COLOR_INVERSE:
152 XSetForeground( XtDisplay(this_screen->area), gc, bg );
153 XSetBackground( XtDisplay(this_screen->area), gc, fg );
154 break;
155 default: /* implement color map later */
156 break;
162 * Memory utilities
165 #ifdef REALLOC
166 #undef REALLOC
167 #endif
169 #define REALLOC( ptr, size ) \
170 ((ptr == NULL) ? malloc(size) : realloc(ptr,size))
173 /* X windows routines.
174 * We currently create a single, top-level shell. In that is a
175 * single drawing area into which we will draw text. This allows
176 * us to put multi-color (and font, but we'll never build that) text
177 * into the drawing area. In the future, we'll add scrollbars to the
178 * drawing areas
181 void select_start();
182 void select_extend();
183 void select_paste();
184 void key_press();
185 void insert_string();
186 void beep();
187 void find();
188 void command();
190 static XtActionsRec area_actions[] = {
191 { "select_start", select_start },
192 { "select_extend", select_extend },
193 { "select_paste", select_paste },
194 { "key_press", key_press },
195 { "insert_string", insert_string },
196 { "beep", beep },
197 { "find", find },
198 { "command", command },
201 char areaTrans[] =
202 "<Btn1Down>: select_start() \n\
203 <Btn1Motion>: select_extend() \n\
204 <Btn2Down>: select_paste() \n\
205 <Btn3Down>: select_extend() \n\
206 <Btn3Motion>: select_extend() \n\
207 <Key>End: command(VI_C_BOTTOM) \n\
208 <Key>Escape: command(EINSERT) \n\
209 <Key>Find: find() \n\
210 <Key>Home: command(VI_C_TOP) \n\
211 <Key>Next: command(VI_C_PGDOWN) \n\
212 <Key>Prior: command(VI_C_PGUP) \n\
213 <Key>osfBackSpace: command(VI_C_LEFT) \n\
214 <Key>osfBeginLine: command(VI_C_BOL) \n\
215 <Key>osfCopy: beep() \n\
216 <Key>osfCut: beep() \n\
217 <Key>osfDelete: command(VI_C_DEL) \n\
218 <Key>osfDown: command(VI_C_DOWN) \n\
219 <Key>osfEndLine: command(VI_C_EOL) \n\
220 <Key>osfInsert: command(VI_C_INSERT) \n\
221 <Key>osfLeft: command(VI_C_LEFT) \n\
222 <Key>osfPageDown: command(VI_C_PGDOWN) \n\
223 <Key>osfPageUp: command(VI_C_PGUP) \n\
224 <Key>osfPaste: insert_string(p) \n\
225 <Key>osfRight: command(VI_C_RIGHT) \n\
226 <Key>osfUndo: command(VI_UNDO) \n\
227 <Key>osfUp: command(VI_C_UP) \n\
228 Ctrl<Key>C: command(VI_INTERRUPT) \n\
229 <Key>: key_press()";
232 static XutResource resource[] = {
233 { "font", XutRKfont, &font },
234 { "pointerShape", XutRKcursor, &std_cursor },
235 { "busyShape", XutRKcursor, &busy_cursor },
240 * vi_input_func --
241 * We've received input on the pipe from vi.
243 * PUBLIC: void vi_input_func __P((XtPointer, int *, XtInputId *));
245 void
246 vi_input_func(client_data, source, id)
247 XtPointer client_data;
248 int *source;
249 XtInputId *id;
251 /* Parse and dispatch on commands in the queue. */
252 (void)ipvi_motif->input(ipvi_motif, *source);
254 #ifdef notdef
255 /* Check the pipe for unused events when not busy. */
256 XtAppAddWorkProc(ctx, process_pipe_input, NULL);
257 #endif
262 /* Send the window size. */
263 #if defined(__STDC__)
264 static void send_resize( xvi_screen *this_screen )
265 #else
266 static void send_resize( this_screen )
267 xvi_screen *this_screen;
268 #endif
270 IP_BUF ipb;
272 ipb.val1 = this_screen->rows;
273 ipb.val2 = this_screen->cols;
274 ipb.code = VI_RESIZE;
276 #ifdef TRACE
277 vtrace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols);
278 #endif
280 /* send up the pipe */
281 vi_send(vi_ofd, "12", &ipb);
285 #if defined(__STDC__)
286 static void resize_backing_store( xvi_screen *this_screen )
287 #else
288 static void resize_backing_store( this_screen )
289 xvi_screen *this_screen;
290 #endif
292 int total_chars = this_screen->rows * this_screen->cols;
294 this_screen->characters = REALLOC( this_screen->characters,
295 total_chars
297 memset( this_screen->characters, ' ', total_chars );
299 this_screen->flags = REALLOC( this_screen->flags,
300 total_chars
302 memset( this_screen->flags, 0, total_chars );
307 /* X will call this when we are resized */
308 #if defined(__STDC__)
309 static void resize_func( Widget wid,
310 XtPointer client_data,
311 XtPointer call_data
313 #else
314 static void resize_func( wid, client_data, call_data )
315 Widget wid;
316 XtPointer client_data;
317 XtPointer call_data;
318 #endif
320 xvi_screen *this_screen = (xvi_screen *) client_data;
321 Dimension height, width;
323 XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 );
325 /* generate correct sizes when we have font metrics implemented */
326 this_screen->cols = width / this_screen->ch_width;
327 this_screen->rows = height / this_screen->ch_height;
329 resize_backing_store( this_screen );
330 send_resize( this_screen );
335 * __vi_draw_text --
336 * Draw from backing store.
338 * PUBLIC: void __vi_draw_text __P((xvi_screen *, int, int, int));
340 void
341 __vi_draw_text(this_screen, row, start_col, len)
342 xvi_screen *this_screen;
343 int row, start_col, len;
345 int col, color, xpos;
346 char *start, *end;
348 start = CharAt( __vi_screen, row, start_col );
349 color = *FlagAt( __vi_screen, row, start_col );
350 xpos = XPOS( __vi_screen, start_col );
352 /* one column at a time */
353 for ( col=start_col;
354 col<this_screen->cols && col<start_col+len;
355 col++ ) {
357 /* has the color changed? */
358 if ( *FlagAt( __vi_screen, row, col ) == color )
359 continue;
361 /* is there anything to write? */
362 end = CharAt( __vi_screen, row, col );
363 if ( end == start )
364 continue;
366 /* yes. write in the previous color */
367 set_gc_colors( __vi_screen, color );
369 /* add to display */
370 XDrawImageString( XtDisplay(__vi_screen->area),
371 XtWindow(__vi_screen->area),
373 xpos,
374 YPOS( __vi_screen, row ),
375 start,
376 end - start
379 /* this is the new context */
380 color = *FlagAt( __vi_screen, row, col );
381 xpos = XPOS( __vi_screen, col );
382 start = end;
385 /* is there anything to write? */
386 end = CharAt( __vi_screen, row, col );
387 if ( end != start ) {
388 /* yes. write in the previous color */
389 set_gc_colors( __vi_screen, color );
391 /* add to display */
392 XDrawImageString( XtDisplay(__vi_screen->area),
393 XtWindow(__vi_screen->area),
395 xpos,
396 YPOS( __vi_screen, row ),
397 start,
398 end - start
404 /* set clipping rectangles accordingly */
405 #if defined(__STDC__)
406 static void add_to_clip( xvi_screen *cur_screen, int x, int y, int width, int height )
407 #else
408 static void add_to_clip( cur_screen, x, y, width, height )
409 xvi_screen *cur_screen;
410 int x;
411 int y;
412 int width;
413 int height;
414 #endif
416 XRectangle rect;
417 rect.x = x;
418 rect.y = y;
419 rect.height = height;
420 rect.width = width;
421 if ( cur_screen->clip == NULL )
422 cur_screen->clip = XCreateRegion();
423 XUnionRectWithRegion( &rect, cur_screen->clip, cur_screen->clip );
428 * __vi_expose_func --
429 * Redraw the window's contents.
431 * NOTE: When vi wants to force a redraw, we are called with NULL widget
432 * and call_data.
434 * PUBLIC: void __vi_expose_func __P((Widget, XtPointer, XtPointer));
436 void
437 __vi_expose_func(wid, client_data, call_data)
438 Widget wid;
439 XtPointer client_data, call_data;
441 xvi_screen *this_screen;
442 XmDrawingAreaCallbackStruct *cbs;
443 XExposeEvent *xev;
444 XGraphicsExposeEvent *gev;
445 int row;
447 /* convert pointers */
448 this_screen = (xvi_screen *) client_data;
449 cbs = (XmDrawingAreaCallbackStruct *) call_data;
451 /* first exposure? tell vi we are ready... */
452 if ( this_screen->init == False ) {
454 /* what does the user want to see? */
455 __vi_set_cursor( __vi_screen, False );
457 /* vi wants a resize as the first event */
458 send_resize( __vi_screen );
460 /* fine for now. we'll be back */
461 this_screen->init = True;
462 return;
465 if ( call_data == NULL ) {
467 /* vi core calls this when it wants a full refresh */
468 #ifdef TRACE
469 vtrace("expose_func: full refresh\n");
470 #endif
472 XClearWindow( XtDisplay(this_screen->area),
473 XtWindow(this_screen->area)
476 else {
477 switch ( cbs->event->type ) {
479 case GraphicsExpose:
480 gev = (XGraphicsExposeEvent *) cbs->event;
482 /* set clipping rectangles accordingly */
483 add_to_clip( this_screen,
484 gev->x, gev->y,
485 gev->width, gev->height
488 /* X calls here when XCopyArea exposes new bits */
489 #ifdef TRACE
490 vtrace("expose_func (X): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
491 gev->x, gev->y,
492 gev->width, gev->height,
493 gev->count);
494 #endif
496 /* more coming? do it then */
497 if ( gev->count > 0 ) return;
499 /* set clipping region */
500 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
501 break;
503 case Expose:
504 xev = (XExposeEvent *) cbs->event;
506 /* set clipping rectangles accordingly */
507 add_to_clip( this_screen,
508 xev->x, xev->y,
509 xev->width, xev->height
512 /* Motif calls here when DrawingArea is exposed */
513 #ifdef TRACE
514 vtrace("expose_func (Motif): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
515 xev->x, xev->y,
516 xev->width, xev->height,
517 xev->count);
518 #endif
520 /* more coming? do it then */
521 if ( xev->count > 0 ) return;
523 /* set clipping region */
524 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
525 break;
527 default:
528 /* don't care? */
529 return;
533 /* one row at a time */
534 for (row=0; row<this_screen->rows; row++) {
536 /* draw from the backing store */
537 __vi_draw_text( this_screen, row, 0, this_screen->cols );
540 /* clear clipping region */
541 XSetClipMask( XtDisplay(this_screen->area), gc, None );
542 if ( this_screen->clip != NULL ) {
543 XDestroyRegion( this_screen->clip );
544 this_screen->clip = NULL;
550 #if defined(__STDC__)
551 static void xexpose ( Widget w,
552 XtPointer client_data,
553 XEvent *ev,
554 Boolean *cont
556 #else
557 static void xexpose ( w, client_data, ev, cont )
558 Widget w;
559 XtPointer client_data;
560 XEvent *ev;
561 Boolean *cont;
562 #endif
564 XmDrawingAreaCallbackStruct cbs;
566 switch ( ev->type ) {
567 case GraphicsExpose:
568 cbs.event = ev;
569 cbs.window = XtWindow(w);
570 cbs.reason = XmCR_EXPOSE;
571 __vi_expose_func( w, client_data, (XtPointer) &cbs );
572 *cont = False; /* we took care of it */
573 break;
574 default:
575 /* don't care */
576 break;
581 /* unimplemented keystroke or command */
582 #if defined(__STDC__)
583 static void beep( Widget w )
584 #else
585 static void beep( w )
586 Widget w;
587 #endif
589 XBell(XtDisplay(w),0);
593 /* give me a search dialog */
594 #if defined(__STDC__)
595 static void find( Widget w )
596 #else
597 static void find( w )
598 Widget w;
599 #endif
601 __vi_show_search_dialog( w, "Find" );
605 * command --
606 * Translate simple keyboard input into vi protocol commands.
608 static void
609 command(widget, event, str, cardinal)
610 Widget widget;
611 XKeyEvent *event;
612 String *str;
613 Cardinal *cardinal;
615 static struct {
616 String name;
617 int code;
618 int count;
619 } table[] = {
620 { "VI_C_BOL", VI_C_BOL, 0 },
621 { "VI_C_BOTTOM", VI_C_BOTTOM, 0 },
622 { "VI_C_DEL", VI_C_DEL, 0 },
623 { "VI_C_DOWN", VI_C_DOWN, 1 },
624 { "VI_C_EOL", VI_C_EOL, 0 },
625 { "VI_C_INSERT", VI_C_INSERT, 0 },
626 { "VI_C_LEFT", VI_C_LEFT, 0 },
627 { "VI_C_PGDOWN", VI_C_PGDOWN, 1 },
628 { "VI_C_PGUP", VI_C_PGUP, 1 },
629 { "VI_C_RIGHT", VI_C_RIGHT, 0 },
630 { "VI_C_TOP", VI_C_TOP, 0 },
631 { "VI_C_UP", VI_C_UP, 1 },
632 { "VI_INTERRUPT", VI_INTERRUPT, 0 },
634 IP_BUF ipb;
635 int i;
638 * XXX
639 * Do fast lookup based on character #6 -- sleazy, but I don't
640 * want to do 10 strcmp's per keystroke.
642 ipb.val1 = 1;
643 for (i = 0; i < XtNumber(table); i++)
644 if (table[i].name[6] == (*str)[6] &&
645 strcmp(table[i].name, *str) == 0) {
646 ipb.code = table[i].code;
647 vi_send(vi_ofd, table[i].count ? "1" : NULL, &ipb);
648 return;
651 /* oops. */
652 beep(widget);
655 /* mouse or keyboard input. */
656 #if defined(__STDC__)
657 static void insert_string( Widget widget,
658 XKeyEvent *event,
659 String *str,
660 Cardinal *cardinal
662 #else
663 static void insert_string( widget, event, str, cardinal )
664 Widget widget;
665 XKeyEvent *event;
666 String *str;
667 Cardinal *cardinal;
668 #endif
670 IP_BUF ipb;
672 ipb.len1 = strlen( *str );
673 if ( ipb.len1 != 0 ) {
674 ipb.code = VI_STRING;
675 ipb.str1 = *str;
676 vi_send(vi_ofd, "a", &ipb);
679 #ifdef TRACE
680 vtrace("insert_string {%.*s}\n", strlen( *str ), *str );
681 #endif
685 /* mouse or keyboard input. */
686 #if defined(__STDC__)
687 static void key_press( Widget widget,
688 XKeyEvent *event,
689 String str,
690 Cardinal *cardinal
692 #else
693 static void key_press( widget, event, str, cardinal )
694 Widget widget;
695 XKeyEvent *event;
696 String str;
697 Cardinal *cardinal;
698 #endif
700 IP_BUF ipb;
701 char bp[BufferSize];
703 ipb.len1 = XLookupString( event, bp, BufferSize, NULL, NULL );
704 if ( ipb.len1 != 0 ) {
705 ipb.code = VI_STRING;
706 ipb.str1 = bp;
707 #ifdef TRACE
708 vtrace("key_press {%.*s}\n", ipb.len1, bp );
709 #endif
710 vi_send(vi_ofd, "a", &ipb);
716 #if defined(__STDC__)
717 static void scrollbar_moved( Widget widget,
718 XtPointer ptr,
719 XmScrollBarCallbackStruct *cbs
721 #else
722 static void scrollbar_moved( widget, ptr, cbs )
723 Widget widget;
724 XtPointer ptr;
725 XmScrollBarCallbackStruct *cbs;
726 #endif
728 /* Future: Need to scroll the correct screen! */
729 xvi_screen *cur_screen = (xvi_screen *) ptr;
730 IP_BUF ipb;
732 /* if we are still processing messages from core, skip this event
733 * (see comments near __vi_set_scroll_block())
735 if ( scroll_block ) {
736 return;
738 __vi_set_scroll_block();
740 #ifdef TRACE
741 switch ( cbs->reason ) {
742 case XmCR_VALUE_CHANGED:
743 vtrace( "scrollbar VALUE_CHANGED %d\n", cbs->value );
744 break;
745 case XmCR_DRAG:
746 vtrace( "scrollbar DRAG %d\n", cbs->value );
747 break;
748 default:
749 vtrace( "scrollbar <default> %d\n", cbs->value );
750 break;
752 vtrace("scrollto {%d}\n", cbs->value );
753 #endif
755 /* Send the new cursor position. */
756 ipb.code = VI_C_SETTOP;
757 ipb.val1 = cbs->value;
758 (void)vi_send(vi_ofd, "1", &ipb);
762 #if defined(__STDC__)
763 static xvi_screen *create_screen( Widget parent, int rows, int cols )
764 #else
765 static xvi_screen *create_screen( parent, rows, cols )
766 Widget parent;
767 int rows, cols;
768 #endif
770 xvi_screen *new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) );
771 Widget frame;
773 /* init... */
774 new_screen->color = COLOR_STANDARD;
775 new_screen->parent = parent;
777 /* figure out the sizes */
778 new_screen->rows = rows;
779 new_screen->cols = cols;
780 new_screen->ch_width = font->max_bounds.width;
781 new_screen->ch_height = font->descent + font->ascent;
782 new_screen->ch_descent = font->descent;
783 new_screen->clip = NULL;
785 /* allocate and init the backing stores */
786 resize_backing_store( new_screen );
788 /* set up a translation table for the X toolkit */
789 if ( area_trans == NULL )
790 area_trans = XtParseTranslationTable(areaTrans);
792 /* future, new screen gets inserted into the parent sash
793 * immediately after the current screen. Default Pane action is
794 * to add it to the end
797 /* use a form to hold the drawing area and the scrollbar */
798 new_screen->form = XtVaCreateManagedWidget( "form",
799 xmFormWidgetClass,
800 parent,
801 XmNpaneMinimum, 2*new_screen->ch_height,
802 XmNallowResize, True,
803 NULL
806 /* create a scrollbar. */
807 new_screen->scroll = XtVaCreateManagedWidget( "scroll",
808 xmScrollBarWidgetClass,
809 new_screen->form,
810 XmNtopAttachment, XmATTACH_FORM,
811 XmNbottomAttachment, XmATTACH_FORM,
812 XmNrightAttachment, XmATTACH_FORM,
813 XmNminimum, 1,
814 XmNmaximum, 2,
815 XmNsliderSize, 1,
816 NULL
818 XtAddCallback( new_screen->scroll,
819 XmNvalueChangedCallback,
820 scrollbar_moved,
821 new_screen
823 XtAddCallback( new_screen->scroll,
824 XmNdragCallback,
825 scrollbar_moved,
826 new_screen
829 /* create a frame because they look nice */
830 frame = XtVaCreateManagedWidget( "frame",
831 xmFrameWidgetClass,
832 new_screen->form,
833 XmNshadowType, XmSHADOW_ETCHED_IN,
834 XmNtopAttachment, XmATTACH_FORM,
835 XmNbottomAttachment, XmATTACH_FORM,
836 XmNleftAttachment, XmATTACH_FORM,
837 XmNrightAttachment, XmATTACH_WIDGET,
838 XmNrightWidget, new_screen->scroll,
839 NULL
842 /* create a drawing area into which we will put text */
843 new_screen->area = XtVaCreateManagedWidget( "screen",
844 xmDrawingAreaWidgetClass,
845 frame,
846 XmNheight, new_screen->ch_height * new_screen->rows,
847 XmNwidth, new_screen->ch_width * new_screen->cols,
848 XmNtranslations, area_trans,
849 XmNuserData, new_screen,
850 XmNnavigationType, XmNONE,
851 XmNtraversalOn, False,
852 NULL
855 /* this callback is for when the drawing area is resized */
856 XtAddCallback( new_screen->area,
857 XmNresizeCallback,
858 resize_func,
859 new_screen
862 /* this callback is for when the drawing area is exposed */
863 XtAddCallback( new_screen->area,
864 XmNexposeCallback,
865 __vi_expose_func,
866 new_screen
869 /* this callback is for when we expose obscured bits
870 * (e.g. there is a window over part of our drawing area
872 XtAddEventHandler( new_screen->area,
873 0, /* no standard events */
874 True, /* we *WANT* GraphicsExpose */
875 xexpose, /* what to do */
876 new_screen
879 return new_screen;
883 static xvi_screen *split_screen()
885 Cardinal num;
886 WidgetList c;
887 int rows = __vi_screen->rows / 2;
888 xvi_screen *new_screen;
890 /* Note that (global) cur_screen needs to be correctly set so that
891 * insert_here knows which screen to put the new one after
893 new_screen = create_screen( __vi_screen->parent,
894 rows,
895 __vi_screen->cols
898 /* what are the screens? */
899 XtVaGetValues( __vi_screen->parent,
900 XmNnumChildren, &num,
901 XmNchildren, &c,
902 NULL
905 /* unmanage all children in preparation for resizing */
906 XtUnmanageChildren( c, num );
908 /* force resize of the affected screens */
909 XtVaSetValues( new_screen->form,
910 XmNheight, new_screen->ch_height * rows,
911 NULL
913 XtVaSetValues( __vi_screen->form,
914 XmNheight, __vi_screen->ch_height * rows,
915 NULL
918 /* re-manage */
919 XtManageChildren( c, num );
921 /* done */
922 return new_screen;
926 /* Tell me where to insert the next subpane */
927 #if defined(__STDC__)
928 static Cardinal insert_here( Widget wid )
929 #else
930 static Cardinal insert_here( wid )
931 Widget wid;
932 #endif
934 Cardinal i, num;
935 WidgetList c;
937 XtVaGetValues( XtParent(wid),
938 XmNnumChildren, &num,
939 XmNchildren, &c,
940 NULL
943 /* The default XmNinsertPosition procedure for PanedWindow
944 * causes sashes to be inserted at the end of the list of children
945 * and causes non-sash widgets to be inserted after other
946 * non-sash children but before any sashes.
948 if ( ! XmIsForm( wid ) )
949 return num;
951 /* We will put the widget after the one with the current screen */
952 for (i=0; i<num && XmIsForm(c[i]); i++) {
953 if ( __vi_screen == NULL || __vi_screen->form == c[i] )
954 return i+1; /* after the i-th */
957 /* could not find it? this should never happen */
958 return num;
963 * vi_create_editor --
964 * Create the necessary widgetry.
966 * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void)));
968 Widget
969 vi_create_editor(name, parent, exitp)
970 String name;
971 Widget parent;
972 void (*exitp) __P((void));
974 Widget pane_w;
975 Display *display = XtDisplay( parent );
977 __vi_exitp = exitp;
979 /* first time through? */
980 if ( ctx == NULL ) {
982 /* save this for later */
983 ctx = XtWidgetToApplicationContext( parent );
985 /* add our own special actions */
986 XtAppAddActions( ctx, area_actions, XtNumber(area_actions) );
988 /* how long is double-click? */
989 multi_click_length = XtGetMultiClickTime( display );
991 /* check the resource database for interesting resources */
992 __XutConvertResources( parent,
993 vi_progname,
994 resource,
995 XtNumber(resource)
998 /* we need a context for moving bits around in the windows */
999 __vi_copy_gc = XCreateGC( display,
1000 DefaultRootWindow(display),
1005 /* routines for inter client communications conventions */
1006 __vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf );
1009 /* create the paned window */
1010 pane_w = XtVaCreateManagedWidget( "pane",
1011 xmPanedWindowWidgetClass,
1012 parent,
1013 XmNinsertPosition, insert_here,
1014 NULL
1017 /* allocate our data structure. in the future we will have several
1018 * screens running around at the same time
1020 __vi_screen = create_screen( pane_w, 24, 80 );
1022 /* force creation of our color text context */
1023 set_gc_colors( __vi_screen, COLOR_STANDARD );
1025 /* done */
1026 return pane_w;
1030 /* These routines deal with the selection buffer */
1032 static int selection_start, selection_end, selection_anchor;
1033 static enum select_enum {
1034 select_char, select_word, select_line
1035 } select_type = select_char;
1036 static int last_click;
1038 static char *clipboard = NULL;
1039 static int clipboard_size = 0,
1040 clipboard_length;
1043 #if defined(__STDC__)
1044 static void copy_to_clipboard( xvi_screen *cur_screen )
1045 #else
1046 static void copy_to_clipboard( cur_screen )
1047 xvi_screen *cur_screen;
1048 #endif
1050 /* for now, copy from the backing store. in the future,
1051 * vi core will tell us exactly what the selection buffer contains
1053 clipboard_length = 1 + selection_end - selection_start;
1055 if ( clipboard == NULL )
1056 clipboard = (char *) malloc( clipboard_length );
1057 else if ( clipboard_size < clipboard_length )
1058 clipboard = (char *) realloc( clipboard, clipboard_length );
1060 memcpy( clipboard,
1061 cur_screen->characters + selection_start,
1062 clipboard_length
1067 #if defined(__STDC__)
1068 static void mark_selection( xvi_screen *cur_screen, int start, int end )
1069 #else
1070 static void mark_selection( cur_screen, start, end )
1071 xvi_screen *cur_screen;
1072 int start;
1073 int end;
1074 #endif
1076 int row, col, i;
1078 for ( i=start; i<=end; i++ ) {
1079 if ( !( cur_screen->flags[i] & COLOR_SELECT ) ) {
1080 cur_screen->flags[i] |= COLOR_SELECT;
1081 ToRowCol( cur_screen, i, row, col );
1082 __vi_draw_text( cur_screen, row, col, 1 );
1088 #if defined(__STDC__)
1089 static void erase_selection( xvi_screen *cur_screen, int start, int end )
1090 #else
1091 static void erase_selection( cur_screen, start, end )
1092 xvi_screen *cur_screen;
1093 int start;
1094 int end;
1095 #endif
1097 int row, col, i;
1099 for ( i=start; i<=end; i++ ) {
1100 if ( cur_screen->flags[i] & COLOR_SELECT ) {
1101 cur_screen->flags[i] &= ~COLOR_SELECT;
1102 ToRowCol( cur_screen, i, row, col );
1103 __vi_draw_text( cur_screen, row, col, 1 );
1109 #if defined(__STDC__)
1110 static void left_expand_selection( xvi_screen *cur_screen, int *start )
1111 #else
1112 static void left_expand_selection( cur_screen, start )
1113 xvi_screen *cur_screen;
1114 int *start;
1115 #endif
1117 int row, col;
1119 switch ( select_type ) {
1120 case select_word:
1121 if ( *start == 0 || isspace( cur_screen->characters[*start] ) )
1122 return;
1123 for (;;) {
1124 if ( isspace( cur_screen->characters[*start-1] ) )
1125 return;
1126 if ( --(*start) == 0 )
1127 return;
1129 case select_line:
1130 ToRowCol( cur_screen, *start, row, col );
1131 col = 0;
1132 *start = Linear( cur_screen, row, col );
1133 break;
1138 #if defined(__STDC__)
1139 static void right_expand_selection( xvi_screen *cur_screen, int *end )
1140 #else
1141 static void right_expand_selection( cur_screen, end )
1142 xvi_screen *cur_screen;
1143 int *end;
1144 #endif
1146 int row, col, last = cur_screen->cols * cur_screen->rows - 1;
1148 switch ( select_type ) {
1149 case select_word:
1150 if ( *end == last || isspace( cur_screen->characters[*end] ) )
1151 return;
1152 for (;;) {
1153 if ( isspace( cur_screen->characters[*end+1] ) )
1154 return;
1155 if ( ++(*end) == last )
1156 return;
1158 case select_line:
1159 ToRowCol( cur_screen, *end, row, col );
1160 col = cur_screen->cols -1;
1161 *end = Linear( cur_screen, row, col );
1162 break;
1167 #if defined(__STDC__)
1168 static void select_start( Widget widget,
1169 XEvent *event,
1170 String str,
1171 Cardinal *cardinal
1173 #else
1174 static void select_start( widget, event, str, cardinal )
1175 Widget widget;
1176 XEvent *event;
1177 String str;
1178 Cardinal *cardinal;
1179 #endif
1181 IP_BUF ipb;
1182 int xpos, ypos;
1183 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1184 static int last_click;
1187 * NOTE: when multiple panes are implemented, we need to find the correct
1188 * screen. For now, there is only one.
1190 xpos = COLUMN( __vi_screen, ev->x );
1191 ypos = ROW( __vi_screen, ev->y );
1193 /* Remove the old one. */
1194 erase_selection( __vi_screen, selection_start, selection_end );
1196 /* Send the new cursor position. */
1197 ipb.code = VI_MOUSE_MOVE;
1198 ipb.val1 = ypos;
1199 ipb.val2 = xpos;
1200 (void)vi_send(vi_ofd, "12", &ipb);
1202 /* click-click, and we go for words, lines, etc */
1203 if ( ev->time - last_click < multi_click_length )
1204 select_type = (enum select_enum) ((((int)select_type)+1)%3);
1205 else
1206 select_type = select_char;
1207 last_click = ev->time;
1209 /* put the selection here */
1210 selection_anchor = Linear( __vi_screen, ypos, xpos );
1211 selection_start = selection_anchor;
1212 selection_end = selection_anchor;
1214 /* expand to include words, line, etc */
1215 left_expand_selection( __vi_screen, &selection_start );
1216 right_expand_selection( __vi_screen, &selection_end );
1218 /* draw the new one */
1219 mark_selection( __vi_screen, selection_start, selection_end );
1221 /* and tell the window manager we own the selection */
1222 if ( select_type != select_char ) {
1223 __vi_AcquirePrimary( widget );
1224 copy_to_clipboard( __vi_screen );
1229 #if defined(__STDC__)
1230 static void select_extend( Widget widget,
1231 XEvent *event,
1232 String str,
1233 Cardinal *cardinal
1235 #else
1236 static void select_extend( widget, event, str, cardinal )
1237 Widget widget;
1238 XEvent *event;
1239 String str;
1240 Cardinal *cardinal;
1241 #endif
1243 int xpos, ypos, pos;
1244 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1246 /* NOTE: when multiple panes are implemented, we need to find
1247 * the correct screen. For now, there is only one.
1249 xpos = COLUMN( __vi_screen, ev->x );
1250 ypos = ROW( __vi_screen, ev->y );
1252 /* deal with words, lines, etc */
1253 pos = Linear( __vi_screen, ypos, xpos );
1254 if ( pos < selection_anchor )
1255 left_expand_selection( __vi_screen, &pos );
1256 else
1257 right_expand_selection( __vi_screen, &pos );
1259 /* extend from before the start? */
1260 if ( pos < selection_start ) {
1261 mark_selection( __vi_screen, pos, selection_start-1 );
1262 selection_start = pos;
1265 /* extend past the end? */
1266 else if ( pos > selection_end ) {
1267 mark_selection( __vi_screen, selection_end+1, pos );
1268 selection_end = pos;
1271 /* between the anchor and the start? */
1272 else if ( pos < selection_anchor ) {
1273 erase_selection( __vi_screen, selection_start, pos-1 );
1274 selection_start = pos;
1277 /* between the anchor and the end? */
1278 else {
1279 erase_selection( __vi_screen, pos+1, selection_end );
1280 selection_end = pos;
1283 /* and tell the window manager we own the selection */
1284 __vi_AcquirePrimary( widget );
1285 copy_to_clipboard( __vi_screen );
1289 #if defined(__STDC__)
1290 static void select_paste( Widget widget,
1291 XEvent *event,
1292 String str,
1293 Cardinal *cardinal
1295 #else
1296 static void select_paste( widget, event, str, cardinal )
1297 Widget widget;
1298 XEvent *event;
1299 String str;
1300 Cardinal *cardinal;
1301 #endif
1303 __vi_PasteFromClipboard( widget );
1307 /* Interface to copy and paste
1308 * (a) callbacks from the window manager
1309 * f_copy - it wants our buffer
1310 * f_paste - it wants us to paste some text
1311 * f_clear - we've lost the selection, clear it
1314 #if defined(__STDC__)
1315 static void f_copy( String *buffer, int *len )
1316 #else
1317 static void f_copy( buffer, len )
1318 String *buffer;
1319 int *len;
1320 #endif
1322 #ifdef TRACE
1323 vtrace("f_copy() called");
1324 #endif
1325 *buffer = clipboard;
1326 *len = clipboard_length;
1331 static void f_paste( widget, buffer, length )
1332 int widget, buffer, length;
1334 /* NOTE: when multiple panes are implemented, we need to find
1335 * the correct screen. For now, there is only one.
1337 #ifdef TRACE
1338 vtrace("f_paste() called with '%*.*s'\n", length, length, buffer);
1339 #endif
1343 #if defined(__STDC__)
1344 static void f_clear( Widget widget )
1345 #else
1346 static void f_clear( widget )
1347 Widget widget;
1348 #endif
1350 xvi_screen *cur_screen;
1352 #ifdef TRACE
1353 vtrace("f_clear() called");
1354 #endif
1356 XtVaGetValues( widget, XmNuserData, &cur_screen, 0 );
1358 erase_selection( cur_screen, selection_start, selection_end );
1363 * These routines deal with the cursor.
1365 * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int));
1367 void
1368 __vi_set_cursor(cur_screen, is_busy)
1369 xvi_screen *cur_screen;
1370 int is_busy;
1372 XDefineCursor( XtDisplay(cur_screen->area),
1373 XtWindow(cur_screen->area),
1374 (is_busy) ? busy_cursor : std_cursor
1380 /* hooks for the tags widget */
1382 static String cur_word = NULL;
1385 * PUBLIC: void __vi_set_word_at_caret __P((xvi_screen *));
1387 void
1388 __vi_set_word_at_caret( this_screen )
1389 xvi_screen *this_screen;
1391 char *start, *end, save;
1392 int newx, newy;
1394 newx = this_screen->curx;
1395 newy = this_screen->cury;
1397 /* Note that this really ought to be done by core due to wrapping issues */
1398 for ( end = start = CharAt( this_screen, newy, newx );
1399 (isalnum( *end ) || *end == '_') && (newx < this_screen->cols);
1400 end++, newx++
1402 save = *end;
1403 *end = '\0';
1404 if ( cur_word != NULL ) free( cur_word );
1405 cur_word = strdup( start );
1406 *end = save;
1408 /* if the tag stack widget is active, set the text field there
1409 * to agree with the current caret position.
1411 __vi_set_tag_text( cur_word );
1415 String __vi_get_word_at_caret( this_screen )
1416 xvi_screen *this_screen;
1418 return (cur_word) ? cur_word : "";
1423 * These routines deal with the caret.
1425 * PUBLIC: void draw_caret __P((xvi_screen *));
1427 static void
1428 draw_caret(this_screen)
1429 xvi_screen *this_screen;
1431 /* draw the caret by drawing the text in highlight color */
1432 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) |= COLOR_CARET;
1433 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1437 * PUBLIC: void __vi_erase_caret __P((xvi_screen *));
1439 void
1440 __vi_erase_caret(this_screen)
1441 xvi_screen *this_screen;
1443 /* erase the caret by drawing the text in normal video */
1444 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) &= ~COLOR_CARET;
1445 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1449 * PUBLIC: void __vi_move_caret __P((xvi_screen *, int, int));
1451 void
1452 __vi_move_caret(this_screen, newy, newx)
1453 xvi_screen *this_screen;
1454 int newy, newx;
1456 /* remove the old caret */
1457 __vi_erase_caret( this_screen );
1459 /* caret is now here */
1460 this_screen->curx = newx;
1461 this_screen->cury = newy;
1462 draw_caret( this_screen );