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