1 /* 2 * Copyright 2002-2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Mattias Sundblad 7 * Andrew Bachmann 8 * Philippe Saint-Pierre 9 * Jonas Sundström 10 */ 11 12 #include "Constants.h" 13 #include "ColorMenuItem.h" 14 #include "FindWindow.h" 15 #include "ReplaceWindow.h" 16 #include "StyledEditApp.h" 17 #include "StyledEditView.h" 18 #include "StyledEditWindow.h" 19 20 #include <Alert.h> 21 #include <Autolock.h> 22 #include <CharacterSet.h> 23 #include <CharacterSetRoster.h> 24 #include <Clipboard.h> 25 #include <Debug.h> 26 #include <File.h> 27 #include <FilePanel.h> 28 #include <Menu.h> 29 #include <MenuBar.h> 30 #include <MenuItem.h> 31 #include <Path.h> 32 #include <PrintJob.h> 33 #include <Rect.h> 34 #include <Roster.h> 35 #include <ScrollView.h> 36 #include <TextControl.h> 37 #include <TextView.h> 38 #include <TranslationUtils.h> 39 40 using namespace BPrivate; 41 42 43 const float kLineViewWidth = 30.0; 44 45 46 StyledEditWindow::StyledEditWindow(BRect frame, int32 id, uint32 encoding) 47 : BWindow(frame, "untitled", B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS) 48 { 49 InitWindow(encoding); 50 BString unTitled("Untitled "); 51 unTitled << id; 52 SetTitle(unTitled.String()); 53 fSaveItem->SetEnabled(true); 54 // allow saving empty files 55 Show(); 56 } 57 58 59 StyledEditWindow::StyledEditWindow(BRect frame, entry_ref *ref, uint32 encoding) 60 : BWindow(frame, "untitled", B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS) 61 { 62 InitWindow(encoding); 63 OpenFile(ref); 64 Show(); 65 } 66 67 68 StyledEditWindow::~StyledEditWindow() 69 { 70 delete fSaveMessage; 71 delete fPrintSettings; 72 delete fSavePanel; 73 } 74 75 76 void 77 StyledEditWindow::InitWindow(uint32 encoding) 78 { 79 fPrintSettings = NULL; 80 fSaveMessage = NULL; 81 82 // undo modes 83 fUndoFlag = false; 84 fCanUndo = false; 85 fRedoFlag = false; 86 fCanRedo = false; 87 88 // clean modes 89 fUndoCleans = false; 90 fRedoCleans = false; 91 fClean = true; 92 93 // search- state 94 fReplaceString = ""; 95 fStringToFind = ""; 96 fCaseSens = false; 97 fWrapAround = false; 98 fBackSearch = false; 99 100 // add menubar 101 fMenuBar = new BMenuBar(BRect(0, 0, 0, 0), "menubar"); 102 AddChild(fMenuBar); 103 104 // add textview and scrollview 105 106 BRect viewFrame = Bounds(); 107 viewFrame.top = fMenuBar->Bounds().Height() + 1; 108 viewFrame.right -= B_V_SCROLL_BAR_WIDTH; 109 viewFrame.left = 0; 110 viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT; 111 112 BRect textBounds = viewFrame; 113 textBounds.OffsetTo(B_ORIGIN); 114 textBounds.InsetBy(TEXT_INSET, TEXT_INSET); 115 116 fTextView= new StyledEditView(viewFrame, textBounds, this); 117 fTextView->SetDoesUndo(true); 118 fTextView->SetStylable(true); 119 fTextView->SetEncoding(encoding); 120 121 fScrollView = new BScrollView("scrollview", fTextView, B_FOLLOW_ALL, 0, 122 true, true, B_PLAIN_BORDER); 123 AddChild(fScrollView); 124 fTextView->MakeFocus(true); 125 126 // Add "File"-menu: 127 BMenu* menu = new BMenu("File"); 128 fMenuBar->AddItem(menu); 129 130 BMenuItem* menuItem; 131 menu->AddItem(menuItem = new BMenuItem("New", new BMessage(MENU_NEW), 'N')); 132 menuItem->SetTarget(be_app); 133 134 menu->AddItem(menuItem = new BMenuItem(fRecentMenu = new BMenu("Open" B_UTF8_ELLIPSIS), 135 new BMessage(MENU_OPEN))); 136 menuItem->SetShortcut('O', 0); 137 menuItem->SetTarget(be_app); 138 menu->AddSeparatorItem(); 139 140 menu->AddItem(fSaveItem = new BMenuItem("Save", new BMessage(MENU_SAVE), 'S')); 141 fSaveItem->SetEnabled(false); 142 menu->AddItem(menuItem = new BMenuItem("Save As" B_UTF8_ELLIPSIS, new BMessage(MENU_SAVEAS))); 143 menuItem->SetShortcut('S',B_SHIFT_KEY); 144 menuItem->SetEnabled(true); 145 146 menu->AddItem(fRevertItem = new BMenuItem("Revert to Saved" B_UTF8_ELLIPSIS, 147 new BMessage(MENU_REVERT))); 148 fRevertItem->SetEnabled(false); 149 menu->AddItem(menuItem = new BMenuItem("Close", new BMessage(MENU_CLOSE), 'W')); 150 151 menu->AddSeparatorItem(); 152 menu->AddItem(menuItem = new BMenuItem("Page Setup" B_UTF8_ELLIPSIS, new BMessage(MENU_PAGESETUP))); 153 menu->AddItem(menuItem = new BMenuItem("Print" B_UTF8_ELLIPSIS, new BMessage(MENU_PRINT), 'P')); 154 155 menu->AddSeparatorItem(); 156 menu->AddItem(menuItem = new BMenuItem("Quit", new BMessage(MENU_QUIT), 'Q')); 157 158 // Add the "Edit"-menu: 159 menu = new BMenu("Edit"); 160 fMenuBar->AddItem(menu); 161 162 menu->AddItem(fUndoItem = new BMenuItem("Can't Undo", new BMessage(B_UNDO), 'Z')); 163 fUndoItem->SetEnabled(false); 164 165 menu->AddSeparatorItem(); 166 menu->AddItem(fCutItem = new BMenuItem("Cut", new BMessage(B_CUT), 'X')); 167 fCutItem->SetEnabled(false); 168 fCutItem->SetTarget(fTextView); 169 170 menu->AddItem(fCopyItem = new BMenuItem("Copy", new BMessage(B_COPY), 'C')); 171 fCopyItem->SetEnabled(false); 172 fCopyItem->SetTarget(fTextView); 173 174 menu->AddItem(menuItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V')); 175 menuItem->SetTarget(fTextView); 176 menu->AddItem(fClearItem = new BMenuItem("Clear", new BMessage(MENU_CLEAR))); 177 fClearItem->SetEnabled(false); 178 fClearItem->SetTarget(fTextView); 179 180 menu->AddSeparatorItem(); 181 menu->AddItem(menuItem = new BMenuItem("Select All", new BMessage(B_SELECT_ALL), 'A')); 182 menuItem->SetTarget(fTextView); 183 184 menu->AddSeparatorItem(); 185 menu->AddItem(menuItem = new BMenuItem("Find" B_UTF8_ELLIPSIS, new BMessage(MENU_FIND),'F')); 186 menu->AddItem(fFindAgainItem= new BMenuItem("Find Again",new BMessage(MENU_FIND_AGAIN),'G')); 187 fFindAgainItem->SetEnabled(false); 188 189 menu->AddItem(menuItem = new BMenuItem("Find Selection", new BMessage(MENU_FIND_SELECTION),'H')); 190 menu->AddItem(menuItem = new BMenuItem("Replace" B_UTF8_ELLIPSIS, new BMessage(MENU_REPLACE),'R')); 191 menu->AddItem(fReplaceSameItem = new BMenuItem("Replace Same", new BMessage(MENU_REPLACE_SAME),'T')); 192 fReplaceSameItem->SetEnabled(false); 193 194 // Add the "Font"-menu: 195 fFontMenu = new BMenu("Font"); 196 fMenuBar->AddItem(fFontMenu); 197 198 //"Size"-subMenu 199 fFontSizeMenu = new BMenu("Size"); 200 fFontSizeMenu->SetRadioMode(true); 201 fFontMenu->AddItem(fFontSizeMenu); 202 203 const int32 fontSizes[] = {9, 10, 11, 12, 14, 18, 24, 36, 48, 72}; 204 for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) { 205 BMessage* fontMessage = new BMessage(FONT_SIZE); 206 fontMessage->AddFloat("size", fontSizes[i]); 207 208 char label[64]; 209 snprintf(label, sizeof(label), "%ld", fontSizes[i]); 210 fFontSizeMenu->AddItem(menuItem = new BMenuItem(label, fontMessage)); 211 212 if (fontSizes[i] == (int32)be_plain_font->Size()) 213 menuItem->SetMarked(true); 214 } 215 216 // "Color"-subMenu 217 fFontColorMenu = new BMenu("Color"); 218 fFontColorMenu->SetRadioMode(true); 219 fFontMenu->AddItem(fFontColorMenu); 220 221 fFontColorMenu->AddItem(fBlackItem = new BMenuItem("Black", new BMessage(FONT_COLOR))); 222 fBlackItem->SetMarked(true); 223 fFontColorMenu->AddItem(fRedItem = new ColorMenuItem("Red", RED, new BMessage(FONT_COLOR))); 224 fFontColorMenu->AddItem(fGreenItem = new ColorMenuItem("Green", GREEN, new BMessage(FONT_COLOR))); 225 fFontColorMenu->AddItem(fBlueItem = new ColorMenuItem("Blue", BLUE, new BMessage(FONT_COLOR))); 226 fFontColorMenu->AddItem(fCyanItem = new ColorMenuItem("Cyan", CYAN, new BMessage(FONT_COLOR))); 227 fFontColorMenu->AddItem(fMagentaItem = new ColorMenuItem("Magenta", MAGENTA, new BMessage(FONT_COLOR))); 228 fFontColorMenu->AddItem(fYellowItem = new ColorMenuItem("Yellow", YELLOW, new BMessage(FONT_COLOR))); 229 fFontMenu->AddSeparatorItem(); 230 231 // Available fonts 232 233 fCurrentFontItem = 0; 234 fCurrentStyleItem = 0; 235 236 BMenu* subMenu; 237 int32 numFamilies = count_font_families(); 238 for (int32 i = 0; i < numFamilies; i++) { 239 font_family family; 240 if (get_font_family(i, &family) == B_OK) { 241 subMenu = new BMenu(family); 242 subMenu->SetRadioMode(true); 243 fFontMenu->AddItem(menuItem = new BMenuItem(subMenu, 244 new BMessage(FONT_FAMILY))); 245 246 int32 numStyles = count_font_styles(family); 247 for (int32 j = 0; j < numStyles; j++) { 248 font_style style; 249 uint32 flags; 250 if (get_font_style(family, j, &style, &flags) == B_OK) { 251 subMenu->AddItem(menuItem = new BMenuItem(style, 252 new BMessage(FONT_STYLE))); 253 } 254 } 255 } 256 } 257 258 // Add the "Document"-menu: 259 menu = new BMenu("Document"); 260 fMenuBar->AddItem(menu); 261 262 // "Align"-subMenu: 263 subMenu = new BMenu("Align"); 264 subMenu->SetRadioMode(true); 265 266 subMenu->AddItem(fAlignLeft = new BMenuItem("Left", new BMessage(ALIGN_LEFT))); 267 fAlignLeft->SetMarked(true); 268 269 subMenu->AddItem(fAlignCenter = new BMenuItem("Center", new BMessage(ALIGN_CENTER))); 270 subMenu->AddItem(fAlignRight = new BMenuItem("Right", new BMessage(ALIGN_RIGHT))); 271 menu->AddItem(subMenu); 272 menu->AddItem(fWrapItem = new BMenuItem("Wrap Lines", new BMessage(WRAP_LINES))); 273 fWrapItem->SetMarked(true); 274 275 fSavePanel = NULL; 276 fSavePanelEncodingMenu = NULL; 277 // build lazily 278 } 279 280 281 void 282 StyledEditWindow::MessageReceived(BMessage *message) 283 { 284 if (message->WasDropped()) { 285 entry_ref ref; 286 if (message->FindRef("refs", 0, &ref)==B_OK) { 287 message->what = B_REFS_RECEIVED; 288 be_app->PostMessage(message); 289 } 290 } 291 292 switch (message->what) { 293 // File menu 294 case MENU_SAVE: 295 if (!fSaveMessage) 296 SaveAs(); 297 else 298 Save(fSaveMessage); 299 break; 300 301 case MENU_SAVEAS: 302 SaveAs(); 303 break; 304 305 case B_SAVE_REQUESTED: 306 Save(message); 307 break; 308 309 case SAVE_THEN_QUIT: 310 if (Save(message) == B_OK) 311 Quit(); 312 break; 313 314 case MENU_REVERT: 315 RevertToSaved(); 316 break; 317 318 case MENU_CLOSE: 319 if (QuitRequested()) 320 Quit(); 321 break; 322 323 case MENU_PAGESETUP: 324 PageSetup(fTextView->Window()->Title()); 325 break; 326 case MENU_PRINT: 327 Print(fTextView->Window()->Title()); 328 break; 329 case MENU_QUIT: 330 be_app->PostMessage(B_QUIT_REQUESTED); 331 break; 332 333 // Edit menu 334 335 case B_UNDO: 336 ASSERT(fCanUndo || fCanRedo); 337 ASSERT(!(fCanUndo && fCanRedo)); 338 if (fCanUndo) 339 fUndoFlag = true; 340 if (fCanRedo) 341 fRedoFlag = true; 342 343 fTextView->Undo(be_clipboard); 344 break; 345 case B_CUT: 346 fTextView->Cut(be_clipboard); 347 break; 348 case B_COPY: 349 fTextView->Copy(be_clipboard); 350 break; 351 case B_PASTE: 352 fTextView->Paste(be_clipboard); 353 break; 354 case MENU_CLEAR: 355 fTextView->Clear(); 356 break; 357 case MENU_FIND: 358 { 359 BRect findWindowFrame(100, 100, 400, 235); 360 BWindow* window = new FindWindow(findWindowFrame, this, 361 &fStringToFind, fCaseSens, fWrapAround, fBackSearch); 362 window->Show(); 363 break; 364 } 365 case MSG_SEARCH: 366 message->FindString("findtext", &fStringToFind); 367 fFindAgainItem->SetEnabled(true); 368 message->FindBool("casesens", &fCaseSens); 369 message->FindBool("wrap", &fWrapAround); 370 message->FindBool("backsearch", &fBackSearch); 371 372 Search(fStringToFind, fCaseSens, fWrapAround, fBackSearch); 373 break; 374 case MENU_FIND_AGAIN: 375 Search(fStringToFind, fCaseSens, fWrapAround, fBackSearch); 376 break; 377 case MENU_FIND_SELECTION: 378 FindSelection(); 379 break; 380 case MENU_REPLACE: 381 { 382 BRect replaceWindowFrame(100, 100, 400, 284); 383 BWindow* window = new ReplaceWindow(replaceWindowFrame, this, 384 &fStringToFind, &fReplaceString, fCaseSens, fWrapAround, fBackSearch); 385 window->Show(); 386 break; 387 } 388 case MSG_REPLACE: 389 { 390 message->FindBool("casesens", &fCaseSens); 391 message->FindBool("wrap", &fWrapAround); 392 message->FindBool("backsearch", &fBackSearch); 393 394 message->FindString("FindText", &fStringToFind); 395 message->FindString("ReplaceText", &fReplaceString); 396 397 fFindAgainItem->SetEnabled(true); 398 fReplaceSameItem->SetEnabled(true); 399 400 Replace(fStringToFind, fReplaceString, fCaseSens, fWrapAround, fBackSearch); 401 break; 402 } 403 case MENU_REPLACE_SAME: 404 Replace(fStringToFind, fReplaceString, fCaseSens, fWrapAround, fBackSearch); 405 break; 406 407 case MSG_REPLACE_ALL: 408 { 409 message->FindBool("casesens", &fCaseSens); 410 message->FindString("FindText",&fStringToFind); 411 message->FindString("ReplaceText",&fReplaceString); 412 413 bool allWindows; 414 message->FindBool("allwindows", &allWindows); 415 416 fFindAgainItem->SetEnabled(true); 417 fReplaceSameItem->SetEnabled(true); 418 419 if (allWindows) 420 SearchAllWindows(fStringToFind, fReplaceString, fCaseSens); 421 else 422 ReplaceAll(fStringToFind, fReplaceString, fCaseSens); 423 break; 424 } 425 426 // Font menu 427 428 case FONT_SIZE: 429 { 430 float fontSize; 431 if (message->FindFloat("size", &fontSize) == B_OK) 432 SetFontSize(fontSize); 433 break; 434 } 435 case FONT_FAMILY: 436 { 437 const char* fontFamily = NULL; 438 const char* fontStyle = NULL; 439 void* ptr; 440 if (message->FindPointer("source", &ptr) == B_OK) { 441 BMenuItem* item = static_cast<BMenuItem*>(ptr); 442 fontFamily = item->Label(); 443 } 444 SetFontStyle(fontFamily, fontStyle); 445 break; 446 } 447 case FONT_STYLE: 448 { 449 const char* fontFamily = NULL; 450 const char* fontStyle = NULL; 451 void* ptr; 452 if (message->FindPointer("source", &ptr) == B_OK) { 453 BMenuItem* item = static_cast<BMenuItem*>(ptr); 454 fontStyle = item->Label(); 455 BMenu* menu = item->Menu(); 456 if (menu != NULL) { 457 BMenuItem* super_item = menu->Superitem(); 458 if (super_item != NULL) 459 fontFamily = super_item->Label(); 460 } 461 } 462 SetFontStyle(fontFamily, fontStyle); 463 break; 464 } 465 case FONT_COLOR: 466 { 467 void* ptr; 468 if (message->FindPointer("source", &ptr) == B_OK) { 469 if (ptr == fBlackItem) 470 SetFontColor(&BLACK); 471 else if (ptr == fRedItem) 472 SetFontColor(&RED); 473 else if (ptr == fGreenItem) 474 SetFontColor(&GREEN); 475 else if (ptr == fBlueItem) 476 SetFontColor(&BLUE); 477 else if (ptr == fCyanItem) 478 SetFontColor(&CYAN); 479 else if (ptr == fMagentaItem) 480 SetFontColor(&MAGENTA); 481 else if (ptr == fYellowItem) 482 SetFontColor(&YELLOW); 483 } 484 break; 485 } 486 487 // Document menu 488 489 case ALIGN_LEFT: 490 fTextView->SetAlignment(B_ALIGN_LEFT); 491 _UpdateCleanUndoRedoSaveRevert(); 492 break; 493 case ALIGN_CENTER: 494 fTextView->SetAlignment(B_ALIGN_CENTER); 495 _UpdateCleanUndoRedoSaveRevert(); 496 break; 497 case ALIGN_RIGHT: 498 fTextView->SetAlignment(B_ALIGN_RIGHT); 499 _UpdateCleanUndoRedoSaveRevert(); 500 break; 501 case WRAP_LINES: 502 { 503 BRect textRect(fTextView->Bounds()); 504 textRect.OffsetTo(B_ORIGIN); 505 textRect.InsetBy(TEXT_INSET,TEXT_INSET); 506 if (fTextView->DoesWordWrap()) { 507 fTextView->SetWordWrap(false); 508 fWrapItem->SetMarked(false); 509 // the width comes from stylededit R5. TODO: find a better way 510 textRect.SetRightBottom(BPoint(1500.0, textRect.RightBottom().y)); 511 } else { 512 fTextView->SetWordWrap(true); 513 fWrapItem->SetMarked(true); 514 } 515 fTextView->SetTextRect(textRect); 516 517 _UpdateCleanUndoRedoSaveRevert(); 518 break; 519 } 520 case ENABLE_ITEMS: 521 fCutItem->SetEnabled(true); 522 fCopyItem->SetEnabled(true); 523 fClearItem->SetEnabled(true); 524 break; 525 case DISABLE_ITEMS: 526 fCutItem->SetEnabled(false); 527 fCopyItem->SetEnabled(false); 528 fClearItem->SetEnabled(false); 529 break; 530 case TEXT_CHANGED: 531 if (fUndoFlag) { 532 if (fUndoCleans) { 533 // we cleaned! 534 fClean = true; 535 fUndoCleans = false; 536 } else if (fClean) { 537 // if we were clean 538 // then a redo will make us clean again 539 fRedoCleans = true; 540 fClean = false; 541 } 542 // set mode 543 fCanUndo = false; 544 fCanRedo = true; 545 fUndoItem->SetLabel("Redo Typing"); 546 fUndoItem->SetEnabled(true); 547 fUndoFlag = false; 548 } else { 549 if (fRedoFlag && fRedoCleans) { 550 // we cleaned! 551 fClean = true; 552 fRedoCleans = false; 553 } else if (fClean) { 554 // if we were clean 555 // then an undo will make us clean again 556 fUndoCleans = true; 557 fClean = false; 558 } else { 559 // no more cleaning from undo now... 560 fUndoCleans = false; 561 } 562 // set mode 563 fCanUndo = true; 564 fCanRedo = false; 565 fUndoItem->SetLabel("Undo Typing"); 566 fUndoItem->SetEnabled(true); 567 fRedoFlag = false; 568 } 569 if (fClean) { 570 fRevertItem->SetEnabled(false); 571 fSaveItem->SetEnabled(fSaveMessage == NULL); 572 } else { 573 fRevertItem->SetEnabled(fSaveMessage != NULL); 574 fSaveItem->SetEnabled(true); 575 } 576 break; 577 578 case SAVE_AS_ENCODING: 579 void* ptr; 580 if (message->FindPointer("source", &ptr) == B_OK 581 && fSavePanelEncodingMenu != NULL) { 582 fTextView->SetEncoding((uint32)fSavePanelEncodingMenu->IndexOf((BMenuItem*)ptr)); 583 } 584 break; 585 586 default: 587 BWindow::MessageReceived(message); 588 break; 589 } 590 } 591 592 593 void 594 StyledEditWindow::MenusBeginning() 595 { 596 // set up the recent documents menu 597 BMessage documents; 598 be_roster->GetRecentDocuments(&documents, 9, NULL, APP_SIGNATURE); 599 600 // delete old items.. 601 // shatty: it would be preferable to keep the old 602 // menu around instead of continuously thrashing 603 // the menu, but unfortunately there does not 604 // seem to be a straightforward way to update it 605 // going backwards may simplify memory management 606 for (int i = fRecentMenu->CountItems(); i-- > 0;) { 607 delete fRecentMenu->RemoveItem(i); 608 } 609 610 // add new items 611 int count = 0; 612 entry_ref ref; 613 while (documents.FindRef("refs", count++, &ref) == B_OK) { 614 if (ref.device != -1 && ref.directory != -1) { 615 // sanity check passed 616 BMessage* openRecent = new BMessage(B_REFS_RECEIVED); 617 openRecent->AddRef("refs", &ref); 618 BMenuItem* item = new BMenuItem(ref.name, openRecent); 619 item->SetTarget(be_app); 620 fRecentMenu->AddItem(item); 621 } 622 } 623 624 // update the font menu 625 // unselect the old values 626 if (fCurrentFontItem != NULL) { 627 fCurrentFontItem->SetMarked(false); 628 BMenu* menu = fCurrentFontItem->Submenu(); 629 if (menu != NULL) { 630 BMenuItem* item = menu->FindMarked(); 631 if (item != NULL) { 632 item->SetMarked(false); 633 } 634 } 635 } 636 637 if (fCurrentStyleItem != NULL) { 638 fCurrentStyleItem->SetMarked(false); 639 } 640 641 BMenuItem* oldColorItem = fFontColorMenu->FindMarked(); 642 if (oldColorItem != NULL) 643 oldColorItem->SetMarked(false); 644 645 BMenuItem* oldSizeItem = fFontSizeMenu->FindMarked(); 646 if (oldSizeItem != NULL) 647 oldSizeItem->SetMarked(false); 648 649 // find the current font, color, size 650 BFont font; 651 uint32 sameProperties; 652 rgb_color color = BLACK; 653 bool sameColor; 654 fTextView->GetFontAndColor(&font, &sameProperties, &color, &sameColor); 655 656 if (sameColor && color.alpha == 255) { 657 // select the current color 658 if (color.red == 0) { 659 if (color.green == 0) { 660 if (color.blue == 0) { 661 fBlackItem->SetMarked(true); 662 } else if (color.blue == 255) { 663 fBlueItem->SetMarked(true); 664 } 665 } else if (color.green == 255) { 666 if (color.blue == 0) { 667 fGreenItem->SetMarked(true); 668 } else if (color.blue == 255) { 669 fCyanItem->SetMarked(true); 670 } 671 } 672 } else if (color.red == 255) { 673 if (color.green == 0) { 674 if (color.blue == 0) { 675 fRedItem->SetMarked(true); 676 } else if (color.blue == 255) { 677 fMagentaItem->SetMarked(true); 678 } 679 } else if (color.green == 255) { 680 if (color.blue == 0) { 681 fYellowItem->SetMarked(true); 682 } 683 } 684 } 685 } 686 687 if (sameProperties & B_FONT_SIZE) { 688 if ((int)font.Size() == font.Size()) { 689 // select the current font size 690 char fontSizeStr[16]; 691 snprintf(fontSizeStr, 15, "%i", (int)font.Size()); 692 BMenuItem* item = fFontSizeMenu->FindItem(fontSizeStr); 693 if (item != NULL) 694 item->SetMarked(true); 695 } 696 } 697 698 font_family family; 699 font_style style; 700 font.GetFamilyAndStyle(&family, &style); 701 702 fCurrentFontItem = fFontMenu->FindItem(family); 703 704 if (fCurrentFontItem != NULL) { 705 fCurrentFontItem->SetMarked(true); 706 BMenu* menu = fCurrentFontItem->Submenu(); 707 if (menu != NULL) { 708 BMenuItem* item = menu->FindItem(style); 709 fCurrentStyleItem = item; 710 if (fCurrentStyleItem != NULL) { 711 item->SetMarked(true); 712 } 713 } 714 } 715 716 switch (fTextView->Alignment()) { 717 case B_ALIGN_LEFT: 718 default: 719 fAlignLeft->SetMarked(true); 720 break; 721 case B_ALIGN_CENTER: 722 fAlignCenter->SetMarked(true); 723 break; 724 case B_ALIGN_RIGHT: 725 fAlignRight->SetMarked(true); 726 break; 727 } 728 } 729 730 731 void 732 StyledEditWindow::Quit() 733 { 734 styled_edit_app->CloseDocument(); 735 BWindow::Quit(); 736 } 737 738 739 bool 740 StyledEditWindow::QuitRequested() 741 { 742 if (fClean) 743 return true; 744 745 BString alertText("Save changes to the document \""); 746 alertText<< Title() <<"\"? "; 747 int32 index = _ShowAlert(alertText, "Cancel", "Don't Save", "Save", 748 B_WARNING_ALERT); 749 750 if (index == 0) 751 return false; // "cancel": dont save, dont close the window 752 753 if (index == 1) 754 return true; // "don't save": just close the window 755 756 if (!fSaveMessage) { 757 SaveAs(new BMessage(SAVE_THEN_QUIT)); 758 return false; 759 } 760 761 return Save() == B_OK; 762 } 763 764 765 status_t 766 StyledEditWindow::Save(BMessage *message) 767 { 768 if (!message) 769 message = fSaveMessage; 770 771 if (!message) 772 return B_ERROR; 773 774 entry_ref dirRef; 775 const char* name; 776 if (message->FindRef("directory", &dirRef) != B_OK 777 || message->FindString("name", &name) != B_OK) 778 return B_BAD_VALUE; 779 780 BDirectory dir(&dirRef); 781 BEntry entry(&dir, name); 782 783 status_t status = B_ERROR; 784 if (dir.InitCheck() == B_OK && entry.InitCheck() == B_OK) { 785 BFile file(&entry, B_READ_WRITE | B_CREATE_FILE); 786 if (file.InitCheck() == B_OK) 787 status = fTextView->WriteStyledEditFile(&file); 788 } 789 790 if (status != B_OK) { 791 BString alertText("Error saving \""); 792 alertText << name << "\":\n" << strerror(status); 793 794 _ShowAlert(alertText, "OK", "", "", B_STOP_ALERT); 795 return status; 796 } 797 798 SetTitle(name); 799 800 if (fSaveMessage != message) { 801 delete fSaveMessage; 802 fSaveMessage = new BMessage(*message); 803 } 804 805 entry_ref ref; 806 if (entry.GetRef(&ref) == B_OK) 807 be_roster->AddToRecentDocuments(&ref, APP_SIGNATURE); 808 809 // clear clean modes 810 fSaveItem->SetEnabled(false); 811 fRevertItem->SetEnabled(false); 812 fUndoCleans = false; 813 fRedoCleans = false; 814 fClean = true; 815 return status; 816 } 817 818 819 status_t 820 StyledEditWindow::SaveAs(BMessage *message) 821 { 822 if (fSavePanel == NULL) { 823 entry_ref* directory = NULL; 824 if (fSaveMessage != NULL) { 825 entry_ref dirRef; 826 if (fSaveMessage->FindRef("directory", &dirRef) == B_OK) 827 directory = &dirRef; 828 } 829 830 BMessenger target(this); 831 fSavePanel = new BFilePanel(B_SAVE_PANEL, &target, 832 directory, B_FILE_NODE, false); 833 834 BMenuBar* menuBar = dynamic_cast<BMenuBar*>( 835 fSavePanel->Window()->FindView("MenuBar")); 836 837 fSavePanelEncodingMenu= new BMenu("Encoding"); 838 menuBar->AddItem(fSavePanelEncodingMenu); 839 fSavePanelEncodingMenu->SetRadioMode(true); 840 841 BCharacterSetRoster roster; 842 BCharacterSet charset; 843 while (roster.GetNextCharacterSet(&charset) == B_NO_ERROR) { 844 BString name(charset.GetPrintName()); 845 const char* mime = charset.GetMIMEName(); 846 if (mime) { 847 name.Append(" ("); 848 name.Append(mime); 849 name.Append(")"); 850 } 851 BMenuItem * item = new BMenuItem(name.String(), new BMessage(SAVE_AS_ENCODING)); 852 item->SetTarget(this); 853 fSavePanelEncodingMenu->AddItem(item); 854 if (charset.GetFontID() == fTextView->GetEncoding()) 855 item->SetMarked(true); 856 } 857 } 858 859 fSavePanel->SetSaveText(Title()); 860 if (message != NULL) 861 fSavePanel->SetMessage(message); 862 863 fSavePanel->Show(); 864 return B_OK; 865 } 866 867 868 status_t 869 StyledEditWindow::_LoadFile(entry_ref* ref) 870 { 871 BEntry entry(ref, true); 872 // traverse an eventual link 873 874 status_t status = entry.InitCheck(); 875 if (status == B_OK && entry.IsDirectory()) 876 status = B_IS_A_DIRECTORY; 877 878 BFile file; 879 if (status == B_OK) 880 status = file.SetTo(&entry, B_READ_ONLY); 881 if (status == B_OK) 882 status = fTextView->GetStyledText(&file); 883 884 if (status == B_ENTRY_NOT_FOUND) { 885 // Treat non-existing files consideratley; we just want to get an 886 // empty window for them - to create this new document 887 status = B_OK; 888 } 889 890 if (status != B_OK) { 891 // If an error occured, bail out and tell the user what happened 892 BEntry entry(ref, true); 893 char name[B_FILE_NAME_LENGTH]; 894 if (entry.GetName(name) != B_OK) 895 strcpy(name, "???"); 896 897 BString text("Error loading \""); 898 text << name << "\":\n\t"; 899 if (status == B_BAD_TYPE) 900 text << "Unsupported format"; 901 else 902 text << strerror(status); 903 904 _ShowAlert(text, "OK", "", "", B_STOP_ALERT); 905 return status; 906 } 907 908 // update alignment 909 switch (fTextView->Alignment()) { 910 case B_ALIGN_LEFT: 911 default: 912 fAlignLeft->SetMarked(true); 913 break; 914 case B_ALIGN_CENTER: 915 fAlignCenter->SetMarked(true); 916 break; 917 case B_ALIGN_RIGHT: 918 fAlignRight->SetMarked(true); 919 break; 920 } 921 922 // update word wrapping 923 fWrapItem->SetMarked(fTextView->DoesWordWrap()); 924 return B_OK; 925 } 926 927 928 void 929 StyledEditWindow::OpenFile(entry_ref* ref) 930 { 931 if (_LoadFile(ref) != B_OK) { 932 fSaveItem->SetEnabled(true); 933 // allow saving new files 934 return; 935 } 936 937 be_roster->AddToRecentDocuments(ref, APP_SIGNATURE); 938 fSaveMessage = new BMessage(B_SAVE_REQUESTED); 939 if (fSaveMessage) { 940 BEntry entry(ref, true); 941 BEntry parent; 942 entry_ref parentRef; 943 char name[B_FILE_NAME_LENGTH]; 944 945 entry.GetParent(&parent); 946 entry.GetName(name); 947 parent.GetRef(&parentRef); 948 fSaveMessage->AddRef("directory", &parentRef); 949 fSaveMessage->AddString("name", name); 950 SetTitle(name); 951 } 952 fTextView->Select(0, 0); 953 } 954 955 956 void 957 StyledEditWindow::RevertToSaved() 958 { 959 entry_ref ref; 960 const char *name; 961 962 fSaveMessage->FindRef("directory", &ref); 963 fSaveMessage->FindString("name", &name); 964 965 BDirectory dir(&ref); 966 status_t status = dir.InitCheck(); 967 BEntry entry; 968 if (status == B_OK) 969 status = entry.SetTo(&dir, name); 970 971 if (status == B_OK) 972 status = entry.GetRef(&ref); 973 974 if (status != B_OK || !entry.Exists()) { 975 BString alertText("Cannot revert, file not found: \""); 976 alertText << name << "\"."; 977 _ShowAlert(alertText, "OK", "", "", B_STOP_ALERT); 978 return; 979 } 980 981 BString alertText("Revert to the last version of \""); 982 alertText << Title() << "\"? "; 983 if (_ShowAlert(alertText, "Cancel", "OK", "", B_WARNING_ALERT) != 1) 984 return; 985 986 fTextView->Reset(); 987 if (_LoadFile(&ref) != B_OK) 988 return; 989 990 // clear undo modes 991 fUndoItem->SetLabel("Can't Undo"); 992 fUndoItem->SetEnabled(false); 993 fUndoFlag = false; 994 fCanUndo = false; 995 fRedoFlag = false; 996 fCanRedo = false; 997 998 // clear clean modes 999 fSaveItem->SetEnabled(false); 1000 fRevertItem->SetEnabled(false); 1001 fUndoCleans = false; 1002 fRedoCleans = false; 1003 fClean = true; 1004 } 1005 1006 1007 status_t 1008 StyledEditWindow::PageSetup(const char* documentName) 1009 { 1010 BPrintJob printJob(documentName); 1011 1012 if (fPrintSettings != NULL) 1013 printJob.SetSettings(new BMessage(*fPrintSettings)); 1014 1015 status_t result = printJob.ConfigPage(); 1016 if (result == B_OK) { 1017 delete fPrintSettings; 1018 fPrintSettings = printJob.Settings(); 1019 } 1020 1021 return result; 1022 } 1023 1024 1025 void 1026 StyledEditWindow::Print(const char* documentName) 1027 { 1028 BPrintJob printJob(documentName); 1029 if (fPrintSettings) 1030 printJob.SetSettings(new BMessage(*fPrintSettings)); 1031 1032 if (printJob.ConfigJob() != B_OK) 1033 return; 1034 1035 delete fPrintSettings; 1036 fPrintSettings = printJob.Settings(); 1037 1038 // information from printJob 1039 BRect printableRect = printJob.PrintableRect(); 1040 int32 firstPage = printJob.FirstPage(); 1041 int32 lastPage = printJob.LastPage(); 1042 1043 // lines eventually to be used to compute pages to print 1044 int32 firstLine = 0; 1045 int32 lastLine = fTextView->CountLines(); 1046 1047 // values to be computed 1048 int32 pagesInDocument = 1; 1049 int32 linesInDocument = fTextView->CountLines(); 1050 1051 int32 currentLine = 0; 1052 while (currentLine < linesInDocument) { 1053 float currentHeight = 0; 1054 while (currentHeight < printableRect.Height() && currentLine < linesInDocument) { 1055 currentHeight += fTextView->LineHeight(currentLine); 1056 if (currentHeight < printableRect.Height()) 1057 currentLine++; 1058 } 1059 if (pagesInDocument == lastPage) 1060 lastLine = currentLine - 1; 1061 1062 if (currentHeight >= printableRect.Height()) { 1063 pagesInDocument++; 1064 if (pagesInDocument == firstPage) 1065 firstLine = currentLine; 1066 } 1067 } 1068 1069 if (lastPage > pagesInDocument - 1) { 1070 lastPage = pagesInDocument - 1; 1071 lastLine = currentLine - 1; 1072 } 1073 1074 1075 printJob.BeginJob(); 1076 if (fTextView->CountLines() > 0 && fTextView->TextLength() > 0) { 1077 int32 printLine = firstLine; 1078 while (printLine <= lastLine) { 1079 float currentHeight = 0; 1080 int32 firstLineOnPage = printLine; 1081 while (currentHeight < printableRect.Height() && printLine <= lastLine) { 1082 currentHeight += fTextView->LineHeight(printLine); 1083 if (currentHeight < printableRect.Height()) 1084 printLine++; 1085 } 1086 1087 float top = 0; 1088 if (firstLineOnPage != 0) 1089 top = fTextView->TextHeight(0, firstLineOnPage - 1); 1090 1091 float bottom = fTextView->TextHeight(0, printLine - 1); 1092 BRect textRect(0.0, top + TEXT_INSET, printableRect.Width(), bottom + TEXT_INSET); 1093 printJob.DrawView(fTextView, textRect, B_ORIGIN); 1094 printJob.SpoolPage(); 1095 } 1096 } 1097 1098 1099 printJob.CommitJob(); 1100 } 1101 1102 1103 bool 1104 StyledEditWindow::Search(BString string, bool caseSensitive, bool wrap, bool backsearch) 1105 { 1106 int32 start; 1107 int32 finish; 1108 1109 start = B_ERROR; 1110 1111 int32 length = string.Length(); 1112 if (length == 0) 1113 return false; 1114 1115 BString viewText(fTextView->Text()); 1116 int32 textStart, textFinish; 1117 fTextView->GetSelection(&textStart, &textFinish); 1118 if (backsearch) { 1119 if (caseSensitive) { 1120 start = viewText.FindLast(string, textStart); 1121 } else { 1122 start = viewText.IFindLast(string, textStart); 1123 } 1124 } else { 1125 if (caseSensitive == true) { 1126 start = viewText.FindFirst(string, textFinish); 1127 } else { 1128 start = viewText.IFindFirst(string, textFinish); 1129 } 1130 } 1131 if (start == B_ERROR && wrap) { 1132 if (backsearch) { 1133 if (caseSensitive) { 1134 start = viewText.FindLast(string, viewText.Length()); 1135 } else { 1136 start = viewText.IFindLast(string, viewText.Length()); 1137 } 1138 } else { 1139 if (caseSensitive) { 1140 start = viewText.FindFirst(string, 0); 1141 } else { 1142 start = viewText.IFindFirst(string, 0); 1143 } 1144 } 1145 } 1146 1147 if (start != B_ERROR) { 1148 finish = start + length; 1149 fTextView->Select(start, finish); 1150 fTextView->ScrollToSelection(); 1151 return true; 1152 } 1153 1154 return false; 1155 } 1156 1157 1158 void 1159 StyledEditWindow::FindSelection() 1160 { 1161 int32 selectionStart, selectionFinish; 1162 fTextView->GetSelection(&selectionStart, &selectionFinish); 1163 1164 int32 selectionLength = selectionFinish- selectionStart; 1165 1166 BString viewText = fTextView->Text(); 1167 viewText.CopyInto(fStringToFind, selectionStart, selectionLength); 1168 fFindAgainItem->SetEnabled(true); 1169 Search(fStringToFind, fCaseSens, fWrapAround, fBackSearch); 1170 } 1171 1172 1173 bool 1174 StyledEditWindow::Replace(BString findthis, BString replaceWith, bool caseSensitive, 1175 bool wrap, bool backsearch) 1176 { 1177 if (Search(findthis, caseSensitive, wrap, backsearch)) { 1178 int32 start, finish; 1179 fTextView->GetSelection(&start, &finish); 1180 1181 fTextView->Delete(start, start + findthis.Length()); 1182 fTextView->Insert(start, replaceWith.String(), replaceWith.Length()); 1183 fTextView->Select(start, start + replaceWith.Length()); 1184 fTextView->ScrollToSelection(); 1185 return true; 1186 } 1187 1188 return false; 1189 } 1190 1191 1192 void 1193 StyledEditWindow::ReplaceAll(BString findIt, BString replaceWith, bool caseSensitive) 1194 { 1195 BString viewText(fTextView->Text()); 1196 if (caseSensitive) 1197 viewText.ReplaceAll(findIt.String(), replaceWith.String()); 1198 else 1199 viewText.IReplaceAll(findIt.String(), replaceWith.String()); 1200 1201 if (viewText.Compare(fTextView->Text()) == 0) { 1202 // they are the same 1203 return; 1204 } 1205 1206 int32 textStart, textFinish; 1207 fTextView->GetSelection(&textStart, &textFinish); 1208 1209 fTextView->SetText(viewText.String()); 1210 1211 if (viewText.Length() < textStart) 1212 textStart = viewText.Length(); 1213 if (viewText.Length() < textFinish) 1214 textFinish = viewText.Length(); 1215 1216 fTextView->Select(textStart,textFinish); 1217 fTextView->ScrollToSelection(); 1218 1219 _UpdateCleanUndoRedoSaveRevert(); 1220 } 1221 1222 1223 void 1224 StyledEditWindow::SearchAllWindows(BString find, BString replace, bool caseSensitive) 1225 { 1226 int32 numWindows; 1227 numWindows = be_app->CountWindows(); 1228 1229 BMessage *message; 1230 message= new BMessage(MSG_REPLACE_ALL); 1231 message->AddString("FindText", find); 1232 message->AddString("ReplaceText", replace); 1233 message->AddBool("casesens", caseSensitive); 1234 1235 while (numWindows >= 0) { 1236 StyledEditWindow *window = dynamic_cast<StyledEditWindow *>( 1237 be_app->WindowAt(numWindows)); 1238 1239 BMessenger messenger(window); 1240 messenger.SendMessage(message); 1241 1242 numWindows--; 1243 } 1244 } 1245 1246 1247 void 1248 StyledEditWindow::SetFontSize(float fontSize) 1249 { 1250 uint32 sameProperties; 1251 BFont font; 1252 1253 fTextView->GetFontAndColor(&font, &sameProperties); 1254 font.SetSize(fontSize); 1255 fTextView->SetFontAndColor(&font, B_FONT_SIZE); 1256 1257 _UpdateCleanUndoRedoSaveRevert(); 1258 } 1259 1260 1261 void 1262 StyledEditWindow::SetFontColor(const rgb_color *color) 1263 { 1264 uint32 sameProperties; 1265 BFont font; 1266 1267 fTextView->GetFontAndColor(&font, &sameProperties, NULL, NULL); 1268 fTextView->SetFontAndColor(&font, 0, color); 1269 1270 _UpdateCleanUndoRedoSaveRevert(); 1271 } 1272 1273 1274 void 1275 StyledEditWindow::SetFontStyle(const char *fontFamily, const char *fontStyle) 1276 { 1277 BFont font; 1278 uint32 sameProperties; 1279 1280 // find out what the old font was 1281 font_family oldFamily; 1282 font_style oldStyle; 1283 fTextView->GetFontAndColor(&font, &sameProperties); 1284 font.GetFamilyAndStyle(&oldFamily, &oldStyle); 1285 1286 // clear that family's bit on the menu, if necessary 1287 if (strcmp(oldFamily, fontFamily)) { 1288 BMenuItem* oldItem = fFontMenu->FindItem(oldFamily); 1289 if (oldItem != NULL) { 1290 oldItem->SetMarked(false); 1291 BMenu* menu = oldItem->Submenu(); 1292 if (menu != NULL) { 1293 oldItem = menu->FindItem(oldStyle); 1294 if (oldItem != NULL) 1295 oldItem->SetMarked(false); 1296 } 1297 } 1298 } 1299 1300 font.SetFamilyAndStyle(fontFamily, fontStyle); 1301 fTextView->SetFontAndColor(&font); 1302 1303 BMenuItem* superItem; 1304 superItem = fFontMenu->FindItem(fontFamily); 1305 if (superItem != NULL) { 1306 superItem->SetMarked(true); 1307 fCurrentFontItem = superItem; 1308 } 1309 1310 _UpdateCleanUndoRedoSaveRevert(); 1311 } 1312 1313 1314 void 1315 StyledEditWindow::_UpdateCleanUndoRedoSaveRevert() 1316 { 1317 fClean = false; 1318 fUndoCleans = false; 1319 fRedoCleans = false; 1320 fRevertItem->SetEnabled(fSaveMessage != NULL); 1321 fSaveItem->SetEnabled(true); 1322 fUndoItem->SetLabel("Can't Undo"); 1323 fUndoItem->SetEnabled(false); 1324 fCanUndo = false; 1325 fCanRedo = false; 1326 } 1327 1328 1329 int32 1330 StyledEditWindow::_ShowAlert(const BString& text, const BString& label, 1331 const BString& label2, const BString& label3, alert_type type) const 1332 { 1333 const char* button2 = NULL; 1334 if (label2.Length() > 0) 1335 button2 = label2.String(); 1336 1337 const char* button3 = NULL; 1338 button_spacing spacing = B_EVEN_SPACING; 1339 if (label3.Length() > 0) { 1340 button3 = label3.String(); 1341 spacing = B_OFFSET_SPACING; 1342 } 1343 1344 BAlert* alert = new BAlert("Alert", text.String(), label.String(), button2, 1345 button3, B_WIDTH_AS_USUAL, spacing, type); 1346 alert->SetShortcut(0, B_ESCAPE); 1347 1348 return alert->Go(); 1349 } 1350 1351 1352 bool 1353 StyledEditWindow::IsDocumentEntryRef(const entry_ref* ref) 1354 { 1355 if (ref == NULL) 1356 return false; 1357 1358 if (fSaveMessage == NULL) 1359 return false; 1360 1361 entry_ref dir; 1362 const char* name; 1363 if (fSaveMessage->FindRef("directory", &dir) != B_OK 1364 || fSaveMessage->FindString("name", &name) != B_OK) 1365 return false; 1366 1367 entry_ref documentRef; 1368 BPath documentPath(&dir); 1369 documentPath.Append(name); 1370 get_ref_for_path(documentPath.Path(), &documentRef); 1371 1372 if (*ref == documentRef) 1373 return true; 1374 1375 return false; 1376 } 1377 1378