From 8f03a0f9f2645bc296173abce96cc8547a977b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Sun, 28 Sep 2014 00:33:20 +0200 Subject: [PATCH] HaikuDepot: Restore logged in user across sessions. * Store the username in the app settings. The password was already stored in the keyring. Restore the username upon next launch. * Display the logged in user in the main window. * Added "Log out" menu entry. * When the password could not be retrieved from the keyring (also because the user rejects the keyring dialog), unset the username. * Allow unsetting the username by passing an empty name. --- src/apps/haikudepot/model/Model.cpp | 62 ++++++++++++++++--- src/apps/haikudepot/model/Model.h | 20 +++++- src/apps/haikudepot/model/WebAppInterface.h | 3 + src/apps/haikudepot/ui/FilterView.cpp | 36 +++++++++-- src/apps/haikudepot/ui/FilterView.h | 9 ++- src/apps/haikudepot/ui/MainWindow.cpp | 67 ++++++++++++++++++--- src/apps/haikudepot/ui/MainWindow.h | 3 + 7 files changed, 176 insertions(+), 24 deletions(-) diff --git a/src/apps/haikudepot/model/Model.cpp b/src/apps/haikudepot/model/Model.cpp index 1166117743..0560456701 100644 --- a/src/apps/haikudepot/model/Model.cpp +++ b/src/apps/haikudepot/model/Model.cpp @@ -30,14 +30,19 @@ static const char* kHaikuDepotKeyring = "HaikuDepot"; -// #pragma mark - PackageFilters - - PackageFilter::~PackageFilter() { } +ModelListener::~ModelListener() +{ +} + + +// #pragma mark - PackageFilters + + class AnyFilter : public PackageFilter { public: virtual bool AcceptsPackage(const PackageInfoRef& package) const @@ -368,6 +373,13 @@ Model::~Model() } +bool +Model::AddListener(const ModelListenerRef& listener) +{ + return fListeners.Add(listener); +} + + PackageList Model::CreatePackageList() const { @@ -661,14 +673,27 @@ Model::StopPopulatingAllPackages() void -Model::SetUser(const BString& username) +Model::SetUsername(BString username) { - BPasswordKey key; - BKeyStore keyStore; - if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username, - key) == B_OK) { - SetAuthorization(username, key.Password(), false); + BString password; + if (username.Length() > 0) { + BPasswordKey key; + BKeyStore keyStore; + if (keyStore.GetKey(kHaikuDepotKeyring, B_KEY_TYPE_PASSWORD, username, + key) == B_OK) { + password = key.Password(); + } else { + username = ""; + } } + SetAuthorization(username, password, false); +} + + +const BString& +Model::Username() const +{ + return fWebAppInterface.Username(); } @@ -676,7 +701,7 @@ void Model::SetAuthorization(const BString& username, const BString& password, bool storePassword) { - if (storePassword) { + if (storePassword && username.Length() > 0 && password.Length() > 0) { BPasswordKey key(password, B_KEY_PURPOSE_WEB, username); BKeyStore keyStore; keyStore.AddKeyring(kHaikuDepotKeyring); @@ -685,6 +710,8 @@ Model::SetAuthorization(const BString& username, const BString& password, BAutolock locker(&fLock); fWebAppInterface.SetAuthorization(username, password); + + _NotifyAuthorizationChanged(); } @@ -1146,3 +1173,18 @@ Model::_HasNativeIcon(const BMessage& message) const } return false; } + + +// #pragma mark - listener notification methods + + +void +Model::_NotifyAuthorizationChanged() +{ + for (int32 i = fListeners.CountItems() - 1; i >= 0; i--) { + const ModelListenerRef& listener = fListeners.ItemAtFast(i); + if (listener.Get() != NULL) + listener->AuthorizationChanged(); + } +} + diff --git a/src/apps/haikudepot/model/Model.h b/src/apps/haikudepot/model/Model.h index d47aa7fdd1..4d7b21fcad 100644 --- a/src/apps/haikudepot/model/Model.h +++ b/src/apps/haikudepot/model/Model.h @@ -25,6 +25,17 @@ public: typedef BReference PackageFilterRef; +class ModelListener : public BReferenceable { +public: + virtual ~ModelListener(); + + virtual void AuthorizationChanged() = 0; +}; + +typedef BReference ModelListenerRef; +typedef List ModelListenerList; + + class Model { public: Model(); @@ -33,6 +44,8 @@ public: BLocker* Lock() { return &fLock; } + bool AddListener(const ModelListenerRef& listener); + // !Returns new PackageInfoList from current parameters PackageList CreatePackageList() const; @@ -110,7 +123,8 @@ public: const BString& PreferredLanguage() const { return fPreferredLanguage; } - void SetUser(const BString& username); + void SetUsername(BString username); + const BString& Username() const; void SetAuthorization(const BString& username, const BString& password, bool storePassword); @@ -139,6 +153,8 @@ private: int32 scaledWidth, bool fromCacheOnly); + void _NotifyAuthorizationChanged(); + private: BLocker fLock; @@ -182,6 +198,8 @@ private: BString fPreferredLanguage; WebAppInterface fWebAppInterface; + + ModelListenerList fListeners; }; diff --git a/src/apps/haikudepot/model/WebAppInterface.h b/src/apps/haikudepot/model/WebAppInterface.h index 3407b25f31..b2b8262865 100644 --- a/src/apps/haikudepot/model/WebAppInterface.h +++ b/src/apps/haikudepot/model/WebAppInterface.h @@ -28,6 +28,9 @@ public: void SetAuthorization(const BString& username, const BString& password); + const BString& Username() const + { return fUsername; } + void SetPreferredLanguage(const BString& language); void SetArchitecture(const BString& architecture); diff --git a/src/apps/haikudepot/ui/FilterView.cpp b/src/apps/haikudepot/ui/FilterView.cpp index b57264b131..ef52e08f21 100644 --- a/src/apps/haikudepot/ui/FilterView.cpp +++ b/src/apps/haikudepot/ui/FilterView.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "Model.h" @@ -37,16 +38,23 @@ add_categories_to_menu(const CategoryList& categories, BMenu* menu) } +static void +set_small_font(BView* view) +{ + BFont font; + view->GetFont(&font); + font.SetSize(ceilf(font.Size() * 0.8)); + view->SetFont(&font); +} + + static BCheckBox* create_check_box(const char* label, const char* name) { BMessage* message = new BMessage(MSG_FILTER_SELECTED); message->AddString("name", name); BCheckBox* checkBox = new BCheckBox(label, message); - BFont font; - checkBox->GetFont(&font); - font.SetSize(ceilf(font.Size() * 0.75)); - checkBox->SetFont(&font); + set_small_font(checkBox); return checkBox; } @@ -90,6 +98,11 @@ FilterView::FilterView() fSourceCodeCheckBox = create_check_box( B_TRANSLATE("Source code"), "source code"); + // Logged in user label + fUsername = new BStringView("logged in user", ""); + set_small_font(fUsername); + fUsername->SetHighColor(80, 80, 80); + // Build layout BLayoutBuilder::Group<>(this) .AddGroup(B_HORIZONTAL) @@ -107,6 +120,7 @@ FilterView::FilterView() .Add(fDevelopmentCheckBox) .Add(fSourceCodeCheckBox) .AddGlue(0.5f) + .Add(fUsername) .End() .SetInsets(B_USE_DEFAULT_SPACING) @@ -200,3 +214,17 @@ FilterView::AdoptCheckmarks(const Model& model) fSourceCodeCheckBox->SetValue(model.ShowSourcePackages()); } + +void +FilterView::SetUsername(const BString& username) +{ + BString label; + if (username.Length() == 0) { + label = B_TRANSLATE("Not logged in"); + } else { + label = B_TRANSLATE("Logged in as %User%"); + label.ReplaceAll("%User%", username); + } + fUsername->SetText(label); +} + diff --git a/src/apps/haikudepot/ui/FilterView.h b/src/apps/haikudepot/ui/FilterView.h index 78ae76a55a..81e6a6bae7 100644 --- a/src/apps/haikudepot/ui/FilterView.h +++ b/src/apps/haikudepot/ui/FilterView.h @@ -10,6 +10,7 @@ class BCheckBox; class BMenuField; +class BStringView; class BTextControl; class Model; @@ -30,8 +31,10 @@ public: virtual void AttachedToWindow(); virtual void MessageReceived(BMessage* message); - virtual void AdoptModel(const Model& model); - virtual void AdoptCheckmarks(const Model& model); + void AdoptModel(const Model& model); + void AdoptCheckmarks(const Model& model); + + void SetUsername(const BString& username); private: BMenuField* fShowField; @@ -42,6 +45,8 @@ private: BCheckBox* fInstalledCheckBox; BCheckBox* fDevelopmentCheckBox; BCheckBox* fSourceCodeCheckBox; + + BStringView* fUsername; }; #endif // FILTER_VIEW_H diff --git a/src/apps/haikudepot/ui/MainWindow.cpp b/src/apps/haikudepot/ui/MainWindow.cpp index ccf09a3d70..0ff76121d9 100644 --- a/src/apps/haikudepot/ui/MainWindow.cpp +++ b/src/apps/haikudepot/ui/MainWindow.cpp @@ -53,12 +53,14 @@ enum { - MSG_MODEL_WORKER_DONE = 'mmwd', - MSG_REFRESH_DEPOTS = 'mrdp', - MSG_LOG_IN = 'lgin', - MSG_PACKAGE_STATE_CHANGED = 'mpsc', - MSG_SHOW_SOURCE_PACKAGES = 'ssrc', - MSG_SHOW_DEVELOP_PACKAGES = 'sdvl' + MSG_MODEL_WORKER_DONE = 'mmwd', + MSG_REFRESH_DEPOTS = 'mrdp', + MSG_LOG_IN = 'lgin', + MSG_LOG_OUT = 'lgot', + MSG_AUTHORIZATION_CHANGED = 'athc', + MSG_PACKAGE_STATE_CHANGED = 'mpsc', + MSG_SHOW_SOURCE_PACKAGES = 'ssrc', + MSG_SHOW_DEVELOP_PACKAGES = 'sdvl' }; @@ -83,11 +85,31 @@ struct RefreshWorkerParameters { }; +class MessageModelListener : public ModelListener { +public: + MessageModelListener(const BMessenger& messenger) + : + fMessenger(messenger) + { + } + + virtual void AuthorizationChanged() + { + if (fMessenger.IsValid()) + fMessenger.SendMessage(MSG_AUTHORIZATION_CHANGED); + } + +private: + BMessenger fMessenger; +}; + + MainWindow::MainWindow(BRect frame, const BMessage& settings) : BWindow(frame, B_TRANSLATE_SYSTEM_NAME("HaikuDepot"), B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), + fModelListener(new MessageModelListener(BMessenger(this)), true), fTerminating(false), fModelWorker(B_BAD_THREAD_ID) { @@ -117,6 +139,8 @@ MainWindow::MainWindow(BRect frame, const BMessage& settings) fSplitView->SetCollapsible(0, false); fSplitView->SetCollapsible(1, false); + fModel.AddListener(fModelListener); + // Restore settings BMessage columnSettings; if (settings.FindMessage("column settings", &columnSettings) == B_OK) @@ -128,6 +152,13 @@ MainWindow::MainWindow(BRect frame, const BMessage& settings) if (settings.FindBool("show source packages", &showOption) == B_OK) fModel.SetShowSourcePackages(showOption); + BString username; + if (settings.FindString("username", &username) == B_OK + && username.Length() > 0) { + fModel.SetUsername(username); + } + + // start worker threads BPackageRoster().StartWatching(this, B_WATCH_PACKAGE_INSTALLATION_LOCATIONS); @@ -212,6 +243,14 @@ MainWindow::MessageReceived(BMessage* message) _OpenLoginWindow(); break; + case MSG_LOG_OUT: + fModel.SetUsername(""); + break; + + case MSG_AUTHORIZATION_CHANGED: + _UpdateAuthorization(); + break; + case MSG_SHOW_SOURCE_PACKAGES: { BAutolock locker(fModel.Lock()); @@ -358,6 +397,8 @@ MainWindow::StoreSettings(BMessage& settings) const settings.AddBool("show develop packages", fModel.ShowDevelopPackages()); settings.AddBool("show source packages", fModel.ShowSourcePackages()); + + settings.AddString("username", fModel.Username()); } @@ -401,8 +442,12 @@ MainWindow::_BuildMenu(BMenuBar* menuBar) BMenu* menu = new BMenu(B_TRANSLATE("Tools")); menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh depots"), new BMessage(MSG_REFRESH_DEPOTS))); - menu->AddItem(new BMenuItem(B_TRANSLATE("Log in"), + menu->AddSeparatorItem(); + menu->AddItem(new BMenuItem(B_TRANSLATE("Log in" B_UTF8_ELLIPSIS), new BMessage(MSG_LOG_IN))); + fLogOutItem = new BMenuItem(B_TRANSLATE("Log out"), + new BMessage(MSG_LOG_OUT)); + menu->AddItem(fLogOutItem); menuBar->AddItem(menu); // menu = new BMenu(B_TRANSLATE("Options")); @@ -858,3 +903,11 @@ MainWindow::_OpenLoginWindow() window->Show(); } + +void +MainWindow::_UpdateAuthorization() +{ + BString username(fModel.Username()); + fLogOutItem->SetEnabled(username.Length() > 0); + fFilterView->SetUsername(username); +} diff --git a/src/apps/haikudepot/ui/MainWindow.h b/src/apps/haikudepot/ui/MainWindow.h index b2d26e45c3..7e1bb1b398 100644 --- a/src/apps/haikudepot/ui/MainWindow.h +++ b/src/apps/haikudepot/ui/MainWindow.h @@ -72,16 +72,19 @@ private: const char* message); void _OpenLoginWindow(); + void _UpdateAuthorization(); private: FilterView* fFilterView; PackageListView* fPackageListView; PackageInfoView* fPackageInfoView; BSplitView* fSplitView; + BMenuItem* fLogOutItem; BMenuItem* fShowDevelopPackagesItem; BMenuItem* fShowSourcePackagesItem; Model fModel; + ModelListenerRef fModelListener; PackageList fVisiblePackages; bool fTerminating;