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 BAlert* alert = 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); 202 alert->SetShortcut(0, B_ESCAPE); 203 int32 choice = alert->Go(); 204 205 if (choice == 0) 206 break; 207 208 if (fFile) { 209 delete fFile; 210 fFile = NULL; 211 fEntry.Remove(); 212 fSigView->fName->SetText(""); 213 fSigView->fTextView->SetText(NULL, (int32)0); 214 fSigView->fName->MakeFocus(true); 215 } 216 break; 217 } 218 case M_SIGNATURE: 219 if (Clear()) { 220 msg->FindRef("ref", &ref); 221 fEntry.SetTo(&ref); 222 fFile = new BFile(&ref, O_RDWR); 223 if (fFile->InitCheck() == B_NO_ERROR) { 224 fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name)); 225 fSigView->fName->SetText(name); 226 fFile->GetSize(&size); 227 sig = (char *)malloc(size); 228 size = fFile->Read(sig, size); 229 fSigView->fTextView->SetText(sig, size); 230 fSigView->fName->MakeFocus(true); 231 text_view = (BTextView *)fSigView->fName->ChildAt(0); 232 text_view->Select(0, text_view->TextLength()); 233 fSigView->fTextView->fDirty = false; 234 } 235 else { 236 fFile = NULL; 237 beep(); 238 BAlert* alert = new BAlert("", 239 B_TRANSLATE("Couldn't open this signature. Sorry."), 240 B_TRANSLATE("OK")); 241 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 242 alert->Go(); 243 } 244 } 245 break; 246 247 default: 248 BWindow::MessageReceived(msg); 249 } 250 } 251 252 253 bool 254 TSignatureWindow::QuitRequested() 255 { 256 if (Clear()) { 257 BMessage msg(WINDOW_CLOSED); 258 msg.AddInt32("kind", SIG_WINDOW); 259 msg.AddRect("window frame", Frame()); 260 261 be_app->PostMessage(&msg); 262 return true; 263 } 264 return false; 265 } 266 267 268 void 269 TSignatureWindow::FrameResized(float width, float height) 270 { 271 fSigView->FrameResized(width, height); 272 } 273 274 275 void 276 TSignatureWindow::Show() 277 { 278 BTextView *text_view; 279 280 Lock(); 281 text_view = (BTextView *)fSigView->fName->TextView(); 282 fSigView->fName->MakeFocus(true); 283 text_view->Select(0, text_view->TextLength()); 284 Unlock(); 285 286 BWindow::Show(); 287 } 288 289 290 bool 291 TSignatureWindow::Clear() 292 { 293 int32 result; 294 295 if (IsDirty()) { 296 beep(); 297 BAlert *alert = new BAlert("", 298 B_TRANSLATE("Save changes to this signature?"), 299 B_TRANSLATE("Cancel"), 300 B_TRANSLATE("Don't save"), 301 B_TRANSLATE("Save"), 302 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT); 303 alert->SetShortcut(0, B_ESCAPE); 304 alert->SetShortcut(1, 'd'); 305 alert->SetShortcut(2, 's'); 306 result = alert->Go(); 307 if (result == 0) 308 return false; 309 if (result == 2) 310 Save(); 311 } 312 313 delete fFile; 314 fFile = NULL; 315 fSigView->fTextView->fDirty = false; 316 return true; 317 } 318 319 320 bool 321 TSignatureWindow::IsDirty() 322 { 323 char name[B_FILE_NAME_LENGTH]; 324 325 if (fFile) { 326 fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name)); 327 if ((strcmp(name, fSigView->fName->Text())) || (fSigView->fTextView->fDirty)) 328 return true; 329 } 330 else { 331 if ((strlen(fSigView->fName->Text())) || 332 (fSigView->fTextView->TextLength())) 333 return true; 334 } 335 return false; 336 } 337 338 339 void 340 TSignatureWindow::Save() 341 { 342 char name[B_FILE_NAME_LENGTH]; 343 int32 index = 0; 344 status_t result; 345 BDirectory dir; 346 BEntry entry; 347 BNodeInfo *node; 348 BPath path; 349 350 if (!fFile) { 351 find_directory(B_USER_SETTINGS_DIRECTORY, &path, true); 352 dir.SetTo(path.Path()); 353 354 if (dir.FindEntry("Mail", &entry) == B_NO_ERROR) 355 dir.SetTo(&entry); 356 else 357 dir.CreateDirectory("Mail", &dir); 358 359 if (dir.InitCheck() != B_NO_ERROR) 360 goto err_exit; 361 362 if (dir.FindEntry("signatures", &entry) == B_NO_ERROR) 363 dir.SetTo(&entry); 364 else 365 dir.CreateDirectory("signatures", &dir); 366 367 if (dir.InitCheck() != B_NO_ERROR) 368 goto err_exit; 369 370 fFile = new BFile(); 371 while(true) { 372 sprintf(name, "signature_%" B_PRId32, index++); 373 if ((result = dir.CreateFile(name, fFile, true)) == B_NO_ERROR) 374 break; 375 if (result != EEXIST) 376 goto err_exit; 377 } 378 dir.FindEntry(name, &fEntry); 379 node = new BNodeInfo(fFile); 380 node->SetType("text/plain"); 381 delete node; 382 } 383 384 fSigView->fTextView->fDirty = false; 385 fFile->Seek(0, 0); 386 fFile->Write(fSigView->fTextView->Text(), 387 fSigView->fTextView->TextLength()); 388 fFile->SetSize(fFile->Position()); 389 fFile->WriteAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, fSigView->fName->Text(), 390 strlen(fSigView->fName->Text()) + 1); 391 return; 392 393 err_exit: 394 beep(); 395 BAlert* alert = new BAlert("", 396 B_TRANSLATE("An error occurred trying to save this signature."), 397 B_TRANSLATE("Sorry")); 398 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 399 alert->Go(); 400 } 401 402 403 //==================================================================== 404 // #pragma mark - 405 406 407 TSignatureView::TSignatureView(BRect rect) 408 : BBox(rect, "SigView", B_FOLLOW_ALL, B_WILL_DRAW) 409 { 410 } 411 412 413 void 414 TSignatureView::AttachedToWindow() 415 { 416 BRect rect = Bounds(); 417 float name_text_length = StringWidth(B_TRANSLATE("Title:")); 418 float sig_text_length = StringWidth(B_TRANSLATE("Signature:")); 419 float divide_length; 420 421 if (name_text_length > sig_text_length) 422 divide_length = name_text_length; 423 else 424 divide_length = sig_text_length; 425 426 rect.InsetBy(8,0); 427 rect.top+= 8; 428 429 fName = new TNameControl(rect, B_TRANSLATE("Title:"), 430 new BMessage(NAME_FIELD)); 431 AddChild(fName); 432 433 fName->SetDivider(divide_length + 10); 434 fName->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 435 436 rect.OffsetBy(0,fName->Bounds().Height()+5); 437 rect.bottom = rect.top + kSigHeight; 438 rect.left = fName->TextView()->Frame().left; 439 440 BRect text = rect; 441 text.OffsetTo(10,0); 442 fTextView = new TSigTextView(rect, text); 443 BScrollView *scroller = new BScrollView("SigScroller", fTextView, B_FOLLOW_ALL, 0, false, true); 444 AddChild(scroller); 445 scroller->ResizeBy(-1 * scroller->ScrollBar(B_VERTICAL)->Frame().Width() - 9, 0); 446 scroller->MoveBy(7,0); 447 448 /* back up a bit to make room for the label */ 449 450 rect = scroller->Frame(); 451 BStringView *stringView = new BStringView(rect, "SigLabel", 452 B_TRANSLATE("Signature:")); 453 AddChild(stringView); 454 455 float tWidth, tHeight; 456 stringView->GetPreferredSize(&tWidth, &tHeight); 457 458 /* the 5 is for the spacer in the TextView */ 459 460 rect.OffsetBy(-1 *(tWidth) - 5, 0); 461 rect.right = rect.left + tWidth; 462 rect.bottom = rect.top + tHeight; 463 464 stringView->MoveTo(rect.LeftTop()); 465 stringView->ResizeTo(rect.Width(), rect.Height()); 466 467 /* Resize the View to the correct height */ 468 scroller->SetResizingMode(B_FOLLOW_NONE); 469 ResizeTo(Frame().Width(), scroller->Frame().bottom + 8); 470 scroller->SetResizingMode(B_FOLLOW_ALL); 471 } 472 473 474 //==================================================================== 475 // #pragma mark - 476 477 478 TNameControl::TNameControl(BRect rect, const char *label, BMessage *msg) 479 :BTextControl(rect, "", label, "", msg, B_FOLLOW_LEFT_RIGHT) 480 { 481 strcpy(fLabel, label); 482 } 483 484 485 void 486 TNameControl::AttachedToWindow() 487 { 488 BTextControl::AttachedToWindow(); 489 490 SetDivider(StringWidth(fLabel) + 6); 491 TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1); 492 } 493 494 495 void 496 TNameControl::MessageReceived(BMessage *msg) 497 { 498 switch (msg->what) { 499 case M_SELECT: 500 TextView()->Select(0, TextView()->TextLength()); 501 break; 502 503 default: 504 BTextControl::MessageReceived(msg); 505 } 506 } 507 508 509 //==================================================================== 510 // #pragma mark - 511 512 513 TSigTextView::TSigTextView(BRect frame, BRect text) 514 :BTextView(frame, "SignatureView", text, B_FOLLOW_ALL, B_NAVIGABLE | B_WILL_DRAW) 515 { 516 fDirty = false; 517 SetDoesUndo(true); 518 } 519 520 521 void 522 TSigTextView::FrameResized(float /*width*/, float /*height*/) 523 { 524 BRect r(Bounds()); 525 r.InsetBy(3, 3); 526 SetTextRect(r); 527 } 528 529 530 void 531 TSigTextView::DeleteText(int32 offset, int32 len) 532 { 533 fDirty = true; 534 BTextView::DeleteText(offset, len); 535 } 536 537 538 void 539 TSigTextView::InsertText(const char *text, int32 len, int32 offset, 540 const text_run_array *runs) 541 { 542 fDirty = true; 543 BTextView::InsertText(text, len, offset, runs); 544 } 545 546 547 void 548 TSigTextView::KeyDown(const char *key, int32 count) 549 { 550 bool up = false; 551 int32 height; 552 BRect r; 553 554 switch (key[0]) { 555 case B_HOME: 556 Select(0, 0); 557 ScrollToSelection(); 558 break; 559 560 case B_END: 561 Select(TextLength(), TextLength()); 562 ScrollToSelection(); 563 break; 564 565 case B_PAGE_UP: 566 up = true; 567 case B_PAGE_DOWN: 568 r = Bounds(); 569 height = (int32)((up ? r.top - r.bottom : r.bottom - r.top) - 25); 570 if ((up) && (!r.top)) 571 break; 572 ScrollBy(0, height); 573 break; 574 575 default: 576 BTextView::KeyDown(key, count); 577 } 578 } 579 580 581 void 582 TSigTextView::MessageReceived(BMessage *msg) 583 { 584 char type[B_FILE_NAME_LENGTH]; 585 char *text; 586 int32 end; 587 int32 start; 588 BFile file; 589 BNodeInfo *node; 590 entry_ref ref; 591 off_t size; 592 593 switch (msg->what) { 594 case B_SIMPLE_DATA: 595 if (msg->HasRef("refs")) { 596 msg->FindRef("refs", &ref); 597 file.SetTo(&ref, O_RDONLY); 598 if (file.InitCheck() == B_NO_ERROR) { 599 node = new BNodeInfo(&file); 600 node->GetType(type); 601 delete node; 602 file.GetSize(&size); 603 if ((!strncasecmp(type, "text/", 5)) && (size)) { 604 text = (char *)malloc(size); 605 file.Read(text, size); 606 Delete(); 607 GetSelection(&start, &end); 608 Insert(text, size); 609 Select(start, start + size); 610 free(text); 611 } 612 } 613 } 614 else 615 BTextView::MessageReceived(msg); 616 break; 617 618 case M_SELECT: 619 if (IsSelectable()) 620 Select(0, TextLength()); 621 break; 622 623 default: 624 BTextView::MessageReceived(msg); 625 } 626 } 627 628