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