1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2001, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN 23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or 30 registered trademarks of Be Incorporated in the United States and other 31 countries. Other brand product names are registered trademarks or trademarks 32 of their respective holders. All rights reserved. 33 */ 34 35 #include "Signature.h" 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #include <Clipboard.h> 42 #include <InterfaceKit.h> 43 #include <Locale.h> 44 #include <StorageKit.h> 45 46 #include "MailApp.h" 47 #include "MailPopUpMenu.h" 48 #include "MailSupport.h" 49 #include "MailWindow.h" 50 #include "Messages.h" 51 52 53 #define B_TRANSLATION_CONTEXT "Mail" 54 55 56 extern BRect signature_window; 57 extern const char *kUndoStrings[]; 58 extern const char *kRedoStrings[]; 59 60 61 TSignatureWindow::TSignatureWindow(BRect rect) 62 : 63 BWindow (rect, B_TRANSLATE("Signatures"), B_TITLED_WINDOW, 0), 64 fFile(NULL) 65 { 66 BMenu *menu; 67 BMenuBar *menu_bar; 68 BMenuItem *item; 69 70 BRect r = Bounds(); 71 /*** Set up the menus ****/ 72 menu_bar = new BMenuBar(r, "MenuBar"); 73 menu = new BMenu(B_TRANSLATE("Signature")); 74 menu->AddItem(fNew = new BMenuItem(B_TRANSLATE("New"), 75 new BMessage(M_NEW), 'N')); 76 fSignature = new TMenu(B_TRANSLATE("Open"), INDEX_SIGNATURE, M_SIGNATURE); 77 menu->AddItem(new BMenuItem(fSignature)); 78 menu->AddSeparatorItem(); 79 menu->AddItem(fSave = new BMenuItem(B_TRANSLATE("Save"), 80 new BMessage(M_SAVE), 'S')); 81 menu->AddItem(fDelete = new BMenuItem(B_TRANSLATE("Delete"), 82 new BMessage(M_DELETE), 'T')); 83 menu_bar->AddItem(menu); 84 85 menu = new BMenu(B_TRANSLATE("Edit")); 86 menu->AddItem(fUndo = new BMenuItem(B_TRANSLATE("Undo"), 87 new BMessage(B_UNDO), 'Z')); 88 fUndo->SetTarget(NULL, this); 89 menu->AddSeparatorItem(); 90 menu->AddItem(fCut = new BMenuItem(B_TRANSLATE("Cut"), 91 new BMessage(B_CUT), 'X')); 92 fCut->SetTarget(NULL, this); 93 menu->AddItem(fCopy = new BMenuItem(B_TRANSLATE("Copy"), 94 new BMessage(B_COPY), 'C')); 95 fCopy->SetTarget(NULL, this); 96 menu->AddItem(fPaste = new BMenuItem(B_TRANSLATE("Paste"), 97 new BMessage(B_PASTE), 'V')); 98 fPaste->SetTarget(NULL, this); 99 menu->AddSeparatorItem(); 100 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Select all"), 101 new BMessage(M_SELECT), 'A')); 102 item->SetTarget(NULL, this); 103 menu_bar->AddItem(menu); 104 105 AddChild(menu_bar); 106 /**** Done with the menu set up *****/ 107 108 /**** Add on the panel, giving it the width and at least one vertical pixel *****/ 109 fSigView = new TSignatureView(BRect(0, menu_bar->Frame().bottom+1, 110 rect.Width(), menu_bar->Frame().bottom+2)); 111 AddChild(fSigView); 112 113 /* resize the window to the correct height */ 114 fSigView->SetResizingMode(B_FOLLOW_NONE); 115 ResizeTo(rect.Width()-2, fSigView->Frame().bottom-2); 116 fSigView->SetResizingMode(B_FOLLOW_ALL); 117 118 SetSizeLimits(kSigWidth, RIGHT_BOUNDARY, r.top + 100, RIGHT_BOUNDARY); 119 } 120 121 122 TSignatureWindow::~TSignatureWindow() 123 { 124 } 125 126 127 void 128 TSignatureWindow::MenusBeginning() 129 { 130 int32 finish = 0; 131 int32 start = 0; 132 BTextView *text_view; 133 134 fDelete->SetEnabled(fFile); 135 fSave->SetEnabled(IsDirty()); 136 fUndo->SetEnabled(false); // ***TODO*** 137 138 text_view = (BTextView *)fSigView->fName->ChildAt(0); 139 if (text_view->IsFocus()) 140 text_view->GetSelection(&start, &finish); 141 else 142 fSigView->fTextView->GetSelection(&start, &finish); 143 144 fCut->SetEnabled(start != finish); 145 fCopy->SetEnabled(start != finish); 146 147 fNew->SetEnabled(text_view->TextLength() | fSigView->fTextView->TextLength()); 148 be_clipboard->Lock(); 149 fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE)); 150 be_clipboard->Unlock(); 151 152 // Undo stuff 153 bool isRedo = false; 154 undo_state undoState = B_UNDO_UNAVAILABLE; 155 156 BTextView *focusTextView = dynamic_cast<BTextView *>(CurrentFocus()); 157 if (focusTextView != NULL) 158 undoState = focusTextView->UndoState(&isRedo); 159 160 fUndo->SetLabel((isRedo) ? kRedoStrings[undoState] : kUndoStrings[undoState]); 161 fUndo->SetEnabled(undoState != B_UNDO_UNAVAILABLE); 162 } 163 164 165 void 166 TSignatureWindow::MessageReceived(BMessage* msg) 167 { 168 char *sig; 169 char name[B_FILE_NAME_LENGTH]; 170 BFont *font; 171 BTextView *text_view; 172 entry_ref ref; 173 off_t size; 174 175 switch(msg->what) { 176 case CHANGE_FONT: 177 msg->FindPointer("font", (void **)&font); 178 fSigView->fTextView->SetFontAndColor(font); 179 fSigView->fTextView->Invalidate(fSigView->fTextView->Bounds()); 180 break; 181 182 case M_NEW: 183 if (Clear()) { 184 fSigView->fName->SetText(""); 185 // fSigView->fTextView->SetText(NULL, (int32)0); 186 fSigView->fTextView->SetText(""); 187 fSigView->fName->MakeFocus(true); 188 } 189 break; 190 191 case M_SAVE: 192 Save(); 193 break; 194 195 case M_DELETE: 196 if (!(new BAlert("", 197 B_TRANSLATE("Really delete this signature? This cannot " 198 "be undone."), 199 B_TRANSLATE("Cancel"), 200 B_TRANSLATE("Delete"), 201 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go()) 202 break; 203 204 if (fFile) { 205 delete fFile; 206 fFile = NULL; 207 fEntry.Remove(); 208 fSigView->fName->SetText(""); 209 fSigView->fTextView->SetText(NULL, (int32)0); 210 fSigView->fName->MakeFocus(true); 211 } 212 break; 213 214 case M_SIGNATURE: 215 if (Clear()) { 216 msg->FindRef("ref", &ref); 217 fEntry.SetTo(&ref); 218 fFile = new BFile(&ref, O_RDWR); 219 if (fFile->InitCheck() == B_NO_ERROR) { 220 fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name)); 221 fSigView->fName->SetText(name); 222 fFile->GetSize(&size); 223 sig = (char *)malloc(size); 224 size = fFile->Read(sig, size); 225 fSigView->fTextView->SetText(sig, size); 226 fSigView->fName->MakeFocus(true); 227 text_view = (BTextView *)fSigView->fName->ChildAt(0); 228 text_view->Select(0, text_view->TextLength()); 229 fSigView->fTextView->fDirty = false; 230 } 231 else { 232 fFile = NULL; 233 beep(); 234 (new BAlert("", 235 B_TRANSLATE("Couldn't open this signature. Sorry."), 236 B_TRANSLATE("OK")))->Go(); 237 } 238 } 239 break; 240 241 default: 242 BWindow::MessageReceived(msg); 243 } 244 } 245 246 247 bool 248 TSignatureWindow::QuitRequested() 249 { 250 if (Clear()) { 251 BMessage msg(WINDOW_CLOSED); 252 msg.AddInt32("kind", SIG_WINDOW); 253 msg.AddRect("window frame", Frame()); 254 255 be_app->PostMessage(&msg); 256 return true; 257 } 258 return false; 259 } 260 261 262 void 263 TSignatureWindow::FrameResized(float width, float height) 264 { 265 fSigView->FrameResized(width, height); 266 } 267 268 269 void 270 TSignatureWindow::Show() 271 { 272 BTextView *text_view; 273 274 Lock(); 275 text_view = (BTextView *)fSigView->fName->TextView(); 276 fSigView->fName->MakeFocus(true); 277 text_view->Select(0, text_view->TextLength()); 278 Unlock(); 279 280 BWindow::Show(); 281 } 282 283 284 bool 285 TSignatureWindow::Clear() 286 { 287 int32 result; 288 289 if (IsDirty()) { 290 beep(); 291 BAlert *alert = new BAlert("", 292 B_TRANSLATE("Save changes to this signature?"), 293 B_TRANSLATE("Don't save"), 294 B_TRANSLATE("Cancel"), 295 B_TRANSLATE("Save"), 296 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 297 alert->SetShortcut(0, 'd'); 298 alert->SetShortcut(1, B_ESCAPE); 299 result = alert->Go(); 300 if (result == 1) 301 return false; 302 if (result == 2) 303 Save(); 304 } 305 306 delete fFile; 307 fFile = NULL; 308 fSigView->fTextView->fDirty = false; 309 return true; 310 } 311 312 313 bool 314 TSignatureWindow::IsDirty() 315 { 316 char name[B_FILE_NAME_LENGTH]; 317 318 if (fFile) { 319 fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name)); 320 if ((strcmp(name, fSigView->fName->Text())) || (fSigView->fTextView->fDirty)) 321 return true; 322 } 323 else { 324 if ((strlen(fSigView->fName->Text())) || 325 (fSigView->fTextView->TextLength())) 326 return true; 327 } 328 return false; 329 } 330 331 332 void 333 TSignatureWindow::Save() 334 { 335 char name[B_FILE_NAME_LENGTH]; 336 int32 index = 0; 337 status_t result; 338 BDirectory dir; 339 BEntry entry; 340 BNodeInfo *node; 341 BPath path; 342 343 if (!fFile) { 344 find_directory(B_USER_SETTINGS_DIRECTORY, &path, true); 345 dir.SetTo(path.Path()); 346 347 if (dir.FindEntry("Mail", &entry) == B_NO_ERROR) 348 dir.SetTo(&entry); 349 else 350 dir.CreateDirectory("Mail", &dir); 351 352 if (dir.InitCheck() != B_NO_ERROR) 353 goto err_exit; 354 355 if (dir.FindEntry("signatures", &entry) == B_NO_ERROR) 356 dir.SetTo(&entry); 357 else 358 dir.CreateDirectory("signatures", &dir); 359 360 if (dir.InitCheck() != B_NO_ERROR) 361 goto err_exit; 362 363 fFile = new BFile(); 364 while(true) { 365 sprintf(name, "signature_%ld", index++); 366 if ((result = dir.CreateFile(name, fFile, true)) == B_NO_ERROR) 367 break; 368 if (result != EEXIST) 369 goto err_exit; 370 } 371 dir.FindEntry(name, &fEntry); 372 node = new BNodeInfo(fFile); 373 node->SetType("text/plain"); 374 delete node; 375 } 376 377 fSigView->fTextView->fDirty = false; 378 fFile->Seek(0, 0); 379 fFile->Write(fSigView->fTextView->Text(), 380 fSigView->fTextView->TextLength()); 381 fFile->SetSize(fFile->Position()); 382 fFile->WriteAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, fSigView->fName->Text(), 383 strlen(fSigView->fName->Text()) + 1); 384 return; 385 386 err_exit: 387 beep(); 388 (new BAlert("", 389 B_TRANSLATE("An error occurred trying to save this signature."), 390 B_TRANSLATE("Sorry")))->Go(); 391 } 392 393 394 //==================================================================== 395 // #pragma mark - 396 397 398 TSignatureView::TSignatureView(BRect rect) 399 : BBox(rect, "SigView", B_FOLLOW_ALL, B_WILL_DRAW) 400 { 401 } 402 403 404 void 405 TSignatureView::AttachedToWindow() 406 { 407 BRect rect = Bounds(); 408 float name_text_length = StringWidth(B_TRANSLATE("Title:")); 409 float sig_text_length = StringWidth(B_TRANSLATE("Signature:")); 410 float divide_length; 411 412 if (name_text_length > sig_text_length) 413 divide_length = name_text_length; 414 else 415 divide_length = sig_text_length; 416 417 rect.InsetBy(8,0); 418 rect.top+= 8; 419 420 fName = new TNameControl(rect, B_TRANSLATE("Title:"), 421 new BMessage(NAME_FIELD)); 422 AddChild(fName); 423 424 fName->SetDivider(divide_length + 10); 425 fName->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 426 427 rect.OffsetBy(0,fName->Bounds().Height()+5); 428 rect.bottom = rect.top + kSigHeight; 429 rect.left = fName->TextView()->Frame().left; 430 431 BRect text = rect; 432 text.OffsetTo(10,0); 433 fTextView = new TSigTextView(rect, text); 434 BScrollView *scroller = new BScrollView("SigScroller", fTextView, B_FOLLOW_ALL, 0, false, true); 435 AddChild(scroller); 436 scroller->ResizeBy(-1 * scroller->ScrollBar(B_VERTICAL)->Frame().Width() - 9, 0); 437 scroller->MoveBy(7,0); 438 439 /* back up a bit to make room for the label */ 440 441 rect = scroller->Frame(); 442 BStringView *stringView = new BStringView(rect, "SigLabel", 443 B_TRANSLATE("Signature:")); 444 AddChild(stringView); 445 446 float tWidth, tHeight; 447 stringView->GetPreferredSize(&tWidth, &tHeight); 448 449 /* the 5 is for the spacer in the TextView */ 450 451 rect.OffsetBy(-1 *(tWidth) - 5, 0); 452 rect.right = rect.left + tWidth; 453 rect.bottom = rect.top + tHeight; 454 455 stringView->MoveTo(rect.LeftTop()); 456 stringView->ResizeTo(rect.Width(), rect.Height()); 457 458 /* Resize the View to the correct height */ 459 scroller->SetResizingMode(B_FOLLOW_NONE); 460 ResizeTo(Frame().Width(), scroller->Frame().bottom + 8); 461 scroller->SetResizingMode(B_FOLLOW_ALL); 462 } 463 464 465 //==================================================================== 466 // #pragma mark - 467 468 469 TNameControl::TNameControl(BRect rect, const char *label, BMessage *msg) 470 :BTextControl(rect, "", label, "", msg, B_FOLLOW_LEFT_RIGHT) 471 { 472 strcpy(fLabel, label); 473 } 474 475 476 void 477 TNameControl::AttachedToWindow() 478 { 479 BTextControl::AttachedToWindow(); 480 481 SetDivider(StringWidth(fLabel) + 6); 482 TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1); 483 } 484 485 486 void 487 TNameControl::MessageReceived(BMessage *msg) 488 { 489 switch (msg->what) { 490 case M_SELECT: 491 TextView()->Select(0, TextView()->TextLength()); 492 break; 493 494 default: 495 BTextControl::MessageReceived(msg); 496 } 497 } 498 499 500 //==================================================================== 501 // #pragma mark - 502 503 504 TSigTextView::TSigTextView(BRect frame, BRect text) 505 :BTextView(frame, "SignatureView", text, B_FOLLOW_ALL, B_NAVIGABLE | B_WILL_DRAW) 506 { 507 fDirty = false; 508 SetDoesUndo(true); 509 } 510 511 512 void 513 TSigTextView::FrameResized(float /*width*/, float /*height*/) 514 { 515 BRect r(Bounds()); 516 r.InsetBy(3, 3); 517 SetTextRect(r); 518 } 519 520 521 void 522 TSigTextView::DeleteText(int32 offset, int32 len) 523 { 524 fDirty = true; 525 BTextView::DeleteText(offset, len); 526 } 527 528 529 void 530 TSigTextView::InsertText(const char *text, int32 len, int32 offset, 531 const text_run_array *runs) 532 { 533 fDirty = true; 534 BTextView::InsertText(text, len, offset, runs); 535 } 536 537 538 void 539 TSigTextView::KeyDown(const char *key, int32 count) 540 { 541 bool up = false; 542 int32 height; 543 BRect r; 544 545 switch (key[0]) { 546 case B_HOME: 547 Select(0, 0); 548 ScrollToSelection(); 549 break; 550 551 case B_END: 552 Select(TextLength(), TextLength()); 553 ScrollToSelection(); 554 break; 555 556 case B_PAGE_UP: 557 up = true; 558 case B_PAGE_DOWN: 559 r = Bounds(); 560 height = (int32)((up ? r.top - r.bottom : r.bottom - r.top) - 25); 561 if ((up) && (!r.top)) 562 break; 563 ScrollBy(0, height); 564 break; 565 566 default: 567 BTextView::KeyDown(key, count); 568 } 569 } 570 571 572 void 573 TSigTextView::MessageReceived(BMessage *msg) 574 { 575 char type[B_FILE_NAME_LENGTH]; 576 char *text; 577 int32 end; 578 int32 start; 579 BFile file; 580 BNodeInfo *node; 581 entry_ref ref; 582 off_t size; 583 584 switch (msg->what) { 585 case B_SIMPLE_DATA: 586 if (msg->HasRef("refs")) { 587 msg->FindRef("refs", &ref); 588 file.SetTo(&ref, O_RDONLY); 589 if (file.InitCheck() == B_NO_ERROR) { 590 node = new BNodeInfo(&file); 591 node->GetType(type); 592 delete node; 593 file.GetSize(&size); 594 if ((!strncasecmp(type, "text/", 5)) && (size)) { 595 text = (char *)malloc(size); 596 file.Read(text, size); 597 Delete(); 598 GetSelection(&start, &end); 599 Insert(text, size); 600 Select(start, start + size); 601 free(text); 602 } 603 } 604 } 605 else 606 BTextView::MessageReceived(msg); 607 break; 608 609 case M_SELECT: 610 if (IsSelectable()) 611 Select(0, TextLength()); 612 break; 613 614 default: 615 BTextView::MessageReceived(msg); 616 } 617 } 618 619