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