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 return status; 808 } 809 810 811 #undef B_TRANSLATION_CONTEXT 812 #define B_TRANSLATION_CONTEXT "Open_and_SaveAsPanel" 813 814 815 status_t 816 StyledEditWindow::SaveAs(BMessage* message) 817 { 818 if (fSavePanel == NULL) { 819 entry_ref* directory = NULL; 820 entry_ref dirRef; 821 if (fSaveMessage != NULL) { 822 if (fSaveMessage->FindRef("directory", &dirRef) == B_OK) 823 directory = &dirRef; 824 } 825 826 BMessenger target(this); 827 fSavePanel = new BFilePanel(B_SAVE_PANEL, &target, 828 directory, B_FILE_NODE, false); 829 830 BMenuBar* menuBar = dynamic_cast<BMenuBar*>( 831 fSavePanel->Window()->FindView("MenuBar")); 832 if (menuBar != NULL) { 833 fSavePanelEncodingMenu = new BMenu(B_TRANSLATE("Encoding")); 834 fSavePanelEncodingMenu->SetRadioMode(true); 835 menuBar->AddItem(fSavePanelEncodingMenu); 836 837 BCharacterSetRoster roster; 838 BCharacterSet charset; 839 while (roster.GetNextCharacterSet(&charset) == B_NO_ERROR) { 840 BString name(charset.GetPrintName()); 841 const char* mime = charset.GetMIMEName(); 842 if (mime) { 843 name.Append(" ("); 844 name.Append(mime); 845 name.Append(")"); 846 } 847 BMenuItem * item = new BMenuItem(name.String(), 848 new BMessage(SAVE_AS_ENCODING)); 849 item->SetTarget(this); 850 fSavePanelEncodingMenu->AddItem(item); 851 if (charset.GetFontID() == fTextView->GetEncoding()) 852 item->SetMarked(true); 853 } 854 } 855 } 856 857 fSavePanel->SetSaveText(Title()); 858 if (message != NULL) 859 fSavePanel->SetMessage(message); 860 861 fSavePanel->Show(); 862 return B_OK; 863 } 864 865 866 void 867 StyledEditWindow::OpenFile(entry_ref* ref) 868 { 869 if (_LoadFile(ref) != B_OK) { 870 fSaveItem->SetEnabled(true); 871 // allow saving new files 872 return; 873 } 874 875 be_roster->AddToRecentDocuments(ref, APP_SIGNATURE); 876 fSaveMessage = new BMessage(B_SAVE_REQUESTED); 877 if (fSaveMessage) { 878 BEntry entry(ref, true); 879 BEntry parent; 880 entry_ref parentRef; 881 char name[B_FILE_NAME_LENGTH]; 882 883 entry.GetParent(&parent); 884 entry.GetName(name); 885 parent.GetRef(&parentRef); 886 fSaveMessage->AddRef("directory", &parentRef); 887 fSaveMessage->AddString("name", name); 888 SetTitle(name); 889 890 _LoadAttrs(); 891 } 892 893 _SwitchNodeMonitor(true, ref); 894 895 fReloadItem->SetEnabled(fSaveMessage != NULL); 896 fEncodingItem->SetEnabled(fSaveMessage != NULL); 897 } 898 899 900 status_t 901 StyledEditWindow::PageSetup(const char* documentName) 902 { 903 BPrintJob printJob(documentName); 904 905 if (fPrintSettings != NULL) 906 printJob.SetSettings(new BMessage(*fPrintSettings)); 907 908 status_t result = printJob.ConfigPage(); 909 if (result == B_OK) { 910 delete fPrintSettings; 911 fPrintSettings = printJob.Settings(); 912 } 913 914 return result; 915 } 916 917 918 void 919 StyledEditWindow::Print(const char* documentName) 920 { 921 BPrintJob printJob(documentName); 922 if (fPrintSettings) 923 printJob.SetSettings(new BMessage(*fPrintSettings)); 924 925 if (printJob.ConfigJob() != B_OK) 926 return; 927 928 delete fPrintSettings; 929 fPrintSettings = printJob.Settings(); 930 931 // information from printJob 932 BRect printableRect = printJob.PrintableRect(); 933 int32 firstPage = printJob.FirstPage(); 934 int32 lastPage = printJob.LastPage(); 935 936 // lines eventually to be used to compute pages to print 937 int32 firstLine = 0; 938 int32 lastLine = fTextView->CountLines(); 939 940 // values to be computed 941 int32 pagesInDocument = 1; 942 int32 linesInDocument = fTextView->CountLines(); 943 944 int32 currentLine = 0; 945 while (currentLine < linesInDocument) { 946 float currentHeight = 0; 947 while (currentHeight < printableRect.Height() && currentLine 948 < linesInDocument) { 949 currentHeight += fTextView->LineHeight(currentLine); 950 if (currentHeight < printableRect.Height()) 951 currentLine++; 952 } 953 if (pagesInDocument == lastPage) 954 lastLine = currentLine - 1; 955 956 if (currentHeight >= printableRect.Height()) { 957 pagesInDocument++; 958 if (pagesInDocument == firstPage) 959 firstLine = currentLine; 960 } 961 } 962 963 if (lastPage > pagesInDocument - 1) { 964 lastPage = pagesInDocument - 1; 965 lastLine = currentLine - 1; 966 } 967 968 969 printJob.BeginJob(); 970 if (fTextView->CountLines() > 0 && fTextView->TextLength() > 0) { 971 int32 printLine = firstLine; 972 while (printLine <= lastLine) { 973 float currentHeight = 0; 974 int32 firstLineOnPage = printLine; 975 while (currentHeight < printableRect.Height() 976 && printLine <= lastLine) 977 { 978 currentHeight += fTextView->LineHeight(printLine); 979 if (currentHeight < printableRect.Height()) 980 printLine++; 981 } 982 983 float top = 0; 984 if (firstLineOnPage != 0) 985 top = fTextView->TextHeight(0, firstLineOnPage - 1); 986 987 float bottom = fTextView->TextHeight(0, printLine - 1); 988 BRect textRect(0.0, top + TEXT_INSET, 989 printableRect.Width(), bottom + TEXT_INSET); 990 printJob.DrawView(fTextView, textRect, B_ORIGIN); 991 printJob.SpoolPage(); 992 } 993 } 994 995 996 printJob.CommitJob(); 997 } 998 999 1000 void 1001 StyledEditWindow::SearchAllWindows(BString find, BString replace, 1002 bool caseSensitive) 1003 { 1004 int32 numWindows; 1005 numWindows = be_app->CountWindows(); 1006 1007 BMessage* message; 1008 message= new BMessage(MSG_REPLACE_ALL); 1009 message->AddString("FindText", find); 1010 message->AddString("ReplaceText", replace); 1011 message->AddBool("casesens", caseSensitive); 1012 1013 while (numWindows >= 0) { 1014 StyledEditWindow* window = dynamic_cast<StyledEditWindow *>( 1015 be_app->WindowAt(numWindows)); 1016 1017 BMessenger messenger(window); 1018 messenger.SendMessage(message); 1019 1020 numWindows--; 1021 } 1022 } 1023 1024 1025 bool 1026 StyledEditWindow::IsDocumentEntryRef(const entry_ref* ref) 1027 { 1028 if (ref == NULL) 1029 return false; 1030 1031 if (fSaveMessage == NULL) 1032 return false; 1033 1034 entry_ref dir; 1035 const char* name; 1036 if (fSaveMessage->FindRef("directory", &dir) != B_OK 1037 || fSaveMessage->FindString("name", &name) != B_OK) 1038 return false; 1039 1040 entry_ref documentRef; 1041 BPath documentPath(&dir); 1042 documentPath.Append(name); 1043 get_ref_for_path(documentPath.Path(), &documentRef); 1044 1045 return *ref == documentRef; 1046 } 1047 1048 1049 // #pragma mark - private methods 1050 1051 1052 #undef B_TRANSLATION_CONTEXT 1053 #define B_TRANSLATION_CONTEXT "Menus" 1054 1055 1056 void 1057 StyledEditWindow::_InitWindow(uint32 encoding) 1058 { 1059 fPrintSettings = NULL; 1060 fSaveMessage = NULL; 1061 1062 // undo modes 1063 fUndoFlag = false; 1064 fCanUndo = false; 1065 fRedoFlag = false; 1066 fCanRedo = false; 1067 1068 // clean modes 1069 fUndoCleans = false; 1070 fRedoCleans = false; 1071 fClean = true; 1072 1073 // search- state 1074 fReplaceString = ""; 1075 fStringToFind = ""; 1076 fCaseSensitive = false; 1077 fWrapAround = false; 1078 fBackSearch = false; 1079 1080 fNagOnNodeChange = true; 1081 1082 // add menubar 1083 fMenuBar = new BMenuBar(BRect(0, 0, 0, 0), "menubar"); 1084 AddChild(fMenuBar); 1085 1086 // add textview and scrollview 1087 1088 BRect viewFrame = Bounds(); 1089 viewFrame.top = fMenuBar->Bounds().Height() + 1; 1090 viewFrame.right -= B_V_SCROLL_BAR_WIDTH; 1091 viewFrame.left = 0; 1092 viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT; 1093 1094 BRect textBounds = viewFrame; 1095 textBounds.OffsetTo(B_ORIGIN); 1096 textBounds.InsetBy(TEXT_INSET, TEXT_INSET); 1097 1098 fTextView = new StyledEditView(viewFrame, textBounds, this); 1099 fTextView->SetDoesUndo(true); 1100 fTextView->SetStylable(true); 1101 fTextView->SetEncoding(encoding); 1102 1103 fScrollView = new BScrollView("scrollview", fTextView, B_FOLLOW_ALL, 0, 1104 true, true, B_PLAIN_BORDER); 1105 AddChild(fScrollView); 1106 fTextView->MakeFocus(true); 1107 1108 fStatusView = new StatusView(fScrollView); 1109 fScrollView->AddChild(fStatusView); 1110 1111 // Add "File"-menu: 1112 BMenu* menu = new BMenu(B_TRANSLATE("File")); 1113 fMenuBar->AddItem(menu); 1114 1115 BMenuItem* menuItem; 1116 menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("New"), 1117 new BMessage(MENU_NEW), 'N')); 1118 menuItem->SetTarget(be_app); 1119 1120 menu->AddItem(menuItem = new BMenuItem(fRecentMenu 1121 = new BMenu(B_TRANSLATE("Open" B_UTF8_ELLIPSIS)), 1122 new BMessage(MENU_OPEN))); 1123 menuItem->SetShortcut('O', 0); 1124 menuItem->SetTarget(be_app); 1125 menu->AddSeparatorItem(); 1126 1127 menu->AddItem(fSaveItem = new BMenuItem(B_TRANSLATE("Save"), 1128 new BMessage(MENU_SAVE), 'S')); 1129 fSaveItem->SetEnabled(false); 1130 menu->AddItem(menuItem = new BMenuItem( 1131 B_TRANSLATE("Save as" B_UTF8_ELLIPSIS), new BMessage(MENU_SAVEAS))); 1132 menuItem->SetShortcut('S', B_SHIFT_KEY); 1133 menuItem->SetEnabled(true); 1134 1135 menu->AddItem(fReloadItem 1136 = new BMenuItem(B_TRANSLATE("Reload" B_UTF8_ELLIPSIS), 1137 new BMessage(MENU_RELOAD), 'L')); 1138 fReloadItem->SetEnabled(false); 1139 1140 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 1141 new BMessage(MENU_CLOSE), 'W')); 1142 1143 menu->AddSeparatorItem(); 1144 menu->AddItem(new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS), 1145 new BMessage(MENU_PAGESETUP))); 1146 menu->AddItem(new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS), 1147 new BMessage(MENU_PRINT), 'P')); 1148 1149 menu->AddSeparatorItem(); 1150 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 1151 new BMessage(MENU_QUIT), 'Q')); 1152 1153 // Add the "Edit"-menu: 1154 menu = new BMenu(B_TRANSLATE("Edit")); 1155 fMenuBar->AddItem(menu); 1156 1157 menu->AddItem(fUndoItem = new BMenuItem(B_TRANSLATE("Can't undo"), 1158 new BMessage(B_UNDO), 'Z')); 1159 fUndoItem->SetEnabled(false); 1160 1161 menu->AddSeparatorItem(); 1162 menu->AddItem(fCutItem = new BMenuItem(B_TRANSLATE("Cut"), 1163 new BMessage(B_CUT), 'X')); 1164 fCutItem->SetEnabled(false); 1165 fCutItem->SetTarget(fTextView); 1166 1167 menu->AddItem(fCopyItem = new BMenuItem(B_TRANSLATE("Copy"), 1168 new BMessage(B_COPY), 'C')); 1169 fCopyItem->SetEnabled(false); 1170 fCopyItem->SetTarget(fTextView); 1171 1172 menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Paste"), 1173 new BMessage(B_PASTE), 'V')); 1174 menuItem->SetTarget(fTextView); 1175 1176 menu->AddSeparatorItem(); 1177 menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Select all"), 1178 new BMessage(B_SELECT_ALL), 'A')); 1179 menuItem->SetTarget(fTextView); 1180 1181 menu->AddSeparatorItem(); 1182 menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS), 1183 new BMessage(MENU_FIND), 'F')); 1184 menu->AddItem(fFindAgainItem= new BMenuItem(B_TRANSLATE("Find again"), 1185 new BMessage(MENU_FIND_AGAIN), 'G')); 1186 fFindAgainItem->SetEnabled(false); 1187 1188 menu->AddItem(new BMenuItem(B_TRANSLATE("Find selection"), 1189 new BMessage(MENU_FIND_SELECTION), 'H')); 1190 menu->AddItem(fReplaceItem = new BMenuItem(B_TRANSLATE("Replace" B_UTF8_ELLIPSIS), 1191 new BMessage(MENU_REPLACE), 'R')); 1192 menu->AddItem(fReplaceSameItem = new BMenuItem(B_TRANSLATE("Replace next"), 1193 new BMessage(MENU_REPLACE_SAME), 'T')); 1194 fReplaceSameItem->SetEnabled(false); 1195 1196 // Add the "Font"-menu: 1197 fFontMenu = new BMenu(B_TRANSLATE("Font")); 1198 fMenuBar->AddItem(fFontMenu); 1199 1200 // "Size"-subMenu 1201 fFontSizeMenu = new BMenu(B_TRANSLATE("Size")); 1202 fFontSizeMenu->SetRadioMode(true); 1203 fFontMenu->AddItem(fFontSizeMenu); 1204 1205 const int32 fontSizes[] = {9, 10, 11, 12, 14, 18, 24, 36, 48, 72}; 1206 for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) { 1207 BMessage* fontMessage = new BMessage(FONT_SIZE); 1208 fontMessage->AddFloat("size", fontSizes[i]); 1209 1210 char label[64]; 1211 snprintf(label, sizeof(label), "%" B_PRId32, fontSizes[i]); 1212 fFontSizeMenu->AddItem(menuItem = new BMenuItem(label, fontMessage)); 1213 1214 if (fontSizes[i] == (int32)be_plain_font->Size()) 1215 menuItem->SetMarked(true); 1216 } 1217 1218 // "Color"-subMenu 1219 fFontColorMenu = new BMenu(B_TRANSLATE("Color")); 1220 fFontColorMenu->SetRadioMode(true); 1221 fFontMenu->AddItem(fFontColorMenu); 1222 1223 fFontColorMenu->AddItem(fBlackItem = new ColorMenuItem(B_TRANSLATE("Black"), 1224 BLACK, new BMessage(FONT_COLOR))); 1225 fBlackItem->SetMarked(true); 1226 fFontColorMenu->AddItem(fRedItem = new ColorMenuItem(B_TRANSLATE("Red"), 1227 RED, new BMessage(FONT_COLOR))); 1228 fFontColorMenu->AddItem(fGreenItem = new ColorMenuItem(B_TRANSLATE("Green"), 1229 GREEN, new BMessage(FONT_COLOR))); 1230 fFontColorMenu->AddItem(fBlueItem = new ColorMenuItem(B_TRANSLATE("Blue"), 1231 BLUE, new BMessage(FONT_COLOR))); 1232 fFontColorMenu->AddItem(fCyanItem = new ColorMenuItem(B_TRANSLATE("Cyan"), 1233 CYAN, new BMessage(FONT_COLOR))); 1234 fFontColorMenu->AddItem(fMagentaItem 1235 = new ColorMenuItem(B_TRANSLATE("Magenta"), MAGENTA, 1236 new BMessage(FONT_COLOR))); 1237 fFontColorMenu->AddItem(fYellowItem 1238 = new ColorMenuItem(B_TRANSLATE("Yellow"), YELLOW, 1239 new BMessage(FONT_COLOR))); 1240 fFontMenu->AddSeparatorItem(); 1241 1242 // "Bold" & "Italic" menu items 1243 fFontMenu->AddItem(fBoldItem = new BMenuItem(B_TRANSLATE("Bold"), 1244 new BMessage(kMsgSetBold))); 1245 fFontMenu->AddItem(fItalicItem = new BMenuItem(B_TRANSLATE("Italic"), 1246 new BMessage(kMsgSetItalic))); 1247 fBoldItem->SetShortcut('B', 0); 1248 fItalicItem->SetShortcut('I', 0); 1249 fFontMenu->AddSeparatorItem(); 1250 1251 // Available fonts 1252 1253 fCurrentFontItem = 0; 1254 fCurrentStyleItem = 0; 1255 1256 BMenu* subMenu; 1257 int32 numFamilies = count_font_families(); 1258 for (int32 i = 0; i < numFamilies; i++) { 1259 font_family family; 1260 if (get_font_family(i, &family) == B_OK) { 1261 subMenu = new BMenu(family); 1262 subMenu->SetRadioMode(true); 1263 fFontMenu->AddItem(new BMenuItem(subMenu, 1264 new BMessage(FONT_FAMILY))); 1265 1266 int32 numStyles = count_font_styles(family); 1267 for (int32 j = 0; j < numStyles; j++) { 1268 font_style style; 1269 uint32 flags; 1270 if (get_font_style(family, j, &style, &flags) == B_OK) { 1271 subMenu->AddItem(new BMenuItem(style, 1272 new BMessage(FONT_STYLE))); 1273 } 1274 } 1275 } 1276 } 1277 1278 // Add the "Document"-menu: 1279 menu = new BMenu(B_TRANSLATE("Document")); 1280 fMenuBar->AddItem(menu); 1281 1282 // "Align"-subMenu: 1283 subMenu = new BMenu(B_TRANSLATE("Align")); 1284 subMenu->SetRadioMode(true); 1285 1286 subMenu->AddItem(fAlignLeft = new BMenuItem(B_TRANSLATE("Left"), 1287 new BMessage(ALIGN_LEFT))); 1288 fAlignLeft->SetMarked(true); 1289 fAlignLeft->SetShortcut('L', B_OPTION_KEY); 1290 1291 subMenu->AddItem(fAlignCenter = new BMenuItem(B_TRANSLATE("Center"), 1292 new BMessage(ALIGN_CENTER))); 1293 fAlignCenter->SetShortcut('C', B_OPTION_KEY); 1294 1295 subMenu->AddItem(fAlignRight = new BMenuItem(B_TRANSLATE("Right"), 1296 new BMessage(ALIGN_RIGHT))); 1297 fAlignRight->SetShortcut('R', B_OPTION_KEY); 1298 1299 menu->AddItem(subMenu); 1300 menu->AddItem(fWrapItem = new BMenuItem(B_TRANSLATE("Wrap lines"), 1301 new BMessage(WRAP_LINES))); 1302 fWrapItem->SetMarked(true); 1303 fWrapItem->SetShortcut('W', B_OPTION_KEY); 1304 1305 BMessage *message = new BMessage(MENU_RELOAD); 1306 message->AddString("encoding", "auto"); 1307 menu->AddItem(fEncodingItem = new BMenuItem(_PopulateEncodingMenu( 1308 new BMenu(B_TRANSLATE("Text encoding")), "UTF-8"), 1309 message)); 1310 fEncodingItem->SetEnabled(false); 1311 1312 menu->AddSeparatorItem(); 1313 menu->AddItem(new BMenuItem(B_TRANSLATE("Statistics" B_UTF8_ELLIPSIS), 1314 new BMessage(SHOW_STATISTICS))); 1315 1316 fSavePanel = NULL; 1317 fSavePanelEncodingMenu = NULL; 1318 // build lazily 1319 } 1320 1321 1322 void 1323 StyledEditWindow::_LoadAttrs() 1324 { 1325 entry_ref dir; 1326 const char* name; 1327 if (fSaveMessage->FindRef("directory", &dir) != B_OK 1328 || fSaveMessage->FindString("name", &name) != B_OK) 1329 return; 1330 1331 BPath documentPath(&dir); 1332 documentPath.Append(name); 1333 1334 BNode documentNode(documentPath.Path()); 1335 if (documentNode.InitCheck() != B_OK) 1336 return; 1337 1338 BRect newFrame; 1339 ssize_t bytesRead = documentNode.ReadAttr(kInfoAttributeName, B_RECT_TYPE, 1340 0, &newFrame, sizeof(BRect)); 1341 if (bytesRead != sizeof(BRect)) 1342 return; 1343 1344 swap_data(B_RECT_TYPE, &newFrame, sizeof(BRect), B_SWAP_BENDIAN_TO_HOST); 1345 1346 // Check if the frame in on screen, otherwise, ignore it 1347 BScreen screen(this); 1348 if (newFrame.Width() > 32 && newFrame.Height() > 32 1349 && screen.Frame().Contains(newFrame)) { 1350 MoveTo(newFrame.left, newFrame.top); 1351 ResizeTo(newFrame.Width(), newFrame.Height()); 1352 } 1353 1354 // info about position of caret may live in the file attributes 1355 int32 position = 0; 1356 if (documentNode.ReadAttr("be:caret_position", B_INT32_TYPE, 0, 1357 &position, sizeof(position)) != sizeof(position)) 1358 position = 0; 1359 1360 fTextView->Select(position, position); 1361 fTextView->ScrollToOffset(position); 1362 } 1363 1364 1365 void 1366 StyledEditWindow::_SaveAttrs() 1367 { 1368 if (!fSaveMessage) 1369 return; 1370 1371 entry_ref dir; 1372 const char* name; 1373 if (fSaveMessage->FindRef("directory", &dir) != B_OK 1374 || fSaveMessage->FindString("name", &name) != B_OK) 1375 return; 1376 1377 BPath documentPath(&dir); 1378 documentPath.Append(name); 1379 1380 BNode documentNode(documentPath.Path()); 1381 if (documentNode.InitCheck() != B_OK) 1382 return; 1383 1384 BRect frame(Frame()); 1385 swap_data(B_RECT_TYPE, &frame, sizeof(BRect), B_SWAP_HOST_TO_BENDIAN); 1386 1387 documentNode.WriteAttr(kInfoAttributeName, B_RECT_TYPE, 0, &frame, 1388 sizeof(BRect)); 1389 1390 // preserve caret line and position 1391 int32 start, end; 1392 fTextView->GetSelection(&start, &end); 1393 documentNode.WriteAttr("be:caret_position", 1394 B_INT32_TYPE, 0, &start, sizeof(start)); 1395 } 1396 1397 1398 #undef B_TRANSLATION_CONTEXT 1399 #define B_TRANSLATION_CONTEXT "LoadAlert" 1400 1401 1402 status_t 1403 StyledEditWindow::_LoadFile(entry_ref* ref, const char* forceEncoding) 1404 { 1405 BEntry entry(ref, true); 1406 // traverse an eventual link 1407 1408 status_t status = entry.InitCheck(); 1409 if (status == B_OK && entry.IsDirectory()) 1410 status = B_IS_A_DIRECTORY; 1411 1412 BFile file; 1413 if (status == B_OK) 1414 status = file.SetTo(&entry, B_READ_ONLY); 1415 if (status == B_OK) 1416 status = fTextView->GetStyledText(&file, forceEncoding); 1417 1418 if (status == B_ENTRY_NOT_FOUND) { 1419 // Treat non-existing files consideratley; we just want to get an 1420 // empty window for them - to create this new document 1421 status = B_OK; 1422 } 1423 1424 if (status != B_OK) { 1425 // If an error occured, bail out and tell the user what happened 1426 BEntry entry(ref, true); 1427 char name[B_FILE_NAME_LENGTH]; 1428 if (entry.GetName(name) != B_OK) 1429 strlcpy(name, B_TRANSLATE("???"), sizeof(name)); 1430 1431 BString text; 1432 if (status == B_BAD_TYPE) 1433 bs_printf(&text, 1434 B_TRANSLATE("Error loading \"%s\":\n\tUnsupported format"), name); 1435 else 1436 bs_printf(&text, B_TRANSLATE("Error loading \"%s\":\n\t%s"), 1437 name, strerror(status)); 1438 1439 _ShowAlert(text, B_TRANSLATE("OK"), "", "", B_STOP_ALERT); 1440 return status; 1441 } 1442 1443 struct stat st; 1444 if (file.InitCheck() == B_OK && file.GetStat(&st) == B_OK) { 1445 bool editable = (getuid() == st.st_uid && S_IWUSR & st.st_mode) 1446 || (getgid() == st.st_gid && S_IWGRP & st.st_mode) 1447 || (S_IWOTH & st.st_mode); 1448 BVolume volume(ref->device); 1449 editable = editable && !volume.IsReadOnly(); 1450 _SetReadOnly(!editable); 1451 } 1452 1453 // update alignment 1454 switch (fTextView->Alignment()) { 1455 case B_ALIGN_LEFT: 1456 default: 1457 fAlignLeft->SetMarked(true); 1458 break; 1459 case B_ALIGN_CENTER: 1460 fAlignCenter->SetMarked(true); 1461 break; 1462 case B_ALIGN_RIGHT: 1463 fAlignRight->SetMarked(true); 1464 break; 1465 } 1466 1467 // update word wrapping 1468 fWrapItem->SetMarked(fTextView->DoesWordWrap()); 1469 return B_OK; 1470 } 1471 1472 1473 #undef B_TRANSLATION_CONTEXT 1474 #define B_TRANSLATION_CONTEXT "RevertToSavedAlert" 1475 1476 1477 void 1478 StyledEditWindow::_ReloadDocument(BMessage* message) 1479 { 1480 entry_ref ref; 1481 const char* name; 1482 1483 if (fSaveMessage == NULL || message == NULL 1484 || fSaveMessage->FindRef("directory", &ref) != B_OK 1485 || fSaveMessage->FindString("name", &name) != B_OK) 1486 return; 1487 1488 BDirectory dir(&ref); 1489 status_t status = dir.InitCheck(); 1490 BEntry entry; 1491 if (status == B_OK) 1492 status = entry.SetTo(&dir, name); 1493 1494 if (status == B_OK) 1495 status = entry.GetRef(&ref); 1496 1497 if (status != B_OK || !entry.Exists()) { 1498 BString alertText; 1499 bs_printf(&alertText, 1500 B_TRANSLATE("Cannot revert, file not found: \"%s\"."), name); 1501 _ShowAlert(alertText, B_TRANSLATE("OK"), "", "", B_STOP_ALERT); 1502 return; 1503 } 1504 1505 if (!fClean) { 1506 BString alertText; 1507 bs_printf(&alertText, 1508 B_TRANSLATE("\"%s\" has unsaved changes.\n" 1509 "Revert it to the last saved version? "), Title()); 1510 if (_ShowAlert(alertText, B_TRANSLATE("Cancel"), B_TRANSLATE("Revert"), 1511 "", B_WARNING_ALERT) != 1) 1512 return; 1513 } 1514 1515 const BCharacterSet* charset 1516 = BCharacterSetRoster::GetCharacterSetByFontID( 1517 fTextView->GetEncoding()); 1518 const char* forceEncoding = NULL; 1519 if (message->FindString("encoding", &forceEncoding) != B_OK) { 1520 if (charset != NULL) 1521 forceEncoding = charset->GetName(); 1522 } else { 1523 if (charset != NULL) { 1524 // UTF8 id assumed equal to -1 1525 const uint32 idUTF8 = (uint32)-1; 1526 uint32 id = charset->GetConversionID(); 1527 if (strcmp(forceEncoding, "next") == 0) 1528 id = id == B_MS_WINDOWS_1250_CONVERSION ? idUTF8 : id + 1; 1529 else if (strcmp(forceEncoding, "previous") == 0) 1530 id = id == idUTF8 ? B_MS_WINDOWS_1250_CONVERSION : id - 1; 1531 const BCharacterSet* newCharset 1532 = BCharacterSetRoster::GetCharacterSetByConversionID(id); 1533 if (newCharset != NULL) 1534 forceEncoding = newCharset->GetName(); 1535 } 1536 } 1537 1538 BScrollBar* vertBar = fScrollView->ScrollBar(B_VERTICAL); 1539 float vertPos = vertBar != NULL ? vertBar->Value() : 0.f; 1540 1541 DisableUpdates(); 1542 1543 fTextView->Reset(); 1544 1545 status = _LoadFile(&ref, forceEncoding); 1546 1547 if (vertBar != NULL) 1548 vertBar->SetValue(vertPos); 1549 1550 EnableUpdates(); 1551 1552 if (status != B_OK) 1553 return; 1554 1555 #undef B_TRANSLATION_CONTEXT 1556 #define B_TRANSLATION_CONTEXT "Menus" 1557 1558 // clear undo modes 1559 fUndoItem->SetLabel(B_TRANSLATE("Can't undo")); 1560 fUndoItem->SetEnabled(false); 1561 fUndoFlag = false; 1562 fCanUndo = false; 1563 fRedoFlag = false; 1564 fCanRedo = false; 1565 1566 // clear clean modes 1567 fSaveItem->SetEnabled(false); 1568 1569 fUndoCleans = false; 1570 fRedoCleans = false; 1571 fClean = true; 1572 1573 fNagOnNodeChange = true; 1574 } 1575 1576 1577 status_t 1578 StyledEditWindow::_UnlockFile() 1579 { 1580 _NodeMonitorSuspender nodeMonitorSuspender(this); 1581 1582 if (!fSaveMessage) 1583 return B_ERROR; 1584 1585 entry_ref dirRef; 1586 const char* name; 1587 if (fSaveMessage->FindRef("directory", &dirRef) != B_OK 1588 || fSaveMessage->FindString("name", &name) != B_OK) 1589 return B_BAD_VALUE; 1590 1591 BDirectory dir(&dirRef); 1592 BEntry entry(&dir, name); 1593 1594 status_t status = dir.InitCheck(); 1595 if (status != B_OK) 1596 return status; 1597 1598 status = entry.InitCheck(); 1599 if (status != B_OK) 1600 return status; 1601 1602 struct stat st; 1603 BFile file(&entry, B_READ_WRITE); 1604 status = file.InitCheck(); 1605 if (status != B_OK) 1606 return status; 1607 1608 status = file.GetStat(&st); 1609 if (status != B_OK) 1610 return status; 1611 1612 st.st_mode |= S_IWUSR; 1613 status = file.SetPermissions(st.st_mode); 1614 if (status == B_OK) 1615 _SetReadOnly(false); 1616 1617 return status; 1618 } 1619 1620 1621 bool 1622 StyledEditWindow::_Search(BString string, bool caseSensitive, bool wrap, 1623 bool backSearch, bool scrollToOccurence) 1624 { 1625 int32 start; 1626 int32 finish; 1627 1628 start = B_ERROR; 1629 1630 int32 length = string.Length(); 1631 if (length == 0) 1632 return false; 1633 1634 BString viewText(fTextView->Text()); 1635 int32 textStart, textFinish; 1636 fTextView->GetSelection(&textStart, &textFinish); 1637 if (backSearch) { 1638 if (caseSensitive) 1639 start = viewText.FindLast(string, textStart); 1640 else 1641 start = viewText.IFindLast(string, textStart); 1642 } else { 1643 if (caseSensitive) 1644 start = viewText.FindFirst(string, textFinish); 1645 else 1646 start = viewText.IFindFirst(string, textFinish); 1647 } 1648 if (start == B_ERROR && wrap) { 1649 if (backSearch) { 1650 if (caseSensitive) 1651 start = viewText.FindLast(string, viewText.Length()); 1652 else 1653 start = viewText.IFindLast(string, viewText.Length()); 1654 } else { 1655 if (caseSensitive) 1656 start = viewText.FindFirst(string, 0); 1657 else 1658 start = viewText.IFindFirst(string, 0); 1659 } 1660 } 1661 1662 if (start != B_ERROR) { 1663 finish = start + length; 1664 fTextView->Select(start, finish); 1665 1666 if (scrollToOccurence) 1667 fTextView->ScrollToSelection(); 1668 return true; 1669 } 1670 1671 return false; 1672 } 1673 1674 1675 void 1676 StyledEditWindow::_FindSelection() 1677 { 1678 int32 selectionStart, selectionFinish; 1679 fTextView->GetSelection(&selectionStart, &selectionFinish); 1680 1681 int32 selectionLength = selectionFinish- selectionStart; 1682 1683 BString viewText = fTextView->Text(); 1684 viewText.CopyInto(fStringToFind, selectionStart, selectionLength); 1685 fFindAgainItem->SetEnabled(true); 1686 _Search(fStringToFind, fCaseSensitive, fWrapAround, fBackSearch); 1687 } 1688 1689 1690 bool 1691 StyledEditWindow::_Replace(BString findThis, BString replaceWith, 1692 bool caseSensitive, bool wrap, bool backSearch) 1693 { 1694 if (_Search(findThis, caseSensitive, wrap, backSearch)) { 1695 int32 start; 1696 int32 finish; 1697 fTextView->GetSelection(&start, &finish); 1698 1699 _UpdateCleanUndoRedoSaveRevert(); 1700 fTextView->SetSuppressChanges(true); 1701 fTextView->Delete(start, start + findThis.Length()); 1702 fTextView->Insert(start, replaceWith.String(), replaceWith.Length()); 1703 fTextView->SetSuppressChanges(false); 1704 fTextView->Select(start, start + replaceWith.Length()); 1705 fTextView->ScrollToSelection(); 1706 return true; 1707 } 1708 1709 return false; 1710 } 1711 1712 1713 void 1714 StyledEditWindow::_ReplaceAll(BString findThis, BString replaceWith, 1715 bool caseSensitive) 1716 { 1717 bool first = true; 1718 fTextView->SetSuppressChanges(true); 1719 1720 // start from the beginning of text 1721 fTextView->Select(0, 0); 1722 1723 // iterate occurences of findThis without wrapping around 1724 while (_Search(findThis, caseSensitive, false, false, false)) { 1725 if (first) { 1726 _UpdateCleanUndoRedoSaveRevert(); 1727 first = false; 1728 } 1729 int32 start; 1730 int32 finish; 1731 1732 fTextView->GetSelection(&start, &finish); 1733 fTextView->Delete(start, start + findThis.Length()); 1734 fTextView->Insert(start, replaceWith.String(), replaceWith.Length()); 1735 1736 // advance the caret behind the inserted text 1737 start += replaceWith.Length(); 1738 fTextView->Select(start, start); 1739 } 1740 fTextView->ScrollToSelection(); 1741 fTextView->SetSuppressChanges(false); 1742 } 1743 1744 1745 void 1746 StyledEditWindow::_SetFontSize(float fontSize) 1747 { 1748 uint32 sameProperties; 1749 BFont font; 1750 1751 fTextView->GetFontAndColor(&font, &sameProperties); 1752 font.SetSize(fontSize); 1753 fTextView->SetFontAndColor(&font, B_FONT_SIZE); 1754 1755 _UpdateCleanUndoRedoSaveRevert(); 1756 } 1757 1758 1759 void 1760 StyledEditWindow::_SetFontColor(const rgb_color* color) 1761 { 1762 uint32 sameProperties; 1763 BFont font; 1764 1765 fTextView->GetFontAndColor(&font, &sameProperties, NULL, NULL); 1766 fTextView->SetFontAndColor(&font, 0, color); 1767 1768 _UpdateCleanUndoRedoSaveRevert(); 1769 } 1770 1771 1772 void 1773 StyledEditWindow::_SetFontStyle(const char* fontFamily, const char* fontStyle) 1774 { 1775 BFont font; 1776 uint32 sameProperties; 1777 1778 // find out what the old font was 1779 font_family oldFamily; 1780 font_style oldStyle; 1781 fTextView->GetFontAndColor(&font, &sameProperties); 1782 font.GetFamilyAndStyle(&oldFamily, &oldStyle); 1783 1784 // clear that family's bit on the menu, if necessary 1785 if (strcmp(oldFamily, fontFamily)) { 1786 BMenuItem* oldItem = fFontMenu->FindItem(oldFamily); 1787 if (oldItem != NULL) { 1788 oldItem->SetMarked(false); 1789 BMenu* menu = oldItem->Submenu(); 1790 if (menu != NULL) { 1791 oldItem = menu->FindItem(oldStyle); 1792 if (oldItem != NULL) 1793 oldItem->SetMarked(false); 1794 } 1795 } 1796 } 1797 1798 font.SetFamilyAndStyle(fontFamily, fontStyle); 1799 1800 uint16 face = 0; 1801 1802 if (!(font.Face() & B_REGULAR_FACE)) 1803 face = font.Face(); 1804 1805 if (fBoldItem->IsMarked()) 1806 face |= B_BOLD_FACE; 1807 1808 if (fItalicItem->IsMarked()) 1809 face |= B_ITALIC_FACE; 1810 1811 font.SetFace(face); 1812 1813 fTextView->SetFontAndColor(&font, B_FONT_FAMILY_AND_STYLE); 1814 1815 BMenuItem* superItem; 1816 superItem = fFontMenu->FindItem(fontFamily); 1817 if (superItem != NULL) { 1818 superItem->SetMarked(true); 1819 fCurrentFontItem = superItem; 1820 } 1821 1822 _UpdateCleanUndoRedoSaveRevert(); 1823 } 1824 1825 1826 #undef B_TRANSLATION_CONTEXT 1827 #define B_TRANSLATION_CONTEXT "Statistics" 1828 1829 1830 int32 1831 StyledEditWindow::_ShowStatistics() 1832 { 1833 size_t words = 0; 1834 bool inWord = false; 1835 size_t length = fTextView->TextLength(); 1836 1837 for (size_t i = 0; i < length; i++) { 1838 if (BUnicodeChar::IsWhitespace(fTextView->Text()[i])) { 1839 inWord = false; 1840 } else if (!inWord) { 1841 words++; 1842 inWord = true; 1843 } 1844 } 1845 1846 BString result; 1847 result << B_TRANSLATE("Document statistics") << '\n' << '\n' 1848 << B_TRANSLATE("Lines:") << ' ' << fTextView->CountLines() << '\n' 1849 << B_TRANSLATE("Characters:") << ' ' << length << '\n' 1850 << B_TRANSLATE("Words:") << ' ' << words; 1851 1852 BAlert* alert = new BAlert("Statistics", result, B_TRANSLATE("OK"), NULL, 1853 NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_INFO_ALERT); 1854 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1855 1856 return alert->Go(); 1857 } 1858 1859 1860 void 1861 StyledEditWindow::_SetReadOnly(bool readOnly) 1862 { 1863 fReplaceItem->SetEnabled(!readOnly); 1864 fReplaceSameItem->SetEnabled(!readOnly); 1865 fFontMenu->SetEnabled(!readOnly); 1866 fAlignLeft->Menu()->SetEnabled(!readOnly); 1867 fWrapItem->SetEnabled(!readOnly); 1868 fTextView->MakeEditable(!readOnly); 1869 } 1870 1871 1872 #undef B_TRANSLATION_CONTEXT 1873 #define B_TRANSLATION_CONTEXT "Menus" 1874 1875 1876 void 1877 StyledEditWindow::_UpdateCleanUndoRedoSaveRevert() 1878 { 1879 fClean = false; 1880 fUndoCleans = false; 1881 fRedoCleans = false; 1882 fReloadItem->SetEnabled(fSaveMessage != NULL); 1883 fEncodingItem->SetEnabled(fSaveMessage != NULL); 1884 fSaveItem->SetEnabled(true); 1885 fUndoItem->SetLabel(B_TRANSLATE("Can't undo")); 1886 fUndoItem->SetEnabled(false); 1887 fCanUndo = false; 1888 fCanRedo = false; 1889 } 1890 1891 1892 int32 1893 StyledEditWindow::_ShowAlert(const BString& text, const BString& label, 1894 const BString& label2, const BString& label3, alert_type type) const 1895 { 1896 const char* button2 = NULL; 1897 if (label2.Length() > 0) 1898 button2 = label2.String(); 1899 1900 const char* button3 = NULL; 1901 button_spacing spacing = B_EVEN_SPACING; 1902 if (label3.Length() > 0) { 1903 button3 = label3.String(); 1904 spacing = B_OFFSET_SPACING; 1905 } 1906 1907 BAlert* alert = new BAlert("Alert", text.String(), label.String(), button2, 1908 button3, B_WIDTH_AS_USUAL, spacing, type); 1909 alert->SetShortcut(0, B_ESCAPE); 1910 1911 return alert->Go(); 1912 } 1913 1914 1915 BMenu* 1916 StyledEditWindow::_PopulateEncodingMenu(BMenu* menu, const char* currentEncoding) 1917 { 1918 menu->SetRadioMode(true); 1919 BString encoding(currentEncoding); 1920 if (encoding.Length() == 0) 1921 encoding.SetTo("UTF-8"); 1922 1923 BCharacterSetRoster roster; 1924 BCharacterSet charset; 1925 while (roster.GetNextCharacterSet(&charset) == B_OK) { 1926 const char* mime = charset.GetMIMEName(); 1927 BString name(charset.GetPrintName()); 1928 1929 if (mime) 1930 name << " (" << mime << ")"; 1931 1932 BMessage *message = new BMessage(MENU_RELOAD); 1933 if (message != NULL) { 1934 message->AddString("encoding", charset.GetName()); 1935 BMenuItem* item = new BMenuItem(name, message); 1936 if (encoding.Compare(charset.GetName()) == 0) 1937 item->SetMarked(true); 1938 menu->AddItem(item); 1939 } 1940 } 1941 1942 menu->AddSeparatorItem(); 1943 BMessage *message = new BMessage(MENU_RELOAD); 1944 message->AddString("encoding", "auto"); 1945 menu->AddItem(new BMenuItem(B_TRANSLATE("Autodetect"), message)); 1946 1947 message = new BMessage(MENU_RELOAD); 1948 message->AddString("encoding", "next"); 1949 AddShortcut(B_PAGE_DOWN, B_OPTION_KEY, message); 1950 message = new BMessage(MENU_RELOAD); 1951 message->AddString("encoding", "previous"); 1952 AddShortcut(B_PAGE_UP, B_OPTION_KEY, message); 1953 1954 return menu; 1955 } 1956 1957 1958 #undef B_TRANSLATION_CONTEXT 1959 #define B_TRANSLATION_CONTEXT "NodeMonitorAlerts" 1960 1961 1962 void 1963 StyledEditWindow::_ShowNodeChangeAlert(const char* name, bool removed) 1964 { 1965 if (!fNagOnNodeChange) 1966 return; 1967 1968 BString alertText(removed ? B_TRANSLATE("File \"%file%\" was removed by " 1969 "another application, recover it?") 1970 : B_TRANSLATE("File \"%file%\" was modified by " 1971 "another application, reload it?")); 1972 alertText.ReplaceAll("%file%", name); 1973 1974 if (_ShowAlert(alertText, removed ? B_TRANSLATE("Recover") 1975 : B_TRANSLATE("Reload"), B_TRANSLATE("Ignore"), "", 1976 B_WARNING_ALERT) == 0) 1977 { 1978 if (!removed) { 1979 // supress the warning - user has already agreed 1980 fClean = true; 1981 BMessage msg(MENU_RELOAD); 1982 _ReloadDocument(&msg); 1983 } else 1984 Save(); 1985 } else 1986 fNagOnNodeChange = false; 1987 1988 fSaveItem->SetEnabled(!fClean); 1989 } 1990 1991 1992 void 1993 StyledEditWindow::_HandleNodeMonitorEvent(BMessage *message) 1994 { 1995 int32 opcode = 0; 1996 if (message->FindInt32("opcode", &opcode) != B_OK) 1997 return; 1998 1999 if (opcode != B_ENTRY_CREATED 2000 && message->FindInt64("node") != fNodeRef.node) 2001 // bypass foreign nodes' event 2002 return; 2003 2004 switch (opcode) { 2005 case B_STAT_CHANGED: 2006 { 2007 int32 fields = 0; 2008 if (message->FindInt32("fields", &fields) == B_OK 2009 && (fields & (B_STAT_SIZE | B_STAT_MODIFICATION_TIME 2010 | B_STAT_MODE)) == 0) 2011 break; 2012 2013 const char* name = NULL; 2014 if (fSaveMessage->FindString("name", &name) != B_OK) 2015 break; 2016 2017 _ShowNodeChangeAlert(name, false); 2018 } 2019 break; 2020 2021 case B_ENTRY_MOVED: 2022 { 2023 int32 device = 0; 2024 int64 srcFolder = 0; 2025 int64 dstFolder = 0; 2026 const char* name = NULL; 2027 if (message->FindInt32("device", &device) != B_OK 2028 || message->FindInt64("to directory", &dstFolder) != B_OK 2029 || message->FindInt64("from directory", &srcFolder) != B_OK 2030 || message->FindString("name", &name) != B_OK) 2031 break; 2032 2033 entry_ref newRef(device, dstFolder, name); 2034 BEntry entry(&newRef); 2035 2036 BEntry dirEntry; 2037 entry.GetParent(&dirEntry); 2038 2039 entry_ref ref; 2040 dirEntry.GetRef(&ref); 2041 fSaveMessage->ReplaceRef("directory", &ref); 2042 fSaveMessage->ReplaceString("name", name); 2043 2044 // store previous name - it may be useful in case 2045 // we have just moved to temporary copy of file (vim case) 2046 const char* sourceName = NULL; 2047 if (message->FindString("from name", &sourceName) == B_OK) { 2048 fSaveMessage->RemoveName("org.name"); 2049 fSaveMessage->AddString("org.name", sourceName); 2050 fSaveMessage->RemoveName("move time"); 2051 fSaveMessage->AddInt64("move time", system_time()); 2052 } 2053 2054 SetTitle(name); 2055 be_roster->AddToRecentDocuments(&newRef, APP_SIGNATURE); 2056 2057 if (srcFolder != dstFolder) { 2058 _SwitchNodeMonitor(false); 2059 _SwitchNodeMonitor(true); 2060 } 2061 } 2062 break; 2063 2064 case B_ENTRY_REMOVED: 2065 { 2066 _SwitchNodeMonitor(false); 2067 2068 fClean = false; 2069 2070 // some editors like vim save files in following way: 2071 // 1) move t.txt -> t.txt~ 2072 // 2) re-create t.txt and write data to it 2073 // 3) remove t.txt~ 2074 // go to catch this case 2075 int32 device = 0; 2076 int64 directory = 0; 2077 BString orgName; 2078 if (fSaveMessage->FindString("org.name", &orgName) == B_OK 2079 && message->FindInt32("device", &device) == B_OK 2080 && message->FindInt64("directory", &directory) == B_OK) 2081 { 2082 // reuse the source name if it is not too old 2083 bigtime_t time = fSaveMessage->FindInt64("move time"); 2084 if ((system_time() - time) < 1000000) { 2085 entry_ref ref(device, directory, orgName); 2086 BEntry entry(&ref); 2087 if (entry.InitCheck() == B_OK) { 2088 _SwitchNodeMonitor(true, &ref); 2089 } 2090 2091 fSaveMessage->ReplaceString("name", orgName); 2092 fSaveMessage->RemoveName("org.name"); 2093 fSaveMessage->RemoveName("move time"); 2094 2095 SetTitle(orgName); 2096 _ShowNodeChangeAlert(orgName, false); 2097 break; 2098 } 2099 } 2100 2101 const char* name = NULL; 2102 if (message->FindString("name", &name) != B_OK 2103 && fSaveMessage->FindString("name", &name) != B_OK) 2104 name = "Unknown"; 2105 2106 _ShowNodeChangeAlert(name, true); 2107 } 2108 break; 2109 2110 default: 2111 break; 2112 } 2113 } 2114 2115 2116 void 2117 StyledEditWindow::_SwitchNodeMonitor(bool on, entry_ref* ref) 2118 { 2119 if (!on) { 2120 watch_node(&fNodeRef, B_STOP_WATCHING, this); 2121 watch_node(&fFolderNodeRef, B_STOP_WATCHING, this); 2122 fNodeRef = node_ref(); 2123 fFolderNodeRef = node_ref(); 2124 return; 2125 } 2126 2127 BEntry entry, folderEntry; 2128 2129 if (ref != NULL) { 2130 entry.SetTo(ref, true); 2131 entry.GetParent(&folderEntry); 2132 2133 } else if (fSaveMessage != NULL) { 2134 entry_ref ref; 2135 const char* name = NULL; 2136 if (fSaveMessage->FindRef("directory", &ref) != B_OK 2137 || fSaveMessage->FindString("name", &name) != B_OK) 2138 return; 2139 2140 BDirectory dir(&ref); 2141 entry.SetTo(&dir, name); 2142 folderEntry.SetTo(&ref); 2143 2144 } else 2145 return; 2146 2147 if (entry.InitCheck() != B_OK || folderEntry.InitCheck() != B_OK) 2148 return; 2149 2150 entry.GetNodeRef(&fNodeRef); 2151 folderEntry.GetNodeRef(&fFolderNodeRef); 2152 2153 watch_node(&fNodeRef, B_WATCH_STAT, this); 2154 watch_node(&fFolderNodeRef, B_WATCH_DIRECTORY, this); 2155 } 2156