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