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 fAllowBold = 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 fAllowBold = PrefHandler::Default() == NULL ? false 783 : PrefHandler::Default()->getBool(PREF_ALLOW_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 && fAllowBold 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 (fEmulateBold) { 1009 inView->MovePenTo(x1 - 1, y1 + fFontAscent - 1); 1010 inView->DrawString((char *)buf); 1011 inView->SetDrawingMode(B_OP_BLEND); 1012 } else { 1013 rgb_color bright = rgb_fore; 1014 1015 bright.red = saturated_add<uint8>(bright.red, 64); 1016 bright.green = saturated_add<uint8>(bright.green, 64); 1017 bright.blue = saturated_add<uint8>(bright.blue, 64); 1018 1019 inView->SetHighColor(bright); 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), "size", ""); 1455 fResizeView->SetAlignment(B_ALIGN_CENTER); 1456 fResizeView->SetFont(be_bold_font); 1457 fResizeView->SetViewColor(fTextBackColor); 1458 fResizeView->SetLowColor(fTextBackColor); 1459 fResizeView->SetHighColor(fTextForeColor); 1460 1461 BMessage message(MSG_REMOVE_RESIZE_VIEW_IF_NEEDED); 1462 fResizeRunner = new(std::nothrow) BMessageRunner(BMessenger(this), 1463 &message, 25000LL); 1464 } 1465 1466 BString text; 1467 text << columns << " x " << rows; 1468 fResizeView->SetText(text.String()); 1469 fResizeView->GetPreferredSize(&width, &height); 1470 fResizeView->ResizeTo(width * 1.5, height * 1.5); 1471 fResizeView->MoveTo((Bounds().Width() - fResizeView->Bounds().Width()) / 2, 1472 (Bounds().Height()- fResizeView->Bounds().Height()) / 2); 1473 if (!hasResizeView && fResizeViewDisableCount < 1) 1474 AddChild(fResizeView); 1475 1476 if (fResizeViewDisableCount > 0) 1477 fResizeViewDisableCount--; 1478 1479 SetTermSize(rows, columns, true); 1480 } 1481 1482 1483 void 1484 TermView::MessageReceived(BMessage *msg) 1485 { 1486 if (fActiveState->MessageReceived(msg)) 1487 return; 1488 1489 entry_ref ref; 1490 const char *ctrl_l = "\x0c"; 1491 1492 // first check for any dropped message 1493 if (msg->WasDropped() && (msg->what == B_SIMPLE_DATA 1494 || msg->what == B_MIME_DATA)) { 1495 char *text; 1496 ssize_t numBytes; 1497 //rgb_color *color; 1498 1499 int32 i = 0; 1500 1501 if (msg->FindRef("refs", i++, &ref) == B_OK) { 1502 // first check if secondary mouse button is pressed 1503 int32 buttons = 0; 1504 msg->FindInt32("buttons", &buttons); 1505 1506 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 1507 // start popup menu 1508 _SecondaryMouseButtonDropped(msg); 1509 return; 1510 } 1511 1512 _DoFileDrop(ref); 1513 1514 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1515 _WritePTY(" ", 1); 1516 _DoFileDrop(ref); 1517 } 1518 return; 1519 #if 0 1520 } else if (msg->FindData("RGBColor", B_RGB_COLOR_TYPE, 1521 (const void **)&color, &numBytes) == B_OK 1522 && numBytes == sizeof(color)) { 1523 // TODO: handle color drop 1524 // maybe only on replicants ? 1525 return; 1526 #endif 1527 } else if (msg->FindData("text/plain", B_MIME_TYPE, 1528 (const void **)&text, &numBytes) == B_OK) { 1529 _WritePTY(text, numBytes); 1530 return; 1531 } 1532 } 1533 1534 switch (msg->what) { 1535 case B_SIMPLE_DATA: 1536 case B_REFS_RECEIVED: 1537 { 1538 // handle refs if they weren't dropped 1539 int32 i = 0; 1540 if (msg->FindRef("refs", i++, &ref) == B_OK) { 1541 _DoFileDrop(ref); 1542 1543 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1544 _WritePTY(" ", 1); 1545 _DoFileDrop(ref); 1546 } 1547 } else 1548 BView::MessageReceived(msg); 1549 break; 1550 } 1551 1552 case B_COPY: 1553 Copy(be_clipboard); 1554 break; 1555 1556 case B_PASTE: 1557 { 1558 int32 code; 1559 if (msg->FindInt32("index", &code) == B_OK) 1560 Paste(be_clipboard); 1561 break; 1562 } 1563 1564 case B_CLIPBOARD_CHANGED: 1565 // This message originates from the system clipboard. Overwrite 1566 // the contents of the mouse clipboard with the ones from the 1567 // system clipboard, in case it contains text data. 1568 if (be_clipboard->Lock()) { 1569 if (fMouseClipboard->Lock()) { 1570 BMessage* clipMsgA = be_clipboard->Data(); 1571 const char* text; 1572 ssize_t numBytes; 1573 if (clipMsgA->FindData("text/plain", B_MIME_TYPE, 1574 (const void**)&text, &numBytes) == B_OK ) { 1575 fMouseClipboard->Clear(); 1576 BMessage* clipMsgB = fMouseClipboard->Data(); 1577 clipMsgB->AddData("text/plain", B_MIME_TYPE, 1578 text, numBytes); 1579 fMouseClipboard->Commit(); 1580 } 1581 fMouseClipboard->Unlock(); 1582 } 1583 be_clipboard->Unlock(); 1584 } 1585 break; 1586 1587 case B_SELECT_ALL: 1588 SelectAll(); 1589 break; 1590 1591 case B_SET_PROPERTY: 1592 { 1593 int32 i; 1594 int32 encodingID; 1595 BMessage specifier; 1596 if (msg->GetCurrentSpecifier(&i, &specifier) == B_OK 1597 && strcmp("encoding", 1598 specifier.FindString("property", i)) == 0) { 1599 msg->FindInt32 ("data", &encodingID); 1600 SetEncoding(encodingID); 1601 msg->SendReply(B_REPLY); 1602 } else { 1603 BView::MessageReceived(msg); 1604 } 1605 break; 1606 } 1607 1608 case B_GET_PROPERTY: 1609 { 1610 int32 i; 1611 BMessage specifier; 1612 if (msg->GetCurrentSpecifier(&i, &specifier) == B_OK 1613 && strcmp("encoding", 1614 specifier.FindString("property", i)) == 0) { 1615 BMessage reply(B_REPLY); 1616 reply.AddInt32("result", Encoding()); 1617 msg->SendReply(&reply); 1618 } else if (strcmp("tty", 1619 specifier.FindString("property", i)) == 0) { 1620 BMessage reply(B_REPLY); 1621 reply.AddString("result", TerminalName()); 1622 msg->SendReply(&reply); 1623 } else { 1624 BView::MessageReceived(msg); 1625 } 1626 break; 1627 } 1628 1629 case B_MODIFIERS_CHANGED: 1630 { 1631 _UpdateModifiers(); 1632 break; 1633 } 1634 1635 case B_INPUT_METHOD_EVENT: 1636 { 1637 int32 opcode; 1638 if (msg->FindInt32("be:opcode", &opcode) == B_OK) { 1639 switch (opcode) { 1640 case B_INPUT_METHOD_STARTED: 1641 { 1642 BMessenger messenger; 1643 if (msg->FindMessenger("be:reply_to", 1644 &messenger) == B_OK) { 1645 fInline = new (std::nothrow) 1646 InlineInput(messenger); 1647 } 1648 break; 1649 } 1650 1651 case B_INPUT_METHOD_STOPPED: 1652 delete fInline; 1653 fInline = NULL; 1654 break; 1655 1656 case B_INPUT_METHOD_CHANGED: 1657 if (fInline != NULL) 1658 _HandleInputMethodChanged(msg); 1659 break; 1660 1661 case B_INPUT_METHOD_LOCATION_REQUEST: 1662 if (fInline != NULL) 1663 _HandleInputMethodLocationRequest(); 1664 break; 1665 1666 default: 1667 break; 1668 } 1669 } 1670 break; 1671 } 1672 1673 case B_MOUSE_WHEEL_CHANGED: 1674 { 1675 // overridden to allow scrolling emulation in alternative screen 1676 // mode 1677 BAutolock locker(fTextBuffer); 1678 float deltaY = 0; 1679 if (fTextBuffer->IsAlternateScreenActive() 1680 && msg->FindFloat("be:wheel_delta_y", &deltaY) == B_OK 1681 && deltaY != 0) { 1682 // We are in alternative screen mode and have a vertical delta 1683 // we can work with -- emulate scrolling via terminal escape 1684 // sequences. 1685 locker.Unlock(); 1686 1687 // scroll pagewise, if one of Option, Command, or Control is 1688 // pressed 1689 int32 steps; 1690 const char* stepString; 1691 if ((modifiers() & B_SHIFT_KEY) != 0) { 1692 // pagewise 1693 stepString = deltaY > 0 1694 ? PAGE_DOWN_KEY_CODE : PAGE_UP_KEY_CODE; 1695 steps = abs((int)deltaY); 1696 } else { 1697 // three lines per step 1698 stepString = deltaY > 0 1699 ? DOWN_ARROW_KEY_CODE : UP_ARROW_KEY_CODE; 1700 steps = 3 * abs((int)deltaY); 1701 } 1702 1703 // We want to do only a single write(), so compose a string 1704 // repeating the sequence as often as required by the delta. 1705 BString toWrite; 1706 for (int32 i = 0; i <steps; i++) 1707 toWrite << stepString; 1708 1709 _WritePTY(toWrite.String(), toWrite.Length()); 1710 } else { 1711 // let the BView's implementation handle the standard scrolling 1712 locker.Unlock(); 1713 BView::MessageReceived(msg); 1714 } 1715 1716 break; 1717 } 1718 1719 case MENU_CLEAR_ALL: 1720 Clear(); 1721 fShell->Write(ctrl_l, 1); 1722 break; 1723 case kBlinkCursor: 1724 _BlinkCursor(); 1725 break; 1726 case kUpdateSigWinch: 1727 _UpdateSIGWINCH(); 1728 break; 1729 case kSecondaryMouseDropAction: 1730 _DoSecondaryMouseDropAction(msg); 1731 break; 1732 case MSG_TERMINAL_BUFFER_CHANGED: 1733 { 1734 TextBufferSyncLocker _(this); 1735 _SynchronizeWithTextBuffer(0, -1); 1736 break; 1737 } 1738 case MSG_SET_TERMINAL_TITLE: 1739 { 1740 const char* title; 1741 if (msg->FindString("title", &title) == B_OK) { 1742 if (fListener != NULL) 1743 fListener->SetTermViewTitle(this, title); 1744 } 1745 break; 1746 } 1747 case MSG_SET_TERMINAL_COLORS: 1748 { 1749 int32 count = 0; 1750 if (msg->FindInt32("count", &count) != B_OK) 1751 break; 1752 bool dynamic = false; 1753 if (msg->FindBool("dynamic", &dynamic) != B_OK) 1754 break; 1755 for (int i = 0; i < count; i++) { 1756 uint8 index = 0; 1757 if (msg->FindUInt8("index", i, &index) != B_OK) 1758 break; 1759 1760 ssize_t bytes = 0; 1761 rgb_color* color = 0; 1762 if (msg->FindData("color", B_RGB_COLOR_TYPE, 1763 i, (const void**)&color, &bytes) != B_OK) 1764 break; 1765 SetTermColor(index, *color, dynamic); 1766 } 1767 break; 1768 } 1769 case MSG_RESET_TERMINAL_COLORS: 1770 { 1771 int32 count = 0; 1772 if (msg->FindInt32("count", &count) != B_OK) 1773 break; 1774 bool dynamic = false; 1775 if (msg->FindBool("dynamic", &dynamic) != B_OK) 1776 break; 1777 for (int i = 0; i < count; i++) { 1778 uint8 index = 0; 1779 if (msg->FindUInt8("index", i, &index) != B_OK) 1780 break; 1781 1782 if (index < kTermColorCount) 1783 SetTermColor(index, 1784 TermApp::DefaultPalette()[index], dynamic); 1785 } 1786 break; 1787 } 1788 case MSG_SET_CURSOR_STYLE: 1789 { 1790 int32 style = BLOCK_CURSOR; 1791 if (msg->FindInt32("style", &style) == B_OK) 1792 fCursorStyle = style; 1793 1794 bool blinking = fCursorBlinking; 1795 if (msg->FindBool("blinking", &blinking) == B_OK) { 1796 fCursorBlinking = blinking; 1797 _SwitchCursorBlinking(fCursorBlinking); 1798 } 1799 1800 bool hidden = fCursorHidden; 1801 if (msg->FindBool("hidden", &hidden) == B_OK) 1802 fCursorHidden = hidden; 1803 break; 1804 } 1805 case MSG_REPORT_MOUSE_EVENT: 1806 { 1807 bool report; 1808 if (msg->FindBool("reportX10MouseEvent", &report) == B_OK) 1809 fReportX10MouseEvent = report; 1810 1811 if (msg->FindBool("reportNormalMouseEvent", &report) == B_OK) 1812 fReportNormalMouseEvent = report; 1813 1814 if (msg->FindBool("reportButtonMouseEvent", &report) == B_OK) 1815 fReportButtonMouseEvent = report; 1816 1817 if (msg->FindBool("reportAnyMouseEvent", &report) == B_OK) 1818 fReportAnyMouseEvent = report; 1819 break; 1820 } 1821 case MSG_REMOVE_RESIZE_VIEW_IF_NEEDED: 1822 { 1823 BPoint point; 1824 uint32 buttons; 1825 GetMouse(&point, &buttons, false); 1826 if (buttons != 0) 1827 break; 1828 1829 if (fResizeView != NULL) { 1830 fResizeView->RemoveSelf(); 1831 delete fResizeView; 1832 fResizeView = NULL; 1833 } 1834 delete fResizeRunner; 1835 fResizeRunner = NULL; 1836 break; 1837 } 1838 1839 case MSG_QUIT_TERMNAL: 1840 { 1841 int32 reason; 1842 if (msg->FindInt32("reason", &reason) != B_OK) 1843 reason = 0; 1844 if (fListener != NULL) 1845 fListener->NotifyTermViewQuit(this, reason); 1846 break; 1847 } 1848 default: 1849 BView::MessageReceived(msg); 1850 break; 1851 } 1852 } 1853 1854 1855 status_t 1856 TermView::GetSupportedSuites(BMessage *message) 1857 { 1858 BPropertyInfo propInfo(sPropList); 1859 message->AddString("suites", "suite/vnd.naan-termview"); 1860 message->AddFlat("messages", &propInfo); 1861 return BView::GetSupportedSuites(message); 1862 } 1863 1864 1865 void 1866 TermView::ScrollTo(BPoint where) 1867 { 1868 //debug_printf("TermView::ScrollTo(): %f -> %f\n", fScrollOffset, where.y); 1869 float diff = where.y - fScrollOffset; 1870 if (diff == 0) 1871 return; 1872 1873 float bottom = Bounds().bottom; 1874 int32 oldFirstLine = _LineAt(0); 1875 int32 oldLastLine = _LineAt(bottom); 1876 int32 newFirstLine = _LineAt(diff); 1877 int32 newLastLine = _LineAt(bottom + diff); 1878 1879 fScrollOffset = where.y; 1880 1881 // invalidate the current cursor position before scrolling 1882 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 1883 1884 // scroll contents 1885 BRect destRect(Frame().OffsetToCopy(Bounds().LeftTop())); 1886 BRect sourceRect(destRect.OffsetByCopy(0, diff)); 1887 //debug_printf("CopyBits(((%f, %f) - (%f, %f)) -> (%f, %f) - (%f, %f))\n", 1888 //sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom, 1889 //destRect.left, destRect.top, destRect.right, destRect.bottom); 1890 CopyBits(sourceRect, destRect); 1891 1892 // sync visible text buffer with text buffer 1893 if (newFirstLine != oldFirstLine || newLastLine != oldLastLine) { 1894 if (newFirstLine != oldFirstLine) 1895 { 1896 //debug_printf("fVisibleTextBuffer->ScrollBy(%ld)\n", newFirstLine - oldFirstLine); 1897 fVisibleTextBuffer->ScrollBy(newFirstLine - oldFirstLine); 1898 } 1899 TextBufferSyncLocker _(this); 1900 if (diff < 0) 1901 _SynchronizeWithTextBuffer(newFirstLine, oldFirstLine - 1); 1902 else 1903 _SynchronizeWithTextBuffer(oldLastLine + 1, newLastLine); 1904 } 1905 } 1906 1907 1908 void 1909 TermView::TargetedByScrollView(BScrollView *scrollView) 1910 { 1911 BView::TargetedByScrollView(scrollView); 1912 1913 SetScrollBar(scrollView ? scrollView->ScrollBar(B_VERTICAL) : NULL); 1914 } 1915 1916 1917 BHandler* 1918 TermView::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 1919 int32 what, const char* property) 1920 { 1921 BHandler* target = this; 1922 BPropertyInfo propInfo(sPropList); 1923 if (propInfo.FindMatch(message, index, specifier, what, property) < B_OK) { 1924 target = BView::ResolveSpecifier(message, index, specifier, what, 1925 property); 1926 } 1927 1928 return target; 1929 } 1930 1931 1932 void 1933 TermView::_SecondaryMouseButtonDropped(BMessage* msg) 1934 { 1935 // Launch menu to choose what is to do with the msg data 1936 BPoint point; 1937 if (msg->FindPoint("_drop_point_", &point) != B_OK) 1938 return; 1939 1940 BMessage* insertMessage = new BMessage(*msg); 1941 insertMessage->what = kSecondaryMouseDropAction; 1942 insertMessage->AddInt8("action", kInsert); 1943 1944 BMessage* cdMessage = new BMessage(*msg); 1945 cdMessage->what = kSecondaryMouseDropAction; 1946 cdMessage->AddInt8("action", kChangeDirectory); 1947 1948 BMessage* lnMessage = new BMessage(*msg); 1949 lnMessage->what = kSecondaryMouseDropAction; 1950 lnMessage->AddInt8("action", kLinkFiles); 1951 1952 BMessage* mvMessage = new BMessage(*msg); 1953 mvMessage->what = kSecondaryMouseDropAction; 1954 mvMessage->AddInt8("action", kMoveFiles); 1955 1956 BMessage* cpMessage = new BMessage(*msg); 1957 cpMessage->what = kSecondaryMouseDropAction; 1958 cpMessage->AddInt8("action", kCopyFiles); 1959 1960 BMenuItem* insertItem = new BMenuItem( 1961 B_TRANSLATE("Insert path"), insertMessage); 1962 BMenuItem* cdItem = new BMenuItem( 1963 B_TRANSLATE("Change directory"), cdMessage); 1964 BMenuItem* lnItem = new BMenuItem( 1965 B_TRANSLATE("Create link here"), lnMessage); 1966 BMenuItem* mvItem = new BMenuItem(B_TRANSLATE("Move here"), mvMessage); 1967 BMenuItem* cpItem = new BMenuItem(B_TRANSLATE("Copy here"), cpMessage); 1968 BMenuItem* chItem = new BMenuItem(B_TRANSLATE("Cancel"), NULL); 1969 1970 // if the refs point to different directorys disable the cd menu item 1971 bool differentDirs = false; 1972 BDirectory firstDir; 1973 entry_ref ref; 1974 int i = 0; 1975 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1976 BNode node(&ref); 1977 BEntry entry(&ref); 1978 BDirectory dir; 1979 if (node.IsDirectory()) 1980 dir.SetTo(&ref); 1981 else 1982 entry.GetParent(&dir); 1983 1984 if (i == 1) { 1985 node_ref nodeRef; 1986 dir.GetNodeRef(&nodeRef); 1987 firstDir.SetTo(&nodeRef); 1988 } else if (firstDir != dir) { 1989 differentDirs = true; 1990 break; 1991 } 1992 } 1993 if (differentDirs) 1994 cdItem->SetEnabled(false); 1995 1996 BPopUpMenu *menu = new BPopUpMenu( 1997 "Secondary mouse button drop menu"); 1998 menu->SetAsyncAutoDestruct(true); 1999 menu->AddItem(insertItem); 2000 menu->AddSeparatorItem(); 2001 menu->AddItem(cdItem); 2002 menu->AddItem(lnItem); 2003 menu->AddItem(mvItem); 2004 menu->AddItem(cpItem); 2005 menu->AddSeparatorItem(); 2006 menu->AddItem(chItem); 2007 menu->SetTargetForItems(this); 2008 menu->Go(point, true, true, true); 2009 } 2010 2011 2012 void 2013 TermView::_DoSecondaryMouseDropAction(BMessage* msg) 2014 { 2015 int8 action = -1; 2016 msg->FindInt8("action", &action); 2017 2018 BString outString = ""; 2019 BString itemString = ""; 2020 2021 switch (action) { 2022 case kInsert: 2023 break; 2024 case kChangeDirectory: 2025 outString = "cd "; 2026 break; 2027 case kLinkFiles: 2028 outString = "ln -s "; 2029 break; 2030 case kMoveFiles: 2031 outString = "mv "; 2032 break; 2033 case kCopyFiles: 2034 outString = "cp "; 2035 break; 2036 2037 default: 2038 return; 2039 } 2040 2041 bool listContainsDirectory = false; 2042 entry_ref ref; 2043 int32 i = 0; 2044 while (msg->FindRef("refs", i++, &ref) == B_OK) { 2045 BEntry ent(&ref); 2046 BNode node(&ref); 2047 BPath path(&ent); 2048 BString string(path.Path()); 2049 2050 if (node.IsDirectory()) 2051 listContainsDirectory = true; 2052 2053 if (i > 1) 2054 itemString += " "; 2055 2056 if (action == kChangeDirectory) { 2057 if (!node.IsDirectory()) { 2058 int32 slash = string.FindLast("/"); 2059 string.Truncate(slash); 2060 } 2061 string.CharacterEscape(kShellEscapeCharacters, '\\'); 2062 itemString += string; 2063 break; 2064 } 2065 string.CharacterEscape(kShellEscapeCharacters, '\\'); 2066 itemString += string; 2067 } 2068 2069 if (listContainsDirectory && action == kCopyFiles) 2070 outString += "-R "; 2071 2072 outString += itemString; 2073 2074 if (action == kLinkFiles || action == kMoveFiles || action == kCopyFiles) 2075 outString += " ."; 2076 2077 if (action != kInsert) 2078 outString += "\n"; 2079 2080 _WritePTY(outString.String(), outString.Length()); 2081 } 2082 2083 2084 //! Gets dropped file full path and display it at cursor position. 2085 void 2086 TermView::_DoFileDrop(entry_ref& ref) 2087 { 2088 BEntry ent(&ref); 2089 BPath path(&ent); 2090 BString string(path.Path()); 2091 2092 string.CharacterEscape(kShellEscapeCharacters, '\\'); 2093 _WritePTY(string.String(), string.Length()); 2094 } 2095 2096 2097 /*! Text buffer must already be locked. 2098 */ 2099 void 2100 TermView::_SynchronizeWithTextBuffer(int32 visibleDirtyTop, 2101 int32 visibleDirtyBottom) 2102 { 2103 TerminalBufferDirtyInfo& info = fTextBuffer->DirtyInfo(); 2104 int32 linesScrolled = info.linesScrolled; 2105 2106 //debug_printf("TermView::_SynchronizeWithTextBuffer(): dirty: %ld - %ld, " 2107 //"scrolled: %ld, visible dirty: %ld - %ld\n", info.dirtyTop, info.dirtyBottom, 2108 //info.linesScrolled, visibleDirtyTop, visibleDirtyBottom); 2109 2110 bigtime_t now = system_time(); 2111 bigtime_t timeElapsed = now - fLastSyncTime; 2112 if (timeElapsed > 2 * kSyncUpdateGranularity) { 2113 // last sync was ages ago 2114 fLastSyncTime = now; 2115 fScrolledSinceLastSync = linesScrolled; 2116 } 2117 2118 if (fSyncRunner == NULL) { 2119 // We consider clocked syncing when more than a full screen height has 2120 // been scrolled in less than a sync update period. Once we're 2121 // actively considering it, the same condition will convince us to 2122 // actually do it. 2123 if (fScrolledSinceLastSync + linesScrolled <= fRows) { 2124 // Condition doesn't hold yet. Reset if time is up, or otherwise 2125 // keep counting. 2126 if (timeElapsed > kSyncUpdateGranularity) { 2127 fConsiderClockedSync = false; 2128 fLastSyncTime = now; 2129 fScrolledSinceLastSync = linesScrolled; 2130 } else 2131 fScrolledSinceLastSync += linesScrolled; 2132 } else if (fConsiderClockedSync) { 2133 // We are convinced -- create the sync runner. 2134 fLastSyncTime = now; 2135 fScrolledSinceLastSync = 0; 2136 2137 BMessage message(MSG_TERMINAL_BUFFER_CHANGED); 2138 fSyncRunner = new(std::nothrow) BMessageRunner(BMessenger(this), 2139 &message, kSyncUpdateGranularity); 2140 if (fSyncRunner != NULL && fSyncRunner->InitCheck() == B_OK) 2141 return; 2142 2143 delete fSyncRunner; 2144 fSyncRunner = NULL; 2145 } else { 2146 // Looks interesting so far. Reset the counts and consider clocked 2147 // syncing. 2148 fConsiderClockedSync = true; 2149 fLastSyncTime = now; 2150 fScrolledSinceLastSync = 0; 2151 } 2152 } else if (timeElapsed < kSyncUpdateGranularity) { 2153 // sync time not passed yet -- keep counting 2154 fScrolledSinceLastSync += linesScrolled; 2155 return; 2156 } 2157 2158 if (fScrolledSinceLastSync + linesScrolled <= fRows) { 2159 // time's up, but not enough happened 2160 delete fSyncRunner; 2161 fSyncRunner = NULL; 2162 fLastSyncTime = now; 2163 fScrolledSinceLastSync = linesScrolled; 2164 } else { 2165 // Things are still rolling, but the sync time's up. 2166 fLastSyncTime = now; 2167 fScrolledSinceLastSync = 0; 2168 } 2169 2170 fVisibleTextBufferChanged = true; 2171 2172 // Simple case first -- complete invalidation. 2173 if (info.invalidateAll) { 2174 Invalidate(); 2175 _UpdateScrollBarRange(); 2176 _Deselect(); 2177 2178 fCursor = fTextBuffer->Cursor(); 2179 _ActivateCursor(false); 2180 2181 int32 offset = _LineAt(0); 2182 fVisibleTextBuffer->SynchronizeWith(fTextBuffer, offset, offset, 2183 offset + fTextBuffer->Height() + 2); 2184 2185 info.Reset(); 2186 return; 2187 } 2188 2189 BRect bounds = Bounds(); 2190 int32 firstVisible = _LineAt(0); 2191 int32 lastVisible = _LineAt(bounds.bottom); 2192 int32 historySize = fTextBuffer->HistorySize(); 2193 2194 bool doScroll = false; 2195 if (linesScrolled > 0) { 2196 _UpdateScrollBarRange(); 2197 2198 visibleDirtyTop -= linesScrolled; 2199 visibleDirtyBottom -= linesScrolled; 2200 2201 if (firstVisible < 0) { 2202 firstVisible -= linesScrolled; 2203 lastVisible -= linesScrolled; 2204 2205 float scrollOffset; 2206 if (firstVisible < -historySize) { 2207 firstVisible = -historySize; 2208 doScroll = true; 2209 scrollOffset = -historySize * fFontHeight; 2210 // We need to invalidate the lower linesScrolled lines of the 2211 // visible text buffer, since those will be scrolled up and 2212 // need to be replaced. We just use visibleDirty{Top,Bottom} 2213 // for that purpose. Unless invoked from ScrollTo() (i.e. 2214 // user-initiated scrolling) those are unused. In the unlikely 2215 // case that the user is scrolling at the same time we may 2216 // invalidate too many lines, since we have to extend the given 2217 // region. 2218 // Note that in the firstVisible == 0 case the new lines are 2219 // already in the dirty region, so they will be updated anyway. 2220 if (visibleDirtyTop <= visibleDirtyBottom) { 2221 if (lastVisible < visibleDirtyTop) 2222 visibleDirtyTop = lastVisible; 2223 if (visibleDirtyBottom < lastVisible + linesScrolled) 2224 visibleDirtyBottom = lastVisible + linesScrolled; 2225 } else { 2226 visibleDirtyTop = lastVisible + 1; 2227 visibleDirtyBottom = lastVisible + linesScrolled; 2228 } 2229 } else 2230 scrollOffset = fScrollOffset - linesScrolled * fFontHeight; 2231 2232 _ScrollTo(scrollOffset, false); 2233 } else 2234 doScroll = true; 2235 2236 if (doScroll && lastVisible >= firstVisible 2237 && !(info.IsDirtyRegionValid() && firstVisible >= info.dirtyTop 2238 && lastVisible <= info.dirtyBottom)) { 2239 // scroll manually 2240 float scrollBy = linesScrolled * fFontHeight; 2241 BRect destRect(Frame().OffsetToCopy(B_ORIGIN)); 2242 BRect sourceRect(destRect.OffsetByCopy(0, scrollBy)); 2243 2244 // invalidate the current cursor position before scrolling 2245 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 2246 2247 //debug_printf("CopyBits(((%f, %f) - (%f, %f)) -> (%f, %f) - (%f, %f))\n", 2248 //sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom, 2249 //destRect.left, destRect.top, destRect.right, destRect.bottom); 2250 CopyBits(sourceRect, destRect); 2251 2252 fVisibleTextBuffer->ScrollBy(linesScrolled); 2253 } 2254 2255 // move highlights 2256 for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { 2257 if (highlight->IsEmpty()) 2258 continue; 2259 2260 highlight->ScrollRange(linesScrolled); 2261 if (highlight == &fSelection) { 2262 fInitialSelectionStart.y -= linesScrolled; 2263 fInitialSelectionEnd.y -= linesScrolled; 2264 } 2265 2266 if (highlight->Start().y < -historySize) { 2267 if (highlight == &fSelection) 2268 _Deselect(); 2269 else 2270 _ClearHighlight(highlight); 2271 } 2272 } 2273 } 2274 2275 // invalidate dirty region 2276 if (info.IsDirtyRegionValid()) { 2277 _InvalidateTextRect(0, info.dirtyTop, fTextBuffer->Width() - 1, 2278 info.dirtyBottom); 2279 2280 // clear the selection, if affected 2281 if (!fSelection.IsEmpty()) { 2282 // TODO: We're clearing the selection more often than necessary -- 2283 // to avoid that, we'd also need to track the x coordinates of the 2284 // dirty range. 2285 int32 selectionBottom = fSelection.End().x > 0 2286 ? fSelection.End().y : fSelection.End().y - 1; 2287 if (fSelection.Start().y <= info.dirtyBottom 2288 && info.dirtyTop <= selectionBottom) { 2289 _Deselect(); 2290 } 2291 } 2292 } 2293 2294 if (visibleDirtyTop <= visibleDirtyBottom) 2295 info.ExtendDirtyRegion(visibleDirtyTop, visibleDirtyBottom); 2296 2297 if (linesScrolled != 0 || info.IsDirtyRegionValid()) { 2298 fVisibleTextBuffer->SynchronizeWith(fTextBuffer, firstVisible, 2299 info.dirtyTop, info.dirtyBottom); 2300 } 2301 2302 // invalidate cursor, if it changed 2303 TermPos cursor = fTextBuffer->Cursor(); 2304 if (fCursor != cursor || linesScrolled != 0) { 2305 // Before we scrolled we did already invalidate the old cursor. 2306 if (!doScroll) 2307 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 2308 fCursor = cursor; 2309 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 2310 _ActivateCursor(false); 2311 } 2312 2313 info.Reset(); 2314 } 2315 2316 2317 void 2318 TermView::_VisibleTextBufferChanged() 2319 { 2320 if (!fVisibleTextBufferChanged) 2321 return; 2322 2323 fVisibleTextBufferChanged = false; 2324 fActiveState->VisibleTextBufferChanged(); 2325 } 2326 2327 2328 /*! Write strings to PTY device. If encoding system isn't UTF8, change 2329 encoding to UTF8 before writing PTY. 2330 */ 2331 void 2332 TermView::_WritePTY(const char* text, int32 numBytes) 2333 { 2334 if (fEncoding != M_UTF8) { 2335 while (numBytes > 0) { 2336 char buffer[1024]; 2337 int32 bufferSize = sizeof(buffer); 2338 int32 sourceSize = numBytes; 2339 int32 state = 0; 2340 if (convert_to_utf8(fEncoding, text, &sourceSize, buffer, 2341 &bufferSize, &state) != B_OK || bufferSize == 0) { 2342 break; 2343 } 2344 2345 fShell->Write(buffer, bufferSize); 2346 text += sourceSize; 2347 numBytes -= sourceSize; 2348 } 2349 } else { 2350 fShell->Write(text, numBytes); 2351 } 2352 } 2353 2354 2355 //! Returns the square of the actual pixel distance between both points 2356 float 2357 TermView::_MouseDistanceSinceLastClick(BPoint where) 2358 { 2359 return (fLastClickPoint.x - where.x) * (fLastClickPoint.x - where.x) 2360 + (fLastClickPoint.y - where.y) * (fLastClickPoint.y - where.y); 2361 } 2362 2363 2364 void 2365 TermView::_SendMouseEvent(int32 buttons, int32 mode, int32 x, int32 y, 2366 bool motion) 2367 { 2368 char xtermButtons; 2369 if (buttons == B_PRIMARY_MOUSE_BUTTON) 2370 xtermButtons = 32 + 0; 2371 else if (buttons == B_SECONDARY_MOUSE_BUTTON) 2372 xtermButtons = 32 + 1; 2373 else if (buttons == B_TERTIARY_MOUSE_BUTTON) 2374 xtermButtons = 32 + 2; 2375 else 2376 xtermButtons = 32 + 3; 2377 2378 if (motion) 2379 xtermButtons += 32; 2380 2381 char xtermX = x + 1 + 32; 2382 char xtermY = y + 1 + 32; 2383 2384 char destBuffer[6]; 2385 destBuffer[0] = '\033'; 2386 destBuffer[1] = '['; 2387 destBuffer[2] = 'M'; 2388 destBuffer[3] = xtermButtons; 2389 destBuffer[4] = xtermX; 2390 destBuffer[5] = xtermY; 2391 fShell->Write(destBuffer, 6); 2392 } 2393 2394 2395 void 2396 TermView::MouseDown(BPoint where) 2397 { 2398 if (!IsFocus()) 2399 MakeFocus(); 2400 2401 _UpdateModifiers(); 2402 2403 BMessage* currentMessage = Window()->CurrentMessage(); 2404 int32 buttons = currentMessage->GetInt32("buttons", 0); 2405 2406 fActiveState->MouseDown(where, buttons, fModifiers); 2407 2408 fMouseButtons = buttons; 2409 fLastClickPoint = where; 2410 } 2411 2412 2413 void 2414 TermView::MouseMoved(BPoint where, uint32 transit, const BMessage *message) 2415 { 2416 _UpdateModifiers(); 2417 2418 fActiveState->MouseMoved(where, transit, message, fModifiers); 2419 } 2420 2421 2422 void 2423 TermView::MouseUp(BPoint where) 2424 { 2425 _UpdateModifiers(); 2426 2427 int32 buttons = Window()->CurrentMessage()->GetInt32("buttons", 0); 2428 2429 fActiveState->MouseUp(where, buttons); 2430 2431 fMouseButtons = buttons; 2432 } 2433 2434 2435 //! Select a range of text. 2436 void 2437 TermView::_Select(TermPos start, TermPos end, bool inclusive, 2438 bool setInitialSelection) 2439 { 2440 TextBufferSyncLocker _(this); 2441 2442 _SynchronizeWithTextBuffer(0, -1); 2443 2444 if (end < start) 2445 std::swap(start, end); 2446 2447 if (inclusive) 2448 end.x++; 2449 2450 //debug_printf("TermView::_Select(): (%ld, %ld) - (%ld, %ld)\n", start.x, 2451 //start.y, end.x, end.y); 2452 2453 if (start.x < 0) 2454 start.x = 0; 2455 if (end.x >= fColumns) 2456 end.x = fColumns; 2457 2458 TermPos minPos(0, -fTextBuffer->HistorySize()); 2459 TermPos maxPos(0, fTextBuffer->Height()); 2460 start = restrict_value(start, minPos, maxPos); 2461 end = restrict_value(end, minPos, maxPos); 2462 2463 // if the end is past the end of the line, select the line break, too 2464 if (fTextBuffer->LineLength(end.y) < end.x 2465 && end.y < fTextBuffer->Height()) { 2466 end.y++; 2467 end.x = 0; 2468 } 2469 2470 if (fTextBuffer->IsFullWidthChar(start.y, start.x)) { 2471 start.x--; 2472 if (start.x < 0) 2473 start.x = 0; 2474 } 2475 2476 if (fTextBuffer->IsFullWidthChar(end.y, end.x)) { 2477 end.x++; 2478 if (end.x >= fColumns) 2479 end.x = fColumns; 2480 } 2481 2482 if (!fSelection.IsEmpty()) 2483 _InvalidateTextRange(fSelection.Start(), fSelection.End()); 2484 2485 fSelection.SetRange(start, end); 2486 2487 if (setInitialSelection) { 2488 fInitialSelectionStart = fSelection.Start(); 2489 fInitialSelectionEnd = fSelection.End(); 2490 } 2491 2492 _InvalidateTextRange(fSelection.Start(), fSelection.End()); 2493 } 2494 2495 2496 //! Extend selection (shift + mouse click). 2497 void 2498 TermView::_ExtendSelection(TermPos pos, bool inclusive, 2499 bool useInitialSelection) 2500 { 2501 if (!useInitialSelection && !_HasSelection()) 2502 return; 2503 2504 TermPos start = fSelection.Start(); 2505 TermPos end = fSelection.End(); 2506 2507 if (useInitialSelection) { 2508 start = fInitialSelectionStart; 2509 end = fInitialSelectionEnd; 2510 } 2511 2512 if (inclusive) { 2513 if (pos >= start && pos >= end) 2514 pos.x++; 2515 } 2516 2517 if (pos < start) 2518 _Select(pos, end, false, !useInitialSelection); 2519 else if (pos > end) 2520 _Select(start, pos, false, !useInitialSelection); 2521 else if (useInitialSelection) 2522 _Select(start, end, false, false); 2523 } 2524 2525 2526 // clear the selection. 2527 void 2528 TermView::_Deselect() 2529 { 2530 //debug_printf("TermView::_Deselect(): has selection: %d\n", _HasSelection()); 2531 if (_ClearHighlight(&fSelection)) { 2532 fInitialSelectionStart.SetTo(0, 0); 2533 fInitialSelectionEnd.SetTo(0, 0); 2534 } 2535 } 2536 2537 2538 bool 2539 TermView::_HasSelection() const 2540 { 2541 return !fSelection.IsEmpty(); 2542 } 2543 2544 2545 void 2546 TermView::_SelectWord(BPoint where, bool extend, bool useInitialSelection) 2547 { 2548 BAutolock _(fTextBuffer); 2549 2550 TermPos pos = _ConvertToTerminal(where); 2551 TermPos start, end; 2552 if (!fTextBuffer->FindWord(pos, fCharClassifier, true, start, end)) 2553 return; 2554 2555 if (extend) { 2556 if (start 2557 < (useInitialSelection 2558 ? fInitialSelectionStart : fSelection.Start())) { 2559 _ExtendSelection(start, false, useInitialSelection); 2560 } else if (end 2561 > (useInitialSelection 2562 ? fInitialSelectionEnd : fSelection.End())) { 2563 _ExtendSelection(end, false, useInitialSelection); 2564 } else if (useInitialSelection) 2565 _Select(start, end, false, false); 2566 } else 2567 _Select(start, end, false, !useInitialSelection); 2568 } 2569 2570 2571 void 2572 TermView::_SelectLine(BPoint where, bool extend, bool useInitialSelection) 2573 { 2574 TermPos start = TermPos(0, _ConvertToTerminal(where).y); 2575 TermPos end = TermPos(0, start.y + 1); 2576 2577 if (extend) { 2578 if (start 2579 < (useInitialSelection 2580 ? fInitialSelectionStart : fSelection.Start())) { 2581 _ExtendSelection(start, false, useInitialSelection); 2582 } else if (end 2583 > (useInitialSelection 2584 ? fInitialSelectionEnd : fSelection.End())) { 2585 _ExtendSelection(end, false, useInitialSelection); 2586 } else if (useInitialSelection) 2587 _Select(start, end, false, false); 2588 } else 2589 _Select(start, end, false, !useInitialSelection); 2590 } 2591 2592 2593 void 2594 TermView::_AddHighlight(Highlight* highlight) 2595 { 2596 fHighlights.AddItem(highlight); 2597 2598 if (!highlight->IsEmpty()) 2599 _InvalidateTextRange(highlight->Start(), highlight->End()); 2600 } 2601 2602 2603 void 2604 TermView::_RemoveHighlight(Highlight* highlight) 2605 { 2606 if (!highlight->IsEmpty()) 2607 _InvalidateTextRange(highlight->Start(), highlight->End()); 2608 2609 fHighlights.RemoveItem(highlight); 2610 } 2611 2612 2613 bool 2614 TermView::_ClearHighlight(Highlight* highlight) 2615 { 2616 if (highlight->IsEmpty()) 2617 return false; 2618 2619 _InvalidateTextRange(highlight->Start(), highlight->End()); 2620 2621 highlight->SetRange(TermPos(0, 0), TermPos(0, 0)); 2622 return true; 2623 } 2624 2625 2626 TermView::Highlight* 2627 TermView::_CheckHighlightRegion(const TermPos &pos) const 2628 { 2629 for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { 2630 if (highlight->RangeContains(pos)) 2631 return highlight; 2632 } 2633 2634 return NULL; 2635 } 2636 2637 2638 TermView::Highlight* 2639 TermView::_CheckHighlightRegion(int32 row, int32 firstColumn, 2640 int32& lastColumn) const 2641 { 2642 Highlight* nextHighlight = NULL; 2643 2644 for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { 2645 if (highlight->IsEmpty()) 2646 continue; 2647 2648 if (row == highlight->Start().y && firstColumn < highlight->Start().x 2649 && lastColumn >= highlight->Start().x) { 2650 // region starts before the highlight, but intersects with it 2651 if (nextHighlight == NULL 2652 || highlight->Start().x < nextHighlight->Start().x) { 2653 nextHighlight = highlight; 2654 } 2655 continue; 2656 } 2657 2658 if (row == highlight->End().y && firstColumn < highlight->End().x 2659 && lastColumn >= highlight->End().x) { 2660 // region starts in the highlight, but exceeds the end 2661 lastColumn = highlight->End().x - 1; 2662 return highlight; 2663 } 2664 2665 TermPos pos(firstColumn, row); 2666 if (highlight->RangeContains(pos)) 2667 return highlight; 2668 } 2669 2670 if (nextHighlight != NULL) 2671 lastColumn = nextHighlight->Start().x - 1; 2672 return NULL; 2673 } 2674 2675 2676 void 2677 TermView::GetFrameSize(float *width, float *height) 2678 { 2679 int32 historySize; 2680 { 2681 BAutolock _(fTextBuffer); 2682 historySize = fTextBuffer->HistorySize(); 2683 } 2684 2685 if (width != NULL) 2686 *width = fColumns * fFontWidth; 2687 2688 if (height != NULL) 2689 *height = (fRows + historySize) * fFontHeight; 2690 } 2691 2692 2693 // Find a string, and select it if found 2694 bool 2695 TermView::Find(const BString &str, bool forwardSearch, bool matchCase, 2696 bool matchWord) 2697 { 2698 TextBufferSyncLocker _(this); 2699 _SynchronizeWithTextBuffer(0, -1); 2700 2701 TermPos start; 2702 if (_HasSelection()) { 2703 if (forwardSearch) 2704 start = fSelection.End(); 2705 else 2706 start = fSelection.Start(); 2707 } else { 2708 // search from the very beginning/end 2709 if (forwardSearch) 2710 start = TermPos(0, -fTextBuffer->HistorySize()); 2711 else 2712 start = TermPos(0, fTextBuffer->Height()); 2713 } 2714 2715 TermPos matchStart, matchEnd; 2716 if (!fTextBuffer->Find(str.String(), start, forwardSearch, matchCase, 2717 matchWord, matchStart, matchEnd)) { 2718 return false; 2719 } 2720 2721 _Select(matchStart, matchEnd, false, true); 2722 _ScrollToRange(fSelection.Start(), fSelection.End()); 2723 2724 return true; 2725 } 2726 2727 2728 //! Get the selected text and copy to str 2729 void 2730 TermView::GetSelection(BString &str) 2731 { 2732 str.SetTo(""); 2733 BAutolock _(fTextBuffer); 2734 fTextBuffer->GetStringFromRegion(str, fSelection.Start(), fSelection.End()); 2735 } 2736 2737 2738 bool 2739 TermView::CheckShellGone() const 2740 { 2741 if (!fShell) 2742 return false; 2743 2744 // check, if the shell does still live 2745 pid_t pid = fShell->ProcessID(); 2746 team_info info; 2747 return get_team_info(pid, &info) == B_BAD_TEAM_ID; 2748 } 2749 2750 2751 void 2752 TermView::InitiateDrag() 2753 { 2754 BAutolock _(fTextBuffer); 2755 2756 BString copyStr(""); 2757 fTextBuffer->GetStringFromRegion(copyStr, fSelection.Start(), 2758 fSelection.End()); 2759 2760 BMessage message(B_MIME_DATA); 2761 message.AddData("text/plain", B_MIME_TYPE, copyStr.String(), 2762 copyStr.Length()); 2763 2764 BPoint start = _ConvertFromTerminal(fSelection.Start()); 2765 BPoint end = _ConvertFromTerminal(fSelection.End()); 2766 2767 BRect rect; 2768 if (fSelection.Start().y == fSelection.End().y) 2769 rect.Set(start.x, start.y, end.x + fFontWidth, end.y + fFontHeight); 2770 else 2771 rect.Set(0, start.y, fColumns * fFontWidth, end.y + fFontHeight); 2772 2773 rect = rect & Bounds(); 2774 2775 DragMessage(&message, rect); 2776 } 2777 2778 2779 void 2780 TermView::_ScrollTo(float y, bool scrollGfx) 2781 { 2782 if (!scrollGfx) 2783 fScrollOffset = y; 2784 2785 if (fScrollBar != NULL) 2786 fScrollBar->SetValue(y); 2787 else 2788 ScrollTo(BPoint(0, y)); 2789 } 2790 2791 2792 void 2793 TermView::_ScrollToRange(TermPos start, TermPos end) 2794 { 2795 if (start > end) 2796 std::swap(start, end); 2797 2798 float startY = _LineOffset(start.y); 2799 float endY = _LineOffset(end.y) + fFontHeight - 1; 2800 float height = Bounds().Height(); 2801 2802 if (endY - startY > height) { 2803 // The range is greater than the height. Scroll to the closest border. 2804 2805 // already as good as it gets? 2806 if (startY <= 0 && endY >= height) 2807 return; 2808 2809 if (startY > 0) { 2810 // scroll down to align the start with the top of the view 2811 _ScrollTo(fScrollOffset + startY, true); 2812 } else { 2813 // scroll up to align the end with the bottom of the view 2814 _ScrollTo(fScrollOffset + endY - height, true); 2815 } 2816 } else { 2817 // The range is smaller than the height. 2818 2819 // already visible? 2820 if (startY >= 0 && endY <= height) 2821 return; 2822 2823 if (startY < 0) { 2824 // scroll up to make the start visible 2825 _ScrollTo(fScrollOffset + startY, true); 2826 } else { 2827 // scroll down to make the end visible 2828 _ScrollTo(fScrollOffset + endY - height, true); 2829 } 2830 } 2831 } 2832 2833 2834 void 2835 TermView::DisableResizeView(int32 disableCount) 2836 { 2837 fResizeViewDisableCount += disableCount; 2838 } 2839 2840 2841 void 2842 TermView::_DrawInlineMethodString() 2843 { 2844 if (!fInline || !fInline->String()) 2845 return; 2846 2847 const int32 numChars = BString(fInline->String()).CountChars(); 2848 2849 BPoint startPoint = _ConvertFromTerminal(fCursor); 2850 BPoint endPoint = startPoint; 2851 endPoint.x += fFontWidth * numChars; 2852 endPoint.y += fFontHeight + 1; 2853 2854 BRect eraseRect(startPoint, endPoint); 2855 2856 PushState(); 2857 SetHighColor(fTextForeColor); 2858 FillRect(eraseRect); 2859 PopState(); 2860 2861 BPoint loc = _ConvertFromTerminal(fCursor); 2862 loc.y += fFontHeight; 2863 SetFont(&fHalfFont); 2864 SetHighColor(fTextBackColor); 2865 SetLowColor(fTextForeColor); 2866 DrawString(fInline->String(), loc); 2867 } 2868 2869 2870 void 2871 TermView::_HandleInputMethodChanged(BMessage *message) 2872 { 2873 const char *string = NULL; 2874 if (message->FindString("be:string", &string) < B_OK || string == NULL) 2875 return; 2876 2877 _ActivateCursor(false); 2878 2879 if (IsFocus()) 2880 be_app->ObscureCursor(); 2881 2882 // If we find the "be:confirmed" boolean (and the boolean is true), 2883 // it means it's over for now, so the current InlineInput object 2884 // should become inactive. We will probably receive a 2885 // B_INPUT_METHOD_STOPPED message after this one. 2886 bool confirmed; 2887 if (message->FindBool("be:confirmed", &confirmed) != B_OK) 2888 confirmed = false; 2889 2890 fInline->SetString(""); 2891 2892 Invalidate(); 2893 // TODO: Debug only 2894 snooze(100000); 2895 2896 fInline->SetString(string); 2897 fInline->ResetClauses(); 2898 2899 if (!confirmed && !fInline->IsActive()) 2900 fInline->SetActive(true); 2901 2902 // Get the clauses, and pass them to the InlineInput object 2903 // TODO: Find out if what we did it's ok, currently we don't consider 2904 // clauses at all, while the bebook says we should; though the visual 2905 // effect we obtained seems correct. Weird. 2906 int32 clauseCount = 0; 2907 int32 clauseStart; 2908 int32 clauseEnd; 2909 while (message->FindInt32("be:clause_start", clauseCount, &clauseStart) 2910 == B_OK 2911 && message->FindInt32("be:clause_end", clauseCount, &clauseEnd) 2912 == B_OK) { 2913 if (!fInline->AddClause(clauseStart, clauseEnd)) 2914 break; 2915 clauseCount++; 2916 } 2917 2918 if (confirmed) { 2919 fInline->SetString(""); 2920 _ActivateCursor(true); 2921 2922 // now we need to feed ourselves the individual characters as if the 2923 // user would have pressed them now - this lets KeyDown() pick out all 2924 // the special characters like B_BACKSPACE, cursor keys and the like: 2925 const char* currPos = string; 2926 const char* prevPos = currPos; 2927 while (*currPos != '\0') { 2928 if ((*currPos & 0xC0) == 0xC0) { 2929 // found the start of an UTF-8 char, we collect while it lasts 2930 ++currPos; 2931 while ((*currPos & 0xC0) == 0x80) 2932 ++currPos; 2933 } else if ((*currPos & 0xC0) == 0x80) { 2934 // illegal: character starts with utf-8 intermediate byte, skip it 2935 prevPos = ++currPos; 2936 } else { 2937 // single byte character/code, just feed that 2938 ++currPos; 2939 } 2940 KeyDown(prevPos, currPos - prevPos); 2941 prevPos = currPos; 2942 } 2943 2944 Invalidate(); 2945 } else { 2946 // temporarily show transient state of inline input 2947 int32 selectionStart = 0; 2948 int32 selectionEnd = 0; 2949 message->FindInt32("be:selection", 0, &selectionStart); 2950 message->FindInt32("be:selection", 1, &selectionEnd); 2951 2952 fInline->SetSelectionOffset(selectionStart); 2953 fInline->SetSelectionLength(selectionEnd - selectionStart); 2954 Invalidate(); 2955 } 2956 } 2957 2958 2959 void 2960 TermView::_HandleInputMethodLocationRequest() 2961 { 2962 BMessage message(B_INPUT_METHOD_EVENT); 2963 message.AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST); 2964 2965 BString string(fInline->String()); 2966 2967 const int32 &limit = string.CountChars(); 2968 BPoint where = _ConvertFromTerminal(fCursor); 2969 where.y += fFontHeight; 2970 2971 for (int32 i = 0; i < limit; i++) { 2972 // Add the location of the UTF8 characters 2973 2974 where.x += fFontWidth; 2975 ConvertToScreen(&where); 2976 2977 message.AddPoint("be:location_reply", where); 2978 message.AddFloat("be:height_reply", fFontHeight); 2979 } 2980 2981 fInline->Method()->SendMessage(&message); 2982 } 2983 2984 2985 void 2986 TermView::_CancelInputMethod() 2987 { 2988 if (!fInline) 2989 return; 2990 2991 InlineInput *inlineInput = fInline; 2992 fInline = NULL; 2993 2994 if (inlineInput->IsActive() && Window()) { 2995 Invalidate(); 2996 2997 BMessage message(B_INPUT_METHOD_EVENT); 2998 message.AddInt32("be:opcode", B_INPUT_METHOD_STOPPED); 2999 inlineInput->Method()->SendMessage(&message); 3000 } 3001 3002 delete inlineInput; 3003 } 3004 3005 3006 void 3007 TermView::_UpdateModifiers() 3008 { 3009 // TODO: This method is a general work-around for missing or out-of-order 3010 // B_MODIFIERS_CHANGED messages. This should really be fixed where it is 3011 // broken (app server?). 3012 int32 oldModifiers = fModifiers; 3013 fModifiers = modifiers(); 3014 if (fModifiers != oldModifiers && fActiveState != NULL) 3015 fActiveState->ModifiersChanged(oldModifiers, fModifiers); 3016 } 3017 3018 3019 void 3020 TermView::_NextState(State* state) 3021 { 3022 if (state != fActiveState) { 3023 if (fActiveState != NULL) 3024 fActiveState->Exited(); 3025 fActiveState = state; 3026 fActiveState->Entered(); 3027 } 3028 } 3029 3030 3031 // #pragma mark - Listener 3032 3033 3034 TermView::Listener::~Listener() 3035 { 3036 } 3037 3038 3039 void 3040 TermView::Listener::NotifyTermViewQuit(TermView* view, int32 reason) 3041 { 3042 } 3043 3044 3045 void 3046 TermView::Listener::SetTermViewTitle(TermView* view, const char* title) 3047 { 3048 } 3049 3050 3051 void 3052 TermView::Listener::PreviousTermView(TermView* view) 3053 { 3054 } 3055 3056 3057 void 3058 TermView::Listener::NextTermView(TermView* view) 3059 { 3060 } 3061 3062 3063 // #pragma mark - 3064 3065 3066 #ifdef USE_DEBUG_SNAPSHOTS 3067 3068 void 3069 TermView::MakeDebugSnapshots() 3070 { 3071 BAutolock _(fTextBuffer); 3072 time_t timeStamp = time(NULL); 3073 fTextBuffer->MakeLinesSnapshots(timeStamp, ".TextBuffer.dump"); 3074 fVisibleTextBuffer->MakeLinesSnapshots(timeStamp, ".VisualTextBuffer.dump"); 3075 } 3076 3077 3078 void 3079 TermView::StartStopDebugCapture() 3080 { 3081 BAutolock _(fTextBuffer); 3082 fTextBuffer->StartStopDebugCapture(); 3083 } 3084 3085 #endif 3086