*** empty log message ***
[nvi.git] / motif_l / m_vi.c
blob63cf05b732cfbcdfaee9050cd1074c90c49f1c86
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.36 1997/08/03 15:02:51 bostic Exp $ (Berkeley) $Date: 1997/08/03 15:02:51 $";
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. */
69 /* hack for drag scrolling...
70 * I'm not sure why, but the current protocol gets out of sync when
71 * a lot of drag messages get passed around. Likely, we need to wait
72 * for core to finish repainting the screen before sending more drag
73 * messages.
74 * To that end, we set scroll_block when we receive input from the scrollbar,
75 * and we clear it when we process the IPO_REFRESH message from core.
76 * A specific SCROLL_COMPLETED message would be better, but this seems to work.
79 static Boolean scroll_block = False;
82 * PUBLIC: void __vi_set_scroll_block __P((void));
84 void
85 __vi_set_scroll_block()
87 scroll_block = True;
91 * PUBLIC: void __vi_clear_scroll_block __P((void));
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 * vi_input_func --
239 * We've received input on the pipe from vi.
241 * PUBLIC: void vi_input_func __P((XtPointer, int *, XtInputId *));
243 void
244 vi_input_func(client_data, source, id)
245 XtPointer client_data;
246 int *source;
247 XtInputId *id;
249 /* Parse and dispatch on commands in the queue. */
250 (void)vi_input(*source);
252 #ifdef notdef
253 /* Check the pipe for unused events when not busy. */
254 XtAppAddWorkProc(ctx, process_pipe_input, NULL);
255 #endif
260 /* Send the window size. */
261 #if defined(__STDC__)
262 static void send_resize( xvi_screen *this_screen )
263 #else
264 static void send_resize( this_screen )
265 xvi_screen *this_screen;
266 #endif
268 IP_BUF ipb;
270 ipb.val1 = this_screen->rows;
271 ipb.val2 = this_screen->cols;
272 ipb.code = VI_RESIZE;
274 #ifdef TRACE
275 vtrace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols);
276 #endif
278 /* send up the pipe */
279 vi_send("12", &ipb);
283 #if defined(__STDC__)
284 static void resize_backing_store( xvi_screen *this_screen )
285 #else
286 static void resize_backing_store( this_screen )
287 xvi_screen *this_screen;
288 #endif
290 int total_chars = this_screen->rows * this_screen->cols;
292 this_screen->characters = REALLOC( this_screen->characters,
293 total_chars
295 memset( this_screen->characters, ' ', total_chars );
297 this_screen->flags = REALLOC( this_screen->flags,
298 total_chars
300 memset( this_screen->flags, 0, total_chars );
305 /* X will call this when we are resized */
306 #if defined(__STDC__)
307 static void resize_func( Widget wid,
308 XtPointer client_data,
309 XtPointer call_data
311 #else
312 static void resize_func( wid, client_data, call_data )
313 Widget wid;
314 XtPointer client_data;
315 XtPointer call_data;
316 #endif
318 xvi_screen *this_screen = (xvi_screen *) client_data;
319 Dimension height, width;
321 XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 );
323 /* generate correct sizes when we have font metrics implemented */
324 this_screen->cols = width / this_screen->ch_width;
325 this_screen->rows = height / this_screen->ch_height;
327 resize_backing_store( this_screen );
328 send_resize( this_screen );
333 * __vi_draw_text --
334 * Draw from backing store.
336 * PUBLIC: void __vi_draw_text __P((xvi_screen *, int, int, int));
338 void
339 __vi_draw_text(this_screen, row, start_col, len)
340 xvi_screen *this_screen;
341 int row, start_col, len;
343 int col, color, xpos;
344 char *start, *end;
346 start = CharAt( __vi_screen, row, start_col );
347 color = *FlagAt( __vi_screen, row, start_col );
348 xpos = XPOS( __vi_screen, start_col );
350 /* one column at a time */
351 for ( col=start_col;
352 col<this_screen->cols && col<start_col+len;
353 col++ ) {
355 /* has the color changed? */
356 if ( *FlagAt( __vi_screen, row, col ) == color )
357 continue;
359 /* is there anything to write? */
360 end = CharAt( __vi_screen, row, col );
361 if ( end == start )
362 continue;
364 /* yes. write in the previous color */
365 set_gc_colors( __vi_screen, color );
367 /* add to display */
368 XDrawImageString( XtDisplay(__vi_screen->area),
369 XtWindow(__vi_screen->area),
371 xpos,
372 YPOS( __vi_screen, row ),
373 start,
374 end - start
377 /* this is the new context */
378 color = *FlagAt( __vi_screen, row, col );
379 xpos = XPOS( __vi_screen, col );
380 start = end;
383 /* is there anything to write? */
384 end = CharAt( __vi_screen, row, col );
385 if ( end != start ) {
386 /* yes. write in the previous color */
387 set_gc_colors( __vi_screen, color );
389 /* add to display */
390 XDrawImageString( XtDisplay(__vi_screen->area),
391 XtWindow(__vi_screen->area),
393 xpos,
394 YPOS( __vi_screen, row ),
395 start,
396 end - start
402 /* set clipping rectangles accordingly */
403 #if defined(__STDC__)
404 static void add_to_clip( xvi_screen *cur_screen, int x, int y, int width, int height )
405 #else
406 static void add_to_clip( cur_screen, x, y, width, height )
407 xvi_screen *cur_screen;
408 int x;
409 int y;
410 int width;
411 int height;
412 #endif
414 XRectangle rect;
415 rect.x = x;
416 rect.y = y;
417 rect.height = height;
418 rect.width = width;
419 if ( cur_screen->clip == NULL )
420 cur_screen->clip = XCreateRegion();
421 XUnionRectWithRegion( &rect, cur_screen->clip, cur_screen->clip );
426 * __vi_expose_func --
427 * Redraw the window's contents.
429 * NOTE: When vi wants to force a redraw, we are called with NULL widget
430 * and call_data.
432 * PUBLIC: void __vi_expose_func __P((Widget, XtPointer, XtPointer));
434 void
435 __vi_expose_func(wid, client_data, call_data)
436 Widget wid;
437 XtPointer client_data, call_data;
439 xvi_screen *this_screen;
440 XmDrawingAreaCallbackStruct *cbs;
441 XExposeEvent *xev;
442 XGraphicsExposeEvent *gev;
443 int row;
445 /* convert pointers */
446 this_screen = (xvi_screen *) client_data;
447 cbs = (XmDrawingAreaCallbackStruct *) call_data;
449 /* first exposure? tell vi we are ready... */
450 if ( this_screen->init == False ) {
452 /* what does the user want to see? */
453 __vi_set_cursor( __vi_screen, False );
455 /* vi wants a resize as the first event */
456 send_resize( __vi_screen );
458 /* fine for now. we'll be back */
459 this_screen->init = True;
460 return;
463 if ( call_data == NULL ) {
465 /* vi core calls this when it wants a full refresh */
466 #ifdef TRACE
467 vtrace("expose_func: full refresh\n");
468 #endif
470 XClearWindow( XtDisplay(this_screen->area),
471 XtWindow(this_screen->area)
474 else {
475 switch ( cbs->event->type ) {
477 case GraphicsExpose:
478 gev = (XGraphicsExposeEvent *) cbs->event;
480 /* set clipping rectangles accordingly */
481 add_to_clip( this_screen,
482 gev->x, gev->y,
483 gev->width, gev->height
486 /* X calls here when XCopyArea exposes new bits */
487 #ifdef TRACE
488 vtrace("expose_func (X): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
489 gev->x, gev->y,
490 gev->width, gev->height,
491 gev->count);
492 #endif
494 /* more coming? do it then */
495 if ( gev->count > 0 ) return;
497 /* set clipping region */
498 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
499 break;
501 case Expose:
502 xev = (XExposeEvent *) cbs->event;
504 /* set clipping rectangles accordingly */
505 add_to_clip( this_screen,
506 xev->x, xev->y,
507 xev->width, xev->height
510 /* Motif calls here when DrawingArea is exposed */
511 #ifdef TRACE
512 vtrace("expose_func (Motif): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
513 xev->x, xev->y,
514 xev->width, xev->height,
515 xev->count);
516 #endif
518 /* more coming? do it then */
519 if ( xev->count > 0 ) return;
521 /* set clipping region */
522 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
523 break;
525 default:
526 /* don't care? */
527 return;
531 /* one row at a time */
532 for (row=0; row<this_screen->rows; row++) {
534 /* draw from the backing store */
535 __vi_draw_text( this_screen, row, 0, this_screen->cols );
538 /* clear clipping region */
539 XSetClipMask( XtDisplay(this_screen->area), gc, None );
540 if ( this_screen->clip != NULL ) {
541 XDestroyRegion( this_screen->clip );
542 this_screen->clip = NULL;
548 #if defined(__STDC__)
549 static void xexpose ( Widget w,
550 XtPointer client_data,
551 XEvent *ev,
552 Boolean *cont
554 #else
555 static void xexpose ( w, client_data, ev, cont )
556 Widget w;
557 XtPointer client_data;
558 XEvent *ev;
559 Boolean *cont;
560 #endif
562 XmDrawingAreaCallbackStruct cbs;
564 switch ( ev->type ) {
565 case GraphicsExpose:
566 cbs.event = ev;
567 cbs.window = XtWindow(w);
568 cbs.reason = XmCR_EXPOSE;
569 __vi_expose_func( w, client_data, (XtPointer) &cbs );
570 *cont = False; /* we took care of it */
571 break;
572 default:
573 /* don't care */
574 break;
579 /* unimplemented keystroke or command */
580 #if defined(__STDC__)
581 static void beep( Widget w )
582 #else
583 static void beep( w )
584 Widget w;
585 #endif
587 XBell(XtDisplay(w),0);
591 /* give me a search dialog */
592 #if defined(__STDC__)
593 static void find( Widget w )
594 #else
595 static void find( w )
596 Widget w;
597 #endif
599 __vi_show_search_dialog( w, "Find" );
603 * command --
604 * Translate simple keyboard input into vi protocol commands.
606 static void
607 command(widget, event, str, cardinal)
608 Widget widget;
609 XKeyEvent *event;
610 String *str;
611 Cardinal *cardinal;
613 static struct {
614 String name;
615 int code;
616 int count;
617 } table[] = {
618 { "VI_C_BOL", VI_C_BOL, 0 },
619 { "VI_C_BOTTOM", VI_C_BOTTOM, 0 },
620 { "VI_C_DEL", VI_C_DEL, 0 },
621 { "VI_C_DOWN", VI_C_DOWN, 1 },
622 { "VI_C_EOL", VI_C_EOL, 0 },
623 { "VI_C_INSERT", VI_C_INSERT, 0 },
624 { "VI_C_LEFT", VI_C_LEFT, 0 },
625 { "VI_C_PGDOWN", VI_C_PGDOWN, 1 },
626 { "VI_C_PGUP", VI_C_PGUP, 1 },
627 { "VI_C_RIGHT", VI_C_RIGHT, 0 },
628 { "VI_C_TOP", VI_C_TOP, 0 },
629 { "VI_C_UP", VI_C_UP, 1 },
630 { "VI_INTERRUPT", VI_INTERRUPT, 0 },
632 IP_BUF ipb;
633 int i;
636 * XXX
637 * Do fast lookup based on character #6 -- sleazy, but I don't
638 * want to do 10 strcmp's per keystroke.
640 ipb.val1 = 1;
641 for (i = 0; i < XtNumber(table); i++)
642 if (table[i].name[6] == (*str)[6] &&
643 strcmp(table[i].name, *str) == 0) {
644 ipb.code = table[i].code;
645 vi_send(table[i].count ? "1" : NULL, &ipb);
646 return;
649 /* oops. */
650 beep(widget);
653 /* mouse or keyboard input. */
654 #if defined(__STDC__)
655 static void insert_string( Widget widget,
656 XKeyEvent *event,
657 String *str,
658 Cardinal *cardinal
660 #else
661 static void insert_string( widget, event, str, cardinal )
662 Widget widget;
663 XKeyEvent *event;
664 String *str;
665 Cardinal *cardinal;
666 #endif
668 IP_BUF ipb;
670 ipb.len1 = strlen( *str );
671 if ( ipb.len1 != 0 ) {
672 ipb.code = VI_STRING;
673 ipb.str1 = *str;
674 vi_send("a", &ipb);
677 #ifdef TRACE
678 vtrace("insert_string {%.*s}\n", strlen( *str ), *str );
679 #endif
683 /* mouse or keyboard input. */
684 #if defined(__STDC__)
685 static void key_press( Widget widget,
686 XKeyEvent *event,
687 String str,
688 Cardinal *cardinal
690 #else
691 static void key_press( widget, event, str, cardinal )
692 Widget widget;
693 XKeyEvent *event;
694 String str;
695 Cardinal *cardinal;
696 #endif
698 IP_BUF ipb;
699 char bp[BufferSize];
701 ipb.len1 = XLookupString( event, bp, BufferSize, NULL, NULL );
702 if ( ipb.len1 != 0 ) {
703 ipb.code = VI_STRING;
704 ipb.str1 = bp;
705 #ifdef TRACE
706 vtrace("key_press {%.*s}\n", ipb.len1, bp );
707 #endif
708 vi_send("a", &ipb);
714 #if defined(__STDC__)
715 static void scrollbar_moved( Widget widget,
716 XtPointer ptr,
717 XmScrollBarCallbackStruct *cbs
719 #else
720 static void scrollbar_moved( widget, ptr, cbs )
721 Widget widget;
722 XtPointer ptr;
723 XmScrollBarCallbackStruct *cbs;
724 #endif
726 /* Future: Need to scroll the correct screen! */
727 xvi_screen *cur_screen = (xvi_screen *) ptr;
728 IP_BUF ipb;
730 /* if we are still processing messages from core, skip this event
731 * (see comments near __vi_set_scroll_block())
733 if ( scroll_block ) {
734 return;
736 __vi_set_scroll_block();
738 #ifdef TRACE
739 switch ( cbs->reason ) {
740 case XmCR_VALUE_CHANGED:
741 vtrace( "scrollbar VALUE_CHANGED %d\n", cbs->value );
742 break;
743 case XmCR_DRAG:
744 vtrace( "scrollbar DRAG %d\n", cbs->value );
745 break;
746 default:
747 vtrace( "scrollbar <default> %d\n", cbs->value );
748 break;
750 vtrace("scrollto {%d}\n", cbs->value );
751 #endif
753 /* Send the new cursor position. */
754 ipb.code = VI_C_SETTOP;
755 ipb.val1 = cbs->value;
756 (void)vi_send("1", &ipb);
760 #if defined(__STDC__)
761 static xvi_screen *create_screen( Widget parent, int rows, int cols )
762 #else
763 static xvi_screen *create_screen( parent, rows, cols )
764 Widget parent;
765 int rows, cols;
766 #endif
768 xvi_screen *new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) );
769 Widget frame;
771 /* init... */
772 new_screen->color = COLOR_STANDARD;
773 new_screen->parent = parent;
775 /* figure out the sizes */
776 new_screen->rows = rows;
777 new_screen->cols = cols;
778 new_screen->ch_width = font->max_bounds.width;
779 new_screen->ch_height = font->descent + font->ascent;
780 new_screen->ch_descent = font->descent;
781 new_screen->clip = NULL;
783 /* allocate and init the backing stores */
784 resize_backing_store( new_screen );
786 /* set up a translation table for the X toolkit */
787 if ( area_trans == NULL )
788 area_trans = XtParseTranslationTable(areaTrans);
790 /* future, new screen gets inserted into the parent sash
791 * immediately after the current screen. Default Pane action is
792 * to add it to the end
795 /* use a form to hold the drawing area and the scrollbar */
796 new_screen->form = XtVaCreateManagedWidget( "form",
797 xmFormWidgetClass,
798 parent,
799 XmNpaneMinimum, 2*new_screen->ch_height,
800 XmNallowResize, True,
801 NULL
804 /* create a scrollbar. */
805 new_screen->scroll = XtVaCreateManagedWidget( "scroll",
806 xmScrollBarWidgetClass,
807 new_screen->form,
808 XmNtopAttachment, XmATTACH_FORM,
809 XmNbottomAttachment, XmATTACH_FORM,
810 XmNrightAttachment, XmATTACH_FORM,
811 XmNminimum, 1,
812 XmNmaximum, 2,
813 XmNsliderSize, 1,
814 NULL
816 XtAddCallback( new_screen->scroll,
817 XmNvalueChangedCallback,
818 scrollbar_moved,
819 new_screen
821 XtAddCallback( new_screen->scroll,
822 XmNdragCallback,
823 scrollbar_moved,
824 new_screen
827 /* create a frame because they look nice */
828 frame = XtVaCreateManagedWidget( "frame",
829 xmFrameWidgetClass,
830 new_screen->form,
831 XmNshadowType, XmSHADOW_ETCHED_IN,
832 XmNtopAttachment, XmATTACH_FORM,
833 XmNbottomAttachment, XmATTACH_FORM,
834 XmNleftAttachment, XmATTACH_FORM,
835 XmNrightAttachment, XmATTACH_WIDGET,
836 XmNrightWidget, new_screen->scroll,
837 NULL
840 /* create a drawing area into which we will put text */
841 new_screen->area = XtVaCreateManagedWidget( "screen",
842 xmDrawingAreaWidgetClass,
843 frame,
844 XmNheight, new_screen->ch_height * new_screen->rows,
845 XmNwidth, new_screen->ch_width * new_screen->cols,
846 XmNtranslations, area_trans,
847 XmNuserData, new_screen,
848 XmNnavigationType, XmNONE,
849 XmNtraversalOn, False,
850 NULL
853 /* this callback is for when the drawing area is resized */
854 XtAddCallback( new_screen->area,
855 XmNresizeCallback,
856 resize_func,
857 new_screen
860 /* this callback is for when the drawing area is exposed */
861 XtAddCallback( new_screen->area,
862 XmNexposeCallback,
863 __vi_expose_func,
864 new_screen
867 /* this callback is for when we expose obscured bits
868 * (e.g. there is a window over part of our drawing area
870 XtAddEventHandler( new_screen->area,
871 0, /* no standard events */
872 True, /* we *WANT* GraphicsExpose */
873 xexpose, /* what to do */
874 new_screen
877 return new_screen;
881 static xvi_screen *split_screen()
883 Cardinal num;
884 WidgetList c;
885 int rows = __vi_screen->rows / 2;
886 xvi_screen *new_screen;
888 /* Note that (global) cur_screen needs to be correctly set so that
889 * insert_here knows which screen to put the new one after
891 new_screen = create_screen( __vi_screen->parent,
892 rows,
893 __vi_screen->cols
896 /* what are the screens? */
897 XtVaGetValues( __vi_screen->parent,
898 XmNnumChildren, &num,
899 XmNchildren, &c,
900 NULL
903 /* unmanage all children in preparation for resizing */
904 XtUnmanageChildren( c, num );
906 /* force resize of the affected screens */
907 XtVaSetValues( new_screen->form,
908 XmNheight, new_screen->ch_height * rows,
909 NULL
911 XtVaSetValues( __vi_screen->form,
912 XmNheight, __vi_screen->ch_height * rows,
913 NULL
916 /* re-manage */
917 XtManageChildren( c, num );
919 /* done */
920 return new_screen;
924 /* Tell me where to insert the next subpane */
925 #if defined(__STDC__)
926 static Cardinal insert_here( Widget wid )
927 #else
928 static Cardinal insert_here( wid )
929 Widget wid;
930 #endif
932 Cardinal i, num;
933 WidgetList c;
935 XtVaGetValues( XtParent(wid),
936 XmNnumChildren, &num,
937 XmNchildren, &c,
938 NULL
941 /* The default XmNinsertPosition procedure for PanedWindow
942 * causes sashes to be inserted at the end of the list of children
943 * and causes non-sash widgets to be inserted after other
944 * non-sash children but before any sashes.
946 if ( ! XmIsForm( wid ) )
947 return num;
949 /* We will put the widget after the one with the current screen */
950 for (i=0; i<num && XmIsForm(c[i]); i++) {
951 if ( __vi_screen == NULL || __vi_screen->form == c[i] )
952 return i+1; /* after the i-th */
955 /* could not find it? this should never happen */
956 return num;
961 * vi_create_editor --
962 * Create the necessary widgetry.
964 * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void)));
966 Widget
967 vi_create_editor(name, parent, exitp)
968 String name;
969 Widget parent;
970 void (*exitp) __P((void));
972 Widget pane_w;
973 Display *display = XtDisplay( parent );
975 __vi_exitp = exitp;
977 /* first time through? */
978 if ( ctx == NULL ) {
980 /* save this for later */
981 ctx = XtWidgetToApplicationContext( parent );
983 /* add our own special actions */
984 XtAppAddActions( ctx, area_actions, XtNumber(area_actions) );
986 /* how long is double-click? */
987 multi_click_length = XtGetMultiClickTime( display );
989 /* check the resource database for interesting resources */
990 __XutConvertResources( parent,
991 vi_progname,
992 resource,
993 XtNumber(resource)
996 /* we need a context for moving bits around in the windows */
997 __vi_copy_gc = XCreateGC( display,
998 DefaultRootWindow(display),
1003 /* routines for inter client communications conventions */
1004 __vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf );
1007 /* create the paned window */
1008 pane_w = XtVaCreateManagedWidget( "pane",
1009 xmPanedWindowWidgetClass,
1010 parent,
1011 XmNinsertPosition, insert_here,
1012 NULL
1015 /* allocate our data structure. in the future we will have several
1016 * screens running around at the same time
1018 __vi_screen = create_screen( pane_w, 24, 80 );
1020 /* force creation of our color text context */
1021 set_gc_colors( __vi_screen, COLOR_STANDARD );
1023 /* done */
1024 return pane_w;
1028 /* These routines deal with the selection buffer */
1030 static int selection_start, selection_end, selection_anchor;
1031 static enum select_enum {
1032 select_char, select_word, select_line
1033 } select_type = select_char;
1034 static int last_click;
1036 static char *clipboard = NULL;
1037 static int clipboard_size = 0,
1038 clipboard_length;
1041 #if defined(__STDC__)
1042 static void copy_to_clipboard( xvi_screen *cur_screen )
1043 #else
1044 static void copy_to_clipboard( cur_screen )
1045 xvi_screen *cur_screen;
1046 #endif
1048 /* for now, copy from the backing store. in the future,
1049 * vi core will tell us exactly what the selection buffer contains
1051 clipboard_length = 1 + selection_end - selection_start;
1053 if ( clipboard == NULL )
1054 clipboard = (char *) malloc( clipboard_length );
1055 else if ( clipboard_size < clipboard_length )
1056 clipboard = (char *) realloc( clipboard, clipboard_length );
1058 memcpy( clipboard,
1059 cur_screen->characters + selection_start,
1060 clipboard_length
1065 #if defined(__STDC__)
1066 static void mark_selection( xvi_screen *cur_screen, int start, int end )
1067 #else
1068 static void mark_selection( cur_screen, start, end )
1069 xvi_screen *cur_screen;
1070 int start;
1071 int end;
1072 #endif
1074 int row, col, i;
1076 for ( i=start; i<=end; i++ ) {
1077 if ( !( cur_screen->flags[i] & COLOR_SELECT ) ) {
1078 cur_screen->flags[i] |= COLOR_SELECT;
1079 ToRowCol( cur_screen, i, row, col );
1080 __vi_draw_text( cur_screen, row, col, 1 );
1086 #if defined(__STDC__)
1087 static void erase_selection( xvi_screen *cur_screen, int start, int end )
1088 #else
1089 static void erase_selection( cur_screen, start, end )
1090 xvi_screen *cur_screen;
1091 int start;
1092 int end;
1093 #endif
1095 int row, col, i;
1097 for ( i=start; i<=end; i++ ) {
1098 if ( cur_screen->flags[i] & COLOR_SELECT ) {
1099 cur_screen->flags[i] &= ~COLOR_SELECT;
1100 ToRowCol( cur_screen, i, row, col );
1101 __vi_draw_text( cur_screen, row, col, 1 );
1107 #if defined(__STDC__)
1108 static void left_expand_selection( xvi_screen *cur_screen, int *start )
1109 #else
1110 static void left_expand_selection( cur_screen, start )
1111 xvi_screen *cur_screen;
1112 int *start;
1113 #endif
1115 int row, col;
1117 switch ( select_type ) {
1118 case select_word:
1119 if ( *start == 0 || isspace( cur_screen->characters[*start] ) )
1120 return;
1121 for (;;) {
1122 if ( isspace( cur_screen->characters[*start-1] ) )
1123 return;
1124 if ( --(*start) == 0 )
1125 return;
1127 case select_line:
1128 ToRowCol( cur_screen, *start, row, col );
1129 col = 0;
1130 *start = Linear( cur_screen, row, col );
1131 break;
1136 #if defined(__STDC__)
1137 static void right_expand_selection( xvi_screen *cur_screen, int *end )
1138 #else
1139 static void right_expand_selection( cur_screen, end )
1140 xvi_screen *cur_screen;
1141 int *end;
1142 #endif
1144 int row, col, last = cur_screen->cols * cur_screen->rows - 1;
1146 switch ( select_type ) {
1147 case select_word:
1148 if ( *end == last || isspace( cur_screen->characters[*end] ) )
1149 return;
1150 for (;;) {
1151 if ( isspace( cur_screen->characters[*end+1] ) )
1152 return;
1153 if ( ++(*end) == last )
1154 return;
1156 case select_line:
1157 ToRowCol( cur_screen, *end, row, col );
1158 col = cur_screen->cols -1;
1159 *end = Linear( cur_screen, row, col );
1160 break;
1165 #if defined(__STDC__)
1166 static void select_start( Widget widget,
1167 XEvent *event,
1168 String str,
1169 Cardinal *cardinal
1171 #else
1172 static void select_start( widget, event, str, cardinal )
1173 Widget widget;
1174 XEvent *event;
1175 String str;
1176 Cardinal *cardinal;
1177 #endif
1179 IP_BUF ipb;
1180 int xpos, ypos;
1181 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1182 static int last_click;
1185 * NOTE: when multiple panes are implemented, we need to find the correct
1186 * screen. For now, there is only one.
1188 xpos = COLUMN( __vi_screen, ev->x );
1189 ypos = ROW( __vi_screen, ev->y );
1191 /* Remove the old one. */
1192 erase_selection( __vi_screen, selection_start, selection_end );
1194 /* Send the new cursor position. */
1195 ipb.code = VI_MOUSE_MOVE;
1196 ipb.val1 = ypos;
1197 ipb.val2 = xpos;
1198 (void)vi_send("12", &ipb);
1200 /* click-click, and we go for words, lines, etc */
1201 if ( ev->time - last_click < multi_click_length )
1202 select_type = (enum select_enum) ((((int)select_type)+1)%3);
1203 else
1204 select_type = select_char;
1205 last_click = ev->time;
1207 /* put the selection here */
1208 selection_anchor = Linear( __vi_screen, ypos, xpos );
1209 selection_start = selection_anchor;
1210 selection_end = selection_anchor;
1212 /* expand to include words, line, etc */
1213 left_expand_selection( __vi_screen, &selection_start );
1214 right_expand_selection( __vi_screen, &selection_end );
1216 /* draw the new one */
1217 mark_selection( __vi_screen, selection_start, selection_end );
1219 /* and tell the window manager we own the selection */
1220 if ( select_type != select_char ) {
1221 __vi_AcquirePrimary( widget );
1222 copy_to_clipboard( __vi_screen );
1227 #if defined(__STDC__)
1228 static void select_extend( Widget widget,
1229 XEvent *event,
1230 String str,
1231 Cardinal *cardinal
1233 #else
1234 static void select_extend( widget, event, str, cardinal )
1235 Widget widget;
1236 XEvent *event;
1237 String str;
1238 Cardinal *cardinal;
1239 #endif
1241 int xpos, ypos, pos;
1242 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1244 /* NOTE: when multiple panes are implemented, we need to find
1245 * the correct screen. For now, there is only one.
1247 xpos = COLUMN( __vi_screen, ev->x );
1248 ypos = ROW( __vi_screen, ev->y );
1250 /* deal with words, lines, etc */
1251 pos = Linear( __vi_screen, ypos, xpos );
1252 if ( pos < selection_anchor )
1253 left_expand_selection( __vi_screen, &pos );
1254 else
1255 right_expand_selection( __vi_screen, &pos );
1257 /* extend from before the start? */
1258 if ( pos < selection_start ) {
1259 mark_selection( __vi_screen, pos, selection_start-1 );
1260 selection_start = pos;
1263 /* extend past the end? */
1264 else if ( pos > selection_end ) {
1265 mark_selection( __vi_screen, selection_end+1, pos );
1266 selection_end = pos;
1269 /* between the anchor and the start? */
1270 else if ( pos < selection_anchor ) {
1271 erase_selection( __vi_screen, selection_start, pos-1 );
1272 selection_start = pos;
1275 /* between the anchor and the end? */
1276 else {
1277 erase_selection( __vi_screen, pos+1, selection_end );
1278 selection_end = pos;
1281 /* and tell the window manager we own the selection */
1282 __vi_AcquirePrimary( widget );
1283 copy_to_clipboard( __vi_screen );
1287 #if defined(__STDC__)
1288 static void select_paste( Widget widget,
1289 XEvent *event,
1290 String str,
1291 Cardinal *cardinal
1293 #else
1294 static void select_paste( widget, event, str, cardinal )
1295 Widget widget;
1296 XEvent *event;
1297 String str;
1298 Cardinal *cardinal;
1299 #endif
1301 __vi_PasteFromClipboard( widget );
1305 /* Interface to copy and paste
1306 * (a) callbacks from the window manager
1307 * f_copy - it wants our buffer
1308 * f_paste - it wants us to paste some text
1309 * f_clear - we've lost the selection, clear it
1312 #if defined(__STDC__)
1313 static void f_copy( String *buffer, int *len )
1314 #else
1315 static void f_copy( buffer, len )
1316 String *buffer;
1317 int *len;
1318 #endif
1320 #ifdef TRACE
1321 vtrace("f_copy() called");
1322 #endif
1323 *buffer = clipboard;
1324 *len = clipboard_length;
1329 static void f_paste( widget, buffer, length )
1330 int widget, buffer, length;
1332 /* NOTE: when multiple panes are implemented, we need to find
1333 * the correct screen. For now, there is only one.
1335 #ifdef TRACE
1336 vtrace("f_paste() called with '%*.*s'\n", length, length, buffer);
1337 #endif
1341 #if defined(__STDC__)
1342 static void f_clear( Widget widget )
1343 #else
1344 static void f_clear( widget )
1345 Widget widget;
1346 #endif
1348 xvi_screen *cur_screen;
1350 #ifdef TRACE
1351 vtrace("f_clear() called");
1352 #endif
1354 XtVaGetValues( widget, XmNuserData, &cur_screen, 0 );
1356 erase_selection( cur_screen, selection_start, selection_end );
1361 * These routines deal with the cursor.
1363 * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int));
1365 void
1366 __vi_set_cursor(cur_screen, is_busy)
1367 xvi_screen *cur_screen;
1368 int is_busy;
1370 XDefineCursor( XtDisplay(cur_screen->area),
1371 XtWindow(cur_screen->area),
1372 (is_busy) ? busy_cursor : std_cursor
1378 /* hooks for the tags widget */
1380 static String cur_word = NULL;
1383 * PUBLIC: void __vi_set_word_at_caret __P((xvi_screen *));
1385 void
1386 __vi_set_word_at_caret( this_screen )
1387 xvi_screen *this_screen;
1389 char *start, *end, save;
1390 int newx, newy;
1392 newx = this_screen->curx;
1393 newy = this_screen->cury;
1395 /* Note that this really ought to be done by core due to wrapping issues */
1396 for ( end = start = CharAt( this_screen, newy, newx );
1397 (isalnum( *end ) || *end == '_') && (newx < this_screen->cols);
1398 end++, newx++
1400 save = *end;
1401 *end = '\0';
1402 if ( cur_word != NULL ) free( cur_word );
1403 cur_word = strdup( start );
1404 *end = save;
1406 /* if the tag stack widget is active, set the text field there
1407 * to agree with the current caret position.
1409 __vi_set_tag_text( cur_word );
1413 String __vi_get_word_at_caret( this_screen )
1414 xvi_screen *this_screen;
1416 return (cur_word) ? cur_word : "";
1421 * These routines deal with the caret.
1423 * PUBLIC: void draw_caret __P((xvi_screen *));
1425 static void
1426 draw_caret(this_screen)
1427 xvi_screen *this_screen;
1429 /* draw the caret by drawing the text in highlight color */
1430 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) |= COLOR_CARET;
1431 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1435 * PUBLIC: void __vi_erase_caret __P((xvi_screen *));
1437 void
1438 __vi_erase_caret(this_screen)
1439 xvi_screen *this_screen;
1441 /* erase the caret by drawing the text in normal video */
1442 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) &= ~COLOR_CARET;
1443 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1447 * PUBLIC: void __vi_move_caret __P((xvi_screen *, int, int));
1449 void
1450 __vi_move_caret(this_screen, newy, newx)
1451 xvi_screen *this_screen;
1452 int newy, newx;
1454 /* remove the old caret */
1455 __vi_erase_caret( this_screen );
1457 /* caret is now here */
1458 this_screen->curx = newx;
1459 this_screen->cury = newy;
1460 draw_caret( this_screen );