Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / nvi / motif_l / m_vi.c
blob6ec082a5220f023cf9450591532e15f0a1a8332c
1 /* $NetBSD$ */
3 /*-
4 * Copyright (c) 1996
5 * Rob Zimmermann. All rights reserved.
6 * Copyright (c) 1996
7 * Keith Bostic. All rights reserved.
9 * See the LICENSE file for redistribution information.
12 #include "config.h"
14 #ifndef lint
15 static const char sccsid[] = "Id: m_vi.c,v 8.41 2003/11/05 17:10:01 skimo Exp (Berkeley) Date: 2003/11/05 17:10:01";
16 #endif /* not lint */
18 #include <sys/types.h>
19 #include <sys/queue.h>
21 #include <X11/Intrinsic.h>
22 #include <X11/StringDefs.h>
23 #include <X11/cursorfont.h>
24 #include <Xm/PanedW.h>
25 #include <Xm/DrawingA.h>
26 #include <Xm/Form.h>
27 #include <Xm/Frame.h>
28 #include <Xm/ScrollBar.h>
30 #include <bitstring.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
40 #undef LOCK_SUCCESS
41 #include "../common/common.h"
42 #include "../ipc/ip.h"
43 #include "m_motif.h"
44 #include "vi_mextern.h"
45 #include "pathnames.h"
47 extern int vi_ofd;
49 static void f_copy(String *buffer, int *len);
50 static void f_paste(int widget, int buffer, int length);
51 static void f_clear(Widget widget);
55 * Globals and costants
58 #define BufferSize 1024
60 static XFontStruct *font;
61 static GC gc;
62 GC __vi_copy_gc;
63 static XtAppContext ctx;
65 xvi_screen *__vi_screen = NULL;
66 static Cursor std_cursor;
67 static Cursor busy_cursor;
68 static XtTranslations area_trans;
69 static int multi_click_length;
71 void (*__vi_exitp)(); /* Exit function. */
74 /* hack for drag scrolling...
75 * I'm not sure why, but the current protocol gets out of sync when
76 * a lot of drag messages get passed around. Likely, we need to wait
77 * for core to finish repainting the screen before sending more drag
78 * messages.
79 * To that end, we set scroll_block when we receive input from the scrollbar,
80 * and we clear it when we process the IPO_REFRESH message from core.
81 * A specific SCROLL_COMPLETED message would be better, but this seems to work.
84 static Boolean scroll_block = False;
87 * PUBLIC: void __vi_set_scroll_block __P((void));
89 void
90 __vi_set_scroll_block(void)
92 scroll_block = True;
96 * PUBLIC: void __vi_clear_scroll_block __P((void));
98 void
99 __vi_clear_scroll_block(void)
101 scroll_block = False;
105 #if defined(__STDC__)
106 static void set_gc_colors( xvi_screen *this_screen, int val )
107 #else
108 static void set_gc_colors( this_screen, val )
109 xvi_screen *this_screen;
110 int val;
111 #endif
113 static Pixel fg, bg, hi, shade;
114 static int prev = COLOR_INVALID;
116 /* no change? */
117 if ( prev == val ) return;
119 /* init? */
120 if ( gc == NULL ) {
122 /* what colors are selected for the drawing area? */
123 XtVaGetValues( this_screen->area,
124 XtNbackground, &bg,
125 XtNforeground, &fg,
126 XmNhighlightColor, &hi,
127 XmNtopShadowColor, &shade,
131 gc = XCreateGC( XtDisplay(this_screen->area),
132 DefaultRootWindow(XtDisplay(this_screen->area)),
137 XSetFont( XtDisplay(this_screen->area), gc, font->fid );
140 /* special colors? */
141 if ( val & COLOR_CARET ) {
142 XSetForeground( XtDisplay(this_screen->area), gc, fg );
143 XSetBackground( XtDisplay(this_screen->area), gc, hi );
145 else if ( val & COLOR_SELECT ) {
146 XSetForeground( XtDisplay(this_screen->area), gc, fg );
147 XSetBackground( XtDisplay(this_screen->area), gc, shade );
149 else switch (val) {
150 case COLOR_STANDARD:
151 XSetForeground( XtDisplay(this_screen->area), gc, fg );
152 XSetBackground( XtDisplay(this_screen->area), gc, bg );
153 break;
154 case COLOR_INVERSE:
155 XSetForeground( XtDisplay(this_screen->area), gc, bg );
156 XSetBackground( XtDisplay(this_screen->area), gc, fg );
157 break;
158 default: /* implement color map later */
159 break;
165 * Memory utilities
168 #ifdef REALLOC
169 #undef REALLOC
170 #endif
172 #define REALLOC( ptr, size ) \
173 ((ptr == NULL) ? malloc(size) : realloc(ptr,size))
176 /* X windows routines.
177 * We currently create a single, top-level shell. In that is a
178 * single drawing area into which we will draw text. This allows
179 * us to put multi-color (and font, but we'll never build that) text
180 * into the drawing area. In the future, we'll add scrollbars to the
181 * drawing areas
184 void select_start();
185 void select_extend();
186 void select_paste();
187 void key_press();
188 void insert_string();
189 void beep __P((Widget w));
190 void find();
191 void command();
193 static XtActionsRec area_actions[] = {
194 { "select_start", select_start },
195 { "select_extend", select_extend },
196 { "select_paste", select_paste },
197 { "key_press", key_press },
198 { "insert_string", insert_string },
199 { "beep", beep },
200 { "find", find },
201 { "command", command },
204 char areaTrans[] =
205 "<Btn1Down>: select_start() \n\
206 <Btn1Motion>: select_extend() \n\
207 <Btn2Down>: select_paste() \n\
208 <Btn3Down>: select_extend() \n\
209 <Btn3Motion>: select_extend() \n\
210 <Key>End: command(VI_C_BOTTOM) \n\
211 <Key>Escape: command(EINSERT) \n\
212 <Key>Find: find() \n\
213 <Key>Home: command(VI_C_TOP) \n\
214 <Key>Next: command(VI_C_PGDOWN) \n\
215 <Key>Prior: command(VI_C_PGUP) \n\
216 <Key>osfBackSpace: command(VI_C_LEFT) \n\
217 <Key>osfBeginLine: command(VI_C_BOL) \n\
218 <Key>osfCopy: beep() \n\
219 <Key>osfCut: beep() \n\
220 <Key>osfDelete: command(VI_C_DEL) \n\
221 <Key>osfDown: command(VI_C_DOWN) \n\
222 <Key>osfEndLine: command(VI_C_EOL) \n\
223 <Key>osfInsert: command(VI_C_INSERT) \n\
224 <Key>osfLeft: command(VI_C_LEFT) \n\
225 <Key>osfPageDown: command(VI_C_PGDOWN) \n\
226 <Key>osfPageUp: command(VI_C_PGUP) \n\
227 <Key>osfPaste: insert_string(p) \n\
228 <Key>osfRight: command(VI_C_RIGHT) \n\
229 <Key>osfUndo: command(VI_UNDO) \n\
230 <Key>osfUp: command(VI_C_UP) \n\
231 Ctrl<Key>C: command(VI_INTERRUPT) \n\
232 <Key>: key_press()";
235 static XutResource resource[] = {
236 { "font", XutRKfont, &font },
237 { "pointerShape", XutRKcursor, &std_cursor },
238 { "busyShape", XutRKcursor, &busy_cursor },
243 * vi_input_func --
244 * We've received input on the pipe from vi.
246 * PUBLIC: void vi_input_func __P((XtPointer, int *, XtInputId *));
248 void
249 vi_input_func(XtPointer client_data, int *source, XtInputId *id)
251 /* Parse and dispatch on commands in the queue. */
252 (void)ipvi_motif->input(ipvi_motif, *source);
254 #ifdef notdef
255 /* Check the pipe for unused events when not busy. */
256 XtAppAddWorkProc(ctx, process_pipe_input, NULL);
257 #endif
262 /* Send the window size. */
263 #if defined(__STDC__)
264 static void send_resize( xvi_screen *this_screen )
265 #else
266 static void send_resize( this_screen )
267 xvi_screen *this_screen;
268 #endif
270 IP_BUF ipb;
272 ipb.val1 = this_screen->rows;
273 ipb.val2 = this_screen->cols;
274 ipb.code = VI_RESIZE;
276 #ifdef TRACE
277 vtrace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols);
278 #endif
280 /* send up the pipe */
281 vi_send(vi_ofd, "12", &ipb);
285 #if defined(__STDC__)
286 static void resize_backing_store( xvi_screen *this_screen )
287 #else
288 static void resize_backing_store( this_screen )
289 xvi_screen *this_screen;
290 #endif
292 int total_chars = this_screen->rows * this_screen->cols;
294 this_screen->characters = REALLOC( this_screen->characters,
295 total_chars
297 memset( this_screen->characters, ' ', total_chars );
299 this_screen->flags = REALLOC( this_screen->flags,
300 total_chars
302 memset( this_screen->flags, 0, total_chars );
307 /* X will call this when we are resized */
308 #if defined(__STDC__)
309 static void resize_func( Widget wid,
310 XtPointer client_data,
311 XtPointer call_data
313 #else
314 static void resize_func( wid, client_data, call_data )
315 Widget wid;
316 XtPointer client_data;
317 XtPointer call_data;
318 #endif
320 xvi_screen *this_screen = (xvi_screen *) client_data;
321 Dimension height, width;
323 XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 );
325 /* generate correct sizes when we have font metrics implemented */
326 this_screen->cols = width / this_screen->ch_width;
327 this_screen->rows = height / this_screen->ch_height;
329 resize_backing_store( this_screen );
330 send_resize( this_screen );
335 * __vi_draw_text --
336 * Draw from backing store.
338 * PUBLIC: void __vi_draw_text __P((xvi_screen *, int, int, int));
340 void
341 __vi_draw_text(xvi_screen *this_screen, int row, int start_col, int 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(Widget wid, XtPointer client_data, XtPointer call_data)
437 xvi_screen *this_screen;
438 XmDrawingAreaCallbackStruct *cbs;
439 XExposeEvent *xev;
440 XGraphicsExposeEvent *gev;
441 int row;
443 /* convert pointers */
444 this_screen = (xvi_screen *) client_data;
445 cbs = (XmDrawingAreaCallbackStruct *) call_data;
447 /* first exposure? tell vi we are ready... */
448 if ( this_screen->init == False ) {
450 /* what does the user want to see? */
451 __vi_set_cursor( __vi_screen, False );
453 /* vi wants a resize as the first event */
454 send_resize( __vi_screen );
456 /* fine for now. we'll be back */
457 this_screen->init = True;
458 return;
461 if ( call_data == NULL ) {
463 /* vi core calls this when it wants a full refresh */
464 #ifdef TRACE
465 vtrace("expose_func: full refresh\n");
466 #endif
468 XClearWindow( XtDisplay(this_screen->area),
469 XtWindow(this_screen->area)
472 else {
473 switch ( cbs->event->type ) {
475 case GraphicsExpose:
476 gev = (XGraphicsExposeEvent *) cbs->event;
478 /* set clipping rectangles accordingly */
479 add_to_clip( this_screen,
480 gev->x, gev->y,
481 gev->width, gev->height
484 /* X calls here when XCopyArea exposes new bits */
485 #ifdef TRACE
486 vtrace("expose_func (X): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
487 gev->x, gev->y,
488 gev->width, gev->height,
489 gev->count);
490 #endif
492 /* more coming? do it then */
493 if ( gev->count > 0 ) return;
495 /* set clipping region */
496 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
497 break;
499 case Expose:
500 xev = (XExposeEvent *) cbs->event;
502 /* set clipping rectangles accordingly */
503 add_to_clip( this_screen,
504 xev->x, xev->y,
505 xev->width, xev->height
508 /* Motif calls here when DrawingArea is exposed */
509 #ifdef TRACE
510 vtrace("expose_func (Motif): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
511 xev->x, xev->y,
512 xev->width, xev->height,
513 xev->count);
514 #endif
516 /* more coming? do it then */
517 if ( xev->count > 0 ) return;
519 /* set clipping region */
520 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
521 break;
523 default:
524 /* don't care? */
525 return;
529 /* one row at a time */
530 for (row=0; row<this_screen->rows; row++) {
532 /* draw from the backing store */
533 __vi_draw_text( this_screen, row, 0, this_screen->cols );
536 /* clear clipping region */
537 XSetClipMask( XtDisplay(this_screen->area), gc, None );
538 if ( this_screen->clip != NULL ) {
539 XDestroyRegion( this_screen->clip );
540 this_screen->clip = NULL;
546 #if defined(__STDC__)
547 static void xexpose ( Widget w,
548 XtPointer client_data,
549 XEvent *ev,
550 Boolean *cont
552 #else
553 static void xexpose ( w, client_data, ev, cont )
554 Widget w;
555 XtPointer client_data;
556 XEvent *ev;
557 Boolean *cont;
558 #endif
560 XmDrawingAreaCallbackStruct cbs;
562 switch ( ev->type ) {
563 case GraphicsExpose:
564 cbs.event = ev;
565 cbs.window = XtWindow(w);
566 cbs.reason = XmCR_EXPOSE;
567 __vi_expose_func( w, client_data, (XtPointer) &cbs );
568 *cont = False; /* we took care of it */
569 break;
570 default:
571 /* don't care */
572 break;
577 /* unimplemented keystroke or command */
578 #if defined(__STDC__)
579 static void beep( Widget w )
580 #else
581 static void beep( w )
582 Widget w;
583 #endif
585 XBell(XtDisplay(w),0);
589 /* give me a search dialog */
590 #if defined(__STDC__)
591 static void find( Widget w )
592 #else
593 static void find( w )
594 Widget w;
595 #endif
597 __vi_show_search_dialog( w, "Find" );
601 * command --
602 * Translate simple keyboard input into vi protocol commands.
604 static void
605 command(Widget widget, XKeyEvent *event, String *str, Cardinal *cardinal)
607 static struct {
608 String name;
609 int code;
610 int count;
611 } table[] = {
612 { "VI_C_BOL", VI_C_BOL, 0 },
613 { "VI_C_BOTTOM", VI_C_BOTTOM, 0 },
614 { "VI_C_DEL", VI_C_DEL, 0 },
615 { "VI_C_DOWN", VI_C_DOWN, 1 },
616 { "VI_C_EOL", VI_C_EOL, 0 },
617 { "VI_C_INSERT", VI_C_INSERT, 0 },
618 { "VI_C_LEFT", VI_C_LEFT, 0 },
619 { "VI_C_PGDOWN", VI_C_PGDOWN, 1 },
620 { "VI_C_PGUP", VI_C_PGUP, 1 },
621 { "VI_C_RIGHT", VI_C_RIGHT, 0 },
622 { "VI_C_TOP", VI_C_TOP, 0 },
623 { "VI_C_UP", VI_C_UP, 1 },
624 { "VI_INTERRUPT", VI_INTERRUPT, 0 },
626 IP_BUF ipb;
627 int i;
630 * XXX
631 * Do fast lookup based on character #6 -- sleazy, but I don't
632 * want to do 10 strcmp's per keystroke.
634 ipb.val1 = 1;
635 for (i = 0; i < XtNumber(table); i++)
636 if (table[i].name[6] == (*str)[6] &&
637 strcmp(table[i].name, *str) == 0) {
638 ipb.code = table[i].code;
639 vi_send(vi_ofd, table[i].count ? "1" : NULL, &ipb);
640 return;
643 /* oops. */
644 beep(widget);
647 /* mouse or keyboard input. */
648 #if defined(__STDC__)
649 static void insert_string( Widget widget,
650 XKeyEvent *event,
651 String *str,
652 Cardinal *cardinal
654 #else
655 static void insert_string( widget, event, str, cardinal )
656 Widget widget;
657 XKeyEvent *event;
658 String *str;
659 Cardinal *cardinal;
660 #endif
662 IP_BUF ipb;
664 ipb.len1 = strlen( *str );
665 if ( ipb.len1 != 0 ) {
666 ipb.code = VI_STRING;
667 ipb.str1 = *str;
668 vi_send(vi_ofd, "a", &ipb);
671 #ifdef TRACE
672 vtrace("insert_string {%.*s}\n", strlen( *str ), *str );
673 #endif
677 /* mouse or keyboard input. */
678 #if defined(__STDC__)
679 static void key_press( Widget widget,
680 XKeyEvent *event,
681 String str,
682 Cardinal *cardinal
684 #else
685 static void key_press( widget, event, str, cardinal )
686 Widget widget;
687 XKeyEvent *event;
688 String str;
689 Cardinal *cardinal;
690 #endif
692 IP_BUF ipb;
693 char bp[BufferSize];
695 ipb.len1 = XLookupString( event, bp, BufferSize, NULL, NULL );
696 if ( ipb.len1 != 0 ) {
697 ipb.code = VI_STRING;
698 ipb.str1 = bp;
699 #ifdef TRACE
700 vtrace("key_press {%.*s}\n", ipb.len1, bp );
701 #endif
702 vi_send(vi_ofd, "a", &ipb);
708 #if defined(__STDC__)
709 static void scrollbar_moved( Widget widget,
710 XtPointer ptr,
711 XmScrollBarCallbackStruct *cbs
713 #else
714 static void scrollbar_moved( widget, ptr, cbs )
715 Widget widget;
716 XtPointer ptr;
717 XmScrollBarCallbackStruct *cbs;
718 #endif
720 /* Future: Need to scroll the correct screen! */
721 xvi_screen *cur_screen = (xvi_screen *) ptr;
722 IP_BUF ipb;
724 /* if we are still processing messages from core, skip this event
725 * (see comments near __vi_set_scroll_block())
727 if ( scroll_block ) {
728 return;
730 __vi_set_scroll_block();
732 #ifdef TRACE
733 switch ( cbs->reason ) {
734 case XmCR_VALUE_CHANGED:
735 vtrace( "scrollbar VALUE_CHANGED %d\n", cbs->value );
736 break;
737 case XmCR_DRAG:
738 vtrace( "scrollbar DRAG %d\n", cbs->value );
739 break;
740 default:
741 vtrace( "scrollbar <default> %d\n", cbs->value );
742 break;
744 vtrace("scrollto {%d}\n", cbs->value );
745 #endif
747 /* Send the new cursor position. */
748 ipb.code = VI_C_SETTOP;
749 ipb.val1 = cbs->value;
750 (void)vi_send(vi_ofd, "1", &ipb);
754 #if defined(__STDC__)
755 static xvi_screen *create_screen( Widget parent, int rows, int cols )
756 #else
757 static xvi_screen *create_screen( parent, rows, cols )
758 Widget parent;
759 int rows, cols;
760 #endif
762 xvi_screen *new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) );
763 Widget frame;
765 /* init... */
766 new_screen->color = COLOR_STANDARD;
767 new_screen->parent = parent;
769 /* figure out the sizes */
770 new_screen->rows = rows;
771 new_screen->cols = cols;
772 new_screen->ch_width = font->max_bounds.width;
773 new_screen->ch_height = font->descent + font->ascent;
774 new_screen->ch_descent = font->descent;
775 new_screen->clip = NULL;
777 /* allocate and init the backing stores */
778 resize_backing_store( new_screen );
780 /* set up a translation table for the X toolkit */
781 if ( area_trans == NULL )
782 area_trans = XtParseTranslationTable(areaTrans);
784 /* future, new screen gets inserted into the parent sash
785 * immediately after the current screen. Default Pane action is
786 * to add it to the end
789 /* use a form to hold the drawing area and the scrollbar */
790 new_screen->form = XtVaCreateManagedWidget( "form",
791 xmFormWidgetClass,
792 parent,
793 XmNpaneMinimum, 2*new_screen->ch_height,
794 XmNallowResize, True,
795 NULL
798 /* create a scrollbar. */
799 new_screen->scroll = XtVaCreateManagedWidget( "scroll",
800 xmScrollBarWidgetClass,
801 new_screen->form,
802 XmNtopAttachment, XmATTACH_FORM,
803 XmNbottomAttachment, XmATTACH_FORM,
804 XmNrightAttachment, XmATTACH_FORM,
805 XmNminimum, 1,
806 XmNmaximum, 2,
807 XmNsliderSize, 1,
808 NULL
810 XtAddCallback( new_screen->scroll,
811 XmNvalueChangedCallback,
812 scrollbar_moved,
813 new_screen
815 XtAddCallback( new_screen->scroll,
816 XmNdragCallback,
817 scrollbar_moved,
818 new_screen
821 /* create a frame because they look nice */
822 frame = XtVaCreateManagedWidget( "frame",
823 xmFrameWidgetClass,
824 new_screen->form,
825 XmNshadowType, XmSHADOW_ETCHED_IN,
826 XmNtopAttachment, XmATTACH_FORM,
827 XmNbottomAttachment, XmATTACH_FORM,
828 XmNleftAttachment, XmATTACH_FORM,
829 XmNrightAttachment, XmATTACH_WIDGET,
830 XmNrightWidget, new_screen->scroll,
831 NULL
834 /* create a drawing area into which we will put text */
835 new_screen->area = XtVaCreateManagedWidget( "screen",
836 xmDrawingAreaWidgetClass,
837 frame,
838 XmNheight, new_screen->ch_height * new_screen->rows,
839 XmNwidth, new_screen->ch_width * new_screen->cols,
840 XmNtranslations, area_trans,
841 XmNuserData, new_screen,
842 XmNnavigationType, XmNONE,
843 XmNtraversalOn, False,
844 NULL
847 /* this callback is for when the drawing area is resized */
848 XtAddCallback( new_screen->area,
849 XmNresizeCallback,
850 resize_func,
851 new_screen
854 /* this callback is for when the drawing area is exposed */
855 XtAddCallback( new_screen->area,
856 XmNexposeCallback,
857 __vi_expose_func,
858 new_screen
861 /* this callback is for when we expose obscured bits
862 * (e.g. there is a window over part of our drawing area
864 XtAddEventHandler( new_screen->area,
865 0, /* no standard events */
866 True, /* we *WANT* GraphicsExpose */
867 xexpose, /* what to do */
868 new_screen
871 return new_screen;
875 static xvi_screen *split_screen(void)
877 Cardinal num;
878 WidgetList c;
879 int rows = __vi_screen->rows / 2;
880 xvi_screen *new_screen;
882 /* Note that (global) cur_screen needs to be correctly set so that
883 * insert_here knows which screen to put the new one after
885 new_screen = create_screen( __vi_screen->parent,
886 rows,
887 __vi_screen->cols
890 /* what are the screens? */
891 XtVaGetValues( __vi_screen->parent,
892 XmNnumChildren, &num,
893 XmNchildren, &c,
894 NULL
897 /* unmanage all children in preparation for resizing */
898 XtUnmanageChildren( c, num );
900 /* force resize of the affected screens */
901 XtVaSetValues( new_screen->form,
902 XmNheight, new_screen->ch_height * rows,
903 NULL
905 XtVaSetValues( __vi_screen->form,
906 XmNheight, __vi_screen->ch_height * rows,
907 NULL
910 /* re-manage */
911 XtManageChildren( c, num );
913 /* done */
914 return new_screen;
918 /* Tell me where to insert the next subpane */
919 #if defined(__STDC__)
920 static Cardinal insert_here( Widget wid )
921 #else
922 static Cardinal insert_here( wid )
923 Widget wid;
924 #endif
926 Cardinal i, num;
927 WidgetList c;
929 XtVaGetValues( XtParent(wid),
930 XmNnumChildren, &num,
931 XmNchildren, &c,
932 NULL
935 /* The default XmNinsertPosition procedure for PanedWindow
936 * causes sashes to be inserted at the end of the list of children
937 * and causes non-sash widgets to be inserted after other
938 * non-sash children but before any sashes.
940 if ( ! XmIsForm( wid ) )
941 return num;
943 /* We will put the widget after the one with the current screen */
944 for (i=0; i<num && XmIsForm(c[i]); i++) {
945 if ( __vi_screen == NULL || __vi_screen->form == c[i] )
946 return i+1; /* after the i-th */
949 /* could not find it? this should never happen */
950 return num;
955 * vi_create_editor --
956 * Create the necessary widgetry.
958 * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void)));
960 Widget
961 vi_create_editor(String name, Widget parent, void (*exitp) (void))
963 Widget pane_w;
964 Display *display = XtDisplay( parent );
966 __vi_exitp = exitp;
968 /* first time through? */
969 if ( ctx == NULL ) {
971 /* save this for later */
972 ctx = XtWidgetToApplicationContext( parent );
974 /* add our own special actions */
975 XtAppAddActions( ctx, area_actions, XtNumber(area_actions) );
977 /* how long is double-click? */
978 multi_click_length = XtGetMultiClickTime( display );
980 /* check the resource database for interesting resources */
981 __XutConvertResources( parent,
982 vi_progname,
983 resource,
984 XtNumber(resource)
987 /* we need a context for moving bits around in the windows */
988 __vi_copy_gc = XCreateGC( display,
989 DefaultRootWindow(display),
994 /* routines for inter client communications conventions */
995 __vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf );
998 /* create the paned window */
999 pane_w = XtVaCreateManagedWidget( "pane",
1000 xmPanedWindowWidgetClass,
1001 parent,
1002 XmNinsertPosition, insert_here,
1003 NULL
1006 /* allocate our data structure. in the future we will have several
1007 * screens running around at the same time
1009 __vi_screen = create_screen( pane_w, 24, 80 );
1011 /* force creation of our color text context */
1012 set_gc_colors( __vi_screen, COLOR_STANDARD );
1014 /* done */
1015 return pane_w;
1019 /* These routines deal with the selection buffer */
1021 static int selection_start, selection_end, selection_anchor;
1022 static enum select_enum {
1023 select_char, select_word, select_line
1024 } select_type = select_char;
1025 static int last_click;
1027 static char *clipboard = NULL;
1028 static int clipboard_size = 0,
1029 clipboard_length;
1032 #if defined(__STDC__)
1033 static void copy_to_clipboard( xvi_screen *cur_screen )
1034 #else
1035 static void copy_to_clipboard( cur_screen )
1036 xvi_screen *cur_screen;
1037 #endif
1039 /* for now, copy from the backing store. in the future,
1040 * vi core will tell us exactly what the selection buffer contains
1042 clipboard_length = 1 + selection_end - selection_start;
1044 if ( clipboard == NULL )
1045 clipboard = (char *) malloc( clipboard_length );
1046 else if ( clipboard_size < clipboard_length )
1047 clipboard = (char *) realloc( clipboard, clipboard_length );
1049 memcpy( clipboard,
1050 cur_screen->characters + selection_start,
1051 clipboard_length
1056 #if defined(__STDC__)
1057 static void mark_selection( xvi_screen *cur_screen, int start, int end )
1058 #else
1059 static void mark_selection( cur_screen, start, end )
1060 xvi_screen *cur_screen;
1061 int start;
1062 int end;
1063 #endif
1065 int row, col, i;
1067 for ( i=start; i<=end; i++ ) {
1068 if ( !( cur_screen->flags[i] & COLOR_SELECT ) ) {
1069 cur_screen->flags[i] |= COLOR_SELECT;
1070 ToRowCol( cur_screen, i, row, col );
1071 __vi_draw_text( cur_screen, row, col, 1 );
1077 #if defined(__STDC__)
1078 static void erase_selection( xvi_screen *cur_screen, int start, int end )
1079 #else
1080 static void erase_selection( cur_screen, start, end )
1081 xvi_screen *cur_screen;
1082 int start;
1083 int end;
1084 #endif
1086 int row, col, i;
1088 for ( i=start; i<=end; i++ ) {
1089 if ( cur_screen->flags[i] & COLOR_SELECT ) {
1090 cur_screen->flags[i] &= ~COLOR_SELECT;
1091 ToRowCol( cur_screen, i, row, col );
1092 __vi_draw_text( cur_screen, row, col, 1 );
1098 #if defined(__STDC__)
1099 static void left_expand_selection( xvi_screen *cur_screen, int *start )
1100 #else
1101 static void left_expand_selection( cur_screen, start )
1102 xvi_screen *cur_screen;
1103 int *start;
1104 #endif
1106 int row, col;
1108 switch ( select_type ) {
1109 case select_word:
1110 if ( *start == 0 || isspace( cur_screen->characters[*start] ) )
1111 return;
1112 for (;;) {
1113 if ( isspace( cur_screen->characters[*start-1] ) )
1114 return;
1115 if ( --(*start) == 0 )
1116 return;
1118 case select_line:
1119 ToRowCol( cur_screen, *start, row, col );
1120 col = 0;
1121 *start = Linear( cur_screen, row, col );
1122 break;
1127 #if defined(__STDC__)
1128 static void right_expand_selection( xvi_screen *cur_screen, int *end )
1129 #else
1130 static void right_expand_selection( cur_screen, end )
1131 xvi_screen *cur_screen;
1132 int *end;
1133 #endif
1135 int row, col, last = cur_screen->cols * cur_screen->rows - 1;
1137 switch ( select_type ) {
1138 case select_word:
1139 if ( *end == last || isspace( cur_screen->characters[*end] ) )
1140 return;
1141 for (;;) {
1142 if ( isspace( cur_screen->characters[*end+1] ) )
1143 return;
1144 if ( ++(*end) == last )
1145 return;
1147 case select_line:
1148 ToRowCol( cur_screen, *end, row, col );
1149 col = cur_screen->cols -1;
1150 *end = Linear( cur_screen, row, col );
1151 break;
1156 #if defined(__STDC__)
1157 static void select_start( Widget widget,
1158 XEvent *event,
1159 String str,
1160 Cardinal *cardinal
1162 #else
1163 static void select_start( widget, event, str, cardinal )
1164 Widget widget;
1165 XEvent *event;
1166 String str;
1167 Cardinal *cardinal;
1168 #endif
1170 IP_BUF ipb;
1171 int xpos, ypos;
1172 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1173 static int last_click;
1176 * NOTE: when multiple panes are implemented, we need to find the correct
1177 * screen. For now, there is only one.
1179 xpos = COLUMN( __vi_screen, ev->x );
1180 ypos = ROW( __vi_screen, ev->y );
1182 /* Remove the old one. */
1183 erase_selection( __vi_screen, selection_start, selection_end );
1185 /* Send the new cursor position. */
1186 ipb.code = VI_MOUSE_MOVE;
1187 ipb.val1 = ypos;
1188 ipb.val2 = xpos;
1189 (void)vi_send(vi_ofd, "12", &ipb);
1191 /* click-click, and we go for words, lines, etc */
1192 if ( ev->time - last_click < multi_click_length )
1193 select_type = (enum select_enum) ((((int)select_type)+1)%3);
1194 else
1195 select_type = select_char;
1196 last_click = ev->time;
1198 /* put the selection here */
1199 selection_anchor = Linear( __vi_screen, ypos, xpos );
1200 selection_start = selection_anchor;
1201 selection_end = selection_anchor;
1203 /* expand to include words, line, etc */
1204 left_expand_selection( __vi_screen, &selection_start );
1205 right_expand_selection( __vi_screen, &selection_end );
1207 /* draw the new one */
1208 mark_selection( __vi_screen, selection_start, selection_end );
1210 /* and tell the window manager we own the selection */
1211 if ( select_type != select_char ) {
1212 __vi_AcquirePrimary( widget );
1213 copy_to_clipboard( __vi_screen );
1218 #if defined(__STDC__)
1219 static void select_extend( Widget widget,
1220 XEvent *event,
1221 String str,
1222 Cardinal *cardinal
1224 #else
1225 static void select_extend( widget, event, str, cardinal )
1226 Widget widget;
1227 XEvent *event;
1228 String str;
1229 Cardinal *cardinal;
1230 #endif
1232 int xpos, ypos, pos;
1233 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1235 /* NOTE: when multiple panes are implemented, we need to find
1236 * the correct screen. For now, there is only one.
1238 xpos = COLUMN( __vi_screen, ev->x );
1239 ypos = ROW( __vi_screen, ev->y );
1241 /* deal with words, lines, etc */
1242 pos = Linear( __vi_screen, ypos, xpos );
1243 if ( pos < selection_anchor )
1244 left_expand_selection( __vi_screen, &pos );
1245 else
1246 right_expand_selection( __vi_screen, &pos );
1248 /* extend from before the start? */
1249 if ( pos < selection_start ) {
1250 mark_selection( __vi_screen, pos, selection_start-1 );
1251 selection_start = pos;
1254 /* extend past the end? */
1255 else if ( pos > selection_end ) {
1256 mark_selection( __vi_screen, selection_end+1, pos );
1257 selection_end = pos;
1260 /* between the anchor and the start? */
1261 else if ( pos < selection_anchor ) {
1262 erase_selection( __vi_screen, selection_start, pos-1 );
1263 selection_start = pos;
1266 /* between the anchor and the end? */
1267 else {
1268 erase_selection( __vi_screen, pos+1, selection_end );
1269 selection_end = pos;
1272 /* and tell the window manager we own the selection */
1273 __vi_AcquirePrimary( widget );
1274 copy_to_clipboard( __vi_screen );
1278 #if defined(__STDC__)
1279 static void select_paste( Widget widget,
1280 XEvent *event,
1281 String str,
1282 Cardinal *cardinal
1284 #else
1285 static void select_paste( widget, event, str, cardinal )
1286 Widget widget;
1287 XEvent *event;
1288 String str;
1289 Cardinal *cardinal;
1290 #endif
1292 __vi_PasteFromClipboard( widget );
1296 /* Interface to copy and paste
1297 * (a) callbacks from the window manager
1298 * f_copy - it wants our buffer
1299 * f_paste - it wants us to paste some text
1300 * f_clear - we've lost the selection, clear it
1303 #if defined(__STDC__)
1304 static void f_copy( String *buffer, int *len )
1305 #else
1306 static void f_copy( buffer, len )
1307 String *buffer;
1308 int *len;
1309 #endif
1311 #ifdef TRACE
1312 vtrace("f_copy() called");
1313 #endif
1314 *buffer = clipboard;
1315 *len = clipboard_length;
1320 static void f_paste(int widget, int buffer, int length)
1322 /* NOTE: when multiple panes are implemented, we need to find
1323 * the correct screen. For now, there is only one.
1325 #ifdef TRACE
1326 vtrace("f_paste() called with '%*.*s'\n", length, length, buffer);
1327 #endif
1331 #if defined(__STDC__)
1332 static void f_clear( Widget widget )
1333 #else
1334 static void f_clear( widget )
1335 Widget widget;
1336 #endif
1338 xvi_screen *cur_screen;
1340 #ifdef TRACE
1341 vtrace("f_clear() called");
1342 #endif
1344 XtVaGetValues( widget, XmNuserData, &cur_screen, 0 );
1346 erase_selection( cur_screen, selection_start, selection_end );
1351 * These routines deal with the cursor.
1353 * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int));
1355 void
1356 __vi_set_cursor(xvi_screen *cur_screen, int is_busy)
1358 XDefineCursor( XtDisplay(cur_screen->area),
1359 XtWindow(cur_screen->area),
1360 (is_busy) ? busy_cursor : std_cursor
1366 /* hooks for the tags widget */
1368 static String cur_word = NULL;
1371 * PUBLIC: void __vi_set_word_at_caret __P((xvi_screen *));
1373 void
1374 __vi_set_word_at_caret(xvi_screen *this_screen)
1376 char *start, *end, save;
1377 int newx, newy;
1379 newx = this_screen->curx;
1380 newy = this_screen->cury;
1382 /* Note that this really ought to be done by core due to wrapping issues */
1383 for ( end = start = CharAt( this_screen, newy, newx );
1384 (isalnum( *end ) || *end == '_') && (newx < this_screen->cols);
1385 end++, newx++
1387 save = *end;
1388 *end = '\0';
1389 if ( cur_word != NULL ) free( cur_word );
1390 cur_word = strdup( start );
1391 *end = save;
1393 /* if the tag stack widget is active, set the text field there
1394 * to agree with the current caret position.
1396 __vi_set_tag_text( cur_word );
1400 String __vi_get_word_at_caret(xvi_screen *this_screen)
1402 return (cur_word) ? cur_word : "";
1407 * These routines deal with the caret.
1409 * PUBLIC: void draw_caret __P((xvi_screen *));
1411 static void
1412 draw_caret(xvi_screen *this_screen)
1414 /* draw the caret by drawing the text in highlight color */
1415 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) |= COLOR_CARET;
1416 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1420 * PUBLIC: void __vi_erase_caret __P((xvi_screen *));
1422 void
1423 __vi_erase_caret(xvi_screen *this_screen)
1425 /* erase the caret by drawing the text in normal video */
1426 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) &= ~COLOR_CARET;
1427 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1431 * PUBLIC: void __vi_move_caret __P((xvi_screen *, int, int));
1433 void
1434 __vi_move_caret(xvi_screen *this_screen, int newy, int newx)
1436 /* remove the old caret */
1437 __vi_erase_caret( this_screen );
1439 /* caret is now here */
1440 this_screen->curx = newx;
1441 this_screen->cury = newy;
1442 draw_caret( this_screen );