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