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