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 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(int32 x1, int32 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 int x2 = x1 + fFontWidth * width; 972 int 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 if (index < kTermColorCount) 1785 SetTermColor(index, 1786 TermApp::DefaultPalette()[index], dynamic); 1787 } 1788 break; 1789 } 1790 case MSG_SET_CURSOR_STYLE: 1791 { 1792 int32 style = BLOCK_CURSOR; 1793 if (msg->FindInt32("style", &style) == B_OK) 1794 fCursorStyle = style; 1795 1796 bool blinking = fCursorBlinking; 1797 if (msg->FindBool("blinking", &blinking) == B_OK) 1798 SwitchCursorBlinking(blinking); 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