/* $NetBSD: m_vi.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */ * Copyright (c) 1996 * Rob Zimmermann. All rights reserved. * Copyright (c) 1996 * Keith Bostic. All rights reserved. * * See the LICENSE file for redistribution information. */ #include "config.h" #include #if 0 #ifndef lint 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 "; #endif /* not lint */ #else __RCSID("$NetBSD: m_vi.c,v 1.3 2014/01/26 21:43:45 christos Exp $"); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef LOCK_SUCCESS #include "../common/common.h" #include "../ipc/ip.h" #include "m_motif.h" #include "vi_mextern.h" #include "pathnames.h" extern int vi_ofd; static void f_copy(String *buffer, int *len); static void f_paste(int widget, int buffer, int length); static void f_clear(Widget widget); /* * Globals and costants */ #define BufferSize 1024 static XFontStruct *font; static GC gc; GC __vi_copy_gc; static XtAppContext ctx; xvi_screen *__vi_screen = NULL; static Cursor std_cursor; static Cursor busy_cursor; static XtTranslations area_trans; static int multi_click_length; void (*__vi_exitp)(); /* Exit function. */ /* hack for drag scrolling... * I'm not sure why, but the current protocol gets out of sync when * a lot of drag messages get passed around. Likely, we need to wait * for core to finish repainting the screen before sending more drag * messages. * To that end, we set scroll_block when we receive input from the scrollbar, * and we clear it when we process the IPO_REFRESH message from core. * A specific SCROLL_COMPLETED message would be better, but this seems to work. */ static Boolean scroll_block = False; /* * PUBLIC: void __vi_set_scroll_block __P((void)); */ void __vi_set_scroll_block(void) { scroll_block = True; } /* * PUBLIC: void __vi_clear_scroll_block __P((void)); */ void __vi_clear_scroll_block(void) { scroll_block = False; } #if defined(__STDC__) static void set_gc_colors( xvi_screen *this_screen, int val ) #else static void set_gc_colors( this_screen, val ) xvi_screen *this_screen; int val; #endif { static Pixel fg, bg, hi, shade; static int prev = COLOR_INVALID; /* no change? */ if ( prev == val ) return; /* init? */ if ( gc == NULL ) { /* what colors are selected for the drawing area? */ XtVaGetValues( this_screen->area, XtNbackground, &bg, XtNforeground, &fg, XmNhighlightColor, &hi, XmNtopShadowColor, &shade, 0 ); gc = XCreateGC( XtDisplay(this_screen->area), DefaultRootWindow(XtDisplay(this_screen->area)), 0, 0 ); XSetFont( XtDisplay(this_screen->area), gc, font->fid ); } /* special colors? */ if ( val & COLOR_CARET ) { XSetForeground( XtDisplay(this_screen->area), gc, fg ); XSetBackground( XtDisplay(this_screen->area), gc, hi ); } else if ( val & COLOR_SELECT ) { XSetForeground( XtDisplay(this_screen->area), gc, fg ); XSetBackground( XtDisplay(this_screen->area), gc, shade ); } else switch (val) { case COLOR_STANDARD: XSetForeground( XtDisplay(this_screen->area), gc, fg ); XSetBackground( XtDisplay(this_screen->area), gc, bg ); break; case COLOR_INVERSE: XSetForeground( XtDisplay(this_screen->area), gc, bg ); XSetBackground( XtDisplay(this_screen->area), gc, fg ); break; default: /* implement color map later */ break; } } /* * Memory utilities */ #ifdef REALLOC #undef REALLOC #endif #define REALLOC( ptr, size ) \ ((ptr == NULL) ? malloc(size) : realloc(ptr,size)) /* X windows routines. * We currently create a single, top-level shell. In that is a * single drawing area into which we will draw text. This allows * us to put multi-color (and font, but we'll never build that) text * into the drawing area. In the future, we'll add scrollbars to the * drawing areas */ void select_start(); void select_extend(); void select_paste(); void key_press(); void insert_string(); void beep __P((Widget w)); void find(); void command(); static XtActionsRec area_actions[] = { { "select_start", select_start }, { "select_extend", select_extend }, { "select_paste", select_paste }, { "key_press", key_press }, { "insert_string", insert_string }, { "beep", beep }, { "find", find }, { "command", command }, }; char areaTrans[] = ": select_start() \n\ : select_extend() \n\ : select_paste() \n\ : select_extend() \n\ : select_extend() \n\ End: command(VI_C_BOTTOM) \n\ Escape: command(EINSERT) \n\ Find: find() \n\ Home: command(VI_C_TOP) \n\ Next: command(VI_C_PGDOWN) \n\ Prior: command(VI_C_PGUP) \n\ osfBackSpace: command(VI_C_LEFT) \n\ osfBeginLine: command(VI_C_BOL) \n\ osfCopy: beep() \n\ osfCut: beep() \n\ osfDelete: command(VI_C_DEL) \n\ osfDown: command(VI_C_DOWN) \n\ osfEndLine: command(VI_C_EOL) \n\ osfInsert: command(VI_C_INSERT) \n\ osfLeft: command(VI_C_LEFT) \n\ osfPageDown: command(VI_C_PGDOWN) \n\ osfPageUp: command(VI_C_PGUP) \n\ osfPaste: insert_string(p) \n\ osfRight: command(VI_C_RIGHT) \n\ osfUndo: command(VI_UNDO) \n\ osfUp: command(VI_C_UP) \n\ CtrlC: command(VI_INTERRUPT) \n\ : key_press()"; static XutResource resource[] = { { "font", XutRKfont, &font }, { "pointerShape", XutRKcursor, &std_cursor }, { "busyShape", XutRKcursor, &busy_cursor }, }; /* * vi_input_func -- * We've received input on the pipe from vi. * * PUBLIC: void vi_input_func __P((XtPointer, int *, XtInputId *)); */ void vi_input_func(XtPointer client_data, int *source, XtInputId *id) { /* Parse and dispatch on commands in the queue. */ (void)ipvi_motif->input(ipvi_motif, *source); #ifdef notdef /* Check the pipe for unused events when not busy. */ XtAppAddWorkProc(ctx, process_pipe_input, NULL); #endif } /* Send the window size. */ #if defined(__STDC__) static void send_resize( xvi_screen *this_screen ) #else static void send_resize( this_screen ) xvi_screen *this_screen; #endif { IP_BUF ipb; ipb.val1 = this_screen->rows; ipb.val2 = this_screen->cols; ipb.code = VI_RESIZE; #ifdef TRACE vtrace("resize_func ( %d x %d )\n", this_screen->rows, this_screen->cols); #endif /* send up the pipe */ vi_send(vi_ofd, "12", &ipb); } #if defined(__STDC__) static void resize_backing_store( xvi_screen *this_screen ) #else static void resize_backing_store( this_screen ) xvi_screen *this_screen; #endif { int total_chars = this_screen->rows * this_screen->cols; this_screen->characters = REALLOC( this_screen->characters, total_chars ); memset( this_screen->characters, ' ', total_chars ); this_screen->flags = REALLOC( this_screen->flags, total_chars ); memset( this_screen->flags, 0, total_chars ); } /* X will call this when we are resized */ #if defined(__STDC__) static void resize_func( Widget wid, XtPointer client_data, XtPointer call_data ) #else static void resize_func( wid, client_data, call_data ) Widget wid; XtPointer client_data; XtPointer call_data; #endif { xvi_screen *this_screen = (xvi_screen *) client_data; Dimension height, width; XtVaGetValues( wid, XmNheight, &height, XmNwidth, &width, 0 ); /* generate correct sizes when we have font metrics implemented */ this_screen->cols = width / this_screen->ch_width; this_screen->rows = height / this_screen->ch_height; resize_backing_store( this_screen ); send_resize( this_screen ); } /* * __vi_draw_text -- * Draw from backing store. * * PUBLIC: void __vi_draw_text __P((xvi_screen *, int, int, int)); */ void __vi_draw_text(xvi_screen *this_screen, int row, int start_col, int len) { int col, color, xpos; char *start, *end; start = CharAt( __vi_screen, row, start_col ); color = *FlagAt( __vi_screen, row, start_col ); xpos = XPOS( __vi_screen, start_col ); /* one column at a time */ for ( col=start_col; colcols && colarea), XtWindow(__vi_screen->area), gc, xpos, YPOS( __vi_screen, row ), start, end - start ); /* this is the new context */ color = *FlagAt( __vi_screen, row, col ); xpos = XPOS( __vi_screen, col ); start = end; } /* is there anything to write? */ end = CharAt( __vi_screen, row, col ); if ( end != start ) { /* yes. write in the previous color */ set_gc_colors( __vi_screen, color ); /* add to display */ XDrawImageString( XtDisplay(__vi_screen->area), XtWindow(__vi_screen->area), gc, xpos, YPOS( __vi_screen, row ), start, end - start ); } } /* set clipping rectangles accordingly */ #if defined(__STDC__) static void add_to_clip( xvi_screen *cur_screen, int x, int y, int width, int height ) #else static void add_to_clip( cur_screen, x, y, width, height ) xvi_screen *cur_screen; int x; int y; int width; int height; #endif { XRectangle rect; rect.x = x; rect.y = y; rect.height = height; rect.width = width; if ( cur_screen->clip == NULL ) cur_screen->clip = XCreateRegion(); XUnionRectWithRegion( &rect, cur_screen->clip, cur_screen->clip ); } /* * __vi_expose_func -- * Redraw the window's contents. * * NOTE: When vi wants to force a redraw, we are called with NULL widget * and call_data. * * PUBLIC: void __vi_expose_func __P((Widget, XtPointer, XtPointer)); */ void __vi_expose_func(Widget wid, XtPointer client_data, XtPointer call_data) { xvi_screen *this_screen; XmDrawingAreaCallbackStruct *cbs; XExposeEvent *xev; XGraphicsExposeEvent *gev; int row; /* convert pointers */ this_screen = (xvi_screen *) client_data; cbs = (XmDrawingAreaCallbackStruct *) call_data; /* first exposure? tell vi we are ready... */ if ( this_screen->init == False ) { /* what does the user want to see? */ __vi_set_cursor( __vi_screen, False ); /* vi wants a resize as the first event */ send_resize( __vi_screen ); /* fine for now. we'll be back */ this_screen->init = True; return; } if ( call_data == NULL ) { /* vi core calls this when it wants a full refresh */ #ifdef TRACE vtrace("expose_func: full refresh\n"); #endif XClearWindow( XtDisplay(this_screen->area), XtWindow(this_screen->area) ); } else { switch ( cbs->event->type ) { case GraphicsExpose: gev = (XGraphicsExposeEvent *) cbs->event; /* set clipping rectangles accordingly */ add_to_clip( this_screen, gev->x, gev->y, gev->width, gev->height ); /* X calls here when XCopyArea exposes new bits */ #ifdef TRACE vtrace("expose_func (X): (x=%d,y=%d,w=%d,h=%d), count=%d\n", gev->x, gev->y, gev->width, gev->height, gev->count); #endif /* more coming? do it then */ if ( gev->count > 0 ) return; /* set clipping region */ XSetRegion( XtDisplay(wid), gc, this_screen->clip ); break; case Expose: xev = (XExposeEvent *) cbs->event; /* set clipping rectangles accordingly */ add_to_clip( this_screen, xev->x, xev->y, xev->width, xev->height ); /* Motif calls here when DrawingArea is exposed */ #ifdef TRACE vtrace("expose_func (Motif): (x=%d,y=%d,w=%d,h=%d), count=%d\n", xev->x, xev->y, xev->width, xev->height, xev->count); #endif /* more coming? do it then */ if ( xev->count > 0 ) return; /* set clipping region */ XSetRegion( XtDisplay(wid), gc, this_screen->clip ); break; default: /* don't care? */ return; } } /* one row at a time */ for (row=0; rowrows; row++) { /* draw from the backing store */ __vi_draw_text( this_screen, row, 0, this_screen->cols ); } /* clear clipping region */ XSetClipMask( XtDisplay(this_screen->area), gc, None ); if ( this_screen->clip != NULL ) { XDestroyRegion( this_screen->clip ); this_screen->clip = NULL; } } #if defined(__STDC__) static void xexpose ( Widget w, XtPointer client_data, XEvent *ev, Boolean *cont ) #else static void xexpose ( w, client_data, ev, cont ) Widget w; XtPointer client_data; XEvent *ev; Boolean *cont; #endif { XmDrawingAreaCallbackStruct cbs; switch ( ev->type ) { case GraphicsExpose: cbs.event = ev; cbs.window = XtWindow(w); cbs.reason = XmCR_EXPOSE; __vi_expose_func( w, client_data, (XtPointer) &cbs ); *cont = False; /* we took care of it */ break; default: /* don't care */ break; } } /* unimplemented keystroke or command */ #if defined(__STDC__) static void beep( Widget w ) #else static void beep( w ) Widget w; #endif { XBell(XtDisplay(w),0); } /* give me a search dialog */ #if defined(__STDC__) static void find( Widget w ) #else static void find( w ) Widget w; #endif { __vi_show_search_dialog( w, "Find" ); } /* * command -- * Translate simple keyboard input into vi protocol commands. */ static void command(Widget widget, XKeyEvent *event, String *str, Cardinal *cardinal) { static struct { String name; int code; int count; } table[] = { { "VI_C_BOL", VI_C_BOL, 0 }, { "VI_C_BOTTOM", VI_C_BOTTOM, 0 }, { "VI_C_DEL", VI_C_DEL, 0 }, { "VI_C_DOWN", VI_C_DOWN, 1 }, { "VI_C_EOL", VI_C_EOL, 0 }, { "VI_C_INSERT", VI_C_INSERT, 0 }, { "VI_C_LEFT", VI_C_LEFT, 0 }, { "VI_C_PGDOWN", VI_C_PGDOWN, 1 }, { "VI_C_PGUP", VI_C_PGUP, 1 }, { "VI_C_RIGHT", VI_C_RIGHT, 0 }, { "VI_C_TOP", VI_C_TOP, 0 }, { "VI_C_UP", VI_C_UP, 1 }, { "VI_INTERRUPT", VI_INTERRUPT, 0 }, }; IP_BUF ipb; int i; /* * XXX * Do fast lookup based on character #6 -- sleazy, but I don't * want to do 10 strcmp's per keystroke. */ ipb.val1 = 1; for (i = 0; i < XtNumber(table); i++) if (table[i].name[6] == (*str)[6] && strcmp(table[i].name, *str) == 0) { ipb.code = table[i].code; vi_send(vi_ofd, table[i].count ? "1" : NULL, &ipb); return; } /* oops. */ beep(widget); } /* mouse or keyboard input. */ #if defined(__STDC__) static void insert_string( Widget widget, XKeyEvent *event, String *str, Cardinal *cardinal ) #else static void insert_string( widget, event, str, cardinal ) Widget widget; XKeyEvent *event; String *str; Cardinal *cardinal; #endif { IP_BUF ipb; ipb.len1 = strlen( *str ); if ( ipb.len1 != 0 ) { ipb.code = VI_STRING; ipb.str1 = *str; vi_send(vi_ofd, "a", &ipb); } #ifdef TRACE vtrace("insert_string {%.*s}\n", strlen( *str ), *str ); #endif } /* mouse or keyboard input. */ #if defined(__STDC__) static void key_press( Widget widget, XKeyEvent *event, String str, Cardinal *cardinal ) #else static void key_press( widget, event, str, cardinal ) Widget widget; XKeyEvent *event; String str; Cardinal *cardinal; #endif { IP_BUF ipb; char bp[BufferSize]; ipb.len1 = XLookupString( event, bp, BufferSize, NULL, NULL ); if ( ipb.len1 != 0 ) { ipb.code = VI_STRING; ipb.str1 = bp; #ifdef TRACE vtrace("key_press {%.*s}\n", ipb.len1, bp ); #endif vi_send(vi_ofd, "a", &ipb); } } #if defined(__STDC__) static void scrollbar_moved( Widget widget, XtPointer ptr, XmScrollBarCallbackStruct *cbs ) #else static void scrollbar_moved( widget, ptr, cbs ) Widget widget; XtPointer ptr; XmScrollBarCallbackStruct *cbs; #endif { /* Future: Need to scroll the correct screen! */ xvi_screen *cur_screen = (xvi_screen *) ptr; IP_BUF ipb; /* if we are still processing messages from core, skip this event * (see comments near __vi_set_scroll_block()) */ if ( scroll_block ) { return; } __vi_set_scroll_block(); #ifdef TRACE switch ( cbs->reason ) { case XmCR_VALUE_CHANGED: vtrace( "scrollbar VALUE_CHANGED %d\n", cbs->value ); break; case XmCR_DRAG: vtrace( "scrollbar DRAG %d\n", cbs->value ); break; default: vtrace( "scrollbar %d\n", cbs->value ); break; } vtrace("scrollto {%d}\n", cbs->value ); #endif /* Send the new cursor position. */ ipb.code = VI_C_SETTOP; ipb.val1 = cbs->value; (void)vi_send(vi_ofd, "1", &ipb); } #if defined(__STDC__) static xvi_screen *create_screen( Widget parent, int rows, int cols ) #else static xvi_screen *create_screen( parent, rows, cols ) Widget parent; int rows, cols; #endif { xvi_screen *new_screen = (xvi_screen *) calloc( 1, sizeof(xvi_screen) ); Widget frame; /* init... */ new_screen->color = COLOR_STANDARD; new_screen->parent = parent; /* figure out the sizes */ new_screen->rows = rows; new_screen->cols = cols; new_screen->ch_width = font->max_bounds.width; new_screen->ch_height = font->descent + font->ascent; new_screen->ch_descent = font->descent; new_screen->clip = NULL; /* allocate and init the backing stores */ resize_backing_store( new_screen ); /* set up a translation table for the X toolkit */ if ( area_trans == NULL ) area_trans = XtParseTranslationTable(areaTrans); /* future, new screen gets inserted into the parent sash * immediately after the current screen. Default Pane action is * to add it to the end */ /* use a form to hold the drawing area and the scrollbar */ new_screen->form = XtVaCreateManagedWidget( "form", xmFormWidgetClass, parent, XmNpaneMinimum, 2*new_screen->ch_height, XmNallowResize, True, NULL ); /* create a scrollbar. */ new_screen->scroll = XtVaCreateManagedWidget( "scroll", xmScrollBarWidgetClass, new_screen->form, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, XmNminimum, 1, XmNmaximum, 2, XmNsliderSize, 1, NULL ); XtAddCallback( new_screen->scroll, XmNvalueChangedCallback, scrollbar_moved, new_screen ); XtAddCallback( new_screen->scroll, XmNdragCallback, scrollbar_moved, new_screen ); /* create a frame because they look nice */ frame = XtVaCreateManagedWidget( "frame", xmFrameWidgetClass, new_screen->form, XmNshadowType, XmSHADOW_ETCHED_IN, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_WIDGET, XmNrightWidget, new_screen->scroll, NULL ); /* create a drawing area into which we will put text */ new_screen->area = XtVaCreateManagedWidget( "screen", xmDrawingAreaWidgetClass, frame, XmNheight, new_screen->ch_height * new_screen->rows, XmNwidth, new_screen->ch_width * new_screen->cols, XmNtranslations, area_trans, XmNuserData, new_screen, XmNnavigationType, XmNONE, XmNtraversalOn, False, NULL ); /* this callback is for when the drawing area is resized */ XtAddCallback( new_screen->area, XmNresizeCallback, resize_func, new_screen ); /* this callback is for when the drawing area is exposed */ XtAddCallback( new_screen->area, XmNexposeCallback, __vi_expose_func, new_screen ); /* this callback is for when we expose obscured bits * (e.g. there is a window over part of our drawing area */ XtAddEventHandler( new_screen->area, 0, /* no standard events */ True, /* we *WANT* GraphicsExpose */ xexpose, /* what to do */ new_screen ); return new_screen; } static xvi_screen *split_screen(void) { Cardinal num; WidgetList c; int rows = __vi_screen->rows / 2; xvi_screen *new_screen; /* Note that (global) cur_screen needs to be correctly set so that * insert_here knows which screen to put the new one after */ new_screen = create_screen( __vi_screen->parent, rows, __vi_screen->cols ); /* what are the screens? */ XtVaGetValues( __vi_screen->parent, XmNnumChildren, &num, XmNchildren, &c, NULL ); /* unmanage all children in preparation for resizing */ XtUnmanageChildren( c, num ); /* force resize of the affected screens */ XtVaSetValues( new_screen->form, XmNheight, new_screen->ch_height * rows, NULL ); XtVaSetValues( __vi_screen->form, XmNheight, __vi_screen->ch_height * rows, NULL ); /* re-manage */ XtManageChildren( c, num ); /* done */ return new_screen; } /* Tell me where to insert the next subpane */ #if defined(__STDC__) static Cardinal insert_here( Widget wid ) #else static Cardinal insert_here( wid ) Widget wid; #endif { Cardinal i, num; WidgetList c; XtVaGetValues( XtParent(wid), XmNnumChildren, &num, XmNchildren, &c, NULL ); /* The default XmNinsertPosition procedure for PanedWindow * causes sashes to be inserted at the end of the list of children * and causes non-sash widgets to be inserted after other * non-sash children but before any sashes. */ if ( ! XmIsForm( wid ) ) return num; /* We will put the widget after the one with the current screen */ for (i=0; iform == c[i] ) return i+1; /* after the i-th */ } /* could not find it? this should never happen */ return num; } /* * vi_create_editor -- * Create the necessary widgetry. * * PUBLIC: Widget vi_create_editor __P((String, Widget, void (*)(void))); */ Widget vi_create_editor(String name, Widget parent, void (*exitp) (void)) { Widget pane_w; Display *display = XtDisplay( parent ); __vi_exitp = exitp; /* first time through? */ if ( ctx == NULL ) { /* save this for later */ ctx = XtWidgetToApplicationContext( parent ); /* add our own special actions */ XtAppAddActions( ctx, area_actions, XtNumber(area_actions) ); /* how long is double-click? */ multi_click_length = XtGetMultiClickTime( display ); /* check the resource database for interesting resources */ __XutConvertResources( parent, vi_progname, resource, XtNumber(resource) ); /* we need a context for moving bits around in the windows */ __vi_copy_gc = XCreateGC( display, DefaultRootWindow(display), 0, 0 ); /* routines for inter client communications conventions */ __vi_InitCopyPaste( f_copy, f_paste, f_clear, fprintf ); } /* create the paned window */ pane_w = XtVaCreateManagedWidget( "pane", xmPanedWindowWidgetClass, parent, XmNinsertPosition, insert_here, NULL ); /* allocate our data structure. in the future we will have several * screens running around at the same time */ __vi_screen = create_screen( pane_w, 24, 80 ); /* force creation of our color text context */ set_gc_colors( __vi_screen, COLOR_STANDARD ); /* done */ return pane_w; } /* These routines deal with the selection buffer */ static int selection_start, selection_end, selection_anchor; static enum select_enum { select_char, select_word, select_line } select_type = select_char; static int last_click; static char *clipboard = NULL; static int clipboard_size = 0, clipboard_length; #if defined(__STDC__) static void copy_to_clipboard( xvi_screen *cur_screen ) #else static void copy_to_clipboard( cur_screen ) xvi_screen *cur_screen; #endif { /* for now, copy from the backing store. in the future, * vi core will tell us exactly what the selection buffer contains */ clipboard_length = 1 + selection_end - selection_start; if ( clipboard == NULL ) clipboard = (char *) malloc( clipboard_length ); else if ( clipboard_size < clipboard_length ) clipboard = (char *) realloc( clipboard, clipboard_length ); memcpy( clipboard, cur_screen->characters + selection_start, clipboard_length ); } #if defined(__STDC__) static void mark_selection( xvi_screen *cur_screen, int start, int end ) #else static void mark_selection( cur_screen, start, end ) xvi_screen *cur_screen; int start; int end; #endif { int row, col, i; for ( i=start; i<=end; i++ ) { if ( !( cur_screen->flags[i] & COLOR_SELECT ) ) { cur_screen->flags[i] |= COLOR_SELECT; ToRowCol( cur_screen, i, row, col ); __vi_draw_text( cur_screen, row, col, 1 ); } } } #if defined(__STDC__) static void erase_selection( xvi_screen *cur_screen, int start, int end ) #else static void erase_selection( cur_screen, start, end ) xvi_screen *cur_screen; int start; int end; #endif { int row, col, i; for ( i=start; i<=end; i++ ) { if ( cur_screen->flags[i] & COLOR_SELECT ) { cur_screen->flags[i] &= ~COLOR_SELECT; ToRowCol( cur_screen, i, row, col ); __vi_draw_text( cur_screen, row, col, 1 ); } } } #if defined(__STDC__) static void left_expand_selection( xvi_screen *cur_screen, int *start ) #else static void left_expand_selection( cur_screen, start ) xvi_screen *cur_screen; int *start; #endif { int row, col; switch ( select_type ) { case select_word: if ( *start == 0 || isspace( (unsigned char)cur_screen->characters[*start] ) ) return; for (;;) { if ( isspace( (unsigned char)cur_screen->characters[*start-1] ) ) return; if ( --(*start) == 0 ) return; } case select_line: ToRowCol( cur_screen, *start, row, col ); col = 0; *start = Linear( cur_screen, row, col ); break; } } #if defined(__STDC__) static void right_expand_selection( xvi_screen *cur_screen, int *end ) #else static void right_expand_selection( cur_screen, end ) xvi_screen *cur_screen; int *end; #endif { int row, col, last = cur_screen->cols * cur_screen->rows - 1; switch ( select_type ) { case select_word: if ( *end == last || isspace( (unsigned char)cur_screen->characters[*end] ) ) return; for (;;) { if ( isspace( (unsigned char)cur_screen->characters[*end+1] ) ) return; if ( ++(*end) == last ) return; } case select_line: ToRowCol( cur_screen, *end, row, col ); col = cur_screen->cols -1; *end = Linear( cur_screen, row, col ); break; } } #if defined(__STDC__) static void select_start( Widget widget, XEvent *event, String str, Cardinal *cardinal ) #else static void select_start( widget, event, str, cardinal ) Widget widget; XEvent *event; String str; Cardinal *cardinal; #endif { IP_BUF ipb; int xpos, ypos; XPointerMovedEvent *ev = (XPointerMovedEvent *) event; static int last_click; /* * NOTE: when multiple panes are implemented, we need to find the correct * screen. For now, there is only one. */ xpos = COLUMN( __vi_screen, ev->x ); ypos = ROW( __vi_screen, ev->y ); /* Remove the old one. */ erase_selection( __vi_screen, selection_start, selection_end ); /* Send the new cursor position. */ ipb.code = VI_MOUSE_MOVE; ipb.val1 = ypos; ipb.val2 = xpos; (void)vi_send(vi_ofd, "12", &ipb); /* click-click, and we go for words, lines, etc */ if ( ev->time - last_click < multi_click_length ) select_type = (enum select_enum) ((((int)select_type)+1)%3); else select_type = select_char; last_click = ev->time; /* put the selection here */ selection_anchor = Linear( __vi_screen, ypos, xpos ); selection_start = selection_anchor; selection_end = selection_anchor; /* expand to include words, line, etc */ left_expand_selection( __vi_screen, &selection_start ); right_expand_selection( __vi_screen, &selection_end ); /* draw the new one */ mark_selection( __vi_screen, selection_start, selection_end ); /* and tell the window manager we own the selection */ if ( select_type != select_char ) { __vi_AcquirePrimary( widget ); copy_to_clipboard( __vi_screen ); } } #if defined(__STDC__) static void select_extend( Widget widget, XEvent *event, String str, Cardinal *cardinal ) #else static void select_extend( widget, event, str, cardinal ) Widget widget; XEvent *event; String str; Cardinal *cardinal; #endif { int xpos, ypos, pos; XPointerMovedEvent *ev = (XPointerMovedEvent *) event; /* NOTE: when multiple panes are implemented, we need to find * the correct screen. For now, there is only one. */ xpos = COLUMN( __vi_screen, ev->x ); ypos = ROW( __vi_screen, ev->y ); /* deal with words, lines, etc */ pos = Linear( __vi_screen, ypos, xpos ); if ( pos < selection_anchor ) left_expand_selection( __vi_screen, &pos ); else right_expand_selection( __vi_screen, &pos ); /* extend from before the start? */ if ( pos < selection_start ) { mark_selection( __vi_screen, pos, selection_start-1 ); selection_start = pos; } /* extend past the end? */ else if ( pos > selection_end ) { mark_selection( __vi_screen, selection_end+1, pos ); selection_end = pos; } /* between the anchor and the start? */ else if ( pos < selection_anchor ) { erase_selection( __vi_screen, selection_start, pos-1 ); selection_start = pos; } /* between the anchor and the end? */ else { erase_selection( __vi_screen, pos+1, selection_end ); selection_end = pos; } /* and tell the window manager we own the selection */ __vi_AcquirePrimary( widget ); copy_to_clipboard( __vi_screen ); } #if defined(__STDC__) static void select_paste( Widget widget, XEvent *event, String str, Cardinal *cardinal ) #else static void select_paste( widget, event, str, cardinal ) Widget widget; XEvent *event; String str; Cardinal *cardinal; #endif { __vi_PasteFromClipboard( widget ); } /* Interface to copy and paste * (a) callbacks from the window manager * f_copy - it wants our buffer * f_paste - it wants us to paste some text * f_clear - we've lost the selection, clear it */ #if defined(__STDC__) static void f_copy( String *buffer, int *len ) #else static void f_copy( buffer, len ) String *buffer; int *len; #endif { #ifdef TRACE vtrace("f_copy() called"); #endif *buffer = clipboard; *len = clipboard_length; } static void f_paste(int widget, int buffer, int length) { /* NOTE: when multiple panes are implemented, we need to find * the correct screen. For now, there is only one. */ #ifdef TRACE vtrace("f_paste() called with '%*.*s'\n", length, length, buffer); #endif } #if defined(__STDC__) static void f_clear( Widget widget ) #else static void f_clear( widget ) Widget widget; #endif { xvi_screen *cur_screen; #ifdef TRACE vtrace("f_clear() called"); #endif XtVaGetValues( widget, XmNuserData, &cur_screen, 0 ); erase_selection( cur_screen, selection_start, selection_end ); } /* * These routines deal with the cursor. * * PUBLIC: void __vi_set_cursor __P((xvi_screen *, int)); */ void __vi_set_cursor(xvi_screen *cur_screen, int is_busy) { XDefineCursor( XtDisplay(cur_screen->area), XtWindow(cur_screen->area), (is_busy) ? busy_cursor : std_cursor ); } /* hooks for the tags widget */ static String cur_word = NULL; /* * PUBLIC: void __vi_set_word_at_caret __P((xvi_screen *)); */ void __vi_set_word_at_caret(xvi_screen *this_screen) { char *start, *end, save; int newx, newy; newx = this_screen->curx; newy = this_screen->cury; /* Note that this really ought to be done by core due to wrapping issues */ for ( end = start = CharAt( this_screen, newy, newx ); (isalnum( (unsigned char)*end ) || *end == '_') && (newx < this_screen->cols); end++, newx++ ); save = *end; *end = '\0'; if ( cur_word != NULL ) free( cur_word ); cur_word = strdup( start ); *end = save; /* if the tag stack widget is active, set the text field there * to agree with the current caret position. */ __vi_set_tag_text( cur_word ); } String __vi_get_word_at_caret(xvi_screen *this_screen) { return (cur_word) ? cur_word : ""; } /* * These routines deal with the caret. * * PUBLIC: void draw_caret __P((xvi_screen *)); */ static void draw_caret(xvi_screen *this_screen) { /* draw the caret by drawing the text in highlight color */ *FlagAt( this_screen, this_screen->cury, this_screen->curx ) |= COLOR_CARET; __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 ); } /* * PUBLIC: void __vi_erase_caret __P((xvi_screen *)); */ void __vi_erase_caret(xvi_screen *this_screen) { /* erase the caret by drawing the text in normal video */ *FlagAt( this_screen, this_screen->cury, this_screen->curx ) &= ~COLOR_CARET; __vi_draw_text( this_screen, this_screen->cury, this_screen->curx, 1 ); } /* * PUBLIC: void __vi_move_caret __P((xvi_screen *, int, int)); */ void __vi_move_caret(xvi_screen *this_screen, int newy, int newx) { /* remove the old caret */ __vi_erase_caret( this_screen ); /* caret is now here */ this_screen->curx = newx; this_screen->cury = newy; draw_caret( this_screen ); }