from RAZ: ruler dialog box
[nvi.git] / motif_l / m_vi.c
blobaa5f91e331be6cc23da59197eff6d6e6a1e027e6
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.29 1996/12/13 12:23:05 bostic Exp $ (Berkeley) $Date: 1996/12/13 12:23:05 $";
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 "../ip/ip.h"
40 #include "m_motif.h"
41 #include "m_util.h"
42 #include "m_extern.h"
43 #include "vi_mextern.h"
44 #include "ipc_extern.h"
45 #include "pathnames.h"
47 static void f_copy();
48 static void f_paste();
49 static void f_clear();
53 * Globals and costants
56 #define BufferSize 1024
58 static XFontStruct *font;
59 static GC gc;
60 GC __vi_copy_gc;
61 static XtAppContext ctx;
63 xvi_screen *__vi_screen = NULL;
64 static Cursor std_cursor;
65 static Cursor busy_cursor;
66 static XtTranslations area_trans;
67 static int multi_click_length;
69 void (*__vi_exitp)(); /* Exit function. */
71 static char bp[ BufferSize ]; /* input buffer from pipe */
72 static size_t len, blen = sizeof(bp);
75 /* hack for drag scrolling...
76 * I'm not sure why, but the current protocol gets out of sync when
77 * a lot of drag messages get passed around. Likely, we need to wait
78 * for core to finish repainting the screen before sending more drag
79 * messages.
80 * To that end, we set scroll_block when we receive input from the scrollbar,
81 * and we clear it when we process the IPO_REFRESH message from core.
82 * A specific SCROLL_COMPLETED message would be better, but this seems to work.
85 static Boolean scroll_block = False;
87 void
88 __vi_set_scroll_block()
90 scroll_block = True;
93 void
94 __vi_clear_scroll_block()
96 scroll_block = False;
100 #if defined(__STDC__)
101 static void set_gc_colors( xvi_screen *this_screen, int val )
102 #else
103 static void set_gc_colors( this_screen, val )
104 xvi_screen *this_screen;
105 int val;
106 #endif
108 static Pixel fg, bg, hi, shade;
109 static int prev = COLOR_INVALID;
111 /* no change? */
112 if ( prev == val ) return;
114 /* init? */
115 if ( gc == NULL ) {
117 /* what colors are selected for the drawing area? */
118 XtVaGetValues( this_screen->area,
119 XtNbackground, &bg,
120 XtNforeground, &fg,
121 XmNhighlightColor, &hi,
122 XmNtopShadowColor, &shade,
126 gc = XCreateGC( XtDisplay(this_screen->area),
127 DefaultRootWindow(XtDisplay(this_screen->area)),
132 XSetFont( XtDisplay(this_screen->area), gc, font->fid );
135 /* special colors? */
136 if ( val & COLOR_CARET ) {
137 XSetForeground( XtDisplay(this_screen->area), gc, fg );
138 XSetBackground( XtDisplay(this_screen->area), gc, hi );
140 else if ( val & COLOR_SELECT ) {
141 XSetForeground( XtDisplay(this_screen->area), gc, fg );
142 XSetBackground( XtDisplay(this_screen->area), gc, shade );
144 else switch (val) {
145 case COLOR_STANDARD:
146 XSetForeground( XtDisplay(this_screen->area), gc, fg );
147 XSetBackground( XtDisplay(this_screen->area), gc, bg );
148 break;
149 case COLOR_INVERSE:
150 XSetForeground( XtDisplay(this_screen->area), gc, bg );
151 XSetBackground( XtDisplay(this_screen->area), gc, fg );
152 break;
153 default: /* implement color map later */
154 break;
160 * Memory utilities
163 #ifdef REALLOC
164 #undef REALLOC
165 #endif
167 #define REALLOC( ptr, size ) \
168 ((ptr == NULL) ? malloc(size) : realloc(ptr,size))
171 /* X windows routines.
172 * We currently create a single, top-level shell. In that is a
173 * single drawing area into which we will draw text. This allows
174 * us to put multi-color (and font, but we'll never build that) text
175 * into the drawing area. In the future, we'll add scrollbars to the
176 * drawing areas
179 void select_start();
180 void select_extend();
181 void select_paste();
182 void key_press();
183 void insert_string();
184 void beep();
185 void find();
186 void command();
188 static XtActionsRec area_actions[] = {
189 { "select_start", select_start },
190 { "select_extend", select_extend },
191 { "select_paste", select_paste },
192 { "key_press", key_press },
193 { "insert_string", insert_string },
194 { "beep", beep },
195 { "find", find },
196 { "command", command },
199 char areaTrans[] =
200 "<Btn1Down>: select_start() \n\
201 <Btn1Motion>: select_extend() \n\
202 <Btn2Down>: select_paste() \n\
203 <Btn3Down>: select_extend() \n\
204 <Btn3Motion>: select_extend() \n\
205 <Key>End: command(VI_C_BOTTOM) \n\
206 <Key>Escape: command(EINSERT) \n\
207 <Key>Find: find() \n\
208 <Key>Home: command(VI_C_TOP) \n\
209 <Key>Next: command(VI_C_PGDOWN) \n\
210 <Key>Prior: command(VI_C_PGUP) \n\
211 <Key>osfBackSpace: command(VI_C_LEFT) \n\
212 <Key>osfBeginLine: command(VI_C_BOL) \n\
213 <Key>osfCopy: beep() \n\
214 <Key>osfCut: beep() \n\
215 <Key>osfDelete: command(VI_C_DEL) \n\
216 <Key>osfDown: command(VI_C_DOWN) \n\
217 <Key>osfEndLine: command(VI_C_EOL) \n\
218 <Key>osfInsert: command(VI_C_INSERT) \n\
219 <Key>osfLeft: command(VI_C_LEFT) \n\
220 <Key>osfPageDown: command(VI_C_PGDOWN) \n\
221 <Key>osfPageUp: command(VI_C_PGUP) \n\
222 <Key>osfPaste: insert_string(p) \n\
223 <Key>osfRight: command(VI_C_RIGHT) \n\
224 <Key>osfUndo: command(VI_UNDO) \n\
225 <Key>osfUp: command(VI_C_UP) \n\
226 Ctrl<Key>C: command(VI_INTERRUPT) \n\
227 <Key>: key_press()";
230 static XutResource resource[] = {
231 { "font", XutRKfont, &font },
232 { "pointerShape", XutRKcursor, &std_cursor },
233 { "busyShape", XutRKcursor, &busy_cursor },
238 #if defined(__STDC__)
239 static Boolean process_pipe_input( XtPointer pread )
240 #else
241 static Boolean process_pipe_input( pread )
242 XtPointer pread;
243 #endif
245 /* might have read more since the last call */
246 len += (pread) ? *((int *)pread) : 0;
248 /* Parse to data end or partial message. */
249 (void)__vi_trans(bp, &len);
251 if (len > 0) {
252 #ifdef TRACE
253 trace("pipe_input_func: abort with %d in the buffer\n", len);
254 #endif
255 /* call me again later */
256 return False;
259 /* do NOT call me again later */
260 return True;
265 * vi_input_func --
266 * We've received input on the pipe from vi.
268 * PUBLIC: void vi_input_func __P((XtPointer, int *, XtInputId *));
270 void
271 vi_input_func(client_data, source, id)
272 XtPointer client_data;
273 int *source;
274 XtInputId *id;
276 int nr;
278 /* Read waiting vi messags and translate to X calls. */
279 switch (nr = read( *source, bp + len, blen - len)) {
280 case 0:
281 #ifdef TRACE
282 trace("pipe_input_func: empty input from vi\n");
283 #endif
284 return;
285 case -1:
286 perror("ip_cl: read");
287 exit (1);
288 default:
289 #ifdef TRACE
290 trace("input from vi, %d bytes read\n", nr);
291 #endif
292 break;
295 /* parse and dispatch on commands in the queue */
296 if ( ! process_pipe_input( &nr ) ) {
297 /* check the pipe for unused events when not busy */
298 XtAppAddWorkProc( ctx, process_pipe_input, NULL );
304 /* Send the window size. */
305 #if defined(__STDC__)
306 static void send_resize( xvi_screen *this_screen )
307 #else
308 static void send_resize( this_screen )
309 xvi_screen *this_screen;
310 #endif
312 IP_BUF ipb;
314 ipb.val1 = this_screen->rows;
315 ipb.val2 = this_screen->cols;
316 ipb.code = VI_RESIZE;
318 #ifdef TRACE
319 trace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols);
320 #endif
322 /* send up the pipe */
323 __vi_send("12", &ipb);
327 #if defined(__STDC__)
328 static void resize_backing_store( xvi_screen *this_screen )
329 #else
330 static void resize_backing_store( this_screen )
331 xvi_screen *this_screen;
332 #endif
334 int total_chars = this_screen->rows * this_screen->cols;
336 this_screen->characters = REALLOC( this_screen->characters,
337 total_chars
339 memset( this_screen->characters, ' ', total_chars );
341 this_screen->flags = REALLOC( this_screen->flags,
342 total_chars
344 memset( this_screen->flags, 0, total_chars );
349 /* X will call this when we are resized */
350 #if defined(__STDC__)
351 static void resize_func( Widget wid,
352 XtPointer client_data,
353 XtPointer call_data
355 #else
356 static void resize_func( wid, client_data, call_data )
357 Widget wid;
358 XtPointer client_data;
359 XtPointer call_data;
360 #endif
362 xvi_screen *this_screen = (xvi_screen *) client_data;
363 Dimension height, width;
365 XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 );
367 /* generate correct sizes when we have font metrics implemented */
368 this_screen->cols = width / this_screen->ch_width;
369 this_screen->rows = height / this_screen->ch_height;
371 resize_backing_store( this_screen );
372 send_resize( this_screen );
377 * __vi_draw_text --
378 * Draw from backing store.
380 * PUBLIC: void __vi_draw_text __P((xvi_screen *, int, int, int));
382 void
383 __vi_draw_text(this_screen, row, start_col, len)
384 xvi_screen *this_screen;
385 int row, start_col, len;
387 int col, color, xpos;
388 char *start, *end;
390 start = CharAt( __vi_screen, row, start_col );
391 color = *FlagAt( __vi_screen, row, start_col );
392 xpos = XPOS( __vi_screen, start_col );
394 /* one column at a time */
395 for ( col=start_col;
396 col<this_screen->cols && col<start_col+len;
397 col++ ) {
399 /* has the color changed? */
400 if ( *FlagAt( __vi_screen, row, col ) == color )
401 continue;
403 /* is there anything to write? */
404 end = CharAt( __vi_screen, row, col );
405 if ( end == start )
406 continue;
408 /* yes. write in the previous color */
409 set_gc_colors( __vi_screen, color );
411 /* add to display */
412 XDrawImageString( XtDisplay(__vi_screen->area),
413 XtWindow(__vi_screen->area),
415 xpos,
416 YPOS( __vi_screen, row ),
417 start,
418 end - start
421 /* this is the new context */
422 color = *FlagAt( __vi_screen, row, col );
423 xpos = XPOS( __vi_screen, col );
424 start = end;
427 /* is there anything to write? */
428 end = CharAt( __vi_screen, row, col );
429 if ( end != start ) {
430 /* yes. write in the previous color */
431 set_gc_colors( __vi_screen, color );
433 /* add to display */
434 XDrawImageString( XtDisplay(__vi_screen->area),
435 XtWindow(__vi_screen->area),
437 xpos,
438 YPOS( __vi_screen, row ),
439 start,
440 end - start
446 /* set clipping rectangles accordingly */
447 #if defined(__STDC__)
448 static void add_to_clip( xvi_screen *cur_screen, int x, int y, int width, int height )
449 #else
450 static void add_to_clip( cur_screen, x, y, width, height )
451 xvi_screen *cur_screen;
452 int x;
453 int y;
454 int width;
455 int height;
456 #endif
458 XRectangle rect;
459 rect.x = x;
460 rect.y = y;
461 rect.height = height;
462 rect.width = width;
463 if ( cur_screen->clip == NULL )
464 cur_screen->clip = XCreateRegion();
465 XUnionRectWithRegion( &rect, cur_screen->clip, cur_screen->clip );
470 * __vi_expose_func --
471 * Redraw the window's contents.
473 * NOTE: When vi wants to force a redraw, we are called with NULL widget
474 * and call_data.
476 * PUBLIC: void __vi_expose_func __P((Widget, XtPointer, XtPointer));
478 void
479 __vi_expose_func(wid, client_data, call_data)
480 Widget wid;
481 XtPointer client_data, call_data;
483 xvi_screen *this_screen;
484 XmDrawingAreaCallbackStruct *cbs;
485 XExposeEvent *xev;
486 XGraphicsExposeEvent *gev;
487 int row;
489 /* convert pointers */
490 this_screen = (xvi_screen *) client_data;
491 cbs = (XmDrawingAreaCallbackStruct *) call_data;
493 /* first exposure? tell vi we are ready... */
494 if ( this_screen->init == False ) {
496 /* what does the user want to see? */
497 __vi_set_cursor( __vi_screen, False );
499 /* vi wants a resize as the first event */
500 send_resize( __vi_screen );
502 /* fine for now. we'll be back */
503 this_screen->init = True;
504 return;
507 if ( call_data == NULL ) {
509 /* vi core calls this when it wants a full refresh */
510 #ifdef TRACE
511 trace("expose_func: full refresh\n");
512 #endif
514 XClearWindow( XtDisplay(this_screen->area),
515 XtWindow(this_screen->area)
518 else {
519 switch ( cbs->event->type ) {
521 case GraphicsExpose:
522 gev = (XGraphicsExposeEvent *) cbs->event;
524 /* set clipping rectangles accordingly */
525 add_to_clip( this_screen,
526 gev->x, gev->y,
527 gev->width, gev->height
530 /* X calls here when XCopyArea exposes new bits */
531 #ifdef TRACE
532 trace("expose_func (X): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
533 gev->x, gev->y,
534 gev->width, gev->height,
535 gev->count);
536 #endif
538 /* more coming? do it then */
539 if ( gev->count > 0 ) return;
541 /* set clipping region */
542 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
543 break;
545 case Expose:
546 xev = (XExposeEvent *) cbs->event;
548 /* set clipping rectangles accordingly */
549 add_to_clip( this_screen,
550 xev->x, xev->y,
551 xev->width, xev->height
554 /* Motif calls here when DrawingArea is exposed */
555 #ifdef TRACE
556 trace("expose_func (Motif): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
557 xev->x, xev->y,
558 xev->width, xev->height,
559 xev->count);
560 #endif
562 /* more coming? do it then */
563 if ( xev->count > 0 ) return;
565 /* set clipping region */
566 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
567 break;
569 default:
570 /* don't care? */
571 return;
575 /* one row at a time */
576 for (row=0; row<this_screen->rows; row++) {
578 /* draw from the backing store */
579 __vi_draw_text( this_screen, row, 0, this_screen->cols );
582 /* clear clipping region */
583 XSetClipMask( XtDisplay(this_screen->area), gc, None );
584 if ( this_screen->clip != NULL ) {
585 XDestroyRegion( this_screen->clip );
586 this_screen->clip = NULL;
592 #if defined(__STDC__)
593 static void xexpose ( Widget w,
594 XtPointer client_data,
595 XEvent *ev,
596 Boolean *cont
598 #else
599 static void xexpose ( w, client_data, ev, cont )
600 Widget w;
601 XtPointer client_data;
602 XEvent *ev;
603 Boolean *cont;
604 #endif
606 XmDrawingAreaCallbackStruct cbs;
608 switch ( ev->type ) {
609 case GraphicsExpose:
610 cbs.event = ev;
611 cbs.window = XtWindow(w);
612 cbs.reason = XmCR_EXPOSE;
613 __vi_expose_func( w, client_data, (XtPointer) &cbs );
614 *cont = False; /* we took care of it */
615 break;
616 default:
617 /* don't care */
618 break;
623 /* unimplemented keystroke or command */
624 #if defined(__STDC__)
625 static void beep( Widget w )
626 #else
627 static void beep( w )
628 Widget w;
629 #endif
631 XBell(XtDisplay(w),0);
635 /* give me a search dialog */
636 #if defined(__STDC__)
637 static void find( Widget w )
638 #else
639 static void find( w )
640 Widget w;
641 #endif
643 __vi_show_search_dialog( w, "Find" );
647 * command --
648 * Translate simple keyboard input into vi protocol commands.
650 static void
651 command(widget, event, str, cardinal)
652 Widget widget;
653 XKeyEvent *event;
654 String *str;
655 Cardinal *cardinal;
657 static struct {
658 String name;
659 int code;
660 int count;
661 } table[] = {
662 { "VI_C_BOL", VI_C_BOL, 0 },
663 { "VI_C_BOTTOM", VI_C_BOTTOM, 0 },
664 { "VI_C_DEL", VI_C_DEL, 0 },
665 { "VI_C_DOWN", VI_C_DOWN, 1 },
666 { "VI_C_EOL", VI_C_EOL, 0 },
667 { "VI_C_INSERT", VI_C_INSERT, 0 },
668 { "VI_C_LEFT", VI_C_LEFT, 0 },
669 { "VI_C_PGDOWN", VI_C_PGDOWN, 1 },
670 { "VI_C_PGUP", VI_C_PGUP, 1 },
671 { "VI_C_RIGHT", VI_C_RIGHT, 0 },
672 { "VI_C_TOP", VI_C_TOP, 0 },
673 { "VI_C_UP", VI_C_UP, 1 },
674 { "VI_INTERRUPT", VI_INTERRUPT, 0 },
676 IP_BUF ipb;
677 int i;
680 * XXX
681 * Do fast lookup based on character #6 -- sleazy, but I don't
682 * want to do 10 strcmp's per keystroke.
684 ipb.val1 = 1;
685 for (i = 0; i < XtNumber(table); i++)
686 if (table[i].name[6] == (*str)[6] &&
687 strcmp(table[i].name, *str) == 0) {
688 ipb.code = table[i].code;
689 __vi_send(table[i].count ? "1" : NULL, &ipb);
690 return;
693 /* oops. */
694 beep(widget);
697 /* mouse or keyboard input. */
698 #if defined(__STDC__)
699 static void insert_string( Widget widget,
700 XKeyEvent *event,
701 String *str,
702 Cardinal *cardinal
704 #else
705 static void insert_string( widget, event, str, cardinal )
706 Widget widget;
707 XKeyEvent *event;
708 String *str;
709 Cardinal *cardinal;
710 #endif
712 IP_BUF ipb;
714 ipb.len = strlen( *str );
715 if ( ipb.len != 0 ) {
716 ipb.code = VI_STRING;
717 ipb.str = *str;
718 __vi_send("s", &ipb);
721 #ifdef TRACE
722 trace("insert_string {%.*s}\n", strlen( *str ), *str );
723 #endif
727 /* mouse or keyboard input. */
728 #if defined(__STDC__)
729 static void key_press( Widget widget,
730 XKeyEvent *event,
731 String str,
732 Cardinal *cardinal
734 #else
735 static void key_press( widget, event, str, cardinal )
736 Widget widget;
737 XKeyEvent *event;
738 String str;
739 Cardinal *cardinal;
740 #endif
742 IP_BUF ipb;
743 char bp[BufferSize];
745 ipb.len = XLookupString( event, bp, BufferSize, NULL, NULL );
746 if ( ipb.len != 0 ) {
747 ipb.code = VI_STRING;
748 ipb.str = bp;
749 #ifdef TRACE
750 trace("key_press {%.*s}\n", ipb.len, bp );
751 #endif
752 __vi_send("s", &ipb);
758 #if defined(__STDC__)
759 static void scrollbar_moved( Widget widget,
760 XtPointer ptr,
761 XmScrollBarCallbackStruct *cbs
763 #else
764 static void scrollbar_moved( widget, ptr, cbs )
765 Widget widget;
766 XtPointer ptr;
767 XmScrollBarCallbackStruct *cbs;
768 #endif
770 /* Future: Need to scroll the correct screen! */
771 xvi_screen *cur_screen = (xvi_screen *) ptr;
772 IP_BUF ipb;
774 /* if we are still processing messages from core, skip this event
775 * (see comments near __vi_set_scroll_block())
777 if ( scroll_block ) {
778 #ifdef TRACE
779 trace( "punting scroll request with %d in buffer\n", len );
780 #endif
781 return;
783 __vi_set_scroll_block();
785 #ifdef TRACE
786 switch ( cbs->reason ) {
787 case XmCR_VALUE_CHANGED:
788 trace( "scrollbar VALUE_CHANGED %d\n", cbs->value );
789 break;
790 case XmCR_DRAG:
791 trace( "scrollbar DRAG %d\n", cbs->value );
792 break;
793 default:
794 trace( "scrollbar <default> %d\n", cbs->value );
795 break;
797 trace("scrollto {%d}\n", cbs->value );
798 #endif
800 /* Send the new cursor position. */
801 ipb.code = VI_C_SETTOP;
802 ipb.val1 = cbs->value;
803 (void)__vi_send("1", &ipb);
807 #if defined(__STDC__)
808 static xvi_screen *create_screen( Widget parent, int rows, int cols )
809 #else
810 static xvi_screen *create_screen( parent, rows, cols )
811 Widget parent;
812 int rows, cols;
813 #endif
815 xvi_screen *new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) );
816 Widget frame;
818 /* init... */
819 new_screen->color = COLOR_STANDARD;
820 new_screen->parent = parent;
822 /* figure out the sizes */
823 new_screen->rows = rows;
824 new_screen->cols = cols;
825 new_screen->ch_width = font->max_bounds.width;
826 new_screen->ch_height = font->descent + font->ascent;
827 new_screen->ch_descent = font->descent;
828 new_screen->clip = NULL;
830 /* allocate and init the backing stores */
831 resize_backing_store( new_screen );
833 /* set up a translation table for the X toolkit */
834 if ( area_trans == NULL )
835 area_trans = XtParseTranslationTable(areaTrans);
837 /* future, new screen gets inserted into the parent sash
838 * immediately after the current screen. Default Pane action is
839 * to add it to the end
842 /* use a form to hold the drawing area and the scrollbar */
843 new_screen->form = XtVaCreateManagedWidget( "form",
844 xmFormWidgetClass,
845 parent,
846 XmNpaneMinimum, 2*new_screen->ch_height,
847 XmNallowResize, True,
848 NULL
851 /* create a scrollbar. */
852 new_screen->scroll = XtVaCreateManagedWidget( "scroll",
853 xmScrollBarWidgetClass,
854 new_screen->form,
855 XmNtopAttachment, XmATTACH_FORM,
856 XmNbottomAttachment, XmATTACH_FORM,
857 XmNrightAttachment, XmATTACH_FORM,
858 XmNminimum, 1,
859 XmNmaximum, 2,
860 XmNsliderSize, 1,
861 NULL
863 XtAddCallback( new_screen->scroll,
864 XmNvalueChangedCallback,
865 scrollbar_moved,
866 new_screen
868 XtAddCallback( new_screen->scroll,
869 XmNdragCallback,
870 scrollbar_moved,
871 new_screen
874 /* create a frame because they look nice */
875 frame = XtVaCreateManagedWidget( "frame",
876 xmFrameWidgetClass,
877 new_screen->form,
878 XmNshadowType, XmSHADOW_ETCHED_IN,
879 XmNtopAttachment, XmATTACH_FORM,
880 XmNbottomAttachment, XmATTACH_FORM,
881 XmNleftAttachment, XmATTACH_FORM,
882 XmNrightAttachment, XmATTACH_WIDGET,
883 XmNrightWidget, new_screen->scroll,
884 NULL
887 /* create a drawing area into which we will put text */
888 new_screen->area = XtVaCreateManagedWidget( "screen",
889 xmDrawingAreaWidgetClass,
890 frame,
891 XmNheight, new_screen->ch_height * new_screen->rows,
892 XmNwidth, new_screen->ch_width * new_screen->cols,
893 XmNtranslations, area_trans,
894 XmNuserData, new_screen,
895 XmNnavigationType, XmNONE,
896 XmNtraversalOn, False,
897 NULL
900 /* this callback is for when the drawing area is resized */
901 XtAddCallback( new_screen->area,
902 XmNresizeCallback,
903 resize_func,
904 new_screen
907 /* this callback is for when the drawing area is exposed */
908 XtAddCallback( new_screen->area,
909 XmNexposeCallback,
910 __vi_expose_func,
911 new_screen
914 /* this callback is for when we expose obscured bits
915 * (e.g. there is a window over part of our drawing area
917 XtAddEventHandler( new_screen->area,
918 0, /* no standard events */
919 True, /* we *WANT* GraphicsExpose */
920 xexpose, /* what to do */
921 new_screen
924 return new_screen;
928 static xvi_screen *split_screen()
930 Cardinal num;
931 WidgetList c;
932 int rows = __vi_screen->rows / 2;
933 xvi_screen *new_screen;
935 /* Note that (global) cur_screen needs to be correctly set so that
936 * insert_here knows which screen to put the new one after
938 new_screen = create_screen( __vi_screen->parent,
939 rows,
940 __vi_screen->cols
943 /* what are the screens? */
944 XtVaGetValues( __vi_screen->parent,
945 XmNnumChildren, &num,
946 XmNchildren, &c,
947 NULL
950 /* unmanage all children in preparation for resizing */
951 XtUnmanageChildren( c, num );
953 /* force resize of the affected screens */
954 XtVaSetValues( new_screen->form,
955 XmNheight, new_screen->ch_height * rows,
956 NULL
958 XtVaSetValues( __vi_screen->form,
959 XmNheight, __vi_screen->ch_height * rows,
960 NULL
963 /* re-manage */
964 XtManageChildren( c, num );
966 /* done */
967 return new_screen;
971 /* Tell me where to insert the next subpane */
972 #if defined(__STDC__)
973 static Cardinal insert_here( Widget wid )
974 #else
975 static Cardinal insert_here( wid )
976 Widget wid;
977 #endif
979 Cardinal i, num;
980 WidgetList c;
982 XtVaGetValues( XtParent(wid),
983 XmNnumChildren, &num,
984 XmNchildren, &c,
985 NULL
988 /* The default XmNinsertPosition procedure for PanedWindow
989 * causes sashes to be inserted at the end of the list of children
990 * and causes non-sash widgets to be inserted after other
991 * non-sash children but before any sashes.
993 if ( ! XmIsForm( wid ) )
994 return num;
996 /* We will put the widget after the one with the current screen */
997 for (i=0; i<num && XmIsForm(c[i]); i++) {
998 if ( __vi_screen == NULL || __vi_screen->form == c[i] )
999 return i+1; /* after the i-th */
1002 /* could not find it? this should never happen */
1003 return num;
1008 * vi_create_editor --
1009 * Create the necessary widgetry.
1011 * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void)));
1013 Widget
1014 vi_create_editor(name, parent, exitp)
1015 String name;
1016 Widget parent;
1017 void (*exitp) __P((void));
1019 Widget pane_w;
1020 Display *display = XtDisplay( parent );
1022 __vi_exitp = exitp;
1024 /* first time through? */
1025 if ( ctx == NULL ) {
1027 /* save this for later */
1028 ctx = XtWidgetToApplicationContext( parent );
1030 /* add our own special actions */
1031 XtAppAddActions( ctx, area_actions, XtNumber(area_actions) );
1033 /* how long is double-click? */
1034 multi_click_length = XtGetMultiClickTime( display );
1036 /* check the resource database for interesting resources */
1037 XutConvertResources( parent,
1038 vi_progname,
1039 resource,
1040 XtNumber(resource)
1043 /* we need a context for moving bits around in the windows */
1044 __vi_copy_gc = XCreateGC( display,
1045 DefaultRootWindow(display),
1050 /* routines for inter client communications conventions */
1051 __vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf );
1054 /* create the paned window */
1055 pane_w = XtVaCreateManagedWidget( "pane",
1056 xmPanedWindowWidgetClass,
1057 parent,
1058 XmNinsertPosition, insert_here,
1059 NULL
1062 /* allocate our data structure. in the future we will have several
1063 * screens running around at the same time
1065 __vi_screen = create_screen( pane_w, 24, 80 );
1067 /* force creation of our color text context */
1068 set_gc_colors( __vi_screen, COLOR_STANDARD );
1070 /* done */
1071 return pane_w;
1075 /* These routines deal with the selection buffer */
1077 static int selection_start, selection_end, selection_anchor;
1078 static enum select_enum {
1079 select_char, select_word, select_line
1080 } select_type = select_char;
1081 static int last_click;
1083 static char *clipboard = NULL;
1084 static int clipboard_size = 0,
1085 clipboard_length;
1088 #if defined(__STDC__)
1089 static void copy_to_clipboard( xvi_screen *cur_screen )
1090 #else
1091 static void copy_to_clipboard( cur_screen )
1092 xvi_screen *cur_screen;
1093 #endif
1095 /* for now, copy from the backing store. in the future,
1096 * vi core will tell us exactly what the selection buffer contains
1098 clipboard_length = 1 + selection_end - selection_start;
1100 if ( clipboard == NULL )
1101 clipboard = (char *) malloc( clipboard_length );
1102 else if ( clipboard_size < clipboard_length )
1103 clipboard = (char *) realloc( clipboard, clipboard_length );
1105 memcpy( clipboard,
1106 cur_screen->characters + selection_start,
1107 clipboard_length
1112 #if defined(__STDC__)
1113 static void mark_selection( xvi_screen *cur_screen, int start, int end )
1114 #else
1115 static void mark_selection( cur_screen, start, end )
1116 xvi_screen *cur_screen;
1117 int start;
1118 int end;
1119 #endif
1121 int row, col, i;
1123 for ( i=start; i<=end; i++ ) {
1124 if ( !( cur_screen->flags[i] & COLOR_SELECT ) ) {
1125 cur_screen->flags[i] |= COLOR_SELECT;
1126 ToRowCol( cur_screen, i, row, col );
1127 __vi_draw_text( cur_screen, row, col, 1 );
1133 #if defined(__STDC__)
1134 static void erase_selection( xvi_screen *cur_screen, int start, int end )
1135 #else
1136 static void erase_selection( cur_screen, start, end )
1137 xvi_screen *cur_screen;
1138 int start;
1139 int end;
1140 #endif
1142 int row, col, i;
1144 for ( i=start; i<=end; i++ ) {
1145 if ( cur_screen->flags[i] & COLOR_SELECT ) {
1146 cur_screen->flags[i] &= ~COLOR_SELECT;
1147 ToRowCol( cur_screen, i, row, col );
1148 __vi_draw_text( cur_screen, row, col, 1 );
1154 #if defined(__STDC__)
1155 static void left_expand_selection( xvi_screen *cur_screen, int *start )
1156 #else
1157 static void left_expand_selection( cur_screen, start )
1158 xvi_screen *cur_screen;
1159 int *start;
1160 #endif
1162 int row, col;
1164 switch ( select_type ) {
1165 case select_word:
1166 if ( *start == 0 || isspace( cur_screen->characters[*start] ) )
1167 return;
1168 for (;;) {
1169 if ( isspace( cur_screen->characters[*start-1] ) )
1170 return;
1171 if ( --(*start) == 0 )
1172 return;
1174 case select_line:
1175 ToRowCol( cur_screen, *start, row, col );
1176 col = 0;
1177 *start = Linear( cur_screen, row, col );
1178 break;
1183 #if defined(__STDC__)
1184 static void right_expand_selection( xvi_screen *cur_screen, int *end )
1185 #else
1186 static void right_expand_selection( cur_screen, end )
1187 xvi_screen *cur_screen;
1188 int *end;
1189 #endif
1191 int row, col, last = cur_screen->cols * cur_screen->rows - 1;
1193 switch ( select_type ) {
1194 case select_word:
1195 if ( *end == last || isspace( cur_screen->characters[*end] ) )
1196 return;
1197 for (;;) {
1198 if ( isspace( cur_screen->characters[*end+1] ) )
1199 return;
1200 if ( ++(*end) == last )
1201 return;
1203 case select_line:
1204 ToRowCol( cur_screen, *end, row, col );
1205 col = cur_screen->cols -1;
1206 *end = Linear( cur_screen, row, col );
1207 break;
1212 #if defined(__STDC__)
1213 static void select_start( Widget widget,
1214 XEvent *event,
1215 String str,
1216 Cardinal *cardinal
1218 #else
1219 static void select_start( widget, event, str, cardinal )
1220 Widget widget;
1221 XEvent *event;
1222 String str;
1223 Cardinal *cardinal;
1224 #endif
1226 char buffer[BufferSize];
1227 IP_BUF ipb;
1228 int xpos, ypos;
1229 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1230 static int last_click;
1233 * NOTE: when multiple panes are implemented, we need to find the correct
1234 * screen. For now, there is only one.
1236 xpos = COLUMN( __vi_screen, ev->x );
1237 ypos = ROW( __vi_screen, ev->y );
1239 /* Remove the old one. */
1240 erase_selection( __vi_screen, selection_start, selection_end );
1242 /* Send the new cursor position. */
1243 ipb.code = VI_MOUSE_MOVE;
1244 ipb.val1 = ypos;
1245 ipb.val2 = xpos;
1246 (void)__vi_send("12", &ipb);
1248 /* click-click, and we go for words, lines, etc */
1249 if ( ev->time - last_click < multi_click_length )
1250 select_type = (enum select_enum) ((((int)select_type)+1)%3);
1251 else
1252 select_type = select_char;
1253 last_click = ev->time;
1255 /* put the selection here */
1256 selection_anchor = Linear( __vi_screen, ypos, xpos );
1257 selection_start = selection_anchor;
1258 selection_end = selection_anchor;
1260 /* expand to include words, line, etc */
1261 left_expand_selection( __vi_screen, &selection_start );
1262 right_expand_selection( __vi_screen, &selection_end );
1264 /* draw the new one */
1265 mark_selection( __vi_screen, selection_start, selection_end );
1267 /* and tell the window manager we own the selection */
1268 if ( select_type != select_char ) {
1269 __vi_AcquirePrimary( widget );
1270 copy_to_clipboard( __vi_screen );
1275 #if defined(__STDC__)
1276 static void select_extend( Widget widget,
1277 XEvent *event,
1278 String str,
1279 Cardinal *cardinal
1281 #else
1282 static void select_extend( widget, event, str, cardinal )
1283 Widget widget;
1284 XEvent *event;
1285 String str;
1286 Cardinal *cardinal;
1287 #endif
1289 int xpos, ypos, pos;
1290 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1292 /* NOTE: when multiple panes are implemented, we need to find
1293 * the correct screen. For now, there is only one.
1295 xpos = COLUMN( __vi_screen, ev->x );
1296 ypos = ROW( __vi_screen, ev->y );
1298 /* deal with words, lines, etc */
1299 pos = Linear( __vi_screen, ypos, xpos );
1300 if ( pos < selection_anchor )
1301 left_expand_selection( __vi_screen, &pos );
1302 else
1303 right_expand_selection( __vi_screen, &pos );
1305 /* extend from before the start? */
1306 if ( pos < selection_start ) {
1307 mark_selection( __vi_screen, pos, selection_start-1 );
1308 selection_start = pos;
1311 /* extend past the end? */
1312 else if ( pos > selection_end ) {
1313 mark_selection( __vi_screen, selection_end+1, pos );
1314 selection_end = pos;
1317 /* between the anchor and the start? */
1318 else if ( pos < selection_anchor ) {
1319 erase_selection( __vi_screen, selection_start, pos-1 );
1320 selection_start = pos;
1323 /* between the anchor and the end? */
1324 else {
1325 erase_selection( __vi_screen, pos+1, selection_end );
1326 selection_end = pos;
1329 /* and tell the window manager we own the selection */
1330 __vi_AcquirePrimary( widget );
1331 copy_to_clipboard( __vi_screen );
1335 #if defined(__STDC__)
1336 static void select_paste( Widget widget,
1337 XEvent *event,
1338 String str,
1339 Cardinal *cardinal
1341 #else
1342 static void select_paste( widget, event, str, cardinal )
1343 Widget widget;
1344 XEvent *event;
1345 String str;
1346 Cardinal *cardinal;
1347 #endif
1349 __vi_PasteFromClipboard( widget );
1353 /* Interface to copy and paste
1354 * (a) callbacks from the window manager
1355 * f_copy - it wants our buffer
1356 * f_paste - it wants us to paste some text
1357 * f_clear - we've lost the selection, clear it
1360 #if defined(__STDC__)
1361 static void f_copy( String *buffer, int *len )
1362 #else
1363 static void f_copy( buffer, len )
1364 String *buffer;
1365 int *len;
1366 #endif
1368 #ifdef TRACE
1369 trace("f_copy() called");
1370 #endif
1371 *buffer = clipboard;
1372 *len = clipboard_length;
1377 static void f_paste( widget, buffer, length )
1378 int widget, buffer, length;
1380 /* NOTE: when multiple panes are implemented, we need to find
1381 * the correct screen. For now, there is only one.
1383 #ifdef TRACE
1384 trace("f_paste() called with '%*.*s'\n", length, length, buffer);
1385 #endif
1389 #if defined(__STDC__)
1390 static void f_clear( Widget widget )
1391 #else
1392 static void f_clear( widget )
1393 Widget widget;
1394 #endif
1396 xvi_screen *cur_screen;
1398 #ifdef TRACE
1399 trace("f_clear() called");
1400 #endif
1402 XtVaGetValues( widget, XmNuserData, &cur_screen, 0 );
1404 erase_selection( cur_screen, selection_start, selection_end );
1409 * These routines deal with the cursor.
1411 * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int));
1413 void
1414 __vi_set_cursor(cur_screen, is_busy)
1415 xvi_screen *cur_screen;
1416 int is_busy;
1418 XDefineCursor( XtDisplay(cur_screen->area),
1419 XtWindow(cur_screen->area),
1420 (is_busy) ? busy_cursor : std_cursor
1426 /* hooks for the tags widget */
1428 static String cur_word = NULL;
1430 void
1431 __vi_set_word_at_caret( this_screen )
1432 xvi_screen *this_screen;
1434 char *start, *end, save;
1435 int newx, newy;
1437 newx = this_screen->curx;
1438 newy = this_screen->cury;
1440 /* Note that this really ought to be done by core due to wrapping issues */
1441 for ( end = start = CharAt( this_screen, newy, newx );
1442 (isalnum( *end ) || *end == '_') && (newx < this_screen->cols);
1443 end++, newx++
1445 save = *end;
1446 *end = '\0';
1447 if ( cur_word != NULL ) free( cur_word );
1448 cur_word = strdup( start );
1449 *end = save;
1451 /* if the tag stack widget is active, set the text field there
1452 * to agree with the current caret position.
1454 __vi_set_tag_text( cur_word );
1458 String __vi_get_word_at_caret( this_screen )
1459 xvi_screen *this_screen;
1461 return (cur_word) ? cur_word : "";
1466 * These routines deal with the caret.
1468 * PUBLIC: void draw_caret __P((xvi_screen *));
1470 static void
1471 draw_caret(this_screen)
1472 xvi_screen *this_screen;
1474 /* draw the caret by drawing the text in highlight color */
1475 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) |= COLOR_CARET;
1476 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1480 * PUBLIC: void __vi_erase_caret __P((xvi_screen *));
1482 void
1483 __vi_erase_caret(this_screen)
1484 xvi_screen *this_screen;
1486 /* erase the caret by drawing the text in normal video */
1487 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) &= ~COLOR_CARET;
1488 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1492 * PUBLIC: void __vi_move_caret __P((xvi_screen *, int, int));
1494 void
1495 __vi_move_caret(this_screen, newy, newx)
1496 xvi_screen *this_screen;
1497 int newy, newx;
1499 /* remove the old caret */
1500 __vi_erase_caret( this_screen );
1502 /* caret is now here */
1503 this_screen->curx = newx;
1504 this_screen->cury = newy;
1505 draw_caret( this_screen );