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