/* * Copyright 2011-2013, Haiku, Inc. All rights reserved. * Copyright 2011, Clemens Zeidler * Distributed under the terms of the MIT License. */ #include "FolderConfigWindow.h" #include #include #include #include #include #include #include #include #include #include #include "Settings.h" #include "Utilities.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "IMAPFolderConfig" class EditableListItem { public: EditableListItem(); virtual ~EditableListItem() {} virtual void MouseDown(BPoint where) = 0; virtual void MouseUp(BPoint where) = 0; void SetListView(BListView* list) { fListView = list; } protected: BListView* fListView; }; class CheckBoxItem : public BStringItem, public EditableListItem { public: CheckBoxItem(const char* text, bool checked); void DrawItem(BView* owner, BRect itemRect, bool drawEverything = false); void MouseDown(BPoint where); void MouseUp(BPoint where); bool Checked() { return fChecked; } private: BRect fBoxRect; bool fChecked; bool fMouseDown; }; class EditListView : public BListView { public: EditListView(const char* name, list_view_type type = B_SINGLE_SELECTION_LIST, uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE); virtual void MouseDown(BPoint where); virtual void MouseUp(BPoint where); virtual void FrameResized(float newWidth, float newHeight); private: EditableListItem* fLastMouseDown; }; class StatusWindow : public BWindow { public: StatusWindow(BWindow* parent, const char* text) : BWindow(BRect(0, 0, 10, 10), B_TRANSLATE("status"), B_MODAL_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL, B_NO_WORKSPACE_ACTIVATION | B_NOT_ZOOMABLE | B_AVOID_FRONT | B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS) { BLayoutBuilder::Group<>(this) .SetInsets(B_USE_DEFAULT_SPACING) .Add(new BStringView("text", text)); CenterIn(parent->Frame()); } }; const uint32 kMsgApplyButton = '&Abu'; const uint32 kMsgInit = '&Ini'; // #pragma mark - EditableListItem::EditableListItem() : fListView(NULL) { } // #pragma mark - CheckBoxItem::CheckBoxItem(const char* text, bool checked) : BStringItem(text), fChecked(checked), fMouseDown(false) { } void CheckBoxItem::DrawItem(BView* owner, BRect itemRect, bool drawEverything) { rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); rgb_color lowColor = owner->LowColor(); uint32 flags = 0; if (fMouseDown) flags |= BControlLook::B_CLICKED; if (fChecked) flags |= BControlLook::B_ACTIVATED; font_height fontHeight; owner->GetFontHeight(&fontHeight); BRect boxRect(0.0f, 2.0f, ceilf(3.0f + fontHeight.ascent), ceilf(5.0f + fontHeight.ascent)); owner->PushState(); float left = itemRect.left; fBoxRect.left = left + 3; fBoxRect.top = itemRect.top + (itemRect.Height() - boxRect.Height()) / 2; fBoxRect.right = fBoxRect.left + boxRect.Width(); fBoxRect.bottom = itemRect.top + boxRect.Height(); itemRect.left = fBoxRect.right + be_control_look->DefaultLabelSpacing(); if (IsSelected() || drawEverything) { if (IsSelected()) { owner->SetHighColor(tint_color(lowColor, B_DARKEN_2_TINT)); owner->SetLowColor(owner->HighColor()); } else owner->SetHighColor(lowColor); owner->FillRect( BRect(left, itemRect.top, itemRect.left, itemRect.bottom)); } be_control_look->DrawCheckBox(owner, fBoxRect, fBoxRect, base, flags); owner->PopState(); BStringItem::DrawItem(owner, itemRect, drawEverything); } void CheckBoxItem::MouseDown(BPoint where) { if (!fBoxRect.Contains(where)) return; fMouseDown = true; fListView->InvalidateItem(fListView->IndexOf(this)); } void CheckBoxItem::MouseUp(BPoint where) { if (!fMouseDown) return; fMouseDown = false; if (fBoxRect.Contains(where)) { if (fChecked) fChecked = false; else fChecked = true; } fListView->InvalidateItem(fListView->IndexOf(this)); } // #pragma mark - EditListView::EditListView(const char* name, list_view_type type, uint32 flags) : BListView(name, type, flags), fLastMouseDown(NULL) { } void EditListView::MouseDown(BPoint where) { BListView::MouseDown(where); int32 index = IndexOf(where); EditableListItem* handler = dynamic_cast(ItemAt(index)); if (handler == NULL) return; fLastMouseDown = handler; handler->MouseDown(where); SetMouseEventMask(B_POINTER_EVENTS); } void EditListView::MouseUp(BPoint where) { BListView::MouseUp(where); if (fLastMouseDown) { fLastMouseDown->MouseUp(where); fLastMouseDown = NULL; } int32 index = IndexOf(where); EditableListItem* handler = dynamic_cast(ItemAt(index)); if (handler == NULL) return; handler->MouseUp(where); } void EditListView::FrameResized(float newWidth, float newHeight) { Invalidate(); } // #pragma mark - FolderConfigWindow::FolderConfigWindow(BRect parent, const BMessage& settings) : BWindow(BRect(0, 0, 350, 350), B_TRANSLATE("IMAP Folders"), B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL, B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), fSettings("in", settings) { fQuotaView = new BStringView("quota view", B_TRANSLATE("Failed to fetch available storage.")); fQuotaView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_CENTER)); fQuotaView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); fFolderListView = new EditListView(B_TRANSLATE("IMAP Folders")); fFolderListView->SetExplicitPreferredSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); BButton* cancelButton = new BButton("cancel", B_TRANSLATE("Cancel"), new BMessage(B_QUIT_REQUESTED)); BButton* applyButton = new BButton("apply", B_TRANSLATE("Apply"), new BMessage(kMsgApplyButton)); BLayoutBuilder::Group<>(this, B_VERTICAL) .SetInsets(B_USE_DEFAULT_SPACING) .Add(fQuotaView) .Add(new BScrollView("scroller", fFolderListView, 0, false, true)) .AddGroup(B_HORIZONTAL) .AddGlue() .Add(cancelButton) .Add(applyButton); PostMessage(kMsgInit); CenterIn(parent); } void FolderConfigWindow::MessageReceived(BMessage* message) { switch (message->what) { case kMsgInit: _LoadFolders(); break; case kMsgApplyButton: _ApplyChanges(); PostMessage(B_QUIT_REQUESTED); break; default: BWindow::MessageReceived(message); } } void FolderConfigWindow::_LoadFolders() { StatusWindow* statusWindow = new StatusWindow(this, B_TRANSLATE("Fetching IMAP folders, please be patient" B_UTF8_ELLIPSIS)); statusWindow->Show(); status_t status = fProtocol.Connect(fSettings.ServerAddress(), fSettings.Username(), fSettings.Password(), fSettings.UseSSL()); if (status != B_OK) { statusWindow->PostMessage(B_QUIT_REQUESTED); // Show error message on screen BString message = B_TRANSLATE("Could not connect to server " "\"%server%\":\n%error%"); message.ReplaceFirst("%server%", fSettings.Server()); message.ReplaceFirst("%error%", strerror(status)); BAlert* alert = new BAlert("IMAP error", message.String(), B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); alert->Go(); PostMessage(B_QUIT_REQUESTED); return; } // TODO: don't get all of them at once, but retrieve them level by level fFolderList.clear(); BString separator; fProtocol.GetFolders(fFolderList, separator); for (size_t i = 0; i < fFolderList.size(); i++) { IMAP::FolderEntry& entry = fFolderList[i]; CheckBoxItem* item = new CheckBoxItem( MailboxToFolderName(entry.folder, separator), entry.subscribed); fFolderListView->AddItem(item); item->SetListView(fFolderListView); } uint64 used, total; if (fProtocol.GetQuota(used, total) == B_OK) { BString quotaString; char usedBuffer[128], totalBuffer[128]; quotaString.SetToFormat(B_TRANSLATE("Server storage: %s / %s used."), string_for_size(used, usedBuffer, 128), string_for_size(total, totalBuffer, 128)); fQuotaView->SetText(quotaString); } statusWindow->PostMessage(B_QUIT_REQUESTED); } void FolderConfigWindow::_ApplyChanges() { bool haveChanges = false; for (size_t i = 0; i < fFolderList.size(); i++) { IMAP::FolderEntry& entry = fFolderList[i]; CheckBoxItem* item = (CheckBoxItem*)fFolderListView->ItemAt(i); if (entry.subscribed != item->Checked()) { haveChanges = true; break; } } if (!haveChanges) return; StatusWindow* status = new StatusWindow(this, B_TRANSLATE("Updating subscriptions to IMAP folders, please be patient" B_UTF8_ELLIPSIS)); status->Show(); for (size_t i = 0; i < fFolderList.size(); i++) { IMAP::FolderEntry& entry = fFolderList[i]; CheckBoxItem* item = (CheckBoxItem*)fFolderListView->ItemAt(i); if (entry.subscribed && !item->Checked()) fProtocol.UnsubscribeFolder(entry.folder); else if (!entry.subscribed && item->Checked()) fProtocol.SubscribeFolder(entry.folder); } status->PostMessage(B_QUIT_REQUESTED); }