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