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