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