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