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