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(float* _width, float* _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 = int((rect.IntegerWidth() + 1) / fFontWidth); 635 int rows = int((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 float 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 float tmpWidth = 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 bool blinking = PrefHandler::Default()->getBool(PREF_BLINK_CURSOR); 778 SwitchCursorBlinking(blinking); 779 780 fEmulateBold = PrefHandler::Default() == NULL ? false 781 : PrefHandler::Default()->getBool(PREF_EMULATE_BOLD); 782 783 fAllowBold = PrefHandler::Default() == NULL ? false 784 : PrefHandler::Default()->getBool(PREF_ALLOW_BOLD); 785 786 _ScrollTo(0, false); 787 if (fScrollBar != NULL) 788 fScrollBar->SetSteps(fFontHeight, fFontHeight * fRows); 789 } 790 791 792 void 793 TermView::SetScrollBar(BScrollBar *scrollBar) 794 { 795 fScrollBar = scrollBar; 796 if (fScrollBar != NULL) 797 fScrollBar->SetSteps(fFontHeight, fFontHeight * fRows); 798 } 799 800 801 void 802 TermView::SwitchCursorBlinking(bool blinkingOn) 803 { 804 fCursorBlinking = blinkingOn; 805 if (blinkingOn) { 806 if (fCursorBlinkRunner == NULL) { 807 BMessage blinkMessage(kBlinkCursor); 808 fCursorBlinkRunner = new (std::nothrow) BMessageRunner( 809 BMessenger(this), &blinkMessage, kCursorBlinkInterval); 810 } 811 } else { 812 // make sure the cursor becomes visible 813 fCursorState = 0; 814 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 815 delete fCursorBlinkRunner; 816 fCursorBlinkRunner = NULL; 817 } 818 } 819 820 821 void 822 TermView::Copy(BClipboard *clipboard) 823 { 824 BAutolock _(fTextBuffer); 825 826 if (!_HasSelection()) 827 return; 828 829 BString copyStr; 830 fTextBuffer->GetStringFromRegion(copyStr, fSelection.Start(), 831 fSelection.End()); 832 833 if (clipboard->Lock()) { 834 BMessage *clipMsg = NULL; 835 clipboard->Clear(); 836 837 if ((clipMsg = clipboard->Data()) != NULL) { 838 clipMsg->AddData("text/plain", B_MIME_TYPE, copyStr.String(), 839 copyStr.Length()); 840 clipboard->Commit(); 841 } 842 clipboard->Unlock(); 843 } 844 } 845 846 847 void 848 TermView::Paste(BClipboard *clipboard) 849 { 850 if (clipboard->Lock()) { 851 BMessage *clipMsg = clipboard->Data(); 852 const char* text; 853 ssize_t numBytes; 854 if (clipMsg->FindData("text/plain", B_MIME_TYPE, 855 (const void**)&text, &numBytes) == B_OK ) { 856 _WritePTY(text, numBytes); 857 } 858 859 clipboard->Unlock(); 860 861 _ScrollTo(0, true); 862 } 863 } 864 865 866 void 867 TermView::SelectAll() 868 { 869 BAutolock _(fTextBuffer); 870 871 _Select(TermPos(0, -fTextBuffer->HistorySize()), 872 TermPos(0, fTextBuffer->Height()), false, true); 873 } 874 875 876 void 877 TermView::Clear() 878 { 879 _Deselect(); 880 881 { 882 BAutolock _(fTextBuffer); 883 fTextBuffer->Clear(true); 884 } 885 fVisibleTextBuffer->Clear(true); 886 887 //debug_printf("Invalidate()\n"); 888 Invalidate(); 889 890 _ScrollTo(0, false); 891 if (fScrollBar) { 892 fScrollBar->SetRange(0, 0); 893 fScrollBar->SetProportion(1); 894 } 895 } 896 897 898 //! Draw region 899 void 900 TermView::_InvalidateTextRange(TermPos start, TermPos end) 901 { 902 if (end < start) 903 std::swap(start, end); 904 905 if (start.y == end.y) { 906 _InvalidateTextRect(start.x, start.y, end.x, end.y); 907 } else { 908 _InvalidateTextRect(start.x, start.y, fColumns, start.y); 909 910 if (end.y - start.y > 0) 911 _InvalidateTextRect(0, start.y + 1, fColumns, end.y - 1); 912 913 _InvalidateTextRect(0, end.y, end.x, end.y); 914 } 915 } 916 917 918 status_t 919 TermView::_AttachShell(Shell *shell) 920 { 921 if (shell == NULL) 922 return B_BAD_VALUE; 923 924 fShell = shell; 925 926 return fShell->AttachBuffer(TextBuffer()); 927 } 928 929 930 void 931 TermView::_DetachShell() 932 { 933 fShell->DetachBuffer(); 934 fShell = NULL; 935 } 936 937 938 void 939 TermView::_Activate() 940 { 941 fActive = true; 942 SwitchCursorBlinking(fCursorBlinking); 943 } 944 945 946 void 947 TermView::_Deactivate() 948 { 949 // make sure the cursor becomes visible 950 fCursorState = 0; 951 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 952 953 SwitchCursorBlinking(false); 954 955 fActive = false; 956 } 957 958 959 //! Draw part of a line in the given view. 960 void 961 TermView::_DrawLinePart(float x1, float y1, uint32 attr, char *buf, 962 int32 width, Highlight* highlight, bool cursor, BView *inView) 963 { 964 if (highlight != NULL) 965 attr = highlight->Highlighter()->AdjustTextAttributes(attr); 966 967 inView->SetFont(IS_BOLD(attr) && !fEmulateBold && fAllowBold 968 ? &fBoldFont : &fHalfFont); 969 970 // Set pen point 971 float x2 = x1 + fFontWidth * width; 972 float y2 = y1 + fFontHeight; 973 974 rgb_color rgb_fore = fTextForeColor; 975 rgb_color rgb_back = fTextBackColor; 976 977 // color attribute 978 int forecolor = IS_FORECOLOR(attr); 979 int backcolor = IS_BACKCOLOR(attr); 980 981 if (IS_FORESET(attr)) 982 rgb_fore = fTextBuffer->PaletteColor(forecolor); 983 if (IS_BACKSET(attr)) 984 rgb_back = fTextBuffer->PaletteColor(backcolor); 985 986 // Selection check. 987 if (cursor) { 988 rgb_fore = fCursorForeColor; 989 rgb_back = fCursorBackColor; 990 } else if (highlight != NULL) { 991 rgb_fore = highlight->Highlighter()->ForegroundColor(); 992 rgb_back = highlight->Highlighter()->BackgroundColor(); 993 } else { 994 // Reverse attribute(If selected area, don't reverse color). 995 if (IS_INVERSE(attr)) { 996 rgb_color rgb_tmp = rgb_fore; 997 rgb_fore = rgb_back; 998 rgb_back = rgb_tmp; 999 } 1000 } 1001 1002 // Fill color at Background color and set low color. 1003 inView->SetHighColor(rgb_back); 1004 inView->FillRect(BRect(x1, y1, x2 - 1, y2 - 1)); 1005 inView->SetLowColor(rgb_back); 1006 inView->SetHighColor(rgb_fore); 1007 1008 // Draw character. 1009 if (IS_BOLD(attr)) { 1010 if (fEmulateBold) { 1011 inView->MovePenTo(x1 - 1, y1 + fFontAscent - 1); 1012 inView->DrawString((char *)buf); 1013 inView->SetDrawingMode(B_OP_BLEND); 1014 } else { 1015 rgb_color bright = rgb_fore; 1016 1017 bright.red = saturated_add<uint8>(bright.red, 64); 1018 bright.green = saturated_add<uint8>(bright.green, 64); 1019 bright.blue = saturated_add<uint8>(bright.blue, 64); 1020 1021 inView->SetHighColor(bright); 1022 } 1023 } 1024 1025 inView->MovePenTo(x1, y1 + fFontAscent); 1026 inView->DrawString((char *)buf); 1027 inView->SetDrawingMode(B_OP_COPY); 1028 1029 // underline attribute 1030 if (IS_UNDER(attr)) { 1031 inView->MovePenTo(x1, y1 + fFontAscent); 1032 inView->StrokeLine(BPoint(x1 , y1 + fFontAscent), 1033 BPoint(x2 , y1 + fFontAscent)); 1034 } 1035 } 1036 1037 1038 /*! Caller must have locked fTextBuffer. 1039 */ 1040 void 1041 TermView::_DrawCursor() 1042 { 1043 BRect rect(fFontWidth * fCursor.x, _LineOffset(fCursor.y), 0, 0); 1044 rect.right = rect.left + fFontWidth - 1; 1045 rect.bottom = rect.top + fFontHeight - 1; 1046 int32 firstVisible = _LineAt(0); 1047 1048 UTF8Char character; 1049 uint32 attr = 0; 1050 1051 bool cursorVisible = _IsCursorVisible(); 1052 1053 if (cursorVisible) { 1054 switch (fCursorStyle) { 1055 case UNDERLINE_CURSOR: 1056 rect.top = rect.bottom - 2; 1057 break; 1058 case IBEAM_CURSOR: 1059 rect.right = rect.left + 1; 1060 break; 1061 case BLOCK_CURSOR: 1062 default: 1063 break; 1064 } 1065 } 1066 1067 Highlight* highlight = _CheckHighlightRegion(TermPos(fCursor.x, fCursor.y)); 1068 if (fVisibleTextBuffer->GetChar(fCursor.y - firstVisible, fCursor.x, 1069 character, attr) == A_CHAR 1070 && (fCursorStyle == BLOCK_CURSOR || !cursorVisible)) { 1071 1072 int32 width = IS_WIDTH(attr) ? FULL_WIDTH : HALF_WIDTH; 1073 char buffer[5]; 1074 int32 bytes = UTF8Char::ByteCount(character.bytes[0]); 1075 memcpy(buffer, character.bytes, bytes); 1076 buffer[bytes] = '\0'; 1077 1078 _DrawLinePart(fCursor.x * fFontWidth, (int32)rect.top, attr, buffer, 1079 width, highlight, cursorVisible, this); 1080 } else { 1081 if (highlight != NULL) 1082 SetHighColor(highlight->Highlighter()->BackgroundColor()); 1083 else if (cursorVisible) 1084 SetHighColor(fCursorBackColor ); 1085 else { 1086 uint32 count = 0; 1087 rgb_color rgb_back = fTextBackColor; 1088 if (fTextBuffer->IsAlternateScreenActive()) 1089 // alternate screen uses cell attributes beyond the line ends 1090 fTextBuffer->GetCellAttributes( 1091 fCursor.y, fCursor.x, attr, count); 1092 else 1093 attr = fVisibleTextBuffer->GetLineColor( 1094 fCursor.y - firstVisible); 1095 1096 if (IS_BACKSET(attr)) 1097 rgb_back = fTextBuffer->PaletteColor(IS_BACKCOLOR(attr)); 1098 SetHighColor(rgb_back); 1099 } 1100 1101 if (IS_WIDTH(attr) && fCursorStyle != IBEAM_CURSOR) 1102 rect.right += fFontWidth; 1103 1104 FillRect(rect); 1105 } 1106 } 1107 1108 1109 bool 1110 TermView::_IsCursorVisible() const 1111 { 1112 return !fCursorHidden && fCursorState < kCursorVisibleIntervals; 1113 } 1114 1115 1116 void 1117 TermView::_BlinkCursor() 1118 { 1119 bool wasVisible = _IsCursorVisible(); 1120 1121 if (!wasVisible && fInline && fInline->IsActive()) 1122 return; 1123 1124 bigtime_t now = system_time(); 1125 if (Window()->IsActive() && now - fLastActivityTime >= kCursorBlinkInterval) 1126 fCursorState = (fCursorState + 1) % kCursorBlinkIntervals; 1127 else 1128 fCursorState = 0; 1129 1130 if (wasVisible != _IsCursorVisible()) 1131 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 1132 } 1133 1134 1135 void 1136 TermView::_ActivateCursor(bool invalidate) 1137 { 1138 fLastActivityTime = system_time(); 1139 if (invalidate && fCursorState != 0) 1140 _BlinkCursor(); 1141 else 1142 fCursorState = 0; 1143 } 1144 1145 1146 //! Update scroll bar range and knob size. 1147 void 1148 TermView::_UpdateScrollBarRange() 1149 { 1150 if (fScrollBar == NULL) 1151 return; 1152 1153 int32 historySize; 1154 { 1155 BAutolock _(fTextBuffer); 1156 historySize = fTextBuffer->HistorySize(); 1157 } 1158 1159 float viewHeight = fRows * fFontHeight; 1160 float historyHeight = (float)historySize * fFontHeight; 1161 1162 //debug_printf("TermView::_UpdateScrollBarRange(): history: %ld, range: %f - 0\n", 1163 //historySize, -historyHeight); 1164 1165 fScrollBar->SetRange(-historyHeight, 0); 1166 if (historySize > 0) 1167 fScrollBar->SetProportion(viewHeight / (viewHeight + historyHeight)); 1168 } 1169 1170 1171 //! Handler for SIGWINCH 1172 void 1173 TermView::_UpdateSIGWINCH() 1174 { 1175 if (fFrameResized) { 1176 fShell->UpdateWindowSize(fRows, fColumns); 1177 fFrameResized = false; 1178 } 1179 } 1180 1181 1182 void 1183 TermView::AttachedToWindow() 1184 { 1185 fMouseButtons = 0; 1186 1187 _UpdateModifiers(); 1188 1189 // update the terminal size because it may have changed while the TermView 1190 // was detached from the window. On such conditions FrameResized was not 1191 // called when the resize occured 1192 SetTermSize(Bounds(), true); 1193 MakeFocus(true); 1194 if (fScrollBar) { 1195 fScrollBar->SetSteps(fFontHeight, fFontHeight * fRows); 1196 _UpdateScrollBarRange(); 1197 } 1198 1199 BMessenger thisMessenger(this); 1200 1201 BMessage message(kUpdateSigWinch); 1202 fWinchRunner = new (std::nothrow) BMessageRunner(thisMessenger, 1203 &message, 500000); 1204 1205 { 1206 TextBufferSyncLocker _(this); 1207 fTextBuffer->SetListener(thisMessenger); 1208 _SynchronizeWithTextBuffer(0, -1); 1209 } 1210 1211 be_clipboard->StartWatching(thisMessenger); 1212 } 1213 1214 1215 void 1216 TermView::DetachedFromWindow() 1217 { 1218 be_clipboard->StopWatching(BMessenger(this)); 1219 1220 _NextState(fDefaultState); 1221 1222 delete fWinchRunner; 1223 fWinchRunner = NULL; 1224 1225 delete fCursorBlinkRunner; 1226 fCursorBlinkRunner = NULL; 1227 1228 delete fResizeRunner; 1229 fResizeRunner = NULL; 1230 1231 { 1232 BAutolock _(fTextBuffer); 1233 fTextBuffer->UnsetListener(); 1234 } 1235 } 1236 1237 1238 void 1239 TermView::Draw(BRect updateRect) 1240 { 1241 int32 x1 = (int32)(updateRect.left / fFontWidth); 1242 int32 x2 = std::min((int)(updateRect.right / fFontWidth), fColumns - 1); 1243 1244 int32 firstVisible = _LineAt(0); 1245 int32 y1 = _LineAt(updateRect.top); 1246 int32 y2 = std::min(_LineAt(updateRect.bottom), (int32)fRows - 1); 1247 1248 // clear the area to the right of the line ends 1249 if (y1 <= y2) { 1250 float clearLeft = fColumns * fFontWidth; 1251 if (clearLeft <= updateRect.right) { 1252 BRect rect(clearLeft, updateRect.top, updateRect.right, 1253 updateRect.bottom); 1254 SetHighColor(fTextBackColor); 1255 FillRect(rect); 1256 } 1257 } 1258 1259 // clear the area below the last line 1260 if (y2 == fRows - 1) { 1261 float clearTop = _LineOffset(fRows); 1262 if (clearTop <= updateRect.bottom) { 1263 BRect rect(updateRect.left, clearTop, updateRect.right, 1264 updateRect.bottom); 1265 SetHighColor(fTextBackColor); 1266 FillRect(rect); 1267 } 1268 } 1269 1270 // draw the affected line parts 1271 if (x1 <= x2) { 1272 uint32 attr = 0; 1273 1274 for (int32 j = y1; j <= y2; j++) { 1275 int32 k = x1; 1276 char buf[fColumns * 4 + 1]; 1277 1278 if (fVisibleTextBuffer->IsFullWidthChar(j - firstVisible, k)) 1279 k--; 1280 1281 if (k < 0) 1282 k = 0; 1283 1284 for (int32 i = k; i <= x2;) { 1285 int32 lastColumn = x2; 1286 Highlight* highlight = _CheckHighlightRegion(j, i, lastColumn); 1287 // This will clip lastColumn to the selection start or end 1288 // to ensure the selection is not drawn at the same time as 1289 // something else 1290 int32 count = fVisibleTextBuffer->GetString(j - firstVisible, i, 1291 lastColumn, buf, attr); 1292 1293 // debug_printf(" fVisibleTextBuffer->GetString(%ld, %ld, %ld) -> (%ld, \"%.*s\"), highlight: %p\n", 1294 // j - firstVisible, i, lastColumn, count, (int)count, buf, highlight); 1295 1296 if (count == 0) { 1297 // No chars to draw : we just fill the rectangle with the 1298 // back color of the last char at the left 1299 int nextColumn = lastColumn + 1; 1300 BRect rect(fFontWidth * i, _LineOffset(j), 1301 fFontWidth * nextColumn - 1, 0); 1302 rect.bottom = rect.top + fFontHeight - 1; 1303 1304 rgb_color rgb_back = highlight != NULL 1305 ? highlight->Highlighter()->BackgroundColor() 1306 : fTextBackColor; 1307 1308 if (fTextBuffer->IsAlternateScreenActive()) { 1309 // alternate screen uses cell attributes 1310 // beyond the line ends 1311 uint32 count = 0; 1312 fTextBuffer->GetCellAttributes(j, i, attr, count); 1313 rect.right = rect.left + fFontWidth * count - 1; 1314 nextColumn = i + count; 1315 } else 1316 attr = fVisibleTextBuffer->GetLineColor(j - firstVisible); 1317 1318 if (IS_BACKSET(attr)) { 1319 int backcolor = IS_BACKCOLOR(attr); 1320 rgb_back = fTextBuffer->PaletteColor(backcolor); 1321 } 1322 1323 SetHighColor(rgb_back); 1324 rgb_back = HighColor(); 1325 FillRect(rect); 1326 1327 // Go on to the next block 1328 i = nextColumn; 1329 continue; 1330 } 1331 1332 // Note: full-width characters GetString()-ed always 1333 // with count 1, so this hardcoding is safe. From the other 1334 // side - drawing the whole string with one call render the 1335 // characters not aligned to cells grid - that looks much more 1336 // inaccurate for full-width strings than for half-width ones. 1337 if (IS_WIDTH(attr)) 1338 count = FULL_WIDTH; 1339 1340 _DrawLinePart(fFontWidth * i, (int32)_LineOffset(j), 1341 attr, buf, count, highlight, false, this); 1342 i += count; 1343 } 1344 } 1345 } 1346 1347 if (fInline && fInline->IsActive()) 1348 _DrawInlineMethodString(); 1349 1350 if (fCursor >= TermPos(x1, y1) && fCursor <= TermPos(x2, y2)) 1351 _DrawCursor(); 1352 } 1353 1354 1355 void 1356 TermView::_DoPrint(BRect updateRect) 1357 { 1358 #if 0 1359 uint32 attr; 1360 uchar buf[1024]; 1361 1362 const int numLines = (int)((updateRect.Height()) / fFontHeight); 1363 1364 int y1 = (int)(updateRect.top) / fFontHeight; 1365 y1 = y1 -(fScrBufSize - numLines * 2); 1366 if (y1 < 0) 1367 y1 = 0; 1368 1369 const int y2 = y1 + numLines -1; 1370 1371 const int x1 = (int)(updateRect.left) / fFontWidth; 1372 const int x2 = (int)(updateRect.right) / fFontWidth; 1373 1374 for (int j = y1; j <= y2; j++) { 1375 // If(x1, y1) Buffer is in string full width character, 1376 // alignment start position. 1377 1378 int k = x1; 1379 if (fTextBuffer->IsFullWidthChar(j, k)) 1380 k--; 1381 1382 if (k < 0) 1383 k = 0; 1384 1385 for (int i = k; i <= x2;) { 1386 int count = fTextBuffer->GetString(j, i, x2, buf, &attr); 1387 if (count < 0) { 1388 i += abs(count); 1389 continue; 1390 } 1391 1392 _DrawLinePart(fFontWidth * i, fFontHeight * j, 1393 attr, buf, count, false, false, this); 1394 i += count; 1395 } 1396 } 1397 #endif // 0 1398 } 1399 1400 1401 void 1402 TermView::WindowActivated(bool active) 1403 { 1404 BView::WindowActivated(active); 1405 if (active && IsFocus()) { 1406 if (!fActive) 1407 _Activate(); 1408 } else { 1409 if (fActive) 1410 _Deactivate(); 1411 } 1412 1413 _UpdateModifiers(); 1414 1415 fActiveState->WindowActivated(active); 1416 } 1417 1418 1419 void 1420 TermView::MakeFocus(bool focusState) 1421 { 1422 BView::MakeFocus(focusState); 1423 1424 if (focusState && Window() && Window()->IsActive()) { 1425 if (!fActive) 1426 _Activate(); 1427 } else { 1428 if (fActive) 1429 _Deactivate(); 1430 } 1431 } 1432 1433 1434 void 1435 TermView::KeyDown(const char *bytes, int32 numBytes) 1436 { 1437 _UpdateModifiers(); 1438 1439 fActiveState->KeyDown(bytes, numBytes); 1440 } 1441 1442 1443 void 1444 TermView::FrameResized(float width, float height) 1445 { 1446 //debug_printf("TermView::FrameResized(%f, %f)\n", width, height); 1447 int32 columns = (int32)((width + 1) / fFontWidth); 1448 int32 rows = (int32)((height + 1) / fFontHeight); 1449 1450 if (columns == fColumns && rows == fRows) 1451 return; 1452 1453 bool hasResizeView = fResizeRunner != NULL; 1454 if (!hasResizeView) { 1455 // show the current size in a view 1456 fResizeView = new BStringView(BRect(100, 100, 300, 140), "size", ""); 1457 fResizeView->SetAlignment(B_ALIGN_CENTER); 1458 fResizeView->SetFont(be_bold_font); 1459 fResizeView->SetViewColor(fTextBackColor); 1460 fResizeView->SetLowColor(fTextBackColor); 1461 fResizeView->SetHighColor(fTextForeColor); 1462 1463 BMessage message(MSG_REMOVE_RESIZE_VIEW_IF_NEEDED); 1464 fResizeRunner = new(std::nothrow) BMessageRunner(BMessenger(this), 1465 &message, 25000LL); 1466 } 1467 1468 BString text; 1469 text << columns << " x " << rows; 1470 fResizeView->SetText(text.String()); 1471 fResizeView->GetPreferredSize(&width, &height); 1472 fResizeView->ResizeTo(width * 1.5, height * 1.5); 1473 fResizeView->MoveTo((Bounds().Width() - fResizeView->Bounds().Width()) / 2, 1474 (Bounds().Height()- fResizeView->Bounds().Height()) / 2); 1475 if (!hasResizeView && fResizeViewDisableCount < 1) 1476 AddChild(fResizeView); 1477 1478 if (fResizeViewDisableCount > 0) 1479 fResizeViewDisableCount--; 1480 1481 SetTermSize(rows, columns, true); 1482 } 1483 1484 1485 void 1486 TermView::MessageReceived(BMessage *msg) 1487 { 1488 if (fActiveState->MessageReceived(msg)) 1489 return; 1490 1491 entry_ref ref; 1492 const char *ctrl_l = "\x0c"; 1493 1494 // first check for any dropped message 1495 if (msg->WasDropped() && (msg->what == B_SIMPLE_DATA 1496 || msg->what == B_MIME_DATA)) { 1497 char *text; 1498 ssize_t numBytes; 1499 //rgb_color *color; 1500 1501 int32 i = 0; 1502 1503 if (msg->FindRef("refs", i++, &ref) == B_OK) { 1504 // first check if secondary mouse button is pressed 1505 int32 buttons = 0; 1506 msg->FindInt32("buttons", &buttons); 1507 1508 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 1509 // start popup menu 1510 _SecondaryMouseButtonDropped(msg); 1511 return; 1512 } 1513 1514 _DoFileDrop(ref); 1515 1516 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1517 _WritePTY(" ", 1); 1518 _DoFileDrop(ref); 1519 } 1520 return; 1521 #if 0 1522 } else if (msg->FindData("RGBColor", B_RGB_COLOR_TYPE, 1523 (const void **)&color, &numBytes) == B_OK 1524 && numBytes == sizeof(color)) { 1525 // TODO: handle color drop 1526 // maybe only on replicants ? 1527 return; 1528 #endif 1529 } else if (msg->FindData("text/plain", B_MIME_TYPE, 1530 (const void **)&text, &numBytes) == B_OK) { 1531 _WritePTY(text, numBytes); 1532 return; 1533 } 1534 } 1535 1536 switch (msg->what) { 1537 case B_SIMPLE_DATA: 1538 case B_REFS_RECEIVED: 1539 { 1540 // handle refs if they weren't dropped 1541 int32 i = 0; 1542 if (msg->FindRef("refs", i++, &ref) == B_OK) { 1543 _DoFileDrop(ref); 1544 1545 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1546 _WritePTY(" ", 1); 1547 _DoFileDrop(ref); 1548 } 1549 } else 1550 BView::MessageReceived(msg); 1551 break; 1552 } 1553 1554 case B_COPY: 1555 Copy(be_clipboard); 1556 break; 1557 1558 case B_PASTE: 1559 { 1560 int32 code; 1561 if (msg->FindInt32("index", &code) == B_OK) 1562 Paste(be_clipboard); 1563 break; 1564 } 1565 1566 case B_CLIPBOARD_CHANGED: 1567 // This message originates from the system clipboard. Overwrite 1568 // the contents of the mouse clipboard with the ones from the 1569 // system clipboard, in case it contains text data. 1570 if (be_clipboard->Lock()) { 1571 if (fMouseClipboard->Lock()) { 1572 BMessage* clipMsgA = be_clipboard->Data(); 1573 const char* text; 1574 ssize_t numBytes; 1575 if (clipMsgA->FindData("text/plain", B_MIME_TYPE, 1576 (const void**)&text, &numBytes) == B_OK ) { 1577 fMouseClipboard->Clear(); 1578 BMessage* clipMsgB = fMouseClipboard->Data(); 1579 clipMsgB->AddData("text/plain", B_MIME_TYPE, 1580 text, numBytes); 1581 fMouseClipboard->Commit(); 1582 } 1583 fMouseClipboard->Unlock(); 1584 } 1585 be_clipboard->Unlock(); 1586 } 1587 break; 1588 1589 case B_SELECT_ALL: 1590 SelectAll(); 1591 break; 1592 1593 case B_SET_PROPERTY: 1594 { 1595 int32 i; 1596 int32 encodingID; 1597 BMessage specifier; 1598 if (msg->GetCurrentSpecifier(&i, &specifier) == B_OK 1599 && strcmp("encoding", 1600 specifier.FindString("property", i)) == 0) { 1601 msg->FindInt32 ("data", &encodingID); 1602 SetEncoding(encodingID); 1603 msg->SendReply(B_REPLY); 1604 } else { 1605 BView::MessageReceived(msg); 1606 } 1607 break; 1608 } 1609 1610 case B_GET_PROPERTY: 1611 { 1612 int32 i; 1613 BMessage specifier; 1614 if (msg->GetCurrentSpecifier(&i, &specifier) == B_OK 1615 && strcmp("encoding", 1616 specifier.FindString("property", i)) == 0) { 1617 BMessage reply(B_REPLY); 1618 reply.AddInt32("result", Encoding()); 1619 msg->SendReply(&reply); 1620 } else if (strcmp("tty", 1621 specifier.FindString("property", i)) == 0) { 1622 BMessage reply(B_REPLY); 1623 reply.AddString("result", TerminalName()); 1624 msg->SendReply(&reply); 1625 } else { 1626 BView::MessageReceived(msg); 1627 } 1628 break; 1629 } 1630 1631 case B_MODIFIERS_CHANGED: 1632 { 1633 _UpdateModifiers(); 1634 break; 1635 } 1636 1637 case B_INPUT_METHOD_EVENT: 1638 { 1639 int32 opcode; 1640 if (msg->FindInt32("be:opcode", &opcode) == B_OK) { 1641 switch (opcode) { 1642 case B_INPUT_METHOD_STARTED: 1643 { 1644 BMessenger messenger; 1645 if (msg->FindMessenger("be:reply_to", 1646 &messenger) == B_OK) { 1647 fInline = new (std::nothrow) 1648 InlineInput(messenger); 1649 } 1650 break; 1651 } 1652 1653 case B_INPUT_METHOD_STOPPED: 1654 delete fInline; 1655 fInline = NULL; 1656 break; 1657 1658 case B_INPUT_METHOD_CHANGED: 1659 if (fInline != NULL) 1660 _HandleInputMethodChanged(msg); 1661 break; 1662 1663 case B_INPUT_METHOD_LOCATION_REQUEST: 1664 if (fInline != NULL) 1665 _HandleInputMethodLocationRequest(); 1666 break; 1667 1668 default: 1669 break; 1670 } 1671 } 1672 break; 1673 } 1674 1675 case B_MOUSE_WHEEL_CHANGED: 1676 { 1677 // overridden to allow scrolling emulation in alternative screen 1678 // mode 1679 BAutolock locker(fTextBuffer); 1680 float deltaY = 0; 1681 if (fTextBuffer->IsAlternateScreenActive() 1682 && msg->FindFloat("be:wheel_delta_y", &deltaY) == B_OK 1683 && deltaY != 0) { 1684 // We are in alternative screen mode and have a vertical delta 1685 // we can work with -- emulate scrolling via terminal escape 1686 // sequences. 1687 locker.Unlock(); 1688 1689 // scroll pagewise, if one of Option, Command, or Control is 1690 // pressed 1691 int32 steps; 1692 const char* stepString; 1693 if ((modifiers() & B_SHIFT_KEY) != 0) { 1694 // pagewise 1695 stepString = deltaY > 0 1696 ? PAGE_DOWN_KEY_CODE : PAGE_UP_KEY_CODE; 1697 steps = abs((int)deltaY); 1698 } else { 1699 // three lines per step 1700 stepString = deltaY > 0 1701 ? DOWN_ARROW_KEY_CODE : UP_ARROW_KEY_CODE; 1702 steps = 3 * abs((int)deltaY); 1703 } 1704 1705 // We want to do only a single write(), so compose a string 1706 // repeating the sequence as often as required by the delta. 1707 BString toWrite; 1708 for (int32 i = 0; i <steps; i++) 1709 toWrite << stepString; 1710 1711 _WritePTY(toWrite.String(), toWrite.Length()); 1712 } else { 1713 // let the BView's implementation handle the standard scrolling 1714 locker.Unlock(); 1715 BView::MessageReceived(msg); 1716 } 1717 1718 break; 1719 } 1720 1721 case MENU_CLEAR_ALL: 1722 Clear(); 1723 fShell->Write(ctrl_l, 1); 1724 break; 1725 case kBlinkCursor: 1726 _BlinkCursor(); 1727 break; 1728 case kUpdateSigWinch: 1729 _UpdateSIGWINCH(); 1730 break; 1731 case kSecondaryMouseDropAction: 1732 _DoSecondaryMouseDropAction(msg); 1733 break; 1734 case MSG_TERMINAL_BUFFER_CHANGED: 1735 { 1736 TextBufferSyncLocker _(this); 1737 _SynchronizeWithTextBuffer(0, -1); 1738 break; 1739 } 1740 case MSG_SET_TERMINAL_TITLE: 1741 { 1742 const char* title; 1743 if (msg->FindString("title", &title) == B_OK) { 1744 if (fListener != NULL) 1745 fListener->SetTermViewTitle(this, title); 1746 } 1747 break; 1748 } 1749 case MSG_SET_TERMINAL_COLORS: 1750 { 1751 int32 count = 0; 1752 if (msg->FindInt32("count", &count) != B_OK) 1753 break; 1754 bool dynamic = false; 1755 if (msg->FindBool("dynamic", &dynamic) != B_OK) 1756 break; 1757 for (int i = 0; i < count; i++) { 1758 uint8 index = 0; 1759 if (msg->FindUInt8("index", i, &index) != B_OK) 1760 break; 1761 1762 ssize_t bytes = 0; 1763 rgb_color* color = 0; 1764 if (msg->FindData("color", B_RGB_COLOR_TYPE, 1765 i, (const void**)&color, &bytes) != B_OK) 1766 break; 1767 SetTermColor(index, *color, dynamic); 1768 } 1769 break; 1770 } 1771 case MSG_RESET_TERMINAL_COLORS: 1772 { 1773 int32 count = 0; 1774 if (msg->FindInt32("count", &count) != B_OK) 1775 break; 1776 bool dynamic = false; 1777 if (msg->FindBool("dynamic", &dynamic) != B_OK) 1778 break; 1779 for (int i = 0; i < count; i++) { 1780 uint8 index = 0; 1781 if (msg->FindUInt8("index", i, &index) != B_OK) 1782 break; 1783 1784 SetTermColor(index, 1785 TermApp::DefaultPalette()[index], dynamic); 1786 } 1787 break; 1788 } 1789 case MSG_SET_CURSOR_STYLE: 1790 { 1791 int32 style = BLOCK_CURSOR; 1792 if (msg->FindInt32("style", &style) == B_OK) 1793 fCursorStyle = style; 1794 1795 bool blinking = fCursorBlinking; 1796 if (msg->FindBool("blinking", &blinking) == B_OK) 1797 SwitchCursorBlinking(blinking); 1798 1799 bool hidden = fCursorHidden; 1800 if (msg->FindBool("hidden", &hidden) == B_OK) 1801 fCursorHidden = hidden; 1802 break; 1803 } 1804 case MSG_REPORT_MOUSE_EVENT: 1805 { 1806 bool report; 1807 if (msg->FindBool("reportX10MouseEvent", &report) == B_OK) 1808 fReportX10MouseEvent = report; 1809 1810 if (msg->FindBool("reportNormalMouseEvent", &report) == B_OK) 1811 fReportNormalMouseEvent = report; 1812 1813 if (msg->FindBool("reportButtonMouseEvent", &report) == B_OK) 1814 fReportButtonMouseEvent = report; 1815 1816 if (msg->FindBool("reportAnyMouseEvent", &report) == B_OK) 1817 fReportAnyMouseEvent = report; 1818 break; 1819 } 1820 case MSG_REMOVE_RESIZE_VIEW_IF_NEEDED: 1821 { 1822 BPoint point; 1823 uint32 buttons; 1824 GetMouse(&point, &buttons, false); 1825 if (buttons != 0) 1826 break; 1827 1828 if (fResizeView != NULL) { 1829 fResizeView->RemoveSelf(); 1830 delete fResizeView; 1831 fResizeView = NULL; 1832 } 1833 delete fResizeRunner; 1834 fResizeRunner = NULL; 1835 break; 1836 } 1837 1838 case MSG_QUIT_TERMNAL: 1839 { 1840 int32 reason; 1841 if (msg->FindInt32("reason", &reason) != B_OK) 1842 reason = 0; 1843 if (fListener != NULL) 1844 fListener->NotifyTermViewQuit(this, reason); 1845 break; 1846 } 1847 default: 1848 BView::MessageReceived(msg); 1849 break; 1850 } 1851 } 1852 1853 1854 status_t 1855 TermView::GetSupportedSuites(BMessage *message) 1856 { 1857 BPropertyInfo propInfo(sPropList); 1858 message->AddString("suites", "suite/vnd.naan-termview"); 1859 message->AddFlat("messages", &propInfo); 1860 return BView::GetSupportedSuites(message); 1861 } 1862 1863 1864 void 1865 TermView::ScrollTo(BPoint where) 1866 { 1867 //debug_printf("TermView::ScrollTo(): %f -> %f\n", fScrollOffset, where.y); 1868 float diff = where.y - fScrollOffset; 1869 if (diff == 0) 1870 return; 1871 1872 float bottom = Bounds().bottom; 1873 int32 oldFirstLine = _LineAt(0); 1874 int32 oldLastLine = _LineAt(bottom); 1875 int32 newFirstLine = _LineAt(diff); 1876 int32 newLastLine = _LineAt(bottom + diff); 1877 1878 fScrollOffset = where.y; 1879 1880 // invalidate the current cursor position before scrolling 1881 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 1882 1883 // scroll contents 1884 BRect destRect(Frame().OffsetToCopy(Bounds().LeftTop())); 1885 BRect sourceRect(destRect.OffsetByCopy(0, diff)); 1886 //debug_printf("CopyBits(((%f, %f) - (%f, %f)) -> (%f, %f) - (%f, %f))\n", 1887 //sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom, 1888 //destRect.left, destRect.top, destRect.right, destRect.bottom); 1889 CopyBits(sourceRect, destRect); 1890 1891 // sync visible text buffer with text buffer 1892 if (newFirstLine != oldFirstLine || newLastLine != oldLastLine) { 1893 if (newFirstLine != oldFirstLine) 1894 { 1895 //debug_printf("fVisibleTextBuffer->ScrollBy(%ld)\n", newFirstLine - oldFirstLine); 1896 fVisibleTextBuffer->ScrollBy(newFirstLine - oldFirstLine); 1897 } 1898 TextBufferSyncLocker _(this); 1899 if (diff < 0) 1900 _SynchronizeWithTextBuffer(newFirstLine, oldFirstLine - 1); 1901 else 1902 _SynchronizeWithTextBuffer(oldLastLine + 1, newLastLine); 1903 } 1904 } 1905 1906 1907 void 1908 TermView::TargetedByScrollView(BScrollView *scrollView) 1909 { 1910 BView::TargetedByScrollView(scrollView); 1911 1912 SetScrollBar(scrollView ? scrollView->ScrollBar(B_VERTICAL) : NULL); 1913 } 1914 1915 1916 BHandler* 1917 TermView::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 1918 int32 what, const char* property) 1919 { 1920 BHandler* target = this; 1921 BPropertyInfo propInfo(sPropList); 1922 if (propInfo.FindMatch(message, index, specifier, what, property) < B_OK) { 1923 target = BView::ResolveSpecifier(message, index, specifier, what, 1924 property); 1925 } 1926 1927 return target; 1928 } 1929 1930 1931 void 1932 TermView::_SecondaryMouseButtonDropped(BMessage* msg) 1933 { 1934 // Launch menu to choose what is to do with the msg data 1935 BPoint point; 1936 if (msg->FindPoint("_drop_point_", &point) != B_OK) 1937 return; 1938 1939 BMessage* insertMessage = new BMessage(*msg); 1940 insertMessage->what = kSecondaryMouseDropAction; 1941 insertMessage->AddInt8("action", kInsert); 1942 1943 BMessage* cdMessage = new BMessage(*msg); 1944 cdMessage->what = kSecondaryMouseDropAction; 1945 cdMessage->AddInt8("action", kChangeDirectory); 1946 1947 BMessage* lnMessage = new BMessage(*msg); 1948 lnMessage->what = kSecondaryMouseDropAction; 1949 lnMessage->AddInt8("action", kLinkFiles); 1950 1951 BMessage* mvMessage = new BMessage(*msg); 1952 mvMessage->what = kSecondaryMouseDropAction; 1953 mvMessage->AddInt8("action", kMoveFiles); 1954 1955 BMessage* cpMessage = new BMessage(*msg); 1956 cpMessage->what = kSecondaryMouseDropAction; 1957 cpMessage->AddInt8("action", kCopyFiles); 1958 1959 BMenuItem* insertItem = new BMenuItem( 1960 B_TRANSLATE("Insert path"), insertMessage); 1961 BMenuItem* cdItem = new BMenuItem( 1962 B_TRANSLATE("Change directory"), cdMessage); 1963 BMenuItem* lnItem = new BMenuItem( 1964 B_TRANSLATE("Create link here"), lnMessage); 1965 BMenuItem* mvItem = new BMenuItem(B_TRANSLATE("Move here"), mvMessage); 1966 BMenuItem* cpItem = new BMenuItem(B_TRANSLATE("Copy here"), cpMessage); 1967 BMenuItem* chItem = new BMenuItem(B_TRANSLATE("Cancel"), NULL); 1968 1969 // if the refs point to different directorys disable the cd menu item 1970 bool differentDirs = false; 1971 BDirectory firstDir; 1972 entry_ref ref; 1973 int i = 0; 1974 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1975 BNode node(&ref); 1976 BEntry entry(&ref); 1977 BDirectory dir; 1978 if (node.IsDirectory()) 1979 dir.SetTo(&ref); 1980 else 1981 entry.GetParent(&dir); 1982 1983 if (i == 1) { 1984 node_ref nodeRef; 1985 dir.GetNodeRef(&nodeRef); 1986 firstDir.SetTo(&nodeRef); 1987 } else if (firstDir != dir) { 1988 differentDirs = true; 1989 break; 1990 } 1991 } 1992 if (differentDirs) 1993 cdItem->SetEnabled(false); 1994 1995 BPopUpMenu *menu = new BPopUpMenu( 1996 "Secondary mouse button drop menu"); 1997 menu->SetAsyncAutoDestruct(true); 1998 menu->AddItem(insertItem); 1999 menu->AddSeparatorItem(); 2000 menu->AddItem(cdItem); 2001 menu->AddItem(lnItem); 2002 menu->AddItem(mvItem); 2003 menu->AddItem(cpItem); 2004 menu->AddSeparatorItem(); 2005 menu->AddItem(chItem); 2006 menu->SetTargetForItems(this); 2007 menu->Go(point, true, true, true); 2008 } 2009 2010 2011 void 2012 TermView::_DoSecondaryMouseDropAction(BMessage* msg) 2013 { 2014 int8 action = -1; 2015 msg->FindInt8("action", &action); 2016 2017 BString outString = ""; 2018 BString itemString = ""; 2019 2020 switch (action) { 2021 case kInsert: 2022 break; 2023 case kChangeDirectory: 2024 outString = "cd "; 2025 break; 2026 case kLinkFiles: 2027 outString = "ln -s "; 2028 break; 2029 case kMoveFiles: 2030 outString = "mv "; 2031 break; 2032 case kCopyFiles: 2033 outString = "cp "; 2034 break; 2035 2036 default: 2037 return; 2038 } 2039 2040 bool listContainsDirectory = false; 2041 entry_ref ref; 2042 int32 i = 0; 2043 while (msg->FindRef("refs", i++, &ref) == B_OK) { 2044 BEntry ent(&ref); 2045 BNode node(&ref); 2046 BPath path(&ent); 2047 BString string(path.Path()); 2048 2049 if (node.IsDirectory()) 2050 listContainsDirectory = true; 2051 2052 if (i > 1) 2053 itemString += " "; 2054 2055 if (action == kChangeDirectory) { 2056 if (!node.IsDirectory()) { 2057 int32 slash = string.FindLast("/"); 2058 string.Truncate(slash); 2059 } 2060 string.CharacterEscape(kShellEscapeCharacters, '\\'); 2061 itemString += string; 2062 break; 2063 } 2064 string.CharacterEscape(kShellEscapeCharacters, '\\'); 2065 itemString += string; 2066 } 2067 2068 if (listContainsDirectory && action == kCopyFiles) 2069 outString += "-R "; 2070 2071 outString += itemString; 2072 2073 if (action == kLinkFiles || action == kMoveFiles || action == kCopyFiles) 2074 outString += " ."; 2075 2076 if (action != kInsert) 2077 outString += "\n"; 2078 2079 _WritePTY(outString.String(), outString.Length()); 2080 } 2081 2082 2083 //! Gets dropped file full path and display it at cursor position. 2084 void 2085 TermView::_DoFileDrop(entry_ref& ref) 2086 { 2087 BEntry ent(&ref); 2088 BPath path(&ent); 2089 BString string(path.Path()); 2090 2091 string.CharacterEscape(kShellEscapeCharacters, '\\'); 2092 _WritePTY(string.String(), string.Length()); 2093 } 2094 2095 2096 /*! Text buffer must already be locked. 2097 */ 2098 void 2099 TermView::_SynchronizeWithTextBuffer(int32 visibleDirtyTop, 2100 int32 visibleDirtyBottom) 2101 { 2102 TerminalBufferDirtyInfo& info = fTextBuffer->DirtyInfo(); 2103 int32 linesScrolled = info.linesScrolled; 2104 2105 //debug_printf("TermView::_SynchronizeWithTextBuffer(): dirty: %ld - %ld, " 2106 //"scrolled: %ld, visible dirty: %ld - %ld\n", info.dirtyTop, info.dirtyBottom, 2107 //info.linesScrolled, visibleDirtyTop, visibleDirtyBottom); 2108 2109 bigtime_t now = system_time(); 2110 bigtime_t timeElapsed = now - fLastSyncTime; 2111 if (timeElapsed > 2 * kSyncUpdateGranularity) { 2112 // last sync was ages ago 2113 fLastSyncTime = now; 2114 fScrolledSinceLastSync = linesScrolled; 2115 } 2116 2117 if (fSyncRunner == NULL) { 2118 // We consider clocked syncing when more than a full screen height has 2119 // been scrolled in less than a sync update period. Once we're 2120 // actively considering it, the same condition will convince us to 2121 // actually do it. 2122 if (fScrolledSinceLastSync + linesScrolled <= fRows) { 2123 // Condition doesn't hold yet. Reset if time is up, or otherwise 2124 // keep counting. 2125 if (timeElapsed > kSyncUpdateGranularity) { 2126 fConsiderClockedSync = false; 2127 fLastSyncTime = now; 2128 fScrolledSinceLastSync = linesScrolled; 2129 } else 2130 fScrolledSinceLastSync += linesScrolled; 2131 } else if (fConsiderClockedSync) { 2132 // We are convinced -- create the sync runner. 2133 fLastSyncTime = now; 2134 fScrolledSinceLastSync = 0; 2135 2136 BMessage message(MSG_TERMINAL_BUFFER_CHANGED); 2137 fSyncRunner = new(std::nothrow) BMessageRunner(BMessenger(this), 2138 &message, kSyncUpdateGranularity); 2139 if (fSyncRunner != NULL && fSyncRunner->InitCheck() == B_OK) 2140 return; 2141 2142 delete fSyncRunner; 2143 fSyncRunner = NULL; 2144 } else { 2145 // Looks interesting so far. Reset the counts and consider clocked 2146 // syncing. 2147 fConsiderClockedSync = true; 2148 fLastSyncTime = now; 2149 fScrolledSinceLastSync = 0; 2150 } 2151 } else if (timeElapsed < kSyncUpdateGranularity) { 2152 // sync time not passed yet -- keep counting 2153 fScrolledSinceLastSync += linesScrolled; 2154 return; 2155 } 2156 2157 if (fScrolledSinceLastSync + linesScrolled <= fRows) { 2158 // time's up, but not enough happened 2159 delete fSyncRunner; 2160 fSyncRunner = NULL; 2161 fLastSyncTime = now; 2162 fScrolledSinceLastSync = linesScrolled; 2163 } else { 2164 // Things are still rolling, but the sync time's up. 2165 fLastSyncTime = now; 2166 fScrolledSinceLastSync = 0; 2167 } 2168 2169 fVisibleTextBufferChanged = true; 2170 2171 // Simple case first -- complete invalidation. 2172 if (info.invalidateAll) { 2173 Invalidate(); 2174 _UpdateScrollBarRange(); 2175 _Deselect(); 2176 2177 fCursor = fTextBuffer->Cursor(); 2178 _ActivateCursor(false); 2179 2180 int32 offset = _LineAt(0); 2181 fVisibleTextBuffer->SynchronizeWith(fTextBuffer, offset, offset, 2182 offset + fTextBuffer->Height() + 2); 2183 2184 info.Reset(); 2185 return; 2186 } 2187 2188 BRect bounds = Bounds(); 2189 int32 firstVisible = _LineAt(0); 2190 int32 lastVisible = _LineAt(bounds.bottom); 2191 int32 historySize = fTextBuffer->HistorySize(); 2192 2193 bool doScroll = false; 2194 if (linesScrolled > 0) { 2195 _UpdateScrollBarRange(); 2196 2197 visibleDirtyTop -= linesScrolled; 2198 visibleDirtyBottom -= linesScrolled; 2199 2200 if (firstVisible < 0) { 2201 firstVisible -= linesScrolled; 2202 lastVisible -= linesScrolled; 2203 2204 float scrollOffset; 2205 if (firstVisible < -historySize) { 2206 firstVisible = -historySize; 2207 doScroll = true; 2208 scrollOffset = -historySize * fFontHeight; 2209 // We need to invalidate the lower linesScrolled lines of the 2210 // visible text buffer, since those will be scrolled up and 2211 // need to be replaced. We just use visibleDirty{Top,Bottom} 2212 // for that purpose. Unless invoked from ScrollTo() (i.e. 2213 // user-initiated scrolling) those are unused. In the unlikely 2214 // case that the user is scrolling at the same time we may 2215 // invalidate too many lines, since we have to extend the given 2216 // region. 2217 // Note that in the firstVisible == 0 case the new lines are 2218 // already in the dirty region, so they will be updated anyway. 2219 if (visibleDirtyTop <= visibleDirtyBottom) { 2220 if (lastVisible < visibleDirtyTop) 2221 visibleDirtyTop = lastVisible; 2222 if (visibleDirtyBottom < lastVisible + linesScrolled) 2223 visibleDirtyBottom = lastVisible + linesScrolled; 2224 } else { 2225 visibleDirtyTop = lastVisible + 1; 2226 visibleDirtyBottom = lastVisible + linesScrolled; 2227 } 2228 } else 2229 scrollOffset = fScrollOffset - linesScrolled * fFontHeight; 2230 2231 _ScrollTo(scrollOffset, false); 2232 } else 2233 doScroll = true; 2234 2235 if (doScroll && lastVisible >= firstVisible 2236 && !(info.IsDirtyRegionValid() && firstVisible >= info.dirtyTop 2237 && lastVisible <= info.dirtyBottom)) { 2238 // scroll manually 2239 float scrollBy = linesScrolled * fFontHeight; 2240 BRect destRect(Frame().OffsetToCopy(B_ORIGIN)); 2241 BRect sourceRect(destRect.OffsetByCopy(0, scrollBy)); 2242 2243 // invalidate the current cursor position before scrolling 2244 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 2245 2246 //debug_printf("CopyBits(((%f, %f) - (%f, %f)) -> (%f, %f) - (%f, %f))\n", 2247 //sourceRect.left, sourceRect.top, sourceRect.right, sourceRect.bottom, 2248 //destRect.left, destRect.top, destRect.right, destRect.bottom); 2249 CopyBits(sourceRect, destRect); 2250 2251 fVisibleTextBuffer->ScrollBy(linesScrolled); 2252 } 2253 2254 // move highlights 2255 for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { 2256 if (highlight->IsEmpty()) 2257 continue; 2258 2259 highlight->ScrollRange(linesScrolled); 2260 if (highlight == &fSelection) { 2261 fInitialSelectionStart.y -= linesScrolled; 2262 fInitialSelectionEnd.y -= linesScrolled; 2263 } 2264 2265 if (highlight->Start().y < -historySize) { 2266 if (highlight == &fSelection) 2267 _Deselect(); 2268 else 2269 _ClearHighlight(highlight); 2270 } 2271 } 2272 } 2273 2274 // invalidate dirty region 2275 if (info.IsDirtyRegionValid()) { 2276 _InvalidateTextRect(0, info.dirtyTop, fTextBuffer->Width() - 1, 2277 info.dirtyBottom); 2278 2279 // clear the selection, if affected 2280 if (!fSelection.IsEmpty()) { 2281 // TODO: We're clearing the selection more often than necessary -- 2282 // to avoid that, we'd also need to track the x coordinates of the 2283 // dirty range. 2284 int32 selectionBottom = fSelection.End().x > 0 2285 ? fSelection.End().y : fSelection.End().y - 1; 2286 if (fSelection.Start().y <= info.dirtyBottom 2287 && info.dirtyTop <= selectionBottom) { 2288 _Deselect(); 2289 } 2290 } 2291 } 2292 2293 if (visibleDirtyTop <= visibleDirtyBottom) 2294 info.ExtendDirtyRegion(visibleDirtyTop, visibleDirtyBottom); 2295 2296 if (linesScrolled != 0 || info.IsDirtyRegionValid()) { 2297 fVisibleTextBuffer->SynchronizeWith(fTextBuffer, firstVisible, 2298 info.dirtyTop, info.dirtyBottom); 2299 } 2300 2301 // invalidate cursor, if it changed 2302 TermPos cursor = fTextBuffer->Cursor(); 2303 if (fCursor != cursor || linesScrolled != 0) { 2304 // Before we scrolled we did already invalidate the old cursor. 2305 if (!doScroll) 2306 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 2307 fCursor = cursor; 2308 _InvalidateTextRect(fCursor.x, fCursor.y, fCursor.x, fCursor.y); 2309 _ActivateCursor(false); 2310 } 2311 2312 info.Reset(); 2313 } 2314 2315 2316 void 2317 TermView::_VisibleTextBufferChanged() 2318 { 2319 if (!fVisibleTextBufferChanged) 2320 return; 2321 2322 fVisibleTextBufferChanged = false; 2323 fActiveState->VisibleTextBufferChanged(); 2324 } 2325 2326 2327 /*! Write strings to PTY device. If encoding system isn't UTF8, change 2328 encoding to UTF8 before writing PTY. 2329 */ 2330 void 2331 TermView::_WritePTY(const char* text, int32 numBytes) 2332 { 2333 if (fEncoding != M_UTF8) { 2334 while (numBytes > 0) { 2335 char buffer[1024]; 2336 int32 bufferSize = sizeof(buffer); 2337 int32 sourceSize = numBytes; 2338 int32 state = 0; 2339 if (convert_to_utf8(fEncoding, text, &sourceSize, buffer, 2340 &bufferSize, &state) != B_OK || bufferSize == 0) { 2341 break; 2342 } 2343 2344 fShell->Write(buffer, bufferSize); 2345 text += sourceSize; 2346 numBytes -= sourceSize; 2347 } 2348 } else { 2349 fShell->Write(text, numBytes); 2350 } 2351 } 2352 2353 2354 //! Returns the square of the actual pixel distance between both points 2355 float 2356 TermView::_MouseDistanceSinceLastClick(BPoint where) 2357 { 2358 return (fLastClickPoint.x - where.x) * (fLastClickPoint.x - where.x) 2359 + (fLastClickPoint.y - where.y) * (fLastClickPoint.y - where.y); 2360 } 2361 2362 2363 void 2364 TermView::_SendMouseEvent(int32 buttons, int32 mode, int32 x, int32 y, 2365 bool motion) 2366 { 2367 char xtermButtons; 2368 if (buttons == B_PRIMARY_MOUSE_BUTTON) 2369 xtermButtons = 32 + 0; 2370 else if (buttons == B_SECONDARY_MOUSE_BUTTON) 2371 xtermButtons = 32 + 1; 2372 else if (buttons == B_TERTIARY_MOUSE_BUTTON) 2373 xtermButtons = 32 + 2; 2374 else 2375 xtermButtons = 32 + 3; 2376 2377 if (motion) 2378 xtermButtons += 32; 2379 2380 char xtermX = x + 1 + 32; 2381 char xtermY = y + 1 + 32; 2382 2383 char destBuffer[6]; 2384 destBuffer[0] = '\033'; 2385 destBuffer[1] = '['; 2386 destBuffer[2] = 'M'; 2387 destBuffer[3] = xtermButtons; 2388 destBuffer[4] = xtermX; 2389 destBuffer[5] = xtermY; 2390 fShell->Write(destBuffer, 6); 2391 } 2392 2393 2394 void 2395 TermView::MouseDown(BPoint where) 2396 { 2397 if (!IsFocus()) 2398 MakeFocus(); 2399 2400 _UpdateModifiers(); 2401 2402 BMessage* currentMessage = Window()->CurrentMessage(); 2403 int32 buttons = currentMessage->GetInt32("buttons", 0); 2404 2405 fActiveState->MouseDown(where, buttons, fModifiers); 2406 2407 fMouseButtons = buttons; 2408 fLastClickPoint = where; 2409 } 2410 2411 2412 void 2413 TermView::MouseMoved(BPoint where, uint32 transit, const BMessage *message) 2414 { 2415 _UpdateModifiers(); 2416 2417 fActiveState->MouseMoved(where, transit, message, fModifiers); 2418 } 2419 2420 2421 void 2422 TermView::MouseUp(BPoint where) 2423 { 2424 _UpdateModifiers(); 2425 2426 int32 buttons = Window()->CurrentMessage()->GetInt32("buttons", 0); 2427 2428 fActiveState->MouseUp(where, buttons); 2429 2430 fMouseButtons = buttons; 2431 } 2432 2433 2434 //! Select a range of text. 2435 void 2436 TermView::_Select(TermPos start, TermPos end, bool inclusive, 2437 bool setInitialSelection) 2438 { 2439 TextBufferSyncLocker _(this); 2440 2441 _SynchronizeWithTextBuffer(0, -1); 2442 2443 if (end < start) 2444 std::swap(start, end); 2445 2446 if (inclusive) 2447 end.x++; 2448 2449 //debug_printf("TermView::_Select(): (%ld, %ld) - (%ld, %ld)\n", start.x, 2450 //start.y, end.x, end.y); 2451 2452 if (start.x < 0) 2453 start.x = 0; 2454 if (end.x >= fColumns) 2455 end.x = fColumns; 2456 2457 TermPos minPos(0, -fTextBuffer->HistorySize()); 2458 TermPos maxPos(0, fTextBuffer->Height()); 2459 start = restrict_value(start, minPos, maxPos); 2460 end = restrict_value(end, minPos, maxPos); 2461 2462 // if the end is past the end of the line, select the line break, too 2463 if (fTextBuffer->LineLength(end.y) < end.x 2464 && end.y < fTextBuffer->Height()) { 2465 end.y++; 2466 end.x = 0; 2467 } 2468 2469 if (fTextBuffer->IsFullWidthChar(start.y, start.x)) { 2470 start.x--; 2471 if (start.x < 0) 2472 start.x = 0; 2473 } 2474 2475 if (fTextBuffer->IsFullWidthChar(end.y, end.x)) { 2476 end.x++; 2477 if (end.x >= fColumns) 2478 end.x = fColumns; 2479 } 2480 2481 if (!fSelection.IsEmpty()) 2482 _InvalidateTextRange(fSelection.Start(), fSelection.End()); 2483 2484 fSelection.SetRange(start, end); 2485 2486 if (setInitialSelection) { 2487 fInitialSelectionStart = fSelection.Start(); 2488 fInitialSelectionEnd = fSelection.End(); 2489 } 2490 2491 _InvalidateTextRange(fSelection.Start(), fSelection.End()); 2492 } 2493 2494 2495 //! Extend selection (shift + mouse click). 2496 void 2497 TermView::_ExtendSelection(TermPos pos, bool inclusive, 2498 bool useInitialSelection) 2499 { 2500 if (!useInitialSelection && !_HasSelection()) 2501 return; 2502 2503 TermPos start = fSelection.Start(); 2504 TermPos end = fSelection.End(); 2505 2506 if (useInitialSelection) { 2507 start = fInitialSelectionStart; 2508 end = fInitialSelectionEnd; 2509 } 2510 2511 if (inclusive) { 2512 if (pos >= start && pos >= end) 2513 pos.x++; 2514 } 2515 2516 if (pos < start) 2517 _Select(pos, end, false, !useInitialSelection); 2518 else if (pos > end) 2519 _Select(start, pos, false, !useInitialSelection); 2520 else if (useInitialSelection) 2521 _Select(start, end, false, false); 2522 } 2523 2524 2525 // clear the selection. 2526 void 2527 TermView::_Deselect() 2528 { 2529 //debug_printf("TermView::_Deselect(): has selection: %d\n", _HasSelection()); 2530 if (_ClearHighlight(&fSelection)) { 2531 fInitialSelectionStart.SetTo(0, 0); 2532 fInitialSelectionEnd.SetTo(0, 0); 2533 } 2534 } 2535 2536 2537 bool 2538 TermView::_HasSelection() const 2539 { 2540 return !fSelection.IsEmpty(); 2541 } 2542 2543 2544 void 2545 TermView::_SelectWord(BPoint where, bool extend, bool useInitialSelection) 2546 { 2547 BAutolock _(fTextBuffer); 2548 2549 TermPos pos = _ConvertToTerminal(where); 2550 TermPos start, end; 2551 if (!fTextBuffer->FindWord(pos, fCharClassifier, true, start, end)) 2552 return; 2553 2554 if (extend) { 2555 if (start 2556 < (useInitialSelection 2557 ? fInitialSelectionStart : fSelection.Start())) { 2558 _ExtendSelection(start, false, useInitialSelection); 2559 } else if (end 2560 > (useInitialSelection 2561 ? fInitialSelectionEnd : fSelection.End())) { 2562 _ExtendSelection(end, false, useInitialSelection); 2563 } else if (useInitialSelection) 2564 _Select(start, end, false, false); 2565 } else 2566 _Select(start, end, false, !useInitialSelection); 2567 } 2568 2569 2570 void 2571 TermView::_SelectLine(BPoint where, bool extend, bool useInitialSelection) 2572 { 2573 TermPos start = TermPos(0, _ConvertToTerminal(where).y); 2574 TermPos end = TermPos(0, start.y + 1); 2575 2576 if (extend) { 2577 if (start 2578 < (useInitialSelection 2579 ? fInitialSelectionStart : fSelection.Start())) { 2580 _ExtendSelection(start, false, useInitialSelection); 2581 } else if (end 2582 > (useInitialSelection 2583 ? fInitialSelectionEnd : fSelection.End())) { 2584 _ExtendSelection(end, false, useInitialSelection); 2585 } else if (useInitialSelection) 2586 _Select(start, end, false, false); 2587 } else 2588 _Select(start, end, false, !useInitialSelection); 2589 } 2590 2591 2592 void 2593 TermView::_AddHighlight(Highlight* highlight) 2594 { 2595 fHighlights.AddItem(highlight); 2596 2597 if (!highlight->IsEmpty()) 2598 _InvalidateTextRange(highlight->Start(), highlight->End()); 2599 } 2600 2601 2602 void 2603 TermView::_RemoveHighlight(Highlight* highlight) 2604 { 2605 if (!highlight->IsEmpty()) 2606 _InvalidateTextRange(highlight->Start(), highlight->End()); 2607 2608 fHighlights.RemoveItem(highlight); 2609 } 2610 2611 2612 bool 2613 TermView::_ClearHighlight(Highlight* highlight) 2614 { 2615 if (highlight->IsEmpty()) 2616 return false; 2617 2618 _InvalidateTextRange(highlight->Start(), highlight->End()); 2619 2620 highlight->SetRange(TermPos(0, 0), TermPos(0, 0)); 2621 return true; 2622 } 2623 2624 2625 TermView::Highlight* 2626 TermView::_CheckHighlightRegion(const TermPos &pos) const 2627 { 2628 for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { 2629 if (highlight->RangeContains(pos)) 2630 return highlight; 2631 } 2632 2633 return NULL; 2634 } 2635 2636 2637 TermView::Highlight* 2638 TermView::_CheckHighlightRegion(int32 row, int32 firstColumn, 2639 int32& lastColumn) const 2640 { 2641 Highlight* nextHighlight = NULL; 2642 2643 for (int32 i = 0; Highlight* highlight = fHighlights.ItemAt(i); i++) { 2644 if (highlight->IsEmpty()) 2645 continue; 2646 2647 if (row == highlight->Start().y && firstColumn < highlight->Start().x 2648 && lastColumn >= highlight->Start().x) { 2649 // region starts before the highlight, but intersects with it 2650 if (nextHighlight == NULL 2651 || highlight->Start().x < nextHighlight->Start().x) { 2652 nextHighlight = highlight; 2653 } 2654 continue; 2655 } 2656 2657 if (row == highlight->End().y && firstColumn < highlight->End().x 2658 && lastColumn >= highlight->End().x) { 2659 // region starts in the highlight, but exceeds the end 2660 lastColumn = highlight->End().x - 1; 2661 return highlight; 2662 } 2663 2664 TermPos pos(firstColumn, row); 2665 if (highlight->RangeContains(pos)) 2666 return highlight; 2667 } 2668 2669 if (nextHighlight != NULL) 2670 lastColumn = nextHighlight->Start().x - 1; 2671 return NULL; 2672 } 2673 2674 2675 void 2676 TermView::GetFrameSize(float *width, float *height) 2677 { 2678 int32 historySize; 2679 { 2680 BAutolock _(fTextBuffer); 2681 historySize = fTextBuffer->HistorySize(); 2682 } 2683 2684 if (width != NULL) 2685 *width = fColumns * fFontWidth; 2686 2687 if (height != NULL) 2688 *height = (fRows + historySize) * fFontHeight; 2689 } 2690 2691 2692 // Find a string, and select it if found 2693 bool 2694 TermView::Find(const BString &str, bool forwardSearch, bool matchCase, 2695 bool matchWord) 2696 { 2697 TextBufferSyncLocker _(this); 2698 _SynchronizeWithTextBuffer(0, -1); 2699 2700 TermPos start; 2701 if (_HasSelection()) { 2702 if (forwardSearch) 2703 start = fSelection.End(); 2704 else 2705 start = fSelection.Start(); 2706 } else { 2707 // search from the very beginning/end 2708 if (forwardSearch) 2709 start = TermPos(0, -fTextBuffer->HistorySize()); 2710 else 2711 start = TermPos(0, fTextBuffer->Height()); 2712 } 2713 2714 TermPos matchStart, matchEnd; 2715 if (!fTextBuffer->Find(str.String(), start, forwardSearch, matchCase, 2716 matchWord, matchStart, matchEnd)) { 2717 return false; 2718 } 2719 2720 _Select(matchStart, matchEnd, false, true); 2721 _ScrollToRange(fSelection.Start(), fSelection.End()); 2722 2723 return true; 2724 } 2725 2726 2727 //! Get the selected text and copy to str 2728 void 2729 TermView::GetSelection(BString &str) 2730 { 2731 str.SetTo(""); 2732 BAutolock _(fTextBuffer); 2733 fTextBuffer->GetStringFromRegion(str, fSelection.Start(), fSelection.End()); 2734 } 2735 2736 2737 bool 2738 TermView::CheckShellGone() const 2739 { 2740 if (!fShell) 2741 return false; 2742 2743 // check, if the shell does still live 2744 pid_t pid = fShell->ProcessID(); 2745 team_info info; 2746 return get_team_info(pid, &info) == B_BAD_TEAM_ID; 2747 } 2748 2749 2750 void 2751 TermView::InitiateDrag() 2752 { 2753 BAutolock _(fTextBuffer); 2754 2755 BString copyStr(""); 2756 fTextBuffer->GetStringFromRegion(copyStr, fSelection.Start(), 2757 fSelection.End()); 2758 2759 BMessage message(B_MIME_DATA); 2760 message.AddData("text/plain", B_MIME_TYPE, copyStr.String(), 2761 copyStr.Length()); 2762 2763 BPoint start = _ConvertFromTerminal(fSelection.Start()); 2764 BPoint end = _ConvertFromTerminal(fSelection.End()); 2765 2766 BRect rect; 2767 if (fSelection.Start().y == fSelection.End().y) 2768 rect.Set(start.x, start.y, end.x + fFontWidth, end.y + fFontHeight); 2769 else 2770 rect.Set(0, start.y, fColumns * fFontWidth, end.y + fFontHeight); 2771 2772 rect = rect & Bounds(); 2773 2774 DragMessage(&message, rect); 2775 } 2776 2777 2778 void 2779 TermView::_ScrollTo(float y, bool scrollGfx) 2780 { 2781 if (!scrollGfx) 2782 fScrollOffset = y; 2783 2784 if (fScrollBar != NULL) 2785 fScrollBar->SetValue(y); 2786 else 2787 ScrollTo(BPoint(0, y)); 2788 } 2789 2790 2791 void 2792 TermView::_ScrollToRange(TermPos start, TermPos end) 2793 { 2794 if (start > end) 2795 std::swap(start, end); 2796 2797 float startY = _LineOffset(start.y); 2798 float endY = _LineOffset(end.y) + fFontHeight - 1; 2799 float height = Bounds().Height(); 2800 2801 if (endY - startY > height) { 2802 // The range is greater than the height. Scroll to the closest border. 2803 2804 // already as good as it gets? 2805 if (startY <= 0 && endY >= height) 2806 return; 2807 2808 if (startY > 0) { 2809 // scroll down to align the start with the top of the view 2810 _ScrollTo(fScrollOffset + startY, true); 2811 } else { 2812 // scroll up to align the end with the bottom of the view 2813 _ScrollTo(fScrollOffset + endY - height, true); 2814 } 2815 } else { 2816 // The range is smaller than the height. 2817 2818 // already visible? 2819 if (startY >= 0 && endY <= height) 2820 return; 2821 2822 if (startY < 0) { 2823 // scroll up to make the start visible 2824 _ScrollTo(fScrollOffset + startY, true); 2825 } else { 2826 // scroll down to make the end visible 2827 _ScrollTo(fScrollOffset + endY - height, true); 2828 } 2829 } 2830 } 2831 2832 2833 void 2834 TermView::DisableResizeView(int32 disableCount) 2835 { 2836 fResizeViewDisableCount += disableCount; 2837 } 2838 2839 2840 void 2841 TermView::_DrawInlineMethodString() 2842 { 2843 if (!fInline || !fInline->String()) 2844 return; 2845 2846 const int32 numChars = BString(fInline->String()).CountChars(); 2847 2848 BPoint startPoint = _ConvertFromTerminal(fCursor); 2849 BPoint endPoint = startPoint; 2850 endPoint.x += fFontWidth * numChars; 2851 endPoint.y += fFontHeight + 1; 2852 2853 BRect eraseRect(startPoint, endPoint); 2854 2855 PushState(); 2856 SetHighColor(fTextForeColor); 2857 FillRect(eraseRect); 2858 PopState(); 2859 2860 BPoint loc = _ConvertFromTerminal(fCursor); 2861 loc.y += fFontHeight; 2862 SetFont(&fHalfFont); 2863 SetHighColor(fTextBackColor); 2864 SetLowColor(fTextForeColor); 2865 DrawString(fInline->String(), loc); 2866 } 2867 2868 2869 void 2870 TermView::_HandleInputMethodChanged(BMessage *message) 2871 { 2872 const char *string = NULL; 2873 if (message->FindString("be:string", &string) < B_OK || string == NULL) 2874 return; 2875 2876 _ActivateCursor(false); 2877 2878 if (IsFocus()) 2879 be_app->ObscureCursor(); 2880 2881 // If we find the "be:confirmed" boolean (and the boolean is true), 2882 // it means it's over for now, so the current InlineInput object 2883 // should become inactive. We will probably receive a 2884 // B_INPUT_METHOD_STOPPED message after this one. 2885 bool confirmed; 2886 if (message->FindBool("be:confirmed", &confirmed) != B_OK) 2887 confirmed = false; 2888 2889 fInline->SetString(""); 2890 2891 Invalidate(); 2892 // TODO: Debug only 2893 snooze(100000); 2894 2895 fInline->SetString(string); 2896 fInline->ResetClauses(); 2897 2898 if (!confirmed && !fInline->IsActive()) 2899 fInline->SetActive(true); 2900 2901 // Get the clauses, and pass them to the InlineInput object 2902 // TODO: Find out if what we did it's ok, currently we don't consider 2903 // clauses at all, while the bebook says we should; though the visual 2904 // effect we obtained seems correct. Weird. 2905 int32 clauseCount = 0; 2906 int32 clauseStart; 2907 int32 clauseEnd; 2908 while (message->FindInt32("be:clause_start", clauseCount, &clauseStart) 2909 == B_OK 2910 && message->FindInt32("be:clause_end", clauseCount, &clauseEnd) 2911 == B_OK) { 2912 if (!fInline->AddClause(clauseStart, clauseEnd)) 2913 break; 2914 clauseCount++; 2915 } 2916 2917 if (confirmed) { 2918 fInline->SetString(""); 2919 _ActivateCursor(true); 2920 2921 // now we need to feed ourselves the individual characters as if the 2922 // user would have pressed them now - this lets KeyDown() pick out all 2923 // the special characters like B_BACKSPACE, cursor keys and the like: 2924 const char* currPos = string; 2925 const char* prevPos = currPos; 2926 while (*currPos != '\0') { 2927 if ((*currPos & 0xC0) == 0xC0) { 2928 // found the start of an UTF-8 char, we collect while it lasts 2929 ++currPos; 2930 while ((*currPos & 0xC0) == 0x80) 2931 ++currPos; 2932 } else if ((*currPos & 0xC0) == 0x80) { 2933 // illegal: character starts with utf-8 intermediate byte, skip it 2934 prevPos = ++currPos; 2935 } else { 2936 // single byte character/code, just feed that 2937 ++currPos; 2938 } 2939 KeyDown(prevPos, currPos - prevPos); 2940 prevPos = currPos; 2941 } 2942 2943 Invalidate(); 2944 } else { 2945 // temporarily show transient state of inline input 2946 int32 selectionStart = 0; 2947 int32 selectionEnd = 0; 2948 message->FindInt32("be:selection", 0, &selectionStart); 2949 message->FindInt32("be:selection", 1, &selectionEnd); 2950 2951 fInline->SetSelectionOffset(selectionStart); 2952 fInline->SetSelectionLength(selectionEnd - selectionStart); 2953 Invalidate(); 2954 } 2955 } 2956 2957 2958 void 2959 TermView::_HandleInputMethodLocationRequest() 2960 { 2961 BMessage message(B_INPUT_METHOD_EVENT); 2962 message.AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST); 2963 2964 BString string(fInline->String()); 2965 2966 const int32 &limit = string.CountChars(); 2967 BPoint where = _ConvertFromTerminal(fCursor); 2968 where.y += fFontHeight; 2969 2970 for (int32 i = 0; i < limit; i++) { 2971 // Add the location of the UTF8 characters 2972 2973 where.x += fFontWidth; 2974 ConvertToScreen(&where); 2975 2976 message.AddPoint("be:location_reply", where); 2977 message.AddFloat("be:height_reply", fFontHeight); 2978 } 2979 2980 fInline->Method()->SendMessage(&message); 2981 } 2982 2983 2984 void 2985 TermView::_CancelInputMethod() 2986 { 2987 if (!fInline) 2988 return; 2989 2990 InlineInput *inlineInput = fInline; 2991 fInline = NULL; 2992 2993 if (inlineInput->IsActive() && Window()) { 2994 Invalidate(); 2995 2996 BMessage message(B_INPUT_METHOD_EVENT); 2997 message.AddInt32("be:opcode", B_INPUT_METHOD_STOPPED); 2998 inlineInput->Method()->SendMessage(&message); 2999 } 3000 3001 delete inlineInput; 3002 } 3003 3004 3005 void 3006 TermView::_UpdateModifiers() 3007 { 3008 // TODO: This method is a general work-around for missing or out-of-order 3009 // B_MODIFIERS_CHANGED messages. This should really be fixed where it is 3010 // broken (app server?). 3011 int32 oldModifiers = fModifiers; 3012 fModifiers = modifiers(); 3013 if (fModifiers != oldModifiers && fActiveState != NULL) 3014 fActiveState->ModifiersChanged(oldModifiers, fModifiers); 3015 } 3016 3017 3018 void 3019 TermView::_NextState(State* state) 3020 { 3021 if (state != fActiveState) { 3022 if (fActiveState != NULL) 3023 fActiveState->Exited(); 3024 fActiveState = state; 3025 fActiveState->Entered(); 3026 } 3027 } 3028 3029 3030 // #pragma mark - Listener 3031 3032 3033 TermView::Listener::~Listener() 3034 { 3035 } 3036 3037 3038 void 3039 TermView::Listener::NotifyTermViewQuit(TermView* view, int32 reason) 3040 { 3041 } 3042 3043 3044 void 3045 TermView::Listener::SetTermViewTitle(TermView* view, const char* title) 3046 { 3047 } 3048 3049 3050 void 3051 TermView::Listener::PreviousTermView(TermView* view) 3052 { 3053 } 3054 3055 3056 void 3057 TermView::Listener::NextTermView(TermView* view) 3058 { 3059 } 3060 3061 3062 // #pragma mark - 3063 3064 3065 #ifdef USE_DEBUG_SNAPSHOTS 3066 3067 void 3068 TermView::MakeDebugSnapshots() 3069 { 3070 BAutolock _(fTextBuffer); 3071 time_t timeStamp = time(NULL); 3072 fTextBuffer->MakeLinesSnapshots(timeStamp, ".TextBuffer.dump"); 3073 fVisibleTextBuffer->MakeLinesSnapshots(timeStamp, ".VisualTextBuffer.dump"); 3074 } 3075 3076 3077 void 3078 TermView::StartStopDebugCapture() 3079 { 3080 BAutolock _(fTextBuffer); 3081 fTextBuffer->StartStopDebugCapture(); 3082 } 3083 3084 #endif 3085