1 /* 2 * Copyright (c) 2003-2004 Kian Duffy <myob@users.sourceforge.net> 3 * Copyright (C) 1998,99 Kazuho Okui and Takashi Murai. 4 * Copyright (c) 2004 Daniel Furrer <assimil8or@users.sourceforge.net> 5 * 6 * Distributed unter the terms of the MIT license. 7 */ 8 #include <app/Application.h> 9 #include <Menu.h> 10 #include <MenuBar.h> 11 #include <MenuItem.h> 12 #include <ScrollBar.h> 13 #include <TextControl.h> 14 #include <PrintJob.h> 15 #include <Alert.h> 16 #include <float.h> 17 #include <WindowScreen.h> 18 #include <PropertyInfo.h> 19 20 #include <stdio.h> 21 #include <sys/time.h> 22 #include <string> 23 #include <unistd.h> 24 25 26 #include "TermApp.h" 27 #include "TermParse.h" 28 #include "TermWindow.h" 29 #include "TermView.h" 30 #include "TermBuffer.h" 31 #include "TermBaseView.h" 32 #include "CodeConv.h" 33 #include "TermConst.h" 34 #include "PrefDlg.h" 35 #include "PrefView.h" 36 #include "PrefHandler.h" 37 #include "MenuUtil.h" 38 #include "spawn.h" 39 #include "ColorWindow.h" 40 41 42 // Global Preference Handler 43 extern PrefHandler *gTermPref; 44 // 45 // help and GPL URL 46 // 47 //#define URL_PREFIX "file:///boot/home/config/settings/MuTerminal/help/" 48 //#define INDEX_FILE "/index.html" 49 //#define GPL_FILE "/gpl.html" 50 //#define CHLP_FILE "file:///boot/beos/documentation/Shell%20Tools/index.html" 51 52 extern int gNowCoding; /* defined TermParce.cpp */ 53 char gWindowName[256] = "Terminal"; 54 55 void SetCoding(int); 56 57 58 TermWindow::TermWindow(BRect frame, int32 windownumber) 59 : BWindow(frame, "Terminal", B_DOCUMENT_WINDOW, B_CURRENT_WORKSPACE) 60 { 61 sprintf(gWindowName, "Terminal %ld", windownumber); 62 63 InitWindow(); 64 SetTitle(gWindowName); 65 fPrintSettings = NULL; 66 fPrefWindow = NULL; 67 fFindPanel = NULL; 68 } 69 70 71 TermWindow::~TermWindow() 72 { 73 delete fWindowUpdate; 74 } 75 76 77 // #pragma mark - public methods 78 79 80 /** Initialize Window object. */ 81 82 void 83 TermWindow::InitWindow(void) 84 { 85 // make menu bar 86 SetupMenu(); 87 88 // Setup font. 89 90 const char *family = gTermPref->getString(PREF_HALF_FONT_FAMILY); 91 92 BFont halfFont; 93 halfFont.SetFamilyAndStyle(family, NULL); 94 halfFont.SetSize(gTermPref->getFloat(PREF_HALF_FONT_SIZE)); 95 halfFont.SetSpacing(B_FIXED_SPACING); 96 97 family = gTermPref->getString(PREF_FULL_FONT_FAMILY); 98 99 BFont fullFont; 100 fullFont.SetFamilyAndStyle(family, NULL); 101 fullFont.SetSize(gTermPref->getFloat(PREF_FULL_FONT_SIZE)); 102 fullFont.SetSpacing(B_FIXED_SPACING); 103 104 // Make Terminal text view. 105 106 BRect textframe = Bounds(); 107 textframe.top = fMenubar->Bounds().bottom + 1.0; 108 109 fCodeConv = new CodeConv(); 110 fTermView = new TermView(Bounds(), fCodeConv); 111 112 /* 113 * MuTerm has two views. BaseView is window base view. 114 * TermView is character Terminal view on BaseView. It has paste 115 * on BaseView shift as VIEW_OFFSET. 116 */ 117 fBaseView = new TermBaseView(textframe, fTermView); 118 119 // Initialize TermView. (font, size and color) 120 121 fTermView->SetTermFont(&halfFont, &fullFont); 122 BRect rect = fTermView->SetTermSize(gTermPref->getInt32(PREF_ROWS), 123 gTermPref->getInt32(PREF_COLS), 1); 124 125 int width, height; 126 127 fTermView->GetFontSize(&width, &height); 128 SetSizeLimits(MIN_COLS * width, MAX_COLS * width, 129 MIN_COLS * height, MAX_COLS * height); 130 131 fTermView->SetTermColor(); 132 fBaseView->SetViewColor(gTermPref->getRGB(PREF_TEXT_BACK_COLOR)); 133 134 // Add offset to baseview. 135 rect.InsetBy(-VIEW_OFFSET, -VIEW_OFFSET); 136 137 // Resize Window 138 139 ResizeTo(rect.Width()+ B_V_SCROLL_BAR_WIDTH, 140 rect.Height() + fMenubar->Bounds().Height()); 141 142 fBaseView->ResizeTo(rect.Width(), rect.Height()); 143 fBaseView->AddChild(fTermView); 144 fTermView->MoveBy(VIEW_OFFSET, VIEW_OFFSET); 145 146 // Make Scroll Bar. 147 148 BRect scrollRect(0, 0, B_V_SCROLL_BAR_WIDTH, 149 rect.Height() - B_H_SCROLL_BAR_HEIGHT + 1); 150 151 scrollRect.OffsetBy(rect.Width() + 1, fMenubar->Bounds().Height()); 152 153 BScrollBar *scrollBar = new BScrollBar(scrollRect, "scrollbar", 154 fTermView, 0, 0, B_VERTICAL); 155 fTermView->SetScrollBar(scrollBar); 156 157 AddChild(scrollBar); 158 AddChild(fBaseView); 159 160 // Set fEditmenu's target to fTermView. (Oh!...) 161 fEditmenu->SetTargetForItems(fTermView); 162 163 // Initialize TermParse 164 165 gNowCoding = longname2op(gTermPref->getString(PREF_TEXT_ENCODING)); 166 fTermParse = new TermParse(); 167 fTermParse->InitPtyReader(this); 168 fTermParse->InitTermParse(fTermView, fCodeConv); 169 170 // Set Coding. 171 172 // Initialize MessageRunner. 173 fWindowUpdate = new BMessageRunner(BMessenger(this), 174 new BMessage (MSGRUN_WINDOW), 500000); 175 } 176 177 178 void 179 TermWindow::MenusBeginning(void) 180 { 181 // Syncronize Encode Menu Pop-up menu and Preference. 182 (fEncodingmenu->FindItem(op2longname(gNowCoding)))->SetMarked(true); 183 BWindow::MenusBeginning(); 184 } 185 186 187 void 188 TermWindow::SetupMenu(void) 189 { 190 PrefHandler menuText; 191 192 LoadLocaleFile (&menuText); 193 194 // Menu bar object. 195 fMenubar = new BMenuBar(Bounds(), "mbar"); 196 197 /* 198 * Make Fiile Menu. 199 */ 200 fFilemenu = new BMenu("Terminal"); 201 fFilemenu->AddItem(new BMenuItem("Switch Terminals", new BMessage(MENU_SWITCH_TERM),'G')); 202 fFilemenu->AddItem(new BMenuItem("Start New Terminal", new BMessage(MENU_NEW_TREM), 'N')); 203 fFilemenu->AddSeparatorItem(); 204 fFilemenu->AddItem(new BMenuItem("Page Setup...", new BMessage(MENU_PAGE_SETUP))); 205 fFilemenu->AddItem(new BMenuItem("Print", new BMessage(MENU_PRINT),'P')); 206 fFilemenu->AddSeparatorItem(); 207 fFilemenu->AddItem(new BMenuItem("About Terminal...", new BMessage(B_ABOUT_REQUESTED))); 208 fFilemenu->AddSeparatorItem(); 209 fFilemenu->AddItem(new BMenuItem("Quit", new BMessage(MENU_FILE_QUIT), 'Q')); 210 fMenubar->AddItem(fFilemenu); 211 212 /* 213 * Make Edit Menu. 214 */ 215 fEditmenu = new BMenu ("Edit"); 216 fEditmenu->AddItem (new BMenuItem ("Copy", new BMessage (B_COPY),'C')); 217 fEditmenu->AddItem (new BMenuItem ("Paste", new BMessage (B_PASTE),'V')); 218 fEditmenu->AddSeparatorItem (); 219 fEditmenu->AddItem (new BMenuItem ("Select All", new BMessage (B_SELECT_ALL), 'A')); 220 fEditmenu->AddItem (new BMenuItem ("Clear All", new BMessage (MENU_CLEAR_ALL), 'L')); 221 222 /* 223 // TODO: Implement Finding 224 fEditmenu->AddSeparatorItem (); 225 fEditmenu->AddItem (new BMenuItem ("Find", new BMessage (MENU_FIND_STRING),'F')); 226 fEditmenu->AddItem (new BMenuItem ("Find Again", new BMessage (MENU_FIND_AGAIN), ']')); 227 */ 228 fMenubar->AddItem (fEditmenu); 229 230 231 /* 232 * Make Help Menu. 233 */ 234 fHelpmenu = new BMenu("Settings"); 235 fWindowSizeMenu = new BMenu("Window Size"); 236 fWindowSizeMenu->AddItem(new BMenuItem("80x24", new BMessage(EIGHTYTWENTYFOUR))); 237 fWindowSizeMenu->AddItem(new BMenuItem("80x25", new BMessage(EIGHTYTWENTYFIVE))); 238 fWindowSizeMenu->AddItem(new BMenuItem("80x40", new BMessage(EIGHTYFORTY))); 239 fWindowSizeMenu->AddItem(new BMenuItem("132x24", new BMessage(ONETHREETWOTWENTYFOUR))); 240 fWindowSizeMenu->AddItem(new BMenuItem("132x25", new BMessage(ONETHREETWOTWENTYFIVE))); 241 242 // Considering we have this in the preferences window, this menu is not 243 // needed and should not be shown if we are to not confuse the user 244 /* fNewFontMenu = new BMenu("Font"); 245 fNewFontMenu->SetRadioMode(true); 246 int32 numFamilies1 = count_font_families(); 247 for ( int32 i = 0; i < numFamilies1; i++ ) { 248 font_family family; 249 uint32 flags; 250 if ( get_font_family(i, &family, &flags) == B_OK ) { 251 fNewFontMenu->AddItem(item = new BMenuItem(family, new BMessage(MSG_FONT_CHANGED))); 252 // if (0 ==i) item->SetMarked(true); 253 } 254 } 255 fNewFontMenu->FindItem (gTermPref->getString(PREF_HALF_FONT_FAMILY))->SetMarked(true); 256 */ 257 258 fEncodingmenu = new BMenu("Font Encoding"); 259 fEncodingmenu->SetRadioMode(true); 260 MakeEncodingMenu(fEncodingmenu, gNowCoding, true); 261 fHelpmenu->AddItem(fWindowSizeMenu); 262 fHelpmenu->AddItem(fEncodingmenu); 263 // fHelpmenu->AddItem(fNewFontMenu); 264 fHelpmenu->AddSeparatorItem(); 265 fHelpmenu->AddItem(new BMenuItem("Preferences", new BMessage(MENU_PREF_OPEN))); 266 fMenubar->AddItem(fHelpmenu); 267 268 AddChild(fMenubar); 269 } 270 271 272 void 273 TermWindow::MessageReceived(BMessage *message) 274 { 275 int32 coding_id; 276 BRect r; 277 BFont halfFont; 278 BFont fullFont; 279 280 switch (message->what) { 281 282 case MENU_SWITCH_TERM: 283 be_app->PostMessage(MENU_SWITCH_TERM); 284 break; 285 286 case MENU_NEW_TREM: 287 be_app->PostMessage(MENU_NEW_TREM); 288 break; 289 290 case MENU_PREF_OPEN: 291 if (!fPrefWindow){ 292 fPrefWindow = new PrefDlg(this); 293 }else{ 294 fPrefWindow->Activate(); 295 } 296 break; 297 298 case MSG_PREF_CLOSED: 299 fPrefWindow = NULL; 300 break; 301 302 case MENU_FILE_QUIT: 303 be_app->PostMessage(B_QUIT_REQUESTED); 304 break; 305 case MENU_ENCODING: 306 message->FindInt32 ("op", &coding_id); 307 gNowCoding = coding_id; 308 SetCoding(coding_id); 309 break; 310 311 /* 312 * Extended B_SET_PROPERTY. Dispatch this message, 313 * Set coding ID. 314 */ 315 case B_SET_PROPERTY: 316 { 317 int32 i; 318 BMessage spe; 319 message->GetCurrentSpecifier(&i, &spe); 320 if (!strcmp("encode", spe.FindString("property", i))){ 321 message->FindInt32 ("data", &coding_id); 322 gNowCoding = coding_id; 323 SetCoding (coding_id); 324 325 message->SendReply(B_REPLY); 326 }else{ 327 BWindow::MessageReceived(message); 328 } 329 break; 330 } 331 332 /* 333 * Extended B_GET_PROPERTY. Dispatch this message, 334 * reply now coding ID. 335 */ 336 case B_GET_PROPERTY: 337 { 338 int32 i; 339 BMessage spe; 340 message->GetCurrentSpecifier(&i, &spe); 341 if (!strcmp("encode", spe.FindString("property", i))){ 342 BMessage reply(B_REPLY); 343 reply.AddInt32("result", gNowCoding); 344 message->SendReply(&reply); 345 }else if (!strcmp("tty", spe.FindString("property", i))){ 346 BMessage reply(B_REPLY); 347 reply.AddString("result", &tty_name[8]); 348 message->SendReply(&reply); 349 }else{ 350 BWindow::MessageReceived(message); 351 } 352 break; 353 } 354 355 /* 356 * Message from Preference panel. 357 */ 358 case MSG_ROWS_CHANGED: 359 case MSG_COLS_CHANGED: 360 r = fTermView->SetTermSize (gTermPref->getInt32 (PREF_ROWS), 361 gTermPref->getInt32 (PREF_COLS), 362 0); 363 364 ResizeTo (r.Width()+ B_V_SCROLL_BAR_WIDTH + VIEW_OFFSET * 2, 365 r.Height()+fMenubar->Bounds().Height() + VIEW_OFFSET *2); 366 break; 367 368 case MSG_HALF_FONT_CHANGED: 369 case MSG_FULL_FONT_CHANGED: 370 case MSG_HALF_SIZE_CHANGED: 371 case MSG_FULL_SIZE_CHANGED: 372 373 halfFont.SetFamilyAndStyle (gTermPref->getString(PREF_HALF_FONT_FAMILY), 374 NULL); 375 halfFont.SetSize (gTermPref->getFloat(PREF_HALF_FONT_SIZE)); 376 halfFont.SetSpacing (B_FIXED_SPACING); 377 378 fullFont.SetFamilyAndStyle (gTermPref->getString(PREF_FULL_FONT_FAMILY), 379 NULL); 380 fullFont.SetSize (gTermPref->getFloat(PREF_FULL_FONT_SIZE)); 381 fullFont.SetSpacing (B_FIXED_SPACING); 382 383 fTermView->SetTermFont (&halfFont, &fullFont); 384 r = fTermView->SetTermSize (0, 0, 0); 385 386 int width, height; 387 388 fTermView->GetFontSize (&width, &height); 389 390 SetSizeLimits (MIN_COLS * width, MAX_COLS * width, 391 MIN_COLS * height, MAX_COLS * height); 392 393 394 395 ResizeTo (r.Width()+ B_V_SCROLL_BAR_WIDTH + VIEW_OFFSET * 2, 396 r.Height()+fMenubar->Bounds().Height() + VIEW_OFFSET * 2); 397 398 fTermView->Invalidate(); 399 break; 400 401 case EIGHTYTWENTYFOUR: 402 gTermPref->setString(PREF_COLS, "80"); 403 gTermPref->setString(PREF_ROWS, "24"); 404 this->PostMessage (MSG_ROWS_CHANGED); 405 this->PostMessage (MSG_COLS_CHANGED); 406 break; 407 408 case EIGHTYTWENTYFIVE: 409 gTermPref->setString(PREF_COLS, "80"); 410 gTermPref->setString(PREF_ROWS, "25"); 411 this->PostMessage (MSG_ROWS_CHANGED); 412 this->PostMessage (MSG_COLS_CHANGED); 413 break; 414 415 case EIGHTYFORTY: 416 gTermPref->setString(PREF_COLS, "80"); 417 gTermPref->setString(PREF_ROWS, "40"); 418 this->PostMessage (MSG_ROWS_CHANGED); 419 this->PostMessage (MSG_COLS_CHANGED); 420 break; 421 422 case ONETHREETWOTWENTYFOUR: 423 gTermPref->setString(PREF_COLS, "132"); 424 gTermPref->setString(PREF_ROWS, "24"); 425 this->PostMessage (MSG_ROWS_CHANGED); 426 this->PostMessage (MSG_COLS_CHANGED); 427 break; 428 429 case ONETHREETWOTWENTYFIVE: 430 gTermPref->setString(PREF_COLS, "132"); 431 gTermPref->setString(PREF_ROWS, "25"); 432 this->PostMessage (MSG_ROWS_CHANGED); 433 this->PostMessage (MSG_COLS_CHANGED); 434 break; 435 436 case MSG_FONT_CHANGED: 437 gTermPref->setString (PREF_HALF_FONT_FAMILY, fNewFontMenu->FindMarked()->Label()); 438 this->PostMessage (MSG_HALF_FONT_CHANGED); 439 break; 440 441 442 case MSG_COLOR_CHANGED: 443 fBaseView->SetViewColor (gTermPref->getRGB (PREF_TEXT_BACK_COLOR)); 444 fTermView->SetTermColor (); 445 fBaseView->Invalidate(); 446 fTermView->Invalidate(); 447 break; 448 449 case MENU_PAGE_SETUP: 450 DoPageSetup (); 451 break; 452 case MENU_PRINT: 453 DoPrint (); 454 break; 455 456 case MSGRUN_WINDOW: 457 fTermView->UpdateSIGWINCH (); 458 break; 459 460 case B_ABOUT_REQUESTED: 461 be_app->PostMessage(B_ABOUT_REQUESTED); 462 break; 463 464 default: 465 BWindow::MessageReceived(message); 466 break; 467 } 468 } 469 //////////////////////////////////////////////////////////////////////////// 470 // WindowActivated (bool) 471 // Dispatch Mesasge. 472 //////////////////////////////////////////////////////////////////////////// 473 void 474 TermWindow::WindowActivated (bool ) 475 { 476 477 } 478 479 //////////////////////////////////////////////////////////////////////////// 480 // Quit (void) 481 // Quit Application. 482 //////////////////////////////////////////////////////////////////////////// 483 //void 484 //TermWindow::colRequested() { 485 // colWindow *colW=new colWindow("Colours for Terminal"); 486 // colW->Show(); 487 // } 488 489 490 void 491 TermWindow::Quit(void) 492 { 493 delete fTermParse; 494 delete fCodeConv; 495 if(fPrefWindow) fPrefWindow->PostMessage (B_QUIT_REQUESTED); 496 be_app->PostMessage (B_QUIT_REQUESTED, be_app); 497 BWindow::Quit (); 498 } 499 500 501 bool 502 TermWindow::QuitRequested(void) 503 { 504 505 return true; 506 } 507 //////////////////////////////////////////////////////////////////////////// 508 // int GetTimeZone (void) 509 // Get Machine Timezone. 510 //////////////////////////////////////////////////////////////////////////// 511 int 512 TermWindow::GetTimeZone () 513 { 514 struct timeval tv; 515 struct timezone tm; 516 517 gettimeofday (&tv, &tm); 518 519 return -tm.tz_minuteswest / 60; 520 } 521 522 523 #include "spawn.h" 524 525 526 void 527 TermWindow::TermWinActivate() 528 { 529 Activate(); 530 531 #ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST 532 if (focus_follows_mouse()) { 533 BPoint aMouseLoc = Frame().LeftTop(); 534 set_mouse_position(int32(aMouseLoc.x + 16), int32(aMouseLoc.y + 2)); 535 be_app->SetCursor(B_HAND_CURSOR); 536 } 537 #endif 538 } 539 540 541 status_t 542 TermWindow::GetSupportedSuites(BMessage *msg) 543 { 544 static property_info prop_list[] = { 545 { "encode", 546 {B_GET_PROPERTY, 0}, 547 {B_DIRECT_SPECIFIER, 0}, 548 "get muterminal encode"}, 549 { "encode", 550 {B_SET_PROPERTY, 0}, 551 {B_DIRECT_SPECIFIER, 0}, 552 "set muterminal encode"}, 553 { "tty", 554 {B_GET_PROPERTY, 0}, 555 {B_DIRECT_SPECIFIER, 0}, 556 "get tty_name."}, 557 { 0 } 558 559 }; 560 msg->AddString("suites", "suite/vnd.naan-termwindow"); 561 BPropertyInfo prop_info(prop_list); 562 msg->AddFlat("messages", &prop_info); 563 return BWindow::GetSupportedSuites(msg); 564 } 565 //////////////////////////////////////////////////////////////////////////// 566 // ResolveSpecifier 567 // 568 //////////////////////////////////////////////////////////////////////////// 569 BHandler* 570 TermWindow::ResolveSpecifier(BMessage *msg, int32 index, 571 BMessage *specifier, int32 form, 572 const char *property) 573 { 574 if ( (strcmp(property, "encode") == 0) 575 && ((msg->what == B_SET_PROPERTY) || (msg->what == B_GET_PROPERTY) )) 576 return this; 577 else if ( (strcmp(property, "tty") == 0) 578 && (msg->what == B_GET_PROPERTY) ) 579 return this; 580 581 return BWindow::ResolveSpecifier(msg, index, specifier, form, property); 582 } 583 584 //////////////////////////////////////////////////////////////////////////// 585 // SetCoding 586 // Set coding utility functions. 587 //////////////////////////////////////////////////////////////////////////// 588 void SetCoding (int coding) 589 { 590 const etable *p = encoding_table; 591 p += coding; 592 593 gNowCoding = coding; 594 595 return; 596 } 597 //////////////////////////////////////////////////////////////////////////// 598 // DoPageSetUp () 599 // 600 //////////////////////////////////////////////////////////////////////////// 601 status_t 602 TermWindow::DoPageSetup() 603 { 604 status_t rv; 605 BPrintJob job("PageSetup"); 606 607 /* display the page configure panel */ 608 rv = job.ConfigPage(); 609 610 /* save a pointer to the settings */ 611 fPrintSettings = job.Settings(); 612 613 return rv; 614 } 615 616 //////////////////////////////////////////////////////////////////////////// 617 // DoPrint () 618 // 619 //////////////////////////////////////////////////////////////////////////// 620 void 621 TermWindow::DoPrint() 622 { 623 //#if B_BEOS_VERSION < 0x0460 624 BPrintJob job("Print"); 625 626 if((! fPrintSettings) && (DoPageSetup() != B_NO_ERROR)) { 627 (new BAlert("Cancel", "Print cancelled.", "OK"))->Go(); 628 return; 629 } 630 631 job.SetSettings(new BMessage(*fPrintSettings)); 632 633 BRect pageRect = job.PrintableRect(); 634 BRect curPageRect = pageRect; 635 636 int pHeight = (int)pageRect.Height(); 637 int pWidth = (int)pageRect.Width(); 638 float w,h; 639 fTermView->GetFrameSize (&w, &h); 640 int xPages = (int)ceil(w / pWidth); 641 int yPages = (int)ceil(h / pHeight); 642 643 /* engage the print server */ 644 job.BeginJob(); 645 646 /* loop through and draw each page, and write to spool */ 647 for(int x = 0; x < xPages; x++) 648 for(int y = 0; y < yPages; y++){ 649 curPageRect.OffsetTo(x * pWidth, y * pHeight); 650 job.DrawView(fTermView, curPageRect, BPoint(0, 0)); 651 job.SpoolPage(); 652 653 if(!job.CanContinue()){ 654 // It is likely that the only way that the job was cancelled is 655 // because the user hit 'Cancel' in the page setup window, in which 656 // case, the user does *not* need to be told that it was cancelled. 657 // He/she will simply expect that it was done. 658 // (new BAlert("Cancel", "Print job cancelled", "OK"))->Go(); 659 return; 660 } 661 } 662 663 /* commit the job, send the spool file */ 664 job.CommitJob(); 665 //#endif 666 } 667