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