1 /* 2 * Copyright (c) 2001-2006, Haiku, Inc. 3 * Copyright (c) 2003-4 Kian Duffy <myob@users.sourceforge.net> 4 * Parts Copyright (C) 1998,99 Kazuho Okui and Takashi Murai. 5 * Distributed under the terms of the MIT license. 6 * 7 * Authors: 8 * Kian Duffy <myob@users.sourceforge.net> 9 */ 10 11 12 #include <support/Debug.h> 13 #include <PopUpMenu.h> 14 #include <ScrollBar.h> 15 #include <storage/Path.h> 16 #include <support/Beep.h> 17 #include <Input.h> 18 #include <String.h> 19 #include <Clipboard.h> 20 #include <Roster.h> 21 #include <Autolock.h> 22 23 #include <string.h> 24 #include <stdlib.h> 25 #include <ctype.h> 26 27 #include "TermView.h" 28 #include "TermWindow.h" 29 #include "TermApp.h" 30 #include "TermParse.h" 31 #include "TermBuffer.h" 32 #include "CodeConv.h" 33 #include "VTkeymap.h" 34 #include "TermConst.h" 35 #include "PrefHandler.h" 36 #include "MenuUtil.h" 37 #include "PrefView.h" 38 39 #include <termios.h> 40 #include <signal.h> 41 #include "spawn.h" 42 43 // defined VTKeyTbl.c 44 extern int function_keycode_table[]; 45 extern char *function_key_char_table[]; 46 47 extern int gNowCoding; /* defined TermParse.cpp */ 48 49 rgb_color gTermColorTable[16] = { 50 { 0, 0, 0, 0}, 51 {255, 0, 0, 0}, 52 { 0, 255, 0, 0}, 53 {255, 255, 0, 0}, 54 { 0, 0, 255, 0}, 55 {255, 0, 255, 0}, 56 { 0, 255, 255, 0}, 57 {255, 255, 255, 0}, 58 }; 59 60 // Global Preference Handler 61 extern PrefHandler *gTermPref; 62 63 TermView::TermView(BRect frame, CodeConv *inCodeConv) 64 : BView(frame, "termview", B_FOLLOW_NONE, 65 B_WILL_DRAW | B_FRAME_EVENTS) 66 { 67 int rows, cols; 68 69 // Cursor reset. 70 fCurPos.Set(0, 0); 71 fCurStack.Set(0, 0); 72 fBufferStartPos = -1; 73 74 rows = gTermPref->getInt32(PREF_ROWS); 75 cols = gTermPref->getInt32(PREF_COLS); 76 77 fTermRows = rows; 78 fTermColumns = cols; 79 80 fCodeConv = inCodeConv; 81 82 // scroll pointer. 83 fTop = 0; 84 85 // create TermBuffer and CodeConv Class. 86 fTextBuffer = new TermBuffer(rows, cols); 87 88 // cursor Blinking and draw flag. 89 fCursorStatus = CURON; 90 fCursorDrawFlag = CURON; 91 fUpdateFlag = false; 92 fCursorBlinkingFlag = CURON; 93 94 fSelStart.Set(-1, -1); 95 fSelEnd.Set(-1, -1); 96 fSelected = false; 97 fMouseTracking = false; 98 99 // scroll bar variables. 100 fScrollUpCount = 0; 101 fScrollBarRange = 0; 102 fScrRegionSet = 0; 103 fScrBufSize = gTermPref->getInt32(PREF_HISTORY_SIZE); 104 105 // resize flag. 106 fFrameResized = 0; 107 108 // terminal mode flag. 109 fInsertModeFlag = MODE_OVER; 110 fInverseFlag = fBoldFlag = fUnderlineFlag = 0; 111 112 // terminal scroll flag. 113 fScrTop = 0; 114 fScrBot = rows - 1; 115 116 fPopMenu = NULL; 117 SetMouseButton(); 118 SetMouseCursor(); 119 fPreviousMousePoint.Set(0, 0); 120 121 //SetIMAware(gTermPref->getInt32(PREF_IM_AWARE)); 122 fIMflag = false; 123 124 fViewThread = -1; 125 fMouseThread = -1; 126 fQuitting = 1; 127 128 fFontHeight = 0; 129 fFontWidth = 0; 130 131 // Set fonts to some defaults 132 SetTermFont(be_plain_font, be_plain_font); 133 134 InitViewThread(); 135 136 fDrawRect_p = 0; 137 } 138 139 140 TermView::~TermView() 141 { 142 delete fTextBuffer; 143 fQuitting = 0; 144 kill_thread(fViewThread); 145 kill_thread(fMouseThread); 146 delete_sem(fDrawRectSem); 147 } 148 149 150 //! Get width and height for terminal font 151 void 152 TermView::GetFontSize(int* _width, int* _height) 153 { 154 *_width = fFontWidth; 155 *_height = fFontHeight; 156 } 157 158 159 //! Set number of rows and columns in terminal 160 BRect 161 TermView::SetTermSize(int rows, int cols, bool resize) 162 { 163 if (rows > 0) 164 fTermRows = rows; 165 if (cols > 0) 166 fTermColumns = cols; 167 168 fTextBuffer->ResizeTo(fTermRows, fTermColumns, 0); 169 170 fScrTop = 0; 171 fScrBot = fTermRows - 1; 172 173 BRect rect(0, 0, fTermColumns * fFontWidth, fTermRows * fFontHeight); 174 175 if (resize) { 176 ResizeTo(fTermColumns * fFontWidth - 1, 177 fTermRows * fFontHeight -1); 178 } 179 Invalidate(Frame()); 180 181 return rect; 182 } 183 184 185 //! Set mouse button assignments 186 void 187 TermView::SetMouseButton() 188 { 189 mSelectButton = SetupMouseButton(gTermPref->getString(PREF_SELECT_MBUTTON)); 190 mSubMenuButton = SetupMouseButton(gTermPref->getString(PREF_SUBMENU_MBUTTON)); 191 mPasteMenuButton = SetupMouseButton(gTermPref->getString(PREF_PASTE_MBUTTON)); 192 } 193 194 195 //! Sets the mouse cursor image 196 void 197 TermView::SetMouseCursor() 198 { 199 if (!strcmp(gTermPref->getString(PREF_MOUSE_IMAGE), "Hand cursor")) 200 fMouseImage = false; 201 else 202 fMouseImage = true; 203 } 204 205 206 //! Sets colors for the terminal 207 void 208 TermView::SetTermColor() 209 { 210 fTextForeColor = gTermPref->getRGB(PREF_TEXT_FORE_COLOR); 211 fTextBackColor = gTermPref->getRGB(PREF_TEXT_BACK_COLOR); 212 fSelectForeColor = gTermPref->getRGB(PREF_SELECT_FORE_COLOR); 213 fSelectBackColor = gTermPref->getRGB(PREF_SELECT_BACK_COLOR); 214 fCursorForeColor = gTermPref->getRGB(PREF_CURSOR_FORE_COLOR); 215 fCursorBackColor = gTermPref->getRGB(PREF_CURSOR_BACK_COLOR); 216 217 SetLowColor(fTextBackColor); 218 SetViewColor(fTextBackColor); 219 } 220 221 222 //! Sets half and full fonts for terminal 223 void 224 TermView::SetTermFont(const BFont *halfFont, const BFont *fullFont) 225 { 226 char buf[4]; 227 int halfWidth = 0; 228 229 fHalfFont = halfFont; 230 fFullFont = fullFont; 231 232 // calculate half font's max width 233 // Not Bounding, check only A-Z(For case of fHalfFont is KanjiFont. ) 234 for (int c = 0x20 ; c <= 0x7e; c++){ 235 sprintf(buf, "%c", c); 236 int tmpWidth = (int)fHalfFont.StringWidth(buf); 237 if (tmpWidth > halfWidth) 238 halfWidth = tmpWidth; 239 } 240 241 // How to calculate FullWidth ? 242 fFontWidth = halfWidth; 243 244 // Second, Calc Font Height 245 font_height fh, hh; 246 fHalfFont.GetHeight(&hh); 247 fFullFont.GetHeight(&fh); 248 249 int font_ascent, font_descent,font_leading; 250 251 font_ascent =(int)((fh.ascent > hh.ascent) ? fh.ascent : hh.ascent); 252 font_descent =(int)((fh.descent > hh.descent) ? fh.descent : hh.descent); 253 font_leading =(int)((fh.leading > hh.leading) ? fh.leading : hh.leading); 254 255 if (font_leading == 0) 256 font_leading = 1; 257 258 if (fTop) 259 fTop = fTop / fFontHeight; 260 261 fFontHeight = font_ascent + font_descent + font_leading + 1; 262 263 fTop = fTop * fFontHeight; 264 265 fFontAscent = font_ascent; 266 fCursorHeight = font_ascent + font_descent + font_leading + 1; 267 } 268 269 270 void 271 TermView::SetScrollBar(BScrollBar *scrollBar) 272 { 273 fScrollBar = scrollBar; 274 } 275 276 277 //! Print one character 278 void 279 TermView::PutChar(uchar *string, ushort attr, int width) 280 { 281 if (width == FULL_WIDTH) 282 attr |= A_WIDTH; 283 284 // check column over flow. 285 if (fCurPos.x + width > fTermColumns) { 286 UpdateLine(); 287 fCurPos.x = 0; 288 289 if (fCurPos.y == fTermRows -1) 290 ScrollScreen(); 291 else 292 fCurPos.y++; 293 } 294 295 if (fInsertModeFlag == MODE_INSERT) 296 fTextBuffer->InsertSpace(fCurPos, width); 297 298 fTextBuffer->WriteChar(fCurPos, string, attr); 299 300 if (!fUpdateFlag) 301 fBufferStartPos = fCurPos.x; 302 303 fCurPos.x += width; 304 fUpdateFlag = true; 305 } 306 307 308 //! Print a CR and move the cursor 309 void 310 TermView::PutCR() 311 { 312 UpdateLine(); 313 fTextBuffer->WriteCR(fCurPos); 314 fCurPos.x = 0; 315 } 316 317 318 //! Print a LF and move the cursor 319 void 320 TermView::PutLF() 321 { 322 UpdateLine(); 323 324 if (fScrRegionSet) { 325 if (fCurPos.y == fScrBot) { 326 ScrollRegion(-1, -1, SCRUP, 1); 327 return; 328 } 329 } 330 331 if (fCurPos.x != fTermColumns){ 332 if (fCurPos.y == fTermRows -1) 333 ScrollScreen(); 334 else 335 fCurPos.y++; 336 } 337 } 338 339 340 //! Print a NL and move the cursor 341 void 342 TermView::PutNL(int num) 343 { 344 ScrollRegion(fCurPos.y, -1, SCRDOWN, num); 345 } 346 347 348 //! Print a space 349 void 350 TermView::InsertSpace(int num) 351 { 352 UpdateLine(); 353 354 fTextBuffer->InsertSpace(fCurPos, num); 355 TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 356 } 357 358 359 //! Set or reset Insert mode 360 void 361 TermView::SetInsertMode(int flag) 362 { 363 UpdateLine(); 364 fInsertModeFlag = flag; 365 } 366 367 368 //! Draw region 369 inline int 370 TermView::TermDraw(const CurPos &start, const CurPos &end) 371 { 372 int x1 = start.x; 373 int y1 = start.y; 374 int x2 = end.x; 375 int y2 = end.y; 376 377 // Send Draw Rectangle data to Draw Engine thread. 378 SendDataToDrawEngine(x1, y1 + fTop / fFontHeight, 379 x2, y2 + fTop / fFontHeight); 380 return 0; 381 } 382 383 384 //! Draw region 385 int 386 TermView::TermDrawSelectedRegion(CurPos start, CurPos end) 387 { 388 CurPos inPos; 389 390 if (end < start) { 391 inPos = start; 392 start = end; 393 end = inPos; 394 } 395 396 if (start.y == end.y) { 397 SendDataToDrawEngine(start.x, start.y, 398 end.x, end.y); 399 } else { 400 SendDataToDrawEngine(start.x, start.y, 401 fTermColumns, start.y); 402 403 if (end.y - start.y > 0) 404 SendDataToDrawEngine(0, start.y + 1, fTermColumns, end.y - 1); 405 406 SendDataToDrawEngine(0, end.y, end.x, end.y); 407 } 408 409 return 0; 410 } 411 412 413 //! Draw region 414 int 415 TermView::TermDrawRegion(CurPos start, CurPos end) 416 { 417 CurPos inPos; 418 int top = fTop / fFontHeight; 419 420 if (end < start) { 421 inPos = start; 422 start = end; 423 end = inPos; 424 } 425 426 start.y += top; 427 end.y += top; 428 429 if (start.y == end.y) { 430 SendDataToDrawEngine(start.x, start.y, 431 end.x, end.y); 432 } else { 433 SendDataToDrawEngine(start.x, start.y, 434 fTermColumns - 1, start.y); 435 436 if (end.y - start.y > 0) { 437 SendDataToDrawEngine(0, start.y + 1, 438 fTermColumns - 1, end.y - 1); 439 } 440 SendDataToDrawEngine(0, end.y, 441 end.x, end.y); 442 } 443 444 return 0; 445 } 446 447 448 //! Erase below cursor below. 449 void 450 TermView::EraseBelow() 451 { 452 UpdateLine(); 453 454 fTextBuffer->EraseBelow(fCurPos); 455 TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 456 if (fCurPos.y != fTermRows - 1) 457 TermDraw(CurPos(0, fCurPos.y + 1), CurPos(fTermColumns - 1, fTermRows - 1)); 458 } 459 460 461 //! Delete num characters from current position. 462 void 463 TermView::DeleteChar(int num) 464 { 465 UpdateLine(); 466 467 fTextBuffer->DeleteChar(fCurPos, num); 468 TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 469 } 470 471 472 //! Delete cursor right characters. 473 void 474 TermView::DeleteColumns() 475 { 476 UpdateLine(); 477 478 fTextBuffer->DeleteChar(fCurPos, fTermColumns - fCurPos.x); 479 TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 480 } 481 482 483 //! Delete 'num' lines from current position with scrolling. 484 void 485 TermView::DeleteLine(int num) 486 { 487 ScrollRegion(fCurPos.y, -1, SCRUP, num); 488 } 489 490 491 //! Sets cursor position 492 void 493 TermView::SetCurPos(int x, int y) 494 { 495 UpdateLine(); 496 497 if (x >= 0 && x < fTermColumns) 498 fCurPos.x = x; 499 if (y >= 0 && y < fTermRows) 500 fCurPos.y = y; 501 } 502 503 504 //! Sets cursor x position 505 void 506 TermView::SetCurX(int x) 507 { 508 if (x >= 0 && x < fTermRows) { 509 UpdateLine(); 510 fCurPos.x = x; 511 } 512 } 513 514 515 //! Sets cursor y position 516 void 517 TermView::SetCurY(int y) 518 { 519 if (y >= 0 && y < fTermColumns) { 520 UpdateLine(); 521 fCurPos.y = y; 522 } 523 } 524 525 526 //! Gets cursor position 527 void 528 TermView::GetCurPos(CurPos *inCurPos) 529 { 530 inCurPos->x = fCurPos.x; 531 inCurPos->y = fCurPos.y; 532 } 533 534 535 //! Gets cursor x position 536 int 537 TermView::GetCurX() 538 { 539 return fCurPos.x; 540 } 541 542 543 //! Gets cursor y position 544 int 545 TermView::GetCurY() 546 { 547 return fCurPos.y; 548 } 549 550 551 //! Saves cursor position 552 void 553 TermView::SaveCursor() 554 { 555 fCurStack = fCurPos; 556 } 557 558 559 //! Restores cursor position 560 void 561 TermView::RestoreCursor() 562 { 563 UpdateLine(); 564 fCurPos = fCurStack; 565 } 566 567 568 //! Move cursor right by 'num' steps. 569 void 570 TermView::MoveCurRight(int num) 571 { 572 UpdateLine(); 573 574 if (fCurPos.x + num >= fTermColumns) { 575 // Wrap around 576 fCurPos.x = 0; 577 PutCR(); 578 PutLF(); 579 } else { 580 fCurPos.x += num; 581 } 582 } 583 584 585 //! Move cursor left by 'num' steps. 586 void 587 TermView::MoveCurLeft(int num) 588 { 589 UpdateLine(); 590 591 fCurPos.x -= num; 592 if (fCurPos.x < 0) 593 fCurPos.x = 0; 594 } 595 596 597 //! Move cursor up by 'num' steps. 598 void 599 TermView::MoveCurUp(int num) 600 { 601 UpdateLine(); 602 603 fCurPos.y -= num; 604 605 if (fCurPos.y < 0) 606 fCurPos.y = 0; 607 } 608 609 610 //! Move cursor down by 'num' steps. 611 void 612 TermView::MoveCurDown(int num) 613 { 614 UpdateLine(); 615 616 fCurPos.y += num; 617 618 if (fCurPos.y >= fTermRows) 619 fCurPos.y = fTermRows - 1; 620 } 621 622 623 void 624 TermView::DrawCursor() 625 { 626 CURSOR_RECT; 627 uchar buf[4]; 628 ushort attr; 629 630 int top = fTop / fFontHeight; 631 int m_flag = false; 632 633 m_flag = CheckSelectedRegion(CurPos(fCurPos.x, 634 fCurPos.y + fTop / fFontHeight)); 635 if (fTextBuffer->GetChar(fCurPos.y + top, fCurPos.x, buf, &attr) == A_CHAR) { 636 int width; 637 if (IS_WIDTH(attr)) 638 width = 2; 639 else 640 width = 1; 641 642 DrawLines(fCurPos.x * fFontWidth, 643 fCurPos.y * fFontHeight + fTop, 644 attr, buf, width, m_flag, true, this); 645 } else { 646 if (m_flag) 647 SetHighColor(fSelectBackColor); 648 else 649 SetHighColor(fCursorBackColor); 650 651 FillRect(r); 652 } 653 654 Sync(); 655 } 656 657 658 void 659 TermView::BlinkCursor() 660 { 661 if (fCursorDrawFlag == CURON 662 && fCursorBlinkingFlag == CURON 663 && Window()->IsActive()) { 664 if (fCursorStatus == CURON) 665 TermDraw(fCurPos, fCurPos); 666 else 667 DrawCursor(); 668 669 fCursorStatus = fCursorStatus == CURON ? CUROFF : CURON; 670 } 671 } 672 673 674 //! Draw / Clear cursor. 675 void 676 TermView::SetCurDraw(bool flag) 677 { 678 if (flag == CUROFF) { 679 if (fCursorStatus == CURON) 680 TermDraw(fCurPos, fCurPos); 681 682 fCursorStatus = CUROFF; 683 fCursorDrawFlag = CUROFF; 684 } else { 685 if (fCursorDrawFlag == CUROFF) { 686 fCursorDrawFlag = CURON; 687 fCursorStatus = CURON; 688 689 if (LockLooper()) { 690 DrawCursor(); 691 UnlockLooper(); 692 } 693 } 694 } 695 } 696 697 698 //! Sets cursor Blinking flag. 699 void 700 TermView::SetCurBlinking(bool flag) 701 { 702 fCursorBlinkingFlag = flag; 703 } 704 705 706 //! Scroll terminal dir directory by 'num' steps. 707 void 708 TermView::ScrollRegion(int top, int bot, int dir, int num) 709 { 710 UpdateLine(); 711 712 if (top == -1) 713 top = fScrTop; 714 715 if (bot == -1) 716 bot = fScrBot; 717 718 if (top < fScrTop) 719 top = fScrTop; 720 721 if (bot > fScrBot) 722 bot = fScrBot; 723 724 fTextBuffer->ScrollRegion(top, bot , dir ,num); 725 TermDraw(CurPos(0, top), CurPos(fTermColumns - 1, bot)); 726 } 727 728 729 //! Sets terminal scroll region. 730 void 731 TermView::SetScrollRegion(int top, int bot) 732 { 733 if (top >= 0 && top < fTermRows) { 734 if (bot >= 0 && bot < fTermRows) { 735 if (top > bot) { 736 fScrTop = bot; 737 fScrBot = top; 738 } else if (top < bot ) { 739 fScrTop = top; 740 fScrBot = bot; 741 } 742 } 743 } 744 745 if (fScrTop != 0 || fScrBot != fTermRows -1 ) 746 fScrRegionSet = 1; 747 else 748 fScrRegionSet = 0; 749 } 750 751 752 //! Scroll to cursor position. 753 void 754 TermView::ScrollAtCursor() 755 { 756 if (LockLooper()) { 757 ResizeScrBarRange(); 758 fScrollUpCount = 0; 759 ScrollTo(0, fTop); 760 UnlockLooper(); 761 } 762 } 763 764 765 thread_id 766 TermView::InitViewThread() 767 { 768 // spwan Draw Engine thread. 769 if (fViewThread < 0) { 770 fViewThread = spawn_thread(ViewThread, "DrawEngine", 771 B_DISPLAY_PRIORITY, this); 772 } else 773 return B_BAD_THREAD_ID; 774 775 fDrawRectSem = create_sem(0, "draw_engine_sem"); 776 resume_thread(fViewThread); 777 778 // spawn Mouse Tracking thread. 779 if (fMouseThread < 0) { 780 fMouseThread = spawn_thread(MouseTracking, "MouseTracking", 781 B_NORMAL_PRIORITY,this); 782 } else 783 return B_BAD_THREAD_ID; 784 785 resume_thread(fMouseThread); 786 787 return fViewThread; 788 } 789 790 791 //! Thread of Draw Character to View. 792 int32 793 TermView::ViewThread(void *data) 794 { 795 int width, height; 796 sDrawRect pos; 797 798 //#define INVALIDATE 799 #ifndef INVALIDATE 800 int i, j, count; 801 int m_flag; 802 ushort attr; 803 uchar buf[256]; 804 BRect eraseRect; 805 #endif 806 807 int inDrawRect_p = 0; 808 809 TermView *theObj =(TermView *)data; 810 811 while (theObj->fQuitting) { 812 // Wait semaphore 813 acquire_sem(theObj->fDrawRectSem); 814 815 pos = theObj->fDrawRectBuffer[inDrawRect_p]; 816 inDrawRect_p++; 817 inDrawRect_p %= RECT_BUF_SIZE; 818 819 width = theObj->fFontWidth; 820 height = theObj->fFontHeight; 821 822 #ifdef INVALIDATE 823 BRect r(pos.x1 * width, pos.y1 * height, 824 (pos.x2 + 1) * width -1,(pos.y2 + 1) * height -1); 825 826 if(theObj->LockLooper()) { 827 theObj->Invalidate(r); 828 theObj->UnlockLooper(); 829 } 830 #else 831 832 if (theObj->LockLooper()) { 833 for (j = pos.y1; j <= pos.y2; j++) { 834 for (i = pos.x1; i <= pos.x2;) { 835 count = theObj->fTextBuffer->GetString(j, i, pos.x2, buf, &attr); 836 m_flag = theObj->CheckSelectedRegion(CurPos(i, j)); 837 838 if (count < 0) { 839 eraseRect.Set(width * i, height * j, 840 width *(i - count) - 1, height *(j + 1) - 1); 841 842 if (m_flag) 843 theObj->SetHighColor(theObj->fSelectBackColor); 844 else 845 theObj->SetHighColor(theObj->fTextBackColor); 846 847 theObj->FillRect(eraseRect); 848 count = -count; 849 } else { 850 theObj->DrawLines(width * i, height * j, 851 attr, buf, count, m_flag, false, theObj); 852 } 853 i += count; 854 } 855 } 856 theObj->UnlockLooper(); 857 } 858 #endif 859 } 860 861 exit_thread(B_OK); 862 return 0; 863 } 864 865 866 //! Thread for tracking mouse. 867 int32 868 TermView::MouseTracking(void *data) 869 { 870 int32 code, selected = false; 871 uint32 button; 872 thread_id sender; 873 CurPos stpos, edpos; 874 BPoint stpoint, edpoint; 875 float scr_start, scr_end, scr_pos; 876 877 TermView *theObj =(TermView *)data; 878 879 while(theObj->fQuitting) { 880 881 if(1) { 882 #ifdef CHANGE_CURSOR_IMAGE 883 if(!has_data(find_thread(NULL))) { 884 BRect r; 885 886 if(theObj->fSelected 887 && ( gTermPref->getInt32(PREF_DRAGN_COPY) 888 || modifiers() & B_CONTROL_KEY)) { 889 890 if(theObj->LockLooper()) { 891 theObj->GetMouse(&stpoint, &button); 892 r = theObj->Bounds(); 893 theObj->UnlockLooper(); 894 } 895 if(r.Contains(stpoint)) { 896 CurPos tmppos = theObj->BPointToCurPos(stpoint); 897 if(theObj->fSelStart > theObj->fSelEnd) { 898 stpos = theObj->fSelEnd; 899 edpos = theObj->fSelStart; 900 } else { 901 stpos = theObj->fSelStart; 902 edpos = theObj->fSelEnd; 903 } 904 905 if(tmppos > stpos && tmppos < edpos) 906 be_app->SetCursor(M_ADD_CURSOR); 907 else 908 be_app->SetCursor(B_HAND_CURSOR); 909 } 910 } 911 snooze(50 * 1000); 912 continue; 913 } else { 914 #endif 915 code = receive_data(&sender,(void *)&stpoint, sizeof(BPoint)); 916 } 917 918 if(code != MOUSE_THR_CODE) 919 continue; 920 921 selected = theObj->fSelected; 922 edpoint.Set(-1, -1); 923 924 stpos = theObj->BPointToCurPos(stpoint); 925 926 do { 927 928 snooze(40 * 1000); 929 930 if(theObj->LockLooper()) { 931 theObj->GetMouse(&edpoint, &button); 932 theObj->UnlockLooper(); 933 } 934 935 edpos = theObj->BPointToCurPos(edpoint); 936 if (edpos.y < 0) 937 continue; 938 939 if(stpoint == edpoint) { 940 continue; 941 } else { 942 if(!selected) { 943 theObj->Select(stpos, edpos); 944 selected = true; 945 } else { 946 947 // Align cursor point to text. 948 if(stpos == edpos) 949 continue; 950 951 if(edpos > stpos) { 952 edpoint.x -= theObj->fFontWidth / 2; 953 edpos = theObj->BPointToCurPos(edpoint); 954 //edpos.x--; 955 if(edpos.x < 0) 956 edpos.x = 0; 957 } 958 else 959 if(edpos < stpos) { 960 edpoint.x += theObj->fFontWidth / 2; 961 edpos = theObj->BPointToCurPos(edpoint); 962 //edpos.x++; 963 if(edpos.x > theObj->fTermColumns) 964 edpos.x = theObj->fTermColumns; 965 } 966 967 // Scroll check 968 if(theObj->LockLooper()) { 969 970 // Get now scroll point 971 theObj->fScrollBar->GetRange(&scr_start, &scr_end); 972 scr_pos = theObj->fScrollBar->Value(); 973 974 if(edpoint.y < theObj->Bounds().LeftTop().y ) 975 976 // mouse point left of window 977 if(scr_pos != scr_start) 978 theObj->ScrollTo(0, edpoint.y); 979 980 if(edpoint.y > theObj->Bounds().LeftBottom().y) { 981 982 // mouse point left of window 983 if(scr_pos != scr_end) 984 theObj->ScrollTo(0, edpoint.y); 985 } 986 theObj->UnlockLooper(); 987 } 988 theObj->ResizeSelectRegion(edpos); 989 } 990 } 991 } while(button); 992 theObj->fMouseTracking = false; 993 } 994 995 exit_thread(B_OK); 996 return 0; 997 } 998 999 1000 //! Draw character on offscreen bitmap. 1001 void 1002 TermView::DrawLines(int x1, int y1, ushort attr, uchar *buf, 1003 int width, int mouse, int cursor, BView *inView) 1004 { 1005 int x2, y2; 1006 int forecolor, backcolor; 1007 rgb_color rgb_fore = fTextForeColor, rgb_back = fTextBackColor, rgb_tmp; 1008 1009 // Set Font. 1010 if (IS_WIDTH(attr)) 1011 inView->SetFont(&fFullFont); 1012 else 1013 inView->SetFont(&fHalfFont); 1014 1015 // Set pen point 1016 x2 = x1 + fFontWidth * width; 1017 y2 = y1 + fFontHeight; 1018 1019 // color attribute 1020 forecolor = IS_FORECOLOR(attr); 1021 backcolor = IS_BACKCOLOR(attr); 1022 1023 if (IS_FORESET(attr)) 1024 rgb_fore = gTermColorTable[forecolor]; 1025 1026 if (IS_BACKSET(attr)) 1027 rgb_back = gTermColorTable[backcolor]; 1028 1029 // Selection check. 1030 if (cursor) { 1031 rgb_fore = fCursorForeColor; 1032 rgb_back = fCursorBackColor; 1033 } else if (mouse){ 1034 rgb_fore = fSelectForeColor; 1035 rgb_back = fSelectBackColor; 1036 } else { 1037 // Reverse attribute(If selected area, don't reverse color). 1038 if (IS_INVERSE(attr)) { 1039 rgb_tmp = rgb_fore; 1040 rgb_fore = rgb_back; 1041 rgb_back = rgb_tmp; 1042 } 1043 } 1044 1045 // Fill color at Background color and set low color. 1046 inView->SetHighColor(rgb_back); 1047 inView->FillRect(BRect(x1, y1, x2 - 1, y2 - 1)); 1048 inView->SetLowColor(rgb_back); 1049 1050 inView->SetHighColor(rgb_fore); 1051 1052 // Draw character. 1053 inView->MovePenTo(x1, y1 + fFontAscent); 1054 inView->DrawString((char *) buf); 1055 1056 // bold attribute. 1057 if (IS_BOLD(attr)) { 1058 inView->MovePenTo(x1 + 1, y1 + fFontAscent); 1059 1060 inView->SetDrawingMode(B_OP_OVER); 1061 inView->DrawString((char *)buf); 1062 inView->SetDrawingMode(B_OP_COPY); 1063 } 1064 1065 // underline attribute 1066 if (IS_UNDER(attr)) { 1067 inView->MovePenTo(x1, y1 + fFontAscent); 1068 inView->StrokeLine(BPoint(x1 , y1 + fFontAscent), 1069 BPoint(x2 , y1 + fFontAscent)); 1070 } 1071 } 1072 1073 1074 //! Resize scroll bar range and knob size. 1075 void 1076 TermView::ResizeScrBarRange() 1077 { 1078 float viewheight, start_pos; 1079 1080 viewheight = fTermRows * fFontHeight; 1081 start_pos = fTop -(fScrBufSize - fTermRows *2) * fFontHeight; 1082 1083 if (start_pos > 0) { 1084 fScrollBar->SetRange(start_pos, viewheight + fTop - fFontHeight); 1085 } else { 1086 fScrollBar->SetRange(0, viewheight + fTop - fFontHeight); 1087 fScrollBar->SetProportion( viewheight /(viewheight + fTop)); 1088 } 1089 } 1090 1091 1092 //! Scrolls screen. 1093 void 1094 TermView::ScrollScreen() 1095 { 1096 fTop += fFontHeight; 1097 fScrollUpCount++; 1098 fTextBuffer->ScrollLine(); 1099 1100 if (fScrollUpCount > fTermRows ) { 1101 if (LockLooper()) { 1102 ResizeScrBarRange(); 1103 fScrollBarRange += fScrollUpCount; 1104 fScrollUpCount = 0; 1105 ScrollTo(0, fTop); 1106 UnlockLooper(); 1107 } 1108 } 1109 } 1110 1111 1112 //! Scrolls screen. 1113 void 1114 TermView::ScrollScreenDraw() 1115 { 1116 if (fScrollUpCount){ 1117 if (LockLooper()) { 1118 ResizeScrBarRange(); 1119 1120 fScrollBarRange += fScrollUpCount; 1121 fScrollUpCount = 0; 1122 ScrollTo(0, fTop); 1123 UnlockLooper(); 1124 } 1125 } 1126 } 1127 1128 1129 //! Handler for SIGWINCH 1130 void 1131 TermView::UpdateSIGWINCH() 1132 { 1133 struct winsize ws; 1134 1135 if (fFrameResized) { 1136 if (fSelected) 1137 TermDrawSelectedRegion(fSelStart, fSelEnd); 1138 ScrollTo(0, fTop); 1139 ResizeScrBarRange(); 1140 1141 ws.ws_row = fTermRows; 1142 ws.ws_col = fTermColumns; 1143 ioctl(pfd, TIOCSWINSZ, &ws); 1144 kill(-sh_pid, SIGWINCH); 1145 1146 fFrameResized = 0; 1147 if (fScrRegionSet == 0) 1148 fScrBot = fTermRows - 1; 1149 } 1150 } 1151 1152 1153 /*! Device Status. 1154 Q & D hack by Y.Hayakawa(hida@sawada.riec.tohoku.ac.jp) 1155 21-JUL-99 1156 */ 1157 void 1158 TermView::DeviceStatusReport(int n) 1159 { 1160 char sbuf[16] ; 1161 int len; 1162 1163 switch (n) { 1164 case 5: 1165 len = sprintf(sbuf,"\033[0n") ; 1166 write(pfd, sbuf, len); 1167 break ; 1168 case 6: 1169 len = sprintf(sbuf,"\033[%d;%dR", fTermRows, fTermColumns) ; 1170 write(pfd, sbuf, len); 1171 break ; 1172 default: 1173 return; 1174 } 1175 } 1176 1177 1178 //! Update line buffer. 1179 void 1180 TermView::UpdateLine() 1181 { 1182 if (fUpdateFlag == true) { 1183 if (fInsertModeFlag == MODE_INSERT) { 1184 TermDraw(CurPos(fBufferStartPos, fCurPos.y), 1185 CurPos(fTermColumns - 1, fCurPos.y)); 1186 } else { 1187 TermDraw(CurPos(fBufferStartPos, fCurPos.y), 1188 CurPos(fCurPos.x - 1, fCurPos.y)); 1189 } 1190 fUpdateFlag = false; 1191 } 1192 } 1193 1194 1195 void 1196 TermView::AttachedToWindow() 1197 { 1198 SetFont(&fHalfFont); 1199 MakeFocus(true); 1200 fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows); 1201 } 1202 1203 1204 void 1205 TermView::Draw(BRect updateRect) 1206 { 1207 if (IsPrinting()) { 1208 DoPrint(updateRect); 1209 return; 1210 } 1211 1212 int x1, x2, y1, y2; 1213 int i, j, k, count; 1214 ushort attr; 1215 uchar buf[256]; 1216 int m_flag; 1217 BRect eraseRect; 1218 1219 x1 =(int)updateRect.left / fFontWidth; 1220 x2 =(int)updateRect.right / fFontWidth; 1221 1222 y1 =(int)updateRect.top / fFontHeight; 1223 y2 =(int)updateRect.bottom / fFontHeight; 1224 1225 Window()->BeginViewTransaction(); 1226 1227 for (j = y1; j <= y2; j++) { 1228 // If(x1, y1) Buffer is in string full width character, 1229 // alignment start position. 1230 1231 k = x1; 1232 if (fTextBuffer->GetChar(j, k, buf, &attr) == IN_STRING) 1233 k--; 1234 1235 if (k < 0) 1236 k = 0; 1237 1238 for (i = k; i <= x2;) { 1239 count = fTextBuffer->GetString(j, i, x2, buf, &attr); 1240 m_flag = CheckSelectedRegion(CurPos(i, j)); 1241 1242 if (count < 0) { 1243 if (m_flag) { 1244 eraseRect.Set(fFontWidth * i, 1245 fFontHeight * j, 1246 fFontWidth *(i - count) -1, 1247 fFontHeight *(j + 1) -1); 1248 1249 SetHighColor(fSelectBackColor); 1250 FillRect(eraseRect); 1251 } 1252 i += abs(count); 1253 continue; 1254 } 1255 1256 DrawLines(fFontWidth * i, fFontHeight * j, 1257 attr, buf, count, m_flag, false, this); 1258 i += count; 1259 if (i >= fTermColumns) 1260 break; 1261 } 1262 } 1263 1264 if (fCursorStatus == CURON) 1265 DrawCursor(); 1266 1267 Window()->EndViewTransaction(); 1268 } 1269 1270 1271 void 1272 TermView::DoPrint(BRect updateRect) 1273 { 1274 ushort attr; 1275 uchar buf[256]; 1276 1277 const int numLines =(int)((updateRect.Height()) / fFontHeight); 1278 1279 int y1 =(int)updateRect.top / fFontHeight; 1280 y1 = y1 -(fScrBufSize - numLines * 2); 1281 if (y1 < 0) 1282 y1 = 0; 1283 1284 const int y2 = y1 + numLines -1; 1285 1286 const int x1 =(int)updateRect.left / fFontWidth; 1287 const int x2 =(int)updateRect.right / fFontWidth; 1288 1289 for (int j = y1; j <= y2; j++) { 1290 // If(x1, y1) Buffer is in string full width character, 1291 // alignment start position. 1292 1293 int k = x1; 1294 if (fTextBuffer->GetChar(j, k, buf, &attr) == IN_STRING) 1295 k--; 1296 1297 if (k < 0) 1298 k = 0; 1299 1300 for (int i = k; i <= x2;) { 1301 int count = fTextBuffer->GetString(j, i, x2, buf, &attr); 1302 if (count < 0) { 1303 i += abs(count); 1304 continue; 1305 } 1306 1307 DrawLines(fFontWidth * i, fFontHeight * j, 1308 attr, buf, count, false, false, this); 1309 i += count; 1310 } 1311 } 1312 } 1313 1314 1315 void 1316 TermView::WindowActivated(bool active) 1317 { 1318 if (active == false) { 1319 // DoIMConfirm(); 1320 } 1321 1322 if (active && fMouseImage) 1323 be_app->SetCursor(B_I_BEAM_CURSOR); 1324 } 1325 1326 1327 void 1328 TermView::KeyDown(const char *bytes, int32 numBytes) 1329 { 1330 char c; 1331 struct termios tio; 1332 int32 key, mod; 1333 1334 uchar dstbuf[1024]; 1335 Looper()->CurrentMessage()->FindInt32("modifiers", &mod); 1336 Looper()->CurrentMessage()->FindInt32("key", &key); 1337 1338 if (fIMflag) 1339 return; 1340 1341 // If bytes[0] equal intr charactor, 1342 // send signal to shell process group. 1343 tcgetattr(pfd, &tio); 1344 if (*bytes == tio.c_cc[VINTR]) { 1345 if(tio.c_lflag & ISIG) 1346 kill(-sh_pid, SIGINT); 1347 } 1348 1349 // Terminal changes RET, ENTER, F1...F12, and ARROW key code. 1350 1351 if (numBytes == 1) { 1352 1353 switch (*bytes) { 1354 case B_RETURN: 1355 c = 0x0d; 1356 if (key == RETURN_KEY || key == ENTER_KEY) { 1357 write(pfd, &c, 1); 1358 return; 1359 } else { 1360 write(pfd, bytes, numBytes); 1361 return; 1362 } 1363 break; 1364 1365 case B_LEFT_ARROW: 1366 if (key == LEFT_ARROW_KEY) { 1367 write(pfd, LEFT_ARROW_KEY_CODE, sizeof(LEFT_ARROW_KEY_CODE)-1); 1368 return; 1369 } 1370 break; 1371 1372 case B_RIGHT_ARROW: 1373 if (key == RIGHT_ARROW_KEY) { 1374 write(pfd, RIGHT_ARROW_KEY_CODE, sizeof(RIGHT_ARROW_KEY_CODE)-1); 1375 return; 1376 } 1377 break; 1378 1379 case B_UP_ARROW: 1380 if (mod & B_SHIFT_KEY) { 1381 if (Bounds().top <= 0) 1382 return; 1383 ScrollBy(0, -fFontHeight); 1384 Window()->UpdateIfNeeded(); 1385 return; 1386 } 1387 1388 if (key == UP_ARROW_KEY) { 1389 write(pfd, UP_ARROW_KEY_CODE, sizeof(UP_ARROW_KEY_CODE)-1); 1390 return; 1391 } 1392 break; 1393 1394 case B_DOWN_ARROW: 1395 if (mod & B_SHIFT_KEY) { 1396 ScrollBy(0, fFontHeight); 1397 Window()->UpdateIfNeeded(); 1398 return; 1399 } 1400 1401 if (key == DOWN_ARROW_KEY) { 1402 write(pfd, DOWN_ARROW_KEY_CODE, sizeof(DOWN_ARROW_KEY_CODE)-1); 1403 return; 1404 } 1405 break; 1406 1407 case B_INSERT: 1408 if (key == INSERT_KEY) { 1409 write(pfd, INSERT_KEY_CODE, sizeof(INSERT_KEY_CODE)-1); 1410 return; 1411 } 1412 break; 1413 1414 case B_HOME: 1415 if (key == HOME_KEY) { 1416 write(pfd, HOME_KEY_CODE, sizeof(HOME_KEY_CODE)-1); 1417 return; 1418 } 1419 break; 1420 1421 case B_PAGE_UP: 1422 if (mod & B_SHIFT_KEY) { 1423 if (Bounds().top <= 0) 1424 return; 1425 ScrollBy(0, -fFontHeight * fTermRows ); 1426 Window()->UpdateIfNeeded(); 1427 return; 1428 } 1429 1430 if (key == PAGE_UP_KEY) { 1431 write(pfd, PAGE_UP_KEY_CODE, sizeof(PAGE_UP_KEY_CODE)-1); 1432 return; 1433 } 1434 break; 1435 1436 case B_PAGE_DOWN: 1437 if (mod & B_SHIFT_KEY) { 1438 ScrollBy(0, fFontHeight * fTermRows); 1439 Window()->UpdateIfNeeded(); 1440 return; 1441 } 1442 1443 if (key == PAGE_DOWN_KEY) { 1444 write(pfd, PAGE_DOWN_KEY_CODE, sizeof(PAGE_DOWN_KEY_CODE)-1); 1445 return; 1446 } 1447 break; 1448 1449 case B_END: 1450 if (key == END_KEY) { 1451 write(pfd, END_KEY_CODE, sizeof(END_KEY_CODE)-1); 1452 return; 1453 } 1454 break; 1455 1456 case B_FUNCTION_KEY: 1457 for (c = 0; c < 12; c++) { 1458 if (key == function_keycode_table[c]) { 1459 write(pfd, function_key_char_table[c], 5); 1460 return; 1461 } 1462 } 1463 break; 1464 1465 default: 1466 break; 1467 } 1468 } else { 1469 // input multibyte character 1470 1471 if (gNowCoding != M_UTF8) { 1472 int cnum = fCodeConv->ConvertFromInternal(bytes, numBytes, 1473 (char *)dstbuf, gNowCoding); 1474 write(pfd, dstbuf, cnum); 1475 return; 1476 } 1477 } 1478 1479 write(pfd, bytes, numBytes); 1480 } 1481 1482 1483 void 1484 TermView::FrameResized(float width, float height) 1485 { 1486 const int cols =((int)width + 1) / fFontWidth; 1487 const int rows =((int)height + 1) / fFontHeight; 1488 1489 int offset = 0; 1490 1491 if (rows < fCurPos.y + 1) { 1492 fTop +=(fCurPos.y + 1 - rows) * fFontHeight; 1493 offset = fCurPos.y + 1 - rows; 1494 fCurPos.y = rows - 1; 1495 } 1496 fTextBuffer->ResizeTo(rows, cols, offset); 1497 fTermRows = rows; 1498 fTermColumns = cols; 1499 1500 fFrameResized = 1; 1501 } 1502 1503 1504 void 1505 TermView::MessageReceived(BMessage *msg) 1506 { 1507 entry_ref ref; 1508 char *ctrl_l = ""; 1509 1510 switch (msg->what){ 1511 case B_SIMPLE_DATA: 1512 { 1513 int32 i = 0; 1514 if (msg->FindRef("refs", i++, &ref) == B_OK) { 1515 DoFileDrop(ref); 1516 1517 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1518 WritePTY((const uchar*)" ", 1); 1519 DoFileDrop(ref); 1520 } 1521 } else 1522 BView::MessageReceived(msg); 1523 break; 1524 } 1525 1526 case B_MIME_DATA: 1527 { 1528 char *text; 1529 int32 numBytes; 1530 status_t sts; 1531 1532 if (msg->WasDropped()) { 1533 sts = msg->FindData("text/plain", 1534 B_MIME_TYPE, (const void **)&text, &numBytes); 1535 if (sts != B_OK) 1536 break; 1537 1538 WritePTY((uchar *)text, numBytes); 1539 } 1540 break; 1541 } 1542 1543 case B_COPY: 1544 DoCopy(); 1545 break; 1546 1547 case B_PASTE: 1548 { 1549 int32 code; 1550 if (msg->FindInt32("index", &code) == B_OK) 1551 DoPaste(); 1552 break; 1553 } 1554 1555 case B_SELECT_ALL: 1556 DoSelectAll(); 1557 break; 1558 1559 case MENU_CLEAR_ALL: 1560 DoClearAll(); 1561 write(pfd, ctrl_l, 1); 1562 break; 1563 1564 case MSGRUN_CURSOR: 1565 BlinkCursor(); 1566 break; 1567 1568 // case B_INPUT_METHOD_EVENT: 1569 // { 1570 // int32 op; 1571 // msg->FindInt32("be:opcode", &op); 1572 // switch(op){ 1573 // case B_INPUT_METHOD_STARTED: 1574 //DoIMStart(msg); 1575 // break; 1576 1577 // case B_INPUT_METHOD_STOPPED: 1578 // DoIMStop(msg); 1579 // break; 1580 1581 // case B_INPUT_METHOD_CHANGED: 1582 // DoIMChange(msg); 1583 // break; 1584 1585 // case B_INPUT_METHOD_LOCATION_REQUEST: 1586 // DoIMLocation(msg); 1587 // break; 1588 // } 1589 // } 1590 default: 1591 BView::MessageReceived(msg); 1592 break; 1593 } 1594 } 1595 1596 1597 //! Gets dropped file full path and display it at cursor position. 1598 void 1599 TermView::DoFileDrop(entry_ref &ref) 1600 { 1601 BEntry ent(&ref); 1602 BPath path(&ent); 1603 BString string(path.Path()); 1604 1605 string.CharacterEscape(" ~`#$&*()\\|[]{};'\"<>?!",'\\'); 1606 WritePTY((const uchar *)string.String(), string.Length()); 1607 } 1608 1609 1610 //! Copy selected text to Clipboard. 1611 void 1612 TermView::DoCopy() 1613 { 1614 if (!fSelected) 1615 return; 1616 1617 BString copyStr; 1618 fTextBuffer->GetStringFromRegion(copyStr); 1619 1620 if (be_clipboard->Lock()) { 1621 BMessage *clipMsg = NULL; 1622 be_clipboard->Clear(); 1623 1624 if ((clipMsg = be_clipboard->Data()) != NULL) { 1625 clipMsg->AddData("text/plain", B_MIME_TYPE, copyStr.String(), 1626 copyStr.Length()); 1627 be_clipboard->Commit(); 1628 } 1629 be_clipboard->Unlock(); 1630 } 1631 1632 // Deselecting the current selection is not the behavior that 1633 // R5's Terminal app displays. We want to mimic the behavior, so we will 1634 // no longer do the deselection 1635 // if(!fMouseTracking) 1636 // DeSelect(); 1637 } 1638 1639 1640 //! Paste clipboard text at cursor position. 1641 void 1642 TermView::DoPaste() 1643 { 1644 if (be_clipboard->Lock()) { 1645 BMessage *clipMsg = be_clipboard->Data(); 1646 char *text; 1647 ssize_t numBytes; 1648 if (clipMsg->FindData("text/plain", B_MIME_TYPE, 1649 (const void **)&text, &numBytes) == B_OK ) { 1650 // Clipboard text doesn't attached EOF? 1651 text[numBytes] = '\0'; 1652 WritePTY((uchar *)text, numBytes); 1653 } 1654 1655 be_clipboard->Unlock(); 1656 } 1657 } 1658 1659 1660 //! Select all displayed text and text /in buffer. 1661 void 1662 TermView::DoSelectAll(void) 1663 { 1664 CurPos start, end; 1665 int screen_top; 1666 int viewheight, start_pos; 1667 1668 screen_top = fTop / fFontHeight; 1669 viewheight = fTermRows; 1670 1671 start_pos = screen_top -(fScrBufSize - viewheight * 2); 1672 1673 start.x = 0; 1674 end.x = fTermColumns -1; 1675 1676 if(start_pos > 0) 1677 start.y = start_pos; 1678 else 1679 start.y = 0; 1680 1681 end.y = fCurPos.y + screen_top; 1682 1683 Select(start, end); 1684 } 1685 1686 // Clear display and text buffer, then moves Cursorr at home position. 1687 void 1688 TermView::DoClearAll(void) 1689 { 1690 DeSelect(); 1691 fTextBuffer->ClearAll(); 1692 1693 fTop = 0; 1694 ScrollTo(0, 0); 1695 1696 if(LockLooper()) { 1697 SetHighColor(fTextBackColor); 1698 1699 FillRect(Bounds()); 1700 SetHighColor(fTextForeColor); 1701 UnlockLooper(); 1702 } 1703 1704 // reset cursor pos 1705 SetCurPos(0, 0); 1706 1707 // reset selection. 1708 fSelected = false; 1709 1710 fScrollBar->SetRange(0, 0); 1711 fScrollBar->SetProportion(1); 1712 } 1713 1714 // Substitution meta character 1715 int 1716 TermView::SubstMetaChar(const char *p, char *q) 1717 { 1718 char *metachar = " ~`#$&*()\\|[]{};'\"<>?!"; 1719 int num_char = 0; 1720 1721 while(*p) { 1722 1723 char *mp = metachar; 1724 for(int i = 0; i < 22; i++){ 1725 1726 if(*p == *mp++) { 1727 *q++ = '\\'; 1728 num_char++; 1729 break; 1730 } 1731 } 1732 1733 *q++ = *p++; 1734 num_char++; 1735 } 1736 1737 // Add null string 1738 *q = *p; 1739 return num_char + 1; 1740 } 1741 1742 1743 /*! Write strings to PTY device. If encoding system isn't UTF8, change 1744 encoding to UTF8 before writing PTY. 1745 */ 1746 void 1747 TermView::WritePTY(const uchar *text, int numBytes) 1748 { 1749 if (gNowCoding != M_UTF8) { 1750 uchar *destBuffer = (uchar *)malloc(numBytes * 3); 1751 numBytes = fCodeConv->ConvertFromInternal((char*)text, numBytes, 1752 (char*)destBuffer, gNowCoding); 1753 write(pfd, destBuffer, numBytes); 1754 free(destBuffer); 1755 } else { 1756 write(pfd, text, numBytes); 1757 } 1758 } 1759 1760 1761 //! Make encoding pop up menu (make copy of window's one.) 1762 void 1763 TermView::SetupPop(void) 1764 { 1765 fPopMenu = new BPopUpMenu(""); 1766 MakeEncodingMenu(fPopMenu, gNowCoding, true); 1767 fPopMenu->SetTargetForItems(Window()); 1768 } 1769 1770 // convert button name to number. 1771 int32 1772 TermView::SetupMouseButton(const char *bname) 1773 { 1774 if(!strcmp(bname, "Disable")) { 1775 return 0; 1776 } 1777 else if(!strcmp(bname, "Button 1")) { 1778 return B_PRIMARY_MOUSE_BUTTON; 1779 } 1780 else if(!strcmp(bname, "Button 2")) { 1781 return B_SECONDARY_MOUSE_BUTTON; 1782 } 1783 else if(!strcmp(bname, "Button 3")) { 1784 return B_TERTIARY_MOUSE_BUTTON; 1785 1786 } else { 1787 1788 return 0; //Disable 1789 } 1790 } 1791 1792 void 1793 TermView::MouseDown(BPoint where) 1794 { 1795 int32 buttons = 0, clicks = 0; 1796 int32 mod; 1797 BPoint inPoint; 1798 ssize_t num_bytes; 1799 1800 Window()->CurrentMessage()->FindInt32("buttons", &buttons); 1801 1802 // paste button 1803 if(buttons == mPasteMenuButton) { 1804 1805 if(fSelected) { 1806 // If selected region, copy text from region. 1807 BString copyStr(""); 1808 1809 fTextBuffer->GetStringFromRegion(copyStr); 1810 1811 num_bytes = copyStr.Length(); 1812 WritePTY((uchar *)copyStr.String(), num_bytes); 1813 } 1814 else { 1815 // If don't selected, copy text from clipboard. 1816 DoPaste(); 1817 } 1818 return; 1819 } 1820 1821 // Select Region 1822 if(buttons == mSelectButton) { 1823 1824 Window()->CurrentMessage()->FindInt32("modifiers", &mod); 1825 Window()->CurrentMessage()->FindInt32("clicks", &clicks); 1826 1827 if(fSelected) { 1828 1829 CurPos inPos, stPos, edPos; 1830 1831 if(fSelStart < fSelEnd) { 1832 stPos = fSelStart; 1833 edPos = fSelEnd; 1834 1835 } else { 1836 stPos = fSelEnd; 1837 edPos = fSelStart; 1838 } 1839 1840 inPos = BPointToCurPos(where); 1841 1842 // If mouse pointer is avove selected Region, start Drag'n Copy. 1843 if(inPos > stPos && inPos < edPos) { 1844 if(mod & B_CONTROL_KEY || gTermPref->getInt32(PREF_DRAGN_COPY)) { 1845 1846 BPoint p; 1847 uint32 bt; 1848 1849 do { 1850 1851 GetMouse(&p, &bt); 1852 1853 if(bt == 0) { 1854 DeSelect(); 1855 return; 1856 } 1857 1858 snooze(40 * 1000); 1859 1860 } while(abs((int)(where.x - p.x)) < 4 1861 && abs((int)(where.y - p.y)) < 4); 1862 1863 BString copyStr(""); 1864 fTextBuffer->GetStringFromRegion(copyStr); 1865 1866 BMessage msg(B_MIME_TYPE); 1867 msg.AddData("text/plain", 1868 B_MIME_TYPE, 1869 copyStr.String(), 1870 copyStr.Length()); 1871 1872 BPoint st = CurPosToBPoint(stPos); 1873 BPoint ed = CurPosToBPoint(edPos); 1874 BRect r; 1875 1876 if(stPos.y == edPos.y) { 1877 r.Set(st.x, st.y - fTop, 1878 ed.x + fFontWidth, ed.y + fFontHeight - fTop); 1879 1880 } else { 1881 1882 r.Set(0, st.y - fTop, 1883 fTermColumns * fFontWidth, ed.y + fFontHeight - fTop); 1884 } 1885 1886 r = r & Bounds(); 1887 1888 DragMessage(&msg, r); 1889 return; 1890 } 1891 } 1892 } 1893 1894 // If mouse has a lot of movement, disable double/triple click. 1895 inPoint = fPreviousMousePoint - where; 1896 if(abs((int)inPoint.x) > 16 || abs((int)inPoint.y) > 16) clicks = 1; 1897 fPreviousMousePoint = where; 1898 1899 if(mod & B_SHIFT_KEY) { 1900 AddSelectRegion(BPointToCurPos(where)); 1901 } else { 1902 DeSelect(); 1903 } 1904 1905 // If clicks larger than 3, reset mouse click counter. 1906 clicks = clicks % 3; 1907 if(clicks == 0) clicks = 3; 1908 1909 switch(clicks){ 1910 case 1: 1911 fMouseTracking = true; 1912 send_data(fMouseThread, 1913 MOUSE_THR_CODE, 1914 (void *)&where, 1915 sizeof(BPoint)); 1916 break; 1917 1918 case 2: 1919 SelectWord(where, mod); 1920 break; 1921 1922 case 3: 1923 SelectLine(where, mod); 1924 break; 1925 } 1926 return; 1927 } 1928 1929 // Sub menu(coding popup menu) 1930 if(buttons == mSubMenuButton){ 1931 ConvertToScreen(&where); 1932 SetupPop(); 1933 fPopMenu->Go(where, true); 1934 delete fPopMenu; 1935 return; 1936 } 1937 1938 BView::MouseDown(where); 1939 } 1940 1941 void 1942 TermView::MouseMoved(BPoint, uint32 transit, const BMessage *) 1943 { 1944 if(fMouseImage && Window()->IsActive()) { 1945 1946 if(transit == B_ENTERED_VIEW) 1947 be_app->SetCursor(B_I_BEAM_CURSOR); 1948 if(transit == B_EXITED_VIEW) 1949 be_app->SetCursor(B_HAND_CURSOR); 1950 } 1951 } 1952 1953 1954 // Select a range of text 1955 void 1956 TermView::Select(CurPos start, CurPos end) 1957 { 1958 uchar buf[4]; 1959 ushort attr; 1960 1961 if(start.x < 0) 1962 start.x = 0; 1963 if(end.x >= fTermColumns) 1964 end.x = fTermColumns - 1; 1965 1966 if(fTextBuffer->GetChar(start.y, start.x, buf, &attr) == IN_STRING) { 1967 start.x--; 1968 if(start.x < 0) start.x = 0; 1969 } 1970 1971 if(fTextBuffer->GetChar(end.y, end.x, buf, &attr) == IN_STRING) { 1972 end.x++; 1973 if(end.x >= fTermColumns) end.x = fTermColumns; 1974 } 1975 1976 fSelStart = start; 1977 fSelEnd = end; 1978 1979 fTextBuffer->Select(fSelStart, fSelEnd); 1980 TermDrawSelectedRegion(fSelStart, fSelEnd); 1981 fSelected = true; 1982 } 1983 1984 // Add select region(shift + mouse click) 1985 void 1986 TermView::AddSelectRegion(CurPos pos) 1987 { 1988 uchar buf[4]; 1989 ushort attr; 1990 CurPos start, end, inPos; 1991 1992 if(!fSelected) 1993 return; 1994 1995 // error check, and if mouse point to a plase full width character, 1996 // select point decliment. 1997 if(pos.x >= fTermColumns) 1998 pos.x = fTermColumns - 1; 1999 else 2000 if(pos.x < 0) 2001 pos.x = 0; 2002 2003 if(pos.y < 0) 2004 pos.y = 0; 2005 2006 if(fTextBuffer->GetChar(pos.y, pos.x, buf, &attr) == IN_STRING) { 2007 pos.x++; 2008 if(pos.x >= fTermColumns) 2009 pos.x = fTermColumns - 1; 2010 } 2011 2012 start = fSelStart; 2013 end = fSelEnd; 2014 2015 // Mouse point is same as selected line. 2016 if(pos.y == fSelStart.y && pos.y == fSelEnd.y) { 2017 2018 if(abs(pos.x - start.x) > abs(pos.x - end.x)) { 2019 2020 fSelStart = start; 2021 fSelEnd = pos; 2022 inPos = end; 2023 2024 } else { 2025 2026 fSelStart = end; 2027 fSelEnd = pos; 2028 inPos = start; 2029 } 2030 } 2031 2032 // else, End point set to near the start or end point. 2033 else 2034 if(abs(pos.y - start.y) > abs(pos.y - end.y)) { 2035 fSelStart = start; 2036 fSelEnd = pos; 2037 inPos = end; 2038 } 2039 else if(abs(pos.y - start.y) > abs(pos.y - end.y)) { 2040 fSelStart = end; 2041 fSelEnd = pos; 2042 inPos = start; 2043 2044 } else { 2045 2046 if(start > end) { 2047 inPos = start; 2048 start = end; 2049 end = inPos; 2050 } 2051 2052 if(pos.y < start.y) { 2053 fSelStart = end; 2054 fSelEnd = pos; 2055 inPos = start; 2056 } else { 2057 fSelStart = start; 2058 fSelEnd = pos; 2059 inPos = end; 2060 } 2061 } 2062 2063 fTextBuffer->Select(fSelStart, fSelEnd); 2064 TermDrawSelectedRegion(inPos, fSelEnd); 2065 } 2066 2067 // Resize select region (mouse drag) 2068 void 2069 TermView::ResizeSelectRegion(CurPos pos) 2070 { 2071 CurPos inPos; 2072 uchar buf[4]; 2073 ushort attr; 2074 2075 inPos = fSelEnd; 2076 2077 // error check, and if mouse point to a plase full width character, 2078 // select point decliment. 2079 if(pos.x >= fTermColumns) 2080 pos.x = fTermColumns - 1; 2081 else 2082 if(pos.x < 0) 2083 pos.x = 0; 2084 2085 if(pos.y < 0) 2086 pos.y = 0; 2087 2088 if(fTextBuffer->GetChar(pos.y, pos.x, buf, &attr) == IN_STRING) { 2089 2090 pos.x++; 2091 2092 if(pos == inPos) 2093 return; 2094 2095 if(pos.x >= fTermColumns) 2096 pos.x = fTermColumns - 1; 2097 } 2098 fSelEnd = pos; 2099 2100 fTextBuffer->Select(fSelStart, pos); 2101 TermDrawSelectedRegion(inPos, pos); 2102 } 2103 2104 2105 // DeSelect a range of text 2106 void 2107 TermView::DeSelect(void) 2108 { 2109 CurPos start, end; 2110 2111 if(!fSelected) 2112 return; 2113 2114 fTextBuffer->DeSelect(); 2115 2116 start = fSelStart; 2117 end = fSelEnd; 2118 2119 fSelStart.Set(-1, -1); 2120 fSelEnd.Set(-1, -1); 2121 2122 TermDrawSelectedRegion(start, end); 2123 2124 fSelected = false; 2125 } 2126 2127 void 2128 TermView::SelectWord(BPoint where, int mod) 2129 { 2130 CurPos start, end, pos; 2131 bool flag; 2132 2133 pos = BPointToCurPos(where); 2134 flag = fTextBuffer->FindWord(pos, &start, &end); 2135 2136 if(mod & B_SHIFT_KEY) { 2137 2138 if(flag) { 2139 2140 if(start < fSelStart) { 2141 AddSelectRegion(start); 2142 } 2143 else 2144 if(end > fSelEnd) { 2145 AddSelectRegion(end); 2146 } 2147 2148 } else { 2149 AddSelectRegion(pos); 2150 } 2151 2152 } else { 2153 DeSelect(); 2154 if(flag) 2155 Select(start, end); 2156 } 2157 } 2158 2159 void 2160 TermView::SelectLine(BPoint where, int mod) 2161 { 2162 CurPos start, end, pos; 2163 2164 pos = BPointToCurPos(where); 2165 2166 if(mod & B_SHIFT_KEY) { 2167 2168 start = CurPos(0, pos.y); 2169 end = CurPos(fTermColumns - 1, pos.y); 2170 2171 if(start < fSelStart) { 2172 AddSelectRegion(start); 2173 } 2174 else 2175 if(end > fSelEnd) { 2176 AddSelectRegion(end); 2177 } 2178 2179 } else { 2180 DeSelect(); 2181 Select(CurPos(0, pos.y), CurPos(fTermColumns - 1, pos.y)); 2182 } 2183 } 2184 2185 // Convert View visible area corrdination to cursor position. 2186 CurPos 2187 TermView::BPointToCurPos(const BPoint &p) 2188 { 2189 return CurPos(p.x / fFontWidth, p.y / fFontHeight); 2190 } 2191 2192 // Convert cursor position to view coordination. 2193 BPoint 2194 TermView::CurPosToBPoint(const CurPos &pos) 2195 { 2196 return BPoint(fFontWidth * pos.x, pos.y * fFontHeight + fTop); 2197 } 2198 2199 bool 2200 TermView::CheckSelectedRegion(const CurPos &pos) 2201 { 2202 CurPos start, end; 2203 2204 if(fSelStart > fSelEnd) { 2205 start = fSelEnd; 2206 end = fSelStart; 2207 2208 } else { 2209 2210 start = fSelStart; 2211 end = fSelEnd; 2212 } 2213 2214 if(pos >= start && pos <= end) 2215 return true; 2216 2217 return false; 2218 2219 } 2220 2221 void 2222 TermView::GetFrameSize(float *width, float *height) 2223 { 2224 if(width != NULL) 2225 *width = fTermColumns * fFontWidth; 2226 2227 if(height == NULL) 2228 return; 2229 2230 if(!fTop){ 2231 *height = fTermRows * fFontHeight; 2232 return; 2233 } 2234 2235 if(fTop - fTermRows * fFontHeight > fScrBufSize * fFontHeight) { 2236 2237 *height = fScrBufSize * fFontHeight; 2238 return; 2239 } 2240 2241 *height = fTop + fTermRows * fFontHeight; 2242 } 2243 2244 // Sets terninal rows and cols. 2245 void 2246 TermView::GetFontInfo(int *width, int *height) 2247 { 2248 *width = fFontWidth; 2249 *height = fFontHeight; 2250 } 2251 2252 // Find a string, and select it if found 2253 bool 2254 TermView::Find (const BString &str, bool forwardSearch, bool matchCase, bool matchWord) 2255 { 2256 BString buffer; 2257 CurPos selectionstart, selectionend; 2258 int offset = 0; 2259 int initialresult = -1; 2260 int result = B_ERROR; 2261 2262 //Get the buffer contents 2263 fTextBuffer->ToString(buffer); 2264 2265 selectionstart = fTextBuffer->GetSelectionStart(); 2266 selectionend = fTextBuffer->GetSelectionEnd(); 2267 2268 if (selectionstart.x >= 0 || selectionstart.y >= 0) { 2269 if (forwardSearch) 2270 //Set the offset to the end of the selection 2271 offset = (selectionend.y) * fTermColumns + selectionend.x; 2272 else 2273 offset = (selectionstart.y) * fTermColumns + selectionstart.x; 2274 } 2275 2276 restart: 2277 //Actual search 2278 if (forwardSearch) { 2279 if (matchCase) { 2280 result = buffer.FindFirst(str, offset); 2281 } else { 2282 result = buffer.IFindFirst(str, offset); 2283 } 2284 } else { 2285 if (matchCase) { 2286 result = buffer.FindLast(str, offset); 2287 } else { 2288 result = buffer.IFindLast(str, offset); 2289 } 2290 } 2291 if (result == B_ERROR) { //Wrap search like Be's Terminal 2292 if (forwardSearch) { 2293 if (matchCase) { 2294 result = buffer.FindFirst(str, 0); 2295 } else { 2296 result = buffer.IFindFirst(str, 0); 2297 } 2298 } else { 2299 if (matchCase) { 2300 result = buffer.FindLast(str, buffer.Length()); 2301 } else { 2302 result = buffer.IFindLast(str, buffer.Length()); 2303 } 2304 } 2305 } 2306 2307 if (result < 0) 2308 return false; 2309 2310 if (matchWord) { 2311 if (isalnum(buffer.ByteAt(result - 1)) || isalnum(buffer.ByteAt(result + str.Length()))) { 2312 if (initialresult == -1) //Set the initial offset to the first result to aid word matching 2313 initialresult = result; 2314 else if (initialresult == result) //We went round the buffer, nothing found 2315 return false; 2316 if (forwardSearch) 2317 offset = result + str.Length(); 2318 else 2319 offset = result; 2320 goto restart; 2321 } 2322 } 2323 2324 //Select the found text 2325 selectionstart.y = result / fTermColumns; 2326 selectionstart.x = result % fTermColumns; 2327 //Length -1 since it seems to count the \0 as well 2328 selectionend.y = (result + str.Length() - 1) / fTermColumns; 2329 selectionend.x = (result + str.Length() - 1) % fTermColumns; 2330 //Update the contents of the view 2331 DeSelect(); 2332 Select(selectionstart, selectionend); 2333 2334 return true; 2335 } 2336 2337 //! Get the selected text and copy to str 2338 void 2339 TermView::GetSelection(BString &str) 2340 { 2341 str.SetTo(""); 2342 fTextBuffer->GetStringFromRegion(str); 2343 } 2344 2345 // Send DrawRect data to Draw Engine thread. 2346 inline void 2347 TermView::SendDataToDrawEngine(int x1, int y1, int x2, int y2) 2348 { 2349 // TODO: remove the goto 2350 2351 sem_info info; 2352 2353 retry: 2354 2355 get_sem_info(fDrawRectSem, &info); 2356 2357 if((RECT_BUF_SIZE - info.count) < 2) { 2358 2359 snooze(10 * 1000); 2360 goto retry; 2361 } 2362 2363 fDrawRectBuffer[fDrawRect_p].x1 = x1; 2364 fDrawRectBuffer[fDrawRect_p].x2 = x2; 2365 fDrawRectBuffer[fDrawRect_p].y1 = y1; 2366 fDrawRectBuffer[fDrawRect_p].y2 = y2; 2367 2368 fDrawRect_p++; 2369 fDrawRect_p %= RECT_BUF_SIZE; 2370 2371 release_sem(fDrawRectSem); 2372 } 2373