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 36 #include "Header.h" 37 38 #include <stdio.h> 39 40 #include <ControlLook.h> 41 #include <DateTimeFormat.h> 42 #include <E-mail.h> 43 #include <LayoutBuilder.h> 44 #include <Locale.h> 45 #include <MenuBar.h> 46 #include <MenuField.h> 47 #include <MenuItem.h> 48 #include <ObjectList.h> 49 #include <PopUpMenu.h> 50 #include <Query.h> 51 #include <String.h> 52 #include <StringView.h> 53 #include <Volume.h> 54 #include <VolumeRoster.h> 55 #include <Window.h> 56 #include <fs_index.h> 57 #include <fs_info.h> 58 59 #include <MailSettings.h> 60 #include <MailMessage.h> 61 62 #include "MailApp.h" 63 #include "MailSupport.h" 64 #include "MailWindow.h" 65 #include "Messages.h" 66 #include "Utilities.h" 67 #include "QueryMenu.h" 68 #include "FieldMsg.h" 69 #include "Prefs.h" 70 71 72 #define B_TRANSLATION_CONTEXT "Mail" 73 74 75 using namespace BPrivate; 76 using std::map; 77 78 79 const uint32 kMsgFrom = 'hFrm'; 80 const uint32 kMsgAddressChosen = 'acsn'; 81 82 83 struct CompareBStrings { 84 bool 85 operator()(const BString *s1, const BString *s2) const 86 { 87 return (s1->Compare(*s2) < 0); 88 } 89 }; 90 91 92 class LabelView : public BStringView { 93 public: 94 LabelView(const char* label); 95 96 bool IsEnabled() const 97 { return fEnabled; } 98 void SetEnabled(bool enabled); 99 100 virtual void Draw(BRect updateRect); 101 102 private: 103 bool fEnabled; 104 }; 105 106 107 class HeaderTextControl : public BTextControl { 108 public: 109 HeaderTextControl(const char* label, 110 const char* name, BMessage* message); 111 112 virtual void AttachedToWindow(); 113 virtual void SetEnabled(bool enabled); 114 virtual void Draw(BRect updateRect); 115 virtual void MessageReceived(BMessage* message); 116 117 private: 118 void _UpdateTextViewColors(); 119 }; 120 121 122 // #pragma mark - LabelView 123 124 125 LabelView::LabelView(const char* label) 126 : 127 BStringView("label", label), 128 fEnabled(true) 129 { 130 SetAlignment(B_ALIGN_RIGHT); 131 } 132 133 134 void 135 LabelView::SetEnabled(bool enabled) 136 { 137 if (enabled != fEnabled) { 138 fEnabled = enabled; 139 Invalidate(); 140 } 141 } 142 143 144 void 145 LabelView::Draw(BRect updateRect) 146 { 147 if (Text() != NULL) { 148 BRect rect = Bounds(); 149 // TODO: solve this better (alignment of label and text) 150 rect.top++; 151 152 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 153 uint32 flags = 0; 154 if (!IsEnabled()) 155 flags |= BControlLook::B_DISABLED; 156 157 be_control_look->DrawLabel(this, Text(), rect, updateRect, 158 base, flags, BAlignment(Alignment(), B_ALIGN_MIDDLE)); 159 } 160 } 161 162 163 // #pragma mark - HeaderTextControl 164 165 166 HeaderTextControl::HeaderTextControl(const char* label, const char* name, 167 BMessage* message) 168 : 169 BTextControl(label, name, message) 170 { 171 } 172 173 174 void 175 HeaderTextControl::AttachedToWindow() 176 { 177 BTextControl::AttachedToWindow(); 178 _UpdateTextViewColors(); 179 } 180 181 182 void 183 HeaderTextControl::SetEnabled(bool enabled) 184 { 185 BTextControl::SetEnabled(enabled); 186 _UpdateTextViewColors(); 187 } 188 189 190 void 191 HeaderTextControl::Draw(BRect updateRect) 192 { 193 bool enabled = IsEnabled(); 194 bool active = TextView()->IsFocus() && Window()->IsActive(); 195 196 BRect rect = TextView()->Frame(); 197 rect.InsetBy(-2, -2); 198 199 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 200 uint32 flags = 0; 201 if (!enabled) 202 flags = BControlLook::B_DISABLED; 203 204 if (enabled) { 205 if (active) 206 flags |= BControlLook::B_FOCUSED; 207 208 be_control_look->DrawTextControlBorder(this, rect, updateRect, base, 209 flags); 210 } 211 212 if (Label() != NULL) { 213 rect = CreateLabelLayoutItem()->Frame().OffsetByCopy(-Frame().left, 214 -Frame().top); 215 // TODO: solve this better (alignment of label and text) 216 rect.top++; 217 218 alignment labelAlignment; 219 GetAlignment(&labelAlignment, NULL); 220 221 be_control_look->DrawLabel(this, Label(), rect, updateRect, 222 base, flags, BAlignment(labelAlignment, B_ALIGN_MIDDLE)); 223 } 224 } 225 226 227 void 228 HeaderTextControl::MessageReceived(BMessage* message) 229 { 230 switch (message->what) { 231 case M_SELECT: 232 { 233 BTextView* textView = TextView(); 234 if (textView != NULL) 235 textView->SelectAll(); 236 break; 237 } 238 239 default: 240 BTextControl::MessageReceived(message); 241 break; 242 } 243 } 244 245 246 void 247 HeaderTextControl::_UpdateTextViewColors() 248 { 249 BTextView* textView = TextView(); 250 251 BFont font; 252 textView->GetFontAndColor(0, &font); 253 254 rgb_color textColor; 255 if (!textView->IsEditable() || IsEnabled()) 256 textColor = ui_color(B_DOCUMENT_TEXT_COLOR); 257 else { 258 textColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 259 B_DISABLED_LABEL_TINT); 260 } 261 262 textView->SetFontAndColor(&font, B_FONT_ALL, &textColor); 263 264 rgb_color color; 265 if (!textView->IsEditable()) 266 color = ui_color(B_PANEL_BACKGROUND_COLOR); 267 else if (IsEnabled()) 268 color = ui_color(B_DOCUMENT_BACKGROUND_COLOR); 269 else { 270 color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 271 B_LIGHTEN_2_TINT); 272 } 273 274 textView->SetViewColor(color); 275 textView->SetLowColor(color); 276 } 277 278 279 // #pragma mark - THeaderView 280 281 282 THeaderView::THeaderView(bool incoming, bool resending, int32 defaultAccount) 283 : 284 fAccountMenu(NULL), 285 fAccountID(defaultAccount), 286 fFromControl(NULL), 287 fCcControl(NULL), 288 fBccControl(NULL), 289 fDateControl(NULL), 290 fIncoming(incoming), 291 fResending(resending) 292 { 293 // From 294 if (fIncoming) { 295 fFromControl = new HeaderTextControl(B_TRANSLATE("From:"), NULL, NULL); 296 fFromControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 297 fFromControl->SetEnabled(false); 298 } 299 300 // From accounts menu 301 BMenuField* fromField = NULL; 302 if (!fIncoming || fResending) { 303 // And now the "from account" pop-up menu, on the left side, taking the 304 // remaining space. 305 306 fAccountMenu = new BPopUpMenu(B_EMPTY_STRING); 307 308 BMailAccounts accounts; 309 bool marked = false; 310 for (int32 i = 0; i < accounts.CountAccounts(); i++) { 311 BMailAccountSettings* account = accounts.AccountAt(i); 312 BString name = account->Name(); 313 name << ": " << account->RealName() << " <" 314 << account->ReturnAddress() << ">"; 315 316 BMessage* msg = new BMessage(kMsgFrom); 317 BMenuItem *item = new BMenuItem(name, msg); 318 319 msg->AddInt32("id", account->AccountID()); 320 321 if (defaultAccount == account->AccountID()) { 322 item->SetMarked(true); 323 marked = true; 324 } 325 fAccountMenu->AddItem(item); 326 } 327 328 if (!marked) { 329 BMenuItem *item = fAccountMenu->ItemAt(0); 330 if (item != NULL) { 331 item->SetMarked(true); 332 fAccountID = item->Message()->FindInt32("id"); 333 } else { 334 fAccountMenu->AddItem( 335 item = new BMenuItem(B_TRANSLATE("<none>"), NULL)); 336 item->SetEnabled(false); 337 fAccountID = ~(int32)0; 338 } 339 // default account is invalid, set to marked 340 // TODO: do this differently, no casting and knowledge 341 // of TMailApp here.... 342 if (TMailApp* app = dynamic_cast<TMailApp*>(be_app)) 343 app->SetDefaultAccount(fAccountID); 344 } 345 346 fromField = new BMenuField("account", B_TRANSLATE("From:"), 347 fAccountMenu, B_WILL_DRAW | B_NAVIGABLE | B_NAVIGABLE_JUMP); 348 fromField->SetAlignment(B_ALIGN_RIGHT); 349 } 350 351 // To 352 fToLabel = new LabelView(B_TRANSLATE("To:")); 353 fToControl = new AddressTextControl(B_TRANSLATE("To:"), 354 new BMessage(TO_FIELD)); 355 if (fIncoming || fResending) { 356 fToLabel->SetEnabled(false); 357 fToControl->SetEditable(false); 358 fToControl->SetEnabled(false); 359 } 360 361 BMessage* msg = new BMessage(FIELD_CHANGED); 362 msg->AddInt32("bitmask", FIELD_TO); 363 fToControl->SetModificationMessage(msg); 364 365 // Carbon copy 366 fCcLabel = new LabelView(B_TRANSLATE("Cc:")); 367 fCcControl = new AddressTextControl("cc", new BMessage(CC_FIELD)); 368 if (fIncoming) { 369 fCcLabel->SetEnabled(false); 370 fCcControl->SetEditable(false); 371 fCcControl->SetEnabled(false); 372 fCcControl->Hide(); 373 fCcLabel->Hide(); 374 } 375 msg = new BMessage(FIELD_CHANGED); 376 msg->AddInt32("bitmask", FIELD_CC); 377 fCcControl->SetModificationMessage(msg); 378 379 // Blind carbon copy 380 if (!fIncoming) { 381 fBccControl = new AddressTextControl("bcc", new BMessage(BCC_FIELD)); 382 msg = new BMessage(FIELD_CHANGED); 383 msg->AddInt32("bitmask", FIELD_BCC); 384 fBccControl->SetModificationMessage(msg); 385 } 386 387 // Subject 388 fSubjectControl = new HeaderTextControl(B_TRANSLATE("Subject:"), NULL, 389 new BMessage(SUBJECT_FIELD)); 390 msg = new BMessage(FIELD_CHANGED); 391 msg->AddInt32("bitmask", FIELD_SUBJECT); 392 fSubjectControl->SetModificationMessage(msg); 393 fSubjectControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 394 if (fIncoming || fResending) 395 fSubjectControl->SetEnabled(false); 396 397 // Date 398 if (fIncoming) { 399 fDateControl = new HeaderTextControl(B_TRANSLATE("Date:"), NULL, NULL); 400 fDateControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 401 fDateControl->SetEnabled(false); 402 } 403 404 BGridLayout* layout = GridLayout(); 405 406 if (fIncoming) { 407 layout->SetHorizontalSpacing(0); 408 layout->SetVerticalSpacing(0); 409 } else 410 layout->SetVerticalSpacing(B_USE_HALF_ITEM_SPACING); 411 412 int32 row = 0; 413 if (fromField != NULL) { 414 layout->AddItem(fromField->CreateLabelLayoutItem(), 0, row); 415 layout->AddItem(fromField->CreateMenuBarLayoutItem(), 1, row++, 3, 1); 416 } else if (fFromControl != NULL) { 417 layout->AddItem(fFromControl->CreateLabelLayoutItem(), 0, row); 418 layout->AddItem(fFromControl->CreateTextViewLayoutItem(), 1, row); 419 } 420 421 if (fIncoming && !fResending) { 422 layout->AddView(fToLabel, 2, row); 423 layout->AddView(fToControl, 3, row++); 424 } else { 425 row++; 426 layout->AddView(fToLabel, 0, row); 427 layout->AddView(fToControl, 1, row++, 3, 1); 428 } 429 430 if (fDateControl != NULL) { 431 layout->AddItem(fDateControl->CreateLabelLayoutItem(), 0, row); 432 layout->AddItem(fDateControl->CreateTextViewLayoutItem(), 1, row); 433 } 434 435 if (fIncoming && (fCcControl != NULL)) { 436 layout->AddView(fCcLabel, 2, row); 437 layout->AddView(fCcControl, 3, row++); 438 } else { 439 row++; 440 layout->AddView(fCcLabel, 0, row); 441 layout->AddView(fCcControl, 1, row, 1, 1); 442 } 443 444 if (fBccControl != NULL) { 445 layout->AddView(new LabelView(B_TRANSLATE("Bcc:")), 2, row); 446 layout->AddView(fBccControl, 3, row++); 447 } else 448 row++; 449 layout->AddItem(fSubjectControl->CreateLabelLayoutItem(), 0, row); 450 layout->AddItem(fSubjectControl->CreateTextViewLayoutItem(), 1, row++, 451 3, 1); 452 } 453 454 455 const char* 456 THeaderView::From() const 457 { 458 return fFromControl != NULL ? fFromControl->Text() : NULL; 459 } 460 461 462 void 463 THeaderView::SetFrom(const char* from) 464 { 465 if (fFromControl != NULL) 466 fFromControl->SetText(from); 467 } 468 469 470 bool 471 THeaderView::IsToEmpty() const 472 { 473 return To() == NULL || To()[0] == '\0'; 474 } 475 476 477 const char* 478 THeaderView::To() const 479 { 480 return fToControl->Text(); 481 } 482 483 484 void 485 THeaderView::SetTo(const char* to) 486 { 487 fToControl->SetText(to); 488 } 489 490 491 bool 492 THeaderView::IsCcEmpty() const 493 { 494 return Cc() == NULL || Cc()[0] == '\0'; 495 } 496 497 498 const char* 499 THeaderView::Cc() const 500 { 501 return fCcControl != NULL ? fCcControl->Text() : NULL; 502 } 503 504 505 void 506 THeaderView::SetCc(const char* cc) 507 { 508 fCcControl->SetText(cc); 509 510 if (fIncoming) { 511 if (cc != NULL && cc[0] != '\0') { 512 if (fCcControl->IsHidden(this)) { 513 fCcControl->Show(); 514 fCcLabel->Show(); 515 } 516 } else if (!fCcControl->IsHidden(this)) { 517 fCcControl->Hide(); 518 fCcLabel->Hide(); 519 } 520 } 521 } 522 523 524 bool 525 THeaderView::IsBccEmpty() const 526 { 527 return Bcc() == NULL || Bcc()[0] == '\0'; 528 } 529 530 531 const char* 532 THeaderView::Bcc() const 533 { 534 return fBccControl != NULL ? fBccControl->Text() : NULL; 535 } 536 537 538 void 539 THeaderView::SetBcc(const char* bcc) 540 { 541 if (fBccControl != NULL) 542 fBccControl->SetText(bcc); 543 } 544 545 546 bool 547 THeaderView::IsSubjectEmpty() const 548 { 549 return Subject() == NULL || Subject()[0] == '\0'; 550 } 551 552 553 const char* 554 THeaderView::Subject() const 555 { 556 return fSubjectControl->Text(); 557 } 558 559 560 void 561 THeaderView::SetSubject(const char* subject) 562 { 563 fSubjectControl->SetText(subject); 564 } 565 566 567 bool 568 THeaderView::IsDateEmpty() const 569 { 570 return Date() == NULL || Date()[0] == '\0'; 571 } 572 573 574 const char* 575 THeaderView::Date() const 576 { 577 return fDateControl != NULL ? fDateControl->Text() : NULL; 578 } 579 580 581 void 582 THeaderView::SetDate(time_t date) 583 { 584 fDate = date; 585 586 if (fDateControl != NULL) { 587 BDateTimeFormat formatter; 588 589 BString string; 590 formatter.Format(string, date, B_FULL_DATE_FORMAT, 591 B_MEDIUM_TIME_FORMAT); 592 SetDate(string); 593 } 594 } 595 596 597 void 598 THeaderView::SetDate(const char* date) 599 { 600 if (fDateControl != NULL) 601 fDateControl->SetText(date); 602 } 603 604 605 int32 606 THeaderView::AccountID() const 607 { 608 return fAccountID; 609 } 610 611 612 const char* 613 THeaderView::AccountName() const 614 { 615 BMenuItem* menuItem = fAccountMenu->FindMarked(); 616 if (menuItem != NULL) 617 return menuItem->Label(); 618 619 return NULL; 620 } 621 622 623 void 624 THeaderView::SetAccount(int32 id) 625 { 626 fAccountID = id; 627 628 if (fAccountMenu == NULL) 629 return; 630 631 for (int32 i = fAccountMenu->CountItems(); i-- > 0;) { 632 BMenuItem* item = fAccountMenu->ItemAt(i); 633 if (item == NULL) 634 continue; 635 636 BMessage* message = item->Message(); 637 if (message->GetInt32("id", -1) == id) { 638 item->SetMarked(true); 639 break; 640 } 641 } 642 } 643 644 645 void 646 THeaderView::SetAccount(const char* name) 647 { 648 if (fAccountMenu == NULL) 649 return; 650 651 BMenuItem* item = fAccountMenu->FindItem(name); 652 if (item != NULL) { 653 item->SetMarked(true); 654 fAccountID = item->Message()->GetInt32("id", -1); 655 } 656 } 657 658 659 status_t 660 THeaderView::SetFromMessage(BEmailMessage* mail) 661 { 662 // Set Subject:, From:, To: & Cc: fields 663 SetSubject(mail->Subject()); 664 SetFrom(mail->From()); 665 SetTo(mail->To()); 666 SetCc(mail->CC()); 667 668 BString accountName; 669 if (mail->GetAccountName(accountName) == B_OK) 670 SetAccount(accountName); 671 672 // Set the date on this message 673 time_t date = mail->Date(); 674 if (date <= 0) { 675 const char* dateField = mail->HeaderField("Date"); 676 SetDate(dateField != NULL ? dateField : B_TRANSLATE("Unknown")); 677 } else 678 SetDate(date); 679 680 return B_OK; 681 } 682 683 684 void 685 THeaderView::MessageReceived(BMessage *msg) 686 { 687 switch (msg->what) { 688 case B_SIMPLE_DATA: 689 { 690 BTextView* textView = dynamic_cast<BTextView*>(Window()->CurrentFocus()); 691 if (dynamic_cast<AddressTextControl *>(textView->Parent()) != NULL) 692 BMessage message(*msg); 693 textView->Parent()->MessageReceived(msg); 694 break; 695 } 696 case B_REFS_RECEIVED: 697 { 698 BMessage message(*msg); 699 message.what = REFS_RECEIVED; 700 Window()->PostMessage(&message, Window()); 701 break; 702 } 703 704 case kMsgFrom: 705 { 706 BMenuItem *item; 707 if (msg->FindPointer("source", (void **)&item) >= B_OK) 708 item->SetMarked(true); 709 710 int32 account; 711 if (msg->FindInt32("id",(int32 *)&account) >= B_OK) 712 fAccountID = account; 713 714 BMessage message(FIELD_CHANGED); 715 // field doesn't matter; no special processing for this field 716 // it's just to turn on the save button 717 message.AddInt32("bitmask", 0); 718 Window()->PostMessage(&message, Window()); 719 break; 720 } 721 } 722 } 723 724 725 void 726 THeaderView::AttachedToWindow() 727 { 728 fToControl->SetTarget(Looper()); 729 fSubjectControl->SetTarget(Looper()); 730 fCcControl->SetTarget(Looper()); 731 if (fBccControl != NULL) 732 fBccControl->SetTarget(Looper()); 733 if (fAccountMenu != NULL) 734 fAccountMenu->SetTargetForItems(this); 735 736 BView::AttachedToWindow(); 737 } 738