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