xref: /haiku/src/preferences/mail/ConfigWindow.cpp (revision ac078a5b110045de12089052a685a6a080545db1)
1 /*
2  * Copyright 2007-2010, Haiku, Inc. All rights reserved.
3  * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 //! main E-Mail config window
10 #include <new>
11 #include <stdio.h>
12 #include <string.h>
13 
14 #include <Application.h>
15 #include <Catalog.h>
16 #include <ListView.h>
17 #include <ScrollView.h>
18 #include <StringView.h>
19 #include <Button.h>
20 #include <CheckBox.h>
21 #include <MenuField.h>
22 #include <TextControl.h>
23 #include <TextView.h>
24 #include <MenuItem.h>
25 #include <Screen.h>
26 #include <PopUpMenu.h>
27 #include <MenuBar.h>
28 #include <TabView.h>
29 #include <Box.h>
30 #include <Alert.h>
31 #include <Bitmap.h>
32 #include <Roster.h>
33 #include <Resources.h>
34 #include <Region.h>
35 
36 #include <AppFileInfo.h>
37 #include <Entry.h>
38 #include <Directory.h>
39 #include <FindDirectory.h>
40 #include <Path.h>
41 
42 #include <Catalog.h>
43 #include <Locale.h>
44 
45 #include <MailDaemon.h>
46 #include <MailSettings.h>
47 
48 #include "AutoConfigWindow.h"
49 #include "ConfigWindow.h"
50 #include "CenterContainer.h"
51 
52 #undef B_TRANSLATE_CONTEXT
53 #define B_TRANSLATE_CONTEXT "Config Window"
54 
55 
56 using std::nothrow;
57 
58 // define if you want to have an apply button
59 //#define HAVE_APPLY_BUTTON
60 
61 const char *kEMail = "bemaildaemon-talk@bug-br.org.br";
62 const char *kMailto = "mailto:bemaildaemon-talk@bug-br.org.br";
63 const char *kBugsitePretty = "Bug-Tracker at SourceForge.net";
64 const char *kBugsite = "http://sourceforge.net/tracker/?func=add&group_id=26926&atid=388726";
65 const char *kWebsite = "http://www.haiku-os.org";
66 const rgb_color kLinkColor = { 40, 40, 180, 255 };
67 
68 
69 const uint32 kMsgAccountsRightClicked = 'arcl';
70 const uint32 kMsgAccountSelected = 'acsl';
71 const uint32 kMsgAddAccount = 'adac';
72 const uint32 kMsgRemoveAccount = 'rmac';
73 
74 const uint32 kMsgIntervalUnitChanged = 'iuch';
75 
76 const uint32 kMsgShowStatusWindowChanged = 'shst';
77 const uint32 kMsgStatusLookChanged = 'lkch';
78 const uint32 kMsgStatusWorkspaceChanged = 'wsch';
79 
80 const uint32 kMsgSaveSettings = 'svst';
81 const uint32 kMsgRevertSettings = 'rvst';
82 const uint32 kMsgCancelSettings = 'cnst';
83 
84 
85 
86 AccountItem::AccountItem(const char* label, BMailAccountSettings* account,
87 	item_types type)
88 	:
89 	BStringItem(label),
90 	fAccount(account),
91 	fType(type),
92 	fConfigPanel(NULL)
93 {
94 }
95 
96 
97 void
98 AccountItem::Update(BView* owner, const BFont* font)
99 {
100 	if (fType == ACCOUNT_ITEM)
101 		font = be_bold_font;
102 
103 	BStringItem::Update(owner,font);
104 }
105 
106 
107 void
108 AccountItem::DrawItem(BView* owner, BRect rect, bool complete)
109 {
110 	owner->PushState();
111 	if (fType == ACCOUNT_ITEM)
112 		owner->SetFont(be_bold_font);
113 
114 	BStringItem::DrawItem(owner, rect, complete);
115 	owner->PopState();
116 }
117 
118 
119 void
120 AccountItem::SetConfigPanel(BView* panel)
121 {
122 	fConfigPanel = panel;
123 }
124 
125 
126 BView*
127 AccountItem::ConfigPanel()
128 {
129 	return fConfigPanel;
130 }
131 
132 
133 //	#pragma mark -
134 
135 
136 class AccountsListView : public BListView {
137 public:
138 	AccountsListView(BRect rect, BHandler* target)
139 		:
140 		BListView(rect, NULL, B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL),
141 		fTarget(target)
142 	{
143 	}
144 
145 	void
146 	KeyDown(const char *bytes, int32 numBytes)
147 	{
148 		if (numBytes != 1)
149 			return;
150 
151 		if ((*bytes == B_DELETE) || (*bytes == B_BACKSPACE))
152 			Window()->PostMessage(kMsgRemoveAccount);
153 
154 		BListView::KeyDown(bytes,numBytes);
155 	}
156 
157 	void
158 	MouseDown(BPoint point)
159 	{
160 		BListView::MouseDown(point);
161 
162 		BPoint dummy;
163 		uint32 buttons;
164 		GetMouse(&dummy, &buttons);
165 		if (buttons != B_SECONDARY_MOUSE_BUTTON)
166 			return;
167 
168 		int32 index = IndexOf(point);
169 		if (index < 0)
170 			return;
171 
172 		BMessage message(kMsgAccountsRightClicked);
173 		ConvertToScreen(&point);
174 		message.AddPoint("point", point);
175 		message.AddInt32("index", index);
176 		BMessenger messenger(fTarget);
177 		messenger.SendMessage(&message);
178 	}
179 
180 private:
181 			BHandler*			fTarget;
182 };
183 
184 
185 class BitmapView : public BView
186 {
187 	public:
188 		BitmapView(BBitmap *bitmap) : BView(bitmap->Bounds(), NULL,
189 			B_FOLLOW_NONE, B_WILL_DRAW)
190 		{
191 			fBitmap = bitmap;
192 
193 			SetDrawingMode(B_OP_ALPHA);
194 			SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
195 		}
196 
197 		~BitmapView()
198 		{
199 			delete fBitmap;
200 		}
201 
202 		virtual void AttachedToWindow()
203 		{
204 			SetViewColor(Parent()->ViewColor());
205 
206 			MoveTo((Parent()->Bounds().Width() - Bounds().Width()) / 2,
207 				Frame().top);
208 		}
209 
210 		virtual void Draw(BRect updateRect)
211 		{
212 			DrawBitmap(fBitmap, updateRect, updateRect);
213 		}
214 
215 	private:
216 		BBitmap *fBitmap;
217 };
218 
219 
220 class AboutTextView : public BTextView
221 {
222 	public:
223 		AboutTextView(BRect rect) : BTextView(rect, NULL,
224 			rect.OffsetToCopy(B_ORIGIN), B_FOLLOW_NONE, B_WILL_DRAW)
225 		{
226 			int32 major = 0,middle = 0,minor = 0,variety = 0,internal = 1;
227 
228 			// get version information for app
229 
230 			app_info appInfo;
231 			if (be_app->GetAppInfo(&appInfo) == B_OK) {
232 				BFile file(&appInfo.ref, B_READ_ONLY);
233 				if (file.InitCheck() == B_OK) {
234 					BAppFileInfo info(&file);
235 					if (info.InitCheck() == B_OK) {
236 						version_info versionInfo;
237 						if (info.GetVersionInfo(&versionInfo,
238 							B_APP_VERSION_KIND) == B_OK) {
239 							major = versionInfo.major;
240 							middle = versionInfo.middle;
241 							minor = versionInfo.minor;
242 							variety = versionInfo.variety;
243 							internal = versionInfo.internal;
244 						}
245 					}
246 				}
247 			}
248 			// prepare version variety string
249 			const char *varietyStrings[] = {B_TRANSLATE("Development"),
250 				B_TRANSLATE("Alpha"),B_TRANSLATE("Beta"),B_TRANSLATE("Gamma"),
251 				B_TRANSLATE("Golden master"),B_TRANSLATE("Final")};
252 			char varietyString[32];
253 			strcpy(varietyString,varietyStrings[variety % 6]);
254 			if (variety < 5)
255 				sprintf(varietyString + strlen(varietyString),"/%li",internal);
256 
257 			char s[512];
258 			sprintf(s,	B_TRANSLATE("Mail Daemon Replacement\n\n"
259 						"by Haiku, Inc. All rights reserved.\n\n"
260 						"Version %ld.%ld.%ld %s\n\n"
261 						"See LICENSE file included in the installation package "
262 						"for more information.\n\n\n\n"
263 						"You can contact us at:\n"
264 						"%s\n\n"
265 						"Please submit bug reports using the %s\n\n"
266 						"Project homepage at:\n%s"),
267 						major,middle,minor,varietyString,
268 						kEMail,kBugsitePretty,kWebsite);
269 
270 			SetText(s);
271 			MakeEditable(false);
272 			MakeSelectable(false);
273 
274 			SetAlignment(B_ALIGN_CENTER);
275 			SetStylable(true);
276 
277 			// aethetical changes
278 			BFont font;
279 			GetFont(&font);
280 			font.SetSize(24);
281 			SetFontAndColor(0,23,&font,B_FONT_SIZE);
282 
283 			// center the view vertically
284 			rect = TextRect();
285 			rect.OffsetTo(0,(Bounds().Height() - TextHeight(0,42)) / 2);
286 			SetTextRect(rect);
287 
288 			// set the link regions
289 			int start = strstr(s,kEMail) - s;
290 			int finish = start + strlen(kEMail);
291 			GetTextRegion(start,finish,&fMail);
292 			SetFontAndColor(start,finish,NULL,0,&kLinkColor);
293 
294 			start = strstr(s,kBugsitePretty) - s;
295 			finish = start + strlen(kBugsitePretty);
296 			GetTextRegion(start,finish,&fBugsite);
297 			SetFontAndColor(start,finish,NULL,0,&kLinkColor);
298 
299 			start = strstr(s,kWebsite) - s;
300 			finish = start + strlen(kWebsite);
301 			GetTextRegion(start,finish,&fWebsite);
302 			SetFontAndColor(start,finish,NULL,0,&kLinkColor);
303 		}
304 
305 		virtual void Draw(BRect updateRect)
306 		{
307 			BTextView::Draw(updateRect);
308 
309 			BRect rect(fMail.Frame());
310 			StrokeLine(BPoint(rect.left,rect.bottom-2),
311 				BPoint(rect.right,rect.bottom-2));
312 			rect = fBugsite.Frame();
313 			StrokeLine(BPoint(rect.left,rect.bottom-2),
314 				BPoint(rect.right,rect.bottom-2));
315 			rect = fWebsite.Frame();
316 			StrokeLine(BPoint(rect.left,rect.bottom-2),
317 				BPoint(rect.right,rect.bottom-2));
318 		}
319 
320 		virtual void MouseDown(BPoint point)
321 		{
322 			if (fMail.Contains(point)) {
323 				char *arg[] = {(char *)kMailto,NULL};
324 				be_roster->Launch("text/x-email",1,arg);
325 			} else if (fBugsite.Contains(point)) {
326 				char *arg[] = {(char *)kBugsite,NULL};
327 				be_roster->Launch("text/html",1,arg);
328 			} else if (fWebsite.Contains(point)) {
329 				char *arg[] = {(char *)kWebsite, NULL};
330 				be_roster->Launch("text/html", 1, arg);
331 			}
332 		}
333 
334 	private:
335 		BRegion	fWebsite,fMail,fBugsite;
336 };
337 
338 
339 //	#pragma mark -
340 
341 
342 ConfigWindow::ConfigWindow()
343 	:
344 	BWindow(BRect(100.0, 100.0, 580.0, 540.0), B_TRANSLATE_SYSTEM_NAME("E-mail"),
345 		B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_NOT_ZOOMABLE
346 		| B_NOT_RESIZABLE),
347 	fLastSelectedAccount(NULL),
348 	fSaveSettings(false)
349 {
350 	// create controls
351 	BRect rect(Bounds());
352 	BView *top = new BView(rect, NULL, B_FOLLOW_ALL, 0);
353 	top->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
354 	AddChild(top);
355 
356 	// determine font height
357 	font_height fontHeight;
358 	top->GetFontHeight(&fontHeight);
359 	int32 height = (int32)(fontHeight.ascent + fontHeight.descent
360 		+ fontHeight.leading) + 5;
361 
362 	rect.InsetBy(5, 5);	rect.bottom -= 11 + height;
363 	BTabView *tabView = new BTabView(rect, NULL);
364 
365 	BView *view;
366 	rect = tabView->Bounds();
367 	rect.bottom -= tabView->TabHeight() + 4;
368 	tabView->AddTab(view = new BView(rect, NULL, B_FOLLOW_ALL, 0));
369 	tabView->TabAt(0)->SetLabel(B_TRANSLATE("Accounts"));
370 	view->SetViewColor(top->ViewColor());
371 
372 	// accounts listview
373 
374 	rect = view->Bounds().InsetByCopy(8, 8);
375 	rect.right = 140 - B_V_SCROLL_BAR_WIDTH;
376 	rect.bottom -= height + 12;
377 	fAccountsListView = new AccountsListView(rect, this);
378 	view->AddChild(new BScrollView(NULL, fAccountsListView, B_FOLLOW_ALL, 0,
379 		false, true));
380 	rect.right += B_V_SCROLL_BAR_WIDTH;
381 
382 	rect.top = rect.bottom + 8;  rect.bottom = rect.top + height;
383 	BRect sizeRect = rect;
384 	sizeRect.right = sizeRect.left + 30 + view->StringWidth(
385 		B_TRANSLATE("Add"));
386 	view->AddChild(new BButton(sizeRect, NULL, B_TRANSLATE("Add"),
387 		new BMessage(kMsgAddAccount), B_FOLLOW_BOTTOM));
388 
389 	sizeRect.left = sizeRect.right+3;
390 	sizeRect.right = sizeRect.left + 30 + view->StringWidth(
391 		B_TRANSLATE("Remove"));
392 	view->AddChild(fRemoveButton = new BButton(
393 		sizeRect, NULL, B_TRANSLATE("Remove"),
394 		new BMessage(kMsgRemoveAccount), B_FOLLOW_BOTTOM));
395 
396 	// accounts config view
397 	rect = view->Bounds();
398 	rect.left = fAccountsListView->Frame().right + B_V_SCROLL_BAR_WIDTH + 16;
399 	rect.right -= 10;
400 	fConfigView = new CenterContainer(rect);
401 	view->AddChild(fConfigView);
402 
403 	_MakeHowToView();
404 
405 	// general settings
406 
407 	rect = tabView->Bounds();
408 	rect.bottom -= tabView->TabHeight() + 4;
409 	tabView->AddTab(view = new CenterContainer(rect));
410 	tabView->TabAt(1)->SetLabel(B_TRANSLATE("Settings"));
411 
412 	rect = view->Bounds().InsetByCopy(8, 8);
413 	rect.right -= 1;
414 	rect.bottom = rect.top + height * 5 + 15;
415 	BBox *box = new BBox(rect);
416 	box->SetLabel(B_TRANSLATE("Mail checking"));
417 	view->AddChild(box);
418 
419 	rect = box->Bounds().InsetByCopy(8, 8);
420 	rect.top += 7;
421 	rect.bottom = rect.top + height + 5;
422 	BRect tile = rect.OffsetByCopy(0, 1);
423 	int32 labelWidth = (int32)view->StringWidth(B_TRANSLATE("Check every")) + 6;
424 	tile.right = 80 + labelWidth;
425 	fIntervalControl = new BTextControl(tile, "time",
426 		B_TRANSLATE("Check every"), NULL, NULL);
427 	fIntervalControl->SetDivider(labelWidth);
428 	box->AddChild(fIntervalControl);
429 
430 	BPopUpMenu* frequencyPopUp = new BPopUpMenu(B_EMPTY_STRING);
431 	const char* frequencyStrings[] = {
432 		B_TRANSLATE("never"),
433 		B_TRANSLATE("minutes"),
434 		B_TRANSLATE("hours"),
435 		B_TRANSLATE("days")};
436 
437 	for (int32 i = 0; i < 4; i++) {
438 		BMenuItem* item = new BMenuItem(frequencyStrings[i],
439 			new BMessage(kMsgIntervalUnitChanged));
440 		frequencyPopUp->AddItem(item);
441 	}
442 	tile.left = tile.right + 5;
443 	tile.right = rect.right;
444 	tile.OffsetBy(0,-1);
445 	fIntervalUnitField = new BMenuField(tile, "frequency", B_EMPTY_STRING,
446 		frequencyPopUp);
447 	fIntervalUnitField->SetDivider(0.0);
448 	box->AddChild(fIntervalUnitField);
449 
450 	rect.OffsetBy(0,height + 9);
451 	rect.bottom -= 2;
452 	fPPPActiveCheckBox = new BCheckBox(rect, "ppp active",
453 		B_TRANSLATE("Only when dial-up is connected"), NULL);
454 	box->AddChild(fPPPActiveCheckBox);
455 
456 	rect.OffsetBy(0,height + 9);
457 	rect.bottom -= 2;
458 	fPPPActiveSendCheckBox = new BCheckBox(rect, "ppp activesend",
459 		B_TRANSLATE("Schedule outgoing mail when dial-up is disconnected"),
460 		NULL);
461 	box->AddChild(fPPPActiveSendCheckBox);
462 
463 	// Miscellaneous settings box
464 
465 	rect = box->Frame();  rect.bottom = rect.top + 3 * height + 30;
466 	box = new BBox(rect);
467 	box->SetLabel(B_TRANSLATE("Miscellaneous"));
468 	view->AddChild(box);
469 
470 	BPopUpMenu *statusPopUp = new BPopUpMenu(B_EMPTY_STRING);
471 	const char *statusModes[] = {
472 		B_TRANSLATE("Never"),
473 		B_TRANSLATE("While sending"),
474 		B_TRANSLATE("While sending and receiving"),
475 		B_TRANSLATE("Always")};
476 	for (int32 i = 0; i < 4; i++) {
477 		BMessage* msg = new BMessage(kMsgShowStatusWindowChanged);
478 		BMenuItem* item = new BMenuItem(statusModes[i], msg);
479 		statusPopUp->AddItem(item);
480 		msg->AddInt32("ShowStatusWindow", i);
481 	}
482 	rect = box->Bounds().InsetByCopy(8,8);
483 	rect.top += 7;
484 	rect.bottom = rect.top + height + 5;
485 	labelWidth = (int32)view->StringWidth(
486 		B_TRANSLATE("Show connection status window:"))	+ 8;
487 	fStatusModeField = new BMenuField(rect, "show status",
488 		B_TRANSLATE("Show connection status window:"), statusPopUp);
489 	fStatusModeField->SetDivider(labelWidth);
490 	box->AddChild(fStatusModeField);
491 
492 	rect = fStatusModeField->Frame();;
493 	rect.OffsetBy(0, rect.Height() + 10);
494 	BMessage* msg = new BMessage(B_REFS_RECEIVED);
495 	BButton *button = new BButton(rect, B_EMPTY_STRING,
496 		B_TRANSLATE("Edit mailbox menu…"), msg);
497 	button->ResizeToPreferred();
498 	box->AddChild(button);
499 	button->SetTarget(BMessenger("application/x-vnd.Be-TRAK"));
500 
501 	BPath path;
502 	find_directory(B_USER_SETTINGS_DIRECTORY, &path);
503 	path.Append("Mail/Menu Links");
504 	BEntry entry(path.Path());
505 	if (entry.InitCheck() == B_OK && entry.Exists()) {
506 		entry_ref ref;
507 		entry.GetRef(&ref);
508 		msg->AddRef("refs", &ref);
509 	}
510 	else
511 		button->SetEnabled(false);
512 
513 	rect = button->Frame();
514 	rect.OffsetBy(rect.Width() + 30,0);
515 	fAutoStartCheckBox = new BCheckBox(rect, "start daemon",
516 		B_TRANSLATE("Start mail services on startup"), NULL);
517 	fAutoStartCheckBox->ResizeToPreferred();
518 	box->AddChild(fAutoStartCheckBox);
519 
520 	// save/revert buttons
521 
522 	top->AddChild(tabView);
523 
524 	rect = tabView->Frame();
525 	rect.top = rect.bottom + 5;
526 	rect.bottom = rect.top + height + 5;
527 	BButton *saveButton = new BButton(rect, "apply", B_TRANSLATE("Apply"),
528 		new BMessage(kMsgSaveSettings));
529 	float w,h;
530 	saveButton->GetPreferredSize(&w, &h);
531 	saveButton->ResizeTo(w, h);
532 	saveButton->MoveTo(rect.right - w, rect.top);
533 	top->AddChild(saveButton);
534 
535 	BButton *revertButton = new BButton(rect, "revert", B_TRANSLATE("Revert"),
536 		new BMessage(kMsgRevertSettings));
537 	revertButton->GetPreferredSize(&w, &h);
538 	revertButton->ResizeTo(w,h);
539 	revertButton->MoveTo(saveButton->Frame().left - 25 - w, rect.top);
540 	top->AddChild(revertButton);
541 
542 	_LoadSettings();
543 		// this will also move our window to the stored position
544 
545 	fAccountsListView->SetSelectionMessage(new BMessage(kMsgAccountSelected));
546 	fAccountsListView->MakeFocus(true);
547 }
548 
549 
550 ConfigWindow::~ConfigWindow()
551 {
552 	while (fAccounts.CountItems() > 0)
553 		_RemoveAccount(fAccounts.ItemAt(0));
554 	for (int32 i = 0; i < fToDeleteAccounts.CountItems(); i++)
555 		delete fToDeleteAccounts.ItemAt(i);
556 }
557 
558 
559 void
560 ConfigWindow::_MakeHowToView()
561 {
562 	app_info info;
563 	if (be_app->GetAppInfo(&info) == B_OK) {
564 		BFile appFile(&info.ref, B_READ_ONLY);
565 		BAppFileInfo appFileInfo(&appFile);
566 		if (appFileInfo.InitCheck() == B_OK) {
567 			BBitmap *bitmap = new (nothrow) BBitmap(BRect(0, 0, 63, 63),
568 				B_RGBA32);
569 			if (appFileInfo.GetIcon(bitmap, B_LARGE_ICON) == B_OK) {
570 				fConfigView->AddChild(new BitmapView(bitmap));
571 			} else
572 				delete bitmap;
573 		}
574 	}
575 
576 	BRect rect = fConfigView->Bounds();
577 	BTextView *text = new BTextView(rect, NULL, rect, B_FOLLOW_NONE,
578 		B_WILL_DRAW);
579 	text->SetViewColor(fConfigView->Parent()->ViewColor());
580 	text->SetAlignment(B_ALIGN_CENTER);
581 	text->SetText(B_TRANSLATE(
582 		"\n\nCreate a new account with the Add button.\n\n"
583 		"Remove an account with the Remove button on the selected item.\n\n"
584 		"Select an item in the list to change its settings."));
585 	rect = text->Bounds();
586 	text->ResizeTo(rect.Width(), text->TextHeight(0, 42));
587 	text->SetTextRect(rect);
588 
589 	text->MakeEditable(false);
590 	text->MakeSelectable(false);
591 
592 	fConfigView->AddChild(text);
593 
594 	fConfigView->Layout();
595 }
596 
597 
598 void
599 ConfigWindow::_LoadSettings()
600 {
601 	// load accounts
602 	for (int i = 0; i < fAccounts.CountItems(); i++)
603 		delete fAccounts.ItemAt(i);
604 	fAccounts.MakeEmpty();
605 
606 	_LoadAccounts();
607 
608 	// load in general settings
609 	BMailSettings settings;
610 	status_t status = _SetToGeneralSettings(&settings);
611 	if (status == B_OK) {
612 		// move own window
613 		MoveTo(settings.ConfigWindowFrame().LeftTop());
614 	} else {
615 		fprintf(stderr, B_TRANSLATE("Error retrieving general settings: %s\n"),
616 			strerror(status));
617 	}
618 
619 	BScreen screen(this);
620 	BRect screenFrame(screen.Frame().InsetByCopy(0, 5));
621 	if (!screenFrame.Contains(Frame().LeftTop())
622 		|| !screenFrame.Contains(Frame().RightBottom()))
623 		status = B_ERROR;
624 
625 	if (status != B_OK) {
626 		// center window on screen
627 		MoveTo((screenFrame.Width() - Frame().Width()) / 2,
628 			(screenFrame.Height() - Frame().Height()) / 2);
629 	}
630 }
631 
632 
633 void
634 ConfigWindow::_LoadAccounts()
635 {
636 	BMailAccounts accounts;
637 	for (int32 i = 0; i < accounts.CountAccounts(); i++)
638 		fAccounts.AddItem(new BMailAccountSettings(*accounts.AccountAt(i)));
639 
640 	for (int i = 0; i < fAccounts.CountItems(); i++) {
641 		BMailAccountSettings* account = fAccounts.ItemAt(i);
642 		_AddAccountToView(account);
643 	}
644 }
645 
646 
647 void
648 ConfigWindow::_SaveSettings()
649 {
650 	// remove config views (trigger view archive)
651 	fConfigView->DeleteChildren();
652 
653 	// collect changed accounts
654 	BMessage changedAccounts(kMsgAccountsChanged);
655 	for (int32 i = 0; i < fAccounts.CountItems(); i++) {
656 		BMailAccountSettings* account = fAccounts.ItemAt(i);
657 		if (account && account->HasBeenModified())
658 			changedAccounts.AddInt32("account", account->AccountID());
659 	}
660 	for (int32 i = 0; i < fToDeleteAccounts.CountItems(); i++) {
661 		BMailAccountSettings* account = fToDeleteAccounts.ItemAt(i);
662 		changedAccounts.AddInt32("account", account->AccountID());
663 	}
664 
665 	// cleanup account directory
666 	for (int32 i = 0; i < fToDeleteAccounts.CountItems(); i++) {
667 		BMailAccountSettings* account = fToDeleteAccounts.ItemAt(i);
668 		BEntry entry(account->AccountFile());
669 		entry.Remove();
670 		delete account;
671 	}
672 	fToDeleteAccounts.MakeEmpty();
673 
674 	/*** save general settings ***/
675 
676 	// figure out time interval
677 	float interval;
678 	sscanf(fIntervalControl->Text(),"%f",&interval);
679 	float multiplier = 0;
680 	switch (fIntervalUnitField->Menu()->IndexOf(
681 			fIntervalUnitField->Menu()->FindMarked())) {
682 		case 1:		// minutes
683 			multiplier = 60;
684 			break;
685 		case 2:		// hours
686 			multiplier = 60 * 60;
687 			break;
688 		case 3:		// days
689 			multiplier = 24 * 60 * 60;
690 			break;
691 	}
692 	time_t time = (time_t)(multiplier * interval);
693 
694 	// apply and save general settings
695 	BMailSettings settings;
696 	if (fSaveSettings) {
697 		settings.SetAutoCheckInterval(time * 1e6);
698 		settings.SetCheckOnlyIfPPPUp(fPPPActiveCheckBox->Value()
699 			== B_CONTROL_ON);
700 		settings.SetSendOnlyIfPPPUp(fPPPActiveSendCheckBox->Value()
701 			== B_CONTROL_ON);
702 		settings.SetDaemonAutoStarts(fAutoStartCheckBox->Value()
703 			== B_CONTROL_ON);
704 
705 		// status mode (alway, fetching/retrieving, ...)
706 		int32 index = fStatusModeField->Menu()->IndexOf(
707 			fStatusModeField->Menu()->FindMarked());
708 		settings.SetShowStatusWindow(index);
709 
710 	} else {
711 		// restore status window look
712 		settings.SetStatusWindowLook(settings.StatusWindowLook());
713 	}
714 
715 	settings.SetConfigWindowFrame(Frame());
716 	settings.Save();
717 
718 	/*** save accounts ***/
719 
720 	if (fSaveSettings) {
721 		for (int i = 0; i < fAccounts.CountItems(); i++)
722 			fAccounts.ItemAt(i)->Save();
723 	}
724 
725 	BMessenger messenger("application/x-vnd.Be-POST");
726 	if (messenger.IsValid()) {
727 		// server should reload general settings
728 		messenger.SendMessage(kMsgSettingsUpdated);
729 		// notify server about changed accounts
730 		messenger.SendMessage(&changedAccounts);
731 	}
732 
733 	// start the mail_daemon if auto start was selected
734 	if (fSaveSettings && fAutoStartCheckBox->Value() == B_CONTROL_ON
735 		&& !be_roster->IsRunning("application/x-vnd.Be-POST"))
736 	{
737 		be_roster->Launch("application/x-vnd.Be-POST");
738 	}
739 }
740 
741 
742 bool
743 ConfigWindow::QuitRequested()
744 {
745 	_SaveSettings();
746 
747 	be_app->PostMessage(B_QUIT_REQUESTED);
748 	return true;
749 }
750 
751 
752 void
753 ConfigWindow::MessageReceived(BMessage *msg)
754 {
755 	BRect autoConfigRect(0, 0, 400, 300);
756 	BRect frame;
757 
758 	AutoConfigWindow *autoConfigWindow = NULL;
759 	switch (msg->what) {
760 		case kMsgAccountsRightClicked:
761 		{
762 			BPoint point;
763 			msg->FindPoint("point", &point);
764 			int32 index = msg->FindInt32("index");
765 			AccountItem* clickedItem = dynamic_cast<AccountItem*>(
766 				fAccountsListView->ItemAt(index));
767 			if (clickedItem == NULL || clickedItem->GetType() != ACCOUNT_ITEM)
768 				break;
769 
770 			BPopUpMenu rightClickMenu("accounts", false, false);
771 
772 			BMenuItem* inMenuItem = new BMenuItem("Incoming", NULL);
773 			BMenuItem* outMenuItem = new BMenuItem("Outgoing", NULL);
774 			rightClickMenu.AddItem(inMenuItem);
775 			rightClickMenu.AddItem(outMenuItem);
776 
777 			BMailAccountSettings* settings = clickedItem->GetAccount();
778 			if (settings->IsInboundEnabled())
779 				inMenuItem->SetMarked(true);
780 			if (settings->IsOutboundEnabled())
781 				outMenuItem->SetMarked(true);
782 
783 			BMenuItem* selectedItem = rightClickMenu.Go(point);
784 			if (selectedItem == NULL)
785 				break;
786 			if (selectedItem == inMenuItem) {
787 				AccountItem* item = dynamic_cast<AccountItem*>(
788 					fAccountsListView->ItemAt(index + 1));
789 				if (item == NULL)
790 					break;
791 				if (settings->IsInboundEnabled()) {
792 					settings->SetInboundEnabled(false);
793 					item->SetEnabled(false);
794 				} else {
795 					settings->SetInboundEnabled(true);
796 					item->SetEnabled(true);
797 				}
798 			} else {
799 				AccountItem* item = dynamic_cast<AccountItem*>(
800 					fAccountsListView->ItemAt(index + 2));
801 				if (item == NULL)
802 					break;
803 				if (settings->IsOutboundEnabled()) {
804 					settings->SetOutboundEnabled(false);
805 					item->SetEnabled(false);
806 				} else {
807 					settings->SetOutboundEnabled(true);
808 					item->SetEnabled(true);
809 				}
810 			}
811 		}
812 
813 		case kMsgAccountSelected:
814 		{
815 			int32 index;
816 			if (msg->FindInt32("index", &index) != B_OK || index < 0) {
817 				// deselect current item
818 				fConfigView->DeleteChildren();
819 				_MakeHowToView();
820 				break;
821 			}
822 			AccountItem *item = (AccountItem *)fAccountsListView->ItemAt(index);
823 			if (item)
824 				_AccountSelected(item);
825 			break;
826 		}
827 
828 		case kMsgAddAccount:
829 		{
830 			frame = Frame();
831 			autoConfigRect.OffsetTo(
832 				frame.left + (frame.Width() - autoConfigRect.Width()) / 2,
833 				frame.top + (frame.Width() - autoConfigRect.Height()) / 2);
834 			autoConfigWindow = new AutoConfigWindow(autoConfigRect, this);
835 			autoConfigWindow->Show();
836 			break;
837 		}
838 
839 		case kMsgRemoveAccount:
840 		{
841 			int32 index = fAccountsListView->CurrentSelection();
842 			if (index >= 0) {
843 				AccountItem *item = (AccountItem *)fAccountsListView->ItemAt(
844 					index);
845 				if (item != NULL) {
846 					_RemoveAccount(item->GetAccount());
847 					_MakeHowToView();
848 				}
849 			}
850 			break;
851 		}
852 
853 		case kMsgIntervalUnitChanged:
854 		{
855 			int32 index;
856 			if (msg->FindInt32("index",&index) == B_OK)
857 				fIntervalControl->SetEnabled(index != 0);
858 			break;
859 		}
860 
861 		case kMsgShowStatusWindowChanged:
862 		{
863 			// the status window stuff is the only "live" setting
864 			BMessenger messenger("application/x-vnd.Be-POST");
865 			if (messenger.IsValid())
866 				messenger.SendMessage(msg);
867 			break;
868 		}
869 
870 		case kMsgRevertSettings:
871 			_RevertToLastSettings();
872 			break;
873 
874 		case kMsgSaveSettings:
875 			fSaveSettings = true;
876 			_SaveSettings();
877 			AccountUpdated(fLastSelectedAccount);
878 			_MakeHowToView();
879 			fAccountsListView->DeselectAll();
880 			break;
881 
882 		default:
883 			BWindow::MessageReceived(msg);
884 			break;
885 	}
886 }
887 
888 
889 BMailAccountSettings*
890 ConfigWindow::AddAccount()
891 {
892 	BMailAccountSettings* account = new BMailAccountSettings;
893 	if (!account)
894 		return NULL;
895 	fAccounts.AddItem(account);
896 	_AddAccountToView(account);
897 	return account;
898 }
899 
900 
901 void
902 ConfigWindow::AccountUpdated(BMailAccountSettings* account)
903 {
904 	if (account == NULL)
905 		return;
906 
907 	for (int i = 0; i < fAccountsListView->CountItems(); i++) {
908 		AccountItem* item = (AccountItem*)fAccountsListView->ItemAt(i);
909 		if (item->GetAccount() == account) {
910 			if (item->GetType() == ACCOUNT_ITEM) {
911 				item->SetText(account->Name());
912 				fAccountsListView->Invalidate();
913 			}
914 		}
915 	}
916 }
917 
918 
919 status_t
920 ConfigWindow::_SetToGeneralSettings(BMailSettings *settings)
921 {
922 	if (!settings)
923 		return B_BAD_VALUE;
924 
925 	status_t status = settings->InitCheck();
926 	if (status != B_OK)
927 		return status;
928 
929 	// retrieval frequency
930 
931 	time_t interval = time_t(settings->AutoCheckInterval() / 1e6L);
932 	char text[25];
933 	text[0] = 0;
934 	int timeIndex = 0;
935 	if (interval >= 60) {
936 		timeIndex = 1;
937 		sprintf(text, "%ld", interval / (60));
938 	}
939 	if (interval >= (60*60)) {
940 		timeIndex = 2;
941 		sprintf(text, "%ld", interval / (60*60));
942 	}
943 	if (interval >= (60*60*24)) {
944 		timeIndex = 3;
945 		sprintf(text, "%ld", interval / (60*60*24));
946 	}
947 	fIntervalControl->SetText(text);
948 
949 	if (BMenuItem *item = fIntervalUnitField->Menu()->ItemAt(timeIndex))
950 		item->SetMarked(true);
951 	fIntervalControl->SetEnabled(timeIndex != 0);
952 
953 	fPPPActiveCheckBox->SetValue(settings->CheckOnlyIfPPPUp());
954 	fPPPActiveSendCheckBox->SetValue(settings->SendOnlyIfPPPUp());
955 
956 	fAutoStartCheckBox->SetValue(settings->DaemonAutoStarts());
957 
958 	int32 showStatusIndex = settings->ShowStatusWindow();
959 	BMenuItem *item = fStatusModeField->Menu()->ItemAt(showStatusIndex);
960 	if (item) {
961 		item->SetMarked(true);
962 		// send live update to the server by simulating a menu click
963 		BMessage msg(kMsgShowStatusWindowChanged);
964 		msg.AddInt32("ShowStatusWindow", showStatusIndex);
965 		PostMessage(&msg);
966 	}
967 
968 	return B_OK;
969 }
970 
971 
972 void
973 ConfigWindow::_RevertToLastSettings()
974 {
975 	// revert general settings
976 	BMailSettings settings;
977 
978 	// restore status window look
979 	settings.SetStatusWindowLook(settings.StatusWindowLook());
980 
981 	status_t status = _SetToGeneralSettings(&settings);
982 	if (status != B_OK) {
983 		char text[256];
984 		sprintf(text, B_TRANSLATE(
985 				"\nThe general settings couldn't be reverted.\n\n"
986 				"Error retrieving general settings:\n%s\n"),
987 			strerror(status));
988 		(new BAlert(B_TRANSLATE("Error"), text, B_TRANSLATE("OK"), NULL, NULL,
989 			B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go();
990 	}
991 
992 	// revert account data
993 
994 	if (fAccountsListView->CurrentSelection() != -1)
995 		fConfigView->DeleteChildren();
996 
997 	for (int32 i = 0; i < fAccounts.CountItems(); i++) {
998 		BMailAccountSettings* account = fAccounts.ItemAt(i);
999 		_RemoveAccountFromListView(account);
1000 		delete account;
1001 	}
1002 	fAccounts.MakeEmpty();
1003 	_LoadAccounts();
1004 
1005 	if (fConfigView->CountChildren() == 0)
1006 		_MakeHowToView();
1007 }
1008 
1009 
1010 void
1011 ConfigWindow::_AddAccountToView(BMailAccountSettings* account)
1012 {
1013 	BString label;
1014 	label << account->Name();
1015 
1016 	AccountItem* item;
1017 	item = new AccountItem(label, account, ACCOUNT_ITEM);
1018 	fAccountsListView->AddItem(item);
1019 
1020 	item = new AccountItem(B_TRANSLATE("   · Incoming"), account, INBOUND_ITEM);
1021 	fAccountsListView->AddItem(item);
1022 	if (!account->IsInboundEnabled())
1023 		item->SetEnabled(false);
1024 
1025 	item = new AccountItem(B_TRANSLATE("   · Outgoing"), account,
1026 		OUTBOUND_ITEM);
1027 	fAccountsListView->AddItem(item);
1028 	if (!account->IsOutboundEnabled())
1029 		item->SetEnabled(false);
1030 
1031 	item = new AccountItem(B_TRANSLATE("   · E-mail filters"), account,
1032 		FILTER_ITEM);
1033 	fAccountsListView->AddItem(item);
1034 }
1035 
1036 
1037 void
1038 ConfigWindow::_RemoveAccount(BMailAccountSettings* account)
1039 {
1040 	_RemoveAccountFromListView(account);
1041 	fAccounts.RemoveItem(account);
1042 	fToDeleteAccounts.AddItem(account);
1043 }
1044 
1045 
1046 void
1047 ConfigWindow::_RemoveAccountFromListView(BMailAccountSettings* account)
1048 {
1049 	for (int i = 0; i < fAccountsListView->CountItems(); i++) {
1050 		AccountItem* item = (AccountItem*)fAccountsListView->ItemAt(i);
1051 		if (item->GetAccount() == account) {
1052 			fAccountsListView->RemoveItem(i);
1053 			fConfigView->RemoveChild(item->ConfigPanel());
1054 			delete item;
1055 			i--;
1056 		}
1057 	}
1058 }
1059 
1060 
1061 void
1062 ConfigWindow::_AccountSelected(AccountItem* item)
1063 {
1064 	AccountUpdated(fLastSelectedAccount);
1065 
1066 	BMailAccountSettings* account = item->GetAccount();
1067 	fLastSelectedAccount = account;
1068 
1069 	fConfigView->Hide();
1070 	fConfigView->DeleteChildren();
1071 
1072 	BView* view = NULL;
1073 	switch (item->GetType()) {
1074 		case ACCOUNT_ITEM:
1075 			view = new AccountConfigView(fConfigView->Bounds(), account);
1076 			break;
1077 
1078 		case INBOUND_ITEM:
1079 		{
1080 			view = new InProtocolsConfigView(account);
1081 			break;
1082 		}
1083 
1084 		case OUTBOUND_ITEM:
1085 		{
1086 			view = new OutProtocolsConfigView(account);
1087 			break;
1088 		}
1089 
1090 		case FILTER_ITEM:
1091 		{
1092 			view = new FiltersConfigView(fConfigView->Bounds(), *account);
1093 			break;
1094 		}
1095 	}
1096 	if (view) {
1097 		item->SetConfigPanel(view);
1098 		fConfigView->AddChild(view);
1099 	}
1100 
1101 	fConfigView->Layout();
1102 	fConfigView->Show();
1103 }
1104