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