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