1 /* 2 * Copyright 2002-2012, 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 * Vlad Slepukhin 12 * Sarzhuk Zharski 13 */ 14 15 16 #include "ColorMenuItem.h" 17 #include "Constants.h" 18 #include "FindWindow.h" 19 #include "ReplaceWindow.h" 20 #include "StatusView.h" 21 #include "StyledEditApp.h" 22 #include "StyledEditView.h" 23 #include "StyledEditWindow.h" 24 25 #include <Alert.h> 26 #include <Autolock.h> 27 #include <Catalog.h> 28 #include <CharacterSet.h> 29 #include <CharacterSetRoster.h> 30 #include <Clipboard.h> 31 #include <Debug.h> 32 #include <File.h> 33 #include <FilePanel.h> 34 #include <fs_attr.h> 35 #include <Locale.h> 36 #include <Menu.h> 37 #include <MenuBar.h> 38 #include <MenuItem.h> 39 #include <NodeMonitor.h> 40 #include <Path.h> 41 #include <PrintJob.h> 42 #include <Rect.h> 43 #include <Roster.h> 44 #include <Screen.h> 45 #include <ScrollView.h> 46 #include <TextControl.h> 47 #include <TextView.h> 48 #include <TranslationUtils.h> 49 #include <UnicodeChar.h> 50 #include <UTF8.h> 51 #include <Volume.h> 52 53 54 using namespace BPrivate; 55 56 57 const float kLineViewWidth = 30.0; 58 const char* kInfoAttributeName = "StyledEdit-info"; 59 60 61 #undef B_TRANSLATION_CONTEXT 62 #define B_TRANSLATION_CONTEXT "StyledEditWindow" 63 64 65 // This is a temporary solution for building BString with printf like format. 66 // will be removed in the future. 67 static void 68 bs_printf(BString* string, const char* format, ...) 69 { 70 va_list ap; 71 va_start(ap, format); 72 char* buf; 73 vasprintf(&buf, format, ap); 74 string->SetTo(buf); 75 free(buf); 76 va_end(ap); 77 } 78 79 80 // #pragma mark - 81 82 83 StyledEditWindow::StyledEditWindow(BRect frame, int32 id, uint32 encoding) 84 : BWindow(frame, "untitled", B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS) 85 { 86 _InitWindow(encoding); 87 BString unTitled(B_TRANSLATE("Untitled ")); 88 unTitled << id; 89 SetTitle(unTitled.String()); 90 fSaveItem->SetEnabled(true); 91 // allow saving empty files 92 Show(); 93 } 94 95 96 StyledEditWindow::StyledEditWindow(BRect frame, entry_ref* ref, uint32 encoding) 97 : BWindow(frame, "untitled", B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS) 98 { 99 _InitWindow(encoding); 100 OpenFile(ref); 101 Show(); 102 } 103 104 105 StyledEditWindow::~StyledEditWindow() 106 { 107 delete fSaveMessage; 108 delete fPrintSettings; 109 delete fSavePanel; 110 } 111 112 113 void 114 StyledEditWindow::Quit() 115 { 116 _SwitchNodeMonitor(false); 117 118 _SaveAttrs(); 119 if (StyledEditApp* app = dynamic_cast<StyledEditApp*>(be_app)) 120 app->CloseDocument(); 121 BWindow::Quit(); 122 } 123 124 125 #undef B_TRANSLATION_CONTEXT 126 #define B_TRANSLATION_CONTEXT "QuitAlert" 127 128 129 bool 130 StyledEditWindow::QuitRequested() 131 { 132 if (fClean) 133 return true; 134 135 if (fTextView->TextLength() == 0 && fSaveMessage == NULL) 136 return true; 137 138 BString alertText; 139 bs_printf(&alertText, 140 B_TRANSLATE("Save changes to the document \"%s\"? "), Title()); 141 142 int32 index = _ShowAlert(alertText, B_TRANSLATE("Cancel"), 143 B_TRANSLATE("Don't save"), B_TRANSLATE("Save"), B_WARNING_ALERT); 144 145 if (index == 0) 146 return false; // "cancel": dont save, dont close the window 147 148 if (index == 1) 149 return true; // "don't save": just close the window 150 151 if (!fSaveMessage) { 152 SaveAs(new BMessage(SAVE_THEN_QUIT)); 153 return false; 154 } 155 156 return Save() == B_OK; 157 } 158 159 160 void 161 StyledEditWindow::MessageReceived(BMessage* message) 162 { 163 if (message->WasDropped()) { 164 entry_ref ref; 165 if (message->FindRef("refs", 0, &ref)==B_OK) { 166 message->what = B_REFS_RECEIVED; 167 be_app->PostMessage(message); 168 } 169 } 170 171 switch (message->what) { 172 // File menu 173 case MENU_SAVE: 174 if (!fSaveMessage) 175 SaveAs(); 176 else 177 Save(fSaveMessage); 178 break; 179 180 case MENU_SAVEAS: 181 SaveAs(); 182 break; 183 184 case B_SAVE_REQUESTED: 185 Save(message); 186 break; 187 188 case SAVE_THEN_QUIT: 189 if (Save(message) == B_OK) 190 Quit(); 191 break; 192 193 case MENU_RELOAD: 194 _ReloadDocument(message); 195 break; 196 197 case MENU_CLOSE: 198 if (QuitRequested()) 199 Quit(); 200 break; 201 202 case MENU_PAGESETUP: 203 PageSetup(fTextView->Window()->Title()); 204 break; 205 case MENU_PRINT: 206 Print(fTextView->Window()->Title()); 207 break; 208 case MENU_QUIT: 209 be_app->PostMessage(B_QUIT_REQUESTED); 210 break; 211 212 // Edit menu 213 214 case B_UNDO: 215 ASSERT(fCanUndo || fCanRedo); 216 ASSERT(!(fCanUndo && fCanRedo)); 217 if (fCanUndo) 218 fUndoFlag = true; 219 if (fCanRedo) 220 fRedoFlag = true; 221 222 fTextView->Undo(be_clipboard); 223 break; 224 case B_CUT: 225 fTextView->Cut(be_clipboard); 226 break; 227 case B_COPY: 228 fTextView->Copy(be_clipboard); 229 break; 230 case B_PASTE: 231 fTextView->Paste(be_clipboard); 232 break; 233 case MENU_CLEAR: 234 fTextView->Clear(); 235 break; 236 case MENU_FIND: 237 { 238 BRect findWindowFrame(100, 100, 400, 235); 239 BWindow* window = new FindWindow(findWindowFrame, this, 240 &fStringToFind, fCaseSensitive, fWrapAround, fBackSearch); 241 window->Show(); 242 break; 243 } 244 case MSG_SEARCH: 245 message->FindString("findtext", &fStringToFind); 246 fFindAgainItem->SetEnabled(true); 247 message->FindBool("casesens", &fCaseSensitive); 248 message->FindBool("wrap", &fWrapAround); 249 message->FindBool("backsearch", &fBackSearch); 250 251 _Search(fStringToFind, fCaseSensitive, fWrapAround, fBackSearch); 252 break; 253 case MENU_FIND_AGAIN: 254 _Search(fStringToFind, fCaseSensitive, fWrapAround, fBackSearch); 255 break; 256 case MENU_FIND_SELECTION: 257 _FindSelection(); 258 break; 259 case MENU_REPLACE: 260 { 261 BRect replaceWindowFrame(100, 100, 400, 284); 262 BWindow* window = new ReplaceWindow(replaceWindowFrame, this, 263 &fStringToFind, &fReplaceString, fCaseSensitive, fWrapAround, 264 fBackSearch); 265 window->Show(); 266 break; 267 } 268 case MSG_REPLACE: 269 { 270 message->FindBool("casesens", &fCaseSensitive); 271 message->FindBool("wrap", &fWrapAround); 272 message->FindBool("backsearch", &fBackSearch); 273 274 message->FindString("FindText", &fStringToFind); 275 message->FindString("ReplaceText", &fReplaceString); 276 277 fFindAgainItem->SetEnabled(true); 278 fReplaceSameItem->SetEnabled(true); 279 280 _Replace(fStringToFind, fReplaceString, fCaseSensitive, fWrapAround, 281 fBackSearch); 282 break; 283 } 284 case MENU_REPLACE_SAME: 285 _Replace(fStringToFind, fReplaceString, fCaseSensitive, fWrapAround, 286 fBackSearch); 287 break; 288 289 case MSG_REPLACE_ALL: 290 { 291 message->FindBool("casesens", &fCaseSensitive); 292 message->FindString("FindText", &fStringToFind); 293 message->FindString("ReplaceText", &fReplaceString); 294 295 bool allWindows; 296 message->FindBool("allwindows", &allWindows); 297 298 fFindAgainItem->SetEnabled(true); 299 fReplaceSameItem->SetEnabled(true); 300 301 if (allWindows) 302 SearchAllWindows(fStringToFind, fReplaceString, fCaseSensitive); 303 else 304 _ReplaceAll(fStringToFind, fReplaceString, fCaseSensitive); 305 break; 306 } 307 308 case B_NODE_MONITOR: 309 _HandleNodeMonitorEvent(message); 310 break; 311 312 // Font menu 313 314 case FONT_SIZE: 315 { 316 float fontSize; 317 if (message->FindFloat("size", &fontSize) == B_OK) 318 _SetFontSize(fontSize); 319 break; 320 } 321 case FONT_FAMILY: 322 { 323 const char* fontFamily = NULL; 324 const char* fontStyle = NULL; 325 void* ptr; 326 if (message->FindPointer("source", &ptr) == B_OK) { 327 BMenuItem* item = static_cast<BMenuItem*>(ptr); 328 fontFamily = item->Label(); 329 } 330 331 BFont font; 332 font.SetFamilyAndStyle(fontFamily, fontStyle); 333 fItalicItem->SetMarked((font.Face() & B_ITALIC_FACE) != 0); 334 fBoldItem->SetMarked((font.Face() & B_BOLD_FACE) != 0); 335 336 _SetFontStyle(fontFamily, fontStyle); 337 break; 338 } 339 case FONT_STYLE: 340 { 341 const char* fontFamily = NULL; 342 const char* fontStyle = NULL; 343 void* ptr; 344 if (message->FindPointer("source", &ptr) == B_OK) { 345 BMenuItem* item = static_cast<BMenuItem*>(ptr); 346 fontStyle = item->Label(); 347 BMenu* menu = item->Menu(); 348 if (menu != NULL) { 349 BMenuItem* super_item = menu->Superitem(); 350 if (super_item != NULL) 351 fontFamily = super_item->Label(); 352 } 353 } 354 355 BFont font; 356 font.SetFamilyAndStyle(fontFamily, fontStyle); 357 fItalicItem->SetMarked((font.Face() & B_ITALIC_FACE) != 0); 358 fBoldItem->SetMarked((font.Face() & B_BOLD_FACE) != 0); 359 360 _SetFontStyle(fontFamily, fontStyle); 361 break; 362 } 363 case kMsgSetItalic: 364 { 365 uint32 sameProperties; 366 BFont font; 367 fTextView->GetFontAndColor(&font, &sameProperties); 368 369 if (fItalicItem->IsMarked()) 370 font.SetFace(B_REGULAR_FACE); 371 fItalicItem->SetMarked(!fItalicItem->IsMarked()); 372 373 font_family family; 374 font_style style; 375 font.GetFamilyAndStyle(&family, &style); 376 377 _SetFontStyle(family, style); 378 break; 379 } 380 case kMsgSetBold: 381 { 382 uint32 sameProperties; 383 BFont font; 384 fTextView->GetFontAndColor(&font, &sameProperties); 385 386 if (fBoldItem->IsMarked()) 387 font.SetFace(B_REGULAR_FACE); 388 fBoldItem->SetMarked(!fBoldItem->IsMarked()); 389 390 font_family family; 391 font_style style; 392 font.GetFamilyAndStyle(&family, &style); 393 394 _SetFontStyle(family, style); 395 break; 396 } 397 case FONT_COLOR: 398 { 399 void* ptr; 400 if (message->FindPointer("source", &ptr) == B_OK) { 401 if (ptr == fBlackItem) 402 _SetFontColor(&BLACK); 403 else if (ptr == fRedItem) 404 _SetFontColor(&RED); 405 else if (ptr == fGreenItem) 406 _SetFontColor(&GREEN); 407 else if (ptr == fBlueItem) 408 _SetFontColor(&BLUE); 409 else if (ptr == fCyanItem) 410 _SetFontColor(&CYAN); 411 else if (ptr == fMagentaItem) 412 _SetFontColor(&MAGENTA); 413 else if (ptr == fYellowItem) 414 _SetFontColor(&YELLOW); 415 } 416 break; 417 } 418 419 // Document menu 420 421 case ALIGN_LEFT: 422 fTextView->SetAlignment(B_ALIGN_LEFT); 423 _UpdateCleanUndoRedoSaveRevert(); 424 break; 425 case ALIGN_CENTER: 426 fTextView->SetAlignment(B_ALIGN_CENTER); 427 _UpdateCleanUndoRedoSaveRevert(); 428 break; 429 case ALIGN_RIGHT: 430 fTextView->SetAlignment(B_ALIGN_RIGHT); 431 _UpdateCleanUndoRedoSaveRevert(); 432 break; 433 case WRAP_LINES: 434 { 435 BRect textRect(fTextView->Bounds()); 436 textRect.OffsetTo(B_ORIGIN); 437 textRect.InsetBy(TEXT_INSET, TEXT_INSET); 438 if (fTextView->DoesWordWrap()) { 439 fTextView->SetWordWrap(false); 440 fWrapItem->SetMarked(false); 441 // the width comes from stylededit R5. TODO: find a better way 442 textRect.SetRightBottom(BPoint(1500.0, textRect.RightBottom().y)); 443 } else { 444 fTextView->SetWordWrap(true); 445 fWrapItem->SetMarked(true); 446 } 447 fTextView->SetTextRect(textRect); 448 449 _UpdateCleanUndoRedoSaveRevert(); 450 break; 451 } 452 case SHOW_STATISTICS: 453 _ShowStatistics(); 454 break; 455 case ENABLE_ITEMS: 456 fCutItem->SetEnabled(true); 457 fCopyItem->SetEnabled(true); 458 break; 459 case DISABLE_ITEMS: 460 fCutItem->SetEnabled(false); 461 fCopyItem->SetEnabled(false); 462 break; 463 case TEXT_CHANGED: 464 if (fUndoFlag) { 465 if (fUndoCleans) { 466 // we cleaned! 467 fClean = true; 468 fUndoCleans = false; 469 } else if (fClean) { 470 // if we were clean 471 // then a redo will make us clean again 472 fRedoCleans = true; 473 fClean = false; 474 } 475 // set mode 476 fCanUndo = false; 477 fCanRedo = true; 478 fUndoItem->SetLabel(B_TRANSLATE("Redo typing")); 479 fUndoItem->SetEnabled(true); 480 fUndoFlag = false; 481 } else { 482 if (fRedoFlag && fRedoCleans) { 483 // we cleaned! 484 fClean = true; 485 fRedoCleans = false; 486 } else if (fClean) { 487 // if we were clean 488 // then an undo will make us clean again 489 fUndoCleans = true; 490 fClean = false; 491 } else { 492 // no more cleaning from undo now... 493 fUndoCleans = false; 494 } 495 // set mode 496 fCanUndo = true; 497 fCanRedo = false; 498 fUndoItem->SetLabel(B_TRANSLATE("Undo typing")); 499 fUndoItem->SetEnabled(true); 500 fRedoFlag = false; 501 } 502 if (fClean) { 503 fSaveItem->SetEnabled(fSaveMessage == NULL); 504 } else { 505 fSaveItem->SetEnabled(true); 506 } 507 fReloadItem->SetEnabled(fSaveMessage != NULL); 508 fEncodingItem->SetEnabled(fSaveMessage != NULL); 509 break; 510 511 case SAVE_AS_ENCODING: 512 void* ptr; 513 if (message->FindPointer("source", &ptr) == B_OK 514 && fSavePanelEncodingMenu != NULL) { 515 fTextView->SetEncoding( 516 (uint32)fSavePanelEncodingMenu->IndexOf((BMenuItem*)ptr)); 517 } 518 break; 519 520 case UPDATE_STATUS: 521 { 522 message->AddBool("modified", !fClean); 523 bool readOnly = !fTextView->IsEditable(); 524 message->AddBool("readOnly", readOnly); 525 if (readOnly) { 526 BVolume volume(fNodeRef.device); 527 message->AddBool("canUnlock", !volume.IsReadOnly()); 528 } 529 fStatusView->SetStatus(message); 530 break; 531 } 532 533 case UNLOCK_FILE: 534 { 535 status_t status = _UnlockFile(); 536 if (status != B_OK) { 537 BString text; 538 bs_printf(&text, 539 B_TRANSLATE("Unable to unlock file\n\t%s"), 540 strerror(status)); 541 _ShowAlert(text, B_TRANSLATE("OK"), "", "", B_STOP_ALERT); 542 } 543 PostMessage(UPDATE_STATUS); 544 break; 545 } 546 547 case UPDATE_LINE_SELECTION: 548 { 549 int32 line; 550 if (message->FindInt32("be:line", &line) == B_OK) { 551 fTextView->GoToLine(line); 552 fTextView->ScrollToSelection(); 553 } 554 555 int32 start, length; 556 if (message->FindInt32("be:selection_offset", &start) == B_OK) { 557 if (message->FindInt32("be:selection_length", &length) != B_OK) 558 length = 0; 559 560 fTextView->Select(start, start + length); 561 fTextView->ScrollToOffset(start); 562 } 563 break; 564 } 565 default: 566 BWindow::MessageReceived(message); 567 break; 568 } 569 } 570 571 572 void 573 StyledEditWindow::MenusBeginning() 574 { 575 // set up the recent documents menu 576 BMessage documents; 577 be_roster->GetRecentDocuments(&documents, 9, NULL, APP_SIGNATURE); 578 579 // delete old items.. 580 // shatty: it would be preferable to keep the old 581 // menu around instead of continuously thrashing 582 // the menu, but unfortunately there does not 583 // seem to be a straightforward way to update it 584 // going backwards may simplify memory management 585 for (int i = fRecentMenu->CountItems(); i-- > 0;) { 586 delete fRecentMenu->RemoveItem(i); 587 } 588 589 // add new items 590 int count = 0; 591 entry_ref ref; 592 while (documents.FindRef("refs", count++, &ref) == B_OK) { 593 if (ref.device != -1 && ref.directory != -1) { 594 // sanity check passed 595 BMessage* openRecent = new BMessage(B_REFS_RECEIVED); 596 openRecent->AddRef("refs", &ref); 597 BMenuItem* item = new BMenuItem(ref.name, openRecent); 598 item->SetTarget(be_app); 599 fRecentMenu->AddItem(item); 600 } 601 } 602 603 // update the font menu 604 // unselect the old values 605 if (fCurrentFontItem != NULL) { 606 fCurrentFontItem->SetMarked(false); 607 BMenu* menu = fCurrentFontItem->Submenu(); 608 if (menu != NULL) { 609 BMenuItem* item = menu->FindMarked(); 610 if (item != NULL) 611 item->SetMarked(false); 612 } 613 } 614 615 if (fCurrentStyleItem != NULL) { 616 fCurrentStyleItem->SetMarked(false); 617 } 618 619 BMenuItem* oldColorItem = fFontColorMenu->FindMarked(); 620 if (oldColorItem != NULL) 621 oldColorItem->SetMarked(false); 622 623 BMenuItem* oldSizeItem = fFontSizeMenu->FindMarked(); 624 if (oldSizeItem != NULL) 625 oldSizeItem->SetMarked(false); 626 627 // find the current font, color, size 628 BFont font; 629 uint32 sameProperties; 630 rgb_color color = BLACK; 631 bool sameColor; 632 fTextView->GetFontAndColor(&font, &sameProperties, &color, &sameColor); 633 color.alpha = 255; 634 635 if (sameColor) { 636 // mark the menu according to the current color 637 if (color.red == 0) { 638 if (color.green == 0) { 639 if (color.blue == 0) { 640 fBlackItem->SetMarked(true); 641 } else if (color.blue == 255) { 642 fBlueItem->SetMarked(true); 643 } 644 } else if (color.green == 255) { 645 if (color.blue == 0) { 646 fGreenItem->SetMarked(true); 647 } else if (color.blue == 255) { 648 fCyanItem->SetMarked(true); 649 } 650 } 651 } else if (color.red == 255) { 652 if (color.green == 0) { 653 if (color.blue == 0) { 654 fRedItem->SetMarked(true); 655 } else if (color.blue == 255) { 656 fMagentaItem->SetMarked(true); 657 } 658 } else if (color.green == 255) { 659 if (color.blue == 0) { 660 fYellowItem->SetMarked(true); 661 } 662 } 663 } 664 } 665 666 if (sameProperties & B_FONT_SIZE) { 667 if ((int)font.Size() == font.Size()) { 668 // select the current font size 669 char fontSizeStr[16]; 670 snprintf(fontSizeStr, 15, "%i", (int)font.Size()); 671 BMenuItem* item = fFontSizeMenu->FindItem(fontSizeStr); 672 if (item != NULL) 673 item->SetMarked(true); 674 } 675 } 676 677 font_family family; 678 font_style style; 679 font.GetFamilyAndStyle(&family, &style); 680 681 fCurrentFontItem = fFontMenu->FindItem(family); 682 683 if (fCurrentFontItem != NULL) { 684 fCurrentFontItem->SetMarked(true); 685 BMenu* menu = fCurrentFontItem->Submenu(); 686 if (menu != NULL) { 687 BMenuItem* item = menu->FindItem(style); 688 fCurrentStyleItem = item; 689 if (fCurrentStyleItem != NULL) 690 item->SetMarked(true); 691 } 692 } 693 694 fBoldItem->SetMarked((font.Face() & B_BOLD_FACE) != 0); 695 fItalicItem->SetMarked((font.Face() & B_ITALIC_FACE) != 0); 696 697 switch (fTextView->Alignment()) { 698 case B_ALIGN_LEFT: 699 default: 700 fAlignLeft->SetMarked(true); 701 break; 702 case B_ALIGN_CENTER: 703 fAlignCenter->SetMarked(true); 704 break; 705 case B_ALIGN_RIGHT: 706 fAlignRight->SetMarked(true); 707 break; 708 } 709 710 // text encoding 711 const BCharacterSet* charset 712 = BCharacterSetRoster::GetCharacterSetByFontID(fTextView->GetEncoding()); 713 BMenu* encodingMenu = fEncodingItem->Submenu(); 714 if (charset != NULL && encodingMenu != NULL) { 715 const char* mime = charset->GetMIMEName(); 716 BString name(charset->GetPrintName()); 717 if (mime) 718 name << " (" << mime << ")"; 719 720 BMenuItem* item = encodingMenu->FindItem(name); 721 if (item != NULL) 722 item->SetMarked(true); 723 } 724 } 725 726 727 #undef B_TRANSLATION_CONTEXT 728 #define B_TRANSLATION_CONTEXT "SaveAlert" 729 730 731 status_t 732 StyledEditWindow::Save(BMessage* message) 733 { 734 _NodeMonitorSuspender nodeMonitorSuspender(this); 735 736 if (!message) 737 message = fSaveMessage; 738 739 if (!message) 740 return B_ERROR; 741 742 entry_ref dirRef; 743 const char* name; 744 if (message->FindRef("directory", &dirRef) != B_OK 745 || message->FindString("name", &name) != B_OK) 746 return B_BAD_VALUE; 747 748 BDirectory dir(&dirRef); 749 BEntry entry(&dir, name); 750 751 status_t status = B_ERROR; 752 if (dir.InitCheck() == B_OK && entry.InitCheck() == B_OK) { 753 struct stat st; 754 BFile file(&entry, B_READ_WRITE | B_CREATE_FILE); 755 if (file.InitCheck() == B_OK 756 && (status = file.GetStat(&st)) == B_OK) { 757 // check the file permissions 758 if (!((getuid() == st.st_uid && (S_IWUSR & st.st_mode)) 759 || (getgid() == st.st_gid && (S_IWGRP & st.st_mode)) 760 || (S_IWOTH & st.st_mode))) { 761 BString alertText; 762 bs_printf(&alertText, B_TRANSLATE("This file is marked " 763 "read-only. Save changes to the document \"%s\"? "), name); 764 switch (_ShowAlert(alertText, B_TRANSLATE("Cancel"), 765 B_TRANSLATE("Don't save"), 766 B_TRANSLATE("Save"), B_WARNING_ALERT)) { 767 case 0: 768 return B_CANCELED; 769 case 1: 770 return B_OK; 771 default: 772 break; 773 } 774 } 775 776 status = fTextView->WriteStyledEditFile(&file); 777 } 778 } 779 780 if (status != B_OK) { 781 BString alertText; 782 bs_printf(&alertText, B_TRANSLATE("Error saving \"%s\":\n%s"), name, 783 strerror(status)); 784 785 _ShowAlert(alertText, B_TRANSLATE("OK"), "", "", B_STOP_ALERT); 786 return status; 787 } 788 789 SetTitle(name); 790 791 if (fSaveMessage != message) { 792 delete fSaveMessage; 793 fSaveMessage = new BMessage(*message); 794 } 795 796 entry_ref ref; 797 if (entry.GetRef(&ref) == B_OK) 798 be_roster->AddToRecentDocuments(&ref, APP_SIGNATURE); 799 800 // clear clean modes 801 fSaveItem->SetEnabled(false); 802 fUndoCleans = false; 803 fRedoCleans = false; 804 fClean = true; 805 fNagOnNodeChange = true; 806 807 PostMessage(UPDATE_STATUS); 808 return status; 809 } 810 811 812 #undef B_TRANSLATION_CONTEXT 813 #define B_TRANSLATION_CONTEXT "Open_and_SaveAsPanel" 814 815 816 status_t 817 StyledEditWindow::SaveAs(BMessage* message) 818 { 819 if (fSavePanel == NULL) { 820 entry_ref* directory = NULL; 821 entry_ref dirRef; 822 if (fSaveMessage != NULL) { 823 if (fSaveMessage->FindRef("directory", &dirRef) == B_OK) 824 directory = &dirRef; 825 } 826 827 BMessenger target(this); 828 fSavePanel = new BFilePanel(B_SAVE_PANEL, &target, 829 directory, B_FILE_NODE, false); 830 831 BMenuBar* menuBar = dynamic_cast<BMenuBar*>( 832 fSavePanel->Window()->FindView("MenuBar")); 833 if (menuBar != NULL) { 834 fSavePanelEncodingMenu = new BMenu(B_TRANSLATE("Encoding")); 835 fSavePanelEncodingMenu->SetRadioMode(true); 836 menuBar->AddItem(fSavePanelEncodingMenu); 837 838 BCharacterSetRoster roster; 839 BCharacterSet charset; 840 while (roster.GetNextCharacterSet(&charset) == B_NO_ERROR) { 841 BString name(charset.GetPrintName()); 842 const char* mime = charset.GetMIMEName(); 843 if (mime) { 844 name.Append(" ("); 845 name.Append(mime); 846 name.Append(")"); 847 } 848 BMenuItem * item = new BMenuItem(name.String(), 849 new BMessage(SAVE_AS_ENCODING)); 850 item->SetTarget(this); 851 fSavePanelEncodingMenu->AddItem(item); 852 if (charset.GetFontID() == fTextView->GetEncoding()) 853 item->SetMarked(true); 854 } 855 } 856 } 857 858 fSavePanel->SetSaveText(Title()); 859 if (message != NULL) 860 fSavePanel->SetMessage(message); 861 862 fSavePanel->Show(); 863 return B_OK; 864 } 865 866 867 void 868 StyledEditWindow::OpenFile(entry_ref* ref) 869 { 870 if (_LoadFile(ref) != B_OK) { 871 fSaveItem->SetEnabled(true); 872 // allow saving new files 873 return; 874 } 875 876 be_roster->AddToRecentDocuments(ref, APP_SIGNATURE); 877 fSaveMessage = new BMessage(B_SAVE_REQUESTED); 878 if (fSaveMessage) { 879 BEntry entry(ref, true); 880 BEntry parent; 881 entry_ref parentRef; 882 char name[B_FILE_NAME_LENGTH]; 883 884 entry.GetParent(&parent); 885 entry.GetName(name); 886 parent.GetRef(&parentRef); 887 fSaveMessage->AddRef("directory", &parentRef); 888 fSaveMessage->AddString("name", name); 889 SetTitle(name); 890 891 _LoadAttrs(); 892 } 893 894 _SwitchNodeMonitor(true, ref); 895 896 fReloadItem->SetEnabled(fSaveMessage != NULL); 897 fEncodingItem->SetEnabled(fSaveMessage != NULL); 898 } 899 900 901 status_t 902 StyledEditWindow::PageSetup(const char* documentName) 903 { 904 BPrintJob printJob(documentName); 905 906 if (fPrintSettings != NULL) 907 printJob.SetSettings(new BMessage(*fPrintSettings)); 908 909 status_t result = printJob.ConfigPage(); 910 if (result == B_OK) { 911 delete fPrintSettings; 912 fPrintSettings = printJob.Settings(); 913 } 914 915 return result; 916 } 917 918 919 void 920 StyledEditWindow::Print(const char* documentName) 921 { 922 BPrintJob printJob(documentName); 923 if (fPrintSettings) 924 printJob.SetSettings(new BMessage(*fPrintSettings)); 925 926 if (printJob.ConfigJob() != B_OK) 927 return; 928 929 delete fPrintSettings; 930 fPrintSettings = printJob.Settings(); 931 932 // information from printJob 933 BRect printableRect = printJob.PrintableRect(); 934 int32 firstPage = printJob.FirstPage(); 935 int32 lastPage = printJob.LastPage(); 936 937 // lines eventually to be used to compute pages to print 938 int32 firstLine = 0; 939 int32 lastLine = fTextView->CountLines(); 940 941 // values to be computed 942 int32 pagesInDocument = 1; 943 int32 linesInDocument = fTextView->CountLines(); 944 945 int32 currentLine = 0; 946 while (currentLine < linesInDocument) { 947 float currentHeight = 0; 948 while (currentHeight < printableRect.Height() && currentLine 949 < linesInDocument) { 950 currentHeight += fTextView->LineHeight(currentLine); 951 if (currentHeight < printableRect.Height()) 952 currentLine++; 953 } 954 if (pagesInDocument == lastPage) 955 lastLine = currentLine - 1; 956 957 if (currentHeight >= printableRect.Height()) { 958 pagesInDocument++; 959 if (pagesInDocument == firstPage) 960 firstLine = currentLine; 961 } 962 } 963 964 if (lastPage > pagesInDocument - 1) { 965 lastPage = pagesInDocument - 1; 966 lastLine = currentLine - 1; 967 } 968 969 970 printJob.BeginJob(); 971 if (fTextView->CountLines() > 0 && fTextView->TextLength() > 0) { 972 int32 printLine = firstLine; 973 while (printLine <= lastLine) { 974 float currentHeight = 0; 975 int32 firstLineOnPage = printLine; 976 while (currentHeight < printableRect.Height() 977 && printLine <= lastLine) 978 { 979 currentHeight += fTextView->LineHeight(printLine); 980 if (currentHeight < printableRect.Height()) 981 printLine++; 982 } 983 984 float top = 0; 985 if (firstLineOnPage != 0) 986 top = fTextView->TextHeight(0, firstLineOnPage - 1); 987 988 float bottom = fTextView->TextHeight(0, printLine - 1); 989 BRect textRect(0.0, top + TEXT_INSET, 990 printableRect.Width(), bottom + TEXT_INSET); 991 printJob.DrawView(fTextView, textRect, B_ORIGIN); 992 printJob.SpoolPage(); 993 } 994 } 995 996 997 printJob.CommitJob(); 998 } 999 1000 1001 void 1002 StyledEditWindow::SearchAllWindows(BString find, BString replace, 1003 bool caseSensitive) 1004 { 1005 int32 numWindows; 1006 numWindows = be_app->CountWindows(); 1007 1008 BMessage* message; 1009 message= new BMessage(MSG_REPLACE_ALL); 1010 message->AddString("FindText", find); 1011 message->AddString("ReplaceText", replace); 1012 message->AddBool("casesens", caseSensitive); 1013 1014 while (numWindows >= 0) { 1015 StyledEditWindow* window = dynamic_cast<StyledEditWindow *>( 1016 be_app->WindowAt(numWindows)); 1017 1018 BMessenger messenger(window); 1019 messenger.SendMessage(message); 1020 1021 numWindows--; 1022 } 1023 } 1024 1025 1026 bool 1027 StyledEditWindow::IsDocumentEntryRef(const entry_ref* ref) 1028 { 1029 if (ref == NULL) 1030 return false; 1031 1032 if (fSaveMessage == NULL) 1033 return false; 1034 1035 entry_ref dir; 1036 const char* name; 1037 if (fSaveMessage->FindRef("directory", &dir) != B_OK 1038 || fSaveMessage->FindString("name", &name) != B_OK) 1039 return false; 1040 1041 entry_ref documentRef; 1042 BPath documentPath(&dir); 1043 documentPath.Append(name); 1044 get_ref_for_path(documentPath.Path(), &documentRef); 1045 1046 return *ref == documentRef; 1047 } 1048 1049 1050 // #pragma mark - private methods 1051 1052 1053 #undef B_TRANSLATION_CONTEXT 1054 #define B_TRANSLATION_CONTEXT "Menus" 1055 1056 1057 void 1058 StyledEditWindow::_InitWindow(uint32 encoding) 1059 { 1060 fPrintSettings = NULL; 1061 fSaveMessage = NULL; 1062 1063 // undo modes 1064 fUndoFlag = false; 1065 fCanUndo = false; 1066 fRedoFlag = false; 1067 fCanRedo = false; 1068 1069 // clean modes 1070 fUndoCleans = false; 1071 fRedoCleans = false; 1072 fClean = true; 1073 1074 // search- state 1075 fReplaceString = ""; 1076 fStringToFind = ""; 1077 fCaseSensitive = false; 1078 fWrapAround = false; 1079 fBackSearch = false; 1080 1081 fNagOnNodeChange = true; 1082 1083 // add menubar 1084 fMenuBar = new BMenuBar(BRect(0, 0, 0, 0), "menubar"); 1085 AddChild(fMenuBar); 1086 1087 // add textview and scrollview 1088 1089 BRect viewFrame = Bounds(); 1090 viewFrame.top = fMenuBar->Bounds().Height() + 1; 1091 viewFrame.right -= B_V_SCROLL_BAR_WIDTH; 1092 viewFrame.left = 0; 1093 viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT; 1094 1095 BRect textBounds = viewFrame; 1096 textBounds.OffsetTo(B_ORIGIN); 1097 textBounds.InsetBy(TEXT_INSET, TEXT_INSET); 1098 1099 fTextView = new StyledEditView(viewFrame, textBounds, this); 1100 fTextView->SetDoesUndo(true); 1101 fTextView->SetStylable(true); 1102 fTextView->SetEncoding(encoding); 1103 1104 fScrollView = new BScrollView("scrollview", fTextView, B_FOLLOW_ALL, 0, 1105 true, true, B_PLAIN_BORDER); 1106 AddChild(fScrollView); 1107 fTextView->MakeFocus(true); 1108 1109 fStatusView = new StatusView(fScrollView); 1110 fScrollView->AddChild(fStatusView); 1111 1112 // Add "File"-menu: 1113 BMenu* menu = new BMenu(B_TRANSLATE("File")); 1114 fMenuBar->AddItem(menu); 1115 1116 BMenuItem* menuItem; 1117 menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("New"), 1118 new BMessage(MENU_NEW), 'N')); 1119 menuItem->SetTarget(be_app); 1120 1121 menu->AddItem(menuItem = new BMenuItem(fRecentMenu 1122 = new BMenu(B_TRANSLATE("Open" B_UTF8_ELLIPSIS)), 1123 new BMessage(MENU_OPEN))); 1124 menuItem->SetShortcut('O', 0); 1125 menuItem->SetTarget(be_app); 1126 menu->AddSeparatorItem(); 1127 1128 menu->AddItem(fSaveItem = new BMenuItem(B_TRANSLATE("Save"), 1129 new BMessage(MENU_SAVE), 'S')); 1130 fSaveItem->SetEnabled(false); 1131 menu->AddItem(menuItem = new BMenuItem( 1132 B_TRANSLATE("Save as" B_UTF8_ELLIPSIS), new BMessage(MENU_SAVEAS))); 1133 menuItem->SetShortcut('S', B_SHIFT_KEY); 1134 menuItem->SetEnabled(true); 1135 1136 menu->AddItem(fReloadItem 1137 = new BMenuItem(B_TRANSLATE("Reload" B_UTF8_ELLIPSIS), 1138 new BMessage(MENU_RELOAD), 'L')); 1139 fReloadItem->SetEnabled(false); 1140 1141 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 1142 new BMessage(MENU_CLOSE), 'W')); 1143 1144 menu->AddSeparatorItem(); 1145 menu->AddItem(new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS), 1146 new BMessage(MENU_PAGESETUP))); 1147 menu->AddItem(new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS), 1148 new BMessage(MENU_PRINT), 'P')); 1149 1150 menu->AddSeparatorItem(); 1151 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 1152 new BMessage(MENU_QUIT), 'Q')); 1153 1154 // Add the "Edit"-menu: 1155 menu = new BMenu(B_TRANSLATE("Edit")); 1156 fMenuBar->AddItem(menu); 1157 1158 menu->AddItem(fUndoItem = new BMenuItem(B_TRANSLATE("Can't undo"), 1159 new BMessage(B_UNDO), 'Z')); 1160 fUndoItem->SetEnabled(false); 1161 1162 menu->AddSeparatorItem(); 1163 menu->AddItem(fCutItem = new BMenuItem(B_TRANSLATE("Cut"), 1164 new BMessage(B_CUT), 'X')); 1165 fCutItem->SetEnabled(false); 1166 fCutItem->SetTarget(fTextView); 1167 1168 menu->AddItem(fCopyItem = new BMenuItem(B_TRANSLATE("Copy"), 1169 new BMessage(B_COPY), 'C')); 1170 fCopyItem->SetEnabled(false); 1171 fCopyItem->SetTarget(fTextView); 1172 1173 menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Paste"), 1174 new BMessage(B_PASTE), 'V')); 1175 menuItem->SetTarget(fTextView); 1176 1177 menu->AddSeparatorItem(); 1178 menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Select all"), 1179 new BMessage(B_SELECT_ALL), 'A')); 1180 menuItem->SetTarget(fTextView); 1181 1182 menu->AddSeparatorItem(); 1183 menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS), 1184 new BMessage(MENU_FIND), 'F')); 1185 menu->AddItem(fFindAgainItem= new BMenuItem(B_TRANSLATE("Find again"), 1186 new BMessage(MENU_FIND_AGAIN), 'G')); 1187 fFindAgainItem->SetEnabled(false); 1188 1189 menu->AddItem(new BMenuItem(B_TRANSLATE("Find selection"), 1190 new BMessage(MENU_FIND_SELECTION), 'H')); 1191 menu->AddItem(fReplaceItem = new BMenuItem(B_TRANSLATE("Replace" B_UTF8_ELLIPSIS), 1192 new BMessage(MENU_REPLACE), 'R')); 1193 menu->AddItem(fReplaceSameItem = new BMenuItem(B_TRANSLATE("Replace next"), 1194 new BMessage(MENU_REPLACE_SAME), 'T')); 1195 fReplaceSameItem->SetEnabled(false); 1196 1197 // Add the "Font"-menu: 1198 fFontMenu = new BMenu(B_TRANSLATE("Font")); 1199 fMenuBar->AddItem(fFontMenu); 1200 1201 // "Size"-subMenu 1202 fFontSizeMenu = new BMenu(B_TRANSLATE("Size")); 1203 fFontSizeMenu->SetRadioMode(true); 1204 fFontMenu->AddItem(fFontSizeMenu); 1205 1206 const int32 fontSizes[] = {9, 10, 11, 12, 14, 18, 24, 36, 48, 72}; 1207 for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) { 1208 BMessage* fontMessage = new BMessage(FONT_SIZE); 1209 fontMessage->AddFloat("size", fontSizes[i]); 1210 1211 char label[64]; 1212 snprintf(label, sizeof(label), "%" B_PRId32, fontSizes[i]); 1213 fFontSizeMenu->AddItem(menuItem = new BMenuItem(label, fontMessage)); 1214 1215 if (fontSizes[i] == (int32)be_plain_font->Size()) 1216 menuItem->SetMarked(true); 1217 } 1218 1219 // "Color"-subMenu 1220 fFontColorMenu = new BMenu(B_TRANSLATE("Color")); 1221 fFontColorMenu->SetRadioMode(true); 1222 fFontMenu->AddItem(fFontColorMenu); 1223 1224 fFontColorMenu->AddItem(fBlackItem = new ColorMenuItem(B_TRANSLATE("Black"), 1225 BLACK, new BMessage(FONT_COLOR))); 1226 fBlackItem->SetMarked(true); 1227 fFontColorMenu->AddItem(fRedItem = new ColorMenuItem(B_TRANSLATE("Red"), 1228 RED, new BMessage(FONT_COLOR))); 1229 fFontColorMenu->AddItem(fGreenItem = new ColorMenuItem(B_TRANSLATE("Green"), 1230 GREEN, new BMessage(FONT_COLOR))); 1231 fFontColorMenu->AddItem(fBlueItem = new ColorMenuItem(B_TRANSLATE("Blue"), 1232 BLUE, new BMessage(FONT_COLOR))); 1233 fFontColorMenu->AddItem(fCyanItem = new ColorMenuItem(B_TRANSLATE("Cyan"), 1234 CYAN, new BMessage(FONT_COLOR))); 1235 fFontColorMenu->AddItem(fMagentaItem 1236 = new ColorMenuItem(B_TRANSLATE("Magenta"), MAGENTA, 1237 new BMessage(FONT_COLOR))); 1238 fFontColorMenu->AddItem(fYellowItem 1239 = new ColorMenuItem(B_TRANSLATE("Yellow"), YELLOW, 1240 new BMessage(FONT_COLOR))); 1241 fFontMenu->AddSeparatorItem(); 1242 1243 // "Bold" & "Italic" menu items 1244 fFontMenu->AddItem(fBoldItem = new BMenuItem(B_TRANSLATE("Bold"), 1245 new BMessage(kMsgSetBold))); 1246 fFontMenu->AddItem(fItalicItem = new BMenuItem(B_TRANSLATE("Italic"), 1247 new BMessage(kMsgSetItalic))); 1248 fBoldItem->SetShortcut('B', 0); 1249 fItalicItem->SetShortcut('I', 0); 1250 fFontMenu->AddSeparatorItem(); 1251 1252 // Available fonts 1253 1254 fCurrentFontItem = 0; 1255 fCurrentStyleItem = 0; 1256 1257 BMenu* subMenu; 1258 int32 numFamilies = count_font_families(); 1259 for (int32 i = 0; i < numFamilies; i++) { 1260 font_family family; 1261 if (get_font_family(i, &family) == B_OK) { 1262 subMenu = new BMenu(family); 1263 subMenu->SetRadioMode(true); 1264 fFontMenu->AddItem(new BMenuItem(subMenu, 1265 new BMessage(FONT_FAMILY))); 1266 1267 int32 numStyles = count_font_styles(family); 1268 for (int32 j = 0; j < numStyles; j++) { 1269 font_style style; 1270 uint32 flags; 1271 if (get_font_style(family, j, &style, &flags) == B_OK) { 1272 subMenu->AddItem(new BMenuItem(style, 1273 new BMessage(FONT_STYLE))); 1274 } 1275 } 1276 } 1277 } 1278 1279 // Add the "Document"-menu: 1280 menu = new BMenu(B_TRANSLATE("Document")); 1281 fMenuBar->AddItem(menu); 1282 1283 // "Align"-subMenu: 1284 subMenu = new BMenu(B_TRANSLATE("Align")); 1285 subMenu->SetRadioMode(true); 1286 1287 subMenu->AddItem(fAlignLeft = new BMenuItem(B_TRANSLATE("Left"), 1288 new BMessage(ALIGN_LEFT))); 1289 fAlignLeft->SetMarked(true); 1290 fAlignLeft->SetShortcut('L', B_OPTION_KEY); 1291 1292 subMenu->AddItem(fAlignCenter = new BMenuItem(B_TRANSLATE("Center"), 1293 new BMessage(ALIGN_CENTER))); 1294 fAlignCenter->SetShortcut('C', B_OPTION_KEY); 1295 1296 subMenu->AddItem(fAlignRight = new BMenuItem(B_TRANSLATE("Right"), 1297 new BMessage(ALIGN_RIGHT))); 1298 fAlignRight->SetShortcut('R', B_OPTION_KEY); 1299 1300 menu->AddItem(subMenu); 1301 menu->AddItem(fWrapItem = new BMenuItem(B_TRANSLATE("Wrap lines"), 1302 new BMessage(WRAP_LINES))); 1303 fWrapItem->SetMarked(true); 1304 fWrapItem->SetShortcut('W', B_OPTION_KEY); 1305 1306 BMessage *message = new BMessage(MENU_RELOAD); 1307 message->AddString("encoding", "auto"); 1308 menu->AddItem(fEncodingItem = new BMenuItem(_PopulateEncodingMenu( 1309 new BMenu(B_TRANSLATE("Text encoding")), "UTF-8"), 1310 message)); 1311 fEncodingItem->SetEnabled(false); 1312 1313 menu->AddSeparatorItem(); 1314 menu->AddItem(new BMenuItem(B_TRANSLATE("Statistics" B_UTF8_ELLIPSIS), 1315 new BMessage(SHOW_STATISTICS))); 1316 1317 fSavePanel = NULL; 1318 fSavePanelEncodingMenu = NULL; 1319 // build lazily 1320 } 1321 1322 1323 void 1324 StyledEditWindow::_LoadAttrs() 1325 { 1326 entry_ref dir; 1327 const char* name; 1328 if (fSaveMessage->FindRef("directory", &dir) != B_OK 1329 || fSaveMessage->FindString("name", &name) != B_OK) 1330 return; 1331 1332 BPath documentPath(&dir); 1333 documentPath.Append(name); 1334 1335 BNode documentNode(documentPath.Path()); 1336 if (documentNode.InitCheck() != B_OK) 1337 return; 1338 1339 BRect newFrame; 1340 ssize_t bytesRead = documentNode.ReadAttr(kInfoAttributeName, B_RECT_TYPE, 1341 0, &newFrame, sizeof(BRect)); 1342 if (bytesRead != sizeof(BRect)) 1343 return; 1344 1345 swap_data(B_RECT_TYPE, &newFrame, sizeof(BRect), B_SWAP_BENDIAN_TO_HOST); 1346 1347 // Check if the frame in on screen, otherwise, ignore it 1348 BScreen screen(this); 1349 if (newFrame.Width() > 32 && newFrame.Height() > 32 1350 && screen.Frame().Contains(newFrame)) { 1351 MoveTo(newFrame.left, newFrame.top); 1352 ResizeTo(newFrame.Width(), newFrame.Height()); 1353 } 1354 1355 // info about position of caret may live in the file attributes 1356 int32 position = 0; 1357 if (documentNode.ReadAttr("be:caret_position", B_INT32_TYPE, 0, 1358 &position, sizeof(position)) != sizeof(position)) 1359 position = 0; 1360 1361 fTextView->Select(position, position); 1362 fTextView->ScrollToOffset(position); 1363 } 1364 1365 1366 void 1367 StyledEditWindow::_SaveAttrs() 1368 { 1369 if (!fSaveMessage) 1370 return; 1371 1372 entry_ref dir; 1373 const char* name; 1374 if (fSaveMessage->FindRef("directory", &dir) != B_OK 1375 || fSaveMessage->FindString("name", &name) != B_OK) 1376 return; 1377 1378 BPath documentPath(&dir); 1379 documentPath.Append(name); 1380 1381 BNode documentNode(documentPath.Path()); 1382 if (documentNode.InitCheck() != B_OK) 1383 return; 1384 1385 BRect frame(Frame()); 1386 swap_data(B_RECT_TYPE, &frame, sizeof(BRect), B_SWAP_HOST_TO_BENDIAN); 1387 1388 documentNode.WriteAttr(kInfoAttributeName, B_RECT_TYPE, 0, &frame, 1389 sizeof(BRect)); 1390 1391 // preserve caret line and position 1392 int32 start, end; 1393 fTextView->GetSelection(&start, &end); 1394 documentNode.WriteAttr("be:caret_position", 1395 B_INT32_TYPE, 0, &start, sizeof(start)); 1396 } 1397 1398 1399 #undef B_TRANSLATION_CONTEXT 1400 #define B_TRANSLATION_CONTEXT "LoadAlert" 1401 1402 1403 status_t 1404 StyledEditWindow::_LoadFile(entry_ref* ref, const char* forceEncoding) 1405 { 1406 BEntry entry(ref, true); 1407 // traverse an eventual link 1408 1409 status_t status = entry.InitCheck(); 1410 if (status == B_OK && entry.IsDirectory()) 1411 status = B_IS_A_DIRECTORY; 1412 1413 BFile file; 1414 if (status == B_OK) 1415 status = file.SetTo(&entry, B_READ_ONLY); 1416 if (status == B_OK) 1417 status = fTextView->GetStyledText(&file, forceEncoding); 1418 1419 if (status == B_ENTRY_NOT_FOUND) { 1420 // Treat non-existing files consideratley; we just want to get an 1421 // empty window for them - to create this new document 1422 status = B_OK; 1423 } 1424 1425 if (status != B_OK) { 1426 // If an error occured, bail out and tell the user what happened 1427 BEntry entry(ref, true); 1428 char name[B_FILE_NAME_LENGTH]; 1429 if (entry.GetName(name) != B_OK) 1430 strlcpy(name, B_TRANSLATE("???"), sizeof(name)); 1431 1432 BString text; 1433 if (status == B_BAD_TYPE) 1434 bs_printf(&text, 1435 B_TRANSLATE("Error loading \"%s\":\n\tUnsupported format"), name); 1436 else 1437 bs_printf(&text, B_TRANSLATE("Error loading \"%s\":\n\t%s"), 1438 name, strerror(status)); 1439 1440 _ShowAlert(text, B_TRANSLATE("OK"), "", "", B_STOP_ALERT); 1441 return status; 1442 } 1443 1444 struct stat st; 1445 if (file.InitCheck() == B_OK && file.GetStat(&st) == B_OK) { 1446 bool editable = (getuid() == st.st_uid && S_IWUSR & st.st_mode) 1447 || (getgid() == st.st_gid && S_IWGRP & st.st_mode) 1448 || (S_IWOTH & st.st_mode); 1449 BVolume volume(ref->device); 1450 editable = editable && !volume.IsReadOnly(); 1451 _SetReadOnly(!editable); 1452 } 1453 1454 // update alignment 1455 switch (fTextView->Alignment()) { 1456 case B_ALIGN_LEFT: 1457 default: 1458 fAlignLeft->SetMarked(true); 1459 break; 1460 case B_ALIGN_CENTER: 1461 fAlignCenter->SetMarked(true); 1462 break; 1463 case B_ALIGN_RIGHT: 1464 fAlignRight->SetMarked(true); 1465 break; 1466 } 1467 1468 // update word wrapping 1469 fWrapItem->SetMarked(fTextView->DoesWordWrap()); 1470 return B_OK; 1471 } 1472 1473 1474 #undef B_TRANSLATION_CONTEXT 1475 #define B_TRANSLATION_CONTEXT "RevertToSavedAlert" 1476 1477 1478 void 1479 StyledEditWindow::_ReloadDocument(BMessage* message) 1480 { 1481 entry_ref ref; 1482 const char* name; 1483 1484 if (fSaveMessage == NULL || message == NULL 1485 || fSaveMessage->FindRef("directory", &ref) != B_OK 1486 || fSaveMessage->FindString("name", &name) != B_OK) 1487 return; 1488 1489 BDirectory dir(&ref); 1490 status_t status = dir.InitCheck(); 1491 BEntry entry; 1492 if (status == B_OK) 1493 status = entry.SetTo(&dir, name); 1494 1495 if (status == B_OK) 1496 status = entry.GetRef(&ref); 1497 1498 if (status != B_OK || !entry.Exists()) { 1499 BString alertText; 1500 bs_printf(&alertText, 1501 B_TRANSLATE("Cannot revert, file not found: \"%s\"."), name); 1502 _ShowAlert(alertText, B_TRANSLATE("OK"), "", "", B_STOP_ALERT); 1503 return; 1504 } 1505 1506 if (!fClean) { 1507 BString alertText; 1508 bs_printf(&alertText, 1509 B_TRANSLATE("\"%s\" has unsaved changes.\n" 1510 "Revert it to the last saved version? "), Title()); 1511 if (_ShowAlert(alertText, B_TRANSLATE("Cancel"), B_TRANSLATE("Revert"), 1512 "", B_WARNING_ALERT) != 1) 1513 return; 1514 } 1515 1516 const BCharacterSet* charset 1517 = BCharacterSetRoster::GetCharacterSetByFontID( 1518 fTextView->GetEncoding()); 1519 const char* forceEncoding = NULL; 1520 if (message->FindString("encoding", &forceEncoding) != B_OK) { 1521 if (charset != NULL) 1522 forceEncoding = charset->GetName(); 1523 } else { 1524 if (charset != NULL) { 1525 // UTF8 id assumed equal to -1 1526 const uint32 idUTF8 = (uint32)-1; 1527 uint32 id = charset->GetConversionID(); 1528 if (strcmp(forceEncoding, "next") == 0) 1529 id = id == B_MS_WINDOWS_1250_CONVERSION ? idUTF8 : id + 1; 1530 else if (strcmp(forceEncoding, "previous") == 0) 1531 id = id == idUTF8 ? B_MS_WINDOWS_1250_CONVERSION : id - 1; 1532 const BCharacterSet* newCharset 1533 = BCharacterSetRoster::GetCharacterSetByConversionID(id); 1534 if (newCharset != NULL) 1535 forceEncoding = newCharset->GetName(); 1536 } 1537 } 1538 1539 BScrollBar* vertBar = fScrollView->ScrollBar(B_VERTICAL); 1540 float vertPos = vertBar != NULL ? vertBar->Value() : 0.f; 1541 1542 DisableUpdates(); 1543 1544 fTextView->Reset(); 1545 1546 status = _LoadFile(&ref, forceEncoding); 1547 1548 if (vertBar != NULL) 1549 vertBar->SetValue(vertPos); 1550 1551 EnableUpdates(); 1552 1553 if (status != B_OK) 1554 return; 1555 1556 #undef B_TRANSLATION_CONTEXT 1557 #define B_TRANSLATION_CONTEXT "Menus" 1558 1559 // clear undo modes 1560 fUndoItem->SetLabel(B_TRANSLATE("Can't undo")); 1561 fUndoItem->SetEnabled(false); 1562 fUndoFlag = false; 1563 fCanUndo = false; 1564 fRedoFlag = false; 1565 fCanRedo = false; 1566 1567 // clear clean modes 1568 fSaveItem->SetEnabled(false); 1569 1570 fUndoCleans = false; 1571 fRedoCleans = false; 1572 fClean = true; 1573 1574 fNagOnNodeChange = true; 1575 } 1576 1577 1578 status_t 1579 StyledEditWindow::_UnlockFile() 1580 { 1581 _NodeMonitorSuspender nodeMonitorSuspender(this); 1582 1583 if (!fSaveMessage) 1584 return B_ERROR; 1585 1586 entry_ref dirRef; 1587 const char* name; 1588 if (fSaveMessage->FindRef("directory", &dirRef) != B_OK 1589 || fSaveMessage->FindString("name", &name) != B_OK) 1590 return B_BAD_VALUE; 1591 1592 BDirectory dir(&dirRef); 1593 BEntry entry(&dir, name); 1594 1595 status_t status = dir.InitCheck(); 1596 if (status != B_OK) 1597 return status; 1598 1599 status = entry.InitCheck(); 1600 if (status != B_OK) 1601 return status; 1602 1603 struct stat st; 1604 BFile file(&entry, B_READ_WRITE); 1605 status = file.InitCheck(); 1606 if (status != B_OK) 1607 return status; 1608 1609 status = file.GetStat(&st); 1610 if (status != B_OK) 1611 return status; 1612 1613 st.st_mode |= S_IWUSR; 1614 status = file.SetPermissions(st.st_mode); 1615 if (status == B_OK) 1616 _SetReadOnly(false); 1617 1618 return status; 1619 } 1620 1621 1622 bool 1623 StyledEditWindow::_Search(BString string, bool caseSensitive, bool wrap, 1624 bool backSearch, bool scrollToOccurence) 1625 { 1626 int32 start; 1627 int32 finish; 1628 1629 start = B_ERROR; 1630 1631 int32 length = string.Length(); 1632 if (length == 0) 1633 return false; 1634 1635 BString viewText(fTextView->Text()); 1636 int32 textStart, textFinish; 1637 fTextView->GetSelection(&textStart, &textFinish); 1638 if (backSearch) { 1639 if (caseSensitive) 1640 start = viewText.FindLast(string, textStart); 1641 else 1642 start = viewText.IFindLast(string, textStart); 1643 } else { 1644 if (caseSensitive) 1645 start = viewText.FindFirst(string, textFinish); 1646 else 1647 start = viewText.IFindFirst(string, textFinish); 1648 } 1649 if (start == B_ERROR && wrap) { 1650 if (backSearch) { 1651 if (caseSensitive) 1652 start = viewText.FindLast(string, viewText.Length()); 1653 else 1654 start = viewText.IFindLast(string, viewText.Length()); 1655 } else { 1656 if (caseSensitive) 1657 start = viewText.FindFirst(string, 0); 1658 else 1659 start = viewText.IFindFirst(string, 0); 1660 } 1661 } 1662 1663 if (start != B_ERROR) { 1664 finish = start + length; 1665 fTextView->Select(start, finish); 1666 1667 if (scrollToOccurence) 1668 fTextView->ScrollToSelection(); 1669 return true; 1670 } 1671 1672 return false; 1673 } 1674 1675 1676 void 1677 StyledEditWindow::_FindSelection() 1678 { 1679 int32 selectionStart, selectionFinish; 1680 fTextView->GetSelection(&selectionStart, &selectionFinish); 1681 1682 int32 selectionLength = selectionFinish- selectionStart; 1683 1684 BString viewText = fTextView->Text(); 1685 viewText.CopyInto(fStringToFind, selectionStart, selectionLength); 1686 fFindAgainItem->SetEnabled(true); 1687 _Search(fStringToFind, fCaseSensitive, fWrapAround, fBackSearch); 1688 } 1689 1690 1691 bool 1692 StyledEditWindow::_Replace(BString findThis, BString replaceWith, 1693 bool caseSensitive, bool wrap, bool backSearch) 1694 { 1695 if (_Search(findThis, caseSensitive, wrap, backSearch)) { 1696 int32 start; 1697 int32 finish; 1698 fTextView->GetSelection(&start, &finish); 1699 1700 _UpdateCleanUndoRedoSaveRevert(); 1701 fTextView->SetSuppressChanges(true); 1702 fTextView->Delete(start, start + findThis.Length()); 1703 fTextView->Insert(start, replaceWith.String(), replaceWith.Length()); 1704 fTextView->SetSuppressChanges(false); 1705 fTextView->Select(start, start + replaceWith.Length()); 1706 fTextView->ScrollToSelection(); 1707 return true; 1708 } 1709 1710 return false; 1711 } 1712 1713 1714 void 1715 StyledEditWindow::_ReplaceAll(BString findThis, BString replaceWith, 1716 bool caseSensitive) 1717 { 1718 bool first = true; 1719 fTextView->SetSuppressChanges(true); 1720 1721 // start from the beginning of text 1722 fTextView->Select(0, 0); 1723 1724 // iterate occurences of findThis without wrapping around 1725 while (_Search(findThis, caseSensitive, false, false, false)) { 1726 if (first) { 1727 _UpdateCleanUndoRedoSaveRevert(); 1728 first = false; 1729 } 1730 int32 start; 1731 int32 finish; 1732 1733 fTextView->GetSelection(&start, &finish); 1734 fTextView->Delete(start, start + findThis.Length()); 1735 fTextView->Insert(start, replaceWith.String(), replaceWith.Length()); 1736 1737 // advance the caret behind the inserted text 1738 start += replaceWith.Length(); 1739 fTextView->Select(start, start); 1740 } 1741 fTextView->ScrollToSelection(); 1742 fTextView->SetSuppressChanges(false); 1743 } 1744 1745 1746 void 1747 StyledEditWindow::_SetFontSize(float fontSize) 1748 { 1749 uint32 sameProperties; 1750 BFont font; 1751 1752 fTextView->GetFontAndColor(&font, &sameProperties); 1753 font.SetSize(fontSize); 1754 fTextView->SetFontAndColor(&font, B_FONT_SIZE); 1755 1756 _UpdateCleanUndoRedoSaveRevert(); 1757 } 1758 1759 1760 void 1761 StyledEditWindow::_SetFontColor(const rgb_color* color) 1762 { 1763 uint32 sameProperties; 1764 BFont font; 1765 1766 fTextView->GetFontAndColor(&font, &sameProperties, NULL, NULL); 1767 fTextView->SetFontAndColor(&font, 0, color); 1768 1769 _UpdateCleanUndoRedoSaveRevert(); 1770 } 1771 1772 1773 void 1774 StyledEditWindow::_SetFontStyle(const char* fontFamily, const char* fontStyle) 1775 { 1776 BFont font; 1777 uint32 sameProperties; 1778 1779 // find out what the old font was 1780 font_family oldFamily; 1781 font_style oldStyle; 1782 fTextView->GetFontAndColor(&font, &sameProperties); 1783 font.GetFamilyAndStyle(&oldFamily, &oldStyle); 1784 1785 // clear that family's bit on the menu, if necessary 1786 if (strcmp(oldFamily, fontFamily)) { 1787 BMenuItem* oldItem = fFontMenu->FindItem(oldFamily); 1788 if (oldItem != NULL) { 1789 oldItem->SetMarked(false); 1790 BMenu* menu = oldItem->Submenu(); 1791 if (menu != NULL) { 1792 oldItem = menu->FindItem(oldStyle); 1793 if (oldItem != NULL) 1794 oldItem->SetMarked(false); 1795 } 1796 } 1797 } 1798 1799 font.SetFamilyAndStyle(fontFamily, fontStyle); 1800 1801 uint16 face = 0; 1802 1803 if (!(font.Face() & B_REGULAR_FACE)) 1804 face = font.Face(); 1805 1806 if (fBoldItem->IsMarked()) 1807 face |= B_BOLD_FACE; 1808 1809 if (fItalicItem->IsMarked()) 1810 face |= B_ITALIC_FACE; 1811 1812 font.SetFace(face); 1813 1814 fTextView->SetFontAndColor(&font, B_FONT_FAMILY_AND_STYLE); 1815 1816 BMenuItem* superItem; 1817 superItem = fFontMenu->FindItem(fontFamily); 1818 if (superItem != NULL) { 1819 superItem->SetMarked(true); 1820 fCurrentFontItem = superItem; 1821 } 1822 1823 _UpdateCleanUndoRedoSaveRevert(); 1824 } 1825 1826 1827 #undef B_TRANSLATION_CONTEXT 1828 #define B_TRANSLATION_CONTEXT "Statistics" 1829 1830 1831 int32 1832 StyledEditWindow::_ShowStatistics() 1833 { 1834 size_t words = 0; 1835 bool inWord = false; 1836 size_t length = fTextView->TextLength(); 1837 1838 for (size_t i = 0; i < length; i++) { 1839 if (BUnicodeChar::IsWhitespace(fTextView->Text()[i])) { 1840 inWord = false; 1841 } else if (!inWord) { 1842 words++; 1843 inWord = true; 1844 } 1845 } 1846 1847 BString result; 1848 result << B_TRANSLATE("Document statistics") << '\n' << '\n' 1849 << B_TRANSLATE("Lines:") << ' ' << fTextView->CountLines() << '\n' 1850 << B_TRANSLATE("Characters:") << ' ' << length << '\n' 1851 << B_TRANSLATE("Words:") << ' ' << words; 1852 1853 BAlert* alert = new BAlert("Statistics", result, B_TRANSLATE("OK"), NULL, 1854 NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_INFO_ALERT); 1855 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1856 1857 return alert->Go(); 1858 } 1859 1860 1861 void 1862 StyledEditWindow::_SetReadOnly(bool readOnly) 1863 { 1864 fReplaceItem->SetEnabled(!readOnly); 1865 fReplaceSameItem->SetEnabled(!readOnly); 1866 fFontMenu->SetEnabled(!readOnly); 1867 fAlignLeft->Menu()->SetEnabled(!readOnly); 1868 fWrapItem->SetEnabled(!readOnly); 1869 fTextView->MakeEditable(!readOnly); 1870 } 1871 1872 1873 #undef B_TRANSLATION_CONTEXT 1874 #define B_TRANSLATION_CONTEXT "Menus" 1875 1876 1877 void 1878 StyledEditWindow::_UpdateCleanUndoRedoSaveRevert() 1879 { 1880 fClean = false; 1881 fUndoCleans = false; 1882 fRedoCleans = false; 1883 fReloadItem->SetEnabled(fSaveMessage != NULL); 1884 fEncodingItem->SetEnabled(fSaveMessage != NULL); 1885 fSaveItem->SetEnabled(true); 1886 fUndoItem->SetLabel(B_TRANSLATE("Can't undo")); 1887 fUndoItem->SetEnabled(false); 1888 fCanUndo = false; 1889 fCanRedo = false; 1890 } 1891 1892 1893 int32 1894 StyledEditWindow::_ShowAlert(const BString& text, const BString& label, 1895 const BString& label2, const BString& label3, alert_type type) const 1896 { 1897 const char* button2 = NULL; 1898 if (label2.Length() > 0) 1899 button2 = label2.String(); 1900 1901 const char* button3 = NULL; 1902 button_spacing spacing = B_EVEN_SPACING; 1903 if (label3.Length() > 0) { 1904 button3 = label3.String(); 1905 spacing = B_OFFSET_SPACING; 1906 } 1907 1908 BAlert* alert = new BAlert("Alert", text.String(), label.String(), button2, 1909 button3, B_WIDTH_AS_USUAL, spacing, type); 1910 alert->SetShortcut(0, B_ESCAPE); 1911 1912 return alert->Go(); 1913 } 1914 1915 1916 BMenu* 1917 StyledEditWindow::_PopulateEncodingMenu(BMenu* menu, const char* currentEncoding) 1918 { 1919 menu->SetRadioMode(true); 1920 BString encoding(currentEncoding); 1921 if (encoding.Length() == 0) 1922 encoding.SetTo("UTF-8"); 1923 1924 BCharacterSetRoster roster; 1925 BCharacterSet charset; 1926 while (roster.GetNextCharacterSet(&charset) == B_OK) { 1927 const char* mime = charset.GetMIMEName(); 1928 BString name(charset.GetPrintName()); 1929 1930 if (mime) 1931 name << " (" << mime << ")"; 1932 1933 BMessage *message = new BMessage(MENU_RELOAD); 1934 if (message != NULL) { 1935 message->AddString("encoding", charset.GetName()); 1936 BMenuItem* item = new BMenuItem(name, message); 1937 if (encoding.Compare(charset.GetName()) == 0) 1938 item->SetMarked(true); 1939 menu->AddItem(item); 1940 } 1941 } 1942 1943 menu->AddSeparatorItem(); 1944 BMessage *message = new BMessage(MENU_RELOAD); 1945 message->AddString("encoding", "auto"); 1946 menu->AddItem(new BMenuItem(B_TRANSLATE("Autodetect"), message)); 1947 1948 message = new BMessage(MENU_RELOAD); 1949 message->AddString("encoding", "next"); 1950 AddShortcut(B_PAGE_DOWN, B_OPTION_KEY, message); 1951 message = new BMessage(MENU_RELOAD); 1952 message->AddString("encoding", "previous"); 1953 AddShortcut(B_PAGE_UP, B_OPTION_KEY, message); 1954 1955 return menu; 1956 } 1957 1958 1959 #undef B_TRANSLATION_CONTEXT 1960 #define B_TRANSLATION_CONTEXT "NodeMonitorAlerts" 1961 1962 1963 void 1964 StyledEditWindow::_ShowNodeChangeAlert(const char* name, bool removed) 1965 { 1966 if (!fNagOnNodeChange) 1967 return; 1968 1969 BString alertText(removed ? B_TRANSLATE("File \"%file%\" was removed by " 1970 "another application, recover it?") 1971 : B_TRANSLATE("File \"%file%\" was modified by " 1972 "another application, reload it?")); 1973 alertText.ReplaceAll("%file%", name); 1974 1975 if (_ShowAlert(alertText, removed ? B_TRANSLATE("Recover") 1976 : B_TRANSLATE("Reload"), B_TRANSLATE("Ignore"), "", 1977 B_WARNING_ALERT) == 0) 1978 { 1979 if (!removed) { 1980 // supress the warning - user has already agreed 1981 fClean = true; 1982 BMessage msg(MENU_RELOAD); 1983 _ReloadDocument(&msg); 1984 } else 1985 Save(); 1986 } else 1987 fNagOnNodeChange = false; 1988 1989 fSaveItem->SetEnabled(!fClean); 1990 } 1991 1992 1993 void 1994 StyledEditWindow::_HandleNodeMonitorEvent(BMessage *message) 1995 { 1996 int32 opcode = 0; 1997 if (message->FindInt32("opcode", &opcode) != B_OK) 1998 return; 1999 2000 if (opcode != B_ENTRY_CREATED 2001 && message->FindInt64("node") != fNodeRef.node) 2002 // bypass foreign nodes' event 2003 return; 2004 2005 switch (opcode) { 2006 case B_STAT_CHANGED: 2007 { 2008 int32 fields = 0; 2009 if (message->FindInt32("fields", &fields) == B_OK 2010 && (fields & (B_STAT_SIZE | B_STAT_MODIFICATION_TIME 2011 | B_STAT_MODE)) == 0) 2012 break; 2013 2014 const char* name = NULL; 2015 if (fSaveMessage->FindString("name", &name) != B_OK) 2016 break; 2017 2018 _ShowNodeChangeAlert(name, false); 2019 } 2020 break; 2021 2022 case B_ENTRY_MOVED: 2023 { 2024 int32 device = 0; 2025 int64 srcFolder = 0; 2026 int64 dstFolder = 0; 2027 const char* name = NULL; 2028 if (message->FindInt32("device", &device) != B_OK 2029 || message->FindInt64("to directory", &dstFolder) != B_OK 2030 || message->FindInt64("from directory", &srcFolder) != B_OK 2031 || message->FindString("name", &name) != B_OK) 2032 break; 2033 2034 entry_ref newRef(device, dstFolder, name); 2035 BEntry entry(&newRef); 2036 2037 BEntry dirEntry; 2038 entry.GetParent(&dirEntry); 2039 2040 entry_ref ref; 2041 dirEntry.GetRef(&ref); 2042 fSaveMessage->ReplaceRef("directory", &ref); 2043 fSaveMessage->ReplaceString("name", name); 2044 2045 // store previous name - it may be useful in case 2046 // we have just moved to temporary copy of file (vim case) 2047 const char* sourceName = NULL; 2048 if (message->FindString("from name", &sourceName) == B_OK) { 2049 fSaveMessage->RemoveName("org.name"); 2050 fSaveMessage->AddString("org.name", sourceName); 2051 fSaveMessage->RemoveName("move time"); 2052 fSaveMessage->AddInt64("move time", system_time()); 2053 } 2054 2055 SetTitle(name); 2056 be_roster->AddToRecentDocuments(&newRef, APP_SIGNATURE); 2057 2058 if (srcFolder != dstFolder) { 2059 _SwitchNodeMonitor(false); 2060 _SwitchNodeMonitor(true); 2061 } 2062 } 2063 break; 2064 2065 case B_ENTRY_REMOVED: 2066 { 2067 _SwitchNodeMonitor(false); 2068 2069 fClean = false; 2070 2071 // some editors like vim save files in following way: 2072 // 1) move t.txt -> t.txt~ 2073 // 2) re-create t.txt and write data to it 2074 // 3) remove t.txt~ 2075 // go to catch this case 2076 int32 device = 0; 2077 int64 directory = 0; 2078 BString orgName; 2079 if (fSaveMessage->FindString("org.name", &orgName) == B_OK 2080 && message->FindInt32("device", &device) == B_OK 2081 && message->FindInt64("directory", &directory) == B_OK) 2082 { 2083 // reuse the source name if it is not too old 2084 bigtime_t time = fSaveMessage->FindInt64("move time"); 2085 if ((system_time() - time) < 1000000) { 2086 entry_ref ref(device, directory, orgName); 2087 BEntry entry(&ref); 2088 if (entry.InitCheck() == B_OK) { 2089 _SwitchNodeMonitor(true, &ref); 2090 } 2091 2092 fSaveMessage->ReplaceString("name", orgName); 2093 fSaveMessage->RemoveName("org.name"); 2094 fSaveMessage->RemoveName("move time"); 2095 2096 SetTitle(orgName); 2097 _ShowNodeChangeAlert(orgName, false); 2098 break; 2099 } 2100 } 2101 2102 const char* name = NULL; 2103 if (message->FindString("name", &name) != B_OK 2104 && fSaveMessage->FindString("name", &name) != B_OK) 2105 name = "Unknown"; 2106 2107 _ShowNodeChangeAlert(name, true); 2108 } 2109 break; 2110 2111 default: 2112 break; 2113 } 2114 } 2115 2116 2117 void 2118 StyledEditWindow::_SwitchNodeMonitor(bool on, entry_ref* ref) 2119 { 2120 if (!on) { 2121 watch_node(&fNodeRef, B_STOP_WATCHING, this); 2122 watch_node(&fFolderNodeRef, B_STOP_WATCHING, this); 2123 fNodeRef = node_ref(); 2124 fFolderNodeRef = node_ref(); 2125 return; 2126 } 2127 2128 BEntry entry, folderEntry; 2129 2130 if (ref != NULL) { 2131 entry.SetTo(ref, true); 2132 entry.GetParent(&folderEntry); 2133 2134 } else if (fSaveMessage != NULL) { 2135 entry_ref ref; 2136 const char* name = NULL; 2137 if (fSaveMessage->FindRef("directory", &ref) != B_OK 2138 || fSaveMessage->FindString("name", &name) != B_OK) 2139 return; 2140 2141 BDirectory dir(&ref); 2142 entry.SetTo(&dir, name); 2143 folderEntry.SetTo(&ref); 2144 2145 } else 2146 return; 2147 2148 if (entry.InitCheck() != B_OK || folderEntry.InitCheck() != B_OK) 2149 return; 2150 2151 entry.GetNodeRef(&fNodeRef); 2152 folderEntry.GetNodeRef(&fFolderNodeRef); 2153 2154 watch_node(&fNodeRef, B_WATCH_STAT, this); 2155 watch_node(&fFolderNodeRef, B_WATCH_DIRECTORY, this); 2156 } 2157