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