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