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