1 /*        $NetBSD: m_vi.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */
2  * Copyright (c) 1996
3  *        Rob Zimmermann.  All rights reserved.
4  * Copyright (c) 1996
5  *        Keith Bostic.  All rights reserved.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9 
10 #include "config.h"
11 
12 #include <sys/cdefs.h>
13 #if 0
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 */
17 #else
18 __RCSID("$NetBSD: m_vi.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
19 #endif
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 
24 #include <X11/Intrinsic.h>
25 #include <X11/StringDefs.h>
26 #include <X11/cursorfont.h>
27 #include <Xm/PanedW.h>
28 #include <Xm/DrawingA.h>
29 #include <Xm/Form.h>
30 #include <Xm/Frame.h>
31 #include <Xm/ScrollBar.h>
32 
33 #include <bitstring.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #undef LOCK_SUCCESS
44 #include "../common/common.h"
45 #include "../ipc/ip.h"
46 #include "m_motif.h"
47 #include "vi_mextern.h"
48 #include "pathnames.h"
49 
50 extern int vi_ofd;
51 
52 static    void      f_copy(String *buffer, int *len);
53 static    void      f_paste(int widget, int buffer, int length);
54 static    void      f_clear(Widget widget);
55 
56 
57 /*
58  * Globals and costants
59  */
60 
61 #define   BufferSize          1024
62 
63 static    XFontStruct         *font;
64 static    GC                  gc;
65           GC                  __vi_copy_gc;
66 static    XtAppContext        ctx;
67 
68           xvi_screen          *__vi_screen = NULL;
69 static    Cursor              std_cursor;
70 static    Cursor              busy_cursor;
71 static    XtTranslations      area_trans;
72 static    int                 multi_click_length;
73 
74 void (*__vi_exitp)();                                       /* Exit function. */
75 
76 
77 /* hack for drag scrolling...
78  * I'm not sure why, but the current protocol gets out of sync when
79  * a lot of drag messages get passed around.  Likely, we need to wait
80  * for core to finish repainting the screen before sending more drag
81  * messages.
82  * To that end, we set scroll_block when we receive input from the scrollbar,
83  * and we clear it when we process the IPO_REFRESH message from core.
84  * A specific SCROLL_COMPLETED message would be better, but this seems to work.
85  */
86 
87 static Boolean scroll_block = False;
88 
89 /*
90  * PUBLIC: void __vi_set_scroll_block __P((void));
91  */
92 void
__vi_set_scroll_block(void)93 __vi_set_scroll_block(void)
94 {
95           scroll_block = True;
96 }
97 
98 /*
99  * PUBLIC: void __vi_clear_scroll_block __P((void));
100  */
101 void
__vi_clear_scroll_block(void)102 __vi_clear_scroll_block(void)
103 {
104           scroll_block = False;
105 }
106 
107 
108 #if defined(__STDC__)
set_gc_colors(xvi_screen * this_screen,int val)109 static    void      set_gc_colors( xvi_screen *this_screen, int val )
110 #else
111 static    void      set_gc_colors( this_screen, val )
112 xvi_screen          *this_screen;
113 int                 val;
114 #endif
115 {
116     static Pixel    fg, bg, hi, shade;
117     static int                prev = COLOR_INVALID;
118 
119     /* no change? */
120     if ( prev == val ) return;
121 
122     /* init? */
123     if ( gc == NULL ) {
124 
125           /* what colors are selected for the drawing area? */
126           XtVaGetValues( this_screen->area,
127                            XtNbackground,                   &bg,
128                            XtNforeground,                   &fg,
129                            XmNhighlightColor,     &hi,
130                            XmNtopShadowColor,     &shade,
131                            0
132                            );
133 
134           gc = XCreateGC( XtDisplay(this_screen->area),
135                             DefaultRootWindow(XtDisplay(this_screen->area)),
136                               0,
137                               0
138                               );
139 
140           XSetFont( XtDisplay(this_screen->area), gc, font->fid );
141     }
142 
143     /* special colors? */
144     if ( val & COLOR_CARET ) {
145           XSetForeground( XtDisplay(this_screen->area), gc, fg );
146           XSetBackground( XtDisplay(this_screen->area), gc, hi );
147     }
148     else if ( val & COLOR_SELECT ) {
149           XSetForeground( XtDisplay(this_screen->area), gc, fg );
150           XSetBackground( XtDisplay(this_screen->area), gc, shade );
151     }
152     else switch (val) {
153           case COLOR_STANDARD:
154               XSetForeground( XtDisplay(this_screen->area), gc, fg );
155               XSetBackground( XtDisplay(this_screen->area), gc, bg );
156               break;
157           case COLOR_INVERSE:
158               XSetForeground( XtDisplay(this_screen->area), gc, bg );
159               XSetBackground( XtDisplay(this_screen->area), gc, fg );
160               break;
161           default:  /* implement color map later */
162               break;
163     }
164 }
165 
166 
167 /*
168  * Memory utilities
169  */
170 
171 #ifdef REALLOC
172 #undef REALLOC
173 #endif
174 
175 #define REALLOC( ptr, size )  \
176           ((ptr == NULL) ? malloc(size) : realloc(ptr,size))
177 
178 
179 /* X windows routines.
180  * We currently create a single, top-level shell.  In that is a
181  * single drawing area into which we will draw text.  This allows
182  * us to put multi-color (and font, but we'll never build that) text
183  * into the drawing area.  In the future, we'll add scrollbars to the
184  * drawing areas
185  */
186 
187 void      select_start();
188 void      select_extend();
189 void      select_paste();
190 void      key_press();
191 void      insert_string();
192 void      beep __P((Widget w));
193 void      find();
194 void      command();
195 
196 static XtActionsRec area_actions[] = {
197     { "select_start",         select_start        },
198     { "select_extend",        select_extend       },
199     { "select_paste",         select_paste        },
200     { "key_press",  key_press },
201     { "insert_string",        insert_string       },
202     { "beep",                 beep                },
203     { "find",                 find                },
204     { "command",    command             },
205 };
206 
207 char      areaTrans[] =
208     "<Btn1Down>:    select_start()                \n\
209      <Btn1Motion>:  select_extend()               \n\
210      <Btn2Down>:    select_paste()                \n\
211      <Btn3Down>:    select_extend()               \n\
212      <Btn3Motion>:  select_extend()               \n\
213      <Key>End:                command(VI_C_BOTTOM)          \n\
214      <Key>Escape:   command(EINSERT)    \n\
215      <Key>Find:               find()                        \n\
216      <Key>Home:               command(VI_C_TOP)   \n\
217      <Key>Next:               command(VI_C_PGDOWN)          \n\
218      <Key>Prior:    command(VI_C_PGUP)  \n\
219      <Key>osfBackSpace:       command(VI_C_LEFT)  \n\
220      <Key>osfBeginLine:       command(VI_C_BOL)   \n\
221      <Key>osfCopy:  beep()                        \n\
222      <Key>osfCut:   beep()                        \n\
223      <Key>osfDelete:          command(VI_C_DEL)   \n\
224      <Key>osfDown:  command(VI_C_DOWN)  \n\
225      <Key>osfEndLine:         command(VI_C_EOL)   \n\
226      <Key>osfInsert:          command(VI_C_INSERT)          \n\
227      <Key>osfLeft:  command(VI_C_LEFT)  \n\
228      <Key>osfPageDown:        command(VI_C_PGDOWN)          \n\
229      <Key>osfPageUp:          command(VI_C_PGUP)  \n\
230      <Key>osfPaste: insert_string(p)    \n\
231      <Key>osfRight: command(VI_C_RIGHT) \n\
232      <Key>osfUndo:  command(VI_UNDO)    \n\
233      <Key>osfUp:    command(VI_C_UP)    \n\
234      Ctrl<Key>C:    command(VI_INTERRUPT)         \n\
235      <Key>:                   key_press()";
236 
237 
238 static  XutResource resource[] = {
239     { "font",                 XutRKfont,          &font               },
240     { "pointerShape",         XutRKcursor,        &std_cursor         },
241     { "busyShape",  XutRKcursor,        &busy_cursor        },
242 };
243 
244 
245 /*
246  * vi_input_func --
247  *        We've received input on the pipe from vi.
248  *
249  * PUBLIC: void vi_input_func __P((XtPointer, int *, XtInputId *));
250  */
251 void
vi_input_func(XtPointer client_data,int * source,XtInputId * id)252 vi_input_func(XtPointer client_data, int *source, XtInputId *id)
253 {
254           /* Parse and dispatch on commands in the queue. */
255           (void)ipvi_motif->input(ipvi_motif, *source);
256 
257 #ifdef notdef
258           /* Check the pipe for unused events when not busy. */
259           XtAppAddWorkProc(ctx, process_pipe_input, NULL);
260 #endif
261 }
262 
263 
264 
265 /* Send the window size. */
266 #if defined(__STDC__)
send_resize(xvi_screen * this_screen)267 static    void      send_resize( xvi_screen *this_screen )
268 #else
269 static    void      send_resize( this_screen )
270 xvi_screen          *this_screen;
271 #endif
272 {
273     IP_BUF          ipb;
274 
275     ipb.val1 = this_screen->rows;
276     ipb.val2 = this_screen->cols;
277     ipb.code = VI_RESIZE;
278 
279 #ifdef TRACE
280     vtrace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols);
281 #endif
282 
283     /* send up the pipe */
284     vi_send(vi_ofd, "12", &ipb);
285 }
286 
287 
288 #if defined(__STDC__)
resize_backing_store(xvi_screen * this_screen)289 static    void      resize_backing_store( xvi_screen *this_screen )
290 #else
291 static    void      resize_backing_store( this_screen )
292 xvi_screen          *this_screen;
293 #endif
294 {
295     int   total_chars = this_screen->rows * this_screen->cols;
296 
297     this_screen->characters   = REALLOC( this_screen->characters,
298                                                      total_chars
299                                                      );
300     memset( this_screen->characters, ' ', total_chars );
301 
302     this_screen->flags                  = REALLOC( this_screen->flags,
303                                                      total_chars
304                                                      );
305     memset( this_screen->flags, 0, total_chars );
306 }
307 
308 
309 
310 /* X will call this when we are resized */
311 #if defined(__STDC__)
resize_func(Widget wid,XtPointer client_data,XtPointer call_data)312 static    void      resize_func( Widget wid,
313                                    XtPointer client_data,
314                                    XtPointer call_data
315                                    )
316 #else
317 static    void      resize_func( wid, client_data, call_data )
318 Widget              wid;
319 XtPointer client_data;
320 XtPointer call_data;
321 #endif
322 {
323     xvi_screen                          *this_screen = (xvi_screen *) client_data;
324     Dimension                           height, width;
325 
326     XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 );
327 
328     /* generate correct sizes when we have font metrics implemented */
329     this_screen->cols = width / this_screen->ch_width;
330     this_screen->rows = height / this_screen->ch_height;
331 
332     resize_backing_store( this_screen );
333     send_resize( this_screen );
334 }
335 
336 
337 /*
338  * __vi_draw_text --
339  *        Draw from backing store.
340  *
341  * PUBLIC: void     __vi_draw_text __P((xvi_screen *, int, int, int));
342  */
343 void
__vi_draw_text(xvi_screen * this_screen,int row,int start_col,int len)344 __vi_draw_text(xvi_screen *this_screen, int row, int start_col, int len)
345 {
346     int             col, color, xpos;
347     char  *start, *end;
348 
349     start = CharAt( __vi_screen, row, start_col );
350     color = *FlagAt( __vi_screen, row, start_col );
351     xpos  = XPOS( __vi_screen, start_col );
352 
353     /* one column at a time */
354     for ( col=start_col;
355             col<this_screen->cols && col<start_col+len;
356             col++ ) {
357 
358           /* has the color changed? */
359           if ( *FlagAt( __vi_screen, row, col ) == color )
360               continue;
361 
362           /* is there anything to write? */
363           end  = CharAt( __vi_screen, row, col );
364           if ( end == start )
365               continue;
366 
367           /* yes. write in the previous color */
368           set_gc_colors( __vi_screen, color );
369 
370           /* add to display */
371           XDrawImageString( XtDisplay(__vi_screen->area),
372                                 XtWindow(__vi_screen->area),
373                                 gc,
374                                 xpos,
375                                 YPOS( __vi_screen, row ),
376                                 start,
377                                 end - start
378                                 );
379 
380           /* this is the new context */
381           color = *FlagAt( __vi_screen, row, col );
382           xpos  = XPOS( __vi_screen, col );
383           start = end;
384     }
385 
386     /* is there anything to write? */
387     end = CharAt( __vi_screen, row, col );
388     if ( end != start ) {
389           /* yes. write in the previous color */
390           set_gc_colors( __vi_screen, color );
391 
392           /* add to display */
393           XDrawImageString( XtDisplay(__vi_screen->area),
394                                 XtWindow(__vi_screen->area),
395                                 gc,
396                                 xpos,
397                                 YPOS( __vi_screen, row ),
398                                 start,
399                                 end - start
400                                 );
401     }
402 }
403 
404 
405 /* set clipping rectangles accordingly */
406 #if defined(__STDC__)
add_to_clip(xvi_screen * cur_screen,int x,int y,int width,int height)407 static    void      add_to_clip( xvi_screen *cur_screen, int x, int y, int width, int height )
408 #else
409 static    void      add_to_clip( cur_screen, x, y, width, height )
410           xvi_screen *cur_screen;
411           int       x;
412           int       y;
413           int       width;
414           int       height;
415 #endif
416 {
417     XRectangle      rect;
418     rect.x          = x;
419     rect.y          = y;
420     rect.height     = height;
421     rect.width      = width;
422     if ( cur_screen->clip == NULL )
423           cur_screen->clip = XCreateRegion();
424     XUnionRectWithRegion( &rect, cur_screen->clip, cur_screen->clip );
425 }
426 
427 
428 /*
429  * __vi_expose_func --
430  *        Redraw the window's contents.
431  *
432  * NOTE: When vi wants to force a redraw, we are called with NULL widget
433  *         and call_data.
434  *
435  * PUBLIC: void     __vi_expose_func __P((Widget, XtPointer, XtPointer));
436  */
437 void
__vi_expose_func(Widget wid,XtPointer client_data,XtPointer call_data)438 __vi_expose_func(Widget wid, XtPointer client_data, XtPointer call_data)
439 {
440     xvi_screen                          *this_screen;
441     XmDrawingAreaCallbackStruct         *cbs;
442     XExposeEvent              *xev;
443     XGraphicsExposeEvent      *gev;
444     int                                 row;
445 
446     /* convert pointers */
447     this_screen = (xvi_screen *) client_data;
448     cbs             = (XmDrawingAreaCallbackStruct *) call_data;
449 
450     /* first exposure? tell vi we are ready... */
451     if ( this_screen->init == False ) {
452 
453           /* what does the user want to see? */
454           __vi_set_cursor( __vi_screen, False );
455 
456           /* vi wants a resize as the first event */
457           send_resize( __vi_screen );
458 
459           /* fine for now.  we'll be back */
460           this_screen->init = True;
461           return;
462     }
463 
464     if ( call_data == NULL ) {
465 
466           /* vi core calls this when it wants a full refresh */
467 #ifdef TRACE
468           vtrace("expose_func:  full refresh\n");
469 #endif
470 
471           XClearWindow( XtDisplay(this_screen->area),
472                           XtWindow(this_screen->area)
473                           );
474     }
475     else {
476           switch ( cbs->event->type ) {
477 
478               case GraphicsExpose:
479                     gev = (XGraphicsExposeEvent *) cbs->event;
480 
481                     /* set clipping rectangles accordingly */
482                     add_to_clip( this_screen,
483                                    gev->x, gev->y,
484                                    gev->width, gev->height
485                                    );
486 
487                     /* X calls here when XCopyArea exposes new bits */
488 #ifdef TRACE
489                     vtrace("expose_func (X):  (x=%d,y=%d,w=%d,h=%d), count=%d\n",
490                                    gev->x, gev->y,
491                                    gev->width, gev->height,
492                                    gev->count);
493 #endif
494 
495                     /* more coming?  do it then */
496                     if ( gev->count > 0 ) return;
497 
498                     /* set clipping region */
499                     XSetRegion( XtDisplay(wid), gc, this_screen->clip );
500                     break;
501 
502               case Expose:
503                     xev = (XExposeEvent *) cbs->event;
504 
505                     /* set clipping rectangles accordingly */
506                     add_to_clip( this_screen,
507                                    xev->x, xev->y,
508                                    xev->width, xev->height
509                                    );
510 
511                     /* Motif calls here when DrawingArea is exposed */
512 #ifdef TRACE
513                     vtrace("expose_func (Motif): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
514                                    xev->x, xev->y,
515                                    xev->width, xev->height,
516                                    xev->count);
517 #endif
518 
519                     /* more coming?  do it then */
520                     if ( xev->count > 0 ) return;
521 
522                     /* set clipping region */
523                     XSetRegion( XtDisplay(wid), gc, this_screen->clip );
524                     break;
525 
526               default:
527                     /* don't care? */
528                     return;
529           }
530     }
531 
532     /* one row at a time */
533     for (row=0; row<this_screen->rows; row++) {
534 
535           /* draw from the backing store */
536           __vi_draw_text( this_screen, row, 0, this_screen->cols );
537     }
538 
539     /* clear clipping region */
540     XSetClipMask( XtDisplay(this_screen->area), gc, None );
541     if ( this_screen->clip != NULL ) {
542           XDestroyRegion( this_screen->clip );
543           this_screen->clip = NULL;
544     }
545 
546 }
547 
548 
549 #if defined(__STDC__)
xexpose(Widget w,XtPointer client_data,XEvent * ev,Boolean * cont)550 static void         xexpose   ( Widget w,
551                                 XtPointer client_data,
552                                 XEvent *ev,
553                                 Boolean *cont
554                                 )
555 #else
556 static void         xexpose   ( w, client_data, ev, cont )
557 Widget              w;
558 XtPointer client_data;
559 XEvent              *ev;
560 Boolean             *cont;
561 #endif
562 {
563     XmDrawingAreaCallbackStruct         cbs;
564 
565     switch ( ev->type ) {
566           case GraphicsExpose:
567               cbs.event       = ev;
568               cbs.window      = XtWindow(w);
569               cbs.reason      = XmCR_EXPOSE;
570               __vi_expose_func( w, client_data, (XtPointer) &cbs );
571               *cont = False;  /* we took care of it */
572               break;
573           default:
574               /* don't care */
575               break;
576     }
577 }
578 
579 
580 /* unimplemented keystroke or command */
581 #if defined(__STDC__)
beep(Widget w)582 static void         beep( Widget w )
583 #else
584 static void         beep( w )
585 Widget    w;
586 #endif
587 {
588     XBell(XtDisplay(w),0);
589 }
590 
591 
592 /* give me a search dialog */
593 #if defined(__STDC__)
find(Widget w)594 static void         find( Widget w )
595 #else
596 static void         find( w )
597 Widget    w;
598 #endif
599 {
600     __vi_show_search_dialog( w, "Find" );
601 }
602 
603 /*
604  * command --
605  *        Translate simple keyboard input into vi protocol commands.
606  */
607 static    void
command(Widget widget,XKeyEvent * event,String * str,Cardinal * cardinal)608 command(Widget widget, XKeyEvent *event, String *str, Cardinal *cardinal)
609 {
610           static struct {
611                     String    name;
612                     int       code;
613                     int       count;
614           } table[] = {
615                     { "VI_C_BOL",                 VI_C_BOL, 0 },
616                     { "VI_C_BOTTOM",    VI_C_BOTTOM,        0 },
617                     { "VI_C_DEL",                 VI_C_DEL, 0 },
618                     { "VI_C_DOWN",                VI_C_DOWN,          1 },
619                     { "VI_C_EOL",                 VI_C_EOL, 0 },
620                     { "VI_C_INSERT",    VI_C_INSERT,        0 },
621                     { "VI_C_LEFT",                VI_C_LEFT,          0 },
622                     { "VI_C_PGDOWN",    VI_C_PGDOWN,        1 },
623                     { "VI_C_PGUP",                VI_C_PGUP,          1 },
624                     { "VI_C_RIGHT",               VI_C_RIGHT,         0 },
625                     { "VI_C_TOP",                 VI_C_TOP, 0 },
626                     { "VI_C_UP",                  VI_C_UP,  1 },
627                     { "VI_INTERRUPT",   VI_INTERRUPT,       0 },
628           };
629           IP_BUF ipb;
630           int i;
631 
632           /*
633            * XXX
634            * Do fast lookup based on character #6 -- sleazy, but I don't
635            * want to do 10 strcmp's per keystroke.
636            */
637           ipb.val1 = 1;
638           for (i = 0; i < XtNumber(table); i++)
639                     if (table[i].name[6] == (*str)[6] &&
640                         strcmp(table[i].name, *str) == 0) {
641                               ipb.code = table[i].code;
642                               vi_send(vi_ofd, table[i].count ? "1" : NULL, &ipb);
643                               return;
644                     }
645 
646           /* oops. */
647           beep(widget);
648 }
649 
650 /* mouse or keyboard input. */
651 #if defined(__STDC__)
insert_string(Widget widget,XKeyEvent * event,String * str,Cardinal * cardinal)652 static    void      insert_string( Widget widget,
653                                      XKeyEvent *event,
654                                      String *str,
655                                      Cardinal *cardinal
656                                      )
657 #else
658 static    void      insert_string( widget, event, str, cardinal )
659 Widget          widget;
660 XKeyEvent       *event;
661 String          *str;
662 Cardinal        *cardinal;
663 #endif
664 {
665     IP_BUF          ipb;
666 
667     ipb.len1 = strlen( *str );
668     if ( ipb.len1 != 0 ) {
669           ipb.code = VI_STRING;
670           ipb.str1 = *str;
671           vi_send(vi_ofd, "a", &ipb);
672     }
673 
674 #ifdef TRACE
675     vtrace("insert_string {%.*s}\n", strlen( *str ), *str );
676 #endif
677 }
678 
679 
680 /* mouse or keyboard input. */
681 #if defined(__STDC__)
key_press(Widget widget,XKeyEvent * event,String str,Cardinal * cardinal)682 static    void      key_press( Widget widget,
683                                  XKeyEvent *event,
684                                  String str,
685                                  Cardinal *cardinal
686                                  )
687 #else
688 static    void      key_press( widget, event, str, cardinal )
689 Widget          widget;
690 XKeyEvent       *event;
691 String          str;
692 Cardinal        *cardinal;
693 #endif
694 {
695     IP_BUF          ipb;
696     char  bp[BufferSize];
697 
698     ipb.len1 = XLookupString( event, bp, BufferSize, NULL, NULL );
699     if ( ipb.len1 != 0 ) {
700           ipb.code = VI_STRING;
701           ipb.str1 = bp;
702 #ifdef TRACE
703           vtrace("key_press {%.*s}\n", ipb.len1, bp );
704 #endif
705           vi_send(vi_ofd, "a", &ipb);
706     }
707 
708 }
709 
710 
711 #if defined(__STDC__)
scrollbar_moved(Widget widget,XtPointer ptr,XmScrollBarCallbackStruct * cbs)712 static    void      scrollbar_moved( Widget widget,
713                                          XtPointer ptr,
714                                          XmScrollBarCallbackStruct *cbs
715                                          )
716 #else
717 static    void                                    scrollbar_moved( widget, ptr, cbs )
718           Widget                                  widget;
719           XtPointer                     ptr;
720           XmScrollBarCallbackStruct     *cbs;
721 #endif
722 {
723     /* Future:  Need to scroll the correct screen! */
724     xvi_screen      *cur_screen = (xvi_screen *) ptr;
725     IP_BUF          ipb;
726 
727     /* if we are still processing messages from core, skip this event
728      * (see comments near __vi_set_scroll_block())
729      */
730     if ( scroll_block ) {
731           return;
732     }
733     __vi_set_scroll_block();
734 
735 #ifdef TRACE
736     switch ( cbs->reason ) {
737           case XmCR_VALUE_CHANGED:
738               vtrace( "scrollbar VALUE_CHANGED %d\n", cbs->value );
739               break;
740           case XmCR_DRAG:
741               vtrace( "scrollbar DRAG %d\n", cbs->value );
742               break;
743           default:
744               vtrace( "scrollbar <default> %d\n", cbs->value );
745               break;
746     }
747     vtrace("scrollto {%d}\n", cbs->value );
748 #endif
749 
750     /* Send the new cursor position. */
751     ipb.code = VI_C_SETTOP;
752     ipb.val1 = cbs->value;
753     (void)vi_send(vi_ofd, "1", &ipb);
754 }
755 
756 
757 #if defined(__STDC__)
create_screen(Widget parent,int rows,int cols)758 static    xvi_screen          *create_screen( Widget parent, int rows, int cols )
759 #else
760 static    xvi_screen          *create_screen( parent, rows, cols )
761           Widget              parent;
762           int                 rows, cols;
763 #endif
764 {
765     xvi_screen      *new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) );
766     Widget          frame;
767 
768     /* init... */
769     new_screen->color                   = COLOR_STANDARD;
770     new_screen->parent                  = parent;
771 
772     /* figure out the sizes */
773     new_screen->rows                    = rows;
774     new_screen->cols                    = cols;
775     new_screen->ch_width      = font->max_bounds.width;
776     new_screen->ch_height     = font->descent + font->ascent;
777     new_screen->ch_descent    = font->descent;
778     new_screen->clip                    = NULL;
779 
780     /* allocate and init the backing stores */
781     resize_backing_store( new_screen );
782 
783     /* set up a translation table for the X toolkit */
784     if ( area_trans == NULL )
785           area_trans = XtParseTranslationTable(areaTrans);
786 
787     /* future, new screen gets inserted into the parent sash
788      * immediately after the current screen.  Default Pane action is
789      * to add it to the end
790      */
791 
792     /* use a form to hold the drawing area and the scrollbar */
793     new_screen->form = XtVaCreateManagedWidget( "form",
794               xmFormWidgetClass,
795               parent,
796               XmNpaneMinimum,           2*new_screen->ch_height,
797               XmNallowResize,           True,
798               NULL
799               );
800 
801     /* create a scrollbar. */
802     new_screen->scroll = XtVaCreateManagedWidget( "scroll",
803               xmScrollBarWidgetClass,
804               new_screen->form,
805               XmNtopAttachment,                   XmATTACH_FORM,
806               XmNbottomAttachment,      XmATTACH_FORM,
807               XmNrightAttachment,                 XmATTACH_FORM,
808               XmNminimum,                         1,
809               XmNmaximum,                         2,
810               XmNsliderSize,            1,
811               NULL
812               );
813     XtAddCallback( new_screen->scroll,
814                        XmNvalueChangedCallback,
815                        scrollbar_moved,
816                        new_screen
817                        );
818     XtAddCallback( new_screen->scroll,
819                        XmNdragCallback,
820                        scrollbar_moved,
821                        new_screen
822                        );
823 
824     /* create a frame because they look nice */
825     frame = XtVaCreateManagedWidget( "frame",
826               xmFrameWidgetClass,
827               new_screen->form,
828               XmNshadowType,            XmSHADOW_ETCHED_IN,
829               XmNtopAttachment,                   XmATTACH_FORM,
830               XmNbottomAttachment,      XmATTACH_FORM,
831               XmNleftAttachment,                  XmATTACH_FORM,
832               XmNrightAttachment,                 XmATTACH_WIDGET,
833               XmNrightWidget,           new_screen->scroll,
834               NULL
835               );
836 
837     /* create a drawing area into which we will put text */
838     new_screen->area = XtVaCreateManagedWidget( "screen",
839               xmDrawingAreaWidgetClass,
840               frame,
841               XmNheight,                new_screen->ch_height * new_screen->rows,
842               XmNwidth,                 new_screen->ch_width * new_screen->cols,
843               XmNtranslations,          area_trans,
844               XmNuserData,    new_screen,
845               XmNnavigationType,        XmNONE,
846               XmNtraversalOn, False,
847               NULL
848               );
849 
850     /* this callback is for when the drawing area is resized */
851     XtAddCallback( new_screen->area,
852                        XmNresizeCallback,
853                        resize_func,
854                        new_screen
855                        );
856 
857     /* this callback is for when the drawing area is exposed */
858     XtAddCallback( new_screen->area,
859                        XmNexposeCallback,
860                        __vi_expose_func,
861                        new_screen
862                        );
863 
864     /* this callback is for when we expose obscured bits
865      * (e.g. there is a window over part of our drawing area
866      */
867     XtAddEventHandler( new_screen->area,
868                            0, /* no standard events */
869                            True,        /* we *WANT* GraphicsExpose */
870                            xexpose,     /* what to do */
871                            new_screen
872                            );
873 
874     return new_screen;
875 }
876 
877 
split_screen(void)878 static    xvi_screen          *split_screen(void)
879 {
880     Cardinal        num;
881     WidgetList      c;
882     int             rows = __vi_screen->rows / 2;
883     xvi_screen      *new_screen;
884 
885     /* Note that (global) cur_screen needs to be correctly set so that
886      * insert_here knows which screen to put the new one after
887      */
888     new_screen = create_screen( __vi_screen->parent,
889                                         rows,
890                                         __vi_screen->cols
891                                         );
892 
893     /* what are the screens? */
894     XtVaGetValues( __vi_screen->parent,
895                        XmNnumChildren,  &num,
896                        XmNchildren,               &c,
897                        NULL
898                        );
899 
900     /* unmanage all children in preparation for resizing */
901     XtUnmanageChildren( c, num );
902 
903     /* force resize of the affected screens */
904     XtVaSetValues( new_screen->form,
905                        XmNheight,       new_screen->ch_height * rows,
906                        NULL
907                        );
908     XtVaSetValues( __vi_screen->form,
909                        XmNheight,       __vi_screen->ch_height * rows,
910                        NULL
911                        );
912 
913     /* re-manage */
914     XtManageChildren( c, num );
915 
916     /* done */
917     return new_screen;
918 }
919 
920 
921 /* Tell me where to insert the next subpane */
922 #if defined(__STDC__)
insert_here(Widget wid)923 static    Cardinal  insert_here( Widget wid )
924 #else
925 static    Cardinal  insert_here( wid )
926           Widget              wid;
927 #endif
928 {
929     Cardinal        i, num;
930     WidgetList      c;
931 
932     XtVaGetValues( XtParent(wid),
933                        XmNnumChildren,  &num,
934                        XmNchildren,               &c,
935                        NULL
936                        );
937 
938     /* The  default  XmNinsertPosition  procedure  for  PanedWindow
939      * causes sashes to be inserted at the end of the list of children
940      * and causes non-sash widgets to be inserted after  other
941      * non-sash children but before any sashes.
942      */
943     if ( ! XmIsForm( wid ) )
944           return num;
945 
946     /* We will put the widget after the one with the current screen */
947     for (i=0; i<num && XmIsForm(c[i]); i++) {
948           if ( __vi_screen == NULL || __vi_screen->form == c[i] )
949               return i+1;     /* after the i-th */
950     }
951 
952     /* could not find it?  this should never happen */
953     return num;
954 }
955 
956 
957 /*
958  * vi_create_editor --
959  *        Create the necessary widgetry.
960  *
961  * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void)));
962  */
963 Widget
vi_create_editor(String name,Widget parent,void (* exitp)(void))964 vi_create_editor(String name, Widget parent, void (*exitp) (void))
965 {
966     Widget          pane_w;
967     Display         *display = XtDisplay( parent );
968 
969     __vi_exitp = exitp;
970 
971     /* first time through? */
972     if ( ctx == NULL ) {
973 
974           /* save this for later */
975           ctx = XtWidgetToApplicationContext( parent );
976 
977           /* add our own special actions */
978           XtAppAddActions( ctx, area_actions, XtNumber(area_actions) );
979 
980           /* how long is double-click? */
981           multi_click_length = XtGetMultiClickTime( display );
982 
983           /* check the resource database for interesting resources */
984           __XutConvertResources( parent,
985                                    vi_progname,
986                                    resource,
987                                    XtNumber(resource)
988                                    );
989 
990           /* we need a context for moving bits around in the windows */
991           __vi_copy_gc = XCreateGC( display,
992                                          DefaultRootWindow(display),
993                                          0,
994                                          0
995                                          );
996 
997           /* routines for inter client communications conventions */
998           __vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf );
999     }
1000 
1001     /* create the paned window */
1002     pane_w = XtVaCreateManagedWidget( "pane",
1003                                               xmPanedWindowWidgetClass,
1004                                               parent,
1005                                               XmNinsertPosition,      insert_here,
1006                                               NULL
1007                                               );
1008 
1009     /* allocate our data structure.  in the future we will have several
1010      * screens running around at the same time
1011      */
1012     __vi_screen = create_screen( pane_w, 24, 80 );
1013 
1014     /* force creation of our color text context */
1015     set_gc_colors( __vi_screen, COLOR_STANDARD );
1016 
1017     /* done */
1018     return pane_w;
1019 }
1020 
1021 
1022 /* These routines deal with the selection buffer */
1023 
1024 static    int       selection_start, selection_end, selection_anchor;
1025 static    enum      select_enum {
1026               select_char, select_word, select_line
1027           }         select_type = select_char;
1028 static    int       last_click;
1029 
1030 static    char      *clipboard = NULL;
1031 static    int       clipboard_size = 0,
1032                     clipboard_length;
1033 
1034 
1035 #if defined(__STDC__)
copy_to_clipboard(xvi_screen * cur_screen)1036 static    void      copy_to_clipboard( xvi_screen *cur_screen )
1037 #else
1038 static    void      copy_to_clipboard( cur_screen )
1039 xvi_screen          *cur_screen;
1040 #endif
1041 {
1042     /* for now, copy from the backing store.  in the future,
1043      * vi core will tell us exactly what the selection buffer contains
1044      */
1045     clipboard_length = 1 + selection_end - selection_start;
1046 
1047     if ( clipboard == NULL )
1048           clipboard = (char *) malloc( clipboard_length );
1049     else if ( clipboard_size < clipboard_length )
1050           clipboard = (char *) realloc( clipboard, clipboard_length );
1051 
1052     memcpy( clipboard,
1053               cur_screen->characters + selection_start,
1054               clipboard_length
1055               );
1056 }
1057 
1058 
1059 #if defined(__STDC__)
mark_selection(xvi_screen * cur_screen,int start,int end)1060 static    void      mark_selection( xvi_screen *cur_screen, int start, int end )
1061 #else
1062 static    void      mark_selection( cur_screen, start, end )
1063 xvi_screen          *cur_screen;
1064 int                 start;
1065 int                 end;
1066 #endif
1067 {
1068     int   row, col, i;
1069 
1070     for ( i=start; i<=end; i++ ) {
1071           if ( !( cur_screen->flags[i] & COLOR_SELECT ) ) {
1072               cur_screen->flags[i] |= COLOR_SELECT;
1073               ToRowCol( cur_screen, i, row, col );
1074               __vi_draw_text( cur_screen, row, col, 1 );
1075           }
1076     }
1077 }
1078 
1079 
1080 #if defined(__STDC__)
erase_selection(xvi_screen * cur_screen,int start,int end)1081 static    void      erase_selection( xvi_screen *cur_screen, int start, int end )
1082 #else
1083 static    void      erase_selection( cur_screen, start, end )
1084 xvi_screen          *cur_screen;
1085 int                 start;
1086 int                 end;
1087 #endif
1088 {
1089     int   row, col, i;
1090 
1091     for ( i=start; i<=end; i++ ) {
1092           if ( cur_screen->flags[i] & COLOR_SELECT ) {
1093               cur_screen->flags[i] &= ~COLOR_SELECT;
1094               ToRowCol( cur_screen, i, row, col );
1095               __vi_draw_text( cur_screen, row, col, 1 );
1096           }
1097     }
1098 }
1099 
1100 
1101 #if defined(__STDC__)
left_expand_selection(xvi_screen * cur_screen,int * start)1102 static    void      left_expand_selection( xvi_screen *cur_screen, int *start )
1103 #else
1104 static    void      left_expand_selection( cur_screen, start )
1105 xvi_screen          *cur_screen;
1106 int                 *start;
1107 #endif
1108 {
1109     int row, col;
1110 
1111     switch ( select_type ) {
1112           case select_word:
1113               if ( *start == 0 || isspace( (unsigned char)cur_screen->characters[*start] ) )
1114                     return;
1115               for (;;) {
1116                     if ( isspace( (unsigned char)cur_screen->characters[*start-1] ) )
1117                         return;
1118                     if ( --(*start) == 0 )
1119                        return;
1120               }
1121           case select_line:
1122               ToRowCol( cur_screen, *start, row, col );
1123               col = 0;
1124               *start = Linear( cur_screen, row, col );
1125               break;
1126     }
1127 }
1128 
1129 
1130 #if defined(__STDC__)
right_expand_selection(xvi_screen * cur_screen,int * end)1131 static    void      right_expand_selection( xvi_screen *cur_screen, int *end )
1132 #else
1133 static    void      right_expand_selection( cur_screen, end )
1134 xvi_screen          *cur_screen;
1135 int                 *end;
1136 #endif
1137 {
1138     int row, col, last = cur_screen->cols * cur_screen->rows - 1;
1139 
1140     switch ( select_type ) {
1141           case select_word:
1142               if ( *end == last || isspace( (unsigned char)cur_screen->characters[*end] ) )
1143                     return;
1144               for (;;) {
1145                     if ( isspace( (unsigned char)cur_screen->characters[*end+1] ) )
1146                         return;
1147                     if ( ++(*end) == last )
1148                        return;
1149               }
1150           case select_line:
1151               ToRowCol( cur_screen, *end, row, col );
1152               col = cur_screen->cols -1;
1153               *end = Linear( cur_screen, row, col );
1154               break;
1155     }
1156 }
1157 
1158 
1159 #if defined(__STDC__)
select_start(Widget widget,XEvent * event,String str,Cardinal * cardinal)1160 static    void      select_start( Widget widget,
1161                                     XEvent *event,
1162                                     String str,
1163                                     Cardinal *cardinal
1164                                     )
1165 #else
1166 static    void      select_start( widget, event, str, cardinal )
1167 Widget              widget;
1168 XEvent              *event;
1169 String              str;
1170 Cardinal        *cardinal;
1171 #endif
1172 {
1173     IP_BUF                    ipb;
1174     int                       xpos, ypos;
1175     XPointerMovedEvent        *ev = (XPointerMovedEvent *) event;
1176     static int                last_click;
1177 
1178     /*
1179      * NOTE: when multiple panes are implemented, we need to find the correct
1180      * screen.  For now, there is only one.
1181      */
1182     xpos = COLUMN( __vi_screen, ev->x );
1183     ypos = ROW( __vi_screen, ev->y );
1184 
1185     /* Remove the old one. */
1186     erase_selection( __vi_screen, selection_start, selection_end );
1187 
1188     /* Send the new cursor position. */
1189     ipb.code = VI_MOUSE_MOVE;
1190     ipb.val1 = ypos;
1191     ipb.val2 = xpos;
1192     (void)vi_send(vi_ofd, "12", &ipb);
1193 
1194     /* click-click, and we go for words, lines, etc */
1195     if ( ev->time - last_click < multi_click_length )
1196           select_type = (enum select_enum) ((((int)select_type)+1)%3);
1197     else
1198           select_type = select_char;
1199     last_click = ev->time;
1200 
1201     /* put the selection here */
1202     selection_anchor          = Linear( __vi_screen, ypos, xpos );
1203     selection_start = selection_anchor;
1204     selection_end   = selection_anchor;
1205 
1206     /* expand to include words, line, etc */
1207     left_expand_selection( __vi_screen, &selection_start );
1208     right_expand_selection( __vi_screen, &selection_end );
1209 
1210     /* draw the new one */
1211     mark_selection( __vi_screen, selection_start, selection_end );
1212 
1213     /* and tell the window manager we own the selection */
1214     if ( select_type != select_char ) {
1215           __vi_AcquirePrimary( widget );
1216           copy_to_clipboard( __vi_screen );
1217     }
1218 }
1219 
1220 
1221 #if defined(__STDC__)
select_extend(Widget widget,XEvent * event,String str,Cardinal * cardinal)1222 static    void      select_extend( Widget widget,
1223                                      XEvent *event,
1224                                      String str,
1225                                      Cardinal *cardinal
1226                                      )
1227 #else
1228 static    void      select_extend( widget, event, str, cardinal )
1229 Widget              widget;
1230 XEvent              *event;
1231 String              str;
1232 Cardinal        *cardinal;
1233 #endif
1234 {
1235     int                       xpos, ypos, pos;
1236     XPointerMovedEvent        *ev = (XPointerMovedEvent *) event;
1237 
1238     /* NOTE:  when multiple panes are implemented, we need to find
1239      * the correct screen.  For now, there is only one.
1240      */
1241     xpos = COLUMN( __vi_screen, ev->x );
1242     ypos = ROW( __vi_screen, ev->y );
1243 
1244     /* deal with words, lines, etc */
1245     pos = Linear( __vi_screen, ypos, xpos );
1246     if ( pos < selection_anchor )
1247           left_expand_selection( __vi_screen, &pos );
1248     else
1249           right_expand_selection( __vi_screen, &pos );
1250 
1251     /* extend from before the start? */
1252     if ( pos < selection_start ) {
1253           mark_selection( __vi_screen, pos, selection_start-1 );
1254           selection_start = pos;
1255     }
1256 
1257     /* extend past the end? */
1258     else if ( pos > selection_end ) {
1259           mark_selection( __vi_screen, selection_end+1, pos );
1260           selection_end = pos;
1261     }
1262 
1263     /* between the anchor and the start? */
1264     else if ( pos < selection_anchor ) {
1265           erase_selection( __vi_screen, selection_start, pos-1 );
1266           selection_start = pos;
1267     }
1268 
1269     /* between the anchor and the end? */
1270     else {
1271           erase_selection( __vi_screen, pos+1, selection_end );
1272           selection_end = pos;
1273     }
1274 
1275     /* and tell the window manager we own the selection */
1276     __vi_AcquirePrimary( widget );
1277     copy_to_clipboard( __vi_screen );
1278 }
1279 
1280 
1281 #if defined(__STDC__)
select_paste(Widget widget,XEvent * event,String str,Cardinal * cardinal)1282 static    void      select_paste( Widget widget,
1283                                     XEvent *event,
1284                                     String str,
1285                                     Cardinal *cardinal
1286                                     )
1287 #else
1288 static    void      select_paste( widget, event, str, cardinal )
1289 Widget              widget;
1290 XEvent              *event;
1291 String              str;
1292 Cardinal        *cardinal;
1293 #endif
1294 {
1295     __vi_PasteFromClipboard( widget );
1296 }
1297 
1298 
1299 /* Interface to copy and paste
1300  * (a) callbacks from the window manager
1301  *        f_copy    -         it wants our buffer
1302  *        f_paste   -         it wants us to paste some text
1303  *        f_clear   -         we've lost the selection, clear it
1304  */
1305 
1306 #if defined(__STDC__)
f_copy(String * buffer,int * len)1307 static    void      f_copy( String *buffer, int *len )
1308 #else
1309 static    void      f_copy( buffer, len )
1310           String    *buffer;
1311           int       *len;
1312 #endif
1313 {
1314 #ifdef TRACE
1315     vtrace("f_copy() called");
1316 #endif
1317     *buffer         = clipboard;
1318     *len  = clipboard_length;
1319 }
1320 
1321 
1322 
f_paste(int widget,int buffer,int length)1323 static    void      f_paste(int widget, int buffer, int length)
1324 {
1325     /* NOTE:  when multiple panes are implemented, we need to find
1326      * the correct screen.  For now, there is only one.
1327      */
1328 #ifdef TRACE
1329     vtrace("f_paste() called with '%*.*s'\n", length, length, buffer);
1330 #endif
1331 }
1332 
1333 
1334 #if defined(__STDC__)
f_clear(Widget widget)1335 static    void      f_clear( Widget widget )
1336 #else
1337 static    void      f_clear( widget )
1338 Widget    widget;
1339 #endif
1340 {
1341     xvi_screen      *cur_screen;
1342 
1343 #ifdef TRACE
1344     vtrace("f_clear() called");
1345 #endif
1346 
1347     XtVaGetValues( widget, XmNuserData, &cur_screen, 0 );
1348 
1349     erase_selection( cur_screen, selection_start, selection_end );
1350 }
1351 
1352 
1353 /*
1354  * These routines deal with the cursor.
1355  *
1356  * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int));
1357  */
1358 void
__vi_set_cursor(xvi_screen * cur_screen,int is_busy)1359 __vi_set_cursor(xvi_screen *cur_screen, int is_busy)
1360 {
1361     XDefineCursor( XtDisplay(cur_screen->area),
1362                        XtWindow(cur_screen->area),
1363                        (is_busy) ? busy_cursor : std_cursor
1364                        );
1365 }
1366 
1367 
1368 
1369 /* hooks for the tags widget */
1370 
1371 static    String    cur_word = NULL;
1372 
1373 /*
1374  * PUBLIC: void __vi_set_word_at_caret __P((xvi_screen *));
1375  */
1376 void
__vi_set_word_at_caret(xvi_screen * this_screen)1377 __vi_set_word_at_caret(xvi_screen *this_screen)
1378 {
1379     char  *start, *end, save;
1380     int             newx, newy;
1381 
1382     newx = this_screen->curx;
1383     newy = this_screen->cury;
1384 
1385     /* Note that this really ought to be done by core due to wrapping issues */
1386     for ( end = start = CharAt( this_screen, newy, newx );
1387             (isalnum( (unsigned char)*end ) || *end == '_') && (newx < this_screen->cols);
1388             end++, newx++
1389             );
1390     save = *end;
1391     *end = '\0';
1392     if ( cur_word != NULL ) free( cur_word );
1393     cur_word = strdup( start );
1394     *end = save;
1395 
1396     /* if the tag stack widget is active, set the text field there
1397      * to agree with the current caret position.
1398      */
1399     __vi_set_tag_text( cur_word );
1400 }
1401 
1402 
__vi_get_word_at_caret(xvi_screen * this_screen)1403 String    __vi_get_word_at_caret(xvi_screen *this_screen)
1404 {
1405     return (cur_word) ? cur_word : "";
1406 }
1407 
1408 
1409 /*
1410  * These routines deal with the caret.
1411  *
1412  * PUBLIC: void draw_caret __P((xvi_screen *));
1413  */
1414 static void
draw_caret(xvi_screen * this_screen)1415 draw_caret(xvi_screen *this_screen)
1416 {
1417     /* draw the caret by drawing the text in highlight color */
1418     *FlagAt( this_screen, this_screen->cury, this_screen->curx ) |= COLOR_CARET;
1419     __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1420 }
1421 
1422 /*
1423  * PUBLIC: void __vi_erase_caret __P((xvi_screen *));
1424  */
1425 void
__vi_erase_caret(xvi_screen * this_screen)1426 __vi_erase_caret(xvi_screen *this_screen)
1427 {
1428     /* erase the caret by drawing the text in normal video */
1429     *FlagAt( this_screen, this_screen->cury, this_screen->curx ) &= ~COLOR_CARET;
1430     __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1431 }
1432 
1433 /*
1434  * PUBLIC: void     __vi_move_caret __P((xvi_screen *, int, int));
1435  */
1436 void
__vi_move_caret(xvi_screen * this_screen,int newy,int newx)1437 __vi_move_caret(xvi_screen *this_screen, int newy, int newx)
1438 {
1439     /* remove the old caret */
1440     __vi_erase_caret( this_screen );
1441 
1442     /* caret is now here */
1443     this_screen->curx = newx;
1444     this_screen->cury = newy;
1445     draw_caret( this_screen );
1446 }
1447