1/*	$NetBSD: m_vi.c,v 1.1.1.2 2008/05/18 14:31:29 aymeric Exp $ */
2
3/*-
4 * Copyright (c) 1996
5 *	Rob Zimmermann.  All rights reserved.
6 * Copyright (c) 1996
7 *	Keith Bostic.  All rights reserved.
8 *
9 * See the LICENSE file for redistribution information.
10 */
11
12#include "config.h"
13
14#ifndef lint
15static 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 */
17
18#include <sys/types.h>
19#include <sys/queue.h>
20
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>
29
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>
39
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"
46
47extern int vi_ofd;
48
49static	void	f_copy(String *buffer, int *len);
50static	void	f_paste(int widget, int buffer, int length);
51static	void	f_clear(Widget widget);
52
53
54/*
55 * Globals and costants
56 */
57
58#define	BufferSize	1024
59
60static	XFontStruct	*font;
61static	GC		gc;
62	GC		__vi_copy_gc;
63static	XtAppContext	ctx;
64
65	xvi_screen	*__vi_screen = NULL;
66static	Cursor		std_cursor;
67static	Cursor		busy_cursor;
68static	XtTranslations	area_trans;
69static	int		multi_click_length;
70
71void (*__vi_exitp)();				/* Exit function. */
72
73
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.
82 */
83
84static Boolean scroll_block = False;
85
86/*
87 * PUBLIC: void __vi_set_scroll_block __P((void));
88 */
89void
90__vi_set_scroll_block(void)
91{
92	scroll_block = True;
93}
94
95/*
96 * PUBLIC: void __vi_clear_scroll_block __P((void));
97 */
98void
99__vi_clear_scroll_block(void)
100{
101	scroll_block = False;
102}
103
104
105#if defined(__STDC__)
106static	void	set_gc_colors( xvi_screen *this_screen, int val )
107#else
108static	void	set_gc_colors( this_screen, val )
109xvi_screen	*this_screen;
110int		val;
111#endif
112{
113    static Pixel	fg, bg, hi, shade;
114    static int		prev = COLOR_INVALID;
115
116    /* no change? */
117    if ( prev == val ) return;
118
119    /* init? */
120    if ( gc == NULL ) {
121
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,
128		       0
129		       );
130
131	gc = XCreateGC( XtDisplay(this_screen->area),
132		        DefaultRootWindow(XtDisplay(this_screen->area)),
133			0,
134			0
135			);
136
137	XSetFont( XtDisplay(this_screen->area), gc, font->fid );
138    }
139
140    /* special colors? */
141    if ( val & COLOR_CARET ) {
142	XSetForeground( XtDisplay(this_screen->area), gc, fg );
143	XSetBackground( XtDisplay(this_screen->area), gc, hi );
144    }
145    else if ( val & COLOR_SELECT ) {
146	XSetForeground( XtDisplay(this_screen->area), gc, fg );
147	XSetBackground( XtDisplay(this_screen->area), gc, shade );
148    }
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;
160    }
161}
162
163
164/*
165 * Memory utilities
166 */
167
168#ifdef REALLOC
169#undef REALLOC
170#endif
171
172#define REALLOC( ptr, size )	\
173	((ptr == NULL) ? malloc(size) : realloc(ptr,size))
174
175
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
182 */
183
184void	select_start();
185void	select_extend();
186void	select_paste();
187void	key_press();
188void	insert_string();
189void	beep __P((Widget w));
190void	find();
191void	command();
192
193static 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		},
202};
203
204char	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()";
233
234
235static  XutResource resource[] = {
236    { "font",		XutRKfont,	&font		},
237    { "pointerShape",	XutRKcursor,	&std_cursor	},
238    { "busyShape",	XutRKcursor,	&busy_cursor	},
239};
240
241
242/*
243 * vi_input_func --
244 *	We've received input on the pipe from vi.
245 *
246 * PUBLIC: void vi_input_func __P((XtPointer, int *, XtInputId *));
247 */
248void
249vi_input_func(XtPointer client_data, int *source, XtInputId *id)
250{
251	/* Parse and dispatch on commands in the queue. */
252	(void)ipvi_motif->input(ipvi_motif, *source);
253
254#ifdef notdef
255	/* Check the pipe for unused events when not busy. */
256	XtAppAddWorkProc(ctx, process_pipe_input, NULL);
257#endif
258}
259
260
261
262/* Send the window size. */
263#if defined(__STDC__)
264static	void	send_resize( xvi_screen *this_screen )
265#else
266static	void	send_resize( this_screen )
267xvi_screen	*this_screen;
268#endif
269{
270    IP_BUF	ipb;
271
272    ipb.val1 = this_screen->rows;
273    ipb.val2 = this_screen->cols;
274    ipb.code = VI_RESIZE;
275
276#ifdef TRACE
277    vtrace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols);
278#endif
279
280    /* send up the pipe */
281    vi_send(vi_ofd, "12", &ipb);
282}
283
284
285#if defined(__STDC__)
286static	void	resize_backing_store( xvi_screen *this_screen )
287#else
288static	void	resize_backing_store( this_screen )
289xvi_screen	*this_screen;
290#endif
291{
292    int	total_chars = this_screen->rows * this_screen->cols;
293
294    this_screen->characters	= REALLOC( this_screen->characters,
295					   total_chars
296					   );
297    memset( this_screen->characters, ' ', total_chars );
298
299    this_screen->flags		= REALLOC( this_screen->flags,
300					   total_chars
301					   );
302    memset( this_screen->flags, 0, total_chars );
303}
304
305
306
307/* X will call this when we are resized */
308#if defined(__STDC__)
309static	void	resize_func( Widget wid,
310			     XtPointer client_data,
311			     XtPointer call_data
312			     )
313#else
314static	void	resize_func( wid, client_data, call_data )
315Widget		wid;
316XtPointer	client_data;
317XtPointer	call_data;
318#endif
319{
320    xvi_screen			*this_screen = (xvi_screen *) client_data;
321    Dimension			height, width;
322
323    XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 );
324
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;
328
329    resize_backing_store( this_screen );
330    send_resize( this_screen );
331}
332
333
334/*
335 * __vi_draw_text --
336 *	Draw from backing store.
337 *
338 * PUBLIC: void	__vi_draw_text __P((xvi_screen *, int, int, int));
339 */
340void
341__vi_draw_text(xvi_screen *this_screen, int row, int start_col, int len)
342{
343    int		col, color, xpos;
344    char	*start, *end;
345
346    start = CharAt( __vi_screen, row, start_col );
347    color = *FlagAt( __vi_screen, row, start_col );
348    xpos  = XPOS( __vi_screen, start_col );
349
350    /* one column at a time */
351    for ( col=start_col;
352	  col<this_screen->cols && col<start_col+len;
353	  col++ ) {
354
355	/* has the color changed? */
356	if ( *FlagAt( __vi_screen, row, col ) == color )
357	    continue;
358
359	/* is there anything to write? */
360	end  = CharAt( __vi_screen, row, col );
361	if ( end == start )
362	    continue;
363
364	/* yes. write in the previous color */
365	set_gc_colors( __vi_screen, color );
366
367	/* add to display */
368	XDrawImageString( XtDisplay(__vi_screen->area),
369			  XtWindow(__vi_screen->area),
370			  gc,
371			  xpos,
372			  YPOS( __vi_screen, row ),
373			  start,
374			  end - start
375			  );
376
377	/* this is the new context */
378	color = *FlagAt( __vi_screen, row, col );
379	xpos  = XPOS( __vi_screen, col );
380	start = end;
381    }
382
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 );
388
389	/* add to display */
390	XDrawImageString( XtDisplay(__vi_screen->area),
391			  XtWindow(__vi_screen->area),
392			  gc,
393			  xpos,
394			  YPOS( __vi_screen, row ),
395			  start,
396			  end - start
397			  );
398    }
399}
400
401
402/* set clipping rectangles accordingly */
403#if defined(__STDC__)
404static	void	add_to_clip( xvi_screen *cur_screen, int x, int y, int width, int height )
405#else
406static	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
413{
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 );
422}
423
424
425/*
426 * __vi_expose_func --
427 *	Redraw the window's contents.
428 *
429 * NOTE: When vi wants to force a redraw, we are called with NULL widget
430 *	 and call_data.
431 *
432 * PUBLIC: void	__vi_expose_func __P((Widget, XtPointer, XtPointer));
433 */
434void
435__vi_expose_func(Widget wid, XtPointer client_data, XtPointer call_data)
436{
437    xvi_screen			*this_screen;
438    XmDrawingAreaCallbackStruct	*cbs;
439    XExposeEvent		*xev;
440    XGraphicsExposeEvent	*gev;
441    int				row;
442
443    /* convert pointers */
444    this_screen = (xvi_screen *) client_data;
445    cbs		= (XmDrawingAreaCallbackStruct *) call_data;
446
447    /* first exposure? tell vi we are ready... */
448    if ( this_screen->init == False ) {
449
450	/* what does the user want to see? */
451	__vi_set_cursor( __vi_screen, False );
452
453	/* vi wants a resize as the first event */
454	send_resize( __vi_screen );
455
456	/* fine for now.  we'll be back */
457	this_screen->init = True;
458	return;
459    }
460
461    if ( call_data == NULL ) {
462
463	/* vi core calls this when it wants a full refresh */
464#ifdef TRACE
465	vtrace("expose_func:  full refresh\n");
466#endif
467
468	XClearWindow( XtDisplay(this_screen->area),
469		      XtWindow(this_screen->area)
470		      );
471    }
472    else {
473	switch ( cbs->event->type ) {
474
475	    case GraphicsExpose:
476		gev = (XGraphicsExposeEvent *) cbs->event;
477
478		/* set clipping rectangles accordingly */
479		add_to_clip( this_screen,
480			     gev->x, gev->y,
481			     gev->width, gev->height
482			     );
483
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
491
492		/* more coming?  do it then */
493		if ( gev->count > 0 ) return;
494
495		/* set clipping region */
496		XSetRegion( XtDisplay(wid), gc, this_screen->clip );
497		break;
498
499	    case Expose:
500		xev = (XExposeEvent *) cbs->event;
501
502		/* set clipping rectangles accordingly */
503		add_to_clip( this_screen,
504			     xev->x, xev->y,
505			     xev->width, xev->height
506			     );
507
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
515
516		/* more coming?  do it then */
517		if ( xev->count > 0 ) return;
518
519		/* set clipping region */
520		XSetRegion( XtDisplay(wid), gc, this_screen->clip );
521		break;
522
523	    default:
524		/* don't care? */
525		return;
526	}
527    }
528
529    /* one row at a time */
530    for (row=0; row<this_screen->rows; row++) {
531
532	/* draw from the backing store */
533	__vi_draw_text( this_screen, row, 0, this_screen->cols );
534    }
535
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;
541    }
542
543}
544
545
546#if defined(__STDC__)
547static void	xexpose	( Widget w,
548			  XtPointer client_data,
549			  XEvent *ev,
550			  Boolean *cont
551			  )
552#else
553static void	xexpose	( w, client_data, ev, cont )
554Widget		w;
555XtPointer	client_data;
556XEvent		*ev;
557Boolean		*cont;
558#endif
559{
560    XmDrawingAreaCallbackStruct	cbs;
561
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;
573    }
574}
575
576
577/* unimplemented keystroke or command */
578#if defined(__STDC__)
579static void	beep( Widget w )
580#else
581static void	beep( w )
582Widget	w;
583#endif
584{
585    XBell(XtDisplay(w),0);
586}
587
588
589/* give me a search dialog */
590#if defined(__STDC__)
591static void	find( Widget w )
592#else
593static void	find( w )
594Widget	w;
595#endif
596{
597    __vi_show_search_dialog( w, "Find" );
598}
599
600/*
601 * command --
602 *	Translate simple keyboard input into vi protocol commands.
603 */
604static	void
605command(Widget widget, XKeyEvent *event, String *str, Cardinal *cardinal)
606{
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 },
625	};
626	IP_BUF ipb;
627	int i;
628
629	/*
630	 * XXX
631	 * Do fast lookup based on character #6 -- sleazy, but I don't
632	 * want to do 10 strcmp's per keystroke.
633	 */
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;
641		}
642
643	/* oops. */
644	beep(widget);
645}
646
647/* mouse or keyboard input. */
648#if defined(__STDC__)
649static	void	insert_string( Widget widget,
650			       XKeyEvent *event,
651			       String *str,
652			       Cardinal *cardinal
653			       )
654#else
655static	void	insert_string( widget, event, str, cardinal )
656Widget          widget;
657XKeyEvent       *event;
658String          *str;
659Cardinal        *cardinal;
660#endif
661{
662    IP_BUF	ipb;
663
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);
669    }
670
671#ifdef TRACE
672    vtrace("insert_string {%.*s}\n", strlen( *str ), *str );
673#endif
674}
675
676
677/* mouse or keyboard input. */
678#if defined(__STDC__)
679static	void	key_press( Widget widget,
680			   XKeyEvent *event,
681			   String str,
682			   Cardinal *cardinal
683			   )
684#else
685static	void	key_press( widget, event, str, cardinal )
686Widget          widget;
687XKeyEvent       *event;
688String          str;
689Cardinal        *cardinal;
690#endif
691{
692    IP_BUF	ipb;
693    char	bp[BufferSize];
694
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);
703    }
704
705}
706
707
708#if defined(__STDC__)
709static	void	scrollbar_moved( Widget widget,
710				 XtPointer ptr,
711				 XmScrollBarCallbackStruct *cbs
712				 )
713#else
714static	void				scrollbar_moved( widget, ptr, cbs )
715	Widget				widget;
716	XtPointer			ptr;
717	XmScrollBarCallbackStruct	*cbs;
718#endif
719{
720    /* Future:  Need to scroll the correct screen! */
721    xvi_screen	*cur_screen = (xvi_screen *) ptr;
722    IP_BUF	ipb;
723
724    /* if we are still processing messages from core, skip this event
725     * (see comments near __vi_set_scroll_block())
726     */
727    if ( scroll_block ) {
728	return;
729    }
730    __vi_set_scroll_block();
731
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;
743    }
744    vtrace("scrollto {%d}\n", cbs->value );
745#endif
746
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);
751}
752
753
754#if defined(__STDC__)
755static	xvi_screen	*create_screen( Widget parent, int rows, int cols )
756#else
757static	xvi_screen	*create_screen( parent, rows, cols )
758	Widget		parent;
759	int		rows, cols;
760#endif
761{
762    xvi_screen	*new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) );
763    Widget	frame;
764
765    /* init... */
766    new_screen->color		= COLOR_STANDARD;
767    new_screen->parent		= parent;
768
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;
776
777    /* allocate and init the backing stores */
778    resize_backing_store( new_screen );
779
780    /* set up a translation table for the X toolkit */
781    if ( area_trans == NULL )
782	area_trans = XtParseTranslationTable(areaTrans);
783
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
787     */
788
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
796	    );
797
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
809	    );
810    XtAddCallback( new_screen->scroll,
811		   XmNvalueChangedCallback,
812		   scrollbar_moved,
813		   new_screen
814		   );
815    XtAddCallback( new_screen->scroll,
816		   XmNdragCallback,
817		   scrollbar_moved,
818		   new_screen
819		   );
820
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
832	    );
833
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
845	    );
846
847    /* this callback is for when the drawing area is resized */
848    XtAddCallback( new_screen->area,
849		   XmNresizeCallback,
850		   resize_func,
851		   new_screen
852		   );
853
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
859		   );
860
861    /* this callback is for when we expose obscured bits
862     * (e.g. there is a window over part of our drawing area
863     */
864    XtAddEventHandler( new_screen->area,
865		       0,	/* no standard events */
866		       True,	/* we *WANT* GraphicsExpose */
867		       xexpose,	/* what to do */
868		       new_screen
869		       );
870
871    return new_screen;
872}
873
874
875static	xvi_screen	*split_screen(void)
876{
877    Cardinal	num;
878    WidgetList	c;
879    int		rows = __vi_screen->rows / 2;
880    xvi_screen	*new_screen;
881
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
884     */
885    new_screen = create_screen( __vi_screen->parent,
886				rows,
887				__vi_screen->cols
888				);
889
890    /* what are the screens? */
891    XtVaGetValues( __vi_screen->parent,
892		   XmNnumChildren,	&num,
893		   XmNchildren,		&c,
894		   NULL
895		   );
896
897    /* unmanage all children in preparation for resizing */
898    XtUnmanageChildren( c, num );
899
900    /* force resize of the affected screens */
901    XtVaSetValues( new_screen->form,
902		   XmNheight,	new_screen->ch_height * rows,
903		   NULL
904		   );
905    XtVaSetValues( __vi_screen->form,
906		   XmNheight,	__vi_screen->ch_height * rows,
907		   NULL
908		   );
909
910    /* re-manage */
911    XtManageChildren( c, num );
912
913    /* done */
914    return new_screen;
915}
916
917
918/* Tell me where to insert the next subpane */
919#if defined(__STDC__)
920static	Cardinal	insert_here( Widget wid )
921#else
922static	Cardinal	insert_here( wid )
923	Widget		wid;
924#endif
925{
926    Cardinal	i, num;
927    WidgetList	c;
928
929    XtVaGetValues( XtParent(wid),
930		   XmNnumChildren,	&num,
931		   XmNchildren,		&c,
932		   NULL
933		   );
934
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.
939     */
940    if ( ! XmIsForm( wid ) )
941	return num;
942
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 */
947    }
948
949    /* could not find it?  this should never happen */
950    return num;
951}
952
953
954/*
955 * vi_create_editor --
956 *	Create the necessary widgetry.
957 *
958 * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void)));
959 */
960Widget
961vi_create_editor(String name, Widget parent, void (*exitp) (void))
962{
963    Widget	pane_w;
964    Display	*display = XtDisplay( parent );
965
966    __vi_exitp = exitp;
967
968    /* first time through? */
969    if ( ctx == NULL ) {
970
971	/* save this for later */
972	ctx = XtWidgetToApplicationContext( parent );
973
974	/* add our own special actions */
975	XtAppAddActions( ctx, area_actions, XtNumber(area_actions) );
976
977	/* how long is double-click? */
978	multi_click_length = XtGetMultiClickTime( display );
979
980	/* check the resource database for interesting resources */
981	__XutConvertResources( parent,
982			     vi_progname,
983			     resource,
984			     XtNumber(resource)
985			     );
986
987	/* we need a context for moving bits around in the windows */
988	__vi_copy_gc = XCreateGC( display,
989				 DefaultRootWindow(display),
990				 0,
991				 0
992				 );
993
994	/* routines for inter client communications conventions */
995	__vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf );
996    }
997
998    /* create the paned window */
999    pane_w = XtVaCreateManagedWidget( "pane",
1000				      xmPanedWindowWidgetClass,
1001				      parent,
1002				      XmNinsertPosition,	insert_here,
1003				      NULL
1004				      );
1005
1006    /* allocate our data structure.  in the future we will have several
1007     * screens running around at the same time
1008     */
1009    __vi_screen = create_screen( pane_w, 24, 80 );
1010
1011    /* force creation of our color text context */
1012    set_gc_colors( __vi_screen, COLOR_STANDARD );
1013
1014    /* done */
1015    return pane_w;
1016}
1017
1018
1019/* These routines deal with the selection buffer */
1020
1021static	int	selection_start, selection_end, selection_anchor;
1022static	enum	select_enum {
1023	    select_char, select_word, select_line
1024	}	select_type = select_char;
1025static	int	last_click;
1026
1027static	char	*clipboard = NULL;
1028static	int	clipboard_size = 0,
1029		clipboard_length;
1030
1031
1032#if defined(__STDC__)
1033static	void	copy_to_clipboard( xvi_screen *cur_screen )
1034#else
1035static	void	copy_to_clipboard( cur_screen )
1036xvi_screen	*cur_screen;
1037#endif
1038{
1039    /* for now, copy from the backing store.  in the future,
1040     * vi core will tell us exactly what the selection buffer contains
1041     */
1042    clipboard_length = 1 + selection_end - selection_start;
1043
1044    if ( clipboard == NULL )
1045	clipboard = (char *) malloc( clipboard_length );
1046    else if ( clipboard_size < clipboard_length )
1047	clipboard = (char *) realloc( clipboard, clipboard_length );
1048
1049    memcpy( clipboard,
1050	    cur_screen->characters + selection_start,
1051	    clipboard_length
1052	    );
1053}
1054
1055
1056#if defined(__STDC__)
1057static	void	mark_selection( xvi_screen *cur_screen, int start, int end )
1058#else
1059static	void	mark_selection( cur_screen, start, end )
1060xvi_screen	*cur_screen;
1061int		start;
1062int		end;
1063#endif
1064{
1065    int	row, col, i;
1066
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 );
1072	}
1073    }
1074}
1075
1076
1077#if defined(__STDC__)
1078static	void	erase_selection( xvi_screen *cur_screen, int start, int end )
1079#else
1080static	void	erase_selection( cur_screen, start, end )
1081xvi_screen	*cur_screen;
1082int		start;
1083int		end;
1084#endif
1085{
1086    int	row, col, i;
1087
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 );
1093	}
1094    }
1095}
1096
1097
1098#if defined(__STDC__)
1099static	void	left_expand_selection( xvi_screen *cur_screen, int *start )
1100#else
1101static	void	left_expand_selection( cur_screen, start )
1102xvi_screen	*cur_screen;
1103int		*start;
1104#endif
1105{
1106    int row, col;
1107
1108    switch ( select_type ) {
1109	case select_word:
1110	    if ( *start == 0 || isspace( (unsigned char)cur_screen->characters[*start] ) )
1111		return;
1112	    for (;;) {
1113		if ( isspace( (unsigned char)cur_screen->characters[*start-1] ) )
1114		    return;
1115		if ( --(*start) == 0 )
1116		   return;
1117	    }
1118	case select_line:
1119	    ToRowCol( cur_screen, *start, row, col );
1120	    col = 0;
1121	    *start = Linear( cur_screen, row, col );
1122	    break;
1123    }
1124}
1125
1126
1127#if defined(__STDC__)
1128static	void	right_expand_selection( xvi_screen *cur_screen, int *end )
1129#else
1130static	void	right_expand_selection( cur_screen, end )
1131xvi_screen	*cur_screen;
1132int		*end;
1133#endif
1134{
1135    int row, col, last = cur_screen->cols * cur_screen->rows - 1;
1136
1137    switch ( select_type ) {
1138	case select_word:
1139	    if ( *end == last || isspace( (unsigned char)cur_screen->characters[*end] ) )
1140		return;
1141	    for (;;) {
1142		if ( isspace( (unsigned char)cur_screen->characters[*end+1] ) )
1143		    return;
1144		if ( ++(*end) == last )
1145		   return;
1146	    }
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;
1152    }
1153}
1154
1155
1156#if defined(__STDC__)
1157static	void	select_start( Widget widget,
1158			      XEvent *event,
1159			      String str,
1160			      Cardinal *cardinal
1161			      )
1162#else
1163static	void	select_start( widget, event, str, cardinal )
1164Widget		widget;
1165XEvent		*event;
1166String		str;
1167Cardinal        *cardinal;
1168#endif
1169{
1170    IP_BUF		ipb;
1171    int			xpos, ypos;
1172    XPointerMovedEvent	*ev = (XPointerMovedEvent *) event;
1173    static int		last_click;
1174
1175    /*
1176     * NOTE: when multiple panes are implemented, we need to find the correct
1177     * screen.  For now, there is only one.
1178     */
1179    xpos = COLUMN( __vi_screen, ev->x );
1180    ypos = ROW( __vi_screen, ev->y );
1181
1182    /* Remove the old one. */
1183    erase_selection( __vi_screen, selection_start, selection_end );
1184
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);
1190
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;
1197
1198    /* put the selection here */
1199    selection_anchor	= Linear( __vi_screen, ypos, xpos );
1200    selection_start	= selection_anchor;
1201    selection_end	= selection_anchor;
1202
1203    /* expand to include words, line, etc */
1204    left_expand_selection( __vi_screen, &selection_start );
1205    right_expand_selection( __vi_screen, &selection_end );
1206
1207    /* draw the new one */
1208    mark_selection( __vi_screen, selection_start, selection_end );
1209
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 );
1214    }
1215}
1216
1217
1218#if defined(__STDC__)
1219static	void	select_extend( Widget widget,
1220			       XEvent *event,
1221			       String str,
1222			       Cardinal *cardinal
1223			       )
1224#else
1225static	void	select_extend( widget, event, str, cardinal )
1226Widget		widget;
1227XEvent		*event;
1228String		str;
1229Cardinal        *cardinal;
1230#endif
1231{
1232    int			xpos, ypos, pos;
1233    XPointerMovedEvent	*ev = (XPointerMovedEvent *) event;
1234
1235    /* NOTE:  when multiple panes are implemented, we need to find
1236     * the correct screen.  For now, there is only one.
1237     */
1238    xpos = COLUMN( __vi_screen, ev->x );
1239    ypos = ROW( __vi_screen, ev->y );
1240
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 );
1247
1248    /* extend from before the start? */
1249    if ( pos < selection_start ) {
1250	mark_selection( __vi_screen, pos, selection_start-1 );
1251	selection_start = pos;
1252    }
1253
1254    /* extend past the end? */
1255    else if ( pos > selection_end ) {
1256	mark_selection( __vi_screen, selection_end+1, pos );
1257	selection_end = pos;
1258    }
1259
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;
1264    }
1265
1266    /* between the anchor and the end? */
1267    else {
1268	erase_selection( __vi_screen, pos+1, selection_end );
1269	selection_end = pos;
1270    }
1271
1272    /* and tell the window manager we own the selection */
1273    __vi_AcquirePrimary( widget );
1274    copy_to_clipboard( __vi_screen );
1275}
1276
1277
1278#if defined(__STDC__)
1279static	void	select_paste( Widget widget,
1280			      XEvent *event,
1281			      String str,
1282			      Cardinal *cardinal
1283			      )
1284#else
1285static	void	select_paste( widget, event, str, cardinal )
1286Widget		widget;
1287XEvent		*event;
1288String		str;
1289Cardinal        *cardinal;
1290#endif
1291{
1292    __vi_PasteFromClipboard( widget );
1293}
1294
1295
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
1301 */
1302
1303#if defined(__STDC__)
1304static	void	f_copy( String *buffer, int *len )
1305#else
1306static	void	f_copy( buffer, len )
1307	String	*buffer;
1308	int	*len;
1309#endif
1310{
1311#ifdef TRACE
1312    vtrace("f_copy() called");
1313#endif
1314    *buffer	= clipboard;
1315    *len	= clipboard_length;
1316}
1317
1318
1319
1320static	void	f_paste(int widget, int buffer, int length)
1321{
1322    /* NOTE:  when multiple panes are implemented, we need to find
1323     * the correct screen.  For now, there is only one.
1324     */
1325#ifdef TRACE
1326    vtrace("f_paste() called with '%*.*s'\n", length, length, buffer);
1327#endif
1328}
1329
1330
1331#if defined(__STDC__)
1332static	void	f_clear( Widget widget )
1333#else
1334static	void	f_clear( widget )
1335Widget	widget;
1336#endif
1337{
1338    xvi_screen	*cur_screen;
1339
1340#ifdef TRACE
1341    vtrace("f_clear() called");
1342#endif
1343
1344    XtVaGetValues( widget, XmNuserData, &cur_screen, 0 );
1345
1346    erase_selection( cur_screen, selection_start, selection_end );
1347}
1348
1349
1350/*
1351 * These routines deal with the cursor.
1352 *
1353 * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int));
1354 */
1355void
1356__vi_set_cursor(xvi_screen *cur_screen, int is_busy)
1357{
1358    XDefineCursor( XtDisplay(cur_screen->area),
1359		   XtWindow(cur_screen->area),
1360		   (is_busy) ? busy_cursor : std_cursor
1361		   );
1362}
1363
1364
1365
1366/* hooks for the tags widget */
1367
1368static	String	cur_word = NULL;
1369
1370/*
1371 * PUBLIC: void __vi_set_word_at_caret __P((xvi_screen *));
1372 */
1373void
1374__vi_set_word_at_caret(xvi_screen *this_screen)
1375{
1376    char	*start, *end, save;
1377    int		newx, newy;
1378
1379    newx = this_screen->curx;
1380    newy = this_screen->cury;
1381
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( (unsigned char)*end ) || *end == '_') && (newx < this_screen->cols);
1385	  end++, newx++
1386	  );
1387    save = *end;
1388    *end = '\0';
1389    if ( cur_word != NULL ) free( cur_word );
1390    cur_word = strdup( start );
1391    *end = save;
1392
1393    /* if the tag stack widget is active, set the text field there
1394     * to agree with the current caret position.
1395     */
1396    __vi_set_tag_text( cur_word );
1397}
1398
1399
1400String	__vi_get_word_at_caret(xvi_screen *this_screen)
1401{
1402    return (cur_word) ? cur_word : "";
1403}
1404
1405
1406/*
1407 * These routines deal with the caret.
1408 *
1409 * PUBLIC: void draw_caret __P((xvi_screen *));
1410 */
1411static void
1412draw_caret(xvi_screen *this_screen)
1413{
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 );
1417}
1418
1419/*
1420 * PUBLIC: void __vi_erase_caret __P((xvi_screen *));
1421 */
1422void
1423__vi_erase_caret(xvi_screen *this_screen)
1424{
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 );
1428}
1429
1430/*
1431 * PUBLIC: void	__vi_move_caret __P((xvi_screen *, int, int));
1432 */
1433void
1434__vi_move_caret(xvi_screen *this_screen, int newy, int newx)
1435{
1436    /* remove the old caret */
1437    __vi_erase_caret( this_screen );
1438
1439    /* caret is now here */
1440    this_screen->curx = newx;
1441    this_screen->cury = newy;
1442    draw_caret( this_screen );
1443}
1444