xref: /haiku/src/apps/mail/Header.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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