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