1 /* 2 * Copyright (c) 2001-2005, 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 26 #include "TermView.h" 27 #include "TermWindow.h" 28 #include "TermApp.h" 29 #include "TermParse.h" 30 #include "TermBuffer.h" 31 #include "CodeConv.h" 32 #include "VTkeymap.h" 33 #include "TermConst.h" 34 #include "PrefHandler.h" 35 #include "MenuUtil.h" 36 #include "PrefView.h" 37 38 #include <termios.h> 39 #include <signal.h> 40 #include "spawn.h" 41 42 // defined VTKeyTbl.c 43 extern int function_keycode_table[]; 44 extern char *function_key_char_table[]; 45 46 extern int gNowCoding; /* defined TermParse.cpp */ 47 48 rgb_color gTermColorTable[16] = { 49 { 0, 0, 0, 0}, 50 {255, 0, 0, 0}, 51 { 0, 255, 0, 0}, 52 {255, 255, 0, 0}, 53 { 0, 0, 255, 0}, 54 {255, 0, 255, 0}, 55 { 0, 255, 255, 0}, 56 {255, 255, 255, 0}, 57 }; 58 59 // Global Preference Handler 60 extern PrefHandler *gTermPref; 61 62 TermView::TermView(BRect frame, CodeConv *inCodeConv) 63 : BView(frame, "termview", B_FOLLOW_NONE, 64 B_WILL_DRAW | B_FRAME_EVENTS) 65 { 66 int rows, cols; 67 68 // Cursor reset. 69 fCurPos.Set(0, 0); 70 fCurStack.Set(0, 0); 71 fBufferStartPos = -1; 72 73 rows = gTermPref->getInt32(PREF_ROWS); 74 cols = gTermPref->getInt32(PREF_COLS); 75 76 fTermRows = rows; 77 fTermColumns = cols; 78 79 fCodeConv = inCodeConv; 80 81 // scroll pointer. 82 fTop = 0; 83 84 // create TermBuffer and CodeConv Class. 85 fTextBuffer = new TermBuffer(rows, cols); 86 87 // cursor Blinking and draw flag. 88 fCursorStatus = CURON; 89 fCursorDrawFlag = CURON; 90 fUpdateFlag = false; 91 fCursorBlinkingFlag = CURON; 92 93 fSelStart.Set(-1, -1); 94 fSelEnd.Set(-1, -1); 95 fSelected = false; 96 fMouseTracking = false; 97 98 // scroll bar variables. 99 fScrollUpCount = 0; 100 fScrollBarRange = 0; 101 fScrRegionSet = 0; 102 fScrBufSize = gTermPref->getInt32(PREF_HISTORY_SIZE); 103 104 // resize flag. 105 fFrameResized = 0; 106 107 // terminal mode flag. 108 fInsertModeFlag = MODE_OVER; 109 fInverseFlag = fBoldFlag = fUnderlineFlag = 0; 110 111 // terminal scroll flag. 112 fScrTop = 0; 113 fScrBot = rows - 1; 114 115 fPopMenu = NULL; 116 SetMouseButton(); 117 SetMouseCursor(); 118 fPreviousMousePoint.Set(0, 0); 119 120 //SetIMAware(gTermPref->getInt32(PREF_IM_AWARE)); 121 fIMflag = false; 122 123 fViewThread = -1; 124 fMouseThread = -1; 125 fQuitting = 1; 126 InitViewThread(); 127 128 fDrawRect_p = 0; 129 } 130 131 132 TermView::~TermView() 133 { 134 delete fTextBuffer; 135 fQuitting = 0; 136 kill_thread(fViewThread); 137 kill_thread(fMouseThread); 138 delete_sem(fDrawRectSem); 139 } 140 141 142 //! Get width and height for terminal font 143 void 144 TermView::GetFontSize(int* _width, int* _height) 145 { 146 *_width = fFontWidth; 147 *_height = fFontHeight; 148 } 149 150 151 //! Set number of rows and columns in terminal 152 BRect 153 TermView::SetTermSize(int rows, int cols, bool resize) 154 { 155 if (rows) 156 fTermRows = rows; 157 if (cols) 158 fTermColumns = cols; 159 160 fTextBuffer->ResizeTo(fTermRows, fTermColumns, 0); 161 162 fScrTop = 0; 163 fScrBot = fTermRows - 1; 164 165 BRect rect(0, 0, 166 fTermColumns * fFontWidth, 167 fTermRows * fFontHeight); 168 169 if (resize) { 170 ResizeTo(fTermColumns * fFontWidth -1, 171 fTermRows * fFontHeight -1); 172 } 173 Invalidate(Frame()); 174 175 return rect; 176 } 177 178 179 //! Set mouse button assignments 180 void 181 TermView::SetMouseButton() 182 { 183 mSelectButton = SetupMouseButton(gTermPref->getString(PREF_SELECT_MBUTTON)); 184 mSubMenuButton = SetupMouseButton(gTermPref->getString(PREF_SUBMENU_MBUTTON)); 185 mPasteMenuButton = SetupMouseButton(gTermPref->getString(PREF_PASTE_MBUTTON)); 186 } 187 188 189 //! Sets the mouse cursor image 190 void 191 TermView::SetMouseCursor() 192 { 193 if (!strcmp(gTermPref->getString(PREF_MOUSE_IMAGE), "Hand cursor")) 194 fMouseImage = false; 195 else 196 fMouseImage = true; 197 } 198 199 200 //! Sets colors for the terminal 201 void 202 TermView::SetTermColor() 203 { 204 fTextForeColor = gTermPref->getRGB(PREF_TEXT_FORE_COLOR); 205 fTextBackColor = gTermPref->getRGB(PREF_TEXT_BACK_COLOR); 206 fSelectForeColor = gTermPref->getRGB(PREF_SELECT_FORE_COLOR); 207 fSelectBackColor = gTermPref->getRGB(PREF_SELECT_BACK_COLOR); 208 fCursorForeColor = gTermPref->getRGB(PREF_CURSOR_FORE_COLOR); 209 fCursorBackColor = gTermPref->getRGB(PREF_CURSOR_BACK_COLOR); 210 211 SetLowColor(fTextBackColor); 212 SetViewColor(fTextBackColor); 213 } 214 215 216 //! Sets half and full fonts for terminal 217 void 218 TermView::SetTermFont(BFont *halfFont, BFont *fullFont) 219 { 220 char buf[4]; 221 int halfWidth = 0; 222 223 fHalfFont = halfFont; 224 fFullFont = fullFont; 225 226 // calculate half font's max width 227 // Not Bounding, check only A-Z(For case of fHalfFont is KanjiFont. ) 228 for (int c = 0x20 ; c <= 0x7e; c++){ 229 sprintf(buf, "%c", c); 230 int tmpWidth = (int)fHalfFont.StringWidth(buf); 231 if (tmpWidth > halfWidth) 232 halfWidth = tmpWidth; 233 } 234 235 // How to calculate FullWidth ? 236 fFontWidth = halfWidth; 237 238 // Second, Calc Font Height 239 font_height fh, hh; 240 fHalfFont.GetHeight(&hh); 241 fFullFont.GetHeight(&fh); 242 243 int font_ascent, font_descent,font_leading; 244 245 font_ascent =(int)((fh.ascent > hh.ascent) ? fh.ascent : hh.ascent); 246 font_descent =(int)((fh.descent > hh.descent) ? fh.descent : hh.descent); 247 font_leading =(int)((fh.leading > hh.leading) ? fh.leading : hh.leading); 248 249 if (font_leading == 0) 250 font_leading = 1; 251 252 if (fTop) 253 fTop = fTop / fFontHeight; 254 255 fFontHeight = font_ascent + font_descent + font_leading + 1; 256 257 fTop = fTop * fFontHeight; 258 259 fFontAscent = font_ascent; 260 fCursorHeight = font_ascent + font_descent + font_leading + 1; 261 } 262 263 264 void 265 TermView::SetScrollBar(BScrollBar *scrollBar) 266 { 267 fScrollBar = scrollBar; 268 } 269 270 271 //! Print one character 272 void 273 TermView::PutChar(uchar *string, ushort attr, int width) 274 { 275 if (width == FULL_WIDTH) 276 attr |= A_WIDTH; 277 278 // check column over flow. 279 if (fCurPos.x + width > fTermColumns) { 280 UpdateLine(); 281 fCurPos.x = 0; 282 283 if (fCurPos.y == fTermRows -1) 284 ScrollScreen(); 285 else 286 fCurPos.y++; 287 } 288 289 if (fInsertModeFlag == MODE_INSERT) 290 fTextBuffer->InsertSpace(fCurPos, width); 291 292 fTextBuffer->WriteChar(fCurPos, string, attr); 293 294 if (!fUpdateFlag) 295 fBufferStartPos = fCurPos.x; 296 297 fCurPos.x += width; 298 fUpdateFlag = true; 299 } 300 301 302 //! Print a CR and move the cursor 303 void 304 TermView::PutCR() 305 { 306 UpdateLine(); 307 fTextBuffer->WriteCR(fCurPos); 308 fCurPos.x = 0; 309 } 310 311 312 //! Print a LF and move the cursor 313 void 314 TermView::PutLF() 315 { 316 UpdateLine(); 317 318 if (fScrRegionSet) { 319 if (fCurPos.y == fScrBot) { 320 ScrollRegion(-1, -1, SCRUP, 1); 321 return; 322 } 323 } 324 325 if (fCurPos.x != fTermColumns){ 326 if (fCurPos.y == fTermRows -1) 327 ScrollScreen(); 328 else 329 fCurPos.y++; 330 } 331 } 332 333 334 //! Print a NL and move the cursor 335 void 336 TermView::PutNL(int num) 337 { 338 ScrollRegion(fCurPos.y, -1, SCRDOWN, num); 339 } 340 341 342 //! Print a space 343 void 344 TermView::InsertSpace(int num) 345 { 346 UpdateLine(); 347 348 fTextBuffer->InsertSpace(fCurPos, num); 349 TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 350 } 351 352 353 //! Set or reset Insert mode 354 void 355 TermView::SetInsertMode(int flag) 356 { 357 UpdateLine(); 358 fInsertModeFlag = flag; 359 } 360 361 362 //! Draw region 363 inline int 364 TermView::TermDraw(const CurPos &start, const CurPos &end) 365 { 366 int x1 = start.x; 367 int y1 = start.y; 368 int x2 = end.x; 369 int y2 = end.y; 370 371 // Send Draw Rectangle data to Draw Engine thread. 372 SendDataToDrawEngine(x1, y1 + fTop / fFontHeight, 373 x2, y2 + fTop / fFontHeight); 374 return 0; 375 } 376 377 378 //! Draw region 379 int 380 TermView::TermDrawSelectedRegion(CurPos start, CurPos end) 381 { 382 CurPos inPos; 383 384 if (end < start) { 385 inPos = start; 386 start = end; 387 end = inPos; 388 } 389 390 if (start.y == end.y) { 391 SendDataToDrawEngine(start.x, start.y, 392 end.x, end.y); 393 } else { 394 SendDataToDrawEngine(start.x, start.y, 395 fTermColumns, start.y); 396 397 if (end.y - start.y > 0) 398 SendDataToDrawEngine(0, start.y + 1, fTermColumns, end.y - 1); 399 400 SendDataToDrawEngine(0, end.y, end.x, end.y); 401 } 402 403 return 0; 404 } 405 406 407 //! Draw region 408 int 409 TermView::TermDrawRegion(CurPos start, CurPos end) 410 { 411 CurPos inPos; 412 int top = fTop / fFontHeight; 413 414 if (end < start) { 415 inPos = start; 416 start = end; 417 end = inPos; 418 } 419 420 start.y += top; 421 end.y += top; 422 423 if (start.y == end.y) { 424 SendDataToDrawEngine(start.x, start.y, 425 end.x, end.y); 426 } else { 427 SendDataToDrawEngine(start.x, start.y, 428 fTermColumns - 1, start.y); 429 430 if (end.y - start.y > 0) { 431 SendDataToDrawEngine(0, start.y + 1, 432 fTermColumns - 1, end.y - 1); 433 } 434 SendDataToDrawEngine(0, end.y, 435 end.x, end.y); 436 } 437 438 return 0; 439 } 440 441 442 //! Erase below cursor below. 443 void 444 TermView::EraseBelow() 445 { 446 UpdateLine(); 447 448 fTextBuffer->EraseBelow(fCurPos); 449 TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 450 if (fCurPos.y != fTermRows - 1) 451 TermDraw(CurPos(0, fCurPos.y + 1), CurPos(fTermColumns - 1, fTermRows - 1)); 452 } 453 454 455 //! Delete num characters from current position. 456 void 457 TermView::DeleteChar(int num) 458 { 459 UpdateLine(); 460 461 fTextBuffer->DeleteChar(fCurPos, num); 462 TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 463 } 464 465 466 //! Delete cursor right characters. 467 void 468 TermView::DeleteColumns() 469 { 470 UpdateLine(); 471 472 fTextBuffer->DeleteChar(fCurPos, fTermColumns - fCurPos.x); 473 TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 474 } 475 476 477 //! Delete 'num' lines from current position with scrolling. 478 void 479 TermView::DeleteLine(int num) 480 { 481 ScrollRegion(fCurPos.y, -1, SCRUP, num); 482 } 483 484 485 //! Sets cursor position 486 void 487 TermView::SetCurPos(int x, int y) 488 { 489 UpdateLine(); 490 491 if (x >= 0 && x < fTermColumns) 492 fCurPos.x = x; 493 if (y >= 0 && y < fTermRows) 494 fCurPos.y = y; 495 } 496 497 498 //! Sets cursor x position 499 void 500 TermView::SetCurX(int x) 501 { 502 if (x >= 0 && x < fTermRows) { 503 UpdateLine(); 504 fCurPos.x = x; 505 } 506 } 507 508 509 //! Sets cursor y position 510 void 511 TermView::SetCurY(int y) 512 { 513 if (y >= 0 && y < fTermColumns) { 514 UpdateLine(); 515 fCurPos.y = y; 516 } 517 } 518 519 520 //! Gets cursor position 521 void 522 TermView::GetCurPos(CurPos *inCurPos) 523 { 524 inCurPos->x = fCurPos.x; 525 inCurPos->y = fCurPos.y; 526 } 527 528 529 //! Gets cursor x position 530 int 531 TermView::GetCurX() 532 { 533 return fCurPos.x; 534 } 535 536 537 //! Gets cursor y position 538 int 539 TermView::GetCurY() 540 { 541 return fCurPos.y; 542 } 543 544 545 //! Saves cursor position 546 void 547 TermView::SaveCursor() 548 { 549 fCurStack = fCurPos; 550 } 551 552 553 //! Restores cursor position 554 void 555 TermView::RestoreCursor() 556 { 557 UpdateLine(); 558 fCurPos = fCurStack; 559 } 560 561 562 //! Move cursor right by 'num' steps. 563 void 564 TermView::MoveCurRight(int num) 565 { 566 UpdateLine(); 567 568 if (fCurPos.x + num >= fTermColumns) { 569 // Wrap around 570 fCurPos.x = 0; 571 PutCR(); 572 PutLF(); 573 } else { 574 fCurPos.x += num; 575 } 576 } 577 578 579 //! Move cursor left by 'num' steps. 580 void 581 TermView::MoveCurLeft(int num) 582 { 583 UpdateLine(); 584 585 fCurPos.x -= num; 586 if (fCurPos.x < 0) 587 fCurPos.x = 0; 588 } 589 590 591 //! Move cursor up by 'num' steps. 592 void 593 TermView::MoveCurUp(int num) 594 { 595 UpdateLine(); 596 597 fCurPos.y -= num; 598 599 if (fCurPos.y < 0) 600 fCurPos.y = 0; 601 } 602 603 604 //! Move cursor down by 'num' steps. 605 void 606 TermView::MoveCurDown(int num) 607 { 608 UpdateLine(); 609 610 fCurPos.y += num; 611 612 if (fCurPos.y >= fTermRows) 613 fCurPos.y = fTermRows - 1; 614 } 615 616 617 void 618 TermView::DrawCursor() 619 { 620 CURSOR_RECT; 621 uchar buf[4]; 622 ushort attr; 623 624 int top = fTop / fFontHeight; 625 int m_flag = false; 626 627 m_flag = CheckSelectedRegion(CurPos(fCurPos.x, 628 fCurPos.y + fTop / fFontHeight)); 629 if (fTextBuffer->GetChar(fCurPos.y + top, fCurPos.x, buf, &attr) == A_CHAR) { 630 int width; 631 if (IS_WIDTH(attr)) 632 width = 2; 633 else 634 width = 1; 635 636 DrawLines(fCurPos.x * fFontWidth, 637 fCurPos.y * fFontHeight + fTop, 638 attr, buf, width, m_flag, true, this); 639 } else { 640 if (m_flag) 641 SetHighColor(fSelectBackColor); 642 else 643 SetHighColor(fCursorBackColor); 644 645 FillRect(r); 646 } 647 648 Sync(); 649 } 650 651 652 void 653 TermView::BlinkCursor() 654 { 655 if (fCursorDrawFlag == CURON 656 && fCursorBlinkingFlag == CURON 657 && Window()->IsActive()) { 658 if (fCursorStatus == CURON) 659 TermDraw(fCurPos, fCurPos); 660 else 661 DrawCursor(); 662 663 fCursorStatus = fCursorStatus == CURON ? CUROFF : CURON; 664 } 665 } 666 667 668 //! Draw / Clear cursor. 669 void 670 TermView::SetCurDraw(bool flag) 671 { 672 if (flag == CUROFF) { 673 if (fCursorStatus == CURON) 674 TermDraw(fCurPos, fCurPos); 675 676 fCursorStatus = CUROFF; 677 fCursorDrawFlag = CUROFF; 678 } else { 679 if (fCursorDrawFlag == CUROFF) { 680 fCursorDrawFlag = CURON; 681 fCursorStatus = CURON; 682 683 if (LockLooper()) { 684 DrawCursor(); 685 UnlockLooper(); 686 } 687 } 688 } 689 } 690 691 692 //! Sets cursor Blinking flag. 693 void 694 TermView::SetCurBlinking(bool flag) 695 { 696 fCursorBlinkingFlag = flag; 697 } 698 699 700 //! Scroll terminal dir directory by 'num' steps. 701 void 702 TermView::ScrollRegion(int top, int bot, int dir, int num) 703 { 704 UpdateLine(); 705 706 if (top == -1) 707 top = fScrTop; 708 709 if (bot == -1) 710 bot = fScrBot; 711 712 if (top < fScrTop) 713 top = fScrTop; 714 715 if (bot > fScrBot) 716 bot = fScrBot; 717 718 fTextBuffer->ScrollRegion(top, bot , dir ,num); 719 TermDraw(CurPos(0, top), CurPos(fTermColumns - 1, bot)); 720 } 721 722 723 //! Sets terminal scroll region. 724 void 725 TermView::SetScrollRegion(int top, int bot) 726 { 727 if (top >= 0 && top < fTermRows) { 728 if (bot >= 0 && bot < fTermRows) { 729 if (top > bot) { 730 fScrTop = bot; 731 fScrBot = top; 732 } else if (top < bot ) { 733 fScrTop = top; 734 fScrBot = bot; 735 } 736 } 737 } 738 739 if (fScrTop != 0 || fScrBot != fTermRows -1 ) 740 fScrRegionSet = 1; 741 else 742 fScrRegionSet = 0; 743 } 744 745 746 //! Scroll to cursor position. 747 void 748 TermView::ScrollAtCursor() 749 { 750 if (LockLooper()) { 751 ResizeScrBarRange(); 752 fScrollUpCount = 0; 753 ScrollTo(0, fTop); 754 UnlockLooper(); 755 } 756 } 757 758 759 thread_id 760 TermView::InitViewThread() 761 { 762 // spwan Draw Engine thread. 763 if (fViewThread < 0) { 764 fViewThread = spawn_thread(ViewThread, "DrawEngine", 765 B_DISPLAY_PRIORITY, this); 766 } else 767 return B_BAD_THREAD_ID; 768 769 fDrawRectSem = create_sem(0, "draw_engine_sem"); 770 resume_thread(fViewThread); 771 772 // spawn Mouse Tracking thread. 773 if (fMouseThread < 0) { 774 fMouseThread = spawn_thread(MouseTracking, "MouseTracking", 775 B_NORMAL_PRIORITY,this); 776 } else 777 return B_BAD_THREAD_ID; 778 779 resume_thread(fMouseThread); 780 781 return fViewThread; 782 } 783 784 785 //! Thread of Draw Character to View. 786 int32 787 TermView::ViewThread(void *data) 788 { 789 int width, height; 790 sDrawRect pos; 791 792 //#define INVALIDATE 793 #ifndef INVALIDATE 794 int i, j, count; 795 int m_flag; 796 ushort attr; 797 uchar buf[256]; 798 BRect eraseRect; 799 #endif 800 801 int inDrawRect_p = 0; 802 803 TermView *theObj =(TermView *)data; 804 805 while (theObj->fQuitting) { 806 // Wait semaphore 807 acquire_sem(theObj->fDrawRectSem); 808 809 pos = theObj->fDrawRectBuffer[inDrawRect_p]; 810 inDrawRect_p++; 811 inDrawRect_p %= RECT_BUF_SIZE; 812 813 width = theObj->fFontWidth; 814 height = theObj->fFontHeight; 815 816 #ifdef INVALIDATE 817 BRect r(pos.x1 * width, pos.y1 * height, 818 (pos.x2 + 1) * width -1,(pos.y2 + 1) * height -1); 819 820 if(theObj->LockLooper()) { 821 theObj->Invalidate(r); 822 theObj->UnlockLooper(); 823 } 824 #else 825 826 if (theObj->LockLooper()) { 827 for (j = pos.y1; j <= pos.y2; j++) { 828 for (i = pos.x1; i <= pos.x2;) { 829 count = theObj->fTextBuffer->GetString(j, i, pos.x2, buf, &attr); 830 m_flag = theObj->CheckSelectedRegion(CurPos(i, j)); 831 832 if (count < 0) { 833 eraseRect.Set(width * i, height * j, 834 width *(i - count) - 1, height *(j + 1) - 1); 835 836 if (m_flag) 837 theObj->SetHighColor(theObj->fSelectBackColor); 838 else 839 theObj->SetHighColor(theObj->fTextBackColor); 840 841 theObj->FillRect(eraseRect); 842 count = -count; 843 } else { 844 theObj->DrawLines(width * i, height * j, 845 attr, buf, count, m_flag, false, theObj); 846 } 847 i += count; 848 } 849 } 850 theObj->UnlockLooper(); 851 } 852 #endif 853 } 854 855 exit_thread(B_OK); 856 return 0; 857 } 858 859 860 //! Thread for tracking mouse. 861 int32 862 TermView::MouseTracking(void *data) 863 { 864 int32 code, selected = false; 865 uint32 button; 866 thread_id sender; 867 CurPos stpos, edpos; 868 BPoint stpoint, edpoint; 869 float scr_start, scr_end, scr_pos; 870 871 TermView *theObj =(TermView *)data; 872 873 while(theObj->fQuitting) { 874 875 if(1) { 876 #ifdef CHANGE_CURSOR_IMAGE 877 if(!has_data(find_thread(NULL))) { 878 BRect r; 879 880 if(theObj->fSelected 881 && ( gTermPref->getInt32(PREF_DRAGN_COPY) 882 || modifiers() & B_CONTROL_KEY)) { 883 884 if(theObj->LockLooper()) { 885 theObj->GetMouse(&stpoint, &button); 886 r = theObj->Bounds(); 887 theObj->UnlockLooper(); 888 } 889 if(r.Contains(stpoint)) { 890 CurPos tmppos = theObj->BPointToCurPos(stpoint); 891 if(theObj->fSelStart > theObj->fSelEnd) { 892 stpos = theObj->fSelEnd; 893 edpos = theObj->fSelStart; 894 } else { 895 stpos = theObj->fSelStart; 896 edpos = theObj->fSelEnd; 897 } 898 899 if(tmppos > stpos && tmppos < edpos) 900 be_app->SetCursor(M_ADD_CURSOR); 901 else 902 be_app->SetCursor(B_HAND_CURSOR); 903 } 904 } 905 snooze(50 * 1000); 906 continue; 907 } else { 908 #endif 909 code = receive_data(&sender,(void *)&stpoint, sizeof(BPoint)); 910 } 911 912 if(code != MOUSE_THR_CODE) 913 continue; 914 915 selected = theObj->fSelected; 916 edpoint.Set(-1, -1); 917 918 stpos = theObj->BPointToCurPos(stpoint); 919 920 do { 921 922 snooze(40 * 1000); 923 924 if(theObj->LockLooper()) { 925 theObj->GetMouse(&edpoint, &button); 926 theObj->UnlockLooper(); 927 } 928 929 edpos = theObj->BPointToCurPos(edpoint); 930 if (edpos.y < 0) 931 continue; 932 933 if(stpoint == edpoint) { 934 continue; 935 } else { 936 if(!selected) { 937 theObj->Select(stpos, edpos); 938 selected = true; 939 } else { 940 941 // Align cursor point to text. 942 if(stpos == edpos) 943 continue; 944 945 if(edpos > stpos) { 946 edpoint.x -= theObj->fFontWidth / 2; 947 edpos = theObj->BPointToCurPos(edpoint); 948 //edpos.x--; 949 if(edpos.x < 0) 950 edpos.x = 0; 951 } 952 else 953 if(edpos < stpos) { 954 edpoint.x += theObj->fFontWidth / 2; 955 edpos = theObj->BPointToCurPos(edpoint); 956 //edpos.x++; 957 if(edpos.x > theObj->fTermColumns) 958 edpos.x = theObj->fTermColumns; 959 } 960 961 // Scroll check 962 if(theObj->LockLooper()) { 963 964 // Get now scroll point 965 theObj->fScrollBar->GetRange(&scr_start, &scr_end); 966 scr_pos = theObj->fScrollBar->Value(); 967 968 if(edpoint.y < theObj->Bounds().LeftTop().y ) 969 970 // mouse point left of window 971 if(scr_pos != scr_start) 972 theObj->ScrollTo(0, edpoint.y); 973 974 if(edpoint.y > theObj->Bounds().LeftBottom().y) { 975 976 // mouse point left of window 977 if(scr_pos != scr_end) 978 theObj->ScrollTo(0, edpoint.y); 979 } 980 theObj->UnlockLooper(); 981 } 982 theObj->ResizeSelectRegion(edpos); 983 } 984 } 985 } while(button); 986 theObj->fMouseTracking = false; 987 } 988 989 exit_thread(B_OK); 990 return 0; 991 } 992 993 994 //! Draw character on offscreen bitmap. 995 void 996 TermView::DrawLines(int x1, int y1, ushort attr, uchar *buf, 997 int width, int mouse, int cursor, BView *inView) 998 { 999 int x2, y2; 1000 int forecolor, backcolor; 1001 rgb_color rgb_fore = fTextForeColor, rgb_back = fTextBackColor, rgb_tmp; 1002 1003 // Set Font. 1004 if (IS_WIDTH(attr)) 1005 inView->SetFont(&fFullFont); 1006 else 1007 inView->SetFont(&fHalfFont); 1008 1009 // Set pen point 1010 x2 = x1 + fFontWidth * width; 1011 y2 = y1 + fFontHeight; 1012 1013 // color attribute 1014 forecolor = IS_FORECOLOR(attr); 1015 backcolor = IS_BACKCOLOR(attr); 1016 1017 if (IS_FORESET(attr)) 1018 rgb_fore = gTermColorTable[forecolor]; 1019 1020 if (IS_BACKSET(attr)) 1021 rgb_back = gTermColorTable[backcolor]; 1022 1023 // Selection check. 1024 if (cursor) { 1025 rgb_fore = fCursorForeColor; 1026 rgb_back = fCursorBackColor; 1027 } else if (mouse){ 1028 rgb_fore = fSelectForeColor; 1029 rgb_back = fSelectBackColor; 1030 } else { 1031 // Reverse attribute(If selected area, don't reverse color). 1032 if (IS_INVERSE(attr)) { 1033 rgb_tmp = rgb_fore; 1034 rgb_fore = rgb_back; 1035 rgb_back = rgb_tmp; 1036 } 1037 } 1038 1039 // Fill color at Background color and set low color. 1040 inView->SetHighColor(rgb_back); 1041 inView->FillRect(BRect(x1, y1, x2 - 1, y2 - 1)); 1042 inView->SetLowColor(rgb_back); 1043 1044 inView->SetHighColor(rgb_fore); 1045 1046 // Draw character. 1047 inView->MovePenTo(x1, y1 + fFontAscent); 1048 inView->DrawString((char *) buf); 1049 1050 // bold attribute. 1051 if (IS_BOLD(attr)) { 1052 inView->MovePenTo(x1 + 1, y1 + fFontAscent); 1053 1054 inView->SetDrawingMode(B_OP_OVER); 1055 inView->DrawString((char *)buf); 1056 inView->SetDrawingMode(B_OP_COPY); 1057 } 1058 1059 // underline attribute 1060 if (IS_UNDER(attr)) { 1061 inView->MovePenTo(x1, y1 + fFontAscent); 1062 inView->StrokeLine(BPoint(x1 , y1 + fFontAscent), 1063 BPoint(x2 , y1 + fFontAscent)); 1064 } 1065 } 1066 1067 1068 //! Resize scroll bar range and knob size. 1069 void 1070 TermView::ResizeScrBarRange() 1071 { 1072 float viewheight, start_pos; 1073 1074 viewheight = fTermRows * fFontHeight; 1075 start_pos = fTop -(fScrBufSize - fTermRows *2) * fFontHeight; 1076 1077 if (start_pos > 0) { 1078 fScrollBar->SetRange(start_pos, viewheight + fTop - fFontHeight); 1079 } else { 1080 fScrollBar->SetRange(0, viewheight + fTop - fFontHeight); 1081 fScrollBar->SetProportion( viewheight /(viewheight + fTop)); 1082 } 1083 } 1084 1085 1086 //! Scrolls screen. 1087 void 1088 TermView::ScrollScreen() 1089 { 1090 fTop += fFontHeight; 1091 fScrollUpCount++; 1092 fTextBuffer->ScrollLine(); 1093 1094 if (fScrollUpCount > fTermRows ) { 1095 if (LockLooper()) { 1096 ResizeScrBarRange(); 1097 fScrollBarRange += fScrollUpCount; 1098 fScrollUpCount = 0; 1099 ScrollTo(0, fTop); 1100 UnlockLooper(); 1101 } 1102 } 1103 } 1104 1105 1106 //! Scrolls screen. 1107 void 1108 TermView::ScrollScreenDraw() 1109 { 1110 if (fScrollUpCount){ 1111 if (LockLooper()) { 1112 ResizeScrBarRange(); 1113 1114 fScrollBarRange += fScrollUpCount; 1115 fScrollUpCount = 0; 1116 ScrollTo(0, fTop); 1117 UnlockLooper(); 1118 } 1119 } 1120 } 1121 1122 1123 //! Handler for SIGWINCH 1124 void 1125 TermView::UpdateSIGWINCH() 1126 { 1127 struct winsize ws; 1128 1129 if (fFrameResized) { 1130 if (fSelected) 1131 TermDrawSelectedRegion(fSelStart, fSelEnd); 1132 ScrollTo(0, fTop); 1133 ResizeScrBarRange(); 1134 1135 ws.ws_row = fTermRows; 1136 ws.ws_col = fTermColumns; 1137 ioctl(pfd, TIOCSWINSZ, &ws); 1138 kill(-sh_pid, SIGWINCH); 1139 1140 fFrameResized = 0; 1141 if (fScrRegionSet == 0) 1142 fScrBot = fTermRows - 1; 1143 } 1144 } 1145 1146 1147 /*! Device Status. 1148 Q & D hack by Y.Hayakawa(hida@sawada.riec.tohoku.ac.jp) 1149 21-JUL-99 1150 */ 1151 void 1152 TermView::DeviceStatusReport(int n) 1153 { 1154 char sbuf[16] ; 1155 int len; 1156 1157 switch (n) { 1158 case 5: 1159 len = sprintf(sbuf,"\033[0n") ; 1160 write(pfd, sbuf, len); 1161 break ; 1162 case 6: 1163 len = sprintf(sbuf,"\033[%d;%dR", fTermRows, fTermColumns) ; 1164 write(pfd, sbuf, len); 1165 break ; 1166 default: 1167 return; 1168 } 1169 } 1170 1171 1172 //! Update line buffer. 1173 void 1174 TermView::UpdateLine() 1175 { 1176 if (fUpdateFlag == true) { 1177 if (fInsertModeFlag == MODE_INSERT) { 1178 TermDraw(CurPos(fBufferStartPos, fCurPos.y), 1179 CurPos(fTermColumns - 1, fCurPos.y)); 1180 } else { 1181 TermDraw(CurPos(fBufferStartPos, fCurPos.y), 1182 CurPos(fCurPos.x - 1, fCurPos.y)); 1183 } 1184 fUpdateFlag = false; 1185 } 1186 } 1187 1188 1189 void 1190 TermView::AttachedToWindow() 1191 { 1192 SetFont(&fHalfFont); 1193 MakeFocus(true); 1194 fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows); 1195 } 1196 1197 1198 void 1199 TermView::Draw(BRect updateRect) 1200 { 1201 if (IsPrinting()) { 1202 DoPrint(updateRect); 1203 return; 1204 } 1205 1206 int x1, x2, y1, y2; 1207 int i, j, k, count; 1208 ushort attr; 1209 uchar buf[256]; 1210 int m_flag; 1211 BRect eraseRect; 1212 1213 x1 =(int)updateRect.left / fFontWidth; 1214 x2 =(int)updateRect.right / fFontWidth; 1215 1216 y1 =(int)updateRect.top / fFontHeight; 1217 y2 =(int)updateRect.bottom / fFontHeight; 1218 1219 Window()->BeginViewTransaction(); 1220 1221 for (j = y1; j <= y2; j++) { 1222 // If(x1, y1) Buffer is in string full width character, 1223 // alignment start position. 1224 1225 k = x1; 1226 if (fTextBuffer->GetChar(j, k, buf, &attr) == IN_STRING) 1227 k--; 1228 1229 if (k < 0) 1230 k = 0; 1231 1232 for (i = k; i <= x2;) { 1233 count = fTextBuffer->GetString(j, i, x2, buf, &attr); 1234 m_flag = CheckSelectedRegion(CurPos(i, j)); 1235 1236 if (count < 0) { 1237 if (m_flag) { 1238 eraseRect.Set(fFontWidth * i, 1239 fFontHeight * j, 1240 fFontWidth *(i - count) -1, 1241 fFontHeight *(j + 1) -1); 1242 1243 SetHighColor(fSelectBackColor); 1244 FillRect(eraseRect); 1245 } 1246 i += abs(count); 1247 continue; 1248 } 1249 1250 DrawLines(fFontWidth * i, fFontHeight * j, 1251 attr, buf, count, m_flag, false, this); 1252 i += count; 1253 if (i >= fTermColumns) 1254 break; 1255 } 1256 } 1257 1258 if (fCursorStatus == CURON) 1259 DrawCursor(); 1260 1261 Window()->EndViewTransaction(); 1262 } 1263 1264 1265 void 1266 TermView::DoPrint(BRect updateRect) 1267 { 1268 ushort attr; 1269 uchar buf[256]; 1270 1271 const int numLines =(int)((updateRect.Height()) / fFontHeight); 1272 1273 int y1 =(int)updateRect.top / fFontHeight; 1274 y1 = y1 -(fScrBufSize - numLines * 2); 1275 if (y1 < 0) 1276 y1 = 0; 1277 1278 const int y2 = y1 + numLines -1; 1279 1280 const int x1 =(int)updateRect.left / fFontWidth; 1281 const int x2 =(int)updateRect.right / fFontWidth; 1282 1283 for (int j = y1; j <= y2; j++) { 1284 // If(x1, y1) Buffer is in string full width character, 1285 // alignment start position. 1286 1287 int k = x1; 1288 if (fTextBuffer->GetChar(j, k, buf, &attr) == IN_STRING) 1289 k--; 1290 1291 if (k < 0) 1292 k = 0; 1293 1294 for (int i = k; i <= x2;) { 1295 int count = fTextBuffer->GetString(j, i, x2, buf, &attr); 1296 if (count < 0) { 1297 i += abs(count); 1298 continue; 1299 } 1300 1301 DrawLines(fFontWidth * i, fFontHeight * j, 1302 attr, buf, count, false, false, this); 1303 i += count; 1304 } 1305 } 1306 } 1307 1308 1309 void 1310 TermView::WindowActivated(bool active) 1311 { 1312 if (active == false) { 1313 // DoIMConfirm(); 1314 } 1315 1316 if (active && fMouseImage) 1317 be_app->SetCursor(B_I_BEAM_CURSOR); 1318 } 1319 1320 1321 void 1322 TermView::KeyDown(const char *bytes, int32 numBytes) 1323 { 1324 char c; 1325 struct termios tio; 1326 int32 key, mod; 1327 1328 uchar dstbuf[1024]; 1329 Looper()->CurrentMessage()->FindInt32("modifiers", &mod); 1330 Looper()->CurrentMessage()->FindInt32("key", &key); 1331 1332 if (fIMflag) 1333 return; 1334 1335 // If bytes[0] equal intr charactor, 1336 // send signal to shell process group. 1337 tcgetattr(pfd, &tio); 1338 if (*bytes == tio.c_cc[VINTR]) { 1339 if(tio.c_lflag & ISIG) 1340 kill(-sh_pid, SIGINT); 1341 } 1342 1343 // Terminal changes RET, ENTER, F1...F12, and ARROW key code. 1344 1345 if (numBytes == 1) { 1346 if (mod & B_OPTION_KEY) { 1347 c = *bytes; 1348 c |= 0x80; 1349 write(pfd, &c, 1); 1350 return; 1351 } 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 // Send DrawRect data to Draw Engine thread. 2253 inline void 2254 TermView::SendDataToDrawEngine(int x1, int y1, int x2, int y2) 2255 { 2256 // TODO: remove the goto 2257 2258 sem_info info; 2259 2260 retry: 2261 2262 get_sem_info(fDrawRectSem, &info); 2263 2264 if((RECT_BUF_SIZE - info.count) < 2) { 2265 2266 snooze(10 * 1000); 2267 goto retry; 2268 } 2269 2270 fDrawRectBuffer[fDrawRect_p].x1 = x1; 2271 fDrawRectBuffer[fDrawRect_p].x2 = x2; 2272 fDrawRectBuffer[fDrawRect_p].y1 = y1; 2273 fDrawRectBuffer[fDrawRect_p].y2 = y2; 2274 2275 fDrawRect_p++; 2276 fDrawRect_p %= RECT_BUF_SIZE; 2277 2278 release_sem(fDrawRectSem); 2279 } 2280