1 /* 2 * Copyright 2001-2014, 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 * Jonathan Schleifer, js@webkeks.org 12 * Ingo Weinhold, ingo_weinhold@gmx.de 13 * Clemens Zeidler, haiku@Clemens-Zeidler.de 14 * Siarzhuk Zharski, zharik@gmx.li 15 */ 16 17 18 #include "TermView.h" 19 20 #include <signal.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <termios.h> 24 25 #include <algorithm> 26 #include <new> 27 #include <vector> 28 29 #include <Alert.h> 30 #include <Application.h> 31 #include <Beep.h> 32 #include <Catalog.h> 33 #include <Clipboard.h> 34 #include <Debug.h> 35 #include <Directory.h> 36 #include <Dragger.h> 37 #include <Input.h> 38 #include <Locale.h> 39 #include <MenuItem.h> 40 #include <Message.h> 41 #include <MessageRunner.h> 42 #include <Node.h> 43 #include <Path.h> 44 #include <PopUpMenu.h> 45 #include <PropertyInfo.h> 46 #include <Region.h> 47 #include <Roster.h> 48 #include <ScrollBar.h> 49 #include <ScrollView.h> 50 #include <String.h> 51 #include <StringView.h> 52 #include <UTF8.h> 53 #include <Window.h> 54 55 #include "ActiveProcessInfo.h" 56 #include "Colors.h" 57 #include "InlineInput.h" 58 #include "PrefHandler.h" 59 #include "Shell.h" 60 #include "ShellParameters.h" 61 #include "TermApp.h" 62 #include "TermConst.h" 63 #include "TerminalBuffer.h" 64 #include "TerminalCharClassifier.h" 65 #include "TermViewStates.h" 66 #include "VTkeymap.h" 67 68 69 #define ROWS_DEFAULT 25 70 #define COLUMNS_DEFAULT 80 71 72 73 #undef B_TRANSLATION_CONTEXT 74 #define B_TRANSLATION_CONTEXT "Terminal TermView" 75 76 static property_info sPropList[] = { 77 { "encoding", 78 {B_GET_PROPERTY, 0}, 79 {B_DIRECT_SPECIFIER, 0}, 80 "get terminal encoding"}, 81 { "encoding", 82 {B_SET_PROPERTY, 0}, 83 {B_DIRECT_SPECIFIER, 0}, 84 "set terminal encoding"}, 85 { "tty", 86 {B_GET_PROPERTY, 0}, 87 {B_DIRECT_SPECIFIER, 0}, 88 "get tty name."}, 89 { 0 } 90 }; 91 92 93 static const uint32 kUpdateSigWinch = 'Rwin'; 94 static const uint32 kBlinkCursor = 'BlCr'; 95 96 static const bigtime_t kSyncUpdateGranularity = 100000; // 0.1 s 97 98 static const int32 kCursorBlinkIntervals = 3; 99 static const int32 kCursorVisibleIntervals = 2; 100 static const bigtime_t kCursorBlinkInterval = 500000; 101 102 static const rgb_color kBlackColor = { 0, 0, 0, 255 }; 103 static const rgb_color kWhiteColor = { 255, 255, 255, 255 }; 104 105 // secondary mouse button drop 106 const int32 kSecondaryMouseDropAction = 'SMDA'; 107 108 enum { 109 kInsert, 110 kChangeDirectory, 111 kLinkFiles, 112 kMoveFiles, 113 kCopyFiles 114 }; 115 116 117 template<typename Type> 118 static inline Type 119 restrict_value(const Type& value, const Type& min, const Type& max) 120 { 121 return value < min ? min : (value > max ? max : value); 122 } 123 124 125 template<typename Type> 126 static inline Type 127 saturated_add(Type a, Type b) 128 { 129 const Type max = (Type)(-1); 130 return (max - a >= b ? a + b : max); 131 } 132 133 134 // #pragma mark - TextBufferSyncLocker 135 136 137 class TermView::TextBufferSyncLocker { 138 public: 139 TextBufferSyncLocker(TermView* view) 140 : 141 fView(view) 142 { 143 fView->fTextBuffer->Lock(); 144 } 145 146 ~TextBufferSyncLocker() 147 { 148 fView->fTextBuffer->Unlock(); 149 150 if (fView->fVisibleTextBufferChanged) 151 fView->_VisibleTextBufferChanged(); 152 } 153 154 private: 155 TermView* fView; 156 }; 157 158 159 // #pragma mark - TermView 160 161 162 TermView::TermView(BRect frame, const ShellParameters& shellParameters, 163 int32 historySize) 164 : 165 BView(frame, "termview", B_FOLLOW_ALL, 166 B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE), 167 fListener(NULL), 168 fColumns(COLUMNS_DEFAULT), 169 fRows(ROWS_DEFAULT), 170 fEncoding(M_UTF8), 171 fActive(false), 172 fScrBufSize(historySize), 173 fReportX10MouseEvent(false), 174 fReportNormalMouseEvent(false), 175 fReportButtonMouseEvent(false), 176 fReportAnyMouseEvent(false) 177 { 178 status_t status = _InitObject(shellParameters); 179 if (status != B_OK) 180 throw status; 181 SetTermSize(frame); 182 } 183 184 185 TermView::TermView(int rows, int columns, 186 const ShellParameters& shellParameters, int32 historySize) 187 : 188 BView(BRect(0, 0, 0, 0), "termview", B_FOLLOW_ALL, 189 B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE), 190 fListener(NULL), 191 fColumns(columns), 192 fRows(rows), 193 fEncoding(M_UTF8), 194 fActive(false), 195 fScrBufSize(historySize), 196 fReportX10MouseEvent(false), 197 fReportNormalMouseEvent(false), 198 fReportButtonMouseEvent(false), 199 fReportAnyMouseEvent(false) 200 { 201 status_t status = _InitObject(shellParameters); 202 if (status != B_OK) 203 throw status; 204 205 ResizeToPreferred(); 206 207 // TODO: Don't show the dragger, since replicant capabilities 208 // don't work very well ATM. 209 /* 210 BRect rect(0, 0, 16, 16); 211 rect.OffsetTo(Bounds().right - rect.Width(), 212 Bounds().bottom - rect.Height()); 213 214 SetFlags(Flags() | B_DRAW_ON_CHILDREN | B_FOLLOW_ALL); 215 AddChild(new BDragger(rect, this, 216 B_FOLLOW_RIGHT|B_FOLLOW_BOTTOM, B_WILL_DRAW));*/ 217 } 218 219 220 TermView::TermView(BMessage* archive) 221 : 222 BView(archive), 223 fListener(NULL), 224 fColumns(COLUMNS_DEFAULT), 225 fRows(ROWS_DEFAULT), 226 fEncoding(M_UTF8), 227 fActive(false), 228 fScrBufSize(1000), 229 fReportX10MouseEvent(false), 230 fReportNormalMouseEvent(false), 231 fReportButtonMouseEvent(false), 232 fReportAnyMouseEvent(false) 233 { 234 BRect frame = Bounds(); 235 236 if (archive->FindInt32("encoding", (int32*)&fEncoding) < B_OK) 237 fEncoding = M_UTF8; 238 if (archive->FindInt32("columns", (int32*)&fColumns) < B_OK) 239 fColumns = COLUMNS_DEFAULT; 240 if (archive->FindInt32("rows", (int32*)&fRows) < B_OK) 241 fRows = 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 status_t status = _InitObject(ShellParameters(argc, argv)); 254 if (status != B_OK) 255 throw status; 256 257 bool useRect = false; 258 if ((archive->FindBool("use_rect", &useRect) == B_OK) && useRect) 259 SetTermSize(frame); 260 261 delete[] argv; 262 } 263 264 265 /*! Initializes the object for further use. 266 The members fRows, fColumns, fEncoding, and fScrBufSize must 267 already be initialized; they are not touched by this method. 268 */ 269 status_t 270 TermView::_InitObject(const ShellParameters& shellParameters) 271 { 272 SetFlags(Flags() | B_WILL_DRAW | B_FRAME_EVENTS 273 | B_FULL_UPDATE_ON_RESIZE/* | B_INPUT_METHOD_AWARE*/); 274 275 fShell = NULL; 276 fWinchRunner = NULL; 277 fCursorBlinkRunner = NULL; 278 fAutoScrollRunner = NULL; 279 fResizeRunner = NULL; 280 fResizeView = NULL; 281 fCharClassifier = NULL; 282 fFontWidth = 0; 283 fFontHeight = 0; 284 fFontAscent = 0; 285 fEmulateBold = false; 286 fBrightInsteadOfBold = false; 287 fFrameResized = false; 288 fResizeViewDisableCount = 0; 289 fLastActivityTime = 0; 290 fCursorState = 0; 291 fCursorStyle = BLOCK_CURSOR; 292 fCursorBlinking = true; 293 fCursorHidden = false; 294 fCursor = TermPos(0, 0); 295 fTextBuffer = NULL; 296 fVisibleTextBuffer = NULL; 297 fVisibleTextBufferChanged = false; 298 fScrollBar = NULL; 299 fInline = NULL; 300 fSelectForeColor = kWhiteColor; 301 fSelectBackColor = kBlackColor; 302 fScrollOffset = 0; 303 fLastSyncTime = 0; 304 fScrolledSinceLastSync = 0; 305 fSyncRunner = NULL; 306 fConsiderClockedSync = false; 307 fSelection.SetHighlighter(this); 308 fSelection.SetRange(TermPos(0, 0), TermPos(0, 0)); 309 fPrevPos = TermPos(-1, - 1); 310 fReportX10MouseEvent = false; 311 fReportNormalMouseEvent = false; 312 fReportButtonMouseEvent = false; 313 fReportAnyMouseEvent = false; 314 fMouseClipboard = be_clipboard; 315 fDefaultState = new(std::nothrow) DefaultState(this); 316 fSelectState = new(std::nothrow) SelectState(this); 317 fHyperLinkState = new(std::nothrow) HyperLinkState(this); 318 fHyperLinkMenuState = new(std::nothrow) HyperLinkMenuState(this); 319 fActiveState = NULL; 320 321 fTextBuffer = new(std::nothrow) TerminalBuffer; 322 if (fTextBuffer == NULL) 323 return B_NO_MEMORY; 324 325 fVisibleTextBuffer = new(std::nothrow) BasicTerminalBuffer; 326 if (fVisibleTextBuffer == NULL) 327 return B_NO_MEMORY; 328 329 // TODO: Make the special word chars user-settable! 330 fCharClassifier = new(std::nothrow) DefaultCharClassifier( 331 kDefaultAdditionalWordCharacters); 332 if (fCharClassifier == NULL) 333 return B_NO_MEMORY; 334 335 status_t error = fTextBuffer->Init(fColumns, fRows, fScrBufSize); 336 if (error != B_OK) 337 return error; 338 fTextBuffer->SetEncoding(fEncoding); 339 340 error = fVisibleTextBuffer->Init(fColumns, fRows + 2, 0); 341 if (error != B_OK) 342 return error; 343 344 fShell = new (std::nothrow) Shell(); 345 if (fShell == NULL) 346 return B_NO_MEMORY; 347 348 SetTermFont(be_fixed_font); 349 350 // set the shell parameters' encoding 351 ShellParameters modifiedShellParameters(shellParameters); 352 modifiedShellParameters.SetEncoding(fEncoding); 353 354 error = fShell->Open(fRows, fColumns, modifiedShellParameters); 355 356 if (error < B_OK) 357 return error; 358 359 error = _AttachShell(fShell); 360 if (error < B_OK) 361 return error; 362 363 fHighlights.AddItem(&fSelection); 364 365 if (fDefaultState == NULL || fSelectState == NULL || fHyperLinkState == NULL 366 || fHyperLinkMenuState == NULL) { 367 return B_NO_MEMORY; 368 } 369 370 SetLowColor(fTextBackColor); 371 SetViewColor(B_TRANSPARENT_32_BIT); 372 373 _NextState(fDefaultState); 374 375 return B_OK; 376 } 377 378 379 TermView::~TermView() 380 { 381 Shell* shell = fShell; 382 // _DetachShell sets fShell to NULL 383 384 _DetachShell(); 385 386 delete fDefaultState; 387 delete fSelectState; 388 delete fHyperLinkState; 389 delete fHyperLinkMenuState; 390 delete fSyncRunner; 391 delete fAutoScrollRunner; 392 delete fCharClassifier; 393 delete fVisibleTextBuffer; 394 delete fTextBuffer; 395 delete shell; 396 } 397 398 399 bool 400 TermView::IsShellBusy() const 401 { 402 return fShell != NULL && fShell->HasActiveProcesses(); 403 } 404 405 406 bool 407 TermView::GetActiveProcessInfo(ActiveProcessInfo& _info) const 408 { 409 if (fShell == NULL) { 410 _info.Unset(); 411 return false; 412 } 413 414 return fShell->GetActiveProcessInfo(_info); 415 } 416 417 418 bool 419 TermView::GetShellInfo(ShellInfo& _info) const 420 { 421 if (fShell == NULL) { 422 _info = ShellInfo(); 423 return false; 424 } 425 426 _info = fShell->Info(); 427 return true; 428 } 429 430 431 /* static */ 432 BArchivable * 433 TermView::Instantiate(BMessage* data) 434 { 435 if (validate_instantiation(data, "TermView")) { 436 TermView *view = new (std::nothrow) TermView(data); 437 return view; 438 } 439 440 return NULL; 441 } 442 443 444 status_t 445 TermView::Archive(BMessage* data, bool deep) const 446 { 447 status_t status = BView::Archive(data, deep); 448 if (status == B_OK) 449 status = data->AddString("add_on", TERM_SIGNATURE); 450 if (status == B_OK) 451 status = data->AddInt32("encoding", (int32)fEncoding); 452 if (status == B_OK) 453 status = data->AddInt32("columns", (int32)fColumns); 454 if (status == B_OK) 455 status = data->AddInt32("rows", (int32)fRows); 456 457 if (data->ReplaceString("class", "TermView") != B_OK) 458 data->AddString("class", "TermView"); 459 460 return status; 461 } 462 463 464 rgb_color 465 TermView::ForegroundColor() 466 { 467 return fSelectForeColor; 468 } 469 470 471 rgb_color 472 TermView::BackgroundColor() 473 { 474 return fSelectBackColor; 475 } 476 477 478 inline int32 479 TermView::_LineAt(float y) 480 { 481 int32 location = int32(y + fScrollOffset); 482 483 // Make sure negative offsets are rounded towards the lower neighbor, too. 484 if (location < 0) 485 location -= fFontHeight - 1; 486 487 return location / fFontHeight; 488 } 489 490 491 inline float 492 TermView::_LineOffset(int32 index) 493 { 494 return index * fFontHeight - fScrollOffset; 495 } 496 497 498 // convert view coordinates to terminal text buffer position 499 TermPos 500 TermView::_ConvertToTerminal(const BPoint &p) 501 { 502 return TermPos(p.x >= 0 ? (int32)p.x / fFontWidth : -1, _LineAt(p.y)); 503 } 504 505 506 // convert terminal text buffer position to view coordinates 507 inline BPoint 508 TermView::_ConvertFromTerminal(const TermPos &pos) 509 { 510 return BPoint(fFontWidth * pos.x, _LineOffset(pos.y)); 511 } 512 513 514 inline void 515 TermView::_InvalidateTextRect(int32 x1, int32 y1, int32 x2, int32 y2) 516 { 517 // assume the worst case with full-width characters - invalidate 2 cells 518 BRect rect(x1 * fFontWidth, _LineOffset(y1), 519 (x2 + 1) * fFontWidth * 2 - 1, _LineOffset(y2 + 1) - 1); 520 //debug_printf("Invalidate((%f, %f) - (%f, %f))\n", rect.left, rect.top, 521 //rect.right, rect.bottom); 522 Invalidate(rect); 523 } 524 525 526 void 527 TermView::GetPreferredSize(float *width, float *height) 528 { 529 if (width) 530 *width = fColumns * fFontWidth - 1; 531 if (height) 532 *height = fRows * fFontHeight - 1; 533 } 534 535 536 const char * 537 TermView::TerminalName() const 538 { 539 if (fShell == NULL) 540 return NULL; 541 542 return fShell->TTYName(); 543 } 544 545 546 //! Get width and height for terminal font 547 void 548 TermView::GetFontSize(int* _width, int* _height) 549 { 550 *_width = fFontWidth; 551 *_height = fFontHeight; 552 } 553 554 555 int 556 TermView::Rows() const 557 { 558 return fRows; 559 } 560 561 562 int 563 TermView::Columns() const 564 { 565 return fColumns; 566 } 567 568 569 //! Set number of rows and columns in terminal 570 BRect 571 TermView::SetTermSize(int rows, int columns, bool notifyShell) 572 { 573 //debug_printf("TermView::SetTermSize(%d, %d)\n", rows, columns); 574 if (rows > 0) 575 fRows = rows; 576 if (columns > 0) 577 fColumns = columns; 578 579 // To keep things simple, get rid of the selection first. 580 _Deselect(); 581 582 { 583 BAutolock _(fTextBuffer); 584 if (fTextBuffer->ResizeTo(columns, rows) != B_OK 585 || fVisibleTextBuffer->ResizeTo(columns, rows + 2, 0) 586 != B_OK) { 587 return Bounds(); 588 } 589 } 590 591 //debug_printf("Invalidate()\n"); 592 Invalidate(); 593 594 if (fScrollBar != NULL) { 595 _UpdateScrollBarRange(); 596 fScrollBar->SetSteps(fFontHeight, fFontHeight * fRows); 597 } 598 599 BRect rect(0, 0, fColumns * fFontWidth, fRows * fFontHeight); 600 601 // synchronize the visible text buffer 602 { 603 TextBufferSyncLocker _(this); 604 605 _SynchronizeWithTextBuffer(0, -1); 606 int32 offset = _LineAt(0); 607 fVisibleTextBuffer->SynchronizeWith(fTextBuffer, offset, offset, 608 offset + rows + 2); 609 fVisibleTextBufferChanged = true; 610 } 611 612 if (notifyShell) 613 fFrameResized = true; 614 615 return rect; 616 } 617 618 619 void 620 TermView::SetTermSize(BRect rect, bool notifyShell) 621 { 622 int rows; 623 int columns; 624 625 GetTermSizeFromRect(rect, &rows, &columns); 626 SetTermSize(rows, columns, notifyShell); 627 } 628 629 630 void 631 TermView::GetTermSizeFromRect(const BRect &rect, int *_rows, 632 int *_columns) 633 { 634 int columns = (rect.IntegerWidth() + 1) / fFontWidth; 635 int rows = (rect.IntegerHeight() + 1) / fFontHeight; 636 637 if (_rows) 638 *_rows = rows; 639 if (_columns) 640 *_columns = columns; 641 } 642 643 644 void 645 TermView::SetTextColor(rgb_color fore, rgb_color back) 646 { 647 fTextBackColor = back; 648 fTextForeColor = fore; 649 650 SetLowColor(fTextBackColor); 651 } 652 653 654 void 655 TermView::SetCursorColor(rgb_color fore, rgb_color back) 656 { 657 fCursorForeColor = fore; 658 fCursorBackColor = back; 659 } 660 661 662 void 663 TermView::SetSelectColor(rgb_color fore, rgb_color back) 664 { 665 fSelectForeColor = fore; 666 fSelectBackColor = back; 667 } 668 669 670 void 671 TermView::SetTermColor(uint index, rgb_color color, bool dynamic) 672 { 673 if (!dynamic) { 674 if (index < kTermColorCount) 675 fTextBuffer->SetPaletteColor(index, color); 676 return; 677 } 678 679 switch (index) { 680 case 10: 681 fTextForeColor = color; 682 break; 683 case 11: 684 fTextBackColor = color; 685 SetLowColor(fTextBackColor); 686 break; 687 case 110: 688 fTextForeColor = PrefHandler::Default()->getRGB( 689 PREF_TEXT_FORE_COLOR); 690 break; 691 case 111: 692 fTextBackColor = PrefHandler::Default()->getRGB( 693 PREF_TEXT_BACK_COLOR); 694 SetLowColor(fTextBackColor); 695 break; 696 default: 697 break; 698 } 699 } 700 701 702 int 703 TermView::Encoding() const 704 { 705 return fEncoding; 706 } 707 708 709 void 710 TermView::SetEncoding(int encoding) 711 { 712 fEncoding = encoding; 713 714 if (fShell != NULL) 715 fShell->SetEncoding(fEncoding); 716 717 BAutolock _(fTextBuffer); 718 fTextBuffer->SetEncoding(fEncoding); 719 } 720 721 722 void 723 TermView::SetMouseClipboard(BClipboard *clipboard) 724 { 725 fMouseClipboard = clipboard; 726 } 727 728 729 void 730 TermView::GetTermFont(BFont *font) const 731 { 732 if (font != NULL) 733 *font = fHalfFont; 734 } 735 736 737 //! Sets font for terminal 738 void 739 TermView::SetTermFont(const BFont *font) 740 { 741 int halfWidth = 0; 742 743 fHalfFont = font; 744 fBoldFont = font; 745 uint16 face = fBoldFont.Face(); 746 fBoldFont.SetFace(B_BOLD_FACE | (face & ~B_REGULAR_FACE)); 747 748 fHalfFont.SetSpacing(B_FIXED_SPACING); 749 750 // calculate half font's max width 751 // Not Bounding, check only A-Z (For case of fHalfFont is KanjiFont.) 752 for (int c = 0x20; c <= 0x7e; c++) { 753 char buf[4]; 754 sprintf(buf, "%c", c); 755 int tmpWidth = (int)fHalfFont.StringWidth(buf); 756 if (tmpWidth > halfWidth) 757 halfWidth = tmpWidth; 758 } 759 760 fFontWidth = halfWidth; 761 762 font_height hh; 763 fHalfFont.GetHeight(&hh); 764 765 int font_ascent = (int)hh.ascent; 766 int font_descent =(int)hh.descent; 767 int font_leading =(int)hh.leading; 768 769 if (font_leading == 0) 770 font_leading = 1; 771 772 fFontAscent = font_ascent; 773 fFontHeight = font_ascent + font_descent + font_leading + 1; 774 775 fCursorStyle = PrefHandler::Default() == NULL ? BLOCK_CURSOR 776 : PrefHandler::Default()->getCursor(PREF_CURSOR_STYLE); 777 fCursorBlinking = PrefHandler::Default()->getBool(PREF_BLINK_CURSOR); 778 779 fEmulateBold = PrefHandler::Default() == NULL ? false 780 : PrefHandler::Default()->getBool(PREF_EMULATE_BOLD); 781 782 fBrightInsteadOfBold = PrefHandler::Default() == NULL ? false 783 : PrefHandler::Default()->getBool(PREF_BRIGHT_INSTEAD_OF_BOLD); 784 785 _ScrollTo(0, false); 786 if (fScrollBar != NULL) 787 fScrollBar->SetSteps(fFontHeight, fFontHeight * fRows); 788 } 789 790 791 void 792 TermView::SetScrollBar(BScrollBar *scrollBar) 793 { 794 fScrollBar = scrollBar; 795 if (fScrollBar != NULL) 796 fScrollBar->SetSteps(fFontHeight, fFontHeight * fRows); 797 } 798 799 800 void 801 TermView::Copy(BClipboard *clipboard) 802 { 803 BAutolock _(fTextBuffer); 804 805 if (!_HasSelection()) 806 return; 807 808 BString copyStr; 809 fTextBuffer->GetStringFromRegion(copyStr, fSelection.Start(), 810 fSelection.End()); 811 812 if (clipboard->Lock()) { 813 BMessage *clipMsg = NULL; 814 clipboard->Clear(); 815 816 if ((clipMsg = clipboard->Data()) != NULL) { 817 clipMsg->AddData("text/plain", B_MIME_TYPE, copyStr.String(), 818 copyStr.Length()); 819 clipboard->Commit(); 820 } 821 clipboard->Unlock(); 822 } 823 } 824 825 826 void 827 TermView::Paste(BClipboard *clipboard) 828 { 829 if (clipboard->Lock()) { 830 BMessage *clipMsg = clipboard->Data(); 831 const char* text; 832 ssize_t numBytes; 833 if (clipMsg->FindData("text/plain", B_MIME_TYPE, 834 (const void**)&text, &numBytes) == B_OK ) { 835 _WritePTY(text, numBytes); 836 } 837 838 clipboard->Unlock(); 839 840 _ScrollTo(0, true); 841 } 842 } 843 844 845 void 846 TermView::SelectAll() 847 { 848 BAutolock _(fTextBuffer); 849 850 _Select(TermPos(0, -fTextBuffer->HistorySize()), 851 TermPos(0, fTextBuffer->Height()), false, true); 852 } 853 854 855 void 856 TermView::Clear() 857 { 858 _Deselect(); 859 860 { 861 BAutolock _(fTextBuffer); 862 fTextBuffer->Clear(true); 863 } 864 fVisibleTextBuffer->Clear(true); 865 866 //debug_printf("Invalidate()\n"); 867 Invalidate(); 868 869 _ScrollTo(0, false); 870 if (fScrollBar) { 871 fScrollBar->SetRange(0, 0); 872 fScrollBar->SetProportion(1); 873 } 874 } 875 876 877 //! Draw region 878 void 879 TermView::_InvalidateTextRange(TermPos start, TermPos end) 880 { 881 if (end < start) 882 std::swap(start, end); 883 884 if (start.y == end.y) { 885 _InvalidateTextRect(start.x, start.y, end.x, end.y); 886 } else { 887 _InvalidateTextRect(start.x, start.y, fColumns, start.y); 888 889 if (end.y - start.y > 0) 890 _InvalidateTextRect(0, start.y + 1, fColumns, end.y - 1); 891 892 _InvalidateTextRect(0, end.y, end.x, end.y); 893 } 894 } 895 896 897 status_t 898 TermView::_AttachShell(Shell *shell) 899 { 900 if (shell == NULL) 901 return B_BAD_VALUE; 902 903 fShell = shell; 904 905 return fShell->AttachBuffer(TextBuffer()); 906 } 907 908 909 void 910 TermView::_DetachShell() 911 { 912 fShell->DetachBuffer(); 913 fShell = NULL; 914 } 915 916 917 void 918 TermView::_SwitchCursorBlinking(bool blinkingOn) 919 { 920 if (blinkingOn) { 921 if (fCursorBlinkRunner == NULL) { 922 BMessage blinkMessage(kBlinkCursor); 923 fCursorBlinkRunner = new (std::nothrow) BMessageRunner( 924 BMessenger(this), &blinkMessage, kCursorBlinkInterval); 925 } 926 } else { 927 // make sure the cursor becomes visible 928 fCursorState = 0; 929 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 930 delete fCursorBlinkRunner; 931 fCursorBlinkRunner = NULL; 932 } 933 } 934 935 936 void 937 TermView::_Activate() 938 { 939 fActive = true; 940 _SwitchCursorBlinking(fCursorBlinking); 941 } 942 943 944 void 945 TermView::_Deactivate() 946 { 947 // make sure the cursor becomes visible 948 fCursorState = 0; 949 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 950 951 _SwitchCursorBlinking(false); 952 953 fActive = false; 954 } 955 956 957 //! Draw part of a line in the given view. 958 void 959 TermView::_DrawLinePart(int32 x1, int32 y1, uint32 attr, char *buf, 960 int32 width, Highlight* highlight, bool cursor, BView *inView) 961 { 962 if (highlight != NULL) 963 attr = highlight->Highlighter()->AdjustTextAttributes(attr); 964 965 inView->SetFont(IS_BOLD(attr) && !fEmulateBold && !fBrightInsteadOfBold 966 ? &fBoldFont : &fHalfFont); 967 968 // Set pen point 969 int x2 = x1 + fFontWidth * width; 970 int y2 = y1 + fFontHeight; 971 972 rgb_color rgb_fore = fTextForeColor; 973 rgb_color rgb_back = fTextBackColor; 974 975 // color attribute 976 int forecolor = IS_FORECOLOR(attr); 977 int backcolor = IS_BACKCOLOR(attr); 978 979 if (IS_FORESET(attr)) 980 rgb_fore = fTextBuffer->PaletteColor(forecolor); 981 if (IS_BACKSET(attr)) 982 rgb_back = fTextBuffer->PaletteColor(backcolor); 983 984 // Selection check. 985 if (cursor) { 986 rgb_fore = fCursorForeColor; 987 rgb_back = fCursorBackColor; 988 } else if (highlight != NULL) { 989 rgb_fore = highlight->Highlighter()->ForegroundColor(); 990 rgb_back = highlight->Highlighter()->BackgroundColor(); 991 } else { 992 // Reverse attribute(If selected area, don't reverse color). 993 if (IS_INVERSE(attr)) { 994 rgb_color rgb_tmp = rgb_fore; 995 rgb_fore = rgb_back; 996 rgb_back = rgb_tmp; 997 } 998 } 999 1000 // Fill color at Background color and set low color. 1001 inView->SetHighColor(rgb_back); 1002 inView->FillRect(BRect(x1, y1, x2 - 1, y2 - 1)); 1003 inView->SetLowColor(rgb_back); 1004 inView->SetHighColor(rgb_fore); 1005 1006 // Draw character. 1007 if (IS_BOLD(attr)) { 1008 if (fBrightInsteadOfBold) { 1009 rgb_color bright = rgb_fore; 1010 1011 bright.red = saturated_add<uint8>(bright.red, 64); 1012 bright.green = saturated_add<uint8>(bright.green, 64); 1013 bright.blue = saturated_add<uint8>(bright.blue, 64); 1014 1015 inView->SetHighColor(bright); 1016 } else if (fEmulateBold) { 1017 inView->MovePenTo(x1 - 1, y1 + fFontAscent - 1); 1018 inView->DrawString((char *)buf); 1019 inView->SetDrawingMode(B_OP_BLEND); 1020 } 1021 } 1022 1023 inView->MovePenTo(x1, y1 + fFontAscent); 1024 inView->DrawString((char *)buf); 1025 inView->SetDrawingMode(B_OP_COPY); 1026 1027 // underline attribute 1028 if (IS_UNDER(attr)) { 1029 inView->MovePenTo(x1, y1 + fFontAscent); 1030 inView->StrokeLine(BPoint(x1 , y1 + fFontAscent), 1031 BPoint(x2 , y1 + fFontAscent)); 1032 } 1033 } 1034 1035 1036 /*! Caller must have locked fTextBuffer. 1037 */ 1038 void 1039 TermView::_DrawCursor() 1040 { 1041 BRect rect(fFontWidth * fCursor.x, _LineOffset(fCursor.y), 0, 0); 1042 rect.right = rect.left + fFontWidth - 1; 1043 rect.bottom = rect.top + fFontHeight - 1; 1044 int32 firstVisible = _LineAt(0); 1045 1046 UTF8Char character; 1047 uint32 attr = 0; 1048 1049 bool cursorVisible = _IsCursorVisible(); 1050 1051 if (cursorVisible) { 1052 switch (fCursorStyle) { 1053 case UNDERLINE_CURSOR: 1054 rect.top = rect.bottom - 2; 1055 break; 1056 case IBEAM_CURSOR: 1057 rect.right = rect.left + 1; 1058 break; 1059 case BLOCK_CURSOR: 1060 default: 1061 break; 1062 } 1063 } 1064 1065 Highlight* highlight = _CheckHighlightRegion(TermPos(fCursor.x, fCursor.y)); 1066 if (fVisibleTextBuffer->GetChar(fCursor.y - firstVisible, fCursor.x, 1067 character, attr) == A_CHAR 1068 && (fCursorStyle == BLOCK_CURSOR || !cursorVisible)) { 1069 1070 int32 width = IS_WIDTH(attr) ? FULL_WIDTH : HALF_WIDTH; 1071 char buffer[5]; 1072 int32 bytes = UTF8Char::ByteCount(character.bytes[0]); 1073 memcpy(buffer, character.bytes, bytes); 1074 buffer[bytes] = '\0'; 1075 1076 _DrawLinePart(fCursor.x * fFontWidth, (int32)rect.top, attr, buffer, 1077 width, highlight, cursorVisible, this); 1078 } else { 1079 if (highlight != NULL) 1080 SetHighColor(highlight->Highlighter()->BackgroundColor()); 1081 else if (cursorVisible) 1082 SetHighColor(fCursorBackColor ); 1083 else { 1084 uint32 count = 0; 1085 rgb_color rgb_back = fTextBackColor; 1086 if (fTextBuffer->IsAlternateScreenActive()) 1087 // alternate screen uses cell attributes beyond the line ends 1088 fTextBuffer->GetCellAttributes( 1089 fCursor.y, fCursor.x, attr, count); 1090 else 1091 attr = fVisibleTextBuffer->GetLineColor( 1092 fCursor.y - firstVisible); 1093 1094 if (IS_BACKSET(attr)) 1095 rgb_back = fTextBuffer->PaletteColor(IS_BACKCOLOR(attr)); 1096 SetHighColor(rgb_back); 1097 } 1098 1099 if (IS_WIDTH(attr) && fCursorStyle != IBEAM_CURSOR) 1100 rect.right += fFontWidth; 1101 1102 FillRect(rect); 1103 } 1104 } 1105 1106 1107 bool 1108 TermView::_IsCursorVisible() const 1109 { 1110 return !fCursorHidden && fCursorState < kCursorVisibleIntervals; 1111 } 1112 1113 1114 void 1115 TermView::_BlinkCursor() 1116 { 1117 bool wasVisible = _IsCursorVisible(); 1118 1119 if (!wasVisible && fInline && fInline->IsActive()) 1120 return; 1121 1122 bigtime_t now = system_time(); 1123 if (Window()->IsActive() && now - fLastActivityTime >= kCursorBlinkInterval) 1124 fCursorState = (fCursorState + 1) % kCursorBlinkIntervals; 1125 else 1126 fCursorState = 0; 1127 1128 if (wasVisible != _IsCursorVisible()) 1129 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 1130 } 1131 1132 1133 void 1134 TermView::_ActivateCursor(bool invalidate) 1135 { 1136 fLastActivityTime = system_time(); 1137 if (invalidate && fCursorState != 0) 1138 _BlinkCursor(); 1139 else 1140 fCursorState = 0; 1141 } 1142 1143 1144 //! Update scroll bar range and knob size. 1145 void 1146 TermView::_UpdateScrollBarRange() 1147 { 1148 if (fScrollBar == NULL) 1149 return; 1150 1151 int32 historySize; 1152 { 1153 BAutolock _(fTextBuffer); 1154 historySize = fTextBuffer->HistorySize(); 1155 } 1156 1157 float viewHeight = fRows * fFontHeight; 1158 float historyHeight = (float)historySize * fFontHeight; 1159 1160 //debug_printf("TermView::_UpdateScrollBarRange(): history: %ld, range: %f - 0\n", 1161 //historySize, -historyHeight); 1162 1163 fScrollBar->SetRange(-historyHeight, 0); 1164 if (historySize > 0) 1165 fScrollBar->SetProportion(viewHeight / (viewHeight + historyHeight)); 1166 } 1167 1168 1169 //! Handler for SIGWINCH 1170 void 1171 TermView::_UpdateSIGWINCH() 1172 { 1173 if (fFrameResized) { 1174 fShell->UpdateWindowSize(fRows, fColumns); 1175 fFrameResized = false; 1176 } 1177 } 1178 1179 1180 void 1181 TermView::AttachedToWindow() 1182 { 1183 fMouseButtons = 0; 1184 1185 _UpdateModifiers(); 1186 1187 // update the terminal size because it may have changed while the TermView 1188 // was detached from the window. On such conditions FrameResized was not 1189 // called when the resize occured 1190 SetTermSize(Bounds(), true); 1191 MakeFocus(true); 1192 if (fScrollBar) { 1193 fScrollBar->SetSteps(fFontHeight, fFontHeight * fRows); 1194 _UpdateScrollBarRange(); 1195 } 1196 1197 BMessenger thisMessenger(this); 1198 1199 BMessage message(kUpdateSigWinch); 1200 fWinchRunner = new (std::nothrow) BMessageRunner(thisMessenger, 1201 &message, 500000); 1202 1203 { 1204 TextBufferSyncLocker _(this); 1205 fTextBuffer->SetListener(thisMessenger); 1206 _SynchronizeWithTextBuffer(0, -1); 1207 } 1208 1209 be_clipboard->StartWatching(thisMessenger); 1210 } 1211 1212 1213 void 1214 TermView::DetachedFromWindow() 1215 { 1216 be_clipboard->StopWatching(BMessenger(this)); 1217 1218 _NextState(fDefaultState); 1219 1220 delete fWinchRunner; 1221 fWinchRunner = NULL; 1222 1223 delete fCursorBlinkRunner; 1224 fCursorBlinkRunner = NULL; 1225 1226 delete fResizeRunner; 1227 fResizeRunner = NULL; 1228 1229 { 1230 BAutolock _(fTextBuffer); 1231 fTextBuffer->UnsetListener(); 1232 } 1233 } 1234 1235 1236 void 1237 TermView::Draw(BRect updateRect) 1238 { 1239 int32 x1 = (int32)updateRect.left / fFontWidth; 1240 int32 x2 = std::min((int)updateRect.right / fFontWidth, fColumns - 1); 1241 1242 int32 firstVisible = _LineAt(0); 1243 int32 y1 = _LineAt(updateRect.top); 1244 int32 y2 = std::min(_LineAt(updateRect.bottom), (int32)fRows - 1); 1245 1246 // clear the area to the right of the line ends 1247 if (y1 <= y2) { 1248 float clearLeft = fColumns * fFontWidth; 1249 if (clearLeft <= updateRect.right) { 1250 BRect rect(clearLeft, updateRect.top, updateRect.right, 1251 updateRect.bottom); 1252 SetHighColor(fTextBackColor); 1253 FillRect(rect); 1254 } 1255 } 1256 1257 // clear the area below the last line 1258 if (y2 == fRows - 1) { 1259 float clearTop = _LineOffset(fRows); 1260 if (clearTop <= updateRect.bottom) { 1261 BRect rect(updateRect.left, clearTop, updateRect.right, 1262 updateRect.bottom); 1263 SetHighColor(fTextBackColor); 1264 FillRect(rect); 1265 } 1266 } 1267 1268 // draw the affected line parts 1269 if (x1 <= x2) { 1270 uint32 attr = 0; 1271 1272 for (int32 j = y1; j <= y2; j++) { 1273 int32 k = x1; 1274 char buf[fColumns * 4 + 1]; 1275 1276 if (fVisibleTextBuffer->IsFullWidthChar(j - firstVisible, k)) 1277 k--; 1278 1279 if (k < 0) 1280 k = 0; 1281 1282 for (int32 i = k; i <= x2;) { 1283 int32 lastColumn = x2; 1284 Highlight* highlight = _CheckHighlightRegion(j, i, lastColumn); 1285 // This will clip lastColumn to the selection start or end 1286 // to ensure the selection is not drawn at the same time as 1287 // something else 1288 int32 count = fVisibleTextBuffer->GetString(j - firstVisible, i, 1289 lastColumn, buf, attr); 1290 1291 // debug_printf(" fVisibleTextBuffer->GetString(%ld, %ld, %ld) -> (%ld, \"%.*s\"), highlight: %p\n", 1292 // j - firstVisible, i, lastColumn, count, (int)count, buf, highlight); 1293 1294 if (count == 0) { 1295 // No chars to draw : we just fill the rectangle with the 1296 // back color of the last char at the left 1297 int nextColumn = lastColumn + 1; 1298 BRect rect(fFontWidth * i, _LineOffset(j), 1299 fFontWidth * nextColumn - 1, 0); 1300 rect.bottom = rect.top + fFontHeight - 1; 1301 1302 rgb_color rgb_back = highlight != NULL 1303 ? highlight->Highlighter()->BackgroundColor() 1304 : fTextBackColor; 1305 1306 if (fTextBuffer->IsAlternateScreenActive()) { 1307 // alternate screen uses cell attributes 1308 // beyond the line ends 1309 uint32 count = 0; 1310 fTextBuffer->GetCellAttributes(j, i, attr, count); 1311 rect.right = rect.left + fFontWidth * count - 1; 1312 nextColumn = i + count; 1313 } else 1314 attr = fVisibleTextBuffer->GetLineColor(j - firstVisible); 1315 1316 if (IS_BACKSET(attr)) { 1317 int backcolor = IS_BACKCOLOR(attr); 1318 rgb_back = fTextBuffer->PaletteColor(backcolor); 1319 } 1320 1321 SetHighColor(rgb_back); 1322 rgb_back = HighColor(); 1323 FillRect(rect); 1324 1325 // Go on to the next block 1326 i = nextColumn; 1327 continue; 1328 } 1329 1330 // Note: full-width characters GetString()-ed always 1331 // with count 1, so this hardcoding is safe. From the other 1332 // side - drawing the whole string with one call render the 1333 // characters not aligned to cells grid - that looks much more 1334 // inaccurate for full-width strings than for half-width ones. 1335 if (IS_WIDTH(attr)) 1336 count = FULL_WIDTH; 1337 1338 _DrawLinePart(fFontWidth * i, (int32)_LineOffset(j), 1339 attr, buf, count, highlight, false, this); 1340 i += count; 1341 } 1342 } 1343 } 1344 1345 if (fInline && fInline->IsActive()) 1346 _DrawInlineMethodString(); 1347 1348 if (fCursor >= TermPos(x1, y1) && fCursor <= TermPos(x2, y2)) 1349 _DrawCursor(); 1350 } 1351 1352 1353 void 1354 TermView::_DoPrint(BRect updateRect) 1355 { 1356 #if 0 1357 uint32 attr; 1358 uchar buf[1024]; 1359 1360 const int numLines = (int)((updateRect.Height()) / fFontHeight); 1361 1362 int y1 = (int)(updateRect.top) / fFontHeight; 1363 y1 = y1 -(fScrBufSize - numLines * 2); 1364 if (y1 < 0) 1365 y1 = 0; 1366 1367 const int y2 = y1 + numLines -1; 1368 1369 const int x1 = (int)(updateRect.left) / fFontWidth; 1370 const int x2 = (int)(updateRect.right) / fFontWidth; 1371 1372 for (int j = y1; j <= y2; j++) { 1373 // If(x1, y1) Buffer is in string full width character, 1374 // alignment start position. 1375 1376 int k = x1; 1377 if (fTextBuffer->IsFullWidthChar(j, k)) 1378 k--; 1379 1380 if (k < 0) 1381 k = 0; 1382 1383 for (int i = k; i <= x2;) { 1384 int count = fTextBuffer->GetString(j, i, x2, buf, &attr); 1385 if (count < 0) { 1386 i += abs(count); 1387 continue; 1388 } 1389 1390 _DrawLinePart(fFontWidth * i, fFontHeight * j, 1391 attr, buf, count, false, false, this); 1392 i += count; 1393 } 1394 } 1395 #endif // 0 1396 } 1397 1398 1399 void 1400 TermView::WindowActivated(bool active) 1401 { 1402 BView::WindowActivated(active); 1403 if (active && IsFocus()) { 1404 if (!fActive) 1405 _Activate(); 1406 } else { 1407 if (fActive) 1408 _Deactivate(); 1409 } 1410 1411 _UpdateModifiers(); 1412 1413 fActiveState->WindowActivated(active); 1414 } 1415 1416 1417 void 1418 TermView::MakeFocus(bool focusState) 1419 { 1420 BView::MakeFocus(focusState); 1421 1422 if (focusState && Window() && Window()->IsActive()) { 1423 if (!fActive) 1424 _Activate(); 1425 } else { 1426 if (fActive) 1427 _Deactivate(); 1428 } 1429 } 1430 1431 1432 void 1433 TermView::KeyDown(const char *bytes, int32 numBytes) 1434 { 1435 _UpdateModifiers(); 1436 1437 fActiveState->KeyDown(bytes, numBytes); 1438 } 1439 1440 1441 void 1442 TermView::FrameResized(float width, float height) 1443 { 1444 //debug_printf("TermView::FrameResized(%f, %f)\n", width, height); 1445 int32 columns = ((int32)width + 1) / fFontWidth; 1446 int32 rows = ((int32)height + 1) / fFontHeight; 1447 1448 if (columns == fColumns && rows == fRows) 1449 return; 1450 1451 bool hasResizeView = fResizeRunner != NULL; 1452 if (!hasResizeView) { 1453 // show the current size in a view 1454 fResizeView = new BStringView(BRect(100, 100, 300, 140), 1455 B_TRANSLATE("size"), ""); 1456 fResizeView->SetAlignment(B_ALIGN_CENTER); 1457 fResizeView->SetFont(be_bold_font); 1458 1459 BMessage message(MSG_REMOVE_RESIZE_VIEW_IF_NEEDED); 1460 fResizeRunner = new(std::nothrow) BMessageRunner(BMessenger(this), 1461 &message, 25000LL); 1462 } 1463 1464 BString text; 1465 text << columns << " x " << rows; 1466 fResizeView->SetText(text.String()); 1467 fResizeView->GetPreferredSize(&width, &height); 1468 fResizeView->ResizeTo(width * 1.5, height * 1.5); 1469 fResizeView->MoveTo((Bounds().Width() - fResizeView->Bounds().Width()) / 2, 1470 (Bounds().Height()- fResizeView->Bounds().Height()) / 2); 1471 if (!hasResizeView && fResizeViewDisableCount < 1) 1472 AddChild(fResizeView); 1473 1474 if (fResizeViewDisableCount > 0) 1475 fResizeViewDisableCount--; 1476 1477 SetTermSize(rows, columns, true); 1478 } 1479 1480 1481 void 1482 TermView::MessageReceived(BMessage *msg) 1483 { 1484 if (fActiveState->MessageReceived(msg)) 1485 return; 1486 1487 entry_ref ref; 1488 const char *ctrl_l = "\x0c"; 1489 1490 // first check for any dropped message 1491 if (msg->WasDropped() && (msg->what == B_SIMPLE_DATA 1492 || msg->what == B_MIME_DATA)) { 1493 char *text; 1494 ssize_t numBytes; 1495 //rgb_color *color; 1496 1497 int32 i = 0; 1498 1499 if (msg->FindRef("refs", i++, &ref) == B_OK) { 1500 // first check if secondary mouse button is pressed 1501 int32 buttons = 0; 1502 msg->FindInt32("buttons", &buttons); 1503 1504 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 1505 // start popup menu 1506 _SecondaryMouseButtonDropped(msg); 1507 return; 1508 } 1509 1510 _DoFileDrop(ref); 1511 1512 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1513 _WritePTY(" ", 1); 1514 _DoFileDrop(ref); 1515 } 1516 return; 1517 #if 0 1518 } else if (msg->FindData("RGBColor", B_RGB_COLOR_TYPE, 1519 (const void **)&color, &numBytes) == B_OK 1520 && numBytes == sizeof(color)) { 1521 // TODO: handle color drop 1522 // maybe only on replicants ? 1523 return; 1524 #endif 1525 } else if (msg->FindData("text/plain", B_MIME_TYPE, 1526 (const void **)&text, &numBytes) == B_OK) { 1527 _WritePTY(text, numBytes); 1528 return; 1529 } 1530 } 1531 1532 switch (msg->what) { 1533 case B_SIMPLE_DATA: 1534 case B_REFS_RECEIVED: 1535 { 1536 // handle refs if they weren't dropped 1537 int32 i = 0; 1538 if (msg->FindRef("refs", i++, &ref) == B_OK) { 1539 _DoFileDrop(ref); 1540 1541 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1542 _WritePTY(" ", 1); 1543 _DoFileDrop(ref); 1544 } 1545 } else 1546 BView::MessageReceived(msg); 1547 break; 1548 } 1549 1550 case B_COPY: 1551 Copy(be_clipboard); 1552 break; 1553 1554 case B_PASTE: 1555 { 1556 int32 code; 1557 if (msg->FindInt32("index", &code) == B_OK) 1558 Paste(be_clipboard); 1559 break; 1560 } 1561 1562 case B_CLIPBOARD_CHANGED: 1563 // This message originates from the system clipboard. Overwrite 1564 // the contents of the mouse clipboard with the ones from the 1565 // system clipboard, in case it contains text data. 1566 if (be_clipboard->Lock()) { 1567 if (fMouseClipboard->Lock()) { 1568 BMessage* clipMsgA = be_clipboard->Data(); 1569 const char* text; 1570 ssize_t numBytes; 1571 if (clipMsgA->FindData("text/plain", B_MIME_TYPE, 1572 (const void**)&text, &numBytes) == B_OK ) { 1573 fMouseClipboard->Clear(); 1574 BMessage* clipMsgB = fMouseClipboard->Data(); 1575 clipMsgB->AddData("text/plain", B_MIME_TYPE, 1576 text, numBytes); 1577 fMouseClipboard->Commit(); 1578 } 1579 fMouseClipboard->Unlock(); 1580 } 1581 be_clipboard->Unlock(); 1582 } 1583 break; 1584 1585 case B_SELECT_ALL: 1586 SelectAll(); 1587 break; 1588 1589 case B_SET_PROPERTY: 1590 { 1591 int32 i; 1592 int32 encodingID; 1593 BMessage specifier; 1594 if (msg->GetCurrentSpecifier(&i, &specifier) == B_OK 1595 && !strcmp("encoding", 1596 specifier.FindString("property", i)) == 0) { 1597 msg->FindInt32 ("data", &encodingID); 1598 SetEncoding(encodingID); 1599 msg->SendReply(B_REPLY); 1600 } else { 1601 BView::MessageReceived(msg); 1602 } 1603 break; 1604 } 1605 1606 case B_GET_PROPERTY: 1607 { 1608 int32 i; 1609 BMessage specifier; 1610 if (msg->GetCurrentSpecifier(&i, &specifier) == B_OK 1611 && strcmp("encoding", 1612 specifier.FindString("property", i)) == 0) { 1613 BMessage reply(B_REPLY); 1614 reply.AddInt32("result", Encoding()); 1615 msg->SendReply(&reply); 1616 } else if (strcmp("tty", 1617 specifier.FindString("property", i)) == 0) { 1618 BMessage reply(B_REPLY); 1619 reply.AddString("result", TerminalName()); 1620 msg->SendReply(&reply); 1621 } else { 1622 BView::MessageReceived(msg); 1623 } 1624 break; 1625 } 1626 1627 case B_MODIFIERS_CHANGED: 1628 { 1629 _UpdateModifiers(); 1630 break; 1631 } 1632 1633 case B_INPUT_METHOD_EVENT: 1634 { 1635 int32 opcode; 1636 if (msg->FindInt32("be:opcode", &opcode) == B_OK) { 1637 switch (opcode) { 1638 case B_INPUT_METHOD_STARTED: 1639 { 1640 BMessenger messenger; 1641 if (msg->FindMessenger("be:reply_to", 1642 &messenger) == B_OK) { 1643 fInline = new (std::nothrow) 1644 InlineInput(messenger); 1645 } 1646 break; 1647 } 1648 1649 case B_INPUT_METHOD_STOPPED: 1650 delete fInline; 1651 fInline = NULL; 1652 break; 1653 1654 case B_INPUT_METHOD_CHANGED: 1655 if (fInline != NULL) 1656 _HandleInputMethodChanged(msg); 1657 break; 1658 1659 case B_INPUT_METHOD_LOCATION_REQUEST: 1660 if (fInline != NULL) 1661 _HandleInputMethodLocationRequest(); 1662 break; 1663 1664 default: 1665 break; 1666 } 1667 } 1668 break; 1669 } 1670 1671 case B_MOUSE_WHEEL_CHANGED: 1672 { 1673 // overridden to allow scrolling emulation in alternative screen 1674 // mode 1675 BAutolock locker(fTextBuffer); 1676 float deltaY = 0; 1677 if (fTextBuffer->IsAlternateScreenActive() 1678 && msg->FindFloat("be:wheel_delta_y", &deltaY) == B_OK 1679 && deltaY != 0) { 1680 // We are in alternative screen mode and have a vertical delta 1681 // we can work with -- emulate scrolling via terminal escape 1682 // sequences. 1683 locker.Unlock(); 1684 1685 // scroll pagewise, if one of Option, Command, or Control is 1686 // pressed 1687 int32 steps; 1688 const char* stepString; 1689 if ((modifiers() & B_SHIFT_KEY) != 0) { 1690 // pagewise 1691 stepString = deltaY > 0 1692 ? PAGE_DOWN_KEY_CODE : PAGE_UP_KEY_CODE; 1693 steps = abs((int)deltaY); 1694 } else { 1695 // three lines per step 1696 stepString = deltaY > 0 1697 ? DOWN_ARROW_KEY_CODE : UP_ARROW_KEY_CODE; 1698 steps = 3 * abs((int)deltaY); 1699 } 1700 1701 // We want to do only a single write(), so compose a string 1702 // repeating the sequence as often as required by the delta. 1703 BString toWrite; 1704 for (int32 i = 0; i <steps; i++) 1705 toWrite << stepString; 1706 1707 _WritePTY(toWrite.String(), toWrite.Length()); 1708 } else { 1709 // let the BView's implementation handle the standard scrolling 1710 locker.Unlock(); 1711 BView::MessageReceived(msg); 1712 } 1713 1714 break; 1715 } 1716 1717 case MENU_CLEAR_ALL: 1718 Clear(); 1719 fShell->Write(ctrl_l, 1); 1720 break; 1721 case kBlinkCursor: 1722 _BlinkCursor(); 1723 break; 1724 case kUpdateSigWinch: 1725 _UpdateSIGWINCH(); 1726 break; 1727 case kSecondaryMouseDropAction: 1728 _DoSecondaryMouseDropAction(msg); 1729 break; 1730 case MSG_TERMINAL_BUFFER_CHANGED: 1731 { 1732 TextBufferSyncLocker _(this); 1733 _SynchronizeWithTextBuffer(0, -1); 1734 break; 1735 } 1736 case MSG_SET_TERMINAL_TITLE: 1737 { 1738 const char* title; 1739 if (msg->FindString("title", &title) == B_OK) { 1740 if (fListener != NULL) 1741 fListener->SetTermViewTitle(this, title); 1742 } 1743 break; 1744 } 1745 case MSG_SET_TERMINAL_COLORS: 1746 { 1747 int32 count = 0; 1748 if (msg->FindInt32("count", &count) != B_OK) 1749 break; 1750 bool dynamic = false; 1751 if (msg->FindBool("dynamic", &dynamic) != B_OK) 1752 break; 1753 for (int i = 0; i < count; i++) { 1754 uint8 index = 0; 1755 if (msg->FindUInt8("index", i, &index) != B_OK) 1756 break; 1757 1758 ssize_t bytes = 0; 1759 rgb_color* color = 0; 1760 if (msg->FindData("color", B_RGB_COLOR_TYPE, 1761 i, (const void**)&color, &bytes) != B_OK) 1762 break; 1763 SetTermColor(index, *color, dynamic); 1764 } 1765 break; 1766 } 1767 case MSG_RESET_TERMINAL_COLORS: 1768 { 1769 int32 count = 0; 1770 if (msg->FindInt32("count", &count) != B_OK) 1771 break; 1772 bool dynamic = false; 1773 if (msg->FindBool("dynamic", &dynamic) != B_OK) 1774 break; 1775 for (int i = 0; i < count; i++) { 1776 uint8 index = 0; 1777 if (msg->FindUInt8("index", i, &index) != B_OK) 1778 break; 1779 1780 if (index < kTermColorCount) 1781 SetTermColor(index, 1782 TermApp::DefaultPalette()[index], dynamic); 1783 } 1784 break; 1785 } 1786 case MSG_SET_CURSOR_STYLE: 1787 { 1788 int32 style = BLOCK_CURSOR; 1789 if (msg->FindInt32("style", &style) == B_OK) 1790 fCursorStyle = style; 1791 1792 bool blinking = fCursorBlinking; 1793 if (msg->FindBool("blinking", &blinking) == B_OK) { 1794 fCursorBlinking = blinking; 1795 _SwitchCursorBlinking(fCursorBlinking); 1796 } 1797 1798 bool hidden = fCursorHidden; 1799 if (msg->FindBool("hidden", &hidden) == B_OK) 1800 fCursorHidden = hidden; 1801 break; 1802 } 1803 case MSG_REPORT_MOUSE_EVENT: 1804 { 1805 bool report; 1806 if (msg->FindBool("reportX10MouseEvent", &report) == B_OK) 1807 fReportX10MouseEvent = report; 1808 1809 if (msg->FindBool("reportNormalMouseEvent", &report) == B_OK) 1810 fReportNormalMouseEvent = report; 1811 1812 if (msg->FindBool("reportButtonMouseEvent", &report) == B_OK) 1813 fReportButtonMouseEvent = report; 1814 1815 if (msg->FindBool("reportAnyMouseEvent", &report) == B_OK) 1816 fReportAnyMouseEvent = report; 1817 break; 1818 } 1819 case MSG_REMOVE_RESIZE_VIEW_IF_NEEDED: 1820 { 1821 BPoint point; 1822 uint32 buttons; 1823 GetMouse(&point, &buttons, false); 1824 if (buttons != 0) 1825 break; 1826 1827 if (fResizeView != NULL) { 1828 fResizeView->RemoveSelf(); 1829 delete fResizeView; 1830 fResizeView = NULL; 1831 } 1832 delete fResizeRunner; 1833 fResizeRunner = NULL; 1834 break; 1835 } 1836 1837 case MSG_QUIT_TERMNAL: 1838 { 1839 int32 reason; 1840 if (msg->FindInt32("reason", &reason) != B_OK) 1841 reason = 0; 1842 if (fListener != NULL) 1843 fListener->NotifyTermViewQuit(this, reason); 1844 break; 1845 } 1846 default: 1847 BView::MessageReceived(msg); 1848 break; 1849 } 1850 } 1851 1852 1853 status_t 1854 TermView::GetSupportedSuites(BMessage *message) 1855 { 1856 BPropertyInfo propInfo(sPropList); 1857 message->AddString("suites", "suite/vnd.naan-termview"); 1858 message->AddFlat("messages", &propInfo); 1859 return BView::GetSupportedSuites(message); 1860 } 1861 1862 1863 void 1864 TermView::ScrollTo(BPoint where) 1865 { 1866 //debug_printf("TermView::ScrollTo(): %f -> %f\n", fScrollOffset, where.y); 1867 float diff = where.y - fScrollOffset; 1868 if (diff == 0) 1869 return; 1870 1871 float bottom = Bounds().bottom; 1872 int32 oldFirstLine = _LineAt(0); 1873 int32 oldLastLine = _LineAt(bottom); 1874 int32 newFirstLine = _LineAt(diff); 1875 int32 newLastLine = _LineAt(bottom + diff); 1876 1877 fScrollOffset = where.y; 1878 1879 // invalidate the current cursor position before scrolling 1880 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 1881 1882 // scroll contents 1883 BRect destRect(Frame().OffsetToCopy(Bounds().LeftTop())); 1884 BRect sourceRect(destRect.OffsetByCopy(0, diff)); 1885 //debug_printf("CopyBits(((%f, %f) - (%f, %f)) -> (%f, %f) - (%f, %f))\n", 1886 //sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom, 1887 //destRect.left, destRect.top, destRect.right, destRect.bottom); 1888 CopyBits(sourceRect, destRect); 1889 1890 // sync visible text buffer with text buffer 1891 if (newFirstLine != oldFirstLine || newLastLine != oldLastLine) { 1892 if (newFirstLine != oldFirstLine) 1893 { 1894 //debug_printf("fVisibleTextBuffer->ScrollBy(%ld)\n", newFirstLine - oldFirstLine); 1895 fVisibleTextBuffer->ScrollBy(newFirstLine - oldFirstLine); 1896 } 1897 TextBufferSyncLocker _(this); 1898 if (diff < 0) 1899 _SynchronizeWithTextBuffer(newFirstLine, oldFirstLine - 1); 1900 else 1901 _SynchronizeWithTextBuffer(oldLastLine + 1, newLastLine); 1902 } 1903 } 1904 1905 1906 void 1907 TermView::TargetedByScrollView(BScrollView *scrollView) 1908 { 1909 BView::TargetedByScrollView(scrollView); 1910 1911 SetScrollBar(scrollView ? scrollView->ScrollBar(B_VERTICAL) : NULL); 1912 } 1913 1914 1915 BHandler* 1916 TermView::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 1917 int32 what, const char* property) 1918 { 1919 BHandler* target = this; 1920 BPropertyInfo propInfo(sPropList); 1921 if (propInfo.FindMatch(message, index, specifier, what, property) < B_OK) { 1922 target = BView::ResolveSpecifier(message, index, specifier, what, 1923 property); 1924 } 1925 1926 return target; 1927 } 1928 1929 1930 void 1931 TermView::_SecondaryMouseButtonDropped(BMessage* msg) 1932 { 1933 // Launch menu to choose what is to do with the msg data 1934 BPoint point; 1935 if (msg->FindPoint("_drop_point_", &point) != B_OK) 1936 return; 1937 1938 BMessage* insertMessage = new BMessage(*msg); 1939 insertMessage->what = kSecondaryMouseDropAction; 1940 insertMessage->AddInt8("action", kInsert); 1941 1942 BMessage* cdMessage = new BMessage(*msg); 1943 cdMessage->what = kSecondaryMouseDropAction; 1944 cdMessage->AddInt8("action", kChangeDirectory); 1945 1946 BMessage* lnMessage = new BMessage(*msg); 1947 lnMessage->what = kSecondaryMouseDropAction; 1948 lnMessage->AddInt8("action", kLinkFiles); 1949 1950 BMessage* mvMessage = new BMessage(*msg); 1951 mvMessage->what = kSecondaryMouseDropAction; 1952 mvMessage->AddInt8("action", kMoveFiles); 1953 1954 BMessage* cpMessage = new BMessage(*msg); 1955 cpMessage->what = kSecondaryMouseDropAction; 1956 cpMessage->AddInt8("action", kCopyFiles); 1957 1958 BMenuItem* insertItem = new BMenuItem( 1959 B_TRANSLATE("Insert path"), insertMessage); 1960 BMenuItem* cdItem = new BMenuItem( 1961 B_TRANSLATE("Change directory"), cdMessage); 1962 BMenuItem* lnItem = new BMenuItem( 1963 B_TRANSLATE("Create link here"), lnMessage); 1964 BMenuItem* mvItem = new BMenuItem(B_TRANSLATE("Move here"), mvMessage); 1965 BMenuItem* cpItem = new BMenuItem(B_TRANSLATE("Copy here"), cpMessage); 1966 BMenuItem* chItem = new BMenuItem(B_TRANSLATE("Cancel"), NULL); 1967 1968 // if the refs point to different directorys disable the cd menu item 1969 bool differentDirs = false; 1970 BDirectory firstDir; 1971 entry_ref ref; 1972 int i = 0; 1973 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1974 BNode node(&ref); 1975 BEntry entry(&ref); 1976 BDirectory dir; 1977 if (node.IsDirectory()) 1978 dir.SetTo(&ref); 1979 else 1980 entry.GetParent(&dir); 1981 1982 if (i == 1) { 1983 node_ref nodeRef; 1984 dir.GetNodeRef(&nodeRef); 1985 firstDir.SetTo(&nodeRef); 1986 } else if (firstDir != dir) { 1987 differentDirs = true; 1988 break; 1989 } 1990 } 1991 if (differentDirs) 1992 cdItem->SetEnabled(false); 1993 1994 BPopUpMenu *menu = new BPopUpMenu( 1995 "Secondary mouse button drop menu"); 1996 menu->SetAsyncAutoDestruct(true); 1997 menu->AddItem(insertItem); 1998 menu->AddSeparatorItem(); 1999 menu->AddItem(cdItem); 2000 menu->AddItem(lnItem); 2001 menu->AddItem(mvItem); 2002 menu->AddItem(cpItem); 2003 menu->AddSeparatorItem(); 2004 menu->AddItem(chItem); 2005 menu->SetTargetForItems(this); 2006 menu->Go(point, true, true, true); 2007 } 2008 2009 2010 void 2011 TermView::_DoSecondaryMouseDropAction(BMessage* msg) 2012 { 2013 int8 action = -1; 2014 msg->FindInt8("action", &action); 2015 2016 BString outString = ""; 2017 BString itemString = ""; 2018 2019 switch (action) { 2020 case kInsert: 2021 break; 2022 case kChangeDirectory: 2023 outString = "cd "; 2024 break; 2025 case kLinkFiles: 2026 outString = "ln -s "; 2027 break; 2028 case kMoveFiles: 2029 outString = "mv "; 2030 break; 2031 case kCopyFiles: 2032 outString = "cp "; 2033 break; 2034 2035 default: 2036 return; 2037 } 2038 2039 bool listContainsDirectory = false; 2040 entry_ref ref; 2041 int32 i = 0; 2042 while (msg->FindRef("refs", i++, &ref) == B_OK) { 2043 BEntry ent(&ref); 2044 BNode node(&ref); 2045 BPath path(&ent); 2046 BString string(path.Path()); 2047 2048 if (node.IsDirectory()) 2049 listContainsDirectory = true; 2050 2051 if (i > 1) 2052 itemString += " "; 2053 2054 if (action == kChangeDirectory) { 2055 if (!node.IsDirectory()) { 2056 int32 slash = string.FindLast("/"); 2057 string.Truncate(slash); 2058 } 2059 string.CharacterEscape(kShellEscapeCharacters, '\\'); 2060 itemString += string; 2061 break; 2062 } 2063 string.CharacterEscape(kShellEscapeCharacters, '\\'); 2064 itemString += string; 2065 } 2066 2067 if (listContainsDirectory && action == kCopyFiles) 2068 outString += "-R "; 2069 2070 outString += itemString; 2071 2072 if (action == kLinkFiles || action == kMoveFiles || action == kCopyFiles) 2073 outString += " ."; 2074 2075 if (action != kInsert) 2076 outString += "\n"; 2077 2078 _WritePTY(outString.String(), outString.Length()); 2079 } 2080 2081 2082 //! Gets dropped file full path and display it at cursor position. 2083 void 2084 TermView::_DoFileDrop(entry_ref& ref) 2085 { 2086 BEntry ent(&ref); 2087 BPath path(&ent); 2088 BString string(path.Path()); 2089 2090 string.CharacterEscape(kShellEscapeCharacters, '\\'); 2091 _WritePTY(string.String(), string.Length()); 2092 } 2093 2094 2095 /*! Text buffer must already be locked. 2096 */ 2097 void 2098 TermView::_SynchronizeWithTextBuffer(int32 visibleDirtyTop, 2099 int32 visibleDirtyBottom) 2100 { 2101 TerminalBufferDirtyInfo& info = fTextBuffer->DirtyInfo(); 2102 int32 linesScrolled = info.linesScrolled; 2103 2104 //debug_printf("TermView::_SynchronizeWithTextBuffer(): dirty: %ld - %ld, " 2105 //"scrolled: %ld, visible dirty: %ld - %ld\n", info.dirtyTop, info.dirtyBottom, 2106 //info.linesScrolled, visibleDirtyTop, visibleDirtyBottom); 2107 2108 bigtime_t now = system_time(); 2109 bigtime_t timeElapsed = now - fLastSyncTime; 2110 if (timeElapsed > 2 * kSyncUpdateGranularity) { 2111 // last sync was ages ago 2112 fLastSyncTime = now; 2113 fScrolledSinceLastSync = linesScrolled; 2114 } 2115 2116 if (fSyncRunner == NULL) { 2117 // We consider clocked syncing when more than a full screen height has 2118 // been scrolled in less than a sync update period. Once we're 2119 // actively considering it, the same condition will convince us to 2120 // actually do it. 2121 if (fScrolledSinceLastSync + linesScrolled <= fRows) { 2122 // Condition doesn't hold yet. Reset if time is up, or otherwise 2123 // keep counting. 2124 if (timeElapsed > kSyncUpdateGranularity) { 2125 fConsiderClockedSync = false; 2126 fLastSyncTime = now; 2127 fScrolledSinceLastSync = linesScrolled; 2128 } else 2129 fScrolledSinceLastSync += linesScrolled; 2130 } else if (fConsiderClockedSync) { 2131 // We are convinced -- create the sync runner. 2132 fLastSyncTime = now; 2133 fScrolledSinceLastSync = 0; 2134 2135 BMessage message(MSG_TERMINAL_BUFFER_CHANGED); 2136 fSyncRunner = new(std::nothrow) BMessageRunner(BMessenger(this), 2137 &message, kSyncUpdateGranularity); 2138 if (fSyncRunner != NULL && fSyncRunner->InitCheck() == B_OK) 2139 return; 2140 2141 delete fSyncRunner; 2142 fSyncRunner = NULL; 2143 } else { 2144 // Looks interesting so far. Reset the counts and consider clocked 2145 // syncing. 2146 fConsiderClockedSync = true; 2147 fLastSyncTime = now; 2148 fScrolledSinceLastSync = 0; 2149 } 2150 } else if (timeElapsed < kSyncUpdateGranularity) { 2151 // sync time not passed yet -- keep counting 2152 fScrolledSinceLastSync += linesScrolled; 2153 return; 2154 } 2155 2156 if (fScrolledSinceLastSync + linesScrolled <= fRows) { 2157 // time's up, but not enough happened 2158 delete fSyncRunner; 2159 fSyncRunner = NULL; 2160 fLastSyncTime = now; 2161 fScrolledSinceLastSync = linesScrolled; 2162 } else { 2163 // Things are still rolling, but the sync time's up. 2164 fLastSyncTime = now; 2165 fScrolledSinceLastSync = 0; 2166 } 2167 2168 fVisibleTextBufferChanged = true; 2169 2170 // Simple case first -- complete invalidation. 2171 if (info.invalidateAll) { 2172 Invalidate(); 2173 _UpdateScrollBarRange(); 2174 _Deselect(); 2175 2176 fCursor = fTextBuffer->Cursor(); 2177 _ActivateCursor(false); 2178 2179 int32 offset = _LineAt(0); 2180 fVisibleTextBuffer->SynchronizeWith(fTextBuffer, offset, offset, 2181 offset + fTextBuffer->Height() + 2); 2182 2183 info.Reset(); 2184 return; 2185 } 2186 2187 BRect bounds = Bounds(); 2188 int32 firstVisible = _LineAt(0); 2189 int32 lastVisible = _LineAt(bounds.bottom); 2190 int32 historySize = fTextBuffer->HistorySize(); 2191 2192 bool doScroll = false; 2193 if (linesScrolled > 0) { 2194 _UpdateScrollBarRange(); 2195 2196 visibleDirtyTop -= linesScrolled; 2197 visibleDirtyBottom -= linesScrolled; 2198 2199 if (firstVisible < 0) { 2200 firstVisible -= linesScrolled; 2201 lastVisible -= linesScrolled; 2202 2203 float scrollOffset; 2204 if (firstVisible < -historySize) { 2205 firstVisible = -historySize; 2206 doScroll = true; 2207 scrollOffset = -historySize * fFontHeight; 2208 // We need to invalidate the lower linesScrolled lines of the 2209 // visible text buffer, since those will be scrolled up and 2210 // need to be replaced. We just use visibleDirty{Top,Bottom} 2211 // for that purpose. Unless invoked from ScrollTo() (i.e. 2212 // user-initiated scrolling) those are unused. In the unlikely 2213 // case that the user is scrolling at the same time we may 2214 // invalidate too many lines, since we have to extend the given 2215 // region. 2216 // Note that in the firstVisible == 0 case the new lines are 2217 // already in the dirty region, so they will be updated anyway. 2218 if (visibleDirtyTop <= visibleDirtyBottom) { 2219 if (lastVisible < visibleDirtyTop) 2220 visibleDirtyTop = lastVisible; 2221 if (visibleDirtyBottom < lastVisible + linesScrolled) 2222 visibleDirtyBottom = lastVisible + linesScrolled; 2223 } else { 2224 visibleDirtyTop = lastVisible + 1; 2225 visibleDirtyBottom = lastVisible + linesScrolled; 2226 } 2227 } else 2228 scrollOffset = fScrollOffset - linesScrolled * fFontHeight; 2229 2230 _ScrollTo(scrollOffset, false); 2231 } else 2232 doScroll = true; 2233 2234 if (doScroll && lastVisible >= firstVisible 2235 && !(info.IsDirtyRegionValid() && firstVisible >= info.dirtyTop 2236 && lastVisible <= info.dirtyBottom)) { 2237 // scroll manually 2238 float scrollBy = linesScrolled * fFontHeight; 2239 BRect destRect(Frame().OffsetToCopy(B_ORIGIN)); 2240 BRect sourceRect(destRect.OffsetByCopy(0, scrollBy)); 2241 2242 // invalidate the current cursor position before scrolling 2243 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 2244 2245 //debug_printf("CopyBits(((%f, %f) - (%f, %f)) -> (%f, %f) - (%f, %f))\n", 2246 //sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom, 2247 //destRect.left, destRect.top, destRect.right, destRect.bottom); 2248 CopyBits(sourceRect, destRect); 2249 2250 fVisibleTextBuffer->ScrollBy(linesScrolled); 2251 } 2252 2253 // move highlights 2254 for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { 2255 if (highlight->IsEmpty()) 2256 continue; 2257 2258 highlight->ScrollRange(linesScrolled); 2259 if (highlight == &fSelection) { 2260 fInitialSelectionStart.y -= linesScrolled; 2261 fInitialSelectionEnd.y -= linesScrolled; 2262 } 2263 2264 if (highlight->Start().y < -historySize) { 2265 if (highlight == &fSelection) 2266 _Deselect(); 2267 else 2268 _ClearHighlight(highlight); 2269 } 2270 } 2271 } 2272 2273 // invalidate dirty region 2274 if (info.IsDirtyRegionValid()) { 2275 _InvalidateTextRect(0, info.dirtyTop, fTextBuffer->Width() - 1, 2276 info.dirtyBottom); 2277 2278 // clear the selection, if affected 2279 if (!fSelection.IsEmpty()) { 2280 // TODO: We're clearing the selection more often than necessary -- 2281 // to avoid that, we'd also need to track the x coordinates of the 2282 // dirty range. 2283 int32 selectionBottom = fSelection.End().x > 0 2284 ? fSelection.End().y : fSelection.End().y - 1; 2285 if (fSelection.Start().y <= info.dirtyBottom 2286 && info.dirtyTop <= selectionBottom) { 2287 _Deselect(); 2288 } 2289 } 2290 } 2291 2292 if (visibleDirtyTop <= visibleDirtyBottom) 2293 info.ExtendDirtyRegion(visibleDirtyTop, visibleDirtyBottom); 2294 2295 if (linesScrolled != 0 || info.IsDirtyRegionValid()) { 2296 fVisibleTextBuffer->SynchronizeWith(fTextBuffer, firstVisible, 2297 info.dirtyTop, info.dirtyBottom); 2298 } 2299 2300 // invalidate cursor, if it changed 2301 TermPos cursor = fTextBuffer->Cursor(); 2302 if (fCursor != cursor || linesScrolled != 0) { 2303 // Before we scrolled we did already invalidate the old cursor. 2304 if (!doScroll) 2305 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 2306 fCursor = cursor; 2307 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 2308 _ActivateCursor(false); 2309 } 2310 2311 info.Reset(); 2312 } 2313 2314 2315 void 2316 TermView::_VisibleTextBufferChanged() 2317 { 2318 if (!fVisibleTextBufferChanged) 2319 return; 2320 2321 fVisibleTextBufferChanged = false; 2322 fActiveState->VisibleTextBufferChanged(); 2323 } 2324 2325 2326 /*! Write strings to PTY device. If encoding system isn't UTF8, change 2327 encoding to UTF8 before writing PTY. 2328 */ 2329 void 2330 TermView::_WritePTY(const char* text, int32 numBytes) 2331 { 2332 if (fEncoding != M_UTF8) { 2333 while (numBytes > 0) { 2334 char buffer[1024]; 2335 int32 bufferSize = sizeof(buffer); 2336 int32 sourceSize = numBytes; 2337 int32 state = 0; 2338 if (convert_to_utf8(fEncoding, text, &sourceSize, buffer, 2339 &bufferSize, &state) != B_OK || bufferSize == 0) { 2340 break; 2341 } 2342 2343 fShell->Write(buffer, bufferSize); 2344 text += sourceSize; 2345 numBytes -= sourceSize; 2346 } 2347 } else { 2348 fShell->Write(text, numBytes); 2349 } 2350 } 2351 2352 2353 //! Returns the square of the actual pixel distance between both points 2354 float 2355 TermView::_MouseDistanceSinceLastClick(BPoint where) 2356 { 2357 return (fLastClickPoint.x - where.x) * (fLastClickPoint.x - where.x) 2358 + (fLastClickPoint.y - where.y) * (fLastClickPoint.y - where.y); 2359 } 2360 2361 2362 void 2363 TermView::_SendMouseEvent(int32 buttons, int32 mode, int32 x, int32 y, 2364 bool motion) 2365 { 2366 char xtermButtons; 2367 if (buttons == B_PRIMARY_MOUSE_BUTTON) 2368 xtermButtons = 32 + 0; 2369 else if (buttons == B_SECONDARY_MOUSE_BUTTON) 2370 xtermButtons = 32 + 1; 2371 else if (buttons == B_TERTIARY_MOUSE_BUTTON) 2372 xtermButtons = 32 + 2; 2373 else 2374 xtermButtons = 32 + 3; 2375 2376 if (motion) 2377 xtermButtons += 32; 2378 2379 char xtermX = x + 1 + 32; 2380 char xtermY = y + 1 + 32; 2381 2382 char destBuffer[6]; 2383 destBuffer[0] = '\033'; 2384 destBuffer[1] = '['; 2385 destBuffer[2] = 'M'; 2386 destBuffer[3] = xtermButtons; 2387 destBuffer[4] = xtermX; 2388 destBuffer[5] = xtermY; 2389 fShell->Write(destBuffer, 6); 2390 } 2391 2392 2393 void 2394 TermView::MouseDown(BPoint where) 2395 { 2396 if (!IsFocus()) 2397 MakeFocus(); 2398 2399 _UpdateModifiers(); 2400 2401 BMessage* currentMessage = Window()->CurrentMessage(); 2402 int32 buttons = currentMessage->GetInt32("buttons", 0); 2403 2404 fActiveState->MouseDown(where, buttons, fModifiers); 2405 2406 fMouseButtons = buttons; 2407 fLastClickPoint = where; 2408 } 2409 2410 2411 void 2412 TermView::MouseMoved(BPoint where, uint32 transit, const BMessage *message) 2413 { 2414 _UpdateModifiers(); 2415 2416 fActiveState->MouseMoved(where, transit, message, fModifiers); 2417 } 2418 2419 2420 void 2421 TermView::MouseUp(BPoint where) 2422 { 2423 _UpdateModifiers(); 2424 2425 int32 buttons = Window()->CurrentMessage()->GetInt32("buttons", 0); 2426 2427 fActiveState->MouseUp(where, buttons); 2428 2429 fMouseButtons = buttons; 2430 } 2431 2432 2433 //! Select a range of text. 2434 void 2435 TermView::_Select(TermPos start, TermPos end, bool inclusive, 2436 bool setInitialSelection) 2437 { 2438 TextBufferSyncLocker _(this); 2439 2440 _SynchronizeWithTextBuffer(0, -1); 2441 2442 if (end < start) 2443 std::swap(start, end); 2444 2445 if (inclusive) 2446 end.x++; 2447 2448 //debug_printf("TermView::_Select(): (%ld, %ld) - (%ld, %ld)\n", start.x, 2449 //start.y, end.x, end.y); 2450 2451 if (start.x < 0) 2452 start.x = 0; 2453 if (end.x >= fColumns) 2454 end.x = fColumns; 2455 2456 TermPos minPos(0, -fTextBuffer->HistorySize()); 2457 TermPos maxPos(0, fTextBuffer->Height()); 2458 start = restrict_value(start, minPos, maxPos); 2459 end = restrict_value(end, minPos, maxPos); 2460 2461 // if the end is past the end of the line, select the line break, too 2462 if (fTextBuffer->LineLength(end.y) < end.x 2463 && end.y < fTextBuffer->Height()) { 2464 end.y++; 2465 end.x = 0; 2466 } 2467 2468 if (fTextBuffer->IsFullWidthChar(start.y, start.x)) { 2469 start.x--; 2470 if (start.x < 0) 2471 start.x = 0; 2472 } 2473 2474 if (fTextBuffer->IsFullWidthChar(end.y, end.x)) { 2475 end.x++; 2476 if (end.x >= fColumns) 2477 end.x = fColumns; 2478 } 2479 2480 if (!fSelection.IsEmpty()) 2481 _InvalidateTextRange(fSelection.Start(), fSelection.End()); 2482 2483 fSelection.SetRange(start, end); 2484 2485 if (setInitialSelection) { 2486 fInitialSelectionStart = fSelection.Start(); 2487 fInitialSelectionEnd = fSelection.End(); 2488 } 2489 2490 _InvalidateTextRange(fSelection.Start(), fSelection.End()); 2491 } 2492 2493 2494 //! Extend selection (shift + mouse click). 2495 void 2496 TermView::_ExtendSelection(TermPos pos, bool inclusive, 2497 bool useInitialSelection) 2498 { 2499 if (!useInitialSelection && !_HasSelection()) 2500 return; 2501 2502 TermPos start = fSelection.Start(); 2503 TermPos end = fSelection.End(); 2504 2505 if (useInitialSelection) { 2506 start = fInitialSelectionStart; 2507 end = fInitialSelectionEnd; 2508 } 2509 2510 if (inclusive) { 2511 if (pos >= start && pos >= end) 2512 pos.x++; 2513 } 2514 2515 if (pos < start) 2516 _Select(pos, end, false, !useInitialSelection); 2517 else if (pos > end) 2518 _Select(start, pos, false, !useInitialSelection); 2519 else if (useInitialSelection) 2520 _Select(start, end, false, false); 2521 } 2522 2523 2524 // clear the selection. 2525 void 2526 TermView::_Deselect() 2527 { 2528 //debug_printf("TermView::_Deselect(): has selection: %d\n", _HasSelection()); 2529 if (_ClearHighlight(&fSelection)) { 2530 fInitialSelectionStart.SetTo(0, 0); 2531 fInitialSelectionEnd.SetTo(0, 0); 2532 } 2533 } 2534 2535 2536 bool 2537 TermView::_HasSelection() const 2538 { 2539 return !fSelection.IsEmpty(); 2540 } 2541 2542 2543 void 2544 TermView::_SelectWord(BPoint where, bool extend, bool useInitialSelection) 2545 { 2546 BAutolock _(fTextBuffer); 2547 2548 TermPos pos = _ConvertToTerminal(where); 2549 TermPos start, end; 2550 if (!fTextBuffer->FindWord(pos, fCharClassifier, true, start, end)) 2551 return; 2552 2553 if (extend) { 2554 if (start 2555 < (useInitialSelection 2556 ? fInitialSelectionStart : fSelection.Start())) { 2557 _ExtendSelection(start, false, useInitialSelection); 2558 } else if (end 2559 > (useInitialSelection 2560 ? fInitialSelectionEnd : fSelection.End())) { 2561 _ExtendSelection(end, false, useInitialSelection); 2562 } else if (useInitialSelection) 2563 _Select(start, end, false, false); 2564 } else 2565 _Select(start, end, false, !useInitialSelection); 2566 } 2567 2568 2569 void 2570 TermView::_SelectLine(BPoint where, bool extend, bool useInitialSelection) 2571 { 2572 TermPos start = TermPos(0, _ConvertToTerminal(where).y); 2573 TermPos end = TermPos(0, start.y + 1); 2574 2575 if (extend) { 2576 if (start 2577 < (useInitialSelection 2578 ? fInitialSelectionStart : fSelection.Start())) { 2579 _ExtendSelection(start, false, useInitialSelection); 2580 } else if (end 2581 > (useInitialSelection 2582 ? fInitialSelectionEnd : fSelection.End())) { 2583 _ExtendSelection(end, false, useInitialSelection); 2584 } else if (useInitialSelection) 2585 _Select(start, end, false, false); 2586 } else 2587 _Select(start, end, false, !useInitialSelection); 2588 } 2589 2590 2591 void 2592 TermView::_AddHighlight(Highlight* highlight) 2593 { 2594 fHighlights.AddItem(highlight); 2595 2596 if (!highlight->IsEmpty()) 2597 _InvalidateTextRange(highlight->Start(), highlight->End()); 2598 } 2599 2600 2601 void 2602 TermView::_RemoveHighlight(Highlight* highlight) 2603 { 2604 if (!highlight->IsEmpty()) 2605 _InvalidateTextRange(highlight->Start(), highlight->End()); 2606 2607 fHighlights.RemoveItem(highlight); 2608 } 2609 2610 2611 bool 2612 TermView::_ClearHighlight(Highlight* highlight) 2613 { 2614 if (highlight->IsEmpty()) 2615 return false; 2616 2617 _InvalidateTextRange(highlight->Start(), highlight->End()); 2618 2619 highlight->SetRange(TermPos(0, 0), TermPos(0, 0)); 2620 return true; 2621 } 2622 2623 2624 TermView::Highlight* 2625 TermView::_CheckHighlightRegion(const TermPos &pos) const 2626 { 2627 for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { 2628 if (highlight->RangeContains(pos)) 2629 return highlight; 2630 } 2631 2632 return NULL; 2633 } 2634 2635 2636 TermView::Highlight* 2637 TermView::_CheckHighlightRegion(int32 row, int32 firstColumn, 2638 int32& lastColumn) const 2639 { 2640 Highlight* nextHighlight = NULL; 2641 2642 for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { 2643 if (highlight->IsEmpty()) 2644 continue; 2645 2646 if (row == highlight->Start().y && firstColumn < highlight->Start().x 2647 && lastColumn >= highlight->Start().x) { 2648 // region starts before the highlight, but intersects with it 2649 if (nextHighlight == NULL 2650 || highlight->Start().x < nextHighlight->Start().x) { 2651 nextHighlight = highlight; 2652 } 2653 continue; 2654 } 2655 2656 if (row == highlight->End().y && firstColumn < highlight->End().x 2657 && lastColumn >= highlight->End().x) { 2658 // region starts in the highlight, but exceeds the end 2659 lastColumn = highlight->End().x - 1; 2660 return highlight; 2661 } 2662 2663 TermPos pos(firstColumn, row); 2664 if (highlight->RangeContains(pos)) 2665 return highlight; 2666 } 2667 2668 if (nextHighlight != NULL) 2669 lastColumn = nextHighlight->Start().x - 1; 2670 return NULL; 2671 } 2672 2673 2674 void 2675 TermView::GetFrameSize(float *width, float *height) 2676 { 2677 int32 historySize; 2678 { 2679 BAutolock _(fTextBuffer); 2680 historySize = fTextBuffer->HistorySize(); 2681 } 2682 2683 if (width != NULL) 2684 *width = fColumns * fFontWidth; 2685 2686 if (height != NULL) 2687 *height = (fRows + historySize) * fFontHeight; 2688 } 2689 2690 2691 // Find a string, and select it if found 2692 bool 2693 TermView::Find(const BString &str, bool forwardSearch, bool matchCase, 2694 bool matchWord) 2695 { 2696 TextBufferSyncLocker _(this); 2697 _SynchronizeWithTextBuffer(0, -1); 2698 2699 TermPos start; 2700 if (_HasSelection()) { 2701 if (forwardSearch) 2702 start = fSelection.End(); 2703 else 2704 start = fSelection.Start(); 2705 } else { 2706 // search from the very beginning/end 2707 if (forwardSearch) 2708 start = TermPos(0, -fTextBuffer->HistorySize()); 2709 else 2710 start = TermPos(0, fTextBuffer->Height()); 2711 } 2712 2713 TermPos matchStart, matchEnd; 2714 if (!fTextBuffer->Find(str.String(), start, forwardSearch, matchCase, 2715 matchWord, matchStart, matchEnd)) { 2716 return false; 2717 } 2718 2719 _Select(matchStart, matchEnd, false, true); 2720 _ScrollToRange(fSelection.Start(), fSelection.End()); 2721 2722 return true; 2723 } 2724 2725 2726 //! Get the selected text and copy to str 2727 void 2728 TermView::GetSelection(BString &str) 2729 { 2730 str.SetTo(""); 2731 BAutolock _(fTextBuffer); 2732 fTextBuffer->GetStringFromRegion(str, fSelection.Start(), fSelection.End()); 2733 } 2734 2735 2736 bool 2737 TermView::CheckShellGone() const 2738 { 2739 if (!fShell) 2740 return false; 2741 2742 // check, if the shell does still live 2743 pid_t pid = fShell->ProcessID(); 2744 team_info info; 2745 return get_team_info(pid, &info) == B_BAD_TEAM_ID; 2746 } 2747 2748 2749 void 2750 TermView::InitiateDrag() 2751 { 2752 BAutolock _(fTextBuffer); 2753 2754 BString copyStr(""); 2755 fTextBuffer->GetStringFromRegion(copyStr, fSelection.Start(), 2756 fSelection.End()); 2757 2758 BMessage message(B_MIME_DATA); 2759 message.AddData("text/plain", B_MIME_TYPE, copyStr.String(), 2760 copyStr.Length()); 2761 2762 BPoint start = _ConvertFromTerminal(fSelection.Start()); 2763 BPoint end = _ConvertFromTerminal(fSelection.End()); 2764 2765 BRect rect; 2766 if (fSelection.Start().y == fSelection.End().y) 2767 rect.Set(start.x, start.y, end.x + fFontWidth, end.y + fFontHeight); 2768 else 2769 rect.Set(0, start.y, fColumns * fFontWidth, end.y + fFontHeight); 2770 2771 rect = rect & Bounds(); 2772 2773 DragMessage(&message, rect); 2774 } 2775 2776 2777 void 2778 TermView::_ScrollTo(float y, bool scrollGfx) 2779 { 2780 if (!scrollGfx) 2781 fScrollOffset = y; 2782 2783 if (fScrollBar != NULL) 2784 fScrollBar->SetValue(y); 2785 else 2786 ScrollTo(BPoint(0, y)); 2787 } 2788 2789 2790 void 2791 TermView::_ScrollToRange(TermPos start, TermPos end) 2792 { 2793 if (start > end) 2794 std::swap(start, end); 2795 2796 float startY = _LineOffset(start.y); 2797 float endY = _LineOffset(end.y) + fFontHeight - 1; 2798 float height = Bounds().Height(); 2799 2800 if (endY - startY > height) { 2801 // The range is greater than the height. Scroll to the closest border. 2802 2803 // already as good as it gets? 2804 if (startY <= 0 && endY >= height) 2805 return; 2806 2807 if (startY > 0) { 2808 // scroll down to align the start with the top of the view 2809 _ScrollTo(fScrollOffset + startY, true); 2810 } else { 2811 // scroll up to align the end with the bottom of the view 2812 _ScrollTo(fScrollOffset + endY - height, true); 2813 } 2814 } else { 2815 // The range is smaller than the height. 2816 2817 // already visible? 2818 if (startY >= 0 && endY <= height) 2819 return; 2820 2821 if (startY < 0) { 2822 // scroll up to make the start visible 2823 _ScrollTo(fScrollOffset + startY, true); 2824 } else { 2825 // scroll down to make the end visible 2826 _ScrollTo(fScrollOffset + endY - height, true); 2827 } 2828 } 2829 } 2830 2831 2832 void 2833 TermView::DisableResizeView(int32 disableCount) 2834 { 2835 fResizeViewDisableCount += disableCount; 2836 } 2837 2838 2839 void 2840 TermView::_DrawInlineMethodString() 2841 { 2842 if (!fInline || !fInline->String()) 2843 return; 2844 2845 const int32 numChars = BString(fInline->String()).CountChars(); 2846 2847 BPoint startPoint = _ConvertFromTerminal(fCursor); 2848 BPoint endPoint = startPoint; 2849 endPoint.x += fFontWidth * numChars; 2850 endPoint.y += fFontHeight + 1; 2851 2852 BRect eraseRect(startPoint, endPoint); 2853 2854 PushState(); 2855 SetHighColor(fTextForeColor); 2856 FillRect(eraseRect); 2857 PopState(); 2858 2859 BPoint loc = _ConvertFromTerminal(fCursor); 2860 loc.y += fFontHeight; 2861 SetFont(&fHalfFont); 2862 SetHighColor(fTextBackColor); 2863 SetLowColor(fTextForeColor); 2864 DrawString(fInline->String(), loc); 2865 } 2866 2867 2868 void 2869 TermView::_HandleInputMethodChanged(BMessage *message) 2870 { 2871 const char *string = NULL; 2872 if (message->FindString("be:string", &string) < B_OK || string == NULL) 2873 return; 2874 2875 _ActivateCursor(false); 2876 2877 if (IsFocus()) 2878 be_app->ObscureCursor(); 2879 2880 // If we find the "be:confirmed" boolean (and the boolean is true), 2881 // it means it's over for now, so the current InlineInput object 2882 // should become inactive. We will probably receive a 2883 // B_INPUT_METHOD_STOPPED message after this one. 2884 bool confirmed; 2885 if (message->FindBool("be:confirmed", &confirmed) != B_OK) 2886 confirmed = false; 2887 2888 fInline->SetString(""); 2889 2890 Invalidate(); 2891 // TODO: Debug only 2892 snooze(100000); 2893 2894 fInline->SetString(string); 2895 fInline->ResetClauses(); 2896 2897 if (!confirmed && !fInline->IsActive()) 2898 fInline->SetActive(true); 2899 2900 // Get the clauses, and pass them to the InlineInput object 2901 // TODO: Find out if what we did it's ok, currently we don't consider 2902 // clauses at all, while the bebook says we should; though the visual 2903 // effect we obtained seems correct. Weird. 2904 int32 clauseCount = 0; 2905 int32 clauseStart; 2906 int32 clauseEnd; 2907 while (message->FindInt32("be:clause_start", clauseCount, &clauseStart) 2908 == B_OK 2909 && message->FindInt32("be:clause_end", clauseCount, &clauseEnd) 2910 == B_OK) { 2911 if (!fInline->AddClause(clauseStart, clauseEnd)) 2912 break; 2913 clauseCount++; 2914 } 2915 2916 if (confirmed) { 2917 fInline->SetString(""); 2918 _ActivateCursor(true); 2919 2920 // now we need to feed ourselves the individual characters as if the 2921 // user would have pressed them now - this lets KeyDown() pick out all 2922 // the special characters like B_BACKSPACE, cursor keys and the like: 2923 const char* currPos = string; 2924 const char* prevPos = currPos; 2925 while (*currPos != '\0') { 2926 if ((*currPos & 0xC0) == 0xC0) { 2927 // found the start of an UTF-8 char, we collect while it lasts 2928 ++currPos; 2929 while ((*currPos & 0xC0) == 0x80) 2930 ++currPos; 2931 } else if ((*currPos & 0xC0) == 0x80) { 2932 // illegal: character starts with utf-8 intermediate byte, skip it 2933 prevPos = ++currPos; 2934 } else { 2935 // single byte character/code, just feed that 2936 ++currPos; 2937 } 2938 KeyDown(prevPos, currPos - prevPos); 2939 prevPos = currPos; 2940 } 2941 2942 Invalidate(); 2943 } else { 2944 // temporarily show transient state of inline input 2945 int32 selectionStart = 0; 2946 int32 selectionEnd = 0; 2947 message->FindInt32("be:selection", 0, &selectionStart); 2948 message->FindInt32("be:selection", 1, &selectionEnd); 2949 2950 fInline->SetSelectionOffset(selectionStart); 2951 fInline->SetSelectionLength(selectionEnd - selectionStart); 2952 Invalidate(); 2953 } 2954 } 2955 2956 2957 void 2958 TermView::_HandleInputMethodLocationRequest() 2959 { 2960 BMessage message(B_INPUT_METHOD_EVENT); 2961 message.AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST); 2962 2963 BString string(fInline->String()); 2964 2965 const int32 &limit = string.CountChars(); 2966 BPoint where = _ConvertFromTerminal(fCursor); 2967 where.y += fFontHeight; 2968 2969 for (int32 i = 0; i < limit; i++) { 2970 // Add the location of the UTF8 characters 2971 2972 where.x += fFontWidth; 2973 ConvertToScreen(&where); 2974 2975 message.AddPoint("be:location_reply", where); 2976 message.AddFloat("be:height_reply", fFontHeight); 2977 } 2978 2979 fInline->Method()->SendMessage(&message); 2980 } 2981 2982 2983 void 2984 TermView::_CancelInputMethod() 2985 { 2986 if (!fInline) 2987 return; 2988 2989 InlineInput *inlineInput = fInline; 2990 fInline = NULL; 2991 2992 if (inlineInput->IsActive() && Window()) { 2993 Invalidate(); 2994 2995 BMessage message(B_INPUT_METHOD_EVENT); 2996 message.AddInt32("be:opcode", B_INPUT_METHOD_STOPPED); 2997 inlineInput->Method()->SendMessage(&message); 2998 } 2999 3000 delete inlineInput; 3001 } 3002 3003 3004 void 3005 TermView::_UpdateModifiers() 3006 { 3007 // TODO: This method is a general work-around for missing or out-of-order 3008 // B_MODIFIERS_CHANGED messages. This should really be fixed where it is 3009 // broken (app server?). 3010 int32 oldModifiers = fModifiers; 3011 fModifiers = modifiers(); 3012 if (fModifiers != oldModifiers && fActiveState != NULL) 3013 fActiveState->ModifiersChanged(oldModifiers, fModifiers); 3014 } 3015 3016 3017 void 3018 TermView::_NextState(State* state) 3019 { 3020 if (state != fActiveState) { 3021 if (fActiveState != NULL) 3022 fActiveState->Exited(); 3023 fActiveState = state; 3024 fActiveState->Entered(); 3025 } 3026 } 3027 3028 3029 // #pragma mark - Listener 3030 3031 3032 TermView::Listener::~Listener() 3033 { 3034 } 3035 3036 3037 void 3038 TermView::Listener::NotifyTermViewQuit(TermView* view, int32 reason) 3039 { 3040 } 3041 3042 3043 void 3044 TermView::Listener::SetTermViewTitle(TermView* view, const char* title) 3045 { 3046 } 3047 3048 3049 void 3050 TermView::Listener::PreviousTermView(TermView* view) 3051 { 3052 } 3053 3054 3055 void 3056 TermView::Listener::NextTermView(TermView* view) 3057 { 3058 } 3059 3060 3061 // #pragma mark - 3062 3063 3064 #ifdef USE_DEBUG_SNAPSHOTS 3065 3066 void 3067 TermView::MakeDebugSnapshots() 3068 { 3069 BAutolock _(fTextBuffer); 3070 time_t timeStamp = time(NULL); 3071 fTextBuffer->MakeLinesSnapshots(timeStamp, ".TextBuffer.dump"); 3072 fVisibleTextBuffer->MakeLinesSnapshots(timeStamp, ".VisualTextBuffer.dump"); 3073 } 3074 3075 3076 void 3077 TermView::StartStopDebugCapture() 3078 { 3079 BAutolock _(fTextBuffer); 3080 fTextBuffer->StartStopDebugCapture(); 3081 } 3082 3083 #endif 3084