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(B_USE_HALF_ITEM_SPACING); 408 409 int32 row = 0; 410 if (fromField != NULL) { 411 layout->AddItem(fromField->CreateLabelLayoutItem(), 0, row); 412 layout->AddItem(fromField->CreateMenuBarLayoutItem(), 1, row++, 3, 1); 413 } else if (fFromControl != NULL) { 414 layout->AddItem(fFromControl->CreateLabelLayoutItem(), 0, row); 415 layout->AddItem(fFromControl->CreateTextViewLayoutItem(), 1, row++, 416 3, 1); 417 } 418 419 layout->AddView(fToLabel, 0, row); 420 layout->AddView(fToControl, 1, row++, 3, 1); 421 layout->AddView(fCcLabel, 0, row); 422 layout->AddView(fCcControl, 1, row, fIncoming ? 3 : 1, 1); 423 if (fBccControl != NULL) { 424 layout->AddView(new LabelView(B_TRANSLATE("Bcc:")), 2, row); 425 layout->AddView(fBccControl, 3, row++); 426 } else 427 row++; 428 layout->AddItem(fSubjectControl->CreateLabelLayoutItem(), 0, row); 429 layout->AddItem(fSubjectControl->CreateTextViewLayoutItem(), 1, row++, 430 3, 1); 431 432 if (fDateControl != NULL) { 433 layout->AddItem(fDateControl->CreateLabelLayoutItem(), 0, row); 434 layout->AddItem(fDateControl->CreateTextViewLayoutItem(), 1, row++, 435 3, 1); 436 } 437 } 438 439 440 const char* 441 THeaderView::From() const 442 { 443 return fFromControl != NULL ? fFromControl->Text() : NULL; 444 } 445 446 447 void 448 THeaderView::SetFrom(const char* from) 449 { 450 if (fFromControl != NULL) 451 fFromControl->SetText(from); 452 } 453 454 455 bool 456 THeaderView::IsToEmpty() const 457 { 458 return To() == NULL || To()[0] == '\0'; 459 } 460 461 462 const char* 463 THeaderView::To() const 464 { 465 return fToControl->Text(); 466 } 467 468 469 void 470 THeaderView::SetTo(const char* to) 471 { 472 fToControl->SetText(to); 473 } 474 475 476 bool 477 THeaderView::IsCcEmpty() const 478 { 479 return Cc() == NULL || Cc()[0] == '\0'; 480 } 481 482 483 const char* 484 THeaderView::Cc() const 485 { 486 return fCcControl != NULL ? fCcControl->Text() : NULL; 487 } 488 489 490 void 491 THeaderView::SetCc(const char* cc) 492 { 493 fCcControl->SetText(cc); 494 495 if (fIncoming) { 496 if (cc != NULL && cc[0] != '\0') { 497 if (fCcControl->IsHidden(this)) { 498 fCcControl->Show(); 499 fCcLabel->Show(); 500 } 501 } else if (!fCcControl->IsHidden(this)) { 502 fCcControl->Hide(); 503 fCcLabel->Hide(); 504 } 505 } 506 } 507 508 509 bool 510 THeaderView::IsBccEmpty() const 511 { 512 return Bcc() == NULL || Bcc()[0] == '\0'; 513 } 514 515 516 const char* 517 THeaderView::Bcc() const 518 { 519 return fBccControl != NULL ? fBccControl->Text() : NULL; 520 } 521 522 523 void 524 THeaderView::SetBcc(const char* bcc) 525 { 526 if (fBccControl != NULL) 527 fBccControl->SetText(bcc); 528 } 529 530 531 bool 532 THeaderView::IsSubjectEmpty() const 533 { 534 return Subject() == NULL || Subject()[0] == '\0'; 535 } 536 537 538 const char* 539 THeaderView::Subject() const 540 { 541 return fSubjectControl->Text(); 542 } 543 544 545 void 546 THeaderView::SetSubject(const char* subject) 547 { 548 fSubjectControl->SetText(subject); 549 } 550 551 552 bool 553 THeaderView::IsDateEmpty() const 554 { 555 return Date() == NULL || Date()[0] == '\0'; 556 } 557 558 559 const char* 560 THeaderView::Date() const 561 { 562 return fDateControl != NULL ? fDateControl->Text() : NULL; 563 } 564 565 566 void 567 THeaderView::SetDate(time_t date) 568 { 569 fDate = date; 570 571 if (fDateControl != NULL) { 572 BDateTimeFormat formatter; 573 574 BString string; 575 formatter.Format(string, date, B_FULL_DATE_FORMAT, 576 B_MEDIUM_TIME_FORMAT); 577 SetDate(string); 578 } 579 } 580 581 582 void 583 THeaderView::SetDate(const char* date) 584 { 585 if (fDateControl != NULL) 586 fDateControl->SetText(date); 587 } 588 589 590 int32 591 THeaderView::AccountID() const 592 { 593 return fAccountID; 594 } 595 596 597 const char* 598 THeaderView::AccountName() const 599 { 600 BMenuItem* menuItem = fAccountMenu->FindMarked(); 601 if (menuItem != NULL) 602 return menuItem->Label(); 603 604 return NULL; 605 } 606 607 608 void 609 THeaderView::SetAccount(int32 id) 610 { 611 fAccountID = id; 612 613 if (fAccountMenu == NULL) 614 return; 615 616 for (int32 i = fAccountMenu->CountItems(); i-- > 0;) { 617 BMenuItem* item = fAccountMenu->ItemAt(i); 618 if (item == NULL) 619 continue; 620 621 BMessage* message = item->Message(); 622 if (message->GetInt32("id", -1) == id) { 623 item->SetMarked(true); 624 break; 625 } 626 } 627 } 628 629 630 void 631 THeaderView::SetAccount(const char* name) 632 { 633 if (fAccountMenu == NULL) 634 return; 635 636 BMenuItem* item = fAccountMenu->FindItem(name); 637 if (item != NULL) { 638 item->SetMarked(true); 639 fAccountID = item->Message()->GetInt32("id", -1); 640 } 641 } 642 643 644 status_t 645 THeaderView::SetFromMessage(BEmailMessage* mail) 646 { 647 // Set Subject:, From:, To: & Cc: fields 648 SetSubject(mail->Subject()); 649 SetFrom(mail->From()); 650 SetTo(mail->To()); 651 SetCc(mail->CC()); 652 653 BString accountName; 654 if (mail->GetAccountName(accountName) == B_OK) 655 SetAccount(accountName); 656 657 // Set the date on this message 658 time_t date = mail->Date(); 659 if (date <= 0) { 660 const char* dateField = mail->HeaderField("Date"); 661 SetDate(dateField != NULL ? dateField : B_TRANSLATE("Unknown")); 662 } else 663 SetDate(date); 664 665 return B_OK; 666 } 667 668 669 void 670 THeaderView::MessageReceived(BMessage *msg) 671 { 672 switch (msg->what) { 673 case B_SIMPLE_DATA: 674 { 675 BTextView* textView = dynamic_cast<BTextView*>(Window()->CurrentFocus()); 676 if (dynamic_cast<AddressTextControl *>(textView->Parent()) != NULL) 677 textView->Parent()->MessageReceived(msg); 678 else { 679 BMessage message(*msg); 680 message.what = REFS_RECEIVED; 681 Window()->PostMessage(&message, Window()); 682 } 683 break; 684 } 685 686 case kMsgFrom: 687 { 688 BMenuItem *item; 689 if (msg->FindPointer("source", (void **)&item) >= B_OK) 690 item->SetMarked(true); 691 692 int32 account; 693 if (msg->FindInt32("id",(int32 *)&account) >= B_OK) 694 fAccountID = account; 695 696 BMessage message(FIELD_CHANGED); 697 // field doesn't matter; no special processing for this field 698 // it's just to turn on the save button 699 message.AddInt32("bitmask", 0); 700 Window()->PostMessage(&message, Window()); 701 break; 702 } 703 } 704 } 705 706 707 void 708 THeaderView::AttachedToWindow() 709 { 710 fToControl->SetTarget(Looper()); 711 fSubjectControl->SetTarget(Looper()); 712 fCcControl->SetTarget(Looper()); 713 if (fBccControl != NULL) 714 fBccControl->SetTarget(Looper()); 715 if (fAccountMenu != NULL) 716 fAccountMenu->SetTargetForItems(this); 717 718 BView::AttachedToWindow(); 719 } 720