Files
haiku-beta6/src/apps/stylededit/StyledEditWindow.cpp
T

1362 lines
33 KiB
C++
Raw Normal View History

/*
* Copyright 2002-2006, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Mattias Sundblad
* Andrew Bachmann
*/
#include "Constants.h"
#include "ColorMenuItem.h"
#include "FindWindow.h"
#include "ReplaceWindow.h"
#include "StyledEditApp.h"
#include "StyledEditView.h"
#include "StyledEditWindow.h"
#include <Alert.h>
#include <Autolock.h>
#include <CharacterSet.h>
#include <CharacterSetRoster.h>
#include <Clipboard.h>
#include <Debug.h>
#include <File.h>
#include <FilePanel.h>
#include <Menu.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <PrintJob.h>
#include <Rect.h>
#include <Roster.h>
#include <ScrollView.h>
#include <TextControl.h>
#include <TextView.h>
#include <TranslationUtils.h>
#include <stdlib.h>
using namespace BPrivate;
StyledEditWindow::StyledEditWindow(BRect frame, int32 id, uint32 encoding)
: BWindow(frame, "untitled", B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS)
{
InitWindow(encoding);
BString unTitled("Untitled ");
unTitled << id;
SetTitle(unTitled.String());
fSaveItem->SetEnabled(true);
// allow saving empty files
Show();
}
StyledEditWindow::StyledEditWindow(BRect frame, entry_ref *ref, uint32 encoding)
: BWindow(frame, "untitled", B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS)
{
InitWindow(encoding);
OpenFile(ref);
Show();
}
StyledEditWindow::~StyledEditWindow()
{
delete fSaveMessage;
delete fPrintSettings;
delete fSavePanel;
}
void
StyledEditWindow::InitWindow(uint32 encoding)
{
fPrintSettings = NULL;
fSaveMessage = NULL;
// undo modes
fUndoFlag = false;
fCanUndo = false;
fRedoFlag = false;
fCanRedo = false;
// clean modes
fUndoCleans = false;
fRedoCleans = false;
fClean = true;
// search- state
fReplaceString = "";
fStringToFind = "";
fCaseSens = false;
fWrapAround = false;
fBackSearch = false;
// add menubar
fMenuBar = new BMenuBar(BRect(0, 0, 0, 0), "menubar");
AddChild(fMenuBar);
// add textview and scrollview
BRect viewFrame = Bounds();
viewFrame.top = fMenuBar->Bounds().Height() + 1;
viewFrame.right -= B_V_SCROLL_BAR_WIDTH;
viewFrame.left = 0;
viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT;
BRect textBounds = viewFrame;
textBounds.OffsetTo(B_ORIGIN);
textBounds.InsetBy(TEXT_INSET, TEXT_INSET);
fTextView= new StyledEditView(viewFrame, textBounds, this);
fTextView->SetDoesUndo(true);
fTextView->SetStylable(true);
fTextView->SetEncoding(encoding);
fScrollView = new BScrollView("scrollview", fTextView, B_FOLLOW_ALL, 0,
true, true, B_PLAIN_BORDER);
AddChild(fScrollView);
fTextView->MakeFocus(true);
// Add "File"-menu:
BMenu* menu = new BMenu("File");
fMenuBar->AddItem(menu);
BMenuItem* menuItem;
menu->AddItem(menuItem = new BMenuItem("New", new BMessage(MENU_NEW), 'N'));
menuItem->SetTarget(be_app);
menu->AddItem(menuItem = new BMenuItem(fRecentMenu = new BMenu("Open" B_UTF8_ELLIPSIS),
new BMessage(MENU_OPEN)));
menuItem->SetShortcut('O', 0);
menuItem->SetTarget(be_app);
menu->AddSeparatorItem();
menu->AddItem(fSaveItem = new BMenuItem("Save", new BMessage(MENU_SAVE), 'S'));
fSaveItem->SetEnabled(false);
menu->AddItem(menuItem = new BMenuItem("Save As" B_UTF8_ELLIPSIS, new BMessage(MENU_SAVEAS)));
menuItem->SetShortcut('S',B_SHIFT_KEY);
menuItem->SetEnabled(true);
menu->AddItem(fRevertItem = new BMenuItem("Revert to Saved" B_UTF8_ELLIPSIS,
new BMessage(MENU_REVERT)));
fRevertItem->SetEnabled(false);
menu->AddItem(menuItem = new BMenuItem("Close", new BMessage(MENU_CLOSE), 'W'));
menu->AddSeparatorItem();
menu->AddItem(menuItem = new BMenuItem("Page Setup" B_UTF8_ELLIPSIS, new BMessage(MENU_PAGESETUP)));
menu->AddItem(menuItem = new BMenuItem("Print" B_UTF8_ELLIPSIS, new BMessage(MENU_PRINT), 'P'));
menu->AddSeparatorItem();
menu->AddItem(menuItem = new BMenuItem("Quit", new BMessage(MENU_QUIT), 'Q'));
// Add the "Edit"-menu:
menu = new BMenu("Edit");
fMenuBar->AddItem(menu);
menu->AddItem(fUndoItem = new BMenuItem("Can't Undo", new BMessage(B_UNDO), 'Z'));
fUndoItem->SetEnabled(false);
menu->AddSeparatorItem();
menu->AddItem(fCutItem = new BMenuItem("Cut", new BMessage(B_CUT), 'X'));
fCutItem->SetEnabled(false);
fCutItem->SetTarget(fTextView);
menu->AddItem(fCopyItem = new BMenuItem("Copy", new BMessage(B_COPY), 'C'));
fCopyItem->SetEnabled(false);
fCopyItem->SetTarget(fTextView);
menu->AddItem(menuItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V'));
menuItem->SetTarget(fTextView);
menu->AddItem(fClearItem = new BMenuItem("Clear", new BMessage(MENU_CLEAR)));
fClearItem->SetEnabled(false);
fClearItem->SetTarget(fTextView);
menu->AddSeparatorItem();
menu->AddItem(menuItem = new BMenuItem("Select All", new BMessage(B_SELECT_ALL), 'A'));
menuItem->SetTarget(fTextView);
menu->AddSeparatorItem();
menu->AddItem(menuItem = new BMenuItem("Find" B_UTF8_ELLIPSIS, new BMessage(MENU_FIND),'F'));
menu->AddItem(fFindAgainItem= new BMenuItem("Find Again",new BMessage(MENU_FIND_AGAIN),'G'));
fFindAgainItem->SetEnabled(false);
menu->AddItem(menuItem = new BMenuItem("Find Selection", new BMessage(MENU_FIND_SELECTION),'H'));
menu->AddItem(menuItem = new BMenuItem("Replace" B_UTF8_ELLIPSIS, new BMessage(MENU_REPLACE),'R'));
menu->AddItem(fReplaceSameItem = new BMenuItem("Replace Same", new BMessage(MENU_REPLACE_SAME),'T'));
fReplaceSameItem->SetEnabled(false);
// Add the "Font"-menu:
fFontMenu = new BMenu("Font");
fMenuBar->AddItem(fFontMenu);
//"Size"-subMenu
fFontSizeMenu = new BMenu("Size");
fFontSizeMenu->SetRadioMode(true);
fFontMenu->AddItem(fFontSizeMenu);
const int32 fontSizes[] = {9, 10, 11, 12, 14, 18, 24, 36, 48, 72};
for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) {
BMessage* fontMessage = new BMessage(FONT_SIZE);
fontMessage->AddFloat("size", fontSizes[i]);
char label[64];
snprintf(label, sizeof(label), "%ld", fontSizes[i]);
fFontSizeMenu->AddItem(menuItem = new BMenuItem(label, fontMessage));
if (fontSizes[i] == (int32)be_plain_font->Size())
menuItem->SetMarked(true);
}
// "Color"-subMenu
fFontColorMenu = new BMenu("Color");
fFontColorMenu->SetRadioMode(true);
fFontMenu->AddItem(fFontColorMenu);
fFontColorMenu->AddItem(fBlackItem = new BMenuItem("Black", new BMessage(FONT_COLOR)));
fBlackItem->SetMarked(true);
fFontColorMenu->AddItem(fRedItem = new ColorMenuItem("Red", RED, new BMessage(FONT_COLOR)));
fFontColorMenu->AddItem(fGreenItem = new ColorMenuItem("Green", GREEN, new BMessage(FONT_COLOR)));
fFontColorMenu->AddItem(fBlueItem = new ColorMenuItem("Blue", BLUE, new BMessage(FONT_COLOR)));
fFontColorMenu->AddItem(fCyanItem = new ColorMenuItem("Cyan", CYAN, new BMessage(FONT_COLOR)));
fFontColorMenu->AddItem(fMagentaItem = new ColorMenuItem("Magenta", MAGENTA, new BMessage(FONT_COLOR)));
fFontColorMenu->AddItem(fYellowItem = new ColorMenuItem("Yellow", YELLOW, new BMessage(FONT_COLOR)));
fFontMenu->AddSeparatorItem();
// Available fonts, mark "be_plain_font" item
font_family plainFamily;
font_style plainStyle;
be_plain_font->GetFamilyAndStyle(&plainFamily, &plainStyle);
fCurrentFontItem = 0;
BMenu* subMenu;
int32 numFamilies = count_font_families();
for (int32 i = 0; i < numFamilies; i++) {
font_family family;
if (get_font_family(i, &family) == B_OK) {
subMenu = new BMenu(family);
subMenu->SetRadioMode(true);
fFontMenu->AddItem(menuItem = new BMenuItem(subMenu, new BMessage(FONT_FAMILY)));
if (!strcmp(plainFamily, family)) {
menuItem->SetMarked(true);
fCurrentFontItem = menuItem;
}
int32 numStyles = count_font_styles(family);
for (int32 j = 0; j < numStyles; j++) {
font_style style;
uint32 flags;
if (get_font_style(family, j, &style, &flags) == B_OK) {
subMenu->AddItem(menuItem = new BMenuItem(style,
new BMessage(FONT_STYLE)));
if (!strcmp(plainStyle, style))
menuItem->SetMarked(true);
}
}
}
}
// Add the "Document"-menu:
menu = new BMenu("Document");
fMenuBar->AddItem(menu);
// "Align"-subMenu:
subMenu = new BMenu("Align");
subMenu->SetRadioMode(true);
subMenu->AddItem(fAlignLeft = new BMenuItem("Left", new BMessage(ALIGN_LEFT)));
menuItem->SetMarked(true);
subMenu->AddItem(fAlignCenter = new BMenuItem("Center", new BMessage(ALIGN_CENTER)));
subMenu->AddItem(fAlignRight = new BMenuItem("Right", new BMessage(ALIGN_RIGHT)));
menu->AddItem(subMenu);
menu->AddItem(fWrapItem = new BMenuItem("Wrap Lines", new BMessage(WRAP_LINES)));
fWrapItem->SetMarked(true);
fSavePanel = NULL;
fSavePanelEncodingMenu = NULL;
// build lazily
}
void
StyledEditWindow::MessageReceived(BMessage *message)
{
if (message->WasDropped()) {
entry_ref ref;
if (message->FindRef("refs", 0, &ref)==B_OK) {
message->what = B_REFS_RECEIVED;
be_app->PostMessage(message);
}
}
switch (message->what) {
// File menu
case MENU_SAVE:
if (!fSaveMessage)
SaveAs();
else
Save(fSaveMessage);
break;
case MENU_SAVEAS:
SaveAs();
break;
case B_SAVE_REQUESTED:
Save(message);
break;
2003-08-08 05:36:15 +00:00
case SAVE_THEN_QUIT:
if (Save(message) == B_OK)
2003-08-08 05:36:15 +00:00
Quit();
break;
case MENU_REVERT:
RevertToSaved();
break;
case MENU_CLOSE:
if (QuitRequested())
Quit();
break;
case MENU_PAGESETUP:
PageSetup(fTextView->Window()->Title());
break;
case MENU_PRINT:
Print(fTextView->Window()->Title());
break;
case MENU_QUIT:
be_app->PostMessage(B_QUIT_REQUESTED);
break;
// Edit menu
case B_UNDO:
ASSERT(fCanUndo || fCanRedo);
ASSERT(!(fCanUndo && fCanRedo));
if (fCanUndo)
fUndoFlag = true;
if (fCanRedo)
fRedoFlag = true;
fTextView->Undo(be_clipboard);
break;
case B_CUT:
fTextView->Cut(be_clipboard);
break;
case B_COPY:
fTextView->Copy(be_clipboard);
break;
case B_PASTE:
fTextView->Paste(be_clipboard);
break;
case MENU_CLEAR:
fTextView->Clear();
break;
case MENU_FIND:
{
BRect findWindowFrame(100, 100, 400, 235);
BWindow* window = new FindWindow(findWindowFrame, this,
&fStringToFind, fCaseSens, fWrapAround, fBackSearch);
window->Show();
break;
}
case MSG_SEARCH:
message->FindString("findtext", &fStringToFind);
fFindAgainItem->SetEnabled(true);
message->FindBool("casesens", &fCaseSens);
message->FindBool("wrap", &fWrapAround);
message->FindBool("backsearch", &fBackSearch);
Search(fStringToFind, fCaseSens, fWrapAround, fBackSearch);
break;
case MENU_FIND_AGAIN:
Search(fStringToFind, fCaseSens, fWrapAround, fBackSearch);
break;
case MENU_FIND_SELECTION:
FindSelection();
break;
case MENU_REPLACE:
{
BRect replaceWindowFrame(100, 100, 400, 284);
BWindow* window = new ReplaceWindow(replaceWindowFrame, this,
&fStringToFind, &fReplaceString, fCaseSens, fWrapAround, fBackSearch);
window->Show();
break;
}
case MSG_REPLACE:
{
message->FindBool("casesens", &fCaseSens);
message->FindBool("wrap", &fWrapAround);
message->FindBool("backsearch", &fBackSearch);
message->FindString("FindText", &fStringToFind);
message->FindString("ReplaceText", &fReplaceString);
fFindAgainItem->SetEnabled(true);
fReplaceSameItem->SetEnabled(true);
Replace(fStringToFind, fReplaceString, fCaseSens, fWrapAround, fBackSearch);
break;
}
case MENU_REPLACE_SAME:
Replace(fStringToFind, fReplaceString, fCaseSens, fWrapAround, fBackSearch);
break;
case MSG_REPLACE_ALL:
{
message->FindBool("casesens", &fCaseSens);
message->FindString("FindText",&fStringToFind);
message->FindString("ReplaceText",&fReplaceString);
bool allWindows;
message->FindBool("allwindows", &allWindows);
fFindAgainItem->SetEnabled(true);
fReplaceSameItem->SetEnabled(true);
if (allWindows)
SearchAllWindows(fStringToFind, fReplaceString, fCaseSens);
else
ReplaceAll(fStringToFind, fReplaceString, fCaseSens);
break;
}
// Font menu
case FONT_SIZE:
{
float fontSize;
if (message->FindFloat("size", &fontSize) == B_OK)
SetFontSize(fontSize);
break;
}
case FONT_FAMILY:
{
const char* fontFamily = NULL;
const char* fontStyle = NULL;
void* ptr;
if (message->FindPointer("source", &ptr) == B_OK) {
fCurrentFontItem = static_cast<BMenuItem*>(ptr);
fontFamily = fCurrentFontItem->Label();
}
SetFontStyle(fontFamily, fontStyle);
break;
}
case FONT_STYLE:
{
const char* fontFamily = NULL;
const char* fontStyle = NULL;
void* ptr;
if (message->FindPointer("source", &ptr) == B_OK) {
BMenuItem* item = static_cast<BMenuItem*>(ptr);
fontStyle = item->Label();
BMenu* menu = item->Menu();
if (menu != NULL) {
fCurrentFontItem = menu->Superitem();
if (fCurrentFontItem != NULL)
fontFamily = fCurrentFontItem->Label();
}
}
SetFontStyle(fontFamily, fontStyle);
break;
}
case FONT_COLOR:
{
void* ptr;
if (message->FindPointer("source", &ptr) == B_OK) {
if (ptr == fBlackItem)
SetFontColor(&BLACK);
else if (ptr == fRedItem)
SetFontColor(&RED);
else if (ptr == fGreenItem)
SetFontColor(&GREEN);
else if (ptr == fBlueItem)
SetFontColor(&BLUE);
else if (ptr == fCyanItem)
SetFontColor(&CYAN);
else if (ptr == fMagentaItem)
SetFontColor(&MAGENTA);
else if (ptr == fYellowItem)
SetFontColor(&YELLOW);
}
break;
}
// Document menu
case ALIGN_LEFT:
fTextView->SetAlignment(B_ALIGN_LEFT);
_UpdateCleanUndoRedoSaveRevert();
break;
case ALIGN_CENTER:
fTextView->SetAlignment(B_ALIGN_CENTER);
_UpdateCleanUndoRedoSaveRevert();
break;
case ALIGN_RIGHT:
fTextView->SetAlignment(B_ALIGN_RIGHT);
_UpdateCleanUndoRedoSaveRevert();
break;
case WRAP_LINES:
{
BRect textRect(fTextView->Bounds());
textRect.OffsetTo(B_ORIGIN);
textRect.InsetBy(TEXT_INSET,TEXT_INSET);
if (fTextView->DoesWordWrap()) {
fTextView->SetWordWrap(false);
fWrapItem->SetMarked(false);
// the width comes from stylededit R5. TODO: find a better way
textRect.SetRightBottom(BPoint(1500.0, textRect.RightBottom().y));
} else {
fTextView->SetWordWrap(true);
fWrapItem->SetMarked(true);
}
fTextView->SetTextRect(textRect);
_UpdateCleanUndoRedoSaveRevert();
break;
}
case ENABLE_ITEMS:
fCutItem->SetEnabled(true);
fCopyItem->SetEnabled(true);
fClearItem->SetEnabled(true);
break;
case DISABLE_ITEMS:
fCutItem->SetEnabled(false);
fCopyItem->SetEnabled(false);
fClearItem->SetEnabled(false);
break;
case TEXT_CHANGED:
if (fUndoFlag) {
if (fUndoCleans) {
// we cleaned!
fClean = true;
fUndoCleans = false;
} else if (fClean) {
// if we were clean
// then a redo will make us clean again
fRedoCleans = true;
fClean = false;
}
// set mode
fCanUndo = false;
fCanRedo = true;
fUndoItem->SetLabel("Redo Typing");
fUndoItem->SetEnabled(true);
fUndoFlag = false;
} else {
if (fRedoFlag && fRedoCleans) {
// we cleaned!
fClean = true;
fRedoCleans = false;
} else if (fClean) {
// if we were clean
// then an undo will make us clean again
fUndoCleans = true;
fClean = false;
} else {
// no more cleaning from undo now...
fUndoCleans = false;
}
// set mode
fCanUndo = true;
fCanRedo = false;
fUndoItem->SetLabel("Undo Typing");
fUndoItem->SetEnabled(true);
fRedoFlag = false;
}
if (fClean) {
fRevertItem->SetEnabled(false);
fSaveItem->SetEnabled(fSaveMessage == NULL);
} else {
fRevertItem->SetEnabled(fSaveMessage != NULL);
fSaveItem->SetEnabled(true);
}
break;
case SAVE_AS_ENCODING:
void* ptr;
if (message->FindPointer("source", &ptr) == B_OK
&& fSavePanelEncodingMenu != NULL) {
fTextView->SetEncoding((uint32)fSavePanelEncodingMenu->IndexOf((BMenuItem*)ptr));
}
break;
default:
BWindow::MessageReceived(message);
break;
}
}
void
StyledEditWindow::MenusBeginning()
{
// set up the recent documents menu
BMessage documents;
be_roster->GetRecentDocuments(&documents, 9, NULL, APP_SIGNATURE);
// delete old items..
// shatty: it would be preferable to keep the old
// menu around instead of continuously thrashing
// the menu, but unfortunately there does not
// seem to be a straightforward way to update it
// going backwards may simplify memory management
for (int i = fRecentMenu->CountItems(); i-- > 0;) {
delete fRecentMenu->RemoveItem(i);
}
// add new items
int count = 0;
entry_ref ref;
while (documents.FindRef("refs", count++, &ref) == B_OK) {
if (ref.device != -1 && ref.directory != -1) {
2003-08-09 05:19:34 +00:00
// sanity check passed
BMessage* openRecent = new BMessage(B_REFS_RECEIVED);
openRecent->AddRef("refs", &ref);
BMenuItem* item = new BMenuItem(ref.name, openRecent);
2003-08-09 05:19:34 +00:00
item->SetTarget(be_app);
fRecentMenu->AddItem(item);
}
}
// update the font menu
// unselect the old values
if (fCurrentFontItem != NULL)
fCurrentFontItem->SetMarked(false);
BMenuItem* oldColorItem = fFontColorMenu->FindMarked();
if (oldColorItem != NULL)
oldColorItem->SetMarked(false);
BMenuItem* oldSizeItem = fFontSizeMenu->FindMarked();
if (oldSizeItem != NULL)
oldSizeItem->SetMarked(false);
// find the current font, color, size
BFont font;
uint32 sameProperties;
rgb_color color = BLACK;
bool sameColor;
fTextView->GetFontAndColor(&font, &sameProperties, &color, &sameColor);
if (sameColor && color.alpha == 255) {
// select the current color
if (color.red == 0) {
if (color.green == 0) {
if (color.blue == 0) {
fBlackItem->SetMarked(true);
} else if (color.blue == 255) {
fBlueItem->SetMarked(true);
}
} else if (color.green == 255) {
if (color.blue == 0) {
fGreenItem->SetMarked(true);
} else if (color.blue == 255) {
fCyanItem->SetMarked(true);
}
}
} else if (color.red == 255) {
if (color.green == 0) {
if (color.blue == 0) {
fRedItem->SetMarked(true);
} else if (color.blue == 255) {
fMagentaItem->SetMarked(true);
}
} else if (color.green == 255) {
if (color.blue == 0) {
fYellowItem->SetMarked(true);
}
}
}
}
if (sameProperties & B_FONT_SIZE) {
if ((int)font.Size() == font.Size()) {
// select the current font size
char fontSizeStr[16];
snprintf(fontSizeStr, 15, "%i", (int)font.Size());
BMenuItem* item = fFontSizeMenu->FindItem(fontSizeStr);
if (item != NULL)
item->SetMarked(true);
}
}
if (sameProperties & B_FONT_FAMILY_AND_STYLE) {
font_family family;
font_style style;
font.GetFamilyAndStyle(&family, &style);
fCurrentFontItem = fFontMenu->FindItem(family);
if (fCurrentFontItem != NULL) {
fCurrentFontItem->SetMarked(true);
BMenu* menu = fCurrentFontItem->Submenu();
if (menu != NULL) {
BMenuItem* item = menu->FindItem(style);
if (item != NULL)
item->SetMarked(true);
}
}
}
switch (fTextView->Alignment()) {
case B_ALIGN_LEFT:
default:
fAlignLeft->SetMarked(true);
break;
case B_ALIGN_CENTER:
fAlignCenter->SetMarked(true);
break;
case B_ALIGN_RIGHT:
fAlignRight->SetMarked(true);
break;
}
}
void
StyledEditWindow::Quit()
{
styled_edit_app->CloseDocument();
BWindow::Quit();
}
bool
StyledEditWindow::QuitRequested()
{
int32 buttonIndex = 0;
if (fClean)
return true;
BAlert *saveAlert;
BString alertText;
2003-08-08 05:36:15 +00:00
alertText.SetTo("Save changes to the document \"");
alertText<< Title();
2003-08-08 05:36:15 +00:00
alertText<<"\"? ";
saveAlert = new BAlert("savealert",alertText.String(), "Cancel", "Don't Save","Save",
B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
saveAlert->SetShortcut(0, B_ESCAPE);
saveAlert->SetShortcut(1, 'd');
saveAlert->SetShortcut(2, 's');
buttonIndex = saveAlert->Go();
2003-08-08 05:05:10 +00:00
if (buttonIndex == 0) {
// "cancel": dont save, dont close the window
return false;
} else if (buttonIndex == 1) {
// "don't save": just close the window
return true;
} else if (!fSaveMessage) {
// save as
BMessage* message = new BMessage(SAVE_THEN_QUIT);
2003-08-08 05:36:15 +00:00
SaveAs(message);
return false;
}
return Save() == B_OK;
}
status_t
StyledEditWindow::Save(BMessage *message)
{
2003-08-08 05:05:10 +00:00
status_t err = B_OK;
if (!message){
message = fSaveMessage;
if (!message)
return B_ERROR;
}
entry_ref dirRef;
err = message->FindRef("directory", &dirRef);
if (err!= B_OK)
return err;
const char* name;
err = message->FindString("name", &name);
if (err!= B_OK)
return err;
BDirectory dir(&dirRef);
err = dir.InitCheck();
if (err != B_OK)
return err;
BEntry entry(&dir, name);
err = entry.InitCheck();
if (err != B_OK)
return err;
BFile file(&entry, B_READ_WRITE | B_CREATE_FILE);
err = file.InitCheck();
if (err != B_OK)
return err;
err = fTextView->WriteStyledEditFile(&file);
if (err != B_OK) {
BAlert *saveFailedAlert;
BString alertText;
if (err == B_TRANSLATION_ERROR_BASE)
alertText.SetTo("Translation error saving \"");
else
alertText.SetTo("Unknown error saving \"");
alertText << name;
alertText << "\".";
saveFailedAlert = new BAlert("saveFailedAlert", alertText.String(), "Bummer",
0, 0, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_STOP_ALERT);
saveFailedAlert->SetShortcut(0, B_ESCAPE);
saveFailedAlert->Go();
return err;
}
SetTitle(name);
if (fSaveMessage != message) {
delete fSaveMessage;
fSaveMessage = new BMessage(*message);
}
entry_ref ref;
if (entry.GetRef(&ref) == B_OK)
be_roster->AddToRecentDocuments(&ref, APP_SIGNATURE);
// clear clean modes
2003-08-08 05:05:10 +00:00
fSaveItem->SetEnabled(false);
fRevertItem->SetEnabled(false);
fUndoCleans = false;
fRedoCleans = false;
fClean = true;
2003-08-08 05:05:10 +00:00
return err;
}
status_t
2003-08-08 05:36:15 +00:00
StyledEditWindow::SaveAs(BMessage *message)
{
if (fSavePanel == NULL) {
entry_ref* directory = NULL;
if (fSaveMessage != NULL) {
entry_ref dirRef;
if (fSaveMessage->FindRef("directory", &dirRef))
directory = new entry_ref(dirRef);
}
fSavePanel = new BFilePanel(B_SAVE_PANEL, new BMessenger(this),
directory, B_FILE_NODE, false);
BMenuBar* menuBar = dynamic_cast<BMenuBar*>(
fSavePanel->Window()->FindView("MenuBar"));
fSavePanelEncodingMenu= new BMenu("Encoding");
menuBar->AddItem(fSavePanelEncodingMenu);
2003-07-05 00:51:07 +00:00
fSavePanelEncodingMenu->SetRadioMode(true);
BCharacterSetRoster roster;
BCharacterSet charset;
while (roster.GetNextCharacterSet(&charset) == B_NO_ERROR) {
BString name(charset.GetPrintName());
const char* mime = charset.GetMIMEName();
if (mime) {
name.Append(" (");
name.Append(mime);
name.Append(")");
}
BMenuItem * item = new BMenuItem(name.String(), new BMessage(SAVE_AS_ENCODING));
item->SetTarget(this);
fSavePanelEncodingMenu->AddItem(item);
if (charset.GetFontID() == fTextView->GetEncoding())
item->SetMarked(true);
2003-07-05 00:51:07 +00:00
}
}
fSavePanel->SetSaveText(Title());
if (message != NULL)
2003-08-08 05:36:15 +00:00
fSavePanel->SetMessage(message);
fSavePanel->Show();
2003-08-08 05:05:10 +00:00
return B_OK;
}
status_t
StyledEditWindow::_LoadFile(entry_ref* ref)
{
BEntry entry(ref, true);
// traverse an eventual link
status_t status = entry.InitCheck();
if (status == B_OK && entry.IsDirectory())
status = B_IS_A_DIRECTORY;
BFile file;
if (status == B_OK)
status = file.SetTo(&entry, B_READ_ONLY);
if (status == B_OK)
status = fTextView->GetStyledText(&file);
if (status == B_ENTRY_NOT_FOUND) {
// Treat non-existing files consideratley; we just want to get an
// empty window for them - to create this new document
status = B_OK;
}
if (status != B_OK) {
// If an error occured, bail out and tell the user what happened
BEntry entry(ref, true);
char name[B_FILE_NAME_LENGTH];
if (entry.GetName(name) != B_OK)
strcpy(name, "???");
char text[B_PATH_NAME_LENGTH + 100];
snprintf(text, sizeof(text), "Error loading \"%s\":\n\t%s", name,
strerror(status));
BAlert* alert = new BAlert("StyledEdit Load Failed", text,
"Bummer", 0, 0, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_STOP_ALERT);
alert->Go();
return status;
}
// update alignment
switch (fTextView->Alignment()) {
case B_ALIGN_LEFT:
default:
fAlignLeft->SetMarked(true);
break;
case B_ALIGN_CENTER:
fAlignCenter->SetMarked(true);
break;
case B_ALIGN_RIGHT:
fAlignRight->SetMarked(true);
break;
2002-11-20 23:29:00 +00:00
}
// update word wrapping
fWrapItem->SetMarked(fTextView->DoesWordWrap());
return B_OK;
}
void
StyledEditWindow::OpenFile(entry_ref* ref)
{
if (_LoadFile(ref) != B_OK) {
fSaveItem->SetEnabled(true);
// allow saving new files
}
be_roster->AddToRecentDocuments(ref, APP_SIGNATURE);
2002-11-20 23:29:00 +00:00
fSaveMessage = new BMessage(B_SAVE_REQUESTED);
if (fSaveMessage) {
2002-11-20 23:29:00 +00:00
BEntry entry(ref, true);
BEntry parent;
entry_ref parentRef;
char name[B_FILE_NAME_LENGTH];
2002-11-20 23:29:00 +00:00
entry.GetParent(&parent);
entry.GetName(name);
parent.GetRef(&parentRef);
fSaveMessage->AddRef("directory", &parentRef);
2002-11-20 23:29:00 +00:00
fSaveMessage->AddString("name", name);
SetTitle(name);
}
fTextView->Select(0, 0);
}
void
StyledEditWindow::RevertToSaved()
2002-11-06 18:54:47 +00:00
{
entry_ref ref;
const char *name;
fSaveMessage->FindRef("directory", &ref);
fSaveMessage->FindString("name", &name);
BDirectory dir(&ref);
status_t status = dir.InitCheck();
BEntry entry;
if (status == B_OK)
status = entry.SetTo(&dir, name);
if (status == B_OK)
status = entry.GetRef(&ref);
if (status != B_OK || !entry.Exists()) {
BAlert *vanishedAlert;
BString alertText;
alertText.SetTo("Cannot revert, file not found: \"");
alertText << name;
alertText << "\".";
vanishedAlert = new BAlert("vanishedAlert", alertText.String(), "Bummer", 0, 0,
B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_STOP_ALERT);
vanishedAlert->SetShortcut(0, B_ESCAPE);
vanishedAlert->Go();
return;
}
int32 buttonIndex = 0;
BAlert* revertAlert;
2002-11-06 18:54:47 +00:00
BString alertText;
alertText.SetTo("Revert to the last version of \"");
alertText << Title();
alertText << "\"? ";
revertAlert= new BAlert("revertAlert", alertText.String(), "Cancel", "OK", 0,
2002-11-06 18:54:47 +00:00
B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
revertAlert->SetShortcut(0, B_ESCAPE);
revertAlert->SetShortcut(1, 'o');
buttonIndex = revertAlert->Go();
if (buttonIndex != 1) {
// some sort of cancel, don't revert
return;
}
fTextView->Reset();
if (_LoadFile(&ref) != B_OK)
return;
2002-11-06 18:54:47 +00:00
// clear undo modes
fUndoItem->SetLabel("Can't Undo");
fUndoItem->SetEnabled(false);
fUndoFlag = false;
fCanUndo = false;
fRedoFlag = false;
fCanRedo = false;
2002-11-06 18:54:47 +00:00
// clear clean modes
fSaveItem->SetEnabled(false);
fRevertItem->SetEnabled(false);
fUndoCleans = false;
fRedoCleans = false;
fClean = true;
}
status_t
StyledEditWindow::PageSetup(const char* documentName)
{
BPrintJob printJob(documentName);
if (fPrintSettings != NULL)
printJob.SetSettings(new BMessage(*fPrintSettings));
status_t result = printJob.ConfigPage();
if (result == B_OK) {
delete fPrintSettings;
fPrintSettings = printJob.Settings();
}
return result;
}
void
StyledEditWindow::Print(const char* documentName)
{
status_t result;
2003-07-27 06:24:05 +00:00
if (fPrintSettings == NULL) {
result = PageSetup(documentName);
if (result != B_OK)
return;
}
BPrintJob printJob(documentName);
printJob.SetSettings(new BMessage(*fPrintSettings));
2003-07-27 06:24:05 +00:00
result = printJob.ConfigJob();
if (result != B_OK)
2003-07-27 06:24:05 +00:00
return;
2003-07-27 06:24:05 +00:00
// information from printJob
BRect printableRect = printJob.PrintableRect();
2003-07-27 06:24:05 +00:00
int32 firstPage = printJob.FirstPage();
int32 lastPage = printJob.LastPage();
2003-07-27 06:24:05 +00:00
// lines eventually to be used to compute pages to print
int32 firstLine = 0;
int32 lastLine = fTextView->CountLines();
2003-07-27 06:24:05 +00:00
// values to be computed
int32 pagesInDocument = 1;
int32 linesInDocument = fTextView->CountLines();
2003-07-27 06:24:05 +00:00
int32 currentLine = 0;
while (currentLine < linesInDocument) {
float currentHeight = 0;
while (currentHeight < printableRect.Height() && currentLine < linesInDocument) {
2003-07-27 06:24:05 +00:00
currentHeight += fTextView->LineHeight(currentLine);
if (currentHeight < printableRect.Height())
2003-07-27 06:24:05 +00:00
currentLine++;
}
if (pagesInDocument == lastPage)
2003-07-27 06:24:05 +00:00
lastLine = currentLine;
if (currentHeight >= printableRect.Height()) {
2003-07-27 07:25:47 +00:00
pagesInDocument++;
if (pagesInDocument == firstPage)
2003-07-27 07:25:47 +00:00
firstLine = currentLine;
2003-07-27 06:24:05 +00:00
}
}
2003-07-27 06:24:05 +00:00
if (lastPage > pagesInDocument - 1) {
lastPage = pagesInDocument - 1;
lastLine = currentLine - 1;
}
2003-07-27 06:24:05 +00:00
printJob.BeginJob();
if (fTextView->CountLines() > 0 && fTextView->TextLength() > 0) {
int32 printLine = firstLine;
while (printLine <= lastLine) {
float currentHeight = 0;
int32 firstLineOnPage = printLine;
while (currentHeight < printableRect.Height() && printLine <= lastLine) {
currentHeight += fTextView->LineHeight(printLine);
if (currentHeight < printableRect.Height())
printLine++;
}
float top = 0;
if (firstLineOnPage != 0)
top = fTextView->TextHeight(0, firstLineOnPage - 1);
float bottom = fTextView->TextHeight(0, printLine - 1);
BRect textRect(0.0, top + TEXT_INSET, printableRect.Width(), bottom + TEXT_INSET);
printJob.DrawView(fTextView, textRect, B_ORIGIN);
printJob.SpoolPage();
}
2003-07-27 06:24:05 +00:00
}
2003-07-27 06:24:05 +00:00
printJob.CommitJob();
}
bool
StyledEditWindow::Search(BString string, bool caseSensitive, bool wrap, bool backsearch)
{
int32 start;
int32 finish;
start = B_ERROR;
int32 length = string.Length();
if (length == 0)
return false;
BString viewText(fTextView->Text());
int32 textStart, textFinish;
fTextView->GetSelection(&textStart, &textFinish);
if (backsearch) {
if (caseSensitive) {
start = viewText.FindLast(string, textStart);
} else {
start = viewText.IFindLast(string, textStart);
}
} else {
if (caseSensitive == true) {
start = viewText.FindFirst(string, textFinish);
} else {
start = viewText.IFindFirst(string, textFinish);
}
}
if (start == B_ERROR && wrap) {
if (backsearch) {
if (caseSensitive) {
start = viewText.FindLast(string, viewText.Length());
} else {
start = viewText.IFindLast(string, viewText.Length());
}
} else {
if (caseSensitive) {
start = viewText.FindFirst(string, 0);
} else {
start = viewText.IFindFirst(string, 0);
}
}
}
if (start != B_ERROR) {
finish = start + length;
fTextView->Select(start, finish);
fTextView->ScrollToSelection();
return true;
}
return false;
}
void
StyledEditWindow::FindSelection()
{
int32 selectionStart, selectionFinish;
fTextView->GetSelection(&selectionStart, &selectionFinish);
int32 selectionLength = selectionFinish- selectionStart;
BString viewText = fTextView->Text();
viewText.CopyInto(fStringToFind, selectionStart, selectionLength);
fFindAgainItem->SetEnabled(true);
Search(fStringToFind, fCaseSens, fWrapAround, fBackSearch);
}
bool
StyledEditWindow::Replace(BString findthis, BString replaceWith, bool caseSensitive,
bool wrap, bool backsearch)
{
if (Search(findthis, caseSensitive, wrap, backsearch)) {
int32 start, finish;
fTextView->GetSelection(&start, &finish);
fTextView->Delete(start, start + findthis.Length());
fTextView->Insert(start, replaceWith.String(), replaceWith.Length());
fTextView->Select(start, start + replaceWith.Length());
fTextView->ScrollToSelection();
return true;
}
return false;
}
void
StyledEditWindow::ReplaceAll(BString findIt, BString replaceWith, bool caseSensitive)
{
BString viewText(fTextView->Text());
if (caseSensitive)
viewText.ReplaceAll(findIt.String(), replaceWith.String());
2002-11-07 12:03:23 +00:00
else
viewText.IReplaceAll(findIt.String(), replaceWith.String());
if (viewText.Compare(fTextView->Text()) == 0) {
// they are the same
return;
}
int32 textStart, textFinish;
fTextView->GetSelection(&textStart, &textFinish);
2002-11-07 12:03:23 +00:00
fTextView->SetText(viewText.String());
if (viewText.Length() < textStart)
textStart = viewText.Length();
if (viewText.Length() < textFinish)
textFinish = viewText.Length();
2002-11-07 12:03:23 +00:00
fTextView->Select(textStart,textFinish);
fTextView->ScrollToSelection();
_UpdateCleanUndoRedoSaveRevert();
}
void
StyledEditWindow::SearchAllWindows(BString find, BString replace, bool caseSensitive)
{
int32 numWindows;
numWindows = be_app->CountWindows();
BMessage *message;
message= new BMessage(MSG_REPLACE_ALL);
message->AddString("FindText", find);
message->AddString("ReplaceText", replace);
message->AddBool("casesens", caseSensitive);
while (numWindows >= 0) {
StyledEditWindow *window = dynamic_cast<StyledEditWindow *>(
be_app->WindowAt(numWindows));
BMessenger messenger(window);
messenger.SendMessage(message);
numWindows--;
}
}
void
StyledEditWindow::SetFontSize(float fontSize)
{
uint32 sameProperties;
BFont font;
fTextView->GetFontAndColor(&font, &sameProperties);
font.SetSize(fontSize);
fTextView->SetFontAndColor(&font, B_FONT_SIZE);
_UpdateCleanUndoRedoSaveRevert();
}
void
StyledEditWindow::SetFontColor(const rgb_color *color)
{
uint32 sameProperties;
BFont font;
fTextView->GetFontAndColor(&font, &sameProperties, NULL, NULL);
fTextView->SetFontAndColor(&font, 0, color);
_UpdateCleanUndoRedoSaveRevert();
}
void
StyledEditWindow::SetFontStyle(const char *fontFamily, const char *fontStyle)
{
BFont font;
uint32 sameProperties;
// find out what the old font was
font_family oldFamily;
font_style oldStyle;
fTextView->GetFontAndColor(&font, &sameProperties);
font.GetFamilyAndStyle(&oldFamily, &oldStyle);
// clear that family's bit on the menu, if necessary
if (strcmp(oldFamily, fontFamily)) {
BMenuItem* oldItem = fFontMenu->FindItem(oldFamily);
if (oldItem != NULL)
oldItem->SetMarked(false);
}
font.SetFamilyAndStyle(fontFamily, fontStyle);
fTextView->SetFontAndColor(&font);
BMenuItem* superItem;
superItem = fFontMenu->FindItem(fontFamily);
if (superItem != NULL)
superItem->SetMarked(true);
_UpdateCleanUndoRedoSaveRevert();
}
void
StyledEditWindow::_UpdateCleanUndoRedoSaveRevert()
{
fClean = false;
fUndoCleans = false;
fRedoCleans = false;
fRevertItem->SetEnabled(fSaveMessage != NULL);
fSaveItem->SetEnabled(true);
2002-11-07 12:13:09 +00:00
fUndoItem->SetLabel("Can't Undo");
fUndoItem->SetEnabled(false);
fCanUndo = false;
fCanRedo = false;
}