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