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_TRANSLATE_CONTEXT 56 #define B_TRANSLATE_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_TRANSLATE_CONTEXT 118 #define B_TRANSLATE_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_TRANSLATE_CONTEXT 653 #define B_TRANSLATE_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_TRANSLATE_CONTEXT 734 #define B_TRANSLATE_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 < linesInDocument) { 866 currentHeight += fTextView->LineHeight(currentLine); 867 if (currentHeight < printableRect.Height()) 868 currentLine++; 869 } 870 if (pagesInDocument == lastPage) 871 lastLine = currentLine - 1; 872 873 if (currentHeight >= printableRect.Height()) { 874 pagesInDocument++; 875 if (pagesInDocument == firstPage) 876 firstLine = currentLine; 877 } 878 } 879 880 if (lastPage > pagesInDocument - 1) { 881 lastPage = pagesInDocument - 1; 882 lastLine = currentLine - 1; 883 } 884 885 886 printJob.BeginJob(); 887 if (fTextView->CountLines() > 0 && fTextView->TextLength() > 0) { 888 int32 printLine = firstLine; 889 while (printLine <= lastLine) { 890 float currentHeight = 0; 891 int32 firstLineOnPage = printLine; 892 while (currentHeight < printableRect.Height() && printLine <= lastLine) { 893 currentHeight += fTextView->LineHeight(printLine); 894 if (currentHeight < printableRect.Height()) 895 printLine++; 896 } 897 898 float top = 0; 899 if (firstLineOnPage != 0) 900 top = fTextView->TextHeight(0, firstLineOnPage - 1); 901 902 float bottom = fTextView->TextHeight(0, printLine - 1); 903 BRect textRect(0.0, top + TEXT_INSET, 904 printableRect.Width(), bottom + TEXT_INSET); 905 printJob.DrawView(fTextView, textRect, B_ORIGIN); 906 printJob.SpoolPage(); 907 } 908 } 909 910 911 printJob.CommitJob(); 912 } 913 914 915 void 916 StyledEditWindow::SearchAllWindows(BString find, BString replace, 917 bool caseSensitive) 918 { 919 int32 numWindows; 920 numWindows = be_app->CountWindows(); 921 922 BMessage* message; 923 message= new BMessage(MSG_REPLACE_ALL); 924 message->AddString("FindText", find); 925 message->AddString("ReplaceText", replace); 926 message->AddBool("casesens", caseSensitive); 927 928 while (numWindows >= 0) { 929 StyledEditWindow* window = dynamic_cast<StyledEditWindow *>( 930 be_app->WindowAt(numWindows)); 931 932 BMessenger messenger(window); 933 messenger.SendMessage(message); 934 935 numWindows--; 936 } 937 } 938 939 940 bool 941 StyledEditWindow::IsDocumentEntryRef(const entry_ref* ref) 942 { 943 if (ref == NULL) 944 return false; 945 946 if (fSaveMessage == NULL) 947 return false; 948 949 entry_ref dir; 950 const char* name; 951 if (fSaveMessage->FindRef("directory", &dir) != B_OK 952 || fSaveMessage->FindString("name", &name) != B_OK) 953 return false; 954 955 entry_ref documentRef; 956 BPath documentPath(&dir); 957 documentPath.Append(name); 958 get_ref_for_path(documentPath.Path(), &documentRef); 959 960 return *ref == documentRef; 961 } 962 963 964 // #pragma mark - private methods 965 966 967 #undef B_TRANSLATE_CONTEXT 968 #define B_TRANSLATE_CONTEXT "Menus" 969 970 971 void 972 StyledEditWindow::_InitWindow(uint32 encoding) 973 { 974 fPrintSettings = NULL; 975 fSaveMessage = NULL; 976 977 // undo modes 978 fUndoFlag = false; 979 fCanUndo = false; 980 fRedoFlag = false; 981 fCanRedo = false; 982 983 // clean modes 984 fUndoCleans = false; 985 fRedoCleans = false; 986 fClean = true; 987 988 // search- state 989 fReplaceString = ""; 990 fStringToFind = ""; 991 fCaseSensitive = false; 992 fWrapAround = false; 993 fBackSearch = false; 994 995 // add menubar 996 fMenuBar = new BMenuBar(BRect(0, 0, 0, 0), "menubar"); 997 AddChild(fMenuBar); 998 999 // add textview and scrollview 1000 1001 BRect viewFrame = Bounds(); 1002 viewFrame.top = fMenuBar->Bounds().Height() + 1; 1003 viewFrame.right -= B_V_SCROLL_BAR_WIDTH; 1004 viewFrame.left = 0; 1005 viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT; 1006 1007 BRect textBounds = viewFrame; 1008 textBounds.OffsetTo(B_ORIGIN); 1009 textBounds.InsetBy(TEXT_INSET, TEXT_INSET); 1010 1011 fTextView= new StyledEditView(viewFrame, textBounds, this); 1012 fTextView->SetDoesUndo(true); 1013 fTextView->SetStylable(true); 1014 fTextView->SetEncoding(encoding); 1015 1016 fScrollView = new BScrollView("scrollview", fTextView, B_FOLLOW_ALL, 0, 1017 true, true, B_PLAIN_BORDER); 1018 AddChild(fScrollView); 1019 fTextView->MakeFocus(true); 1020 1021 // Add "File"-menu: 1022 BMenu* menu = new BMenu(B_TRANSLATE("File")); 1023 fMenuBar->AddItem(menu); 1024 1025 BMenuItem* menuItem; 1026 menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("New"), 1027 new BMessage(MENU_NEW), 'N')); 1028 menuItem->SetTarget(be_app); 1029 1030 menu->AddItem(menuItem = new BMenuItem(fRecentMenu = 1031 new BMenu(B_TRANSLATE("Open" B_UTF8_ELLIPSIS)), 1032 new BMessage(MENU_OPEN))); 1033 menuItem->SetShortcut('O', 0); 1034 menuItem->SetTarget(be_app); 1035 menu->AddSeparatorItem(); 1036 1037 menu->AddItem(fSaveItem = new BMenuItem(B_TRANSLATE("Save"), 1038 new BMessage(MENU_SAVE), 'S')); 1039 fSaveItem->SetEnabled(false); 1040 menu->AddItem(menuItem = new BMenuItem( 1041 B_TRANSLATE("Save as" B_UTF8_ELLIPSIS), new BMessage(MENU_SAVEAS))); 1042 menuItem->SetShortcut('S', B_SHIFT_KEY); 1043 menuItem->SetEnabled(true); 1044 1045 menu->AddItem(fRevertItem = 1046 new BMenuItem(B_TRANSLATE("Revert to saved" B_UTF8_ELLIPSIS), 1047 new BMessage(MENU_REVERT))); 1048 fRevertItem->SetEnabled(false); 1049 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 1050 new BMessage(MENU_CLOSE), 'W')); 1051 1052 menu->AddSeparatorItem(); 1053 menu->AddItem(new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS), 1054 new BMessage(MENU_PAGESETUP))); 1055 menu->AddItem(new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS), 1056 new BMessage(MENU_PRINT), 'P')); 1057 1058 menu->AddSeparatorItem(); 1059 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 1060 new BMessage(MENU_QUIT), 'Q')); 1061 1062 // Add the "Edit"-menu: 1063 menu = new BMenu(B_TRANSLATE("Edit")); 1064 fMenuBar->AddItem(menu); 1065 1066 menu->AddItem(fUndoItem = new BMenuItem(B_TRANSLATE("Can't undo"), 1067 new BMessage(B_UNDO), 'Z')); 1068 fUndoItem->SetEnabled(false); 1069 1070 menu->AddSeparatorItem(); 1071 menu->AddItem(fCutItem = new BMenuItem(B_TRANSLATE("Cut"), 1072 new BMessage(B_CUT), 'X')); 1073 fCutItem->SetEnabled(false); 1074 fCutItem->SetTarget(fTextView); 1075 1076 menu->AddItem(fCopyItem = new BMenuItem(B_TRANSLATE("Copy"), 1077 new BMessage(B_COPY), 'C')); 1078 fCopyItem->SetEnabled(false); 1079 fCopyItem->SetTarget(fTextView); 1080 1081 menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Paste"), 1082 new BMessage(B_PASTE), 'V')); 1083 menuItem->SetTarget(fTextView); 1084 1085 menu->AddSeparatorItem(); 1086 menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Select all"), 1087 new BMessage(B_SELECT_ALL), 'A')); 1088 menuItem->SetTarget(fTextView); 1089 1090 menu->AddSeparatorItem(); 1091 menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS), 1092 new BMessage(MENU_FIND), 'F')); 1093 menu->AddItem(fFindAgainItem= new BMenuItem(B_TRANSLATE("Find again"), 1094 new BMessage(MENU_FIND_AGAIN), 'G')); 1095 fFindAgainItem->SetEnabled(false); 1096 1097 menu->AddItem(new BMenuItem(B_TRANSLATE("Find selection"), 1098 new BMessage(MENU_FIND_SELECTION), 'H')); 1099 menu->AddItem(new BMenuItem(B_TRANSLATE("Replace" B_UTF8_ELLIPSIS), 1100 new BMessage(MENU_REPLACE), 'R')); 1101 menu->AddItem(fReplaceSameItem = new BMenuItem(B_TRANSLATE("Replace next"), 1102 new BMessage(MENU_REPLACE_SAME), 'T')); 1103 fReplaceSameItem->SetEnabled(false); 1104 1105 // Add the "Font"-menu: 1106 fFontMenu = new BMenu(B_TRANSLATE("Font")); 1107 fMenuBar->AddItem(fFontMenu); 1108 1109 //"Size"-subMenu 1110 fFontSizeMenu = new BMenu(B_TRANSLATE("Size")); 1111 fFontSizeMenu->SetRadioMode(true); 1112 fFontMenu->AddItem(fFontSizeMenu); 1113 1114 const int32 fontSizes[] = {9, 10, 11, 12, 14, 18, 24, 36, 48, 72}; 1115 for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) { 1116 BMessage* fontMessage = new BMessage(FONT_SIZE); 1117 fontMessage->AddFloat("size", fontSizes[i]); 1118 1119 char label[64]; 1120 snprintf(label, sizeof(label), "%ld", fontSizes[i]); 1121 fFontSizeMenu->AddItem(menuItem = new BMenuItem(label, fontMessage)); 1122 1123 if (fontSizes[i] == (int32)be_plain_font->Size()) 1124 menuItem->SetMarked(true); 1125 } 1126 1127 // "Color"-subMenu 1128 fFontColorMenu = new BMenu(B_TRANSLATE("Color")); 1129 fFontColorMenu->SetRadioMode(true); 1130 fFontMenu->AddItem(fFontColorMenu); 1131 1132 fFontColorMenu->AddItem(fBlackItem = new BMenuItem(B_TRANSLATE("Black"), 1133 new BMessage(FONT_COLOR))); 1134 fBlackItem->SetMarked(true); 1135 fFontColorMenu->AddItem(fRedItem = new ColorMenuItem(B_TRANSLATE("Red"), 1136 RED, new BMessage(FONT_COLOR))); 1137 fFontColorMenu->AddItem(fGreenItem = new ColorMenuItem(B_TRANSLATE("Green"), 1138 GREEN, new BMessage(FONT_COLOR))); 1139 fFontColorMenu->AddItem(fBlueItem = new ColorMenuItem(B_TRANSLATE("Blue"), 1140 BLUE, new BMessage(FONT_COLOR))); 1141 fFontColorMenu->AddItem(fCyanItem = new ColorMenuItem(B_TRANSLATE("Cyan"), 1142 CYAN, new BMessage(FONT_COLOR))); 1143 fFontColorMenu->AddItem(fMagentaItem = new ColorMenuItem(B_TRANSLATE("Magenta"), 1144 MAGENTA, new BMessage(FONT_COLOR))); 1145 fFontColorMenu->AddItem(fYellowItem = new ColorMenuItem(B_TRANSLATE("Yellow"), 1146 YELLOW, new BMessage(FONT_COLOR))); 1147 fFontMenu->AddSeparatorItem(); 1148 1149 // "Bold" & "Italic" menu items 1150 fFontMenu->AddItem(fBoldItem = new BMenuItem(B_TRANSLATE("Bold"), 1151 new BMessage(kMsgSetBold))); 1152 fFontMenu->AddItem(fItalicItem = new BMenuItem(B_TRANSLATE("Italic"), 1153 new BMessage(kMsgSetItalic))); 1154 fBoldItem->SetShortcut('B', 0); 1155 fItalicItem->SetShortcut('I', 0); 1156 fFontMenu->AddSeparatorItem(); 1157 1158 // Available fonts 1159 1160 fCurrentFontItem = 0; 1161 fCurrentStyleItem = 0; 1162 1163 BMenu* subMenu; 1164 int32 numFamilies = count_font_families(); 1165 for (int32 i = 0; i < numFamilies; i++) { 1166 font_family family; 1167 if (get_font_family(i, &family) == B_OK) { 1168 subMenu = new BMenu(family); 1169 subMenu->SetRadioMode(true); 1170 fFontMenu->AddItem(new BMenuItem(subMenu, 1171 new BMessage(FONT_FAMILY))); 1172 1173 int32 numStyles = count_font_styles(family); 1174 for (int32 j = 0; j < numStyles; j++) { 1175 font_style style; 1176 uint32 flags; 1177 if (get_font_style(family, j, &style, &flags) == B_OK) { 1178 subMenu->AddItem(new BMenuItem(style, 1179 new BMessage(FONT_STYLE))); 1180 } 1181 } 1182 } 1183 } 1184 1185 // Add the "Document"-menu: 1186 menu = new BMenu(B_TRANSLATE("Document")); 1187 fMenuBar->AddItem(menu); 1188 1189 // "Align"-subMenu: 1190 subMenu = new BMenu(B_TRANSLATE("Align")); 1191 subMenu->SetRadioMode(true); 1192 1193 subMenu->AddItem(fAlignLeft = new BMenuItem(B_TRANSLATE("Left"), 1194 new BMessage(ALIGN_LEFT))); 1195 fAlignLeft->SetMarked(true); 1196 fAlignLeft->SetShortcut('L', B_OPTION_KEY); 1197 1198 subMenu->AddItem(fAlignCenter = new BMenuItem(B_TRANSLATE("Center"), 1199 new BMessage(ALIGN_CENTER))); 1200 fAlignCenter->SetShortcut('C', B_OPTION_KEY); 1201 1202 subMenu->AddItem(fAlignRight = new BMenuItem(B_TRANSLATE("Right"), 1203 new BMessage(ALIGN_RIGHT))); 1204 fAlignRight->SetShortcut('R', B_OPTION_KEY); 1205 1206 menu->AddItem(subMenu); 1207 menu->AddItem(fWrapItem = new BMenuItem(B_TRANSLATE("Wrap lines"), 1208 new BMessage(WRAP_LINES))); 1209 fWrapItem->SetMarked(true); 1210 fWrapItem->SetShortcut('W', B_OPTION_KEY); 1211 1212 menu->AddSeparatorItem(); 1213 menu->AddItem(new BMenuItem(B_TRANSLATE("Statistics" B_UTF8_ELLIPSIS), 1214 new BMessage(SHOW_STATISTICS))); 1215 1216 fSavePanel = NULL; 1217 fSavePanelEncodingMenu = NULL; 1218 // build lazily 1219 } 1220 1221 1222 void 1223 StyledEditWindow::_LoadAttrs() 1224 { 1225 entry_ref dir; 1226 const char* name; 1227 if (fSaveMessage->FindRef("directory", &dir) != B_OK 1228 || fSaveMessage->FindString("name", &name) != B_OK) 1229 return; 1230 1231 BPath documentPath(&dir); 1232 documentPath.Append(name); 1233 1234 BNode documentNode(documentPath.Path()); 1235 if (documentNode.InitCheck() != B_OK) 1236 return; 1237 1238 BRect newFrame; 1239 ssize_t bytesRead = documentNode.ReadAttr(kInfoAttributeName, B_RECT_TYPE, 1240 0, &newFrame, sizeof(BRect)); 1241 if (bytesRead != sizeof(BRect)) 1242 return; 1243 1244 swap_data(B_RECT_TYPE, &newFrame, sizeof(BRect), B_SWAP_BENDIAN_TO_HOST); 1245 1246 // Check if the frame in on screen, otherwise, ignore it 1247 BScreen screen(this); 1248 if (newFrame.Width() > 32 && newFrame.Height() > 32 1249 && screen.Frame().Contains(newFrame)) { 1250 MoveTo(newFrame.left, newFrame.top); 1251 ResizeTo(newFrame.Width(), newFrame.Height()); 1252 } 1253 } 1254 1255 1256 void 1257 StyledEditWindow::_SaveAttrs() 1258 { 1259 if (!fSaveMessage) 1260 return; 1261 1262 entry_ref dir; 1263 const char* name; 1264 if (fSaveMessage->FindRef("directory", &dir) != B_OK 1265 || fSaveMessage->FindString("name", &name) != B_OK) 1266 return; 1267 1268 BPath documentPath(&dir); 1269 documentPath.Append(name); 1270 1271 BNode documentNode(documentPath.Path()); 1272 if (documentNode.InitCheck() != B_OK) 1273 return; 1274 1275 BRect frame(Frame()); 1276 swap_data(B_RECT_TYPE, &frame, sizeof(BRect), B_SWAP_HOST_TO_BENDIAN); 1277 1278 documentNode.WriteAttr(kInfoAttributeName, B_RECT_TYPE, 0, &frame, 1279 sizeof(BRect)); 1280 } 1281 1282 1283 #undef B_TRANSLATE_CONTEXT 1284 #define B_TRANSLATE_CONTEXT "LoadAlert" 1285 1286 1287 status_t 1288 StyledEditWindow::_LoadFile(entry_ref* ref) 1289 { 1290 BEntry entry(ref, true); 1291 // traverse an eventual link 1292 1293 status_t status = entry.InitCheck(); 1294 if (status == B_OK && entry.IsDirectory()) 1295 status = B_IS_A_DIRECTORY; 1296 1297 BFile file; 1298 if (status == B_OK) 1299 status = file.SetTo(&entry, B_READ_ONLY); 1300 if (status == B_OK) 1301 status = fTextView->GetStyledText(&file); 1302 1303 if (status == B_ENTRY_NOT_FOUND) { 1304 // Treat non-existing files consideratley; we just want to get an 1305 // empty window for them - to create this new document 1306 status = B_OK; 1307 } 1308 1309 if (status != B_OK) { 1310 // If an error occured, bail out and tell the user what happened 1311 BEntry entry(ref, true); 1312 char name[B_FILE_NAME_LENGTH]; 1313 if (entry.GetName(name) != B_OK) 1314 strlcpy(name, B_TRANSLATE("???"), sizeof(name)); 1315 1316 BString text; 1317 if (status == B_BAD_TYPE) 1318 bs_printf(&text, 1319 B_TRANSLATE("Error loading \"%s\":\n\tUnsupported format"), name); 1320 else 1321 bs_printf(&text, B_TRANSLATE("Error loading \"%s\":\n\t%s"), 1322 name, strerror(status)); 1323 1324 _ShowAlert(text, B_TRANSLATE("OK"), "", "", B_STOP_ALERT); 1325 return status; 1326 } 1327 1328 // update alignment 1329 switch (fTextView->Alignment()) { 1330 case B_ALIGN_LEFT: 1331 default: 1332 fAlignLeft->SetMarked(true); 1333 break; 1334 case B_ALIGN_CENTER: 1335 fAlignCenter->SetMarked(true); 1336 break; 1337 case B_ALIGN_RIGHT: 1338 fAlignRight->SetMarked(true); 1339 break; 1340 } 1341 1342 // update word wrapping 1343 fWrapItem->SetMarked(fTextView->DoesWordWrap()); 1344 return B_OK; 1345 } 1346 1347 1348 #undef B_TRANSLATE_CONTEXT 1349 #define B_TRANSLATE_CONTEXT "RevertToSavedAlert" 1350 1351 1352 void 1353 StyledEditWindow::_RevertToSaved() 1354 { 1355 entry_ref ref; 1356 const char* name; 1357 1358 fSaveMessage->FindRef("directory", &ref); 1359 fSaveMessage->FindString("name", &name); 1360 1361 BDirectory dir(&ref); 1362 status_t status = dir.InitCheck(); 1363 BEntry entry; 1364 if (status == B_OK) 1365 status = entry.SetTo(&dir, name); 1366 1367 if (status == B_OK) 1368 status = entry.GetRef(&ref); 1369 1370 if (status != B_OK || !entry.Exists()) { 1371 BString alertText; 1372 bs_printf(&alertText, 1373 B_TRANSLATE("Cannot revert, file not found: \"%s\"."), name); 1374 _ShowAlert(alertText, B_TRANSLATE("OK"), "", "", B_STOP_ALERT); 1375 return; 1376 } 1377 1378 BString alertText; 1379 bs_printf(&alertText, 1380 B_TRANSLATE("Revert to the last version of \"%s\"? "), Title()); 1381 if (_ShowAlert(alertText, B_TRANSLATE("Cancel"), B_TRANSLATE("OK"), 1382 "", B_WARNING_ALERT) != 1) 1383 return; 1384 1385 fTextView->Reset(); 1386 if (_LoadFile(&ref) != B_OK) 1387 return; 1388 1389 #undef B_TRANSLATE_CONTEXT 1390 #define B_TRANSLATE_CONTEXT "Menus" 1391 1392 // clear undo modes 1393 fUndoItem->SetLabel(B_TRANSLATE("Can't undo")); 1394 fUndoItem->SetEnabled(false); 1395 fUndoFlag = false; 1396 fCanUndo = false; 1397 fRedoFlag = false; 1398 fCanRedo = false; 1399 1400 // clear clean modes 1401 fSaveItem->SetEnabled(false); 1402 fRevertItem->SetEnabled(false); 1403 fUndoCleans = false; 1404 fRedoCleans = false; 1405 fClean = true; 1406 } 1407 1408 1409 bool 1410 StyledEditWindow::_Search(BString string, bool caseSensitive, bool wrap, 1411 bool backSearch) 1412 { 1413 int32 start; 1414 int32 finish; 1415 1416 start = B_ERROR; 1417 1418 int32 length = string.Length(); 1419 if (length == 0) 1420 return false; 1421 1422 BString viewText(fTextView->Text()); 1423 int32 textStart, textFinish; 1424 fTextView->GetSelection(&textStart, &textFinish); 1425 if (backSearch) { 1426 if (caseSensitive) 1427 start = viewText.FindLast(string, textStart); 1428 else 1429 start = viewText.IFindLast(string, textStart); 1430 } else { 1431 if (caseSensitive) 1432 start = viewText.FindFirst(string, textFinish); 1433 else 1434 start = viewText.IFindFirst(string, textFinish); 1435 } 1436 if (start == B_ERROR && wrap) { 1437 if (backSearch) { 1438 if (caseSensitive) 1439 start = viewText.FindLast(string, viewText.Length()); 1440 else 1441 start = viewText.IFindLast(string, viewText.Length()); 1442 } else { 1443 if (caseSensitive) 1444 start = viewText.FindFirst(string, 0); 1445 else 1446 start = viewText.IFindFirst(string, 0); 1447 } 1448 } 1449 1450 if (start != B_ERROR) { 1451 finish = start + length; 1452 fTextView->Select(start, finish); 1453 fTextView->ScrollToSelection(); 1454 return true; 1455 } 1456 1457 return false; 1458 } 1459 1460 1461 void 1462 StyledEditWindow::_FindSelection() 1463 { 1464 int32 selectionStart, selectionFinish; 1465 fTextView->GetSelection(&selectionStart, &selectionFinish); 1466 1467 int32 selectionLength = selectionFinish- selectionStart; 1468 1469 BString viewText = fTextView->Text(); 1470 viewText.CopyInto(fStringToFind, selectionStart, selectionLength); 1471 fFindAgainItem->SetEnabled(true); 1472 _Search(fStringToFind, fCaseSensitive, fWrapAround, fBackSearch); 1473 } 1474 1475 1476 bool 1477 StyledEditWindow::_Replace(BString findThis, BString replaceWith, 1478 bool caseSensitive, bool wrap, bool backSearch) 1479 { 1480 if (_Search(findThis, caseSensitive, wrap, backSearch)) { 1481 int32 start, finish; 1482 fTextView->GetSelection(&start, &finish); 1483 1484 _UpdateCleanUndoRedoSaveRevert(); 1485 fTextView->SetSuppressChanges(true); 1486 fTextView->Delete(start, start + findThis.Length()); 1487 fTextView->Insert(start, replaceWith.String(), replaceWith.Length()); 1488 fTextView->SetSuppressChanges(false); 1489 fTextView->Select(start, start + replaceWith.Length()); 1490 fTextView->ScrollToSelection(); 1491 return true; 1492 } 1493 1494 return false; 1495 } 1496 1497 1498 void 1499 StyledEditWindow::_ReplaceAll(BString findThis, BString replaceWith, 1500 bool caseSensitive) 1501 { 1502 bool first = true; 1503 fTextView->SetSuppressChanges(true); 1504 while (_Search(findThis, caseSensitive, true, false)) { 1505 if (first) { 1506 _UpdateCleanUndoRedoSaveRevert(); 1507 first = false; 1508 } 1509 int32 start, finish; 1510 fTextView->GetSelection(&start, &finish); 1511 1512 fTextView->Delete(start, start + findThis.Length()); 1513 fTextView->Insert(start, replaceWith.String(), replaceWith.Length()); 1514 } 1515 fTextView->SetSuppressChanges(false); 1516 } 1517 1518 1519 void 1520 StyledEditWindow::_SetFontSize(float fontSize) 1521 { 1522 uint32 sameProperties; 1523 BFont font; 1524 1525 fTextView->GetFontAndColor(&font, &sameProperties); 1526 font.SetSize(fontSize); 1527 fTextView->SetFontAndColor(&font, B_FONT_SIZE); 1528 1529 _UpdateCleanUndoRedoSaveRevert(); 1530 } 1531 1532 1533 void 1534 StyledEditWindow::_SetFontColor(const rgb_color* color) 1535 { 1536 uint32 sameProperties; 1537 BFont font; 1538 1539 fTextView->GetFontAndColor(&font, &sameProperties, NULL, NULL); 1540 fTextView->SetFontAndColor(&font, 0, color); 1541 1542 _UpdateCleanUndoRedoSaveRevert(); 1543 } 1544 1545 1546 void 1547 StyledEditWindow::_SetFontStyle(const char* fontFamily, const char* fontStyle) 1548 { 1549 BFont font; 1550 uint32 sameProperties; 1551 1552 // find out what the old font was 1553 font_family oldFamily; 1554 font_style oldStyle; 1555 fTextView->GetFontAndColor(&font, &sameProperties); 1556 font.GetFamilyAndStyle(&oldFamily, &oldStyle); 1557 1558 // clear that family's bit on the menu, if necessary 1559 if (strcmp(oldFamily, fontFamily)) { 1560 BMenuItem* oldItem = fFontMenu->FindItem(oldFamily); 1561 if (oldItem != NULL) { 1562 oldItem->SetMarked(false); 1563 BMenu* menu = oldItem->Submenu(); 1564 if (menu != NULL) { 1565 oldItem = menu->FindItem(oldStyle); 1566 if (oldItem != NULL) 1567 oldItem->SetMarked(false); 1568 } 1569 } 1570 } 1571 1572 font.SetFamilyAndStyle(fontFamily, fontStyle); 1573 1574 uint16 face = 0; 1575 1576 if (!(font.Face() & B_REGULAR_FACE)) 1577 face = font.Face(); 1578 1579 if (fBoldItem->IsMarked()) 1580 face |= B_BOLD_FACE; 1581 1582 if (fItalicItem->IsMarked()) 1583 face |= B_ITALIC_FACE; 1584 1585 font.SetFace(face); 1586 1587 fTextView->SetFontAndColor(&font); 1588 1589 BMenuItem* superItem; 1590 superItem = fFontMenu->FindItem(fontFamily); 1591 if (superItem != NULL) { 1592 superItem->SetMarked(true); 1593 fCurrentFontItem = superItem; 1594 } 1595 1596 _UpdateCleanUndoRedoSaveRevert(); 1597 } 1598 1599 1600 #undef B_TRANSLATE_CONTEXT 1601 #define B_TRANSLATE_CONTEXT "Statistics" 1602 1603 1604 int32 1605 StyledEditWindow::_ShowStatistics() 1606 { 1607 size_t words = 0; 1608 bool inWord = false; 1609 size_t length = fTextView->TextLength(); 1610 1611 for (size_t i = 0; i < length; i++) { 1612 if (BUnicodeChar::IsSpace(fTextView->Text()[i])) { 1613 inWord = false; 1614 } else if (!inWord) { 1615 words++; 1616 inWord = true; 1617 } 1618 } 1619 1620 BString result; 1621 result << B_TRANSLATE("Document statistics") << '\n' << '\n' 1622 << B_TRANSLATE("Lines:") << ' ' << fTextView->CountLines() << '\n' 1623 << B_TRANSLATE("Characters:") << ' ' << length << '\n' 1624 << B_TRANSLATE("Words:") << ' ' << words; 1625 1626 BAlert* alert = new BAlert("Statistics", result, B_TRANSLATE("OK"), NULL, 1627 NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_INFO_ALERT); 1628 1629 return alert->Go(); 1630 } 1631 1632 1633 #undef B_TRANSLATE_CONTEXT 1634 #define B_TRANSLATE_CONTEXT "Menus" 1635 1636 1637 void 1638 StyledEditWindow::_UpdateCleanUndoRedoSaveRevert() 1639 { 1640 fClean = false; 1641 fUndoCleans = false; 1642 fRedoCleans = false; 1643 fRevertItem->SetEnabled(fSaveMessage != NULL); 1644 fSaveItem->SetEnabled(true); 1645 fUndoItem->SetLabel(B_TRANSLATE("Can't undo")); 1646 fUndoItem->SetEnabled(false); 1647 fCanUndo = false; 1648 fCanRedo = false; 1649 } 1650 1651 1652 int32 1653 StyledEditWindow::_ShowAlert(const BString& text, const BString& label, 1654 const BString& label2, const BString& label3, alert_type type) const 1655 { 1656 const char* button2 = NULL; 1657 if (label2.Length() > 0) 1658 button2 = label2.String(); 1659 1660 const char* button3 = NULL; 1661 button_spacing spacing = B_EVEN_SPACING; 1662 if (label3.Length() > 0) { 1663 button3 = label3.String(); 1664 spacing = B_OFFSET_SPACING; 1665 } 1666 1667 BAlert* alert = new BAlert("Alert", text.String(), label.String(), button2, 1668 button3, B_WIDTH_AS_USUAL, spacing, type); 1669 alert->SetShortcut(0, B_ESCAPE); 1670 1671 return alert->Go(); 1672 } 1673