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