1444 lines
34 KiB
C
1444 lines
34 KiB
C
/* $NetBSD: m_vi.c,v 1.1.1.2 2008/05/18 14:31:29 aymeric 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"
|
||
|
||
#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 */
|
||
|
||
#include <sys/types.h>
|
||
#include <sys/queue.h>
|
||
|
||
#include <X11/Intrinsic.h>
|
||
#include <X11/StringDefs.h>
|
||
#include <X11/cursorfont.h>
|
||
#include <Xm/PanedW.h>
|
||
#include <Xm/DrawingA.h>
|
||
#include <Xm/Form.h>
|
||
#include <Xm/Frame.h>
|
||
#include <Xm/ScrollBar.h>
|
||
|
||
#include <bitstring.h>
|
||
#include <ctype.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
#include <signal.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
|
||
#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[] =
|
||
"<Btn1Down>: select_start() \n\
|
||
<Btn1Motion>: select_extend() \n\
|
||
<Btn2Down>: select_paste() \n\
|
||
<Btn3Down>: select_extend() \n\
|
||
<Btn3Motion>: select_extend() \n\
|
||
<Key>End: command(VI_C_BOTTOM) \n\
|
||
<Key>Escape: command(EINSERT) \n\
|
||
<Key>Find: find() \n\
|
||
<Key>Home: command(VI_C_TOP) \n\
|
||
<Key>Next: command(VI_C_PGDOWN) \n\
|
||
<Key>Prior: command(VI_C_PGUP) \n\
|
||
<Key>osfBackSpace: command(VI_C_LEFT) \n\
|
||
<Key>osfBeginLine: command(VI_C_BOL) \n\
|
||
<Key>osfCopy: beep() \n\
|
||
<Key>osfCut: beep() \n\
|
||
<Key>osfDelete: command(VI_C_DEL) \n\
|
||
<Key>osfDown: command(VI_C_DOWN) \n\
|
||
<Key>osfEndLine: command(VI_C_EOL) \n\
|
||
<Key>osfInsert: command(VI_C_INSERT) \n\
|
||
<Key>osfLeft: command(VI_C_LEFT) \n\
|
||
<Key>osfPageDown: command(VI_C_PGDOWN) \n\
|
||
<Key>osfPageUp: command(VI_C_PGUP) \n\
|
||
<Key>osfPaste: insert_string(p) \n\
|
||
<Key>osfRight: command(VI_C_RIGHT) \n\
|
||
<Key>osfUndo: command(VI_UNDO) \n\
|
||
<Key>osfUp: command(VI_C_UP) \n\
|
||
Ctrl<Key>C: command(VI_INTERRUPT) \n\
|
||
<Key>: 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;
|
||
col<this_screen->cols && col<start_col+len;
|
||
col++ ) {
|
||
|
||
/* has the color changed? */
|
||
if ( *FlagAt( __vi_screen, row, col ) == color )
|
||
continue;
|
||
|
||
/* is there anything to write? */
|
||
end = CharAt( __vi_screen, row, col );
|
||
if ( end == start )
|
||
continue;
|
||
|
||
/* 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
|
||
);
|
||
|
||
/* 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; row<this_screen->rows; 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 <default> %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; i<num && XmIsForm(c[i]); i++) {
|
||
if ( __vi_screen == NULL || __vi_screen->form == 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( cur_screen->characters[*start] ) )
|
||
return;
|
||
for (;;) {
|
||
if ( isspace( 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( cur_screen->characters[*end] ) )
|
||
return;
|
||
for (;;) {
|
||
if ( isspace( 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( *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 );
|
||
}
|