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