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