xref: /haiku/src/add-ons/mail_daemon/inbound_protocols/imap/FolderConfigWindow.cpp (revision 46511867cc2dfa2699701c6aaa183ab7734e42b9)
1 /*
2  * Copyright 2011-2013, Haiku, Inc. All rights reserved.
3  * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include "FolderConfigWindow.h"
9 
10 #include <Alert.h>
11 #include <Button.h>
12 #include <Catalog.h>
13 #include <ControlLook.h>
14 #include <LayoutBuilder.h>
15 #include <ListItem.h>
16 #include <ScrollView.h>
17 #include <SpaceLayoutItem.h>
18 #include <StringView.h>
19 
20 #include <StringForSize.h>
21 
22 #include "Settings.h"
23 #include "Utilities.h"
24 
25 
26 #undef B_TRANSLATION_CONTEXT
27 #define B_TRANSLATION_CONTEXT "IMAPFolderConfig"
28 
29 
30 class EditableListItem {
31 public:
32 								EditableListItem();
~EditableListItem()33 	virtual						~EditableListItem() {}
34 
35 	virtual void				MouseDown(BPoint where) = 0;
36 	virtual	void				MouseUp(BPoint where) = 0;
37 
SetListView(BListView * list)38 			void				SetListView(BListView* list)
39 									{ fListView = list; }
40 
41 protected:
42 			BListView*			fListView;
43 };
44 
45 
46 class CheckBoxItem : public BStringItem, public EditableListItem {
47 public:
48 								CheckBoxItem(const char* text, bool checked);
49 
50 			void				DrawItem(BView* owner, BRect itemRect,
51 									bool drawEverything = false);
52 
53 			void				MouseDown(BPoint where);
54 			void				MouseUp(BPoint where);
55 
Checked()56 			bool				Checked() { return fChecked; }
57 private:
58 			BRect				fBoxRect;
59 			bool				fChecked;
60 			bool				fMouseDown;
61 };
62 
63 
64 class EditListView : public BListView {
65 public:
66 								EditListView(const char* name,
67 									list_view_type type
68 										= B_SINGLE_SELECTION_LIST,
69 									uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS
70 										| B_NAVIGABLE);
71 
72 	virtual void				MouseDown(BPoint where);
73 	virtual void				MouseUp(BPoint where);
74 	virtual void				FrameResized(float newWidth, float newHeight);
75 
76 private:
77 			EditableListItem*	fLastMouseDown;
78 };
79 
80 
81 class StatusWindow : public BWindow {
82 public:
StatusWindow(BWindow * parent,const char * text)83 	StatusWindow(BWindow* parent, const char* text)
84 		:
85 		BWindow(BRect(0, 0, 10, 10), B_TRANSLATE("status"), B_MODAL_WINDOW_LOOK,
86 			B_MODAL_APP_WINDOW_FEEL, B_NO_WORKSPACE_ACTIVATION | B_NOT_ZOOMABLE
87 				| B_AVOID_FRONT | B_NOT_RESIZABLE | B_NOT_ZOOMABLE
88 				| B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
89 	{
90 		BLayoutBuilder::Group<>(this)
91 			.SetInsets(B_USE_DEFAULT_SPACING)
92 			.Add(new BStringView("text", text));
93 		CenterIn(parent->Frame());
94 	}
95 };
96 
97 
98 const uint32 kMsgApplyButton = '&Abu';
99 const uint32 kMsgInit = '&Ini';
100 
101 
102 // #pragma mark -
103 
104 
EditableListItem()105 EditableListItem::EditableListItem()
106 	:
107 	fListView(NULL)
108 {
109 
110 }
111 
112 
113 // #pragma mark -
114 
115 
CheckBoxItem(const char * text,bool checked)116 CheckBoxItem::CheckBoxItem(const char* text, bool checked)
117 	:
118 	BStringItem(text),
119 	fChecked(checked),
120 	fMouseDown(false)
121 {
122 }
123 
124 
125 void
DrawItem(BView * owner,BRect itemRect,bool drawEverything)126 CheckBoxItem::DrawItem(BView* owner, BRect itemRect, bool drawEverything)
127 {
128 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
129 	rgb_color lowColor = owner->LowColor();
130 	uint32 flags = 0;
131 	if (fMouseDown)
132 		flags |= BControlLook::B_CLICKED;
133 	if (fChecked)
134 		flags |= BControlLook::B_ACTIVATED;
135 
136 	font_height fontHeight;
137 	owner->GetFontHeight(&fontHeight);
138 	BRect boxRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent),
139 		ceilf(5.0f + fontHeight.ascent));
140 
141 	owner->PushState();
142 
143 	float left = itemRect.left;
144 	fBoxRect.left = left + 3;
145 	fBoxRect.top = itemRect.top + (itemRect.Height() - boxRect.Height()) / 2;
146 	fBoxRect.right = fBoxRect.left + boxRect.Width();
147 	fBoxRect.bottom = itemRect.top + boxRect.Height();
148 
149 	itemRect.left = fBoxRect.right + be_control_look->DefaultLabelSpacing();
150 
151 	if (IsSelected() || drawEverything) {
152 		if (IsSelected()) {
153 			owner->SetHighColor(tint_color(lowColor, B_DARKEN_2_TINT));
154 			owner->SetLowColor(owner->HighColor());
155 		} else
156 			owner->SetHighColor(lowColor);
157 
158 		owner->FillRect(
159 			BRect(left, itemRect.top, itemRect.left, itemRect.bottom));
160 	}
161 
162 	be_control_look->DrawCheckBox(owner, fBoxRect, fBoxRect, base, flags);
163 	owner->PopState();
164 
165 	BStringItem::DrawItem(owner, itemRect, drawEverything);
166 }
167 
168 
169 void
MouseDown(BPoint where)170 CheckBoxItem::MouseDown(BPoint where)
171 {
172 	if (!fBoxRect.Contains(where))
173 		return;
174 
175 	fMouseDown = true;
176 
177 	fListView->InvalidateItem(fListView->IndexOf(this));
178 }
179 
180 
181 void
MouseUp(BPoint where)182 CheckBoxItem::MouseUp(BPoint where)
183 {
184 	if (!fMouseDown)
185 		return;
186 	fMouseDown = false;
187 
188 	if (fBoxRect.Contains(where)) {
189 		if (fChecked)
190 			fChecked = false;
191 		else
192 			fChecked = true;
193 	}
194 
195 	fListView->InvalidateItem(fListView->IndexOf(this));
196 }
197 
198 
199 // #pragma mark -
200 
201 
EditListView(const char * name,list_view_type type,uint32 flags)202 EditListView::EditListView(const char* name, list_view_type type, uint32 flags)
203 	:
204 	BListView(name, type, flags),
205 	fLastMouseDown(NULL)
206 {
207 }
208 
209 
210 void
MouseDown(BPoint where)211 EditListView::MouseDown(BPoint where)
212 {
213 	BListView::MouseDown(where);
214 
215 	int32 index = IndexOf(where);
216 	EditableListItem* handler = dynamic_cast<EditableListItem*>(ItemAt(index));
217 	if (handler == NULL)
218 		return;
219 
220 	fLastMouseDown = handler;
221 	handler->MouseDown(where);
222 	SetMouseEventMask(B_POINTER_EVENTS);
223 }
224 
225 
226 void
MouseUp(BPoint where)227 EditListView::MouseUp(BPoint where)
228 {
229 	BListView::MouseUp(where);
230 	if (fLastMouseDown) {
231 		fLastMouseDown->MouseUp(where);
232 		fLastMouseDown = NULL;
233 	}
234 
235 	int32 index = IndexOf(where);
236 	EditableListItem* handler = dynamic_cast<EditableListItem*>(ItemAt(index));
237 	if (handler == NULL)
238 		return;
239 
240 	handler->MouseUp(where);
241 }
242 
243 
244 void
FrameResized(float newWidth,float newHeight)245 EditListView::FrameResized(float newWidth, float newHeight)
246 {
247 	Invalidate();
248 }
249 
250 
251 // #pragma mark -
252 
253 
FolderConfigWindow(BRect parent,const BMessage & settings)254 FolderConfigWindow::FolderConfigWindow(BRect parent, const BMessage& settings)
255 	:
256 	BWindow(BRect(0, 0, 350, 350), B_TRANSLATE("IMAP Folders"),
257 		B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
258 		B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
259 	fSettings("in", settings)
260 {
261 	fQuotaView = new BStringView("quota view",
262 		B_TRANSLATE("Failed to fetch available storage."));
263 	fQuotaView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
264 		B_ALIGN_VERTICAL_CENTER));
265 	fQuotaView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
266 
267 	fFolderListView = new EditListView(B_TRANSLATE("IMAP Folders"));
268 	fFolderListView->SetExplicitPreferredSize(BSize(B_SIZE_UNLIMITED,
269 		B_SIZE_UNLIMITED));
270 
271 	BButton* cancelButton = new BButton("cancel", B_TRANSLATE("Cancel"),
272 		new BMessage(B_QUIT_REQUESTED));
273 	BButton* applyButton = new BButton("apply", B_TRANSLATE("Apply"),
274 		new BMessage(kMsgApplyButton));
275 
276 	BLayoutBuilder::Group<>(this, B_VERTICAL)
277 		.SetInsets(B_USE_DEFAULT_SPACING)
278 		.Add(fQuotaView)
279 		.Add(new BScrollView("scroller", fFolderListView, 0, false, true))
280 		.AddGroup(B_HORIZONTAL)
281 			.AddGlue()
282 			.Add(cancelButton)
283 			.Add(applyButton);
284 
285 	PostMessage(kMsgInit);
286 	CenterIn(parent);
287 }
288 
289 
290 void
MessageReceived(BMessage * message)291 FolderConfigWindow::MessageReceived(BMessage* message)
292 {
293 	switch (message->what) {
294 		case kMsgInit:
295 			_LoadFolders();
296 			break;
297 
298 		case kMsgApplyButton:
299 			_ApplyChanges();
300 			PostMessage(B_QUIT_REQUESTED);
301 			break;
302 
303 		default:
304 			BWindow::MessageReceived(message);
305 	}
306 }
307 
308 
309 void
_LoadFolders()310 FolderConfigWindow::_LoadFolders()
311 {
312 	StatusWindow* statusWindow = new StatusWindow(this,
313 		B_TRANSLATE("Fetching IMAP folders, please be patient" B_UTF8_ELLIPSIS));
314 	statusWindow->Show();
315 
316 	status_t status = fProtocol.Connect(fSettings.ServerAddress(),
317 		fSettings.Username(), fSettings.Password(), fSettings.UseSSL());
318 	if (status != B_OK) {
319 		statusWindow->PostMessage(B_QUIT_REQUESTED);
320 
321 		// Show error message on screen
322 		BString message = B_TRANSLATE("Could not connect to server "
323 			"\"%server%\":\n%error%");
324 		message.ReplaceFirst("%server%", fSettings.Server());
325 		message.ReplaceFirst("%error%", strerror(status));
326 		BAlert* alert = new BAlert("IMAP error", message.String(),
327 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
328 		alert->Go();
329 
330 		PostMessage(B_QUIT_REQUESTED);
331 		return;
332 	}
333 
334 	// TODO: don't get all of them at once, but retrieve them level by level
335 	fFolderList.clear();
336 	BString separator;
337 	fProtocol.GetFolders(fFolderList, separator);
338 	for (size_t i = 0; i < fFolderList.size(); i++) {
339 		IMAP::FolderEntry& entry = fFolderList[i];
340 		CheckBoxItem* item = new CheckBoxItem(
341 			MailboxToFolderName(entry.folder, separator), entry.subscribed);
342 		fFolderListView->AddItem(item);
343 		item->SetListView(fFolderListView);
344 	}
345 
346 	uint64 used, total;
347 	if (fProtocol.GetQuota(used, total) == B_OK) {
348 		BString quotaString;
349 		char usedBuffer[128], totalBuffer[128];
350 		quotaString.SetToFormat(B_TRANSLATE("Server storage: %s / %s used."),
351 			string_for_size(used, usedBuffer, 128),
352 			string_for_size(total, totalBuffer, 128));
353 		fQuotaView->SetText(quotaString);
354 	}
355 
356 	statusWindow->PostMessage(B_QUIT_REQUESTED);
357 }
358 
359 
360 void
_ApplyChanges()361 FolderConfigWindow::_ApplyChanges()
362 {
363 	bool haveChanges = false;
364 	for (size_t i = 0; i < fFolderList.size(); i++) {
365 		IMAP::FolderEntry& entry = fFolderList[i];
366 		CheckBoxItem* item = (CheckBoxItem*)fFolderListView->ItemAt(i);
367 		if (entry.subscribed != item->Checked()) {
368 			haveChanges = true;
369 			break;
370 		}
371 	}
372 	if (!haveChanges)
373 		return;
374 
375 	StatusWindow* status = new StatusWindow(this,
376 		B_TRANSLATE("Updating subscriptions to IMAP folders, please be patient"
377 		B_UTF8_ELLIPSIS));
378 	status->Show();
379 
380 	for (size_t i = 0; i < fFolderList.size(); i++) {
381 		IMAP::FolderEntry& entry = fFolderList[i];
382 		CheckBoxItem* item = (CheckBoxItem*)fFolderListView->ItemAt(i);
383 		if (entry.subscribed && !item->Checked())
384 			fProtocol.UnsubscribeFolder(entry.folder);
385 		else if (!entry.subscribed && item->Checked())
386 			fProtocol.SubscribeFolder(entry.folder);
387 	}
388 
389 	status->PostMessage(B_QUIT_REQUESTED);
390 }
391 
392