protoizing glitch
[nvi.git] / motif_l / m_vi.c
blobff7c153bee0903e3668375b13933a4e741ec362f
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.40 2001/06/28 17:53:39 skimo Exp $ (Berkeley) $Date: 2001/06/28 17:53:39 $";
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(String *buffer, int *len);
47 static void f_paste(int widget, int buffer, int length);
48 static void f_clear(Widget widget);
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(void)
89 scroll_block = True;
93 * PUBLIC: void __vi_clear_scroll_block __P((void));
95 void
96 __vi_clear_scroll_block(void)
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 __P((Widget w));
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(XtPointer client_data, int *source, XtInputId *id)
248 /* Parse and dispatch on commands in the queue. */
249 (void)ipvi_motif->input(ipvi_motif, *source);
251 #ifdef notdef
252 /* Check the pipe for unused events when not busy. */
253 XtAppAddWorkProc(ctx, process_pipe_input, NULL);
254 #endif
259 /* Send the window size. */
260 #if defined(__STDC__)
261 static void send_resize( xvi_screen *this_screen )
262 #else
263 static void send_resize( this_screen )
264 xvi_screen *this_screen;
265 #endif
267 IP_BUF ipb;
269 ipb.val1 = this_screen->rows;
270 ipb.val2 = this_screen->cols;
271 ipb.code = VI_RESIZE;
273 #ifdef TRACE
274 vtrace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols);
275 #endif
277 /* send up the pipe */
278 vi_send(vi_ofd, "12", &ipb);
282 #if defined(__STDC__)
283 static void resize_backing_store( xvi_screen *this_screen )
284 #else
285 static void resize_backing_store( this_screen )
286 xvi_screen *this_screen;
287 #endif
289 int total_chars = this_screen->rows * this_screen->cols;
291 this_screen->characters = REALLOC( this_screen->characters,
292 total_chars
294 memset( this_screen->characters, ' ', total_chars );
296 this_screen->flags = REALLOC( this_screen->flags,
297 total_chars
299 memset( this_screen->flags, 0, total_chars );
304 /* X will call this when we are resized */
305 #if defined(__STDC__)
306 static void resize_func( Widget wid,
307 XtPointer client_data,
308 XtPointer call_data
310 #else
311 static void resize_func( wid, client_data, call_data )
312 Widget wid;
313 XtPointer client_data;
314 XtPointer call_data;
315 #endif
317 xvi_screen *this_screen = (xvi_screen *) client_data;
318 Dimension height, width;
320 XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 );
322 /* generate correct sizes when we have font metrics implemented */
323 this_screen->cols = width / this_screen->ch_width;
324 this_screen->rows = height / this_screen->ch_height;
326 resize_backing_store( this_screen );
327 send_resize( this_screen );
332 * __vi_draw_text --
333 * Draw from backing store.
335 * PUBLIC: void __vi_draw_text __P((xvi_screen *, int, int, int));
337 void
338 __vi_draw_text(xvi_screen *this_screen, int row, int start_col, int len)
340 int col, color, xpos;
341 char *start, *end;
343 start = CharAt( __vi_screen, row, start_col );
344 color = *FlagAt( __vi_screen, row, start_col );
345 xpos = XPOS( __vi_screen, start_col );
347 /* one column at a time */
348 for ( col=start_col;
349 col<this_screen->cols && col<start_col+len;
350 col++ ) {
352 /* has the color changed? */
353 if ( *FlagAt( __vi_screen, row, col ) == color )
354 continue;
356 /* is there anything to write? */
357 end = CharAt( __vi_screen, row, col );
358 if ( end == start )
359 continue;
361 /* yes. write in the previous color */
362 set_gc_colors( __vi_screen, color );
364 /* add to display */
365 XDrawImageString( XtDisplay(__vi_screen->area),
366 XtWindow(__vi_screen->area),
368 xpos,
369 YPOS( __vi_screen, row ),
370 start,
371 end - start
374 /* this is the new context */
375 color = *FlagAt( __vi_screen, row, col );
376 xpos = XPOS( __vi_screen, col );
377 start = end;
380 /* is there anything to write? */
381 end = CharAt( __vi_screen, row, col );
382 if ( end != start ) {
383 /* yes. write in the previous color */
384 set_gc_colors( __vi_screen, color );
386 /* add to display */
387 XDrawImageString( XtDisplay(__vi_screen->area),
388 XtWindow(__vi_screen->area),
390 xpos,
391 YPOS( __vi_screen, row ),
392 start,
393 end - start
399 /* set clipping rectangles accordingly */
400 #if defined(__STDC__)
401 static void add_to_clip( xvi_screen *cur_screen, int x, int y, int width, int height )
402 #else
403 static void add_to_clip( cur_screen, x, y, width, height )
404 xvi_screen *cur_screen;
405 int x;
406 int y;
407 int width;
408 int height;
409 #endif
411 XRectangle rect;
412 rect.x = x;
413 rect.y = y;
414 rect.height = height;
415 rect.width = width;
416 if ( cur_screen->clip == NULL )
417 cur_screen->clip = XCreateRegion();
418 XUnionRectWithRegion( &rect, cur_screen->clip, cur_screen->clip );
423 * __vi_expose_func --
424 * Redraw the window's contents.
426 * NOTE: When vi wants to force a redraw, we are called with NULL widget
427 * and call_data.
429 * PUBLIC: void __vi_expose_func __P((Widget, XtPointer, XtPointer));
431 void
432 __vi_expose_func(Widget wid, XtPointer client_data, XtPointer call_data)
434 xvi_screen *this_screen;
435 XmDrawingAreaCallbackStruct *cbs;
436 XExposeEvent *xev;
437 XGraphicsExposeEvent *gev;
438 int row;
440 /* convert pointers */
441 this_screen = (xvi_screen *) client_data;
442 cbs = (XmDrawingAreaCallbackStruct *) call_data;
444 /* first exposure? tell vi we are ready... */
445 if ( this_screen->init == False ) {
447 /* what does the user want to see? */
448 __vi_set_cursor( __vi_screen, False );
450 /* vi wants a resize as the first event */
451 send_resize( __vi_screen );
453 /* fine for now. we'll be back */
454 this_screen->init = True;
455 return;
458 if ( call_data == NULL ) {
460 /* vi core calls this when it wants a full refresh */
461 #ifdef TRACE
462 vtrace("expose_func: full refresh\n");
463 #endif
465 XClearWindow( XtDisplay(this_screen->area),
466 XtWindow(this_screen->area)
469 else {
470 switch ( cbs->event->type ) {
472 case GraphicsExpose:
473 gev = (XGraphicsExposeEvent *) cbs->event;
475 /* set clipping rectangles accordingly */
476 add_to_clip( this_screen,
477 gev->x, gev->y,
478 gev->width, gev->height
481 /* X calls here when XCopyArea exposes new bits */
482 #ifdef TRACE
483 vtrace("expose_func (X): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
484 gev->x, gev->y,
485 gev->width, gev->height,
486 gev->count);
487 #endif
489 /* more coming? do it then */
490 if ( gev->count > 0 ) return;
492 /* set clipping region */
493 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
494 break;
496 case Expose:
497 xev = (XExposeEvent *) cbs->event;
499 /* set clipping rectangles accordingly */
500 add_to_clip( this_screen,
501 xev->x, xev->y,
502 xev->width, xev->height
505 /* Motif calls here when DrawingArea is exposed */
506 #ifdef TRACE
507 vtrace("expose_func (Motif): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
508 xev->x, xev->y,
509 xev->width, xev->height,
510 xev->count);
511 #endif
513 /* more coming? do it then */
514 if ( xev->count > 0 ) return;
516 /* set clipping region */
517 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
518 break;
520 default:
521 /* don't care? */
522 return;
526 /* one row at a time */
527 for (row=0; row<this_screen->rows; row++) {
529 /* draw from the backing store */
530 __vi_draw_text( this_screen, row, 0, this_screen->cols );
533 /* clear clipping region */
534 XSetClipMask( XtDisplay(this_screen->area), gc, None );
535 if ( this_screen->clip != NULL ) {
536 XDestroyRegion( this_screen->clip );
537 this_screen->clip = NULL;
543 #if defined(__STDC__)
544 static void xexpose ( Widget w,
545 XtPointer client_data,
546 XEvent *ev,
547 Boolean *cont
549 #else
550 static void xexpose ( w, client_data, ev, cont )
551 Widget w;
552 XtPointer client_data;
553 XEvent *ev;
554 Boolean *cont;
555 #endif
557 XmDrawingAreaCallbackStruct cbs;
559 switch ( ev->type ) {
560 case GraphicsExpose:
561 cbs.event = ev;
562 cbs.window = XtWindow(w);
563 cbs.reason = XmCR_EXPOSE;
564 __vi_expose_func( w, client_data, (XtPointer) &cbs );
565 *cont = False; /* we took care of it */
566 break;
567 default:
568 /* don't care */
569 break;
574 /* unimplemented keystroke or command */
575 #if defined(__STDC__)
576 static void beep( Widget w )
577 #else
578 static void beep( w )
579 Widget w;
580 #endif
582 XBell(XtDisplay(w),0);
586 /* give me a search dialog */
587 #if defined(__STDC__)
588 static void find( Widget w )
589 #else
590 static void find( w )
591 Widget w;
592 #endif
594 __vi_show_search_dialog( w, "Find" );
598 * command --
599 * Translate simple keyboard input into vi protocol commands.
601 static void
602 command(Widget widget, XKeyEvent *event, String *str, Cardinal *cardinal)
604 static struct {
605 String name;
606 int code;
607 int count;
608 } table[] = {
609 { "VI_C_BOL", VI_C_BOL, 0 },
610 { "VI_C_BOTTOM", VI_C_BOTTOM, 0 },
611 { "VI_C_DEL", VI_C_DEL, 0 },
612 { "VI_C_DOWN", VI_C_DOWN, 1 },
613 { "VI_C_EOL", VI_C_EOL, 0 },
614 { "VI_C_INSERT", VI_C_INSERT, 0 },
615 { "VI_C_LEFT", VI_C_LEFT, 0 },
616 { "VI_C_PGDOWN", VI_C_PGDOWN, 1 },
617 { "VI_C_PGUP", VI_C_PGUP, 1 },
618 { "VI_C_RIGHT", VI_C_RIGHT, 0 },
619 { "VI_C_TOP", VI_C_TOP, 0 },
620 { "VI_C_UP", VI_C_UP, 1 },
621 { "VI_INTERRUPT", VI_INTERRUPT, 0 },
623 IP_BUF ipb;
624 int i;
627 * XXX
628 * Do fast lookup based on character #6 -- sleazy, but I don't
629 * want to do 10 strcmp's per keystroke.
631 ipb.val1 = 1;
632 for (i = 0; i < XtNumber(table); i++)
633 if (table[i].name[6] == (*str)[6] &&
634 strcmp(table[i].name, *str) == 0) {
635 ipb.code = table[i].code;
636 vi_send(vi_ofd, table[i].count ? "1" : NULL, &ipb);
637 return;
640 /* oops. */
641 beep(widget);
644 /* mouse or keyboard input. */
645 #if defined(__STDC__)
646 static void insert_string( Widget widget,
647 XKeyEvent *event,
648 String *str,
649 Cardinal *cardinal
651 #else
652 static void insert_string( widget, event, str, cardinal )
653 Widget widget;
654 XKeyEvent *event;
655 String *str;
656 Cardinal *cardinal;
657 #endif
659 IP_BUF ipb;
661 ipb.len1 = strlen( *str );
662 if ( ipb.len1 != 0 ) {
663 ipb.code = VI_STRING;
664 ipb.str1 = *str;
665 vi_send(vi_ofd, "a", &ipb);
668 #ifdef TRACE
669 vtrace("insert_string {%.*s}\n", strlen( *str ), *str );
670 #endif
674 /* mouse or keyboard input. */
675 #if defined(__STDC__)
676 static void key_press( Widget widget,
677 XKeyEvent *event,
678 String str,
679 Cardinal *cardinal
681 #else
682 static void key_press( widget, event, str, cardinal )
683 Widget widget;
684 XKeyEvent *event;
685 String str;
686 Cardinal *cardinal;
687 #endif
689 IP_BUF ipb;
690 char bp[BufferSize];
692 ipb.len1 = XLookupString( event, bp, BufferSize, NULL, NULL );
693 if ( ipb.len1 != 0 ) {
694 ipb.code = VI_STRING;
695 ipb.str1 = bp;
696 #ifdef TRACE
697 vtrace("key_press {%.*s}\n", ipb.len1, bp );
698 #endif
699 vi_send(vi_ofd, "a", &ipb);
705 #if defined(__STDC__)
706 static void scrollbar_moved( Widget widget,
707 XtPointer ptr,
708 XmScrollBarCallbackStruct *cbs
710 #else
711 static void scrollbar_moved( widget, ptr, cbs )
712 Widget widget;
713 XtPointer ptr;
714 XmScrollBarCallbackStruct *cbs;
715 #endif
717 /* Future: Need to scroll the correct screen! */
718 xvi_screen *cur_screen = (xvi_screen *) ptr;
719 IP_BUF ipb;
721 /* if we are still processing messages from core, skip this event
722 * (see comments near __vi_set_scroll_block())
724 if ( scroll_block ) {
725 return;
727 __vi_set_scroll_block();
729 #ifdef TRACE
730 switch ( cbs->reason ) {
731 case XmCR_VALUE_CHANGED:
732 vtrace( "scrollbar VALUE_CHANGED %d\n", cbs->value );
733 break;
734 case XmCR_DRAG:
735 vtrace( "scrollbar DRAG %d\n", cbs->value );
736 break;
737 default:
738 vtrace( "scrollbar <default> %d\n", cbs->value );
739 break;
741 vtrace("scrollto {%d}\n", cbs->value );
742 #endif
744 /* Send the new cursor position. */
745 ipb.code = VI_C_SETTOP;
746 ipb.val1 = cbs->value;
747 (void)vi_send(vi_ofd, "1", &ipb);
751 #if defined(__STDC__)
752 static xvi_screen *create_screen( Widget parent, int rows, int cols )
753 #else
754 static xvi_screen *create_screen( parent, rows, cols )
755 Widget parent;
756 int rows, cols;
757 #endif
759 xvi_screen *new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) );
760 Widget frame;
762 /* init... */
763 new_screen->color = COLOR_STANDARD;
764 new_screen->parent = parent;
766 /* figure out the sizes */
767 new_screen->rows = rows;
768 new_screen->cols = cols;
769 new_screen->ch_width = font->max_bounds.width;
770 new_screen->ch_height = font->descent + font->ascent;
771 new_screen->ch_descent = font->descent;
772 new_screen->clip = NULL;
774 /* allocate and init the backing stores */
775 resize_backing_store( new_screen );
777 /* set up a translation table for the X toolkit */
778 if ( area_trans == NULL )
779 area_trans = XtParseTranslationTable(areaTrans);
781 /* future, new screen gets inserted into the parent sash
782 * immediately after the current screen. Default Pane action is
783 * to add it to the end
786 /* use a form to hold the drawing area and the scrollbar */
787 new_screen->form = XtVaCreateManagedWidget( "form",
788 xmFormWidgetClass,
789 parent,
790 XmNpaneMinimum, 2*new_screen->ch_height,
791 XmNallowResize, True,
792 NULL
795 /* create a scrollbar. */
796 new_screen->scroll = XtVaCreateManagedWidget( "scroll",
797 xmScrollBarWidgetClass,
798 new_screen->form,
799 XmNtopAttachment, XmATTACH_FORM,
800 XmNbottomAttachment, XmATTACH_FORM,
801 XmNrightAttachment, XmATTACH_FORM,
802 XmNminimum, 1,
803 XmNmaximum, 2,
804 XmNsliderSize, 1,
805 NULL
807 XtAddCallback( new_screen->scroll,
808 XmNvalueChangedCallback,
809 scrollbar_moved,
810 new_screen
812 XtAddCallback( new_screen->scroll,
813 XmNdragCallback,
814 scrollbar_moved,
815 new_screen
818 /* create a frame because they look nice */
819 frame = XtVaCreateManagedWidget( "frame",
820 xmFrameWidgetClass,
821 new_screen->form,
822 XmNshadowType, XmSHADOW_ETCHED_IN,
823 XmNtopAttachment, XmATTACH_FORM,
824 XmNbottomAttachment, XmATTACH_FORM,
825 XmNleftAttachment, XmATTACH_FORM,
826 XmNrightAttachment, XmATTACH_WIDGET,
827 XmNrightWidget, new_screen->scroll,
828 NULL
831 /* create a drawing area into which we will put text */
832 new_screen->area = XtVaCreateManagedWidget( "screen",
833 xmDrawingAreaWidgetClass,
834 frame,
835 XmNheight, new_screen->ch_height * new_screen->rows,
836 XmNwidth, new_screen->ch_width * new_screen->cols,
837 XmNtranslations, area_trans,
838 XmNuserData, new_screen,
839 XmNnavigationType, XmNONE,
840 XmNtraversalOn, False,
841 NULL
844 /* this callback is for when the drawing area is resized */
845 XtAddCallback( new_screen->area,
846 XmNresizeCallback,
847 resize_func,
848 new_screen
851 /* this callback is for when the drawing area is exposed */
852 XtAddCallback( new_screen->area,
853 XmNexposeCallback,
854 __vi_expose_func,
855 new_screen
858 /* this callback is for when we expose obscured bits
859 * (e.g. there is a window over part of our drawing area
861 XtAddEventHandler( new_screen->area,
862 0, /* no standard events */
863 True, /* we *WANT* GraphicsExpose */
864 xexpose, /* what to do */
865 new_screen
868 return new_screen;
872 static xvi_screen *split_screen(void)
874 Cardinal num;
875 WidgetList c;
876 int rows = __vi_screen->rows / 2;
877 xvi_screen *new_screen;
879 /* Note that (global) cur_screen needs to be correctly set so that
880 * insert_here knows which screen to put the new one after
882 new_screen = create_screen( __vi_screen->parent,
883 rows,
884 __vi_screen->cols
887 /* what are the screens? */
888 XtVaGetValues( __vi_screen->parent,
889 XmNnumChildren, &num,
890 XmNchildren, &c,
891 NULL
894 /* unmanage all children in preparation for resizing */
895 XtUnmanageChildren( c, num );
897 /* force resize of the affected screens */
898 XtVaSetValues( new_screen->form,
899 XmNheight, new_screen->ch_height * rows,
900 NULL
902 XtVaSetValues( __vi_screen->form,
903 XmNheight, __vi_screen->ch_height * rows,
904 NULL
907 /* re-manage */
908 XtManageChildren( c, num );
910 /* done */
911 return new_screen;
915 /* Tell me where to insert the next subpane */
916 #if defined(__STDC__)
917 static Cardinal insert_here( Widget wid )
918 #else
919 static Cardinal insert_here( wid )
920 Widget wid;
921 #endif
923 Cardinal i, num;
924 WidgetList c;
926 XtVaGetValues( XtParent(wid),
927 XmNnumChildren, &num,
928 XmNchildren, &c,
929 NULL
932 /* The default XmNinsertPosition procedure for PanedWindow
933 * causes sashes to be inserted at the end of the list of children
934 * and causes non-sash widgets to be inserted after other
935 * non-sash children but before any sashes.
937 if ( ! XmIsForm( wid ) )
938 return num;
940 /* We will put the widget after the one with the current screen */
941 for (i=0; i<num && XmIsForm(c[i]); i++) {
942 if ( __vi_screen == NULL || __vi_screen->form == c[i] )
943 return i+1; /* after the i-th */
946 /* could not find it? this should never happen */
947 return num;
952 * vi_create_editor --
953 * Create the necessary widgetry.
955 * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void)));
957 Widget
958 vi_create_editor(String name, Widget parent, void (*exitp) (void))
960 Widget pane_w;
961 Display *display = XtDisplay( parent );
963 __vi_exitp = exitp;
965 /* first time through? */
966 if ( ctx == NULL ) {
968 /* save this for later */
969 ctx = XtWidgetToApplicationContext( parent );
971 /* add our own special actions */
972 XtAppAddActions( ctx, area_actions, XtNumber(area_actions) );
974 /* how long is double-click? */
975 multi_click_length = XtGetMultiClickTime( display );
977 /* check the resource database for interesting resources */
978 __XutConvertResources( parent,
979 vi_progname,
980 resource,
981 XtNumber(resource)
984 /* we need a context for moving bits around in the windows */
985 __vi_copy_gc = XCreateGC( display,
986 DefaultRootWindow(display),
991 /* routines for inter client communications conventions */
992 __vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf );
995 /* create the paned window */
996 pane_w = XtVaCreateManagedWidget( "pane",
997 xmPanedWindowWidgetClass,
998 parent,
999 XmNinsertPosition, insert_here,
1000 NULL
1003 /* allocate our data structure. in the future we will have several
1004 * screens running around at the same time
1006 __vi_screen = create_screen( pane_w, 24, 80 );
1008 /* force creation of our color text context */
1009 set_gc_colors( __vi_screen, COLOR_STANDARD );
1011 /* done */
1012 return pane_w;
1016 /* These routines deal with the selection buffer */
1018 static int selection_start, selection_end, selection_anchor;
1019 static enum select_enum {
1020 select_char, select_word, select_line
1021 } select_type = select_char;
1022 static int last_click;
1024 static char *clipboard = NULL;
1025 static int clipboard_size = 0,
1026 clipboard_length;
1029 #if defined(__STDC__)
1030 static void copy_to_clipboard( xvi_screen *cur_screen )
1031 #else
1032 static void copy_to_clipboard( cur_screen )
1033 xvi_screen *cur_screen;
1034 #endif
1036 /* for now, copy from the backing store. in the future,
1037 * vi core will tell us exactly what the selection buffer contains
1039 clipboard_length = 1 + selection_end - selection_start;
1041 if ( clipboard == NULL )
1042 clipboard = (char *) malloc( clipboard_length );
1043 else if ( clipboard_size < clipboard_length )
1044 clipboard = (char *) realloc( clipboard, clipboard_length );
1046 memcpy( clipboard,
1047 cur_screen->characters + selection_start,
1048 clipboard_length
1053 #if defined(__STDC__)
1054 static void mark_selection( xvi_screen *cur_screen, int start, int end )
1055 #else
1056 static void mark_selection( cur_screen, start, end )
1057 xvi_screen *cur_screen;
1058 int start;
1059 int end;
1060 #endif
1062 int row, col, i;
1064 for ( i=start; i<=end; i++ ) {
1065 if ( !( cur_screen->flags[i] & COLOR_SELECT ) ) {
1066 cur_screen->flags[i] |= COLOR_SELECT;
1067 ToRowCol( cur_screen, i, row, col );
1068 __vi_draw_text( cur_screen, row, col, 1 );
1074 #if defined(__STDC__)
1075 static void erase_selection( xvi_screen *cur_screen, int start, int end )
1076 #else
1077 static void erase_selection( cur_screen, start, end )
1078 xvi_screen *cur_screen;
1079 int start;
1080 int end;
1081 #endif
1083 int row, col, i;
1085 for ( i=start; i<=end; i++ ) {
1086 if ( cur_screen->flags[i] & COLOR_SELECT ) {
1087 cur_screen->flags[i] &= ~COLOR_SELECT;
1088 ToRowCol( cur_screen, i, row, col );
1089 __vi_draw_text( cur_screen, row, col, 1 );
1095 #if defined(__STDC__)
1096 static void left_expand_selection( xvi_screen *cur_screen, int *start )
1097 #else
1098 static void left_expand_selection( cur_screen, start )
1099 xvi_screen *cur_screen;
1100 int *start;
1101 #endif
1103 int row, col;
1105 switch ( select_type ) {
1106 case select_word:
1107 if ( *start == 0 || isspace( cur_screen->characters[*start] ) )
1108 return;
1109 for (;;) {
1110 if ( isspace( cur_screen->characters[*start-1] ) )
1111 return;
1112 if ( --(*start) == 0 )
1113 return;
1115 case select_line:
1116 ToRowCol( cur_screen, *start, row, col );
1117 col = 0;
1118 *start = Linear( cur_screen, row, col );
1119 break;
1124 #if defined(__STDC__)
1125 static void right_expand_selection( xvi_screen *cur_screen, int *end )
1126 #else
1127 static void right_expand_selection( cur_screen, end )
1128 xvi_screen *cur_screen;
1129 int *end;
1130 #endif
1132 int row, col, last = cur_screen->cols * cur_screen->rows - 1;
1134 switch ( select_type ) {
1135 case select_word:
1136 if ( *end == last || isspace( cur_screen->characters[*end] ) )
1137 return;
1138 for (;;) {
1139 if ( isspace( cur_screen->characters[*end+1] ) )
1140 return;
1141 if ( ++(*end) == last )
1142 return;
1144 case select_line:
1145 ToRowCol( cur_screen, *end, row, col );
1146 col = cur_screen->cols -1;
1147 *end = Linear( cur_screen, row, col );
1148 break;
1153 #if defined(__STDC__)
1154 static void select_start( Widget widget,
1155 XEvent *event,
1156 String str,
1157 Cardinal *cardinal
1159 #else
1160 static void select_start( widget, event, str, cardinal )
1161 Widget widget;
1162 XEvent *event;
1163 String str;
1164 Cardinal *cardinal;
1165 #endif
1167 IP_BUF ipb;
1168 int xpos, ypos;
1169 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1170 static int last_click;
1173 * NOTE: when multiple panes are implemented, we need to find the correct
1174 * screen. For now, there is only one.
1176 xpos = COLUMN( __vi_screen, ev->x );
1177 ypos = ROW( __vi_screen, ev->y );
1179 /* Remove the old one. */
1180 erase_selection( __vi_screen, selection_start, selection_end );
1182 /* Send the new cursor position. */
1183 ipb.code = VI_MOUSE_MOVE;
1184 ipb.val1 = ypos;
1185 ipb.val2 = xpos;
1186 (void)vi_send(vi_ofd, "12", &ipb);
1188 /* click-click, and we go for words, lines, etc */
1189 if ( ev->time - last_click < multi_click_length )
1190 select_type = (enum select_enum) ((((int)select_type)+1)%3);
1191 else
1192 select_type = select_char;
1193 last_click = ev->time;
1195 /* put the selection here */
1196 selection_anchor = Linear( __vi_screen, ypos, xpos );
1197 selection_start = selection_anchor;
1198 selection_end = selection_anchor;
1200 /* expand to include words, line, etc */
1201 left_expand_selection( __vi_screen, &selection_start );
1202 right_expand_selection( __vi_screen, &selection_end );
1204 /* draw the new one */
1205 mark_selection( __vi_screen, selection_start, selection_end );
1207 /* and tell the window manager we own the selection */
1208 if ( select_type != select_char ) {
1209 __vi_AcquirePrimary( widget );
1210 copy_to_clipboard( __vi_screen );
1215 #if defined(__STDC__)
1216 static void select_extend( Widget widget,
1217 XEvent *event,
1218 String str,
1219 Cardinal *cardinal
1221 #else
1222 static void select_extend( widget, event, str, cardinal )
1223 Widget widget;
1224 XEvent *event;
1225 String str;
1226 Cardinal *cardinal;
1227 #endif
1229 int xpos, ypos, pos;
1230 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1232 /* NOTE: when multiple panes are implemented, we need to find
1233 * the correct screen. For now, there is only one.
1235 xpos = COLUMN( __vi_screen, ev->x );
1236 ypos = ROW( __vi_screen, ev->y );
1238 /* deal with words, lines, etc */
1239 pos = Linear( __vi_screen, ypos, xpos );
1240 if ( pos < selection_anchor )
1241 left_expand_selection( __vi_screen, &pos );
1242 else
1243 right_expand_selection( __vi_screen, &pos );
1245 /* extend from before the start? */
1246 if ( pos < selection_start ) {
1247 mark_selection( __vi_screen, pos, selection_start-1 );
1248 selection_start = pos;
1251 /* extend past the end? */
1252 else if ( pos > selection_end ) {
1253 mark_selection( __vi_screen, selection_end+1, pos );
1254 selection_end = pos;
1257 /* between the anchor and the start? */
1258 else if ( pos < selection_anchor ) {
1259 erase_selection( __vi_screen, selection_start, pos-1 );
1260 selection_start = pos;
1263 /* between the anchor and the end? */
1264 else {
1265 erase_selection( __vi_screen, pos+1, selection_end );
1266 selection_end = pos;
1269 /* and tell the window manager we own the selection */
1270 __vi_AcquirePrimary( widget );
1271 copy_to_clipboard( __vi_screen );
1275 #if defined(__STDC__)
1276 static void select_paste( Widget widget,
1277 XEvent *event,
1278 String str,
1279 Cardinal *cardinal
1281 #else
1282 static void select_paste( widget, event, str, cardinal )
1283 Widget widget;
1284 XEvent *event;
1285 String str;
1286 Cardinal *cardinal;
1287 #endif
1289 __vi_PasteFromClipboard( widget );
1293 /* Interface to copy and paste
1294 * (a) callbacks from the window manager
1295 * f_copy - it wants our buffer
1296 * f_paste - it wants us to paste some text
1297 * f_clear - we've lost the selection, clear it
1300 #if defined(__STDC__)
1301 static void f_copy( String *buffer, int *len )
1302 #else
1303 static void f_copy( buffer, len )
1304 String *buffer;
1305 int *len;
1306 #endif
1308 #ifdef TRACE
1309 vtrace("f_copy() called");
1310 #endif
1311 *buffer = clipboard;
1312 *len = clipboard_length;
1317 static void f_paste(int widget, int buffer, int length)
1319 /* NOTE: when multiple panes are implemented, we need to find
1320 * the correct screen. For now, there is only one.
1322 #ifdef TRACE
1323 vtrace("f_paste() called with '%*.*s'\n", length, length, buffer);
1324 #endif
1328 #if defined(__STDC__)
1329 static void f_clear( Widget widget )
1330 #else
1331 static void f_clear( widget )
1332 Widget widget;
1333 #endif
1335 xvi_screen *cur_screen;
1337 #ifdef TRACE
1338 vtrace("f_clear() called");
1339 #endif
1341 XtVaGetValues( widget, XmNuserData, &cur_screen, 0 );
1343 erase_selection( cur_screen, selection_start, selection_end );
1348 * These routines deal with the cursor.
1350 * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int));
1352 void
1353 __vi_set_cursor(xvi_screen *cur_screen, int is_busy)
1355 XDefineCursor( XtDisplay(cur_screen->area),
1356 XtWindow(cur_screen->area),
1357 (is_busy) ? busy_cursor : std_cursor
1363 /* hooks for the tags widget */
1365 static String cur_word = NULL;
1368 * PUBLIC: void __vi_set_word_at_caret __P((xvi_screen *));
1370 void
1371 __vi_set_word_at_caret(xvi_screen *this_screen)
1373 char *start, *end, save;
1374 int newx, newy;
1376 newx = this_screen->curx;
1377 newy = this_screen->cury;
1379 /* Note that this really ought to be done by core due to wrapping issues */
1380 for ( end = start = CharAt( this_screen, newy, newx );
1381 (isalnum( *end ) || *end == '_') && (newx < this_screen->cols);
1382 end++, newx++
1384 save = *end;
1385 *end = '\0';
1386 if ( cur_word != NULL ) free( cur_word );
1387 cur_word = strdup( start );
1388 *end = save;
1390 /* if the tag stack widget is active, set the text field there
1391 * to agree with the current caret position.
1393 __vi_set_tag_text( cur_word );
1397 String __vi_get_word_at_caret(xvi_screen *this_screen)
1399 return (cur_word) ? cur_word : "";
1404 * These routines deal with the caret.
1406 * PUBLIC: void draw_caret __P((xvi_screen *));
1408 static void
1409 draw_caret(xvi_screen *this_screen)
1411 /* draw the caret by drawing the text in highlight color */
1412 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) |= COLOR_CARET;
1413 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1417 * PUBLIC: void __vi_erase_caret __P((xvi_screen *));
1419 void
1420 __vi_erase_caret(xvi_screen *this_screen)
1422 /* erase the caret by drawing the text in normal video */
1423 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) &= ~COLOR_CARET;
1424 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1428 * PUBLIC: void __vi_move_caret __P((xvi_screen *, int, int));
1430 void
1431 __vi_move_caret(xvi_screen *this_screen, int newy, int newx)
1433 /* remove the old caret */
1434 __vi_erase_caret( this_screen );
1436 /* caret is now here */
1437 this_screen->curx = newx;
1438 this_screen->cury = newy;
1439 draw_caret( this_screen );