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