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