#include "Signature.h"
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <Clipboard.h>
#include <Directory.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <ScrollView.h>
#include <StringView.h>
#include "MailApp.h"
#include "MailPopUpMenu.h"
#include "MailSupport.h"
#include "MailWindow.h"
#include "Messages.h"
#define B_TRANSLATION_CONTEXT "Mail"
const float kSigHeight = 250;
const float kSigWidth = 300;
extern const char* kUndoStrings[];
extern const char* kRedoStrings[];
TSignatureWindow::TSignatureWindow(BRect rect)
:
BWindow(rect, B_TRANSLATE("Signatures"), B_TITLED_WINDOW,
B_AUTO_UPDATE_SIZE_LIMITS),
fFile(NULL)
{
BMenuItem* item;
BMenuBar* menuBar = new BMenuBar("MenuBar");
BMenu* menu = new BMenu(B_TRANSLATE("Signature"));
menu->AddItem(fNew = new BMenuItem(B_TRANSLATE("New"),
new BMessage(M_NEW), 'N'));
fSignature = new TMenu(B_TRANSLATE("Open"), INDEX_SIGNATURE, M_SIGNATURE);
menu->AddItem(new BMenuItem(fSignature));
menu->AddSeparatorItem();
menu->AddItem(fSave = new BMenuItem(B_TRANSLATE("Save"),
new BMessage(M_SAVE), 'S'));
menu->AddItem(fDelete = new BMenuItem(B_TRANSLATE("Delete"),
new BMessage(M_DELETE), 'T'));
menuBar->AddItem(menu);
menu = new BMenu(B_TRANSLATE("Edit"));
menu->AddItem(fUndo = new BMenuItem(B_TRANSLATE("Undo"),
new BMessage(B_UNDO), 'Z'));
fUndo->SetTarget(NULL, this);
menu->AddSeparatorItem();
menu->AddItem(fCut = new BMenuItem(B_TRANSLATE("Cut"),
new BMessage(B_CUT), 'X'));
fCut->SetTarget(NULL, this);
menu->AddItem(fCopy = new BMenuItem(B_TRANSLATE("Copy"),
new BMessage(B_COPY), 'C'));
fCopy->SetTarget(NULL, this);
menu->AddItem(fPaste = new BMenuItem(B_TRANSLATE("Paste"),
new BMessage(B_PASTE), 'V'));
fPaste->SetTarget(NULL, this);
menu->AddSeparatorItem();
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Select all"),
new BMessage(M_SELECT), 'A'));
item->SetTarget(NULL, this);
menuBar->AddItem(menu);
fSigView = new TSignatureView();
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.Add(menuBar)
.Add(fSigView);
if (!rect.IsValid()) {
float fontFactor = be_plain_font->Size() / 12.0f;
ResizeTo(kSigWidth * fontFactor, kSigHeight * fontFactor);
}
}
TSignatureWindow::~TSignatureWindow()
{
}
void
TSignatureWindow::MenusBeginning()
{
fDelete->SetEnabled(fFile);
fSave->SetEnabled(IsDirty());
fUndo->SetEnabled(false);
BTextView* textView = fSigView->fName->TextView();
int32 finish = 0;
int32 start = 0;
if (textView->IsFocus())
textView->GetSelection(&start, &finish);
else
fSigView->fTextView->GetSelection(&start, &finish);
fCut->SetEnabled(start != finish);
fCopy->SetEnabled(start != finish);
fNew->SetEnabled(textView->TextLength()
| fSigView->fTextView->TextLength());
be_clipboard->Lock();
fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain",
B_MIME_TYPE));
be_clipboard->Unlock();
bool isRedo = false;
undo_state undoState = B_UNDO_UNAVAILABLE;
BTextView *focusTextView = dynamic_cast<BTextView *>(CurrentFocus());
if (focusTextView != NULL)
undoState = focusTextView->UndoState(&isRedo);
fUndo->SetLabel(isRedo ? kRedoStrings[undoState] : kUndoStrings[undoState]);
fUndo->SetEnabled(undoState != B_UNDO_UNAVAILABLE);
}
void
TSignatureWindow::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case CHANGE_FONT:
{
BFont* font;
msg->FindPointer("font", (void **)&font);
fSigView->fTextView->SetFontAndColor(font);
fSigView->fTextView->Invalidate(fSigView->fTextView->Bounds());
break;
}
case M_NEW:
if (Clear()) {
fSigView->fName->SetText("");
fSigView->fTextView->SetText("");
fSigView->fName->MakeFocus(true);
}
break;
case M_SAVE:
Save();
break;
case M_DELETE: {
BAlert* alert = new BAlert("",
B_TRANSLATE("Really delete this signature? This cannot "
"be undone."),
B_TRANSLATE("Cancel"),
B_TRANSLATE("Delete"),
NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
int32 choice = alert->Go();
if (choice == 0)
break;
if (fFile) {
delete fFile;
fFile = NULL;
fEntry.Remove();
fSigView->fName->SetText("");
fSigView->fTextView->SetText(NULL, (int32)0);
fSigView->fName->MakeFocus(true);
}
break;
}
case M_SIGNATURE:
if (Clear()) {
entry_ref ref;
msg->FindRef("ref", &ref);
fEntry.SetTo(&ref);
fFile = new BFile(&ref, O_RDWR);
if (fFile->InitCheck() == B_OK) {
char name[B_FILE_NAME_LENGTH];
fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name,
sizeof(name));
fSigView->fName->SetText(name);
off_t size;
fFile->GetSize(&size);
char* sig = (char*)malloc(size);
if (sig == NULL)
break;
size = fFile->Read(sig, size);
fSigView->fTextView->SetText(sig, size);
fSigView->fName->MakeFocus(true);
BTextView* textView = fSigView->fName->TextView();
textView->Select(0, textView->TextLength());
fSigView->fTextView->fDirty = false;
} else {
fFile = NULL;
beep();
BAlert* alert = new BAlert("",
B_TRANSLATE("Couldn't open this signature. Sorry."),
B_TRANSLATE("OK"));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
}
break;
default:
BWindow::MessageReceived(msg);
break;
}
}
bool
TSignatureWindow::QuitRequested()
{
if (Clear()) {
BMessage msg(WINDOW_CLOSED);
msg.AddInt32("kind", SIG_WINDOW);
msg.AddRect("window frame", Frame());
be_app->PostMessage(&msg);
return true;
}
return false;
}
void
TSignatureWindow::FrameResized(float width, float height)
{
fSigView->FrameResized(width, height);
}
void
TSignatureWindow::Show()
{
Lock();
BTextView* textView = (BTextView *)fSigView->fName->TextView();
fSigView->fName->MakeFocus(true);
textView->Select(0, textView->TextLength());
Unlock();
BWindow::Show();
}
bool
TSignatureWindow::Clear()
{
if (IsDirty()) {
beep();
BAlert *alert = new BAlert("",
B_TRANSLATE("Save changes to this signature?"),
B_TRANSLATE("Cancel"),
B_TRANSLATE("Don't save"),
B_TRANSLATE("Save"),
B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
alert->SetShortcut(0, B_ESCAPE);
alert->SetShortcut(1, 'd');
alert->SetShortcut(2, 's');
int32 result = alert->Go();
if (result == 0)
return false;
if (result == 2)
Save();
}
delete fFile;
fFile = NULL;
fSigView->fTextView->fDirty = false;
return true;
}
bool
TSignatureWindow::IsDirty()
{
if (fFile != NULL) {
char name[B_FILE_NAME_LENGTH];
fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name));
if (strcmp(name, fSigView->fName->Text()) != 0
|| fSigView->fTextView->fDirty) {
return true;
}
} else if (fSigView->fName->Text()[0] != '\0'
|| fSigView->fTextView->TextLength() != 0) {
return true;
}
return false;
}
void
TSignatureWindow::Save()
{
char name[B_FILE_NAME_LENGTH];
int32 index = 0;
status_t result;
BDirectory dir;
BEntry entry;
BNodeInfo *node;
BPath path;
if (!fFile) {
find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
dir.SetTo(path.Path());
if (dir.FindEntry("Mail", &entry) == B_NO_ERROR)
dir.SetTo(&entry);
else
dir.CreateDirectory("Mail", &dir);
if (dir.InitCheck() != B_NO_ERROR)
goto err_exit;
if (dir.FindEntry("signatures", &entry) == B_NO_ERROR)
dir.SetTo(&entry);
else
dir.CreateDirectory("signatures", &dir);
if (dir.InitCheck() != B_NO_ERROR)
goto err_exit;
fFile = new BFile();
while(true) {
sprintf(name, "signature_%" B_PRId32, index++);
if ((result = dir.CreateFile(name, fFile, true)) == B_NO_ERROR)
break;
if (result != EEXIST)
goto err_exit;
}
dir.FindEntry(name, &fEntry);
node = new BNodeInfo(fFile);
node->SetType("text/plain");
delete node;
}
fSigView->fTextView->fDirty = false;
fFile->Seek(0, 0);
fFile->Write(fSigView->fTextView->Text(),
fSigView->fTextView->TextLength());
fFile->SetSize(fFile->Position());
fFile->WriteAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, fSigView->fName->Text(),
strlen(fSigView->fName->Text()) + 1);
return;
err_exit:
beep();
BAlert* alert = new BAlert("",
B_TRANSLATE("An error occurred trying to save this signature."),
B_TRANSLATE("Sorry"));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
TSignatureView::TSignatureView()
:
BGridView("SigView")
{
GridLayout()->SetInsets(B_USE_DEFAULT_SPACING);
BStringView* nameLabel = new BStringView("NameLabel",
B_TRANSLATE("Title:"));
nameLabel->SetAlignment(B_ALIGN_RIGHT);
GridLayout()->AddView(nameLabel, 0, 0);
fName = new TNameControl("", new BMessage(NAME_FIELD));
GridLayout()->AddItem(fName->CreateTextViewLayoutItem(), 1, 0);
BStringView* signatureLabel = new BStringView("SigLabel",
B_TRANSLATE("Signature:"));
signatureLabel->SetAlignment(B_ALIGN_RIGHT);
GridLayout()->AddView(signatureLabel, 0, 1);
fTextView = new TSigTextView();
font_height fontHeight;
fTextView->GetFontHeight(&fontHeight);
float lineHeight = ceilf(fontHeight.ascent) + ceilf(fontHeight.descent);
BScrollView* scroller = new BScrollView("SigScroller", fTextView, 0,
false, true);
scroller->SetExplicitPreferredSize(
BSize(fTextView->StringWidth("W") * 30, lineHeight * 6));
GridLayout()->AddView(scroller, 1, 1, 1, 2);
GridLayout()->AddItem(BSpaceLayoutItem::CreateGlue(), 0, 2);
}
void
TSignatureView::AttachedToWindow()
{
}
TNameControl::TNameControl(const char* label, BMessage* invocationMessage)
:
BTextControl("", label, "", invocationMessage)
{
strcpy(fLabel, label);
}
void
TNameControl::AttachedToWindow()
{
BTextControl::AttachedToWindow();
TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1);
}
void
TNameControl::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case M_SELECT:
TextView()->Select(0, TextView()->TextLength());
break;
default:
BTextControl::MessageReceived(msg);
}
}
TSigTextView::TSigTextView()
:
BTextView("SignatureView", B_NAVIGABLE | B_WILL_DRAW)
{
fDirty = false;
SetDoesUndo(true);
}
void
TSigTextView::DeleteText(int32 offset, int32 len)
{
fDirty = true;
BTextView::DeleteText(offset, len);
}
void
TSigTextView::InsertText(const char *text, int32 len, int32 offset,
const text_run_array *runs)
{
fDirty = true;
BTextView::InsertText(text, len, offset, runs);
}
void
TSigTextView::KeyDown(const char *key, int32 count)
{
bool up = false;
int32 height;
BRect r;
switch (key[0]) {
case B_HOME:
Select(0, 0);
ScrollToSelection();
break;
case B_END:
Select(TextLength(), TextLength());
ScrollToSelection();
break;
case B_PAGE_UP:
up = true;
case B_PAGE_DOWN:
r = Bounds();
height = (int32)((up ? r.top - r.bottom : r.bottom - r.top) - 25);
if ((up) && (!r.top))
break;
ScrollBy(0, height);
break;
default:
BTextView::KeyDown(key, count);
break;
}
}
void
TSigTextView::MessageReceived(BMessage *msg)
{
char type[B_FILE_NAME_LENGTH];
char *text;
int32 end;
int32 start;
BFile file;
BNodeInfo *node;
entry_ref ref;
off_t size;
switch (msg->what) {
case B_SIMPLE_DATA:
if (msg->HasRef("refs")) {
msg->FindRef("refs", &ref);
file.SetTo(&ref, O_RDONLY);
if (file.InitCheck() == B_NO_ERROR) {
node = new BNodeInfo(&file);
node->GetType(type);
delete node;
file.GetSize(&size);
if ((!strncasecmp(type, "text/", 5)) && (size)) {
text = (char *)malloc(size);
file.Read(text, size);
Delete();
GetSelection(&start, &end);
Insert(text, size);
Select(start, start + size);
free(text);
}
}
}
else
BTextView::MessageReceived(msg);
break;
case M_SELECT:
if (IsSelectable())
Select(0, TextLength());
break;
default:
BTextView::MessageReceived(msg);
break;
}
}