2 * Copyright 2002-2012, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
8 * Philippe Saint-Pierre
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"
28 #include <CharacterSet.h>
29 #include <CharacterSetRoster.h>
30 #include <Clipboard.h>
33 #include <FilePanel.h>
39 #include <NodeMonitor.h>
42 #include <RecentItems.h>
46 #include <ScrollView.h>
47 #include <TextControl.h>
49 #include <TranslationUtils.h>
50 #include <UnicodeChar.h>
55 using namespace BPrivate
;
58 const float kLineViewWidth
= 30.0;
59 const char* kInfoAttributeName
= "StyledEdit-info";
62 #undef B_TRANSLATION_CONTEXT
63 #define B_TRANSLATION_CONTEXT "StyledEditWindow"
66 // This is a temporary solution for building BString with printf like format.
67 // will be removed in the future.
69 bs_printf(BString
* string
, const char* format
, ...)
74 vasprintf(&buf
, format
, ap
);
84 StyledEditWindow::StyledEditWindow(BRect frame
, int32 id
, uint32 encoding
)
86 BWindow(frame
, "untitled", B_DOCUMENT_WINDOW
, B_ASYNCHRONOUS_CONTROLS
),
90 _InitWindow(encoding
);
91 BString
unTitled(B_TRANSLATE("Untitled "));
93 SetTitle(unTitled
.String());
94 fSaveItem
->SetEnabled(true);
95 // allow saving empty files
100 StyledEditWindow::StyledEditWindow(BRect frame
, entry_ref
* ref
, uint32 encoding
)
102 BWindow(frame
, "untitled", B_DOCUMENT_WINDOW
, B_ASYNCHRONOUS_CONTROLS
),
106 _InitWindow(encoding
);
112 StyledEditWindow::~StyledEditWindow()
115 delete fPrintSettings
;
121 StyledEditWindow::Quit()
123 _SwitchNodeMonitor(false);
126 if (StyledEditApp
* app
= dynamic_cast<StyledEditApp
*>(be_app
))
127 app
->CloseDocument();
132 #undef B_TRANSLATION_CONTEXT
133 #define B_TRANSLATION_CONTEXT "QuitAlert"
137 StyledEditWindow::QuitRequested()
142 if (fTextView
->TextLength() == 0 && fSaveMessage
== NULL
)
146 bs_printf(&alertText
,
147 B_TRANSLATE("Save changes to the document \"%s\"? "), Title());
149 int32 index
= _ShowAlert(alertText
, B_TRANSLATE("Cancel"),
150 B_TRANSLATE("Don't save"), B_TRANSLATE("Save"), B_WARNING_ALERT
);
153 return false; // "cancel": dont save, dont close the window
156 return true; // "don't save": just close the window
159 SaveAs(new BMessage(SAVE_THEN_QUIT
));
163 return Save() == B_OK
;
168 StyledEditWindow::MessageReceived(BMessage
* message
)
170 if (message
->WasDropped()) {
172 if (message
->FindRef("refs", 0, &ref
)==B_OK
) {
173 message
->what
= B_REFS_RECEIVED
;
174 be_app
->PostMessage(message
);
178 switch (message
->what
) {
191 case B_SAVE_REQUESTED
:
196 if (Save(message
) == B_OK
)
201 _ReloadDocument(message
);
210 PageSetup(fTextView
->Window()->Title());
213 Print(fTextView
->Window()->Title());
216 be_app
->PostMessage(B_QUIT_REQUESTED
);
222 ASSERT(fCanUndo
|| fCanRedo
);
223 ASSERT(!(fCanUndo
&& fCanRedo
));
229 fTextView
->Undo(be_clipboard
);
232 fTextView
->Cut(be_clipboard
);
235 fTextView
->Copy(be_clipboard
);
238 fTextView
->Paste(be_clipboard
);
245 if (fFindWindow
== NULL
) {
246 BRect
findWindowFrame(Frame());
247 findWindowFrame
.InsetBy(
248 (findWindowFrame
.Width() - 400) / 2,
249 (findWindowFrame
.Height() - 235) / 2);
251 fFindWindow
= new FindWindow(findWindowFrame
, this,
252 &fStringToFind
, fCaseSensitive
, fWrapAround
, fBackSearch
);
255 } else if (fFindWindow
->IsHidden())
258 fFindWindow
->Activate();
261 case MSG_FIND_WINDOW_QUIT
:
266 case MSG_REPLACE_WINDOW_QUIT
:
268 fReplaceWindow
= NULL
;
272 message
->FindString("findtext", &fStringToFind
);
273 fFindAgainItem
->SetEnabled(true);
274 message
->FindBool("casesens", &fCaseSensitive
);
275 message
->FindBool("wrap", &fWrapAround
);
276 message
->FindBool("backsearch", &fBackSearch
);
278 _Search(fStringToFind
, fCaseSensitive
, fWrapAround
, fBackSearch
);
280 case MENU_FIND_AGAIN
:
281 _Search(fStringToFind
, fCaseSensitive
, fWrapAround
, fBackSearch
);
283 case MENU_FIND_SELECTION
:
288 if (fReplaceWindow
== NULL
) {
289 BRect
replaceWindowFrame(Frame());
290 replaceWindowFrame
.InsetBy(
291 (replaceWindowFrame
.Width() - 400) / 2,
292 (replaceWindowFrame
.Height() - 284) / 2);
294 fReplaceWindow
= new ReplaceWindow(replaceWindowFrame
, this,
295 &fStringToFind
, &fReplaceString
, fCaseSensitive
,
296 fWrapAround
, fBackSearch
);
297 fReplaceWindow
->Show();
299 } else if (fReplaceWindow
->IsHidden())
300 fReplaceWindow
->Show();
302 fReplaceWindow
->Activate();
307 message
->FindBool("casesens", &fCaseSensitive
);
308 message
->FindBool("wrap", &fWrapAround
);
309 message
->FindBool("backsearch", &fBackSearch
);
311 message
->FindString("FindText", &fStringToFind
);
312 message
->FindString("ReplaceText", &fReplaceString
);
314 fFindAgainItem
->SetEnabled(true);
315 fReplaceSameItem
->SetEnabled(true);
317 _Replace(fStringToFind
, fReplaceString
, fCaseSensitive
, fWrapAround
,
321 case MENU_REPLACE_SAME
:
322 _Replace(fStringToFind
, fReplaceString
, fCaseSensitive
, fWrapAround
,
326 case MSG_REPLACE_ALL
:
328 message
->FindBool("casesens", &fCaseSensitive
);
329 message
->FindString("FindText", &fStringToFind
);
330 message
->FindString("ReplaceText", &fReplaceString
);
333 message
->FindBool("allwindows", &allWindows
);
335 fFindAgainItem
->SetEnabled(true);
336 fReplaceSameItem
->SetEnabled(true);
339 SearchAllWindows(fStringToFind
, fReplaceString
, fCaseSensitive
);
341 _ReplaceAll(fStringToFind
, fReplaceString
, fCaseSensitive
);
346 _HandleNodeMonitorEvent(message
);
354 if (message
->FindFloat("size", &fontSize
) == B_OK
)
355 _SetFontSize(fontSize
);
360 const char* fontFamily
= NULL
;
361 const char* fontStyle
= NULL
;
363 if (message
->FindPointer("source", &ptr
) == B_OK
) {
364 BMenuItem
* item
= static_cast<BMenuItem
*>(ptr
);
365 fontFamily
= item
->Label();
369 font
.SetFamilyAndStyle(fontFamily
, fontStyle
);
370 fItalicItem
->SetMarked((font
.Face() & B_ITALIC_FACE
) != 0);
371 fBoldItem
->SetMarked((font
.Face() & B_BOLD_FACE
) != 0);
373 _SetFontStyle(fontFamily
, fontStyle
);
378 const char* fontFamily
= NULL
;
379 const char* fontStyle
= NULL
;
381 if (message
->FindPointer("source", &ptr
) == B_OK
) {
382 BMenuItem
* item
= static_cast<BMenuItem
*>(ptr
);
383 fontStyle
= item
->Label();
384 BMenu
* menu
= item
->Menu();
386 BMenuItem
* super_item
= menu
->Superitem();
387 if (super_item
!= NULL
)
388 fontFamily
= super_item
->Label();
393 font
.SetFamilyAndStyle(fontFamily
, fontStyle
);
394 fItalicItem
->SetMarked((font
.Face() & B_ITALIC_FACE
) != 0);
395 fBoldItem
->SetMarked((font
.Face() & B_BOLD_FACE
) != 0);
397 _SetFontStyle(fontFamily
, fontStyle
);
402 uint32 sameProperties
;
404 fTextView
->GetFontAndColor(&font
, &sameProperties
);
406 if (fItalicItem
->IsMarked())
407 font
.SetFace(B_REGULAR_FACE
);
408 fItalicItem
->SetMarked(!fItalicItem
->IsMarked());
412 font
.GetFamilyAndStyle(&family
, &style
);
414 _SetFontStyle(family
, style
);
419 uint32 sameProperties
;
421 fTextView
->GetFontAndColor(&font
, &sameProperties
);
423 if (fBoldItem
->IsMarked())
424 font
.SetFace(B_REGULAR_FACE
);
425 fBoldItem
->SetMarked(!fBoldItem
->IsMarked());
429 font
.GetFamilyAndStyle(&family
, &style
);
431 _SetFontStyle(family
, style
);
438 if (message
->FindData("color", B_RGB_COLOR_TYPE
,
439 (const void**)&color
, &colorLength
) == B_OK
440 && colorLength
== sizeof(rgb_color
)) {
442 * TODO: Ideally, when selecting the default color,
443 * you wouldn't naively apply it; it shouldn't lose its nature.
444 * When reloaded with a different default color, it should
445 * reflect that different choice.
447 _SetFontColor(color
);
455 fTextView
->SetAlignment(B_ALIGN_LEFT
);
456 _UpdateCleanUndoRedoSaveRevert();
459 fTextView
->SetAlignment(B_ALIGN_CENTER
);
460 _UpdateCleanUndoRedoSaveRevert();
463 fTextView
->SetAlignment(B_ALIGN_RIGHT
);
464 _UpdateCleanUndoRedoSaveRevert();
468 BRect
textRect(fTextView
->Bounds());
469 textRect
.OffsetTo(B_ORIGIN
);
470 textRect
.InsetBy(TEXT_INSET
, TEXT_INSET
);
471 if (fTextView
->DoesWordWrap()) {
472 fTextView
->SetWordWrap(false);
473 fWrapItem
->SetMarked(false);
474 // the width comes from stylededit R5. TODO: find a better way
475 textRect
.SetRightBottom(BPoint(1500.0, textRect
.RightBottom().y
));
477 fTextView
->SetWordWrap(true);
478 fWrapItem
->SetMarked(true);
480 fTextView
->SetTextRect(textRect
);
482 _UpdateCleanUndoRedoSaveRevert();
485 case SHOW_STATISTICS
:
489 fCutItem
->SetEnabled(true);
490 fCopyItem
->SetEnabled(true);
493 fCutItem
->SetEnabled(false);
494 fCopyItem
->SetEnabled(false);
504 // then a redo will make us clean again
511 fUndoItem
->SetLabel(B_TRANSLATE("Redo typing"));
512 fUndoItem
->SetEnabled(true);
515 if (fRedoFlag
&& fRedoCleans
) {
521 // then an undo will make us clean again
525 // no more cleaning from undo now...
531 fUndoItem
->SetLabel(B_TRANSLATE("Undo typing"));
532 fUndoItem
->SetEnabled(true);
536 fSaveItem
->SetEnabled(fSaveMessage
== NULL
);
538 fSaveItem
->SetEnabled(true);
540 fReloadItem
->SetEnabled(fSaveMessage
!= NULL
);
541 fEncodingItem
->SetEnabled(fSaveMessage
!= NULL
);
544 case SAVE_AS_ENCODING
:
546 if (message
->FindPointer("source", &ptr
) == B_OK
547 && fSavePanelEncodingMenu
!= NULL
) {
548 fTextView
->SetEncoding(
549 (uint32
)fSavePanelEncodingMenu
->IndexOf((BMenuItem
*)ptr
));
555 message
->AddBool("modified", !fClean
);
556 bool readOnly
= !fTextView
->IsEditable();
557 message
->AddBool("readOnly", readOnly
);
559 BVolume
volume(fNodeRef
.device
);
560 message
->AddBool("canUnlock", !volume
.IsReadOnly());
562 fStatusView
->SetStatus(message
);
566 case UPDATE_STATUS_REF
:
571 if (fSaveMessage
!= NULL
572 && fSaveMessage
->FindRef("directory", &ref
) == B_OK
573 && fSaveMessage
->FindString("name", &name
) == B_OK
) {
575 BDirectory
dir(&ref
);
576 status_t status
= dir
.InitCheck();
579 status
= entry
.SetTo(&dir
, name
);
581 status
= entry
.GetRef(&ref
);
583 fStatusView
->SetRef(ref
);
589 status_t status
= _UnlockFile();
590 if (status
!= B_OK
) {
593 B_TRANSLATE("Unable to unlock file\n\t%s"),
595 _ShowAlert(text
, B_TRANSLATE("OK"), "", "", B_STOP_ALERT
);
597 PostMessage(UPDATE_STATUS
);
601 case UPDATE_LINE_SELECTION
:
604 if (message
->FindInt32("be:line", &line
) == B_OK
) {
605 fTextView
->GoToLine(line
);
606 fTextView
->ScrollToSelection();
610 if (message
->FindInt32("be:selection_offset", &start
) == B_OK
) {
611 if (message
->FindInt32("be:selection_length", &length
) != B_OK
)
614 fTextView
->Select(start
, start
+ length
);
615 fTextView
->ScrollToOffset(start
);
620 BWindow::MessageReceived(message
);
627 StyledEditWindow::MenusBeginning()
629 // update the font menu
630 // unselect the old values
631 if (fCurrentFontItem
!= NULL
) {
632 fCurrentFontItem
->SetMarked(false);
633 BMenu
* menu
= fCurrentFontItem
->Submenu();
635 BMenuItem
* item
= menu
->FindMarked();
637 item
->SetMarked(false);
641 if (fCurrentStyleItem
!= NULL
) {
642 fCurrentStyleItem
->SetMarked(false);
645 BMenuItem
* oldColorItem
= fFontColorMenu
->FindMarked();
646 if (oldColorItem
!= NULL
)
647 oldColorItem
->SetMarked(false);
649 BMenuItem
* oldSizeItem
= fFontSizeMenu
->FindMarked();
650 if (oldSizeItem
!= NULL
)
651 oldSizeItem
->SetMarked(false);
653 // find the current font, color, size
655 uint32 sameProperties
;
656 rgb_color color
= ui_color(B_DOCUMENT_TEXT_COLOR
);
658 fTextView
->GetFontAndColor(&font
, &sameProperties
, &color
, &sameColor
);
662 if (fDefaultFontColorItem
->Color() == color
)
663 fDefaultFontColorItem
->SetMarked(true);
665 for (int i
= 0; i
< fFontColorMenu
->CountItems(); i
++) {
666 ColorMenuItem
* item
= dynamic_cast<ColorMenuItem
*>
667 (fFontColorMenu
->ItemAt(i
));
668 if (item
!= NULL
&& item
->Color() == color
) {
669 item
->SetMarked(true);
676 if (sameProperties
& B_FONT_SIZE
) {
677 if ((int)font
.Size() == font
.Size()) {
678 // select the current font size
679 char fontSizeStr
[16];
680 snprintf(fontSizeStr
, 15, "%i", (int)font
.Size());
681 BMenuItem
* item
= fFontSizeMenu
->FindItem(fontSizeStr
);
683 item
->SetMarked(true);
689 font
.GetFamilyAndStyle(&family
, &style
);
691 fCurrentFontItem
= fFontMenu
->FindItem(family
);
693 if (fCurrentFontItem
!= NULL
) {
694 fCurrentFontItem
->SetMarked(true);
695 BMenu
* menu
= fCurrentFontItem
->Submenu();
697 BMenuItem
* item
= menu
->FindItem(style
);
698 fCurrentStyleItem
= item
;
699 if (fCurrentStyleItem
!= NULL
)
700 item
->SetMarked(true);
704 fBoldItem
->SetMarked((font
.Face() & B_BOLD_FACE
) != 0);
705 fItalicItem
->SetMarked((font
.Face() & B_ITALIC_FACE
) != 0);
707 switch (fTextView
->Alignment()) {
710 fAlignLeft
->SetMarked(true);
713 fAlignCenter
->SetMarked(true);
716 fAlignRight
->SetMarked(true);
721 const BCharacterSet
* charset
722 = BCharacterSetRoster::GetCharacterSetByFontID(fTextView
->GetEncoding());
723 BMenu
* encodingMenu
= fEncodingItem
->Submenu();
724 if (charset
!= NULL
&& encodingMenu
!= NULL
) {
725 const char* mime
= charset
->GetMIMEName();
726 BString
name(charset
->GetPrintName());
728 name
<< " (" << mime
<< ")";
730 BMenuItem
* item
= encodingMenu
->FindItem(name
);
732 item
->SetMarked(true);
737 #undef B_TRANSLATION_CONTEXT
738 #define B_TRANSLATION_CONTEXT "SaveAlert"
742 StyledEditWindow::Save(BMessage
* message
)
744 _NodeMonitorSuspender
nodeMonitorSuspender(this);
747 message
= fSaveMessage
;
754 if (message
->FindRef("directory", &dirRef
) != B_OK
755 || message
->FindString("name", &name
) != B_OK
)
758 BDirectory
dir(&dirRef
);
759 BEntry
entry(&dir
, name
);
761 status_t status
= B_ERROR
;
762 if (dir
.InitCheck() == B_OK
&& entry
.InitCheck() == B_OK
) {
764 BFile
file(&entry
, B_READ_WRITE
| B_CREATE_FILE
);
765 if (file
.InitCheck() == B_OK
766 && (status
= file
.GetStat(&st
)) == B_OK
) {
767 // check the file permissions
768 if (!((getuid() == st
.st_uid
&& (S_IWUSR
& st
.st_mode
))
769 || (getgid() == st
.st_gid
&& (S_IWGRP
& st
.st_mode
))
770 || (S_IWOTH
& st
.st_mode
))) {
772 bs_printf(&alertText
, B_TRANSLATE("This file is marked "
773 "read-only. Save changes to the document \"%s\"? "), name
);
774 switch (_ShowAlert(alertText
, B_TRANSLATE("Cancel"),
775 B_TRANSLATE("Don't save"),
776 B_TRANSLATE("Save"), B_WARNING_ALERT
)) {
786 status
= fTextView
->WriteStyledEditFile(&file
);
790 if (status
!= B_OK
) {
792 bs_printf(&alertText
, B_TRANSLATE("Error saving \"%s\":\n%s"), name
,
795 _ShowAlert(alertText
, B_TRANSLATE("OK"), "", "", B_STOP_ALERT
);
801 if (fSaveMessage
!= message
) {
803 fSaveMessage
= new BMessage(*message
);
807 fSaveItem
->SetEnabled(false);
811 fNagOnNodeChange
= true;
813 PostMessage(UPDATE_STATUS
);
814 PostMessage(UPDATE_STATUS_REF
);
820 #undef B_TRANSLATION_CONTEXT
821 #define B_TRANSLATION_CONTEXT "Open_and_SaveAsPanel"
825 StyledEditWindow::SaveAs(BMessage
* message
)
827 if (fSavePanel
== NULL
) {
828 entry_ref
* directory
= NULL
;
830 if (fSaveMessage
!= NULL
) {
831 if (fSaveMessage
->FindRef("directory", &dirRef
) == B_OK
)
835 BMessenger
target(this);
836 fSavePanel
= new BFilePanel(B_SAVE_PANEL
, &target
,
837 directory
, B_FILE_NODE
, false);
839 BMenuBar
* menuBar
= dynamic_cast<BMenuBar
*>(
840 fSavePanel
->Window()->FindView("MenuBar"));
841 if (menuBar
!= NULL
) {
842 fSavePanelEncodingMenu
= new BMenu(B_TRANSLATE("Encoding"));
843 fSavePanelEncodingMenu
->SetRadioMode(true);
844 menuBar
->AddItem(fSavePanelEncodingMenu
);
846 BCharacterSetRoster roster
;
847 BCharacterSet charset
;
848 while (roster
.GetNextCharacterSet(&charset
) == B_NO_ERROR
) {
849 BString
name(charset
.GetPrintName());
850 const char* mime
= charset
.GetMIMEName();
856 BMenuItem
* item
= new BMenuItem(name
.String(),
857 new BMessage(SAVE_AS_ENCODING
));
858 item
->SetTarget(this);
859 fSavePanelEncodingMenu
->AddItem(item
);
860 if (charset
.GetFontID() == fTextView
->GetEncoding())
861 item
->SetMarked(true);
866 fSavePanel
->SetSaveText(Title());
868 fSavePanel
->SetMessage(message
);
876 StyledEditWindow::OpenFile(entry_ref
* ref
)
878 if (_LoadFile(ref
) != B_OK
) {
879 fSaveItem
->SetEnabled(true);
880 // allow saving new files
884 fSaveMessage
= new BMessage(B_SAVE_REQUESTED
);
886 BEntry
entry(ref
, true);
889 char name
[B_FILE_NAME_LENGTH
];
891 entry
.GetParent(&parent
);
893 parent
.GetRef(&parentRef
);
894 fSaveMessage
->AddRef("directory", &parentRef
);
895 fSaveMessage
->AddString("name", name
);
901 _SwitchNodeMonitor(true, ref
);
903 PostMessage(UPDATE_STATUS_REF
);
905 fReloadItem
->SetEnabled(fSaveMessage
!= NULL
);
906 fEncodingItem
->SetEnabled(fSaveMessage
!= NULL
);
911 StyledEditWindow::PageSetup(const char* documentName
)
913 BPrintJob
printJob(documentName
);
915 if (fPrintSettings
!= NULL
)
916 printJob
.SetSettings(new BMessage(*fPrintSettings
));
918 status_t result
= printJob
.ConfigPage();
919 if (result
== B_OK
) {
920 delete fPrintSettings
;
921 fPrintSettings
= printJob
.Settings();
929 StyledEditWindow::Print(const char* documentName
)
931 BPrintJob
printJob(documentName
);
933 printJob
.SetSettings(new BMessage(*fPrintSettings
));
935 if (printJob
.ConfigJob() != B_OK
)
938 delete fPrintSettings
;
939 fPrintSettings
= printJob
.Settings();
941 // information from printJob
942 BRect printableRect
= printJob
.PrintableRect();
943 int32 firstPage
= printJob
.FirstPage();
944 int32 lastPage
= printJob
.LastPage();
946 // lines eventually to be used to compute pages to print
948 int32 lastLine
= fTextView
->CountLines();
950 // values to be computed
951 int32 pagesInDocument
= 1;
952 int32 linesInDocument
= fTextView
->CountLines();
954 int32 currentLine
= 0;
955 while (currentLine
< linesInDocument
) {
956 float currentHeight
= 0;
957 while (currentHeight
< printableRect
.Height() && currentLine
959 currentHeight
+= fTextView
->LineHeight(currentLine
);
960 if (currentHeight
< printableRect
.Height())
963 if (pagesInDocument
== lastPage
)
964 lastLine
= currentLine
- 1;
966 if (currentHeight
>= printableRect
.Height()) {
968 if (pagesInDocument
== firstPage
)
969 firstLine
= currentLine
;
973 if (lastPage
> pagesInDocument
- 1) {
974 lastPage
= pagesInDocument
- 1;
975 lastLine
= currentLine
- 1;
980 if (fTextView
->CountLines() > 0 && fTextView
->TextLength() > 0) {
981 int32 printLine
= firstLine
;
982 while (printLine
<= lastLine
) {
983 float currentHeight
= 0;
984 int32 firstLineOnPage
= printLine
;
985 while (currentHeight
< printableRect
.Height()
986 && printLine
<= lastLine
)
988 currentHeight
+= fTextView
->LineHeight(printLine
);
989 if (currentHeight
< printableRect
.Height())
994 if (firstLineOnPage
!= 0)
995 top
= fTextView
->TextHeight(0, firstLineOnPage
- 1);
997 float bottom
= fTextView
->TextHeight(0, printLine
- 1);
998 BRect
textRect(0.0, top
+ TEXT_INSET
,
999 printableRect
.Width(), bottom
+ TEXT_INSET
);
1000 printJob
.DrawView(fTextView
, textRect
, B_ORIGIN
);
1001 printJob
.SpoolPage();
1006 printJob
.CommitJob();
1011 StyledEditWindow::SearchAllWindows(BString find
, BString replace
,
1015 numWindows
= be_app
->CountWindows();
1018 message
= new BMessage(MSG_REPLACE_ALL
);
1019 message
->AddString("FindText", find
);
1020 message
->AddString("ReplaceText", replace
);
1021 message
->AddBool("casesens", caseSensitive
);
1023 while (numWindows
>= 0) {
1024 StyledEditWindow
* window
= dynamic_cast<StyledEditWindow
*>(
1025 be_app
->WindowAt(numWindows
));
1027 BMessenger
messenger(window
);
1028 messenger
.SendMessage(message
);
1036 StyledEditWindow::IsDocumentEntryRef(const entry_ref
* ref
)
1041 if (fSaveMessage
== NULL
)
1046 if (fSaveMessage
->FindRef("directory", &dir
) != B_OK
1047 || fSaveMessage
->FindString("name", &name
) != B_OK
)
1050 entry_ref documentRef
;
1051 BPath
documentPath(&dir
);
1052 documentPath
.Append(name
);
1053 get_ref_for_path(documentPath
.Path(), &documentRef
);
1055 return *ref
== documentRef
;
1059 // #pragma mark - private methods
1062 #undef B_TRANSLATION_CONTEXT
1063 #define B_TRANSLATION_CONTEXT "Menus"
1067 StyledEditWindow::_InitWindow(uint32 encoding
)
1069 fPrintSettings
= NULL
;
1070 fSaveMessage
= NULL
;
1079 fUndoCleans
= false;
1080 fRedoCleans
= false;
1084 fReplaceString
= "";
1086 fCaseSensitive
= false;
1087 fWrapAround
= false;
1088 fBackSearch
= false;
1090 fNagOnNodeChange
= true;
1093 fMenuBar
= new BMenuBar(BRect(0, 0, 0, 0), "menubar");
1096 // add textview and scrollview
1098 BRect viewFrame
= Bounds();
1099 viewFrame
.top
= fMenuBar
->Bounds().Height() + 1;
1100 viewFrame
.right
-= B_V_SCROLL_BAR_WIDTH
;
1102 viewFrame
.bottom
-= B_H_SCROLL_BAR_HEIGHT
;
1104 BRect textBounds
= viewFrame
;
1105 textBounds
.OffsetTo(B_ORIGIN
);
1106 textBounds
.InsetBy(TEXT_INSET
, TEXT_INSET
);
1108 fTextView
= new StyledEditView(viewFrame
, textBounds
, this);
1109 fTextView
->SetDoesUndo(true);
1110 fTextView
->SetStylable(true);
1111 fTextView
->SetEncoding(encoding
);
1113 fScrollView
= new BScrollView("scrollview", fTextView
, B_FOLLOW_ALL
, 0,
1114 true, true, B_PLAIN_BORDER
);
1115 AddChild(fScrollView
);
1116 fTextView
->MakeFocus(true);
1118 fStatusView
= new StatusView(fScrollView
);
1119 fScrollView
->AddChild(fStatusView
);
1122 BMenu
* menu
= new BMenu(B_TRANSLATE("File"));
1123 fMenuBar
->AddItem(menu
);
1125 BMenuItem
* menuItem
;
1126 menu
->AddItem(menuItem
= new BMenuItem(B_TRANSLATE("New"),
1127 new BMessage(MENU_NEW
), 'N'));
1128 menuItem
->SetTarget(be_app
);
1130 menu
->AddItem(menuItem
= new BMenuItem(BRecentFilesList::NewFileListMenu(
1131 B_TRANSLATE("Open" B_UTF8_ELLIPSIS
), NULL
, NULL
, be_app
, 9, true,
1132 NULL
, APP_SIGNATURE
), new BMessage(MENU_OPEN
)));
1133 menuItem
->SetShortcut('O', 0);
1134 menuItem
->SetTarget(be_app
);
1135 menu
->AddSeparatorItem();
1137 menu
->AddItem(fSaveItem
= new BMenuItem(B_TRANSLATE("Save"),
1138 new BMessage(MENU_SAVE
), 'S'));
1139 fSaveItem
->SetEnabled(false);
1140 menu
->AddItem(menuItem
= new BMenuItem(
1141 B_TRANSLATE("Save as" B_UTF8_ELLIPSIS
), new BMessage(MENU_SAVEAS
)));
1142 menuItem
->SetShortcut('S', B_SHIFT_KEY
);
1143 menuItem
->SetEnabled(true);
1145 menu
->AddItem(fReloadItem
1146 = new BMenuItem(B_TRANSLATE("Reload" B_UTF8_ELLIPSIS
),
1147 new BMessage(MENU_RELOAD
), 'L'));
1148 fReloadItem
->SetEnabled(false);
1150 menu
->AddItem(new BMenuItem(B_TRANSLATE("Close"),
1151 new BMessage(MENU_CLOSE
), 'W'));
1153 menu
->AddSeparatorItem();
1154 menu
->AddItem(new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS
),
1155 new BMessage(MENU_PAGESETUP
)));
1156 menu
->AddItem(new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS
),
1157 new BMessage(MENU_PRINT
), 'P'));
1159 menu
->AddSeparatorItem();
1160 menu
->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
1161 new BMessage(MENU_QUIT
), 'Q'));
1163 // Add the "Edit"-menu:
1164 menu
= new BMenu(B_TRANSLATE("Edit"));
1165 fMenuBar
->AddItem(menu
);
1167 menu
->AddItem(fUndoItem
= new BMenuItem(B_TRANSLATE("Can't undo"),
1168 new BMessage(B_UNDO
), 'Z'));
1169 fUndoItem
->SetEnabled(false);
1171 menu
->AddSeparatorItem();
1172 menu
->AddItem(fCutItem
= new BMenuItem(B_TRANSLATE("Cut"),
1173 new BMessage(B_CUT
), 'X'));
1174 fCutItem
->SetEnabled(false);
1175 fCutItem
->SetTarget(fTextView
);
1177 menu
->AddItem(fCopyItem
= new BMenuItem(B_TRANSLATE("Copy"),
1178 new BMessage(B_COPY
), 'C'));
1179 fCopyItem
->SetEnabled(false);
1180 fCopyItem
->SetTarget(fTextView
);
1182 menu
->AddItem(menuItem
= new BMenuItem(B_TRANSLATE("Paste"),
1183 new BMessage(B_PASTE
), 'V'));
1184 menuItem
->SetTarget(fTextView
);
1186 menu
->AddSeparatorItem();
1187 menu
->AddItem(menuItem
= new BMenuItem(B_TRANSLATE("Select all"),
1188 new BMessage(B_SELECT_ALL
), 'A'));
1189 menuItem
->SetTarget(fTextView
);
1191 menu
->AddSeparatorItem();
1192 menu
->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS
),
1193 new BMessage(MENU_FIND
), 'F'));
1194 menu
->AddItem(fFindAgainItem
= new BMenuItem(B_TRANSLATE("Find again"),
1195 new BMessage(MENU_FIND_AGAIN
), 'G'));
1196 fFindAgainItem
->SetEnabled(false);
1198 menu
->AddItem(new BMenuItem(B_TRANSLATE("Find selection"),
1199 new BMessage(MENU_FIND_SELECTION
), 'H'));
1200 menu
->AddItem(fReplaceItem
= new BMenuItem(B_TRANSLATE("Replace" B_UTF8_ELLIPSIS
),
1201 new BMessage(MENU_REPLACE
), 'R'));
1202 menu
->AddItem(fReplaceSameItem
= new BMenuItem(B_TRANSLATE("Replace next"),
1203 new BMessage(MENU_REPLACE_SAME
), 'T'));
1204 fReplaceSameItem
->SetEnabled(false);
1206 // Add the "Font"-menu:
1207 fFontMenu
= new BMenu(B_TRANSLATE("Font"));
1208 fMenuBar
->AddItem(fFontMenu
);
1211 fFontSizeMenu
= new BMenu(B_TRANSLATE("Size"));
1212 fFontSizeMenu
->SetRadioMode(true);
1213 fFontMenu
->AddItem(fFontSizeMenu
);
1215 const int32 fontSizes
[] = {9, 10, 11, 12, 14, 18, 24, 36, 48, 72};
1216 for (uint32 i
= 0; i
< sizeof(fontSizes
) / sizeof(fontSizes
[0]); i
++) {
1217 BMessage
* fontMessage
= new BMessage(FONT_SIZE
);
1218 fontMessage
->AddFloat("size", fontSizes
[i
]);
1221 snprintf(label
, sizeof(label
), "%" B_PRId32
, fontSizes
[i
]);
1222 fFontSizeMenu
->AddItem(menuItem
= new BMenuItem(label
, fontMessage
));
1224 if (fontSizes
[i
] == (int32
)be_plain_font
->Size())
1225 menuItem
->SetMarked(true);
1229 fFontColorMenu
= new BMenu(B_TRANSLATE("Color"), 0, 0);
1230 fFontColorMenu
->SetRadioMode(true);
1231 fFontMenu
->AddItem(fFontColorMenu
);
1233 _BuildFontColorMenu(fFontColorMenu
);
1235 fFontMenu
->AddSeparatorItem();
1237 // "Bold" & "Italic" menu items
1238 fFontMenu
->AddItem(fBoldItem
= new BMenuItem(B_TRANSLATE("Bold"),
1239 new BMessage(kMsgSetBold
)));
1240 fFontMenu
->AddItem(fItalicItem
= new BMenuItem(B_TRANSLATE("Italic"),
1241 new BMessage(kMsgSetItalic
)));
1242 fBoldItem
->SetShortcut('B', 0);
1243 fItalicItem
->SetShortcut('I', 0);
1244 fFontMenu
->AddSeparatorItem();
1248 fCurrentFontItem
= 0;
1249 fCurrentStyleItem
= 0;
1252 int32 numFamilies
= count_font_families();
1253 for (int32 i
= 0; i
< numFamilies
; i
++) {
1255 if (get_font_family(i
, &family
) == B_OK
) {
1256 subMenu
= new BMenu(family
);
1257 subMenu
->SetRadioMode(true);
1258 fFontMenu
->AddItem(new BMenuItem(subMenu
,
1259 new BMessage(FONT_FAMILY
)));
1261 int32 numStyles
= count_font_styles(family
);
1262 for (int32 j
= 0; j
< numStyles
; j
++) {
1265 if (get_font_style(family
, j
, &style
, &flags
) == B_OK
) {
1266 subMenu
->AddItem(new BMenuItem(style
,
1267 new BMessage(FONT_STYLE
)));
1273 // Add the "Document"-menu:
1274 menu
= new BMenu(B_TRANSLATE("Document"));
1275 fMenuBar
->AddItem(menu
);
1278 subMenu
= new BMenu(B_TRANSLATE("Align"));
1279 subMenu
->SetRadioMode(true);
1281 subMenu
->AddItem(fAlignLeft
= new BMenuItem(B_TRANSLATE("Left"),
1282 new BMessage(ALIGN_LEFT
)));
1283 fAlignLeft
->SetMarked(true);
1284 fAlignLeft
->SetShortcut('L', B_OPTION_KEY
);
1286 subMenu
->AddItem(fAlignCenter
= new BMenuItem(B_TRANSLATE("Center"),
1287 new BMessage(ALIGN_CENTER
)));
1288 fAlignCenter
->SetShortcut('C', B_OPTION_KEY
);
1290 subMenu
->AddItem(fAlignRight
= new BMenuItem(B_TRANSLATE("Right"),
1291 new BMessage(ALIGN_RIGHT
)));
1292 fAlignRight
->SetShortcut('R', B_OPTION_KEY
);
1294 menu
->AddItem(subMenu
);
1295 menu
->AddItem(fWrapItem
= new BMenuItem(B_TRANSLATE("Wrap lines"),
1296 new BMessage(WRAP_LINES
)));
1297 fWrapItem
->SetMarked(true);
1298 fWrapItem
->SetShortcut('W', B_OPTION_KEY
);
1300 BMessage
*message
= new BMessage(MENU_RELOAD
);
1301 message
->AddString("encoding", "auto");
1302 menu
->AddItem(fEncodingItem
= new BMenuItem(_PopulateEncodingMenu(
1303 new BMenu(B_TRANSLATE("Text encoding")), "UTF-8"),
1305 fEncodingItem
->SetEnabled(false);
1307 menu
->AddSeparatorItem();
1308 menu
->AddItem(new BMenuItem(B_TRANSLATE("Statistics" B_UTF8_ELLIPSIS
),
1309 new BMessage(SHOW_STATISTICS
)));
1312 fSavePanelEncodingMenu
= NULL
;
1318 StyledEditWindow::_BuildFontColorMenu(BMenu
* menu
)
1324 menu
->GetFont(&font
);
1326 font
.GetHeight(&fh
);
1328 const float itemHeight
= ceilf(fh
.ascent
+ fh
.descent
+ 2 * fh
.leading
);
1329 const float margin
= 8.0;
1330 const int nbColumns
= 5;
1332 BMessage
msgTemplate(FONT_COLOR
);
1333 BRect
matrixArea(0, 0, 0, 0);
1335 // we place the color palette, reserving room at the top
1336 for (uint i
= 0; i
< sizeof(palette
) / sizeof(rgb_color
); i
++) {
1337 BPoint
topLeft((i
% nbColumns
) * (itemHeight
+ margin
),
1338 (i
/ nbColumns
) * (itemHeight
+ margin
));
1339 BRect
buttonArea(topLeft
.x
, topLeft
.y
, topLeft
.x
+ itemHeight
,
1340 topLeft
.y
+ itemHeight
);
1341 buttonArea
.OffsetBy(margin
, itemHeight
+ margin
+ margin
);
1343 new ColorMenuItem("", palette
[i
], new BMessage(msgTemplate
)),
1345 buttonArea
.OffsetBy(margin
, margin
);
1346 matrixArea
= matrixArea
| buttonArea
;
1349 // separator at the bottom to add spacing in the matrix menu
1350 matrixArea
.top
= matrixArea
.bottom
;
1351 menu
->AddItem(new BSeparatorItem(), matrixArea
);
1354 matrixArea
.bottom
= itemHeight
+ 4;
1356 BMessage
* msg
= new BMessage(msgTemplate
);
1357 msg
->AddBool("default", true);
1358 fDefaultFontColorItem
= new ColorMenuItem(B_TRANSLATE("Default"),
1359 ui_color(B_DOCUMENT_TEXT_COLOR
), msg
);
1360 menu
->AddItem(fDefaultFontColorItem
, matrixArea
);
1362 matrixArea
.top
= matrixArea
.bottom
;
1363 matrixArea
.bottom
= matrixArea
.top
+ margin
;
1364 menu
->AddItem(new BSeparatorItem(), matrixArea
);
1369 StyledEditWindow::_LoadAttrs()
1373 if (fSaveMessage
->FindRef("directory", &dir
) != B_OK
1374 || fSaveMessage
->FindString("name", &name
) != B_OK
)
1377 BPath
documentPath(&dir
);
1378 documentPath
.Append(name
);
1380 BNode
documentNode(documentPath
.Path());
1381 if (documentNode
.InitCheck() != B_OK
)
1384 // info about position of caret may live in the file attributes
1386 if (documentNode
.ReadAttr("be:caret_position", B_INT32_TYPE
, 0,
1387 &position
, sizeof(position
)) != sizeof(position
))
1390 fTextView
->Select(position
, position
);
1391 fTextView
->ScrollToOffset(position
);
1394 ssize_t bytesRead
= documentNode
.ReadAttr(kInfoAttributeName
, B_RECT_TYPE
,
1395 0, &newFrame
, sizeof(BRect
));
1396 if (bytesRead
!= sizeof(BRect
))
1399 swap_data(B_RECT_TYPE
, &newFrame
, sizeof(BRect
), B_SWAP_BENDIAN_TO_HOST
);
1401 // Check if the frame in on screen, otherwise, ignore it
1402 BScreen
screen(this);
1403 if (newFrame
.Width() > 32 && newFrame
.Height() > 32
1404 && screen
.Frame().Contains(newFrame
)) {
1405 MoveTo(newFrame
.left
, newFrame
.top
);
1406 ResizeTo(newFrame
.Width(), newFrame
.Height());
1412 StyledEditWindow::_SaveAttrs()
1419 if (fSaveMessage
->FindRef("directory", &dir
) != B_OK
1420 || fSaveMessage
->FindString("name", &name
) != B_OK
)
1423 BPath
documentPath(&dir
);
1424 documentPath
.Append(name
);
1426 BNode
documentNode(documentPath
.Path());
1427 if (documentNode
.InitCheck() != B_OK
)
1430 BRect
frame(Frame());
1431 swap_data(B_RECT_TYPE
, &frame
, sizeof(BRect
), B_SWAP_HOST_TO_BENDIAN
);
1433 documentNode
.WriteAttr(kInfoAttributeName
, B_RECT_TYPE
, 0, &frame
,
1436 // preserve caret line and position
1438 fTextView
->GetSelection(&start
, &end
);
1439 documentNode
.WriteAttr("be:caret_position",
1440 B_INT32_TYPE
, 0, &start
, sizeof(start
));
1444 #undef B_TRANSLATION_CONTEXT
1445 #define B_TRANSLATION_CONTEXT "LoadAlert"
1449 StyledEditWindow::_LoadFile(entry_ref
* ref
, const char* forceEncoding
)
1451 BEntry
entry(ref
, true);
1452 // traverse an eventual link
1454 status_t status
= entry
.InitCheck();
1455 if (status
== B_OK
&& entry
.IsDirectory())
1456 status
= B_IS_A_DIRECTORY
;
1460 status
= file
.SetTo(&entry
, B_READ_ONLY
);
1462 status
= fTextView
->GetStyledText(&file
, forceEncoding
);
1464 if (status
== B_ENTRY_NOT_FOUND
) {
1465 // Treat non-existing files consideratley; we just want to get an
1466 // empty window for them - to create this new document
1470 if (status
!= B_OK
) {
1471 // If an error occured, bail out and tell the user what happened
1472 BEntry
entry(ref
, true);
1473 char name
[B_FILE_NAME_LENGTH
];
1474 if (entry
.GetName(name
) != B_OK
)
1475 strlcpy(name
, B_TRANSLATE("???"), sizeof(name
));
1478 if (status
== B_BAD_TYPE
)
1480 B_TRANSLATE("Error loading \"%s\":\n\tUnsupported format"), name
);
1482 bs_printf(&text
, B_TRANSLATE("Error loading \"%s\":\n\t%s"),
1483 name
, strerror(status
));
1485 _ShowAlert(text
, B_TRANSLATE("OK"), "", "", B_STOP_ALERT
);
1490 if (file
.InitCheck() == B_OK
&& file
.GetStat(&st
) == B_OK
) {
1491 bool editable
= (getuid() == st
.st_uid
&& S_IWUSR
& st
.st_mode
)
1492 || (getgid() == st
.st_gid
&& S_IWGRP
& st
.st_mode
)
1493 || (S_IWOTH
& st
.st_mode
);
1494 BVolume
volume(ref
->device
);
1495 editable
= editable
&& !volume
.IsReadOnly();
1496 _SetReadOnly(!editable
);
1500 switch (fTextView
->Alignment()) {
1503 fAlignLeft
->SetMarked(true);
1505 case B_ALIGN_CENTER
:
1506 fAlignCenter
->SetMarked(true);
1509 fAlignRight
->SetMarked(true);
1513 // update word wrapping
1514 fWrapItem
->SetMarked(fTextView
->DoesWordWrap());
1519 #undef B_TRANSLATION_CONTEXT
1520 #define B_TRANSLATION_CONTEXT "RevertToSavedAlert"
1524 StyledEditWindow::_ReloadDocument(BMessage
* message
)
1529 if (fSaveMessage
== NULL
|| message
== NULL
1530 || fSaveMessage
->FindRef("directory", &ref
) != B_OK
1531 || fSaveMessage
->FindString("name", &name
) != B_OK
)
1534 BDirectory
dir(&ref
);
1535 status_t status
= dir
.InitCheck();
1538 status
= entry
.SetTo(&dir
, name
);
1541 status
= entry
.GetRef(&ref
);
1543 if (status
!= B_OK
|| !entry
.Exists()) {
1545 bs_printf(&alertText
,
1546 B_TRANSLATE("Cannot revert, file not found: \"%s\"."), name
);
1547 _ShowAlert(alertText
, B_TRANSLATE("OK"), "", "", B_STOP_ALERT
);
1553 bs_printf(&alertText
,
1554 B_TRANSLATE("\"%s\" has unsaved changes.\n"
1555 "Revert it to the last saved version? "), Title());
1556 if (_ShowAlert(alertText
, B_TRANSLATE("Cancel"), B_TRANSLATE("Revert"),
1557 "", B_WARNING_ALERT
) != 1)
1561 const BCharacterSet
* charset
1562 = BCharacterSetRoster::GetCharacterSetByFontID(
1563 fTextView
->GetEncoding());
1564 const char* forceEncoding
= NULL
;
1565 if (message
->FindString("encoding", &forceEncoding
) != B_OK
) {
1566 if (charset
!= NULL
)
1567 forceEncoding
= charset
->GetName();
1569 if (charset
!= NULL
) {
1570 // UTF8 id assumed equal to -1
1571 const uint32 idUTF8
= (uint32
)-1;
1572 uint32 id
= charset
->GetConversionID();
1573 if (strcmp(forceEncoding
, "next") == 0)
1574 id
= id
== B_MS_WINDOWS_1250_CONVERSION
? idUTF8
: id
+ 1;
1575 else if (strcmp(forceEncoding
, "previous") == 0)
1576 id
= id
== idUTF8
? B_MS_WINDOWS_1250_CONVERSION
: id
- 1;
1577 const BCharacterSet
* newCharset
1578 = BCharacterSetRoster::GetCharacterSetByConversionID(id
);
1579 if (newCharset
!= NULL
)
1580 forceEncoding
= newCharset
->GetName();
1584 BScrollBar
* vertBar
= fScrollView
->ScrollBar(B_VERTICAL
);
1585 float vertPos
= vertBar
!= NULL
? vertBar
->Value() : 0.f
;
1591 status
= _LoadFile(&ref
, forceEncoding
);
1593 if (vertBar
!= NULL
)
1594 vertBar
->SetValue(vertPos
);
1601 #undef B_TRANSLATION_CONTEXT
1602 #define B_TRANSLATION_CONTEXT "Menus"
1605 fUndoItem
->SetLabel(B_TRANSLATE("Can't undo"));
1606 fUndoItem
->SetEnabled(false);
1612 // clear clean modes
1613 fSaveItem
->SetEnabled(false);
1615 fUndoCleans
= false;
1616 fRedoCleans
= false;
1619 fNagOnNodeChange
= true;
1624 StyledEditWindow::_UnlockFile()
1626 _NodeMonitorSuspender
nodeMonitorSuspender(this);
1633 if (fSaveMessage
->FindRef("directory", &dirRef
) != B_OK
1634 || fSaveMessage
->FindString("name", &name
) != B_OK
)
1637 BDirectory
dir(&dirRef
);
1638 BEntry
entry(&dir
, name
);
1640 status_t status
= dir
.InitCheck();
1644 status
= entry
.InitCheck();
1649 BFile
file(&entry
, B_READ_WRITE
);
1650 status
= file
.InitCheck();
1654 status
= file
.GetStat(&st
);
1658 st
.st_mode
|= S_IWUSR
;
1659 status
= file
.SetPermissions(st
.st_mode
);
1661 _SetReadOnly(false);
1668 StyledEditWindow::_Search(BString string
, bool caseSensitive
, bool wrap
,
1669 bool backSearch
, bool scrollToOccurence
)
1676 int32 length
= string
.Length();
1680 BString
viewText(fTextView
->Text());
1681 int32 textStart
, textFinish
;
1682 fTextView
->GetSelection(&textStart
, &textFinish
);
1685 start
= viewText
.FindLast(string
, textStart
);
1687 start
= viewText
.IFindLast(string
, textStart
);
1690 start
= viewText
.FindFirst(string
, textFinish
);
1692 start
= viewText
.IFindFirst(string
, textFinish
);
1694 if (start
== B_ERROR
&& wrap
) {
1697 start
= viewText
.FindLast(string
, viewText
.Length());
1699 start
= viewText
.IFindLast(string
, viewText
.Length());
1702 start
= viewText
.FindFirst(string
, 0);
1704 start
= viewText
.IFindFirst(string
, 0);
1708 if (start
!= B_ERROR
) {
1709 finish
= start
+ length
;
1710 fTextView
->Select(start
, finish
);
1712 if (scrollToOccurence
)
1713 fTextView
->ScrollToSelection();
1722 StyledEditWindow::_FindSelection()
1724 int32 selectionStart
, selectionFinish
;
1725 fTextView
->GetSelection(&selectionStart
, &selectionFinish
);
1727 int32 selectionLength
= selectionFinish
- selectionStart
;
1729 BString viewText
= fTextView
->Text();
1730 viewText
.CopyInto(fStringToFind
, selectionStart
, selectionLength
);
1731 fFindAgainItem
->SetEnabled(true);
1732 _Search(fStringToFind
, fCaseSensitive
, fWrapAround
, fBackSearch
);
1737 StyledEditWindow::_Replace(BString findThis
, BString replaceWith
,
1738 bool caseSensitive
, bool wrap
, bool backSearch
)
1740 if (_Search(findThis
, caseSensitive
, wrap
, backSearch
)) {
1743 fTextView
->GetSelection(&start
, &finish
);
1745 _UpdateCleanUndoRedoSaveRevert();
1746 fTextView
->SetSuppressChanges(true);
1747 fTextView
->Delete(start
, start
+ findThis
.Length());
1748 fTextView
->Insert(start
, replaceWith
.String(), replaceWith
.Length());
1749 fTextView
->SetSuppressChanges(false);
1750 fTextView
->Select(start
, start
+ replaceWith
.Length());
1751 fTextView
->ScrollToSelection();
1760 StyledEditWindow::_ReplaceAll(BString findThis
, BString replaceWith
,
1764 fTextView
->SetSuppressChanges(true);
1766 // start from the beginning of text
1767 fTextView
->Select(0, 0);
1769 // iterate occurences of findThis without wrapping around
1770 while (_Search(findThis
, caseSensitive
, false, false, false)) {
1772 _UpdateCleanUndoRedoSaveRevert();
1778 fTextView
->GetSelection(&start
, &finish
);
1779 fTextView
->Delete(start
, start
+ findThis
.Length());
1780 fTextView
->Insert(start
, replaceWith
.String(), replaceWith
.Length());
1782 // advance the caret behind the inserted text
1783 start
+= replaceWith
.Length();
1784 fTextView
->Select(start
, start
);
1786 fTextView
->ScrollToSelection();
1787 fTextView
->SetSuppressChanges(false);
1792 StyledEditWindow::_SetFontSize(float fontSize
)
1794 uint32 sameProperties
;
1797 fTextView
->GetFontAndColor(&font
, &sameProperties
);
1798 font
.SetSize(fontSize
);
1799 fTextView
->SetFontAndColor(&font
, B_FONT_SIZE
);
1801 _UpdateCleanUndoRedoSaveRevert();
1806 StyledEditWindow::_SetFontColor(const rgb_color
* color
)
1808 uint32 sameProperties
;
1811 fTextView
->GetFontAndColor(&font
, &sameProperties
, NULL
, NULL
);
1812 fTextView
->SetFontAndColor(&font
, 0, color
);
1814 _UpdateCleanUndoRedoSaveRevert();
1819 StyledEditWindow::_SetFontStyle(const char* fontFamily
, const char* fontStyle
)
1822 uint32 sameProperties
;
1824 // find out what the old font was
1825 font_family oldFamily
;
1826 font_style oldStyle
;
1827 fTextView
->GetFontAndColor(&font
, &sameProperties
);
1828 font
.GetFamilyAndStyle(&oldFamily
, &oldStyle
);
1830 // clear that family's bit on the menu, if necessary
1831 if (strcmp(oldFamily
, fontFamily
)) {
1832 BMenuItem
* oldItem
= fFontMenu
->FindItem(oldFamily
);
1833 if (oldItem
!= NULL
) {
1834 oldItem
->SetMarked(false);
1835 BMenu
* menu
= oldItem
->Submenu();
1837 oldItem
= menu
->FindItem(oldStyle
);
1838 if (oldItem
!= NULL
)
1839 oldItem
->SetMarked(false);
1844 font
.SetFamilyAndStyle(fontFamily
, fontStyle
);
1848 if (!(font
.Face() & B_REGULAR_FACE
))
1851 if (fBoldItem
->IsMarked())
1852 face
|= B_BOLD_FACE
;
1854 if (fItalicItem
->IsMarked())
1855 face
|= B_ITALIC_FACE
;
1859 fTextView
->SetFontAndColor(&font
, B_FONT_FAMILY_AND_STYLE
);
1861 BMenuItem
* superItem
;
1862 superItem
= fFontMenu
->FindItem(fontFamily
);
1863 if (superItem
!= NULL
) {
1864 superItem
->SetMarked(true);
1865 fCurrentFontItem
= superItem
;
1868 _UpdateCleanUndoRedoSaveRevert();
1872 #undef B_TRANSLATION_CONTEXT
1873 #define B_TRANSLATION_CONTEXT "Statistics"
1877 StyledEditWindow::_ShowStatistics()
1880 bool inWord
= false;
1881 size_t length
= fTextView
->TextLength();
1883 for (size_t i
= 0; i
< length
; i
++) {
1884 if (BUnicodeChar::IsWhitespace(fTextView
->Text()[i
])) {
1886 } else if (!inWord
) {
1893 result
<< B_TRANSLATE("Document statistics") << '\n' << '\n'
1894 << B_TRANSLATE("Lines:") << ' ' << fTextView
->CountLines() << '\n'
1895 << B_TRANSLATE("Characters:") << ' ' << length
<< '\n'
1896 << B_TRANSLATE("Words:") << ' ' << words
;
1898 BAlert
* alert
= new BAlert("Statistics", result
, B_TRANSLATE("OK"), NULL
,
1899 NULL
, B_WIDTH_AS_USUAL
, B_EVEN_SPACING
, B_INFO_ALERT
);
1900 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1907 StyledEditWindow::_SetReadOnly(bool readOnly
)
1909 fReplaceItem
->SetEnabled(!readOnly
);
1910 fReplaceSameItem
->SetEnabled(!readOnly
);
1911 fFontMenu
->SetEnabled(!readOnly
);
1912 fAlignLeft
->Menu()->SetEnabled(!readOnly
);
1913 fWrapItem
->SetEnabled(!readOnly
);
1914 fTextView
->MakeEditable(!readOnly
);
1918 #undef B_TRANSLATION_CONTEXT
1919 #define B_TRANSLATION_CONTEXT "Menus"
1923 StyledEditWindow::_UpdateCleanUndoRedoSaveRevert()
1926 fUndoCleans
= false;
1927 fRedoCleans
= false;
1928 fReloadItem
->SetEnabled(fSaveMessage
!= NULL
);
1929 fEncodingItem
->SetEnabled(fSaveMessage
!= NULL
);
1930 fSaveItem
->SetEnabled(true);
1931 fUndoItem
->SetLabel(B_TRANSLATE("Can't undo"));
1932 fUndoItem
->SetEnabled(false);
1939 StyledEditWindow::_ShowAlert(const BString
& text
, const BString
& label
,
1940 const BString
& label2
, const BString
& label3
, alert_type type
) const
1942 const char* button2
= NULL
;
1943 if (label2
.Length() > 0)
1944 button2
= label2
.String();
1946 const char* button3
= NULL
;
1947 button_spacing spacing
= B_EVEN_SPACING
;
1948 if (label3
.Length() > 0) {
1949 button3
= label3
.String();
1950 spacing
= B_OFFSET_SPACING
;
1953 BAlert
* alert
= new BAlert("Alert", text
.String(), label
.String(), button2
,
1954 button3
, B_WIDTH_AS_USUAL
, spacing
, type
);
1955 alert
->SetShortcut(0, B_ESCAPE
);
1962 StyledEditWindow::_PopulateEncodingMenu(BMenu
* menu
, const char* currentEncoding
)
1964 menu
->SetRadioMode(true);
1965 BString
encoding(currentEncoding
);
1966 if (encoding
.Length() == 0)
1967 encoding
.SetTo("UTF-8");
1969 BCharacterSetRoster roster
;
1970 BCharacterSet charset
;
1971 while (roster
.GetNextCharacterSet(&charset
) == B_OK
) {
1972 const char* mime
= charset
.GetMIMEName();
1973 BString
name(charset
.GetPrintName());
1976 name
<< " (" << mime
<< ")";
1978 BMessage
*message
= new BMessage(MENU_RELOAD
);
1979 if (message
!= NULL
) {
1980 message
->AddString("encoding", charset
.GetName());
1981 BMenuItem
* item
= new BMenuItem(name
, message
);
1982 if (encoding
.Compare(charset
.GetName()) == 0)
1983 item
->SetMarked(true);
1984 menu
->AddItem(item
);
1988 menu
->AddSeparatorItem();
1989 BMessage
*message
= new BMessage(MENU_RELOAD
);
1990 message
->AddString("encoding", "auto");
1991 menu
->AddItem(new BMenuItem(B_TRANSLATE("Autodetect"), message
));
1993 message
= new BMessage(MENU_RELOAD
);
1994 message
->AddString("encoding", "next");
1995 AddShortcut(B_PAGE_DOWN
, B_OPTION_KEY
, message
);
1996 message
= new BMessage(MENU_RELOAD
);
1997 message
->AddString("encoding", "previous");
1998 AddShortcut(B_PAGE_UP
, B_OPTION_KEY
, message
);
2004 #undef B_TRANSLATION_CONTEXT
2005 #define B_TRANSLATION_CONTEXT "NodeMonitorAlerts"
2009 StyledEditWindow::_ShowNodeChangeAlert(const char* name
, bool removed
)
2011 if (!fNagOnNodeChange
)
2014 BString
alertText(removed
? B_TRANSLATE("File \"%file%\" was removed by "
2015 "another application, recover it?")
2016 : B_TRANSLATE("File \"%file%\" was modified by "
2017 "another application, reload it?"));
2018 alertText
.ReplaceAll("%file%", name
);
2020 if (_ShowAlert(alertText
, removed
? B_TRANSLATE("Recover")
2021 : B_TRANSLATE("Reload"), B_TRANSLATE("Ignore"), "",
2022 B_WARNING_ALERT
) == 0)
2025 // supress the warning - user has already agreed
2027 BMessage
msg(MENU_RELOAD
);
2028 _ReloadDocument(&msg
);
2032 fNagOnNodeChange
= false;
2034 fSaveItem
->SetEnabled(!fClean
);
2039 StyledEditWindow::_HandleNodeMonitorEvent(BMessage
*message
)
2042 if (message
->FindInt32("opcode", &opcode
) != B_OK
)
2045 if (opcode
!= B_ENTRY_CREATED
2046 && message
->FindInt64("node") != fNodeRef
.node
)
2047 // bypass foreign nodes' event
2051 case B_STAT_CHANGED
:
2054 if (message
->FindInt32("fields", &fields
) == B_OK
2055 && (fields
& (B_STAT_SIZE
| B_STAT_MODIFICATION_TIME
2056 | B_STAT_MODE
)) == 0)
2059 const char* name
= NULL
;
2060 if (fSaveMessage
->FindString("name", &name
) != B_OK
)
2063 _ShowNodeChangeAlert(name
, false);
2070 int64 srcFolder
= 0;
2071 int64 dstFolder
= 0;
2072 const char* name
= NULL
;
2073 if (message
->FindInt32("device", &device
) != B_OK
2074 || message
->FindInt64("to directory", &dstFolder
) != B_OK
2075 || message
->FindInt64("from directory", &srcFolder
) != B_OK
2076 || message
->FindString("name", &name
) != B_OK
)
2079 entry_ref
newRef(device
, dstFolder
, name
);
2080 BEntry
entry(&newRef
);
2083 entry
.GetParent(&dirEntry
);
2086 dirEntry
.GetRef(&ref
);
2087 fSaveMessage
->ReplaceRef("directory", &ref
);
2088 fSaveMessage
->ReplaceString("name", name
);
2090 // store previous name - it may be useful in case
2091 // we have just moved to temporary copy of file (vim case)
2092 const char* sourceName
= NULL
;
2093 if (message
->FindString("from name", &sourceName
) == B_OK
) {
2094 fSaveMessage
->RemoveName("org.name");
2095 fSaveMessage
->AddString("org.name", sourceName
);
2096 fSaveMessage
->RemoveName("move time");
2097 fSaveMessage
->AddInt64("move time", system_time());
2102 if (srcFolder
!= dstFolder
) {
2103 _SwitchNodeMonitor(false);
2104 _SwitchNodeMonitor(true);
2106 PostMessage(UPDATE_STATUS_REF
);
2110 case B_ENTRY_REMOVED
:
2112 _SwitchNodeMonitor(false);
2116 // some editors like vim save files in following way:
2117 // 1) move t.txt -> t.txt~
2118 // 2) re-create t.txt and write data to it
2120 // go to catch this case
2122 int64 directory
= 0;
2124 if (fSaveMessage
->FindString("org.name", &orgName
) == B_OK
2125 && message
->FindInt32("device", &device
) == B_OK
2126 && message
->FindInt64("directory", &directory
) == B_OK
)
2128 // reuse the source name if it is not too old
2129 bigtime_t time
= fSaveMessage
->FindInt64("move time");
2130 if ((system_time() - time
) < 1000000) {
2131 entry_ref
ref(device
, directory
, orgName
);
2133 if (entry
.InitCheck() == B_OK
) {
2134 _SwitchNodeMonitor(true, &ref
);
2137 fSaveMessage
->ReplaceString("name", orgName
);
2138 fSaveMessage
->RemoveName("org.name");
2139 fSaveMessage
->RemoveName("move time");
2142 _ShowNodeChangeAlert(orgName
, false);
2147 const char* name
= NULL
;
2148 if (message
->FindString("name", &name
) != B_OK
2149 && fSaveMessage
->FindString("name", &name
) != B_OK
)
2152 _ShowNodeChangeAlert(name
, true);
2153 PostMessage(UPDATE_STATUS_REF
);
2164 StyledEditWindow::_SwitchNodeMonitor(bool on
, entry_ref
* ref
)
2167 watch_node(&fNodeRef
, B_STOP_WATCHING
, this);
2168 watch_node(&fFolderNodeRef
, B_STOP_WATCHING
, this);
2169 fNodeRef
= node_ref();
2170 fFolderNodeRef
= node_ref();
2174 BEntry entry
, folderEntry
;
2177 entry
.SetTo(ref
, true);
2178 entry
.GetParent(&folderEntry
);
2180 } else if (fSaveMessage
!= NULL
) {
2182 const char* name
= NULL
;
2183 if (fSaveMessage
->FindRef("directory", &ref
) != B_OK
2184 || fSaveMessage
->FindString("name", &name
) != B_OK
)
2187 BDirectory
dir(&ref
);
2188 entry
.SetTo(&dir
, name
);
2189 folderEntry
.SetTo(&ref
);
2194 if (entry
.InitCheck() != B_OK
|| folderEntry
.InitCheck() != B_OK
)
2197 entry
.GetNodeRef(&fNodeRef
);
2198 folderEntry
.GetNodeRef(&fFolderNodeRef
);
2200 watch_node(&fNodeRef
, B_WATCH_STAT
, this);
2201 watch_node(&fFolderNodeRef
, B_WATCH_DIRECTORY
, this);