common/log.c: minor whitespace change
[nvi.git] / motif_l / m_vi.c
blobf20c00fa3a6ac9919b5d9848bb6c464a0db8aca9
1 /*-
2 * Copyright (c) 1996
3 * Rob Zimmermann. All rights reserved.
4 * Copyright (c) 1996
5 * Keith Bostic. All rights reserved.
7 * See the LICENSE file for redistribution information.
8 */
10 #include "config.h"
12 #ifndef lint
13 static const char sccsid[] = "$Id: m_vi.c,v 8.41 2003/11/05 17:10:01 skimo Exp $ (Berkeley) $Date: 2003/11/05 17:10:01 $";
14 #endif /* not lint */
16 #include <sys/types.h>
17 #include <sys/queue.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/StringDefs.h>
21 #include <X11/cursorfont.h>
22 #include <Xm/PanedW.h>
23 #include <Xm/DrawingA.h>
24 #include <Xm/Form.h>
25 #include <Xm/Frame.h>
26 #include <Xm/ScrollBar.h>
28 #include <bitstring.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
38 #undef LOCK_SUCCESS
39 #include "../common/common.h"
40 #include "../ipc/ip.h"
41 #include "m_motif.h"
42 #include "vi_mextern.h"
43 #include "pathnames.h"
45 extern int vi_ofd;
47 static void f_copy(String *buffer, int *len);
48 static void f_paste(int widget, int buffer, int length);
49 static void f_clear(Widget widget);
53 * Globals and costants
56 #define BufferSize 1024
58 static XFontStruct *font;
59 static GC gc;
60 GC __vi_copy_gc;
61 static XtAppContext ctx;
63 xvi_screen *__vi_screen = NULL;
64 static Cursor std_cursor;
65 static Cursor busy_cursor;
66 static XtTranslations area_trans;
67 static int multi_click_length;
69 void (*__vi_exitp)(); /* Exit function. */
72 /* hack for drag scrolling...
73 * I'm not sure why, but the current protocol gets out of sync when
74 * a lot of drag messages get passed around. Likely, we need to wait
75 * for core to finish repainting the screen before sending more drag
76 * messages.
77 * To that end, we set scroll_block when we receive input from the scrollbar,
78 * and we clear it when we process the IPO_REFRESH message from core.
79 * A specific SCROLL_COMPLETED message would be better, but this seems to work.
82 static Boolean scroll_block = False;
85 * PUBLIC: void __vi_set_scroll_block __P((void));
87 void
88 __vi_set_scroll_block(void)
90 scroll_block = True;
94 * PUBLIC: void __vi_clear_scroll_block __P((void));
96 void
97 __vi_clear_scroll_block(void)
99 scroll_block = False;
103 #if defined(__STDC__)
104 static void set_gc_colors( xvi_screen *this_screen, int val )
105 #else
106 static void set_gc_colors( this_screen, val )
107 xvi_screen *this_screen;
108 int val;
109 #endif
111 static Pixel fg, bg, hi, shade;
112 static int prev = COLOR_INVALID;
114 /* no change? */
115 if ( prev == val ) return;
117 /* init? */
118 if ( gc == NULL ) {
120 /* what colors are selected for the drawing area? */
121 XtVaGetValues( this_screen->area,
122 XtNbackground, &bg,
123 XtNforeground, &fg,
124 XmNhighlightColor, &hi,
125 XmNtopShadowColor, &shade,
129 gc = XCreateGC( XtDisplay(this_screen->area),
130 DefaultRootWindow(XtDisplay(this_screen->area)),
135 XSetFont( XtDisplay(this_screen->area), gc, font->fid );
138 /* special colors? */
139 if ( val & COLOR_CARET ) {
140 XSetForeground( XtDisplay(this_screen->area), gc, fg );
141 XSetBackground( XtDisplay(this_screen->area), gc, hi );
143 else if ( val & COLOR_SELECT ) {
144 XSetForeground( XtDisplay(this_screen->area), gc, fg );
145 XSetBackground( XtDisplay(this_screen->area), gc, shade );
147 else switch (val) {
148 case COLOR_STANDARD:
149 XSetForeground( XtDisplay(this_screen->area), gc, fg );
150 XSetBackground( XtDisplay(this_screen->area), gc, bg );
151 break;
152 case COLOR_INVERSE:
153 XSetForeground( XtDisplay(this_screen->area), gc, bg );
154 XSetBackground( XtDisplay(this_screen->area), gc, fg );
155 break;
156 default: /* implement color map later */
157 break;
163 * Memory utilities
166 #ifdef REALLOC
167 #undef REALLOC
168 #endif
170 #define REALLOC( ptr, size ) \
171 ((ptr == NULL) ? malloc(size) : realloc(ptr,size))
174 /* X windows routines.
175 * We currently create a single, top-level shell. In that is a
176 * single drawing area into which we will draw text. This allows
177 * us to put multi-color (and font, but we'll never build that) text
178 * into the drawing area. In the future, we'll add scrollbars to the
179 * drawing areas
182 void select_start();
183 void select_extend();
184 void select_paste();
185 void key_press();
186 void insert_string();
187 void beep __P((Widget w));
188 void find();
189 void command();
191 static XtActionsRec area_actions[] = {
192 { "select_start", select_start },
193 { "select_extend", select_extend },
194 { "select_paste", select_paste },
195 { "key_press", key_press },
196 { "insert_string", insert_string },
197 { "beep", beep },
198 { "find", find },
199 { "command", command },
202 char areaTrans[] =
203 "<Btn1Down>: select_start() \n\
204 <Btn1Motion>: select_extend() \n\
205 <Btn2Down>: select_paste() \n\
206 <Btn3Down>: select_extend() \n\
207 <Btn3Motion>: select_extend() \n\
208 <Key>End: command(VI_C_BOTTOM) \n\
209 <Key>Escape: command(EINSERT) \n\
210 <Key>Find: find() \n\
211 <Key>Home: command(VI_C_TOP) \n\
212 <Key>Next: command(VI_C_PGDOWN) \n\
213 <Key>Prior: command(VI_C_PGUP) \n\
214 <Key>osfBackSpace: command(VI_C_LEFT) \n\
215 <Key>osfBeginLine: command(VI_C_BOL) \n\
216 <Key>osfCopy: beep() \n\
217 <Key>osfCut: beep() \n\
218 <Key>osfDelete: command(VI_C_DEL) \n\
219 <Key>osfDown: command(VI_C_DOWN) \n\
220 <Key>osfEndLine: command(VI_C_EOL) \n\
221 <Key>osfInsert: command(VI_C_INSERT) \n\
222 <Key>osfLeft: command(VI_C_LEFT) \n\
223 <Key>osfPageDown: command(VI_C_PGDOWN) \n\
224 <Key>osfPageUp: command(VI_C_PGUP) \n\
225 <Key>osfPaste: insert_string(p) \n\
226 <Key>osfRight: command(VI_C_RIGHT) \n\
227 <Key>osfUndo: command(VI_UNDO) \n\
228 <Key>osfUp: command(VI_C_UP) \n\
229 Ctrl<Key>C: command(VI_INTERRUPT) \n\
230 <Key>: key_press()";
233 static XutResource resource[] = {
234 { "font", XutRKfont, &font },
235 { "pointerShape", XutRKcursor, &std_cursor },
236 { "busyShape", XutRKcursor, &busy_cursor },
241 * vi_input_func --
242 * We've received input on the pipe from vi.
244 * PUBLIC: void vi_input_func __P((XtPointer, int *, XtInputId *));
246 void
247 vi_input_func(XtPointer client_data, int *source, XtInputId *id)
249 /* Parse and dispatch on commands in the queue. */
250 (void)ipvi_motif->input(ipvi_motif, *source);
252 #ifdef notdef
253 /* Check the pipe for unused events when not busy. */
254 XtAppAddWorkProc(ctx, process_pipe_input, NULL);
255 #endif
260 /* Send the window size. */
261 #if defined(__STDC__)
262 static void send_resize( xvi_screen *this_screen )
263 #else
264 static void send_resize( this_screen )
265 xvi_screen *this_screen;
266 #endif
268 IP_BUF ipb;
270 ipb.val1 = this_screen->rows;
271 ipb.val2 = this_screen->cols;
272 ipb.code = VI_RESIZE;
274 #ifdef TRACE
275 vtrace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols);
276 #endif
278 /* send up the pipe */
279 vi_send(vi_ofd, "12", &ipb);
283 #if defined(__STDC__)
284 static void resize_backing_store( xvi_screen *this_screen )
285 #else
286 static void resize_backing_store( this_screen )
287 xvi_screen *this_screen;
288 #endif
290 int total_chars = this_screen->rows * this_screen->cols;
292 this_screen->characters = REALLOC( this_screen->characters,
293 total_chars
295 memset( this_screen->characters, ' ', total_chars );
297 this_screen->flags = REALLOC( this_screen->flags,
298 total_chars
300 memset( this_screen->flags, 0, total_chars );
305 /* X will call this when we are resized */
306 #if defined(__STDC__)
307 static void resize_func( Widget wid,
308 XtPointer client_data,
309 XtPointer call_data
311 #else
312 static void resize_func( wid, client_data, call_data )
313 Widget wid;
314 XtPointer client_data;
315 XtPointer call_data;
316 #endif
318 xvi_screen *this_screen = (xvi_screen *) client_data;
319 Dimension height, width;
321 XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 );
323 /* generate correct sizes when we have font metrics implemented */
324 this_screen->cols = width / this_screen->ch_width;
325 this_screen->rows = height / this_screen->ch_height;
327 resize_backing_store( this_screen );
328 send_resize( this_screen );
333 * __vi_draw_text --
334 * Draw from backing store.
336 * PUBLIC: void __vi_draw_text __P((xvi_screen *, int, int, int));
338 void
339 __vi_draw_text(xvi_screen *this_screen, int row, int start_col, int len)
341 int col, color, xpos;
342 char *start, *end;
344 start = CharAt( __vi_screen, row, start_col );
345 color = *FlagAt( __vi_screen, row, start_col );
346 xpos = XPOS( __vi_screen, start_col );
348 /* one column at a time */
349 for ( col=start_col;
350 col<this_screen->cols && col<start_col+len;
351 col++ ) {
353 /* has the color changed? */
354 if ( *FlagAt( __vi_screen, row, col ) == color )
355 continue;
357 /* is there anything to write? */
358 end = CharAt( __vi_screen, row, col );
359 if ( end == start )
360 continue;
362 /* yes. write in the previous color */
363 set_gc_colors( __vi_screen, color );
365 /* add to display */
366 XDrawImageString( XtDisplay(__vi_screen->area),
367 XtWindow(__vi_screen->area),
369 xpos,
370 YPOS( __vi_screen, row ),
371 start,
372 end - start
375 /* this is the new context */
376 color = *FlagAt( __vi_screen, row, col );
377 xpos = XPOS( __vi_screen, col );
378 start = end;
381 /* is there anything to write? */
382 end = CharAt( __vi_screen, row, col );
383 if ( end != start ) {
384 /* yes. write in the previous color */
385 set_gc_colors( __vi_screen, color );
387 /* add to display */
388 XDrawImageString( XtDisplay(__vi_screen->area),
389 XtWindow(__vi_screen->area),
391 xpos,
392 YPOS( __vi_screen, row ),
393 start,
394 end - start
400 /* set clipping rectangles accordingly */
401 #if defined(__STDC__)
402 static void add_to_clip( xvi_screen *cur_screen, int x, int y, int width, int height )
403 #else
404 static void add_to_clip( cur_screen, x, y, width, height )
405 xvi_screen *cur_screen;
406 int x;
407 int y;
408 int width;
409 int height;
410 #endif
412 XRectangle rect;
413 rect.x = x;
414 rect.y = y;
415 rect.height = height;
416 rect.width = width;
417 if ( cur_screen->clip == NULL )
418 cur_screen->clip = XCreateRegion();
419 XUnionRectWithRegion( &rect, cur_screen->clip, cur_screen->clip );
424 * __vi_expose_func --
425 * Redraw the window's contents.
427 * NOTE: When vi wants to force a redraw, we are called with NULL widget
428 * and call_data.
430 * PUBLIC: void __vi_expose_func __P((Widget, XtPointer, XtPointer));
432 void
433 __vi_expose_func(Widget wid, XtPointer client_data, XtPointer call_data)
435 xvi_screen *this_screen;
436 XmDrawingAreaCallbackStruct *cbs;
437 XExposeEvent *xev;
438 XGraphicsExposeEvent *gev;
439 int row;
441 /* convert pointers */
442 this_screen = (xvi_screen *) client_data;
443 cbs = (XmDrawingAreaCallbackStruct *) call_data;
445 /* first exposure? tell vi we are ready... */
446 if ( this_screen->init == False ) {
448 /* what does the user want to see? */
449 __vi_set_cursor( __vi_screen, False );
451 /* vi wants a resize as the first event */
452 send_resize( __vi_screen );
454 /* fine for now. we'll be back */
455 this_screen->init = True;
456 return;
459 if ( call_data == NULL ) {
461 /* vi core calls this when it wants a full refresh */
462 #ifdef TRACE
463 vtrace("expose_func: full refresh\n");
464 #endif
466 XClearWindow( XtDisplay(this_screen->area),
467 XtWindow(this_screen->area)
470 else {
471 switch ( cbs->event->type ) {
473 case GraphicsExpose:
474 gev = (XGraphicsExposeEvent *) cbs->event;
476 /* set clipping rectangles accordingly */
477 add_to_clip( this_screen,
478 gev->x, gev->y,
479 gev->width, gev->height
482 /* X calls here when XCopyArea exposes new bits */
483 #ifdef TRACE
484 vtrace("expose_func (X): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
485 gev->x, gev->y,
486 gev->width, gev->height,
487 gev->count);
488 #endif
490 /* more coming? do it then */
491 if ( gev->count > 0 ) return;
493 /* set clipping region */
494 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
495 break;
497 case Expose:
498 xev = (XExposeEvent *) cbs->event;
500 /* set clipping rectangles accordingly */
501 add_to_clip( this_screen,
502 xev->x, xev->y,
503 xev->width, xev->height
506 /* Motif calls here when DrawingArea is exposed */
507 #ifdef TRACE
508 vtrace("expose_func (Motif): (x=%d,y=%d,w=%d,h=%d), count=%d\n",
509 xev->x, xev->y,
510 xev->width, xev->height,
511 xev->count);
512 #endif
514 /* more coming? do it then */
515 if ( xev->count > 0 ) return;
517 /* set clipping region */
518 XSetRegion( XtDisplay(wid), gc, this_screen->clip );
519 break;
521 default:
522 /* don't care? */
523 return;
527 /* one row at a time */
528 for (row=0; row<this_screen->rows; row++) {
530 /* draw from the backing store */
531 __vi_draw_text( this_screen, row, 0, this_screen->cols );
534 /* clear clipping region */
535 XSetClipMask( XtDisplay(this_screen->area), gc, None );
536 if ( this_screen->clip != NULL ) {
537 XDestroyRegion( this_screen->clip );
538 this_screen->clip = NULL;
544 #if defined(__STDC__)
545 static void xexpose ( Widget w,
546 XtPointer client_data,
547 XEvent *ev,
548 Boolean *cont
550 #else
551 static void xexpose ( w, client_data, ev, cont )
552 Widget w;
553 XtPointer client_data;
554 XEvent *ev;
555 Boolean *cont;
556 #endif
558 XmDrawingAreaCallbackStruct cbs;
560 switch ( ev->type ) {
561 case GraphicsExpose:
562 cbs.event = ev;
563 cbs.window = XtWindow(w);
564 cbs.reason = XmCR_EXPOSE;
565 __vi_expose_func( w, client_data, (XtPointer) &cbs );
566 *cont = False; /* we took care of it */
567 break;
568 default:
569 /* don't care */
570 break;
575 /* unimplemented keystroke or command */
576 #if defined(__STDC__)
577 static void beep( Widget w )
578 #else
579 static void beep( w )
580 Widget w;
581 #endif
583 XBell(XtDisplay(w),0);
587 /* give me a search dialog */
588 #if defined(__STDC__)
589 static void find( Widget w )
590 #else
591 static void find( w )
592 Widget w;
593 #endif
595 __vi_show_search_dialog( w, "Find" );
599 * command --
600 * Translate simple keyboard input into vi protocol commands.
602 static void
603 command(Widget widget, XKeyEvent *event, String *str, Cardinal *cardinal)
605 static struct {
606 String name;
607 int code;
608 int count;
609 } table[] = {
610 { "VI_C_BOL", VI_C_BOL, 0 },
611 { "VI_C_BOTTOM", VI_C_BOTTOM, 0 },
612 { "VI_C_DEL", VI_C_DEL, 0 },
613 { "VI_C_DOWN", VI_C_DOWN, 1 },
614 { "VI_C_EOL", VI_C_EOL, 0 },
615 { "VI_C_INSERT", VI_C_INSERT, 0 },
616 { "VI_C_LEFT", VI_C_LEFT, 0 },
617 { "VI_C_PGDOWN", VI_C_PGDOWN, 1 },
618 { "VI_C_PGUP", VI_C_PGUP, 1 },
619 { "VI_C_RIGHT", VI_C_RIGHT, 0 },
620 { "VI_C_TOP", VI_C_TOP, 0 },
621 { "VI_C_UP", VI_C_UP, 1 },
622 { "VI_INTERRUPT", VI_INTERRUPT, 0 },
624 IP_BUF ipb;
625 int i;
628 * XXX
629 * Do fast lookup based on character #6 -- sleazy, but I don't
630 * want to do 10 strcmp's per keystroke.
632 ipb.val1 = 1;
633 for (i = 0; i < XtNumber(table); i++)
634 if (table[i].name[6] == (*str)[6] &&
635 strcmp(table[i].name, *str) == 0) {
636 ipb.code = table[i].code;
637 vi_send(vi_ofd, table[i].count ? "1" : NULL, &ipb);
638 return;
641 /* oops. */
642 beep(widget);
645 /* mouse or keyboard input. */
646 #if defined(__STDC__)
647 static void insert_string( Widget widget,
648 XKeyEvent *event,
649 String *str,
650 Cardinal *cardinal
652 #else
653 static void insert_string( widget, event, str, cardinal )
654 Widget widget;
655 XKeyEvent *event;
656 String *str;
657 Cardinal *cardinal;
658 #endif
660 IP_BUF ipb;
662 ipb.len1 = strlen( *str );
663 if ( ipb.len1 != 0 ) {
664 ipb.code = VI_STRING;
665 ipb.str1 = *str;
666 vi_send(vi_ofd, "a", &ipb);
669 #ifdef TRACE
670 vtrace("insert_string {%.*s}\n", strlen( *str ), *str );
671 #endif
675 /* mouse or keyboard input. */
676 #if defined(__STDC__)
677 static void key_press( Widget widget,
678 XKeyEvent *event,
679 String str,
680 Cardinal *cardinal
682 #else
683 static void key_press( widget, event, str, cardinal )
684 Widget widget;
685 XKeyEvent *event;
686 String str;
687 Cardinal *cardinal;
688 #endif
690 IP_BUF ipb;
691 char bp[BufferSize];
693 ipb.len1 = XLookupString( event, bp, BufferSize, NULL, NULL );
694 if ( ipb.len1 != 0 ) {
695 ipb.code = VI_STRING;
696 ipb.str1 = bp;
697 #ifdef TRACE
698 vtrace("key_press {%.*s}\n", ipb.len1, bp );
699 #endif
700 vi_send(vi_ofd, "a", &ipb);
706 #if defined(__STDC__)
707 static void scrollbar_moved( Widget widget,
708 XtPointer ptr,
709 XmScrollBarCallbackStruct *cbs
711 #else
712 static void scrollbar_moved( widget, ptr, cbs )
713 Widget widget;
714 XtPointer ptr;
715 XmScrollBarCallbackStruct *cbs;
716 #endif
718 /* Future: Need to scroll the correct screen! */
719 xvi_screen *cur_screen = (xvi_screen *) ptr;
720 IP_BUF ipb;
722 /* if we are still processing messages from core, skip this event
723 * (see comments near __vi_set_scroll_block())
725 if ( scroll_block ) {
726 return;
728 __vi_set_scroll_block();
730 #ifdef TRACE
731 switch ( cbs->reason ) {
732 case XmCR_VALUE_CHANGED:
733 vtrace( "scrollbar VALUE_CHANGED %d\n", cbs->value );
734 break;
735 case XmCR_DRAG:
736 vtrace( "scrollbar DRAG %d\n", cbs->value );
737 break;
738 default:
739 vtrace( "scrollbar <default> %d\n", cbs->value );
740 break;
742 vtrace("scrollto {%d}\n", cbs->value );
743 #endif
745 /* Send the new cursor position. */
746 ipb.code = VI_C_SETTOP;
747 ipb.val1 = cbs->value;
748 (void)vi_send(vi_ofd, "1", &ipb);
752 #if defined(__STDC__)
753 static xvi_screen *create_screen( Widget parent, int rows, int cols )
754 #else
755 static xvi_screen *create_screen( parent, rows, cols )
756 Widget parent;
757 int rows, cols;
758 #endif
760 xvi_screen *new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) );
761 Widget frame;
763 /* init... */
764 new_screen->color = COLOR_STANDARD;
765 new_screen->parent = parent;
767 /* figure out the sizes */
768 new_screen->rows = rows;
769 new_screen->cols = cols;
770 new_screen->ch_width = font->max_bounds.width;
771 new_screen->ch_height = font->descent + font->ascent;
772 new_screen->ch_descent = font->descent;
773 new_screen->clip = NULL;
775 /* allocate and init the backing stores */
776 resize_backing_store( new_screen );
778 /* set up a translation table for the X toolkit */
779 if ( area_trans == NULL )
780 area_trans = XtParseTranslationTable(areaTrans);
782 /* future, new screen gets inserted into the parent sash
783 * immediately after the current screen. Default Pane action is
784 * to add it to the end
787 /* use a form to hold the drawing area and the scrollbar */
788 new_screen->form = XtVaCreateManagedWidget( "form",
789 xmFormWidgetClass,
790 parent,
791 XmNpaneMinimum, 2*new_screen->ch_height,
792 XmNallowResize, True,
793 NULL
796 /* create a scrollbar. */
797 new_screen->scroll = XtVaCreateManagedWidget( "scroll",
798 xmScrollBarWidgetClass,
799 new_screen->form,
800 XmNtopAttachment, XmATTACH_FORM,
801 XmNbottomAttachment, XmATTACH_FORM,
802 XmNrightAttachment, XmATTACH_FORM,
803 XmNminimum, 1,
804 XmNmaximum, 2,
805 XmNsliderSize, 1,
806 NULL
808 XtAddCallback( new_screen->scroll,
809 XmNvalueChangedCallback,
810 scrollbar_moved,
811 new_screen
813 XtAddCallback( new_screen->scroll,
814 XmNdragCallback,
815 scrollbar_moved,
816 new_screen
819 /* create a frame because they look nice */
820 frame = XtVaCreateManagedWidget( "frame",
821 xmFrameWidgetClass,
822 new_screen->form,
823 XmNshadowType, XmSHADOW_ETCHED_IN,
824 XmNtopAttachment, XmATTACH_FORM,
825 XmNbottomAttachment, XmATTACH_FORM,
826 XmNleftAttachment, XmATTACH_FORM,
827 XmNrightAttachment, XmATTACH_WIDGET,
828 XmNrightWidget, new_screen->scroll,
829 NULL
832 /* create a drawing area into which we will put text */
833 new_screen->area = XtVaCreateManagedWidget( "screen",
834 xmDrawingAreaWidgetClass,
835 frame,
836 XmNheight, new_screen->ch_height * new_screen->rows,
837 XmNwidth, new_screen->ch_width * new_screen->cols,
838 XmNtranslations, area_trans,
839 XmNuserData, new_screen,
840 XmNnavigationType, XmNONE,
841 XmNtraversalOn, False,
842 NULL
845 /* this callback is for when the drawing area is resized */
846 XtAddCallback( new_screen->area,
847 XmNresizeCallback,
848 resize_func,
849 new_screen
852 /* this callback is for when the drawing area is exposed */
853 XtAddCallback( new_screen->area,
854 XmNexposeCallback,
855 __vi_expose_func,
856 new_screen
859 /* this callback is for when we expose obscured bits
860 * (e.g. there is a window over part of our drawing area
862 XtAddEventHandler( new_screen->area,
863 0, /* no standard events */
864 True, /* we *WANT* GraphicsExpose */
865 xexpose, /* what to do */
866 new_screen
869 return new_screen;
873 static xvi_screen *split_screen(void)
875 Cardinal num;
876 WidgetList c;
877 int rows = __vi_screen->rows / 2;
878 xvi_screen *new_screen;
880 /* Note that (global) cur_screen needs to be correctly set so that
881 * insert_here knows which screen to put the new one after
883 new_screen = create_screen( __vi_screen->parent,
884 rows,
885 __vi_screen->cols
888 /* what are the screens? */
889 XtVaGetValues( __vi_screen->parent,
890 XmNnumChildren, &num,
891 XmNchildren, &c,
892 NULL
895 /* unmanage all children in preparation for resizing */
896 XtUnmanageChildren( c, num );
898 /* force resize of the affected screens */
899 XtVaSetValues( new_screen->form,
900 XmNheight, new_screen->ch_height * rows,
901 NULL
903 XtVaSetValues( __vi_screen->form,
904 XmNheight, __vi_screen->ch_height * rows,
905 NULL
908 /* re-manage */
909 XtManageChildren( c, num );
911 /* done */
912 return new_screen;
916 /* Tell me where to insert the next subpane */
917 #if defined(__STDC__)
918 static Cardinal insert_here( Widget wid )
919 #else
920 static Cardinal insert_here( wid )
921 Widget wid;
922 #endif
924 Cardinal i, num;
925 WidgetList c;
927 XtVaGetValues( XtParent(wid),
928 XmNnumChildren, &num,
929 XmNchildren, &c,
930 NULL
933 /* The default XmNinsertPosition procedure for PanedWindow
934 * causes sashes to be inserted at the end of the list of children
935 * and causes non-sash widgets to be inserted after other
936 * non-sash children but before any sashes.
938 if ( ! XmIsForm( wid ) )
939 return num;
941 /* We will put the widget after the one with the current screen */
942 for (i=0; i<num && XmIsForm(c[i]); i++) {
943 if ( __vi_screen == NULL || __vi_screen->form == c[i] )
944 return i+1; /* after the i-th */
947 /* could not find it? this should never happen */
948 return num;
953 * vi_create_editor --
954 * Create the necessary widgetry.
956 * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void)));
958 Widget
959 vi_create_editor(String name, Widget parent, void (*exitp) (void))
961 Widget pane_w;
962 Display *display = XtDisplay( parent );
964 __vi_exitp = exitp;
966 /* first time through? */
967 if ( ctx == NULL ) {
969 /* save this for later */
970 ctx = XtWidgetToApplicationContext( parent );
972 /* add our own special actions */
973 XtAppAddActions( ctx, area_actions, XtNumber(area_actions) );
975 /* how long is double-click? */
976 multi_click_length = XtGetMultiClickTime( display );
978 /* check the resource database for interesting resources */
979 __XutConvertResources( parent,
980 vi_progname,
981 resource,
982 XtNumber(resource)
985 /* we need a context for moving bits around in the windows */
986 __vi_copy_gc = XCreateGC( display,
987 DefaultRootWindow(display),
992 /* routines for inter client communications conventions */
993 __vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf );
996 /* create the paned window */
997 pane_w = XtVaCreateManagedWidget( "pane",
998 xmPanedWindowWidgetClass,
999 parent,
1000 XmNinsertPosition, insert_here,
1001 NULL
1004 /* allocate our data structure. in the future we will have several
1005 * screens running around at the same time
1007 __vi_screen = create_screen( pane_w, 24, 80 );
1009 /* force creation of our color text context */
1010 set_gc_colors( __vi_screen, COLOR_STANDARD );
1012 /* done */
1013 return pane_w;
1017 /* These routines deal with the selection buffer */
1019 static int selection_start, selection_end, selection_anchor;
1020 static enum select_enum {
1021 select_char, select_word, select_line
1022 } select_type = select_char;
1023 static int last_click;
1025 static char *clipboard = NULL;
1026 static int clipboard_size = 0,
1027 clipboard_length;
1030 #if defined(__STDC__)
1031 static void copy_to_clipboard( xvi_screen *cur_screen )
1032 #else
1033 static void copy_to_clipboard( cur_screen )
1034 xvi_screen *cur_screen;
1035 #endif
1037 /* for now, copy from the backing store. in the future,
1038 * vi core will tell us exactly what the selection buffer contains
1040 clipboard_length = 1 + selection_end - selection_start;
1042 if ( clipboard == NULL )
1043 clipboard = (char *) malloc( clipboard_length );
1044 else if ( clipboard_size < clipboard_length )
1045 clipboard = (char *) realloc( clipboard, clipboard_length );
1047 memcpy( clipboard,
1048 cur_screen->characters + selection_start,
1049 clipboard_length
1054 #if defined(__STDC__)
1055 static void mark_selection( xvi_screen *cur_screen, int start, int end )
1056 #else
1057 static void mark_selection( cur_screen, start, end )
1058 xvi_screen *cur_screen;
1059 int start;
1060 int end;
1061 #endif
1063 int row, col, i;
1065 for ( i=start; i<=end; i++ ) {
1066 if ( !( cur_screen->flags[i] & COLOR_SELECT ) ) {
1067 cur_screen->flags[i] |= COLOR_SELECT;
1068 ToRowCol( cur_screen, i, row, col );
1069 __vi_draw_text( cur_screen, row, col, 1 );
1075 #if defined(__STDC__)
1076 static void erase_selection( xvi_screen *cur_screen, int start, int end )
1077 #else
1078 static void erase_selection( cur_screen, start, end )
1079 xvi_screen *cur_screen;
1080 int start;
1081 int end;
1082 #endif
1084 int row, col, i;
1086 for ( i=start; i<=end; i++ ) {
1087 if ( cur_screen->flags[i] & COLOR_SELECT ) {
1088 cur_screen->flags[i] &= ~COLOR_SELECT;
1089 ToRowCol( cur_screen, i, row, col );
1090 __vi_draw_text( cur_screen, row, col, 1 );
1096 #if defined(__STDC__)
1097 static void left_expand_selection( xvi_screen *cur_screen, int *start )
1098 #else
1099 static void left_expand_selection( cur_screen, start )
1100 xvi_screen *cur_screen;
1101 int *start;
1102 #endif
1104 int row, col;
1106 switch ( select_type ) {
1107 case select_word:
1108 if ( *start == 0 || isspace( cur_screen->characters[*start] ) )
1109 return;
1110 for (;;) {
1111 if ( isspace( cur_screen->characters[*start-1] ) )
1112 return;
1113 if ( --(*start) == 0 )
1114 return;
1116 case select_line:
1117 ToRowCol( cur_screen, *start, row, col );
1118 col = 0;
1119 *start = Linear( cur_screen, row, col );
1120 break;
1125 #if defined(__STDC__)
1126 static void right_expand_selection( xvi_screen *cur_screen, int *end )
1127 #else
1128 static void right_expand_selection( cur_screen, end )
1129 xvi_screen *cur_screen;
1130 int *end;
1131 #endif
1133 int row, col, last = cur_screen->cols * cur_screen->rows - 1;
1135 switch ( select_type ) {
1136 case select_word:
1137 if ( *end == last || isspace( cur_screen->characters[*end] ) )
1138 return;
1139 for (;;) {
1140 if ( isspace( cur_screen->characters[*end+1] ) )
1141 return;
1142 if ( ++(*end) == last )
1143 return;
1145 case select_line:
1146 ToRowCol( cur_screen, *end, row, col );
1147 col = cur_screen->cols -1;
1148 *end = Linear( cur_screen, row, col );
1149 break;
1154 #if defined(__STDC__)
1155 static void select_start( Widget widget,
1156 XEvent *event,
1157 String str,
1158 Cardinal *cardinal
1160 #else
1161 static void select_start( widget, event, str, cardinal )
1162 Widget widget;
1163 XEvent *event;
1164 String str;
1165 Cardinal *cardinal;
1166 #endif
1168 IP_BUF ipb;
1169 int xpos, ypos;
1170 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1171 static int last_click;
1174 * NOTE: when multiple panes are implemented, we need to find the correct
1175 * screen. For now, there is only one.
1177 xpos = COLUMN( __vi_screen, ev->x );
1178 ypos = ROW( __vi_screen, ev->y );
1180 /* Remove the old one. */
1181 erase_selection( __vi_screen, selection_start, selection_end );
1183 /* Send the new cursor position. */
1184 ipb.code = VI_MOUSE_MOVE;
1185 ipb.val1 = ypos;
1186 ipb.val2 = xpos;
1187 (void)vi_send(vi_ofd, "12", &ipb);
1189 /* click-click, and we go for words, lines, etc */
1190 if ( ev->time - last_click < multi_click_length )
1191 select_type = (enum select_enum) ((((int)select_type)+1)%3);
1192 else
1193 select_type = select_char;
1194 last_click = ev->time;
1196 /* put the selection here */
1197 selection_anchor = Linear( __vi_screen, ypos, xpos );
1198 selection_start = selection_anchor;
1199 selection_end = selection_anchor;
1201 /* expand to include words, line, etc */
1202 left_expand_selection( __vi_screen, &selection_start );
1203 right_expand_selection( __vi_screen, &selection_end );
1205 /* draw the new one */
1206 mark_selection( __vi_screen, selection_start, selection_end );
1208 /* and tell the window manager we own the selection */
1209 if ( select_type != select_char ) {
1210 __vi_AcquirePrimary( widget );
1211 copy_to_clipboard( __vi_screen );
1216 #if defined(__STDC__)
1217 static void select_extend( Widget widget,
1218 XEvent *event,
1219 String str,
1220 Cardinal *cardinal
1222 #else
1223 static void select_extend( widget, event, str, cardinal )
1224 Widget widget;
1225 XEvent *event;
1226 String str;
1227 Cardinal *cardinal;
1228 #endif
1230 int xpos, ypos, pos;
1231 XPointerMovedEvent *ev = (XPointerMovedEvent *) event;
1233 /* NOTE: when multiple panes are implemented, we need to find
1234 * the correct screen. For now, there is only one.
1236 xpos = COLUMN( __vi_screen, ev->x );
1237 ypos = ROW( __vi_screen, ev->y );
1239 /* deal with words, lines, etc */
1240 pos = Linear( __vi_screen, ypos, xpos );
1241 if ( pos < selection_anchor )
1242 left_expand_selection( __vi_screen, &pos );
1243 else
1244 right_expand_selection( __vi_screen, &pos );
1246 /* extend from before the start? */
1247 if ( pos < selection_start ) {
1248 mark_selection( __vi_screen, pos, selection_start-1 );
1249 selection_start = pos;
1252 /* extend past the end? */
1253 else if ( pos > selection_end ) {
1254 mark_selection( __vi_screen, selection_end+1, pos );
1255 selection_end = pos;
1258 /* between the anchor and the start? */
1259 else if ( pos < selection_anchor ) {
1260 erase_selection( __vi_screen, selection_start, pos-1 );
1261 selection_start = pos;
1264 /* between the anchor and the end? */
1265 else {
1266 erase_selection( __vi_screen, pos+1, selection_end );
1267 selection_end = pos;
1270 /* and tell the window manager we own the selection */
1271 __vi_AcquirePrimary( widget );
1272 copy_to_clipboard( __vi_screen );
1276 #if defined(__STDC__)
1277 static void select_paste( Widget widget,
1278 XEvent *event,
1279 String str,
1280 Cardinal *cardinal
1282 #else
1283 static void select_paste( widget, event, str, cardinal )
1284 Widget widget;
1285 XEvent *event;
1286 String str;
1287 Cardinal *cardinal;
1288 #endif
1290 __vi_PasteFromClipboard( widget );
1294 /* Interface to copy and paste
1295 * (a) callbacks from the window manager
1296 * f_copy - it wants our buffer
1297 * f_paste - it wants us to paste some text
1298 * f_clear - we've lost the selection, clear it
1301 #if defined(__STDC__)
1302 static void f_copy( String *buffer, int *len )
1303 #else
1304 static void f_copy( buffer, len )
1305 String *buffer;
1306 int *len;
1307 #endif
1309 #ifdef TRACE
1310 vtrace("f_copy() called");
1311 #endif
1312 *buffer = clipboard;
1313 *len = clipboard_length;
1318 static void f_paste(int widget, int buffer, int length)
1320 /* NOTE: when multiple panes are implemented, we need to find
1321 * the correct screen. For now, there is only one.
1323 #ifdef TRACE
1324 vtrace("f_paste() called with '%*.*s'\n", length, length, buffer);
1325 #endif
1329 #if defined(__STDC__)
1330 static void f_clear( Widget widget )
1331 #else
1332 static void f_clear( widget )
1333 Widget widget;
1334 #endif
1336 xvi_screen *cur_screen;
1338 #ifdef TRACE
1339 vtrace("f_clear() called");
1340 #endif
1342 XtVaGetValues( widget, XmNuserData, &cur_screen, 0 );
1344 erase_selection( cur_screen, selection_start, selection_end );
1349 * These routines deal with the cursor.
1351 * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int));
1353 void
1354 __vi_set_cursor(xvi_screen *cur_screen, int is_busy)
1356 XDefineCursor( XtDisplay(cur_screen->area),
1357 XtWindow(cur_screen->area),
1358 (is_busy) ? busy_cursor : std_cursor
1364 /* hooks for the tags widget */
1366 static String cur_word = NULL;
1369 * PUBLIC: void __vi_set_word_at_caret __P((xvi_screen *));
1371 void
1372 __vi_set_word_at_caret(xvi_screen *this_screen)
1374 char *start, *end, save;
1375 int newx, newy;
1377 newx = this_screen->curx;
1378 newy = this_screen->cury;
1380 /* Note that this really ought to be done by core due to wrapping issues */
1381 for ( end = start = CharAt( this_screen, newy, newx );
1382 (isalnum( *end ) || *end == '_') && (newx < this_screen->cols);
1383 end++, newx++
1385 save = *end;
1386 *end = '\0';
1387 if ( cur_word != NULL ) free( cur_word );
1388 cur_word = strdup( start );
1389 *end = save;
1391 /* if the tag stack widget is active, set the text field there
1392 * to agree with the current caret position.
1394 __vi_set_tag_text( cur_word );
1398 String __vi_get_word_at_caret(xvi_screen *this_screen)
1400 return (cur_word) ? cur_word : "";
1405 * These routines deal with the caret.
1407 * PUBLIC: void draw_caret __P((xvi_screen *));
1409 static void
1410 draw_caret(xvi_screen *this_screen)
1412 /* draw the caret by drawing the text in highlight color */
1413 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) |= COLOR_CARET;
1414 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1418 * PUBLIC: void __vi_erase_caret __P((xvi_screen *));
1420 void
1421 __vi_erase_caret(xvi_screen *this_screen)
1423 /* erase the caret by drawing the text in normal video */
1424 *FlagAt( this_screen, this_screen->cury, this_screen->curx ) &= ~COLOR_CARET;
1425 __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 );
1429 * PUBLIC: void __vi_move_caret __P((xvi_screen *, int, int));
1431 void
1432 __vi_move_caret(xvi_screen *this_screen, int newy, int newx)
1434 /* remove the old caret */
1435 __vi_erase_caret( this_screen );
1437 /* caret is now here */
1438 this_screen->curx = newx;
1439 this_screen->cury = newy;
1440 draw_caret( this_screen );