prettiness police
[nvi.git] / motif_l / m_vi.c
blobc5714ff7163fdd558ccb97f7fedeed1179842a2b
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.34 1997/04/13 10:28:26 bostic Exp $ (Berkeley) $Date: 1997/04/13 10:28:26 $";
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 static void f_copy();
45 static void f_paste();
46 static void f_clear();
50 * Globals and costants
53 #define BufferSize 1024
55 static XFontStruct *font;
56 static GC gc;
57 GC __vi_copy_gc;
58 static XtAppContext ctx;
60 xvi_screen *__vi_screen = NULL;
61 static Cursor std_cursor;
62 static Cursor busy_cursor;
63 static XtTranslations area_trans;
64 static int multi_click_length;
66 void (*__vi_exitp)(); /* Exit function. */
68 static char bp[ BufferSize ]; /* input buffer from pipe */
69 static size_t len, blen = sizeof(bp);
72 /* hack for drag scrolling...
73 * I'm not sure why, but the current protocol gets out of sync when
74 * a lot of drag messages get passed around. Likely, we need to wait
75 * for core to finish repainting the screen before sending more drag
76 * messages.
77 * To that end, we set scroll_block when we receive input from the scrollbar,
78 * and we clear it when we process the IPO_REFRESH message from core.
79 * A specific SCROLL_COMPLETED message would be better, but this seems to work.
82 static Boolean scroll_block = False;
85 * PUBLIC: void __vi_set_scroll_block __P((void));
87 void
88 __vi_set_scroll_block()
90 scroll_block = True;
94 * PUBLIC: void __vi_clear_scroll_block __P((void));
96 void
97 __vi_clear_scroll_block()
99 scroll_block = False;
103 #if defined(__STDC__)
104 static void set_gc_colors( xvi_screen *this_screen, int val )
105 #else
106 static void set_gc_colors( this_screen, val )
107 xvi_screen *this_screen;
108 int val;
109 #endif
111 static Pixel fg, bg, hi, shade;
112 static int prev = COLOR_INVALID;
114 /* no change? */
115 if ( prev == val ) return;
117 /* init? */
118 if ( gc == NULL ) {
120 /* what colors are selected for the drawing area? */
121 XtVaGetValues( this_screen->area,
122 XtNbackground, &bg,
123 XtNforeground, &fg,
124 XmNhighlightColor, &hi,
125 XmNtopShadowColor, &shade,
129 gc = XCreateGC( XtDisplay(this_screen->area),
130 DefaultRootWindow(XtDisplay(this_screen->area)),
135 XSetFont( XtDisplay(this_screen->area), gc, font->fid );
138 /* special colors? */
139 if ( val & COLOR_CARET ) {
140 XSetForeground( XtDisplay(this_screen->area), gc, fg );
141 XSetBackground( XtDisplay(this_screen->area), gc, hi );
143 else if ( val & COLOR_SELECT ) {
144 XSetForeground( XtDisplay(this_screen->area), gc, fg );
145 XSetBackground( XtDisplay(this_screen->area), gc, shade );
147 else switch (val) {
148 case COLOR_STANDARD:
149 XSetForeground( XtDisplay(this_screen->area), gc, fg );
150 XSetBackground( XtDisplay(this_screen->area), gc, bg );
151 break;
152 case COLOR_INVERSE:
153 XSetForeground( XtDisplay(this_screen->area), gc, bg );
154 XSetBackground( XtDisplay(this_screen->area), gc, fg );
155 break;
156 default: /* implement color map later */
157 break;
163 * Memory utilities
166 #ifdef REALLOC
167 #undef REALLOC
168 #endif
170 #define REALLOC( ptr, size ) \
171 ((ptr == NULL) ? malloc(size) : realloc(ptr,size))
174 /* X windows routines.
175 * We currently create a single, top-level shell. In that is a
176 * single drawing area into which we will draw text. This allows
177 * us to put multi-color (and font, but we'll never build that) text
178 * into the drawing area. In the future, we'll add scrollbars to the
179 * drawing areas
182 void select_start();
183 void select_extend();
184 void select_paste();
185 void key_press();
186 void insert_string();
187 void beep();
188 void find();
189 void command();
191 static XtActionsRec area_actions[] = {
192 { "select_start", select_start },
193 { "select_extend", select_extend },
194 { "select_paste", select_paste },
195 { "key_press", key_press },
196 { "insert_string", insert_string },
197 { "beep", beep },
198 { "find", find },
199 { "command", command },
202 char areaTrans[] =
203 "<Btn1Down>: select_start() \n\
204 <Btn1Motion>: select_extend() \n\
205 <Btn2Down>: select_paste() \n\
206 <Btn3Down>: select_extend() \n\
207 <Btn3Motion>: select_extend() \n\
208 <Key>End: command(VI_C_BOTTOM) \n\
209 <Key>Escape: command(EINSERT) \n\
210 <Key>Find: find() \n\
211 <Key>Home: command(VI_C_TOP) \n\
212 <Key>Next: command(VI_C_PGDOWN) \n\
213 <Key>Prior: command(VI_C_PGUP) \n\
214 <Key>osfBackSpace: command(VI_C_LEFT) \n\
215 <Key>osfBeginLine: command(VI_C_BOL) \n\
216 <Key>osfCopy: beep() \n\
217 <Key>osfCut: beep() \n\
218 <Key>osfDelete: command(VI_C_DEL) \n\
219 <Key>osfDown: command(VI_C_DOWN) \n\
220 <Key>osfEndLine: command(VI_C_EOL) \n\
221 <Key>osfInsert: command(VI_C_INSERT) \n\
222 <Key>osfLeft: command(VI_C_LEFT) \n\
223 <Key>osfPageDown: command(VI_C_PGDOWN) \n\
224 <Key>osfPageUp: command(VI_C_PGUP) \n\
225 <Key>osfPaste: insert_string(p) \n\
226 <Key>osfRight: command(VI_C_RIGHT) \n\
227 <Key>osfUndo: command(VI_UNDO) \n\
228 <Key>osfUp: command(VI_C_UP) \n\
229 Ctrl<Key>C: command(VI_INTERRUPT) \n\
230 <Key>: key_press()";
233 static XutResource resource[] = {
234 { "font", XutRKfont, &font },
235 { "pointerShape", XutRKcursor, &std_cursor },
236 { "busyShape", XutRKcursor, &busy_cursor },
241 #if defined(__STDC__)
242 static Boolean process_pipe_input( XtPointer pread )
243 #else
244 static Boolean process_pipe_input( pread )
245 XtPointer pread;
246 #endif
248 /* might have read more since the last call */
249 len += (pread) ? *((int *)pread) : 0;
251 /* Parse to data end or partial message. */
252 (void)vi_translate(bp, &len);
254 if (len > 0) {
255 #ifdef TRACE
256 vtrace("vi_input_func: abort with %d in the buffer\n", len);
257 #endif
258 /* call me again later */
259 return False;
262 /* do NOT call me again later */
263 return True;
268 * vi_input_func --
269 * We've received input on the pipe from vi.
271 * PUBLIC: void vi_input_func __P((XtPointer, int *, XtInputId *));
273 void
274 vi_input_func(client_data, source, id)
275 XtPointer client_data;
276 int *source;
277 XtInputId *id;
279 int nr;
281 /* Read waiting vi messags and translate to X calls. */
282 switch (nr = read( *source, bp + len, blen - len)) {
283 case 0:
284 #ifdef TRACE
285 vtrace("vi_input_func: empty input from vi\n");
286 #endif
287 return;
288 case -1:
289 perror("ip_cl: read");
290 exit (1);
291 default:
292 #ifdef TRACE
293 vtrace("input from vi, %d bytes read\n", nr);
294 #endif
295 break;
298 /* parse and dispatch on commands in the queue */
299 if ( ! process_pipe_input( &nr ) ) {
300 /* check the pipe for unused events when not busy */
301 XtAppAddWorkProc( ctx, process_pipe_input, NULL );
307 /* Send the window size. */
308 #if defined(__STDC__)
309 static void send_resize( xvi_screen *this_screen )
310 #else
311 static void send_resize( this_screen )
312 xvi_screen *this_screen;
313 #endif
315 IP_BUF ipb;
317 ipb.val1 = this_screen->rows;
318 ipb.val2 = this_screen->cols;
319 ipb.code = VI_RESIZE;
321 #ifdef TRACE
322 vtrace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols);
323 #endif
325 /* send up the pipe */
326 vi_send("12", &ipb);
330 #if defined(__STDC__)
331 static void resize_backing_store( xvi_screen *this_screen )
332 #else
333 static void resize_backing_store( this_screen )
334 xvi_screen *this_screen;
335 #endif
337 int total_chars = this_screen->rows * this_screen->cols;
339 this_screen->characters = REALLOC( this_screen->characters,
340 total_chars
342 memset( this_screen->characters, ' ', total_chars );
344 this_screen->flags = REALLOC( this_screen->flags,
345 total_chars
347 memset( this_screen->flags, 0, total_chars );
352 /* X will call this when we are resized */
353 #if defined(__STDC__)
354 static void resize_func( Widget wid,
355 XtPointer client_data,
356 XtPointer call_data
358 #else
359 static void resize_func( wid, client_data, call_data )
360 Widget wid;
361 XtPointer client_data;
362 XtPointer call_data;
363 #endif
365 xvi_screen *this_screen = (xvi_screen *) client_data;
366 Dimension height, width;
368 XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 );
370 /* generate correct sizes when we have font metrics implemented */
371 this_screen->cols = width / this_screen->ch_width;
372 this_screen->rows = height / this_screen->ch_height;
374 resize_backing_store( this_screen );
375 send_resize( this_screen );
380 * __vi_draw_text --
381 * Draw from backing store.
383 * PUBLIC: void __vi_draw_text __P((xvi_screen *, int, int, int));
385 void
386 __vi_draw_text(this_screen, row, start_col, len)
387 xvi_screen *this_screen;
388 int row, start_col, len;
390 int col, color, xpos;
391 char *start, *end;
393 start = CharAt( __vi_screen, row, start_col );
394 color = *FlagAt( __vi_screen, row, start_col );
395 xpos = XPOS( __vi_screen, start_col );
397 /* one column at a time */
398 for ( col=start_col;
399 col<this_screen->cols && col<start_col+len;
400 col++ ) {
402 /* has the color changed? */
403 if ( *FlagAt( __vi_screen, row, col ) == color )
404 continue;
406 /* is there anything to write? */
407 end = CharAt( __vi_screen, row, col );
408 if ( end == start )
409 continue;
411 /* yes. write in the previous color */
412 set_gc_colors( __vi_screen, color );
414 /* add to display */
415 XDrawImageString( XtDisplay(__vi_screen->area),
416 XtWindow(__vi_screen->area),
418 xpos,
419 YPOS( __vi_screen, row ),
420 start,
421 end - start
424 /* this is the new context */
425 color = *FlagAt( __vi_screen, row, col );
426 xpos = XPOS( __vi_screen, col );
427 start = end;
430 /* is there anything to write? */
431 end = CharAt( __vi_screen, row, col );
432 if ( end != start ) {
433 /* yes. write in the previous color */
434 set_gc_colors( __vi_screen, color );
436 /* add to display */
437 XDrawImageString( XtDisplay(__vi_screen->area),
438 XtWindow(__vi_screen->area),
440 xpos,
441 YPOS( __vi_screen, row ),
442 start,
443 end - start
449 /* set clipping rectangles accordingly */
450 #if defined(__STDC__)
451 static void add_to_clip( xvi_screen *cur_screen, int x, int y, int width, int height )
452 #else
453 static void add_to_clip( cur_screen, x, y, width, height )
454 xvi_screen *cur_screen;
455 int x;
456 int y;
457 int width;
458 int height;
459 #endif
461 XRectangle rect;
462 rect.x = x;
463 rect.y = y;
464 rect.height = height;
465 rect.width = width;
466 if ( cur_screen->clip == NULL )
467 cur_screen->clip = XCreateRegion();
468 XUnionRectWithRegion( &rect, cur_screen->clip, cur_screen->clip );
473 * __vi_expose_func --
474 * Redraw the window's contents.
476 * NOTE: When vi wants to force a redraw, we are called with NULL widget
477 * and call_data.
479 * PUBLIC: void __vi_expose_func __P((Widget, XtPointer, XtPointer));
481 void
482 __vi_expose_func(wid, client_data, call_data)
483 Widget wid;
484 XtPointer client_data, call_data;
486 xvi_screen *this_screen;
487 XmDrawingAreaCallbackStruct *cbs;
488 XExposeEvent *xev;
489 XGraphicsExposeEvent *gev;
490 int row;
492 /* convert pointers */
493 this_screen = (xvi_screen *) client_data;
494 cbs = (XmDrawingAreaCallbackStruct *) call_data;
496 /* first exposure? tell vi we are ready... */
497 if ( this_screen->init == False ) {
499 /* what does the user want to see? */
500 __vi_set_cursor( __vi_screen, False );
502 /* vi wants a resize as the first event */
503 send_resize( __vi_screen );
505 /* fine for now. we'll be back */
506 this_screen->init = True;
507 return;
510 if ( call_data == NULL ) {
512 /* vi core calls this when it wants a full refresh */
513 #ifdef TRACE
514 vtrace("expose_func: full refresh\n");
515 #endif
517 XClearWindow( XtDisplay(this_screen->area),
518 XtWindow(this_screen->area)
521 else {
522 switch ( cbs->event->type ) {
524 case GraphicsExpose:
525 gev = (XGraphicsExposeEvent *) cbs->event;
527 /* set clipping rectangles accordingly */
528 add_to_clip( this_screen,
529 gev->x, gev->y,
530 gev->width, gev->height
533 /* X calls here when XCopyArea exposes new bits */
534 #ifdef TRACE
535 vtrace("expose_func (X): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
536 gev->x, gev->y,
537 gev->width, gev->height,
538 gev->count);
539 #endif
541 /* more coming? do it then */
542 if ( gev->count > 0 ) return;
544 /* set clipping region */
545 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
546 break;
548 case Expose:
549 xev = (XExposeEvent *) cbs->event;
551 /* set clipping rectangles accordingly */
552 add_to_clip( this_screen,
553 xev->x, xev->y,
554 xev->width, xev->height
557 /* Motif calls here when DrawingArea is exposed */
558 #ifdef TRACE
559 vtrace("expose_func (Motif): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
560 xev->x, xev->y,
561 xev->width, xev->height,
562 xev->count);
563 #endif
565 /* more coming? do it then */
566 if ( xev->count > 0 ) return;
568 /* set clipping region */
569 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
570 break;
572 default:
573 /* don't care? */
574 return;
578 /* one row at a time */
579 for (row=0; row<this_screen->rows; row++) {
581 /* draw from the backing store */
582 __vi_draw_text( this_screen, row, 0, this_screen->cols );
585 /* clear clipping region */
586 XSetClipMask( XtDisplay(this_screen->area), gc, None );
587 if ( this_screen->clip != NULL ) {
588 XDestroyRegion( this_screen->clip );
589 this_screen->clip = NULL;
595 #if defined(__STDC__)
596 static void xexpose ( Widget w,
597 XtPointer client_data,
598 XEvent *ev,
599 Boolean *cont
601 #else
602 static void xexpose ( w, client_data, ev, cont )
603 Widget w;
604 XtPointer client_data;
605 XEvent *ev;
606 Boolean *cont;
607 #endif
609 XmDrawingAreaCallbackStruct cbs;
611 switch ( ev->type ) {
612 case GraphicsExpose:
613 cbs.event = ev;
614 cbs.window = XtWindow(w);
615 cbs.reason = XmCR_EXPOSE;
616 __vi_expose_func( w, client_data, (XtPointer) &cbs );
617 *cont = False; /* we took care of it */
618 break;
619 default:
620 /* don't care */
621 break;
626 /* unimplemented keystroke or command */
627 #if defined(__STDC__)
628 static void beep( Widget w )
629 #else
630 static void beep( w )
631 Widget w;
632 #endif
634 XBell(XtDisplay(w),0);
638 /* give me a search dialog */
639 #if defined(__STDC__)
640 static void find( Widget w )
641 #else
642 static void find( w )
643 Widget w;
644 #endif
646 __vi_show_search_dialog( w, "Find" );
650 * command --
651 * Translate simple keyboard input into vi protocol commands.
653 static void
654 command(widget, event, str, cardinal)
655 Widget widget;
656 XKeyEvent *event;
657 String *str;
658 Cardinal *cardinal;
660 static struct {
661 String name;
662 int code;
663 int count;
664 } table[] = {
665 { "VI_C_BOL", VI_C_BOL, 0 },
666 { "VI_C_BOTTOM", VI_C_BOTTOM, 0 },
667 { "VI_C_DEL", VI_C_DEL, 0 },
668 { "VI_C_DOWN", VI_C_DOWN, 1 },
669 { "VI_C_EOL", VI_C_EOL, 0 },
670 { "VI_C_INSERT", VI_C_INSERT, 0 },
671 { "VI_C_LEFT", VI_C_LEFT, 0 },
672 { "VI_C_PGDOWN", VI_C_PGDOWN, 1 },
673 { "VI_C_PGUP", VI_C_PGUP, 1 },
674 { "VI_C_RIGHT", VI_C_RIGHT, 0 },
675 { "VI_C_TOP", VI_C_TOP, 0 },
676 { "VI_C_UP", VI_C_UP, 1 },
677 { "VI_INTERRUPT", VI_INTERRUPT, 0 },
679 IP_BUF ipb;
680 int i;
683 * XXX
684 * Do fast lookup based on character #6 -- sleazy, but I don't
685 * want to do 10 strcmp's per keystroke.
687 ipb.val1 = 1;
688 for (i = 0; i < XtNumber(table); i++)
689 if (table[i].name[6] == (*str)[6] &&
690 strcmp(table[i].name, *str) == 0) {
691 ipb.code = table[i].code;
692 vi_send(table[i].count ? "1" : NULL, &ipb);
693 return;
696 /* oops. */
697 beep(widget);
700 /* mouse or keyboard input. */
701 #if defined(__STDC__)
702 static void insert_string( Widget widget,
703 XKeyEvent *event,
704 String *str,
705 Cardinal *cardinal
707 #else
708 static void insert_string( widget, event, str, cardinal )
709 Widget widget;
710 XKeyEvent *event;
711 String *str;
712 Cardinal *cardinal;
713 #endif
715 IP_BUF ipb;
717 ipb.len1 = strlen( *str );
718 if ( ipb.len1 != 0 ) {
719 ipb.code = VI_STRING;
720 ipb.str1 = *str;
721 vi_send("a", &ipb);
724 #ifdef TRACE
725 vtrace("insert_string {%.*s}\n", strlen( *str ), *str );
726 #endif
730 /* mouse or keyboard input. */
731 #if defined(__STDC__)
732 static void key_press( Widget widget,
733 XKeyEvent *event,
734 String str,
735 Cardinal *cardinal
737 #else
738 static void key_press( widget, event, str, cardinal )
739 Widget widget;
740 XKeyEvent *event;
741 String str;
742 Cardinal *cardinal;
743 #endif
745 IP_BUF ipb;
746 char bp[BufferSize];
748 ipb.len1 = XLookupString( event, bp, BufferSize, NULL, NULL );
749 if ( ipb.len1 != 0 ) {
750 ipb.code = VI_STRING;
751 ipb.str1 = bp;
752 #ifdef TRACE
753 vtrace("key_press {%.*s}\n", ipb.len1, bp );
754 #endif
755 vi_send("a", &ipb);
761 #if defined(__STDC__)
762 static void scrollbar_moved( Widget widget,
763 XtPointer ptr,
764 XmScrollBarCallbackStruct *cbs
766 #else
767 static void scrollbar_moved( widget, ptr, cbs )
768 Widget widget;
769 XtPointer ptr;
770 XmScrollBarCallbackStruct *cbs;
771 #endif
773 /* Future: Need to scroll the correct screen! */
774 xvi_screen *cur_screen = (xvi_screen *) ptr;
775 IP_BUF ipb;
777 /* if we are still processing messages from core, skip this event
778 * (see comments near __vi_set_scroll_block())
780 if ( scroll_block ) {
781 #ifdef TRACE
782 vtrace( "punting scroll request with %d in buffer\n", len );
783 #endif
784 return;
786 __vi_set_scroll_block();
788 #ifdef TRACE
789 switch ( cbs->reason ) {
790 case XmCR_VALUE_CHANGED:
791 vtrace( "scrollbar VALUE_CHANGED %d\n", cbs->value );
792 break;
793 case XmCR_DRAG:
794 vtrace( "scrollbar DRAG %d\n", cbs->value );
795 break;
796 default:
797 vtrace( "scrollbar <default> %d\n", cbs->value );
798 break;
800 vtrace("scrollto {%d}\n", cbs->value );
801 #endif
803 /* Send the new cursor position. */
804 ipb.code = VI_C_SETTOP;
805 ipb.val1 = cbs->value;
806 (void)vi_send("1", &ipb);
810 #if defined(__STDC__)
811 static xvi_screen *create_screen( Widget parent, int rows, int cols )
812 #else
813 static xvi_screen *create_screen( parent, rows, cols )
814 Widget parent;
815 int rows, cols;
816 #endif
818 xvi_screen *new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) );
819 Widget frame;
821 /* init... */
822 new_screen->color = COLOR_STANDARD;
823 new_screen->parent = parent;
825 /* figure out the sizes */
826 new_screen->rows = rows;
827 new_screen->cols = cols;
828 new_screen->ch_width = font->max_bounds.width;
829 new_screen->ch_height = font->descent + font->ascent;
830 new_screen->ch_descent = font->descent;
831 new_screen->clip = NULL;
833 /* allocate and init the backing stores */
834 resize_backing_store( new_screen );
836 /* set up a translation table for the X toolkit */
837 if ( area_trans == NULL )
838 area_trans = XtParseTranslationTable(areaTrans);
840 /* future, new screen gets inserted into the parent sash
841 * immediately after the current screen. Default Pane action is
842 * to add it to the end
845 /* use a form to hold the drawing area and the scrollbar */
846 new_screen->form = XtVaCreateManagedWidget( "form",
847 xmFormWidgetClass,
848 parent,
849 XmNpaneMinimum, 2*new_screen->ch_height,
850 XmNallowResize, True,
851 NULL
854 /* create a scrollbar. */
855 new_screen->scroll = XtVaCreateManagedWidget( "scroll",
856 xmScrollBarWidgetClass,
857 new_screen->form,
858 XmNtopAttachment, XmATTACH_FORM,
859 XmNbottomAttachment, XmATTACH_FORM,
860 XmNrightAttachment, XmATTACH_FORM,
861 XmNminimum, 1,
862 XmNmaximum, 2,
863 XmNsliderSize, 1,
864 NULL
866 XtAddCallback( new_screen->scroll,
867 XmNvalueChangedCallback,
868 scrollbar_moved,
869 new_screen
871 XtAddCallback( new_screen->scroll,
872 XmNdragCallback,
873 scrollbar_moved,
874 new_screen
877 /* create a frame because they look nice */
878 frame = XtVaCreateManagedWidget( "frame",
879 xmFrameWidgetClass,
880 new_screen->form,
881 XmNshadowType, XmSHADOW_ETCHED_IN,
882 XmNtopAttachment, XmATTACH_FORM,
883 XmNbottomAttachment, XmATTACH_FORM,
884 XmNleftAttachment, XmATTACH_FORM,
885 XmNrightAttachment, XmATTACH_WIDGET,
886 XmNrightWidget, new_screen->scroll,
887 NULL
890 /* create a drawing area into which we will put text */
891 new_screen->area = XtVaCreateManagedWidget( "screen",
892 xmDrawingAreaWidgetClass,
893 frame,
894 XmNheight, new_screen->ch_height * new_screen->rows,
895 XmNwidth, new_screen->ch_width * new_screen->cols,
896 XmNtranslations, area_trans,
897 XmNuserData, new_screen,
898 XmNnavigationType, XmNONE,
899 XmNtraversalOn, False,
900 NULL
903 /* this callback is for when the drawing area is resized */
904 XtAddCallback( new_screen->area,
905 XmNresizeCallback,
906 resize_func,
907 new_screen
910 /* this callback is for when the drawing area is exposed */
911 XtAddCallback( new_screen->area,
912 XmNexposeCallback,
913 __vi_expose_func,
914 new_screen
917 /* this callback is for when we expose obscured bits
918 * (e.g. there is a window over part of our drawing area
920 XtAddEventHandler( new_screen->area,
921 0, /* no standard events */
922 True, /* we *WANT* GraphicsExpose */
923 xexpose, /* what to do */
924 new_screen
927 return new_screen;
931 static xvi_screen *split_screen()
933 Cardinal num;
934 WidgetList c;
935 int rows = __vi_screen->rows / 2;
936 xvi_screen *new_screen;
938 /* Note that (global) cur_screen needs to be correctly set so that
939 * insert_here knows which screen to put the new one after
941 new_screen = create_screen( __vi_screen->parent,
942 rows,
943 __vi_screen->cols
946 /* what are the screens? */
947 XtVaGetValues( __vi_screen->parent,
948 XmNnumChildren, &num,
949 XmNchildren, &c,
950 NULL
953 /* unmanage all children in preparation for resizing */
954 XtUnmanageChildren( c, num );
956 /* force resize of the affected screens */
957 XtVaSetValues( new_screen->form,
958 XmNheight, new_screen->ch_height * rows,
959 NULL
961 XtVaSetValues( __vi_screen->form,
962 XmNheight, __vi_screen->ch_height * rows,
963 NULL
966 /* re-manage */
967 XtManageChildren( c, num );
969 /* done */
970 return new_screen;
974 /* Tell me where to insert the next subpane */
975 #if defined(__STDC__)
976 static Cardinal insert_here( Widget wid )
977 #else
978 static Cardinal insert_here( wid )
979 Widget wid;
980 #endif
982 Cardinal i, num;
983 WidgetList c;
985 XtVaGetValues( XtParent(wid),
986 XmNnumChildren, &num,
987 XmNchildren, &c,
988 NULL
991 /* The default XmNinsertPosition procedure for PanedWindow
992 * causes sashes to be inserted at the end of the list of children
993 * and causes non-sash widgets to be inserted after other
994 * non-sash children but before any sashes.
996 if ( ! XmIsForm( wid ) )
997 return num;
999 /* We will put the widget after the one with the current screen */
1000 for (i=0; i<num && XmIsForm(c[i]); i++) {
1001 if ( __vi_screen == NULL || __vi_screen->form == c[i] )
1002 return i+1; /* after the i-th */
1005 /* could not find it? this should never happen */
1006 return num;
1011 * vi_create_editor --
1012 * Create the necessary widgetry.
1014 * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void)));
1016 Widget
1017 vi_create_editor(name, parent, exitp)
1018 String name;
1019 Widget parent;
1020 void (*exitp) __P((void));
1022 Widget pane_w;
1023 Display *display = XtDisplay( parent );
1025 __vi_exitp = exitp;
1027 /* first time through? */
1028 if ( ctx == NULL ) {
1030 /* save this for later */
1031 ctx = XtWidgetToApplicationContext( parent );
1033 /* add our own special actions */
1034 XtAppAddActions( ctx, area_actions, XtNumber(area_actions) );
1036 /* how long is double-click? */
1037 multi_click_length = XtGetMultiClickTime( display );
1039 /* check the resource database for interesting resources */
1040 __XutConvertResources( parent,
1041 vi_progname,
1042 resource,
1043 XtNumber(resource)
1046 /* we need a context for moving bits around in the windows */
1047 __vi_copy_gc = XCreateGC( display,
1048 DefaultRootWindow(display),
1053 /* routines for inter client communications conventions */
1054 __vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf );
1057 /* create the paned window */
1058 pane_w = XtVaCreateManagedWidget( "pane",
1059 xmPanedWindowWidgetClass,
1060 parent,
1061 XmNinsertPosition, insert_here,
1062 NULL
1065 /* allocate our data structure. in the future we will have several
1066 * screens running around at the same time
1068 __vi_screen = create_screen( pane_w, 24, 80 );
1070 /* force creation of our color text context */
1071 set_gc_colors( __vi_screen, COLOR_STANDARD );
1073 /* done */
1074 return pane_w;
1078 /* These routines deal with the selection buffer */
1080 static int selection_start, selection_end, selection_anchor;
1081 static enum select_enum {
1082 select_char, select_word, select_line
1083 } select_type = select_char;
1084 static int last_click;
1086 static char *clipboard = NULL;
1087 static int clipboard_size = 0,
1088 clipboard_length;
1091 #if defined(__STDC__)
1092 static void copy_to_clipboard( xvi_screen *cur_screen )
1093 #else
1094 static void copy_to_clipboard( cur_screen )
1095 xvi_screen *cur_screen;
1096 #endif
1098 /* for now, copy from the backing store. in the future,
1099 * vi core will tell us exactly what the selection buffer contains
1101 clipboard_length = 1 + selection_end - selection_start;
1103 if ( clipboard == NULL )
1104 clipboard = (char *) malloc( clipboard_length );
1105 else if ( clipboard_size < clipboard_length )
1106 clipboard = (char *) realloc( clipboard, clipboard_length );
1108 memcpy( clipboard,
1109 cur_screen->characters + selection_start,
1110 clipboard_length
1115 #if defined(__STDC__)
1116 static void mark_selection( xvi_screen *cur_screen, int start, int end )
1117 #else
1118 static void mark_selection( cur_screen, start, end )
1119 xvi_screen *cur_screen;
1120 int start;
1121 int end;
1122 #endif
1124 int row, col, i;
1126 for ( i=start; i<=end; i++ ) {
1127 if ( !( cur_screen->flags[i] & COLOR_SELECT ) ) {
1128 cur_screen->flags[i] |= COLOR_SELECT;
1129 ToRowCol( cur_screen, i, row, col );
1130 __vi_draw_text( cur_screen, row, col, 1 );
1136 #if defined(__STDC__)
1137 static void erase_selection( xvi_screen *cur_screen, int start, int end )
1138 #else
1139 static void erase_selection( cur_screen, start, end )
1140 xvi_screen *cur_screen;
1141 int start;
1142 int end;
1143 #endif
1145 int row, col, i;
1147 for ( i=start; i<=end; i++ ) {
1148 if ( cur_screen->flags[i] & COLOR_SELECT ) {
1149 cur_screen->flags[i] &= ~COLOR_SELECT;
1150 ToRowCol( cur_screen, i, row, col );
1151 __vi_draw_text( cur_screen, row, col, 1 );
1157 #if defined(__STDC__)
1158 static void left_expand_selection( xvi_screen *cur_screen, int *start )
1159 #else
1160 static void left_expand_selection( cur_screen, start )
1161 xvi_screen *cur_screen;
1162 int *start;
1163 #endif
1165 int row, col;
1167 switch ( select_type ) {
1168 case select_word:
1169 if ( *start == 0 || isspace( cur_screen->characters[*start] ) )
1170 return;
1171 for (;;) {
1172 if ( isspace( cur_screen->characters[*start-1] ) )
1173 return;
1174 if ( --(*start) == 0 )
1175 return;
1177 case select_line:
1178 ToRowCol( cur_screen, *start, row, col );
1179 col = 0;
1180 *start = Linear( cur_screen, row, col );
1181 break;
1186 #if defined(__STDC__)
1187 static void right_expand_selection( xvi_screen *cur_screen, int *end )
1188 #else
1189 static void right_expand_selection( cur_screen, end )
1190 xvi_screen *cur_screen;
1191 int *end;
1192 #endif
1194 int row, col, last = cur_screen->cols * cur_screen->rows - 1;
1196 switch ( select_type ) {
1197 case select_word:
1198 if ( *end == last || isspace( cur_screen->characters[*end] ) )
1199 return;
1200 for (;;) {
1201 if ( isspace( cur_screen->characters[*end+1] ) )
1202 return;
1203 if ( ++(*end) == last )
1204 return;
1206 case select_line:
1207 ToRowCol( cur_screen, *end, row, col );
1208 col = cur_screen->cols -1;
1209 *end = Linear( cur_screen, row, col );
1210 break;
1215 #if defined(__STDC__)
1216 static void select_start( Widget widget,
1217 XEvent *event,
1218 String str,
1219 Cardinal *cardinal
1221 #else
1222 static void select_start( widget, event, str, cardinal )
1223 Widget widget;
1224 XEvent *event;
1225 String str;
1226 Cardinal *cardinal;
1227 #endif
1229 IP_BUF ipb;
1230 int xpos, ypos;
1231 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1232 static int last_click;
1235 * NOTE: when multiple panes are implemented, we need to find the correct
1236 * screen. For now, there is only one.
1238 xpos = COLUMN( __vi_screen, ev->x );
1239 ypos = ROW( __vi_screen, ev->y );
1241 /* Remove the old one. */
1242 erase_selection( __vi_screen, selection_start, selection_end );
1244 /* Send the new cursor position. */
1245 ipb.code = VI_MOUSE_MOVE;
1246 ipb.val1 = ypos;
1247 ipb.val2 = xpos;
1248 (void)vi_send("12", &ipb);
1250 /* click-click, and we go for words, lines, etc */
1251 if ( ev->time - last_click < multi_click_length )
1252 select_type = (enum select_enum) ((((int)select_type)+1)%3);
1253 else
1254 select_type = select_char;
1255 last_click = ev->time;
1257 /* put the selection here */
1258 selection_anchor = Linear( __vi_screen, ypos, xpos );
1259 selection_start = selection_anchor;
1260 selection_end = selection_anchor;
1262 /* expand to include words, line, etc */
1263 left_expand_selection( __vi_screen, &selection_start );
1264 right_expand_selection( __vi_screen, &selection_end );
1266 /* draw the new one */
1267 mark_selection( __vi_screen, selection_start, selection_end );
1269 /* and tell the window manager we own the selection */
1270 if ( select_type != select_char ) {
1271 __vi_AcquirePrimary( widget );
1272 copy_to_clipboard( __vi_screen );
1277 #if defined(__STDC__)
1278 static void select_extend( Widget widget,
1279 XEvent *event,
1280 String str,
1281 Cardinal *cardinal
1283 #else
1284 static void select_extend( widget, event, str, cardinal )
1285 Widget widget;
1286 XEvent *event;
1287 String str;
1288 Cardinal *cardinal;
1289 #endif
1291 int xpos, ypos, pos;
1292 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1294 /* NOTE: when multiple panes are implemented, we need to find
1295 * the correct screen. For now, there is only one.
1297 xpos = COLUMN( __vi_screen, ev->x );
1298 ypos = ROW( __vi_screen, ev->y );
1300 /* deal with words, lines, etc */
1301 pos = Linear( __vi_screen, ypos, xpos );
1302 if ( pos < selection_anchor )
1303 left_expand_selection( __vi_screen, &pos );
1304 else
1305 right_expand_selection( __vi_screen, &pos );
1307 /* extend from before the start? */
1308 if ( pos < selection_start ) {
1309 mark_selection( __vi_screen, pos, selection_start-1 );
1310 selection_start = pos;
1313 /* extend past the end? */
1314 else if ( pos > selection_end ) {
1315 mark_selection( __vi_screen, selection_end+1, pos );
1316 selection_end = pos;
1319 /* between the anchor and the start? */
1320 else if ( pos < selection_anchor ) {
1321 erase_selection( __vi_screen, selection_start, pos-1 );
1322 selection_start = pos;
1325 /* between the anchor and the end? */
1326 else {
1327 erase_selection( __vi_screen, pos+1, selection_end );
1328 selection_end = pos;
1331 /* and tell the window manager we own the selection */
1332 __vi_AcquirePrimary( widget );
1333 copy_to_clipboard( __vi_screen );
1337 #if defined(__STDC__)
1338 static void select_paste( Widget widget,
1339 XEvent *event,
1340 String str,
1341 Cardinal *cardinal
1343 #else
1344 static void select_paste( widget, event, str, cardinal )
1345 Widget widget;
1346 XEvent *event;
1347 String str;
1348 Cardinal *cardinal;
1349 #endif
1351 __vi_PasteFromClipboard( widget );
1355 /* Interface to copy and paste
1356 * (a) callbacks from the window manager
1357 * f_copy - it wants our buffer
1358 * f_paste - it wants us to paste some text
1359 * f_clear - we've lost the selection, clear it
1362 #if defined(__STDC__)
1363 static void f_copy( String *buffer, int *len )
1364 #else
1365 static void f_copy( buffer, len )
1366 String *buffer;
1367 int *len;
1368 #endif
1370 #ifdef TRACE
1371 vtrace("f_copy() called");
1372 #endif
1373 *buffer = clipboard;
1374 *len = clipboard_length;
1379 static void f_paste( widget, buffer, length )
1380 int widget, buffer, length;
1382 /* NOTE: when multiple panes are implemented, we need to find
1383 * the correct screen. For now, there is only one.
1385 #ifdef TRACE
1386 vtrace("f_paste() called with '%*.*s'\n", length, length, buffer);
1387 #endif
1391 #if defined(__STDC__)
1392 static void f_clear( Widget widget )
1393 #else
1394 static void f_clear( widget )
1395 Widget widget;
1396 #endif
1398 xvi_screen *cur_screen;
1400 #ifdef TRACE
1401 vtrace("f_clear() called");
1402 #endif
1404 XtVaGetValues( widget, XmNuserData, &cur_screen, 0 );
1406 erase_selection( cur_screen, selection_start, selection_end );
1411 * These routines deal with the cursor.
1413 * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int));
1415 void
1416 __vi_set_cursor(cur_screen, is_busy)
1417 xvi_screen *cur_screen;
1418 int is_busy;
1420 XDefineCursor( XtDisplay(cur_screen->area),
1421 XtWindow(cur_screen->area),
1422 (is_busy) ? busy_cursor : std_cursor
1428 /* hooks for the tags widget */
1430 static String cur_word = NULL;
1433 * PUBLIC: void __vi_set_word_at_caret __P((xvi_screen *));
1435 void
1436 __vi_set_word_at_caret( this_screen )
1437 xvi_screen *this_screen;
1439 char *start, *end, save;
1440 int newx, newy;
1442 newx = this_screen->curx;
1443 newy = this_screen->cury;
1445 /* Note that this really ought to be done by core due to wrapping issues */
1446 for ( end = start = CharAt( this_screen, newy, newx );
1447 (isalnum( *end ) || *end == '_') && (newx < this_screen->cols);
1448 end++, newx++
1450 save = *end;
1451 *end = '\0';
1452 if ( cur_word != NULL ) free( cur_word );
1453 cur_word = strdup( start );
1454 *end = save;
1456 /* if the tag stack widget is active, set the text field there
1457 * to agree with the current caret position.
1459 __vi_set_tag_text( cur_word );
1463 String __vi_get_word_at_caret( this_screen )
1464 xvi_screen *this_screen;
1466 return (cur_word) ? cur_word : "";
1471 * These routines deal with the caret.
1473 * PUBLIC: void draw_caret __P((xvi_screen *));
1475 static void
1476 draw_caret(this_screen)
1477 xvi_screen *this_screen;
1479 /* draw the caret by drawing the text in highlight color */
1480 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) |= COLOR_CARET;
1481 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1485 * PUBLIC: void __vi_erase_caret __P((xvi_screen *));
1487 void
1488 __vi_erase_caret(this_screen)
1489 xvi_screen *this_screen;
1491 /* erase the caret by drawing the text in normal video */
1492 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) &= ~COLOR_CARET;
1493 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1497 * PUBLIC: void __vi_move_caret __P((xvi_screen *, int, int));
1499 void
1500 __vi_move_caret(this_screen, newy, newx)
1501 xvi_screen *this_screen;
1502 int newy, newx;
1504 /* remove the old caret */
1505 __vi_erase_caret( this_screen );
1507 /* caret is now here */
1508 this_screen->curx = newx;
1509 this_screen->cury = newy;
1510 draw_caret( this_screen );