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