1 /* 2 * Copyright 2007 Haiku, Inc. 3 * Copyright (c) 2004 Daniel Furrer <assimil8or@users.sourceforge.net> 4 * Copyright (c) 2003-2004 Kian Duffy <myob@users.sourceforge.net> 5 * Copyright (C) 1998,99 Kazuho Okui and Takashi Murai. 6 * 7 * Distributed under the terms of the MIT license. 8 */ 9 10 #include "TermWindow.h" 11 12 #include <stdio.h> 13 #include <string.h> 14 #include <time.h> 15 16 #include <Alert.h> 17 #include <Application.h> 18 #include <Clipboard.h> 19 #include <Dragger.h> 20 #include <Menu.h> 21 #include <MenuBar.h> 22 #include <MenuItem.h> 23 #include <Path.h> 24 #include <PrintJob.h> 25 #include <Roster.h> 26 #include <Screen.h> 27 #include <ScrollBar.h> 28 #include <ScrollView.h> 29 #include <String.h> 30 31 #include "Arguments.h" 32 #include "Coding.h" 33 #include "MenuUtil.h" 34 #include "FindWindow.h" 35 #include "PrefWindow.h" 36 #include "PrefView.h" 37 #include "PrefHandler.h" 38 #include "SmartTabView.h" 39 #include "TermConst.h" 40 #include "TermScrollView.h" 41 #include "TermView.h" 42 43 44 const static int32 kMaxTabs = 6; 45 const static int32 kTermViewOffset = 3; 46 47 // messages constants 48 const static uint32 kNewTab = 'NTab'; 49 const static uint32 kCloseView = 'ClVw'; 50 const static uint32 kIncreaseFontSize = 'InFs'; 51 const static uint32 kDecreaseFontSize = 'DcFs'; 52 53 54 class CustomTermView : public TermView { 55 public: 56 CustomTermView(int32 rows, int32 columns, int32 argc, const char **argv, int32 historySize = 1000); 57 virtual void NotifyQuit(int32 reason); 58 virtual void SetTitle(const char *title); 59 }; 60 61 62 class TermViewContainerView : public BView { 63 public: 64 TermViewContainerView(TermView* termView) 65 : 66 BView(BRect(), "term view container", B_FOLLOW_ALL, 0), 67 fTermView(termView) 68 { 69 termView->MoveTo(kTermViewOffset, kTermViewOffset); 70 BRect frame(termView->Frame()); 71 ResizeTo(frame.right + kTermViewOffset, frame.bottom + kTermViewOffset); 72 AddChild(termView); 73 } 74 75 TermView* GetTermView() const { return fTermView; } 76 77 virtual void GetPreferredSize(float* _width, float* _height) 78 { 79 float width, height; 80 fTermView->GetPreferredSize(&width, &height); 81 *_width = width + 2 * kTermViewOffset; 82 *_height = height + 2 * kTermViewOffset; 83 } 84 85 private: 86 TermView* fTermView; 87 }; 88 89 90 struct TermWindow::Session { 91 int32 id; 92 BString name; 93 BString windowTitle; 94 TermViewContainerView* containerView; 95 96 Session(int32 id, TermViewContainerView* containerView) 97 : 98 id(id), 99 containerView(containerView) 100 { 101 name = "Shell "; 102 name << id; 103 } 104 }; 105 106 107 class TermWindow::TabView : public SmartTabView { 108 public: 109 TabView(TermWindow* window, BRect frame, const char *name) 110 : 111 SmartTabView(frame, name), 112 fWindow(window) 113 { 114 } 115 116 virtual void Select(int32 tab) 117 { 118 SmartTabView::Select(tab); 119 fWindow->SessionChanged(); 120 } 121 122 virtual void RemoveAndDeleteTab(int32 index) 123 { 124 fWindow->_RemoveTab(index); 125 } 126 127 private: 128 TermWindow* fWindow; 129 }; 130 131 132 TermWindow::TermWindow(BRect frame, const char* title, Arguments *args) 133 : 134 BWindow(frame, title, B_DOCUMENT_WINDOW, 135 B_CURRENT_WORKSPACE | B_QUIT_ON_WINDOW_CLOSE), 136 fInitialTitle(title), 137 fTabView(NULL), 138 fMenubar(NULL), 139 fFilemenu(NULL), 140 fEditmenu(NULL), 141 fEncodingmenu(NULL), 142 fHelpmenu(NULL), 143 fWindowSizeMenu(NULL), 144 fPrintSettings(NULL), 145 fPrefWindow(NULL), 146 fFindPanel(NULL), 147 fSavedFrame(0, 0, -1, -1), 148 fFindString(""), 149 fFindForwardMenuItem(NULL), 150 fFindBackwardMenuItem(NULL), 151 fFindSelection(false), 152 fForwardSearch(false), 153 fMatchCase(false), 154 fMatchWord(false) 155 { 156 _InitWindow(); 157 _AddTab(args); 158 } 159 160 161 TermWindow::~TermWindow() 162 { 163 if (fPrefWindow) 164 fPrefWindow->PostMessage(B_QUIT_REQUESTED); 165 166 if (fFindPanel && fFindPanel->Lock()) { 167 fFindPanel->Quit(); 168 fFindPanel = NULL; 169 } 170 171 PrefHandler::DeleteDefault(); 172 173 for (int32 i = 0; Session* session = (Session*)fSessions.ItemAt(i); i++) 174 delete session; 175 } 176 177 178 void 179 TermWindow::SetSessionWindowTitle(TermView* termView, const char* title) 180 { 181 int32 index = _IndexOfTermView(termView); 182 if (Session* session = (Session*)fSessions.ItemAt(index)) { 183 session->windowTitle = title; 184 BTab* tab = fTabView->TabAt(index); 185 tab->SetLabel(session->windowTitle.String()); 186 if (index == fTabView->Selection()) 187 SetTitle(session->windowTitle.String()); 188 } 189 } 190 191 192 void 193 TermWindow::SessionChanged() 194 { 195 int32 index = fTabView->Selection(); 196 if (Session* session = (Session*)fSessions.ItemAt(index)) 197 SetTitle(session->windowTitle.String()); 198 } 199 200 201 void 202 TermWindow::_InitWindow() 203 { 204 // make menu bar 205 _SetupMenu(); 206 207 AddShortcut('+', B_COMMAND_KEY, new BMessage(kIncreaseFontSize)); 208 AddShortcut('-', B_COMMAND_KEY, new BMessage(kDecreaseFontSize)); 209 210 BRect textFrame = Bounds(); 211 textFrame.top = fMenubar->Bounds().bottom + 1.0; 212 213 fTabView = new TabView(this, textFrame, "tab view"); 214 AddChild(fTabView); 215 216 // Make the scroll view one pixel wider than the tab view container view, so 217 // the scroll bar will look good. 218 fTabView->SetInsets(0, 0, -1, 0); 219 } 220 221 222 void 223 TermWindow::MenusBeginning() 224 { 225 // Syncronize Encode Menu Pop-up menu and Preference. 226 BMenuItem *item = fEncodingmenu->FindItem(EncodingAsString(_ActiveTermView()->Encoding())); 227 if (item != NULL) 228 item->SetMarked(true); 229 BWindow::MenusBeginning(); 230 } 231 232 233 void 234 TermWindow::_SetupMenu() 235 { 236 PrefHandler menuText; 237 238 LoadLocaleFile(&menuText); 239 240 // Menu bar object. 241 fMenubar = new BMenuBar(Bounds(), "mbar"); 242 243 // Make File Menu. 244 fFilemenu = new BMenu("Terminal"); 245 fFilemenu->AddItem(new BMenuItem("Switch Terminals", new BMessage(MENU_SWITCH_TERM),'G')); 246 fFilemenu->AddItem(new BMenuItem("New Terminal" B_UTF8_ELLIPSIS, new BMessage(MENU_NEW_TERM), 'N')); 247 fFilemenu->AddItem(new BMenuItem("New Tab", new BMessage(kNewTab), 'T')); 248 249 fFilemenu->AddSeparatorItem(); 250 fFilemenu->AddItem(new BMenuItem("Page Setup" B_UTF8_ELLIPSIS, new BMessage(MENU_PAGE_SETUP))); 251 fFilemenu->AddItem(new BMenuItem("Print", new BMessage(MENU_PRINT),'P')); 252 fFilemenu->AddSeparatorItem(); 253 fFilemenu->AddItem(new BMenuItem("About Terminal" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED))); 254 fFilemenu->AddSeparatorItem(); 255 fFilemenu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q')); 256 fMenubar->AddItem(fFilemenu); 257 258 // Make Edit Menu. 259 fEditmenu = new BMenu ("Edit"); 260 fEditmenu->AddItem (new BMenuItem ("Copy", new BMessage (B_COPY),'C')); 261 fEditmenu->AddItem (new BMenuItem ("Paste", new BMessage (B_PASTE),'V')); 262 fEditmenu->AddSeparatorItem(); 263 fEditmenu->AddItem (new BMenuItem ("Select All", new BMessage (B_SELECT_ALL), 'A')); 264 fEditmenu->AddItem (new BMenuItem ("Clear All", new BMessage (MENU_CLEAR_ALL), 'L')); 265 fEditmenu->AddSeparatorItem(); 266 fEditmenu->AddItem (new BMenuItem ("Find" B_UTF8_ELLIPSIS, new BMessage (MENU_FIND_STRING),'F')); 267 fFindBackwardMenuItem = new BMenuItem ("Find Backward", new BMessage (MENU_FIND_BACKWARD), '['); 268 fEditmenu->AddItem(fFindBackwardMenuItem); 269 fFindBackwardMenuItem->SetEnabled(false); 270 fFindForwardMenuItem = new BMenuItem ("Find Forward", new BMessage (MENU_FIND_FORWARD), ']'); 271 fEditmenu->AddItem (fFindForwardMenuItem); 272 fFindForwardMenuItem->SetEnabled(false); 273 274 fMenubar->AddItem (fEditmenu); 275 276 // Make Help Menu. 277 fHelpmenu = new BMenu("Settings"); 278 fWindowSizeMenu = new BMenu("Window Size"); 279 _BuildWindowSizeMenu(fWindowSizeMenu); 280 281 fEncodingmenu = new BMenu("Text Encoding"); 282 fEncodingmenu->SetRadioMode(true); 283 MakeEncodingMenu(fEncodingmenu, true); 284 fHelpmenu->AddItem(fWindowSizeMenu); 285 fHelpmenu->AddItem(fEncodingmenu); 286 fHelpmenu->AddSeparatorItem(); 287 fHelpmenu->AddItem(new BMenuItem("Preferences" B_UTF8_ELLIPSIS, new BMessage(MENU_PREF_OPEN))); 288 fHelpmenu->AddSeparatorItem(); 289 fHelpmenu->AddItem(new BMenuItem("Save as default", new BMessage(SAVE_AS_DEFAULT))); 290 fMenubar->AddItem(fHelpmenu); 291 292 AddChild(fMenubar); 293 } 294 295 296 void 297 TermWindow::_GetPreferredFont(BFont &font) 298 { 299 const char *family = PrefHandler::Default()->getString(PREF_HALF_FONT_FAMILY); 300 301 font.SetFamilyAndStyle(family, NULL); 302 float size = PrefHandler::Default()->getFloat(PREF_HALF_FONT_SIZE); 303 if (size < 6.0f) 304 size = 6.0f; 305 font.SetSize(size); 306 font.SetSpacing(B_FIXED_SPACING); 307 } 308 309 310 void 311 TermWindow::MessageReceived(BMessage *message) 312 { 313 int32 encodingId; 314 bool findresult; 315 316 switch (message->what) { 317 case B_COPY: 318 _ActiveTermView()->Copy(be_clipboard); 319 break; 320 321 case B_PASTE: 322 _ActiveTermView()->Paste(be_clipboard); 323 break; 324 325 case B_SELECT_ALL: 326 _ActiveTermView()->SelectAll(); 327 break; 328 329 case B_ABOUT_REQUESTED: 330 be_app->PostMessage(B_ABOUT_REQUESTED); 331 break; 332 333 case MENU_CLEAR_ALL: 334 _ActiveTermView()->Clear(); 335 break; 336 337 case MENU_SWITCH_TERM: 338 be_app->PostMessage(MENU_SWITCH_TERM); 339 break; 340 341 case MENU_NEW_TERM: 342 { 343 app_info info; 344 be_app->GetAppInfo(&info); 345 346 // try launching two different ways to work around possible problems 347 if (be_roster->Launch(&info.ref) != B_OK) 348 be_roster->Launch(TERM_SIGNATURE); 349 break; 350 } 351 352 case MENU_PREF_OPEN: 353 if (!fPrefWindow) 354 fPrefWindow = new PrefWindow(this); 355 else 356 fPrefWindow->Activate(); 357 break; 358 359 case MSG_PREF_CLOSED: 360 fPrefWindow = NULL; 361 break; 362 363 case MENU_FIND_STRING: 364 if (!fFindPanel) { 365 BRect r = Frame(); 366 r.left += 20; 367 r.top += 20; 368 r.right = r.left + 260; 369 r.bottom = r.top + 190; 370 fFindPanel = new FindWindow(r, this, fFindString, fFindSelection, fMatchWord, fMatchCase, fForwardSearch); 371 } 372 else 373 fFindPanel->Activate(); 374 break; 375 376 case MSG_FIND: 377 fFindPanel->PostMessage(B_QUIT_REQUESTED); 378 message->FindBool("findselection", &fFindSelection); 379 if (!fFindSelection) 380 message->FindString("findstring", &fFindString); 381 else 382 _ActiveTermView()->GetSelection(fFindString); 383 384 if (fFindString.Length() == 0) { 385 BAlert *alert = new BAlert("find failed", "No search string.", "Okay", NULL, 386 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 387 alert->Go(); 388 fFindBackwardMenuItem->SetEnabled(false); 389 fFindForwardMenuItem->SetEnabled(false); 390 break; 391 } 392 393 message->FindBool("forwardsearch", &fForwardSearch); 394 message->FindBool("matchcase", &fMatchCase); 395 message->FindBool("matchword", &fMatchWord); 396 findresult = _ActiveTermView()->Find(fFindString, fForwardSearch, fMatchCase, fMatchWord); 397 398 if (!findresult) { 399 BAlert *alert = new BAlert("find failed", "Not Found.", "Okay", NULL, 400 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 401 alert->Go(); 402 fFindBackwardMenuItem->SetEnabled(false); 403 fFindForwardMenuItem->SetEnabled(false); 404 break; 405 } 406 407 // Enable the menu items Find Forward and Find Backward 408 fFindBackwardMenuItem->SetEnabled(true); 409 fFindForwardMenuItem->SetEnabled(true); 410 break; 411 412 case MENU_FIND_FORWARD: 413 findresult = _ActiveTermView()->Find(fFindString, true, fMatchCase, fMatchWord); 414 if (!findresult) { 415 BAlert *alert = new BAlert("find failed", "Not Found.", "Okay", NULL, 416 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 417 alert->Go(); 418 } 419 break; 420 421 case MENU_FIND_BACKWARD: 422 findresult = _ActiveTermView()->Find(fFindString, false, fMatchCase, fMatchWord); 423 if (!findresult) { 424 BAlert *alert = new BAlert("find failed", "Not Found.", "Okay", NULL, 425 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 426 alert->Go(); 427 } 428 break; 429 430 case MSG_FIND_CLOSED: 431 fFindPanel = NULL; 432 break; 433 434 case MENU_ENCODING: 435 if (message->FindInt32("op", &encodingId) == B_OK) 436 _ActiveTermView()->SetEncoding(encodingId); 437 break; 438 439 case MSG_COLS_CHANGED: 440 { 441 int32 columns, rows; 442 message->FindInt32("columns", &columns); 443 message->FindInt32("rows", &rows); 444 PrefHandler::Default()->setInt32(PREF_COLS, columns); 445 PrefHandler::Default()->setInt32(PREF_ROWS, rows); 446 447 _ActiveTermView()->SetTermSize(rows, columns, 0); 448 449 _ResizeView(_ActiveTermView()); 450 451 BPath path; 452 if (PrefHandler::GetDefaultPath(path) == B_OK) 453 PrefHandler::Default()->SaveAsText(path.Path(), PREFFILE_MIMETYPE); 454 break; 455 } 456 case MSG_HALF_FONT_CHANGED: 457 case MSG_FULL_FONT_CHANGED: 458 case MSG_HALF_SIZE_CHANGED: 459 case MSG_FULL_SIZE_CHANGED: 460 { 461 BFont font; 462 _GetPreferredFont(font); 463 _ActiveTermView()->SetTermFont(&font); 464 465 _ResizeView(_ActiveTermView()); 466 467 break; 468 } 469 470 case FULLSCREEN: 471 if (!fSavedFrame.IsValid()) { // go fullscreen 472 float mbHeight = fMenubar->Bounds().Height() + 1; 473 fSavedFrame = Frame(); 474 BScreen screen(this); 475 _ActiveTermView()->ScrollBar()->Hide(); 476 fMenubar->Hide(); 477 fTabView->ResizeBy(0, mbHeight); 478 fTabView->MoveBy(0, -mbHeight); 479 fSavedLook = Look(); 480 // done before ResizeTo to work around a Dano bug (not erasing the decor) 481 SetLook(B_NO_BORDER_WINDOW_LOOK); 482 ResizeTo(screen.Frame().Width()+1, screen.Frame().Height()+1); 483 MoveTo(screen.Frame().left, screen.Frame().top); 484 } else { // exit fullscreen 485 float mbHeight = fMenubar->Bounds().Height() + 1; 486 fMenubar->Show(); 487 _ActiveTermView()->ScrollBar()->Show(); 488 ResizeTo(fSavedFrame.Width(), fSavedFrame.Height()); 489 MoveTo(fSavedFrame.left, fSavedFrame.top); 490 fTabView->ResizeBy(0, -mbHeight); 491 fTabView->MoveBy(0, mbHeight); 492 SetLook(fSavedLook); 493 fSavedFrame = BRect(0,0,-1,-1); 494 } 495 break; 496 497 case MSG_FONT_CHANGED: 498 PostMessage(MSG_HALF_FONT_CHANGED); 499 break; 500 501 case MSG_COLOR_CHANGED: 502 { 503 _SetTermColors(_ActiveTermViewContainerView()); 504 _ActiveTermView()->Invalidate(); 505 break; 506 } 507 508 case SAVE_AS_DEFAULT: 509 { 510 BPath path; 511 if (PrefHandler::GetDefaultPath(path) == B_OK) 512 PrefHandler::Default()->SaveAsText(path.Path(), PREFFILE_MIMETYPE); 513 break; 514 } 515 case MENU_PAGE_SETUP: 516 _DoPageSetup(); 517 break; 518 519 case MENU_PRINT: 520 _DoPrint(); 521 break; 522 523 case MSG_CHECK_CHILDREN: 524 _CheckChildren(); 525 break; 526 527 case MSG_PREVIOUS_TAB: 528 case MSG_NEXT_TAB: 529 { 530 TermView* termView; 531 if (message->FindPointer("termView", (void**)&termView) == B_OK) { 532 int32 count = fSessions.CountItems(); 533 int32 index = _IndexOfTermView(termView); 534 if (count > 1 && index >= 0) { 535 index += message->what == MSG_PREVIOUS_TAB ? -1 : 1; 536 fTabView->Select((index + count) % count); 537 } 538 } 539 break; 540 } 541 542 case kNewTab: 543 if (fTabView->CountTabs() < kMaxTabs) 544 _AddTab(NULL); 545 break; 546 547 case kCloseView: 548 { 549 TermView* termView; 550 if (message->FindPointer("termView", (void**)&termView) == B_OK) { 551 int32 index = _IndexOfTermView(termView); 552 if (index >= 0) 553 _RemoveTab(index); 554 } 555 break; 556 } 557 558 case kIncreaseFontSize: 559 case kDecreaseFontSize: 560 { 561 message->PrintToStream(); 562 TermView *view = _ActiveTermView(); 563 BFont font; 564 view->GetTermFont(&font); 565 566 float size = font.Size(); 567 if (message->what == kIncreaseFontSize) 568 size += 1; 569 else 570 size -= 1; 571 572 // limit the font size 573 if (size < 6) 574 size = 6; 575 else if (size > 20) 576 size = 20; 577 578 font.SetSize(size); 579 view->SetTermFont(&font); 580 PrefHandler::Default()->setInt32(PREF_HALF_FONT_SIZE, (int32)size); 581 582 _ResizeView(view); 583 break; 584 } 585 586 default: 587 BWindow::MessageReceived(message); 588 break; 589 } 590 } 591 592 593 void 594 TermWindow::WindowActivated(bool activated) 595 { 596 BWindow::WindowActivated(activated); 597 } 598 599 600 void 601 TermWindow::_SetTermColors(TermViewContainerView *containerView) 602 { 603 PrefHandler* handler = PrefHandler::Default(); 604 rgb_color background = handler->getRGB(PREF_TEXT_BACK_COLOR); 605 606 containerView->SetViewColor(background); 607 608 TermView *termView = containerView->GetTermView(); 609 termView->SetTextColor(handler->getRGB(PREF_TEXT_FORE_COLOR), background); 610 611 termView->SetSelectColor(handler->getRGB(PREF_SELECT_FORE_COLOR), 612 handler->getRGB(PREF_SELECT_BACK_COLOR)); 613 614 termView->SetCursorColor(handler->getRGB(PREF_CURSOR_FORE_COLOR), 615 handler->getRGB(PREF_CURSOR_BACK_COLOR)); 616 } 617 618 619 status_t 620 TermWindow::_DoPageSetup() 621 { 622 BPrintJob job("PageSetup"); 623 624 // display the page configure panel 625 status_t status = job.ConfigPage(); 626 627 // save a pointer to the settings 628 fPrintSettings = job.Settings(); 629 630 return status; 631 } 632 633 634 void 635 TermWindow::_DoPrint() 636 { 637 if (!fPrintSettings || (_DoPageSetup() != B_OK)) { 638 (new BAlert("Cancel", "Print cancelled.", "OK"))->Go(); 639 return; 640 } 641 642 BPrintJob job("Print"); 643 job.SetSettings(new BMessage(*fPrintSettings)); 644 645 BRect pageRect = job.PrintableRect(); 646 BRect curPageRect = pageRect; 647 648 int pHeight = (int)pageRect.Height(); 649 int pWidth = (int)pageRect.Width(); 650 float w,h; 651 _ActiveTermView()->GetFrameSize(&w, &h); 652 int xPages = (int)ceil(w / pWidth); 653 int yPages = (int)ceil(h / pHeight); 654 655 job.BeginJob(); 656 657 // loop through and draw each page, and write to spool 658 for (int x = 0; x < xPages; x++) { 659 for (int y = 0; y < yPages; y++) { 660 curPageRect.OffsetTo(x * pWidth, y * pHeight); 661 job.DrawView(_ActiveTermView(), curPageRect, B_ORIGIN); 662 job.SpoolPage(); 663 664 if (!job.CanContinue()){ 665 // It is likely that the only way that the job was cancelled is 666 // because the user hit 'Cancel' in the page setup window, in which 667 // case, the user does *not* need to be told that it was cancelled. 668 // He/she will simply expect that it was done. 669 return; 670 } 671 } 672 } 673 674 job.CommitJob(); 675 } 676 677 678 void 679 TermWindow::_AddTab(Arguments *args) 680 { 681 int argc = 0; 682 const char *const *argv = NULL; 683 if (args != NULL) 684 args->GetShellArguments(argc, argv); 685 686 try { 687 // Note: I don't pass the Arguments class directly to the termview, 688 // only to avoid adding it as a dependency: in other words, to keep 689 // the TermView class as agnostic as possible about the surrounding world. 690 CustomTermView *view = 691 new CustomTermView(PrefHandler::Default()->getInt32(PREF_ROWS), 692 PrefHandler::Default()->getInt32(PREF_COLS), 693 argc, (const char **)argv); 694 695 TermViewContainerView *containerView = new TermViewContainerView(view); 696 BScrollView *scrollView = new TermScrollView("scrollView", 697 containerView, view); 698 699 Session* session = new Session(_NewSessionID(), containerView); 700 session->windowTitle = fInitialTitle; 701 fSessions.AddItem(session); 702 703 BTab *tab = new BTab; 704 // TODO: Use a better name. For example, do like MacOsX's Terminal 705 // and update the title using the last executed command ? 706 // Or like Gnome's Terminal and use the current path ? 707 fTabView->AddTab(scrollView, tab); 708 tab->SetLabel(session->name.String()); 709 view->SetScrollBar(scrollView->ScrollBar(B_VERTICAL)); 710 711 view->SetEncoding(EncodingID(PrefHandler::Default()->getString(PREF_TEXT_ENCODING))); 712 713 BFont font; 714 _GetPreferredFont(font); 715 view->SetTermFont(&font); 716 717 _SetTermColors(containerView); 718 719 int width, height; 720 view->GetFontSize(&width, &height); 721 722 float minimumHeight = -1; 723 if (fMenubar) 724 minimumHeight += fMenubar->Bounds().Height() + 1; 725 if (fTabView && fTabView->CountTabs() > 1) 726 minimumHeight += fTabView->TabHeight() + 1; 727 SetSizeLimits(MIN_COLS * width - 1, MAX_COLS * width - 1, 728 minimumHeight + MIN_ROWS * height - 1, 729 minimumHeight + MAX_ROWS * height - 1); 730 731 // If it's the first time we're called, setup the window 732 if (fTabView->CountTabs() == 1) { 733 float viewWidth, viewHeight; 734 containerView->GetPreferredSize(&viewWidth, &viewHeight); 735 736 // Resize Window 737 ResizeTo(viewWidth + B_V_SCROLL_BAR_WIDTH, 738 viewHeight + fMenubar->Bounds().Height() + 1); 739 // NOTE: Width is one pixel too small, since the scroll view 740 // is one pixel wider than its parent. 741 } 742 // TODO: No fTabView->Select(tab); ? 743 fTabView->Select(fTabView->CountTabs() - 1); 744 } catch (...) { 745 // most probably out of memory. That's bad. 746 // TODO: Should cleanup, I guess 747 } 748 } 749 750 751 void 752 TermWindow::_RemoveTab(int32 index) 753 { 754 if (fSessions.CountItems() > 1) { 755 if (Session* session = (Session*)fSessions.RemoveItem(index)) { 756 delete session; 757 delete fTabView->RemoveTab(index); 758 } 759 } else 760 PostMessage(B_QUIT_REQUESTED); 761 } 762 763 764 TermViewContainerView* 765 TermWindow::_ActiveTermViewContainerView() const 766 { 767 return _TermViewContainerViewAt(fTabView->Selection()); 768 } 769 770 771 TermViewContainerView* 772 TermWindow::_TermViewContainerViewAt(int32 index) const 773 { 774 if (Session* session = (Session*)fSessions.ItemAt(index)) 775 return session->containerView; 776 return NULL; 777 } 778 779 780 TermView * 781 TermWindow::_ActiveTermView() const 782 { 783 return _ActiveTermViewContainerView()->GetTermView(); 784 } 785 786 787 TermView* 788 TermWindow::_TermViewAt(int32 index) const 789 { 790 TermViewContainerView* view = _TermViewContainerViewAt(index); 791 return view != NULL ? view->GetTermView() : NULL; 792 } 793 794 795 int32 796 TermWindow::_IndexOfTermView(TermView* termView) const 797 { 798 if (!termView) 799 return -1; 800 801 // find the view 802 int32 count = fTabView->CountTabs(); 803 for (int32 i = count - 1; i >= 0; i--) { 804 if (termView == _TermViewAt(i)) 805 return i; 806 } 807 808 return -1; 809 } 810 811 812 void 813 TermWindow::_CheckChildren() 814 { 815 int32 count = fSessions.CountItems(); 816 for (int32 i = count - 1; i >= 0; i--) { 817 Session* session = (Session*)fSessions.ItemAt(i); 818 session->containerView->GetTermView()->CheckShellGone(); 819 } 820 } 821 822 823 void 824 TermWindow::_ResizeView(TermView *view) 825 { 826 int fontWidth, fontHeight; 827 view->GetFontSize(&fontWidth, &fontHeight); 828 829 float minimumHeight = -1; 830 if (fMenubar) 831 minimumHeight += fMenubar->Bounds().Height() + 1; 832 if (fTabView && fTabView->CountTabs() > 1) 833 minimumHeight += fTabView->TabHeight() + 1; 834 835 SetSizeLimits(MIN_COLS * fontWidth - 1, MAX_COLS * fontWidth - 1, 836 minimumHeight + MIN_ROWS * fontHeight - 1, 837 minimumHeight + MAX_ROWS * fontHeight - 1); 838 839 float width, height; 840 view->Parent()->GetPreferredSize(&width, &height); 841 width += B_V_SCROLL_BAR_WIDTH; 842 // NOTE: Width is one pixel too small, since the scroll view 843 // is one pixel wider than its parent. 844 height += fMenubar->Bounds().Height() + 1; 845 846 ResizeTo(width, height); 847 848 view->Invalidate(); 849 } 850 851 852 void 853 TermWindow::_BuildWindowSizeMenu(BMenu *menu) 854 { 855 const int32 windowSizes[5][2] = { 856 { 80, 24 }, 857 { 80, 25 }, 858 { 80, 40 }, 859 { 132, 24 }, 860 { 132, 25 } 861 }; 862 863 const int32 sizeNum = sizeof(windowSizes) / sizeof(windowSizes[0]); 864 for (int32 i = 0; i < sizeNum; i++) { 865 char label[32]; 866 int32 columns = windowSizes[i][0]; 867 int32 rows = windowSizes[i][1]; 868 snprintf(label, sizeof(label), "%ldx%ld", columns, rows); 869 BMessage *message = new BMessage(MSG_COLS_CHANGED); 870 message->AddInt32("columns", columns); 871 message->AddInt32("rows", rows); 872 menu->AddItem(new BMenuItem(label, message)); 873 } 874 875 menu->AddItem(new BMenuItem("Fullscreen", 876 new BMessage(FULLSCREEN), B_ENTER)); 877 } 878 879 880 int32 881 TermWindow::_NewSessionID() 882 { 883 for (int32 id = 1; ; id++) { 884 bool used = false; 885 886 for (int32 i = 0; 887 Session* session = (Session*)fSessions.ItemAt(i); i++) { 888 if (id == session->id) { 889 used = true; 890 break; 891 } 892 } 893 894 if (!used) 895 return id; 896 } 897 } 898 899 900 // #pragma mark - 901 902 903 // CustomTermView 904 CustomTermView::CustomTermView(int32 rows, int32 columns, int32 argc, const char **argv, int32 historySize) 905 : 906 TermView(rows, columns, argc, argv, historySize) 907 { 908 } 909 910 911 void 912 CustomTermView::NotifyQuit(int32 reason) 913 { 914 BWindow *window = Window(); 915 if (window == NULL) 916 window = be_app->WindowAt(0); 917 918 // TODO: If we got this from a view in a tab not currently selected, 919 // Window() will be NULL, as the view is detached. 920 // So we send the message to the first application window 921 // This isn't so cool, but for now, a Terminal app has only one 922 // window. 923 if (window != NULL) { 924 BMessage message(kCloseView); 925 message.AddPointer("termView", this); 926 message.AddInt32("reason", reason); 927 window->PostMessage(&message); 928 } 929 } 930 931 932 void 933 CustomTermView::SetTitle(const char *title) 934 { 935 dynamic_cast<TermWindow*>(Window())->SetSessionWindowTitle(this, title); 936 } 937 938