592 lines
13 KiB
C++
592 lines
13 KiB
C++
/*
|
|
Open Tracker License
|
|
|
|
Terms and Conditions
|
|
|
|
Copyright (c) 1991-2001, Be Incorporated. All rights reserved.
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
this software and associated documentation files (the "Software"), to deal in
|
|
the Software without restriction, including without limitation the rights to
|
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is furnished to do
|
|
so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice applies to all licensees
|
|
and shall be included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
|
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Be Incorporated shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings in
|
|
this Software without prior written authorization from Be Incorporated.
|
|
|
|
BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or
|
|
registered trademarks of Be Incorporated in the United States and other
|
|
countries. Other brand product names are registered trademarks or trademarks
|
|
of their respective holders. All rights reserved.
|
|
*/
|
|
|
|
|
|
#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;
|
|
|
|
// Set up the menu
|
|
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);
|
|
// TODO: this should work, too, but doesn't
|
|
//ResizeToPreferred();
|
|
}
|
|
}
|
|
|
|
|
|
TSignatureWindow::~TSignatureWindow()
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
TSignatureWindow::MenusBeginning()
|
|
{
|
|
fDelete->SetEnabled(fFile);
|
|
fSave->SetEnabled(IsDirty());
|
|
fUndo->SetEnabled(false); // ***TODO***
|
|
|
|
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();
|
|
|
|
// Undo stuff
|
|
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();
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
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()
|
|
{
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
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;
|
|
}
|
|
}
|