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