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