/* 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 #include #include #include #include #include #include #include #include #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(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; } }