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