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