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