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