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 fBccControl(NULL), 288 fDateControl(NULL), 289 fIncoming(incoming), 290 fResending(resending) 291 { 292 // From 293 if (fIncoming) { 294 fFromControl = new HeaderTextControl(B_TRANSLATE("From:"), NULL, NULL); 295 fFromControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 296 fFromControl->SetEnabled(false); 297 } 298 299 // From accounts menu 300 BMenuField* fromField = NULL; 301 if (!fIncoming || resending) { 302 // And now the "from account" pop-up menu, on the left side, taking the 303 // remaining space. 304 305 fAccountMenu = new BPopUpMenu(B_EMPTY_STRING); 306 307 BMailAccounts accounts; 308 bool marked = false; 309 for (int32 i = 0; i < accounts.CountAccounts(); i++) { 310 BMailAccountSettings* account = accounts.AccountAt(i); 311 BString name = account->Name(); 312 name << ": " << account->RealName() << " <" 313 << account->ReturnAddress() << ">"; 314 315 BMessage* msg = new BMessage(kMsgFrom); 316 BMenuItem *item = new BMenuItem(name, msg); 317 318 msg->AddInt32("id", account->AccountID()); 319 320 if (defaultAccount == account->AccountID()) { 321 item->SetMarked(true); 322 marked = true; 323 } 324 fAccountMenu->AddItem(item); 325 } 326 327 if (!marked) { 328 BMenuItem *item = fAccountMenu->ItemAt(0); 329 if (item != NULL) { 330 item->SetMarked(true); 331 fAccountID = item->Message()->FindInt32("id"); 332 } else { 333 fAccountMenu->AddItem( 334 item = new BMenuItem(B_TRANSLATE("<none>"), NULL)); 335 item->SetEnabled(false); 336 fAccountID = ~(int32)0; 337 } 338 // default account is invalid, set to marked 339 // TODO: do this differently, no casting and knowledge 340 // of TMailApp here.... 341 if (TMailApp* app = dynamic_cast<TMailApp*>(be_app)) 342 app->SetDefaultAccount(fAccountID); 343 } 344 345 fromField = new BMenuField("account", B_TRANSLATE("From:"), 346 fAccountMenu, B_WILL_DRAW | B_NAVIGABLE | B_NAVIGABLE_JUMP); 347 fromField->SetAlignment(B_ALIGN_RIGHT); 348 } 349 350 // To 351 fToLabel = new LabelView(B_TRANSLATE("To:")); 352 fToControl = new AddressTextControl(B_TRANSLATE("To:"), 353 new BMessage(TO_FIELD)); 354 if (fIncoming || resending) { 355 fToLabel->SetEnabled(false); 356 fToControl->SetEditable(false); 357 fToControl->SetEnabled(false); 358 } 359 360 BMessage* msg = new BMessage(FIELD_CHANGED); 361 msg->AddInt32("bitmask", FIELD_TO); 362 fToControl->SetModificationMessage(msg); 363 364 // Carbon copy 365 fCcLabel = new LabelView(B_TRANSLATE("Cc:")); 366 fCcControl = new AddressTextControl("cc", new BMessage(CC_FIELD)); 367 if (fIncoming) { 368 fCcLabel->SetEnabled(false); 369 fCcControl->SetEditable(false); 370 fCcControl->SetEnabled(false); 371 fCcControl->Hide(); 372 fCcLabel->Hide(); 373 } 374 msg = new BMessage(FIELD_CHANGED); 375 msg->AddInt32("bitmask", FIELD_CC); 376 fCcControl->SetModificationMessage(msg); 377 378 // Blind carbon copy 379 if (!fIncoming) { 380 fBccControl = new AddressTextControl("bcc", new BMessage(BCC_FIELD)); 381 msg = new BMessage(FIELD_CHANGED); 382 msg->AddInt32("bitmask", FIELD_BCC); 383 fBccControl->SetModificationMessage(msg); 384 } 385 386 // Subject 387 fSubjectControl = new HeaderTextControl(B_TRANSLATE("Subject:"), NULL, 388 new BMessage(SUBJECT_FIELD)); 389 msg = new BMessage(FIELD_CHANGED); 390 msg->AddInt32("bitmask", FIELD_SUBJECT); 391 fSubjectControl->SetModificationMessage(msg); 392 fSubjectControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 393 if (fIncoming || fResending) 394 fSubjectControl->SetEnabled(false); 395 396 // Date 397 if (fIncoming) { 398 fDateControl = new HeaderTextControl(B_TRANSLATE("Date:"), NULL, NULL); 399 fDateControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 400 fDateControl->SetEnabled(false); 401 } 402 403 BGridLayout* layout = GridLayout(); 404 405 if (fIncoming) { 406 layout->SetHorizontalSpacing(0); 407 layout->SetVerticalSpacing(0); 408 } else 409 layout->SetVerticalSpacing(B_USE_HALF_ITEM_SPACING); 410 411 int32 row = 0; 412 if (fromField != NULL) { 413 layout->AddItem(fromField->CreateLabelLayoutItem(), 0, row); 414 layout->AddItem(fromField->CreateMenuBarLayoutItem(), 1, row++, 3, 1); 415 } else if (fFromControl != NULL) { 416 layout->AddItem(fFromControl->CreateLabelLayoutItem(), 0, row); 417 layout->AddItem(fFromControl->CreateTextViewLayoutItem(), 1, row); 418 } 419 420 if (fIncoming) { 421 layout->AddView(fToLabel, 2, row); 422 layout->AddView(fToControl, 3, row++); 423 } else { 424 row++; 425 layout->AddView(fToLabel, 0, row); 426 layout->AddView(fToControl, 1, row++, 3, 1); 427 } 428 429 if (fDateControl != NULL) { 430 layout->AddItem(fDateControl->CreateLabelLayoutItem(), 0, row); 431 layout->AddItem(fDateControl->CreateTextViewLayoutItem(), 1, row); 432 } 433 434 if (fIncoming && (fCcControl != NULL)) { 435 layout->AddView(fCcLabel, 2, row); 436 layout->AddView(fCcControl, 3, row++); 437 } else { 438 row++; 439 layout->AddView(fCcLabel, 0, row); 440 layout->AddView(fCcControl, 1, row, 1, 1); 441 } 442 if (fBccControl != NULL) { 443 layout->AddView(new LabelView(B_TRANSLATE("Bcc:")), 2, row); 444 layout->AddView(fBccControl, 3, row++); 445 } else 446 row++; 447 layout->AddItem(fSubjectControl->CreateLabelLayoutItem(), 0, row); 448 layout->AddItem(fSubjectControl->CreateTextViewLayoutItem(), 1, row++, 449 3, 1); 450 } 451 452 453 const char* 454 THeaderView::From() const 455 { 456 return fFromControl != NULL ? fFromControl->Text() : NULL; 457 } 458 459 460 void 461 THeaderView::SetFrom(const char* from) 462 { 463 if (fFromControl != NULL) 464 fFromControl->SetText(from); 465 } 466 467 468 bool 469 THeaderView::IsToEmpty() const 470 { 471 return To() == NULL || To()[0] == '\0'; 472 } 473 474 475 const char* 476 THeaderView::To() const 477 { 478 return fToControl->Text(); 479 } 480 481 482 void 483 THeaderView::SetTo(const char* to) 484 { 485 fToControl->SetText(to); 486 } 487 488 489 bool 490 THeaderView::IsCcEmpty() const 491 { 492 return Cc() == NULL || Cc()[0] == '\0'; 493 } 494 495 496 const char* 497 THeaderView::Cc() const 498 { 499 return fCcControl != NULL ? fCcControl->Text() : NULL; 500 } 501 502 503 void 504 THeaderView::SetCc(const char* cc) 505 { 506 fCcControl->SetText(cc); 507 508 if (fIncoming) { 509 if (cc != NULL && cc[0] != '\0') { 510 if (fCcControl->IsHidden(this)) { 511 fCcControl->Show(); 512 fCcLabel->Show(); 513 } 514 } else if (!fCcControl->IsHidden(this)) { 515 fCcControl->Hide(); 516 fCcLabel->Hide(); 517 } 518 } 519 } 520 521 522 bool 523 THeaderView::IsBccEmpty() const 524 { 525 return Bcc() == NULL || Bcc()[0] == '\0'; 526 } 527 528 529 const char* 530 THeaderView::Bcc() const 531 { 532 return fBccControl != NULL ? fBccControl->Text() : NULL; 533 } 534 535 536 void 537 THeaderView::SetBcc(const char* bcc) 538 { 539 if (fBccControl != NULL) 540 fBccControl->SetText(bcc); 541 } 542 543 544 bool 545 THeaderView::IsSubjectEmpty() const 546 { 547 return Subject() == NULL || Subject()[0] == '\0'; 548 } 549 550 551 const char* 552 THeaderView::Subject() const 553 { 554 return fSubjectControl->Text(); 555 } 556 557 558 void 559 THeaderView::SetSubject(const char* subject) 560 { 561 fSubjectControl->SetText(subject); 562 } 563 564 565 bool 566 THeaderView::IsDateEmpty() const 567 { 568 return Date() == NULL || Date()[0] == '\0'; 569 } 570 571 572 const char* 573 THeaderView::Date() const 574 { 575 return fDateControl != NULL ? fDateControl->Text() : NULL; 576 } 577 578 579 void 580 THeaderView::SetDate(time_t date) 581 { 582 fDate = date; 583 584 if (fDateControl != NULL) { 585 BDateTimeFormat formatter; 586 587 BString string; 588 formatter.Format(string, date, B_FULL_DATE_FORMAT, 589 B_MEDIUM_TIME_FORMAT); 590 SetDate(string); 591 } 592 } 593 594 595 void 596 THeaderView::SetDate(const char* date) 597 { 598 if (fDateControl != NULL) 599 fDateControl->SetText(date); 600 } 601 602 603 int32 604 THeaderView::AccountID() const 605 { 606 return fAccountID; 607 } 608 609 610 const char* 611 THeaderView::AccountName() const 612 { 613 BMenuItem* menuItem = fAccountMenu->FindMarked(); 614 if (menuItem != NULL) 615 return menuItem->Label(); 616 617 return NULL; 618 } 619 620 621 void 622 THeaderView::SetAccount(int32 id) 623 { 624 fAccountID = id; 625 626 if (fAccountMenu == NULL) 627 return; 628 629 for (int32 i = fAccountMenu->CountItems(); i-- > 0;) { 630 BMenuItem* item = fAccountMenu->ItemAt(i); 631 if (item == NULL) 632 continue; 633 634 BMessage* message = item->Message(); 635 if (message->GetInt32("id", -1) == id) { 636 item->SetMarked(true); 637 break; 638 } 639 } 640 } 641 642 643 void 644 THeaderView::SetAccount(const char* name) 645 { 646 if (fAccountMenu == NULL) 647 return; 648 649 BMenuItem* item = fAccountMenu->FindItem(name); 650 if (item != NULL) { 651 item->SetMarked(true); 652 fAccountID = item->Message()->GetInt32("id", -1); 653 } 654 } 655 656 657 status_t 658 THeaderView::SetFromMessage(BEmailMessage* mail) 659 { 660 // Set Subject:, From:, To: & Cc: fields 661 SetSubject(mail->Subject()); 662 SetFrom(mail->From()); 663 SetTo(mail->To()); 664 SetCc(mail->CC()); 665 666 BString accountName; 667 if (mail->GetAccountName(accountName) == B_OK) 668 SetAccount(accountName); 669 670 // Set the date on this message 671 time_t date = mail->Date(); 672 if (date <= 0) { 673 const char* dateField = mail->HeaderField("Date"); 674 SetDate(dateField != NULL ? dateField : B_TRANSLATE("Unknown")); 675 } else 676 SetDate(date); 677 678 return B_OK; 679 } 680 681 682 void 683 THeaderView::MessageReceived(BMessage *msg) 684 { 685 switch (msg->what) { 686 case B_SIMPLE_DATA: 687 { 688 BTextView* textView = dynamic_cast<BTextView*>(Window()->CurrentFocus()); 689 if (dynamic_cast<AddressTextControl *>(textView->Parent()) != NULL) 690 textView->Parent()->MessageReceived(msg); 691 else { 692 BMessage message(*msg); 693 message.what = REFS_RECEIVED; 694 Window()->PostMessage(&message, Window()); 695 } 696 break; 697 } 698 699 case kMsgFrom: 700 { 701 BMenuItem *item; 702 if (msg->FindPointer("source", (void **)&item) >= B_OK) 703 item->SetMarked(true); 704 705 int32 account; 706 if (msg->FindInt32("id",(int32 *)&account) >= B_OK) 707 fAccountID = account; 708 709 BMessage message(FIELD_CHANGED); 710 // field doesn't matter; no special processing for this field 711 // it's just to turn on the save button 712 message.AddInt32("bitmask", 0); 713 Window()->PostMessage(&message, Window()); 714 break; 715 } 716 } 717 } 718 719 720 void 721 THeaderView::AttachedToWindow() 722 { 723 fToControl->SetTarget(Looper()); 724 fSubjectControl->SetTarget(Looper()); 725 fCcControl->SetTarget(Looper()); 726 if (fBccControl != NULL) 727 fBccControl->SetTarget(Looper()); 728 if (fAccountMenu != NULL) 729 fAccountMenu->SetTargetForItems(this); 730 731 BView::AttachedToWindow(); 732 } 733