├── foo_acfu ├── stdafx.cpp ├── preferences_page.h ├── stdafx.h ├── urls.h ├── scheduler.h ├── version.h ├── utils.h ├── embedded │ ├── sources_dlg.h │ ├── sources_dlg.cpp │ ├── embedded.h │ ├── foo_scrobble.cpp │ ├── foo_jscript_panel.cpp │ ├── embedded.cpp │ ├── foo_ui_columns.cpp │ └── fb2k_core.cpp ├── updates.h ├── authorization.cpp ├── preferences_page.cpp ├── foo_acfu.rc2 ├── status_wnd.h ├── foo_acfu.cpp ├── resource.h ├── status_dui.cpp ├── foo_acfu.sln ├── preferences_dlg.h ├── status_wnd.cpp ├── foo_acfu.rc ├── foo_acfu.vcxproj.filters ├── status_cui.cpp ├── split_button.h ├── updates.cpp ├── foo_acfu.vcxproj ├── scheduler.cpp ├── list_column_auto_size.h └── preferences_dlg.cpp ├── .gitignore ├── .editorconfig └── .gitmodules /foo_acfu/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | [Dd]ebug 3 | [Rr]elease 4 | *.aps 5 | *.user 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{cpp,h}] 2 | indent_style = space 3 | indent_size = 2 4 | end_of_line = crlf 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | -------------------------------------------------------------------------------- /foo_acfu/preferences_page.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // {B9256E8C-C335-4497-94F6-69E58AB358B7} 4 | static const GUID guid_preferences_page = 5 | { 0xb9256e8c, 0xc335, 0x4497, { 0x94, 0xf6, 0x69, 0xe5, 0x8a, 0xb3, 0x58, 0xb7 } }; 6 | -------------------------------------------------------------------------------- /foo_acfu/stdafx.h: -------------------------------------------------------------------------------- 1 | #define NOMINMAX 2 | #include 3 | #include "../foobar2000/ATLHelpers/ATLHelpers.h" 4 | #include 5 | #include "../acfu-sdk/utils/check.h" 6 | #include "../acfu-sdk/utils/github.h" 7 | #include "version.h" 8 | -------------------------------------------------------------------------------- /foo_acfu/urls.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define APP_URL_MAIN "https://3dyd.com" 4 | #define APP_URL_SITE "https://acfu.3dyd.com" 5 | 6 | #define APP_URL_DOWNLOAD APP_URL_SITE "/download/" 7 | #define APP_URL_PREFS APP_URL_SITE "/help/#Preferences" 8 | #define APP_URL_WHY APP_URL_SITE "/help/#Why" 9 | -------------------------------------------------------------------------------- /foo_acfu/scheduler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace acfu { 4 | 5 | extern cfg_guidlist cfg_acfu_sources; 6 | 7 | class Scheduler: public service_base { 8 | FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(Scheduler); 9 | 10 | public: 11 | enum { 12 | kMsInDay = 1000 * 60 * 60 * 24, 13 | kPeriodMax = USER_TIMER_MAXIMUM / kMsInDay, 14 | kPeriodMin = 1, 15 | kPeriodDefault = 7, 16 | }; 17 | 18 | virtual t_uint32 GetPeriod() = 0; 19 | virtual void SetPeriod(t_uint32 period) = 0; 20 | 21 | static void Check(HWND parent, const pfc::list_t& sources); 22 | }; 23 | 24 | } // namespace acfu 25 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "wtl"] 2 | path = wtl 3 | url = https://git.code.sf.net/p/wtl/git 4 | [submodule "pfc"] 5 | path = pfc 6 | url = https://github.com/reupen/pfc.git 7 | [submodule "foobar2000"] 8 | path = foobar2000 9 | url = https://github.com/reupen/foobar2000-sdk.git 10 | ignore = dirty 11 | [submodule "acfu-sdk"] 12 | path = acfu-sdk 13 | url = https://github.com/3dyd/acfu-sdk 14 | [submodule "rapidjson"] 15 | path = rapidjson 16 | url = https://github.com/Tencent/rapidjson 17 | shallow = true 18 | fetchRecurseSubmodules = false 19 | [submodule "columns_ui-sdk"] 20 | path = columns_ui-sdk 21 | url = https://github.com/reupen/columns_ui-sdk 22 | -------------------------------------------------------------------------------- /foo_acfu/version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define APP_LONG_NAME "Updates checker for foobar2000" 4 | #define APP_SHORT_NAME "Auto Check for Updates" 5 | #define APP_DESCRIPTION "Provides generic way for components and their extensions to check for updates." 6 | #define APP_BINARY_NAME "foo_acfu" 7 | 8 | #define APP_VERSION_MAJOR 0 9 | #define APP_VERSION_MINOR 3 10 | #define APP_VERSION_BUILD 0 11 | 12 | #define __STR(text) #text 13 | #define _STR(text) __STR(text) 14 | 15 | #if APP_VERSION_BUILD != 0 16 | #define APP_VERSION _STR(APP_VERSION_MAJOR) "." _STR(APP_VERSION_MINOR) "." _STR(APP_VERSION_BUILD) 17 | #else 18 | #define APP_VERSION _STR(APP_VERSION_MAJOR) "." _STR(APP_VERSION_MINOR) 19 | #endif 20 | -------------------------------------------------------------------------------- /foo_acfu/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Tr { 4 | Tr(unsigned string_id) { 5 | ATLVERIFY(text.LoadString(string_id)); 6 | } 7 | 8 | operator const wchar_t*() const { 9 | return static_cast(text); 10 | } 11 | 12 | CString text; 13 | }; 14 | 15 | template 16 | void for_each_service(t_func&& func) { 17 | service_enum_t e; 18 | for (service_ptr_t ptr; e.next(ptr);) { 19 | func(ptr); 20 | } 21 | } 22 | 23 | template 24 | void for_each_service(t_func&& func) { 25 | for_each_service([&](auto& ptr) { 26 | service_ptr_t ext; 27 | if (ptr->service_query_t(ext)) { 28 | func(ext); 29 | } 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /foo_acfu/embedded/sources_dlg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "embedded.h" 5 | #include "list_column_auto_size.h" 6 | 7 | namespace embedded { 8 | 9 | class SourceDlg: public CDialogImpl { 10 | public: 11 | enum { IDD = IDD_EMBEDDED_SOURCES }; 12 | 13 | BEGIN_MSG_MAP_EX(SourceDlg) 14 | MSG_WM_INITDIALOG(OnInitDialog) 15 | COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) 16 | COMMAND_ID_HANDLER_EX(IDOK, OnOK) 17 | END_MSG_MAP() 18 | 19 | private: 20 | void OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl); 21 | BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam); 22 | void OnOK(UINT uNotifyCode, int nID, CWindow wndCtl); 23 | 24 | private: 25 | CCheckListViewCtrl checkbox_list_; 26 | CListColumnAutoSize list_; 27 | pfc::map_t sources_; 28 | }; 29 | 30 | } // namespace embedded 31 | -------------------------------------------------------------------------------- /foo_acfu/updates.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../acfu-sdk/acfu.h" 4 | 5 | namespace acfu { 6 | 7 | class UpdatesImpl: public updates { 8 | public: 9 | virtual void register_callback(callback* callback); 10 | virtual void unregister_callback(callback* callback); 11 | 12 | virtual bool get_info(const GUID& guid, file_info& info); 13 | virtual void set_info(const GUID& guid, const file_info& info); 14 | 15 | void Load(); 16 | void Save(); 17 | 18 | static void ScheduleCleanup(); 19 | 20 | private: 21 | pfc::string8 GetCacheDir(); 22 | void SetInfoInMainThread(const GUID& guid, const file_info& info); 23 | 24 | private: 25 | pfc::list_t callbacks_; 26 | pfc::map_t cache_; 27 | pfc::list_t updates_; 28 | pfc::mutex mutex_; 29 | bool clean_up_ = false; 30 | }; 31 | 32 | } // namespace acfu 33 | -------------------------------------------------------------------------------- /foo_acfu/authorization.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "utils.h" 3 | 4 | // {FD8FEAE0-6E02-4C2A-8AE6-F22F363218BE} 5 | static const GUID guid_branch_updates = 6 | {0xfd8feae0, 0x6e02, 0x4c2a, { 0x8a, 0xe6, 0xf2, 0x2f, 0x36, 0x32, 0x18, 0xbe }}; 7 | 8 | // {6F895D31-7EA5-4F3B-BF84-2511B1572960} 9 | static const GUID guid_cfg_github_auth = 10 | {0x6f895d31, 0x7ea5, 0x4f3b, { 0xbf, 0x84, 0x25, 0x11, 0xb1, 0x57, 0x29, 0x60 }}; 11 | advconfig_string_factory_MT cfg_github_auth("GitHub access token when auto checking for updates", guid_cfg_github_auth, guid_branch_updates, 0, ""); 12 | 13 | class GitHubAuth: public acfu::authorization { 14 | virtual void authorize(const char* url, http_request::ptr request, abort_callback& abort) override { 15 | if (0 == pfc::strcmp_partial(url, "https://api.github.com/")) { 16 | pfc::string8 token; 17 | cfg_github_auth.get(token); 18 | if (!token.is_empty()) { 19 | token.insert_chars(0, "token "); 20 | request->add_header("Authorization", token); 21 | } 22 | } 23 | } 24 | }; 25 | 26 | static service_factory_t g_github_auth; 27 | -------------------------------------------------------------------------------- /foo_acfu/embedded/sources_dlg.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "resource.h" 3 | #include "sources_dlg.h" 4 | #include "utils.h" 5 | 6 | namespace embedded { 7 | 8 | void SourceDlg::OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl) { 9 | EndDialog(IDCANCEL); 10 | } 11 | 12 | BOOL SourceDlg::OnInitDialog(CWindow wndFocus, LPARAM lInitParam) { 13 | CenterWindow(); 14 | 15 | checkbox_list_.SubclassWindow(GetDlgItem(IDC_EMBEDDED_SOURCES)); 16 | checkbox_list_.AddColumn(Tr(IDS_SOURCE), 0); 17 | 18 | list_.SubclassWindow(checkbox_list_); 19 | ::SetWindowTheme(list_, L"explorer", NULL); 20 | 21 | unsigned id = 1; 22 | for_each_service([&](auto& ptr) { 23 | sources_[id] = ptr; 24 | auto index = listview_helper::insert_item(list_, list_.GetItemCount(), ptr->GetName(), id); 25 | list_.SetCheckState(index, ptr->IsEnabled()); 26 | id ++; 27 | }); 28 | 29 | return TRUE; 30 | } 31 | 32 | void SourceDlg::OnOK(UINT uNotifyCode, int nID, CWindow wndCtl) { 33 | for (int i = list_.GetItemCount() - 1; i >= 0; i --) { 34 | unsigned id = list_.GetItemData(i); 35 | sources_[id]->Enable(list_.GetCheckState(i)); 36 | } 37 | EndDialog(IDOK); 38 | } 39 | 40 | } // namespace embedded 41 | -------------------------------------------------------------------------------- /foo_acfu/preferences_page.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "resource.h" 3 | #include "preferences_page.h" 4 | #include "preferences_dlg.h" 5 | #include "utils.h" 6 | #include "urls.h" 7 | 8 | // {0E966267-7DFB-433B-A07C-3F8CDD31A258} 9 | static const GUID guid_components = 10 | { 0x0E966267, 0x7DFB, 0x433B, { 0xA0, 0x7C, 0x3F, 0x8C, 0xDD, 0x31, 0xA2, 0x58 } }; 11 | 12 | class PreferencesPage: public preferences_page_impl { 13 | public: 14 | virtual GUID get_guid() { 15 | return guid_preferences_page; 16 | } 17 | 18 | virtual bool get_help_url(pfc::string_base & p_out) { 19 | p_out = APP_URL_PREFS; 20 | return true; 21 | } 22 | 23 | virtual const char* get_name() { 24 | return APP_SHORT_NAME; 25 | } 26 | 27 | virtual GUID get_parent_guid() { 28 | auto guid = guid_tools; 29 | for_each_service([&guid](auto ptr) { 30 | if (guid_preferences_page == ptr->get_guid()) { 31 | return; 32 | } 33 | if ((guid_root == ptr->get_parent_guid() && 0 == pfc::stricmp_ascii(ptr->get_name(), "Components")) || 34 | guid_components == ptr->get_guid()) { 35 | guid = ptr->get_guid(); 36 | } 37 | }); 38 | return guid; 39 | } 40 | }; 41 | 42 | static preferences_page_factory_t g_preferences_page; 43 | -------------------------------------------------------------------------------- /foo_acfu/embedded/embedded.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../acfu-sdk/acfu.h" 4 | 5 | namespace embedded { 6 | 7 | class Embedded: public acfu::source { 8 | FB2K_MAKE_SERVICE_INTERFACE(Embedded, acfu::source); 9 | 10 | public: 11 | virtual void Init() = 0; 12 | virtual const char* GetName() = 0; 13 | 14 | virtual void Enable(bool enable) = 0; 15 | virtual bool IsEnabled() = 0; 16 | virtual bool IsModified() = 0; 17 | 18 | static bool IsAnyModified(); 19 | }; 20 | 21 | // {30D4EDA7-9AFE-4B93-894D-AC02460C9582} 22 | FOOGUIDDECL const GUID Embedded::class_guid = 23 | {0x30d4eda7, 0x9afe, 0x4b93, { 0x89, 0x4d, 0xac, 0x2, 0x46, 0xc, 0x95, 0x82 }}; 24 | 25 | class Component: public Embedded { 26 | public: 27 | Component(const GUID& guid, const char* file_name); 28 | 29 | virtual GUID get_guid() override; 30 | virtual void get_info(file_info& info) override; 31 | 32 | virtual void Enable(bool enable) override; 33 | virtual const char* GetName() override; 34 | virtual void Init() override; 35 | virtual bool IsEnabled() override; 36 | virtual bool IsModified() override; 37 | 38 | const file_info& GetInfo() const; 39 | 40 | private: 41 | bool initially_enabled_ = true; 42 | cfg_bool cfg_enabled_; 43 | pfc::string8 file_name_; 44 | GUID guid_ = pfc::guid_null; 45 | file_info_impl info_; 46 | }; 47 | 48 | } // namespace embedded 49 | -------------------------------------------------------------------------------- /foo_acfu/embedded/foo_scrobble.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "embedded.h" 3 | #include "../acfu-sdk/utils/github.h" 4 | 5 | namespace embedded { 6 | 7 | // {3F810CF3-8D01-456C-80EA-062BFF2F5F29} 8 | static const GUID guid_scrobble = 9 | {0x3f810cf3, 0x8d01, 0x456c, { 0x80, 0xea, 0x6, 0x2b, 0xff, 0x2f, 0x5f, 0x29 }}; 10 | 11 | class Scrobble: public Component, public acfu::github_conf { 12 | public: 13 | Scrobble(): Component(guid_scrobble, "foo_scrobble") {} 14 | 15 | static const char* get_owner() { 16 | return "gix"; 17 | } 18 | 19 | static const char* get_repo() { 20 | return "foo_scrobble"; 21 | } 22 | 23 | virtual void get_info(file_info& info) override { 24 | __super::get_info(info); 25 | info.meta_set("download_page", "https://github.com/gix/foo_scrobble/releases"); 26 | } 27 | 28 | virtual acfu::request::ptr create_request() override { 29 | acfu::request::ptr request = new service_impl_t>(); 30 | return request; 31 | } 32 | 33 | virtual bool is_newer(const file_info& info) override { 34 | const char* available = info.meta_get("version", 0); 35 | const char* installed = GetInfo().meta_get("version", 0); 36 | 37 | return acfu::is_newer(available, installed); 38 | } 39 | }; 40 | 41 | static service_factory_single_t g_scrobble; 42 | 43 | } // namespace embedded 44 | -------------------------------------------------------------------------------- /foo_acfu/embedded/foo_jscript_panel.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "embedded.h" 3 | #include "../acfu-sdk/utils/github.h" 4 | 5 | namespace embedded { 6 | 7 | // {0CDC57F6-8BBA-4E02-B6A1-6BB66421674B} 8 | static const GUID guid_jscript = 9 | {0xcdc57f6, 0x8bba, 0x4e02, { 0xb6, 0xa1, 0x6b, 0xb6, 0x64, 0x21, 0x67, 0x4b }}; 10 | 11 | class Jscript: public Component, public acfu::github_conf { 12 | public: 13 | Jscript(): Component(guid_jscript, "foo_jscript_panel") {} 14 | 15 | static const char* get_owner() { 16 | return "kbuffington"; 17 | } 18 | 19 | static const char* get_repo() { 20 | return "foo_jscript_panel"; 21 | } 22 | 23 | virtual void get_info(file_info& info) override { 24 | __super::get_info(info); 25 | info.meta_set("download_page", "https://github.com/kbuffington/foo_jscript_panel/releases"); 26 | } 27 | 28 | virtual acfu::request::ptr create_request() override { 29 | acfu::request::ptr request = new service_impl_t>(); 30 | return request; 31 | } 32 | 33 | virtual bool is_newer(const file_info& info) override { 34 | const char* available = info.meta_get("version", 0); 35 | const char* installed = GetInfo().meta_get("version", 0); 36 | 37 | return acfu::is_newer(available, installed); 38 | } 39 | }; 40 | 41 | static service_factory_single_t g_jscript; 42 | 43 | } // namespace embedded 44 | -------------------------------------------------------------------------------- /foo_acfu/foo_acfu.rc2: -------------------------------------------------------------------------------- 1 | // 2 | // source_filter.rc2 - resources Microsoft Visual C++ does not edit directly 3 | // 4 | 5 | #ifdef APSTUDIO_INVOKED 6 | #error this file is not editable by Microsoft Visual C++ 7 | #endif //APSTUDIO_INVOKED 8 | 9 | #include "urls.h" 10 | #include "version.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | // 14 | // Version 15 | // 16 | 17 | VS_VERSION_INFO VERSIONINFO 18 | FILEVERSION APP_VERSION_MAJOR,APP_VERSION_MINOR,APP_VERSION_BUILD,0 19 | PRODUCTVERSION APP_VERSION_MAJOR,APP_VERSION_MINOR,APP_VERSION_BUILD,0 20 | FILEFLAGSMASK 0x3fL 21 | #ifdef _DEBUG 22 | FILEFLAGS 0x1L 23 | #else 24 | FILEFLAGS 0x0L 25 | #endif 26 | FILEOS 0x4L 27 | FILETYPE 0x1L 28 | FILESUBTYPE 0x0L 29 | BEGIN 30 | BLOCK "StringFileInfo" 31 | BEGIN 32 | BLOCK "040904e4" 33 | BEGIN 34 | VALUE "Comments", APP_URL_MAIN 35 | VALUE "CompanyName", "3DYD Soft" 36 | VALUE "FileDescription", APP_LONG_NAME 37 | VALUE "FileVersion", APP_VERSION 38 | VALUE "InternalName", APP_BINARY_NAME 39 | VALUE "LegalCopyright", "Copyright (C) 2018 3DYD Soft" 40 | VALUE "OriginalFilename", APP_BINARY_NAME ".dll" 41 | VALUE "ProductName", APP_SHORT_NAME 42 | VALUE "ProductVersion", APP_VERSION 43 | END 44 | END 45 | BLOCK "VarFileInfo" 46 | BEGIN 47 | VALUE "Translation", 0x409, 1252 48 | END 49 | END 50 | -------------------------------------------------------------------------------- /foo_acfu/status_wnd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef CWinTraitsOR<0, WS_EX_CONTROLPARENT> StatusWndTraits; 4 | 5 | class StatusWnd: public CWindowImpl, public acfu::updates::callback { 6 | public: 7 | BEGIN_MSG_MAP_EX(StatusWnd) 8 | MSG_WM_CREATE(OnCreate) 9 | MSG_WM_DESTROY(OnDestroy) 10 | MSG_WM_ERASEBKGND(OnEraseBkgnd) 11 | MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo) 12 | MSG_WM_SIZE(OnSize) 13 | COMMAND_ID_HANDLER_EX(ID_PREFERENCES, OnPreferences) 14 | NOTIFY_CODE_HANDLER_EX(TTN_GETDISPINFO, OnToolTipText) 15 | END_MSG_MAP() 16 | 17 | enum Color { 18 | kColorBackground, 19 | kColorHighlight, 20 | }; 21 | 22 | void OnUiChanged(); 23 | 24 | protected: 25 | virtual COLORREF GetUiColor(Color color) const = 0; 26 | virtual CFontHandle GetUiFont() const = 0; 27 | void DrawHighlightedBackground(CDCHandle dc); 28 | 29 | private: 30 | int OnCreate(LPCREATESTRUCT lpCreateStruct); 31 | void OnDestroy(); 32 | BOOL OnEraseBkgnd(CDCHandle dc); 33 | void OnGetMinMaxInfo(LPMINMAXINFO lpMMI); 34 | void OnPreferences(UINT uNotifyCode, int nID, CWindow wndCtl); 35 | void OnSize(UINT nType, CSize size); 36 | LRESULT OnToolTipText(LPNMHDR pnmh); 37 | 38 | virtual void on_updates_available(const pfc::list_base_const_t& ids); 39 | 40 | HBITMAP CreateButtonBitmap(CDCHandle dc, CFontHandle font, CSize size) const; 41 | CSize GetButtonSize(CDCHandle dc, CFontHandle font) const; 42 | void ResetToolBar(); 43 | void UpdateToolBarSize(); 44 | 45 | private: 46 | size_t updates_count_ = 0; 47 | CToolBarCtrl toolbar_; 48 | }; 49 | -------------------------------------------------------------------------------- /foo_acfu/foo_acfu.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "urls.h" 3 | 4 | DECLARE_COMPONENT_VERSION( 5 | APP_SHORT_NAME, 6 | APP_VERSION, 7 | APP_DESCRIPTION "\n\nRelease date: " __DATE__ "\n3dyd, acfu@3dyd.com" 8 | ); 9 | 10 | VALIDATE_COMPONENT_FILENAME(APP_BINARY_NAME ".dll"); 11 | 12 | class AcfuRequest: public acfu::github_latest_release, public acfu::github_conf { 13 | public: 14 | static const char* get_owner() { 15 | return "3dyd"; 16 | } 17 | 18 | static const char* get_repo() { 19 | return "acfu"; 20 | } 21 | 22 | virtual void process_release(const rapidjson::Value& release, file_info& info) { 23 | __super::process_release(release, info); 24 | info.meta_remove_field("download_page"); 25 | } 26 | }; 27 | 28 | class AcfuSource: public acfu::source { 29 | virtual GUID get_guid() { 30 | static const GUID guid = 31 | { 0xe07b43d1, 0x6050, 0x4b8c, { 0xba, 0x5f, 0xee, 0x6b, 0xcd, 0x44, 0xa2, 0x34 } }; 32 | 33 | return guid; 34 | } 35 | 36 | virtual void get_info(file_info& info) { 37 | info.meta_set("version", APP_VERSION); 38 | info.meta_set("name", APP_SHORT_NAME); 39 | info.meta_set("module", APP_BINARY_NAME); 40 | info.meta_set("download_page", APP_URL_DOWNLOAD); 41 | } 42 | 43 | virtual bool is_newer(const file_info& info) { 44 | const char* version = info.meta_get("version", 0); 45 | return acfu::is_newer(version, APP_VERSION); 46 | } 47 | 48 | virtual acfu::request::ptr create_request() { 49 | acfu::request::ptr request = new service_impl_t(); 50 | return request; 51 | } 52 | }; 53 | 54 | static service_factory_t g_acfu_source; 55 | -------------------------------------------------------------------------------- /foo_acfu/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by foo_acfu.rc 4 | // 5 | #define IDD_PROPERTIES 101 6 | #define IDR_MAIN_POPUP 102 7 | #define IDD_EMBEDDED_SOURCES 103 8 | #define IDS_NEED_APPLY 201 9 | #define IDS_GREATER_VERSION 202 10 | #define IDS_NO_UPDATES 203 11 | #define IDS_UPDATES_AVAILABLE 204 12 | #define IDS_COL_NAME 205 13 | #define IDS_COL_AVAILABLE 206 14 | #define IDS_COL_INSTALLED 207 15 | #define IDS_COL_MODULE 208 16 | #define IDS_SOURCE 209 17 | #define IDS_FOLLOW_PRERELEASES 210 18 | #define IDC_SOURCE_LIST 1001 19 | #define IDC_WHY_LINK 1002 20 | #define IDC_AVAILABLE_SOURCES 1003 21 | #define IDC_CFU_ALL 1004 22 | #define IDC_PERIOD_EDIT 1005 23 | #define IDC_PERIOD_SPIN 1006 24 | #define IDC_EMBEDDED_SOURCES 1007 25 | #define ID_CFU_SINGLE 40001 26 | #define ID_CLEAR_CACHE 40002 27 | #define ID_PREFERENCES 40003 28 | #define ID_GOTO_DOWNLOAD_PAGE 40004 29 | #define ID_DOWNLOAD 40005 30 | #define ID_MANAGE_EMBEDDED 40006 31 | #define ID_CONTEXT_MENU_BASE 40100 32 | 33 | // Next default values for new objects 34 | // 35 | #ifdef APSTUDIO_INVOKED 36 | #ifndef APSTUDIO_READONLY_SYMBOLS 37 | #define _APS_NO_MFC 1 38 | #define _APS_NEXT_RESOURCE_VALUE 104 39 | #define _APS_NEXT_COMMAND_VALUE 40007 40 | #define _APS_NEXT_CONTROL_VALUE 1008 41 | #define _APS_NEXT_SYMED_VALUE 101 42 | #endif 43 | #endif 44 | -------------------------------------------------------------------------------- /foo_acfu/embedded/embedded.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "embedded.h" 3 | #include "utils.h" 4 | 5 | namespace embedded { 6 | 7 | bool Embedded::IsAnyModified() { 8 | bool modified = false; 9 | for_each_service([&modified](auto& ptr) { 10 | modified = modified || ptr->IsModified(); 11 | }); 12 | return modified; 13 | } 14 | 15 | Component::Component(const GUID& guid, const char* file_name) 16 | : cfg_enabled_(guid, true), file_name_(file_name) { 17 | } 18 | 19 | void Component::Enable(bool enable) { 20 | cfg_enabled_ = enable; 21 | } 22 | 23 | GUID Component::get_guid() { 24 | return guid_; 25 | } 26 | 27 | void Component::get_info(file_info& info) { 28 | info.copy_meta(info_); 29 | } 30 | 31 | const file_info& Component::GetInfo() const { 32 | return info_; 33 | } 34 | 35 | const char* Component::GetName() { 36 | return file_name_.get_ptr(); 37 | } 38 | 39 | void Component::Init() { 40 | initially_enabled_ = cfg_enabled_; 41 | if (!initially_enabled_) { 42 | return; 43 | } 44 | 45 | componentversion::ptr cv; 46 | for_each_service([&](auto& ptr) { 47 | pfc::string8 file_name; 48 | ptr->get_file_name(file_name); 49 | if (file_name == file_name_) { 50 | cv = ptr; 51 | } 52 | }); 53 | if (cv.is_empty()) { 54 | return; 55 | } 56 | 57 | guid_ = cfg_enabled_.get_guid(); 58 | 59 | info_.meta_set("module", file_name_.get_ptr()); 60 | 61 | pfc::string8 version; 62 | cv->get_component_version(version); 63 | if (!version.is_empty()) { 64 | info_.meta_set("version", version.get_ptr()); 65 | } 66 | 67 | pfc::string8 name; 68 | cv->get_component_name(name); 69 | if (!name.is_empty()) { 70 | info_.meta_set("name", name.get_ptr()); 71 | } 72 | } 73 | 74 | bool Component::IsEnabled() { 75 | return cfg_enabled_; 76 | } 77 | 78 | bool Component::IsModified() { 79 | return initially_enabled_ != cfg_enabled_; 80 | } 81 | 82 | // NOTE: should be in sync with acfu::CacheLoad (executed _before_ it) 83 | class EmbeddedInit: public init_stage_callback { 84 | virtual void on_init_stage(t_uint32 stage) { 85 | if (init_stages::before_ui_init == stage) { 86 | for_each_service([](auto& ptr) { 87 | ptr->Init(); 88 | }); 89 | } 90 | } 91 | }; 92 | 93 | static service_factory_single_t g_init; 94 | 95 | } // namespace embedded 96 | -------------------------------------------------------------------------------- /foo_acfu/embedded/foo_ui_columns.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "resource.h" 3 | #include "embedded.h" 4 | #include "../acfu-sdk/utils/github.h" 5 | #include "utils.h" 6 | 7 | namespace embedded { 8 | 9 | // {61368423-3FDC-44DA-9924-984480BA15B5} 10 | static const GUID guid_columns_ui = 11 | {0x61368423, 0x3fdc, 0x44da, { 0x99, 0x24, 0x98, 0x44, 0x80, 0xba, 0x15, 0xb5 }}; 12 | 13 | // {4D7B6AFF-DA24-4460-98E0-BDF9F9106006} 14 | static const GUID guid_cfg_cui_follow_prereleases = 15 | {0x4d7b6aff, 0xda24, 0x4460, { 0x98, 0xe0, 0xbd, 0xf9, 0xf9, 0x10, 0x60, 0x6 }}; 16 | 17 | cfg_bool cfg_cui_follow_prereleases(guid_cfg_cui_follow_prereleases, false); 18 | 19 | class ColumnsUi: public Component, public acfu::github_conf { 20 | public: 21 | ColumnsUi(): Component(guid_columns_ui, "foo_ui_columns") {} 22 | 23 | static const char* get_owner() { 24 | return "reupen"; 25 | } 26 | 27 | static const char* get_repo() { 28 | return "columns_ui"; 29 | } 30 | 31 | virtual void context_menu_build(HMENU menu, unsigned id_base) override { 32 | AppendMenu(menu, MFT_SEPARATOR, 0, NULL); 33 | 34 | unsigned state = cfg_cui_follow_prereleases ? MFS_CHECKED : MFS_UNCHECKED; 35 | AppendMenu(menu, MFT_STRING | state, id_base, Tr(IDS_FOLLOW_PRERELEASES)); 36 | } 37 | 38 | virtual void context_menu_command(unsigned id, unsigned id_base) override { 39 | if (id_base != id) { 40 | uBugCheck(); 41 | } 42 | cfg_cui_follow_prereleases = !cfg_cui_follow_prereleases; 43 | } 44 | 45 | virtual void get_info(file_info& info) override { 46 | __super::get_info(info); 47 | info.meta_set("download_page", "https://yuo.be/columns_ui"); 48 | } 49 | 50 | virtual acfu::request::ptr create_request() override { 51 | acfu::request::ptr request; 52 | if (cfg_cui_follow_prereleases) { 53 | request = new service_impl_t>(); 54 | } 55 | else { 56 | request = new service_impl_t>(); 57 | } 58 | return request; 59 | } 60 | 61 | virtual bool is_newer(const file_info& info) override { 62 | const char* available = info.meta_get("version", 0); 63 | const char* installed = GetInfo().meta_get("version", 0); 64 | 65 | return acfu::is_newer(available, installed); 66 | } 67 | }; 68 | 69 | static service_factory_single_t g_columns_ui; 70 | 71 | } // namespace embedded 72 | -------------------------------------------------------------------------------- /foo_acfu/status_dui.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "resource.h" 3 | #include "status_wnd.h" 4 | 5 | class StatusDuiImpl: public StatusWnd, public ui_element_instance { 6 | public: 7 | BEGIN_MSG_MAP_EX(StatusDui) 8 | MSG_WM_STYLECHANGING(OnStyleChanging) 9 | CHAIN_MSG_MAP(StatusWnd) 10 | END_MSG_MAP() 11 | 12 | StatusDuiImpl(ui_element_config::ptr config, ui_element_instance_callback_ptr callback): callback_(callback) { 13 | set_configuration(config); 14 | } 15 | 16 | static ui_element_config::ptr g_get_default_configuration() { 17 | ui_element_config_builder builder; 18 | return builder.finish(g_get_guid()); 19 | } 20 | 21 | static const char* g_get_description() { 22 | return APP_DESCRIPTION; 23 | } 24 | 25 | static GUID g_get_guid() { 26 | // {3C3C69FE-AF9C-44B5-A6F3-DCC2AC14A32A} 27 | static const GUID guid = 28 | { 0x3c3c69fe, 0xaf9c, 0x44b5, { 0xa6, 0xf3, 0xdc, 0xc2, 0xac, 0x14, 0xa3, 0x2a } }; 29 | 30 | return guid; 31 | } 32 | 33 | static void g_get_name(pfc::string_base& out) { 34 | out = APP_SHORT_NAME; 35 | } 36 | 37 | static GUID g_get_subclass() { 38 | return ui_element_subclass_utility; 39 | } 40 | 41 | void initialize_window(HWND parent) { 42 | WIN32_OP(Create(parent) != NULL); 43 | } 44 | 45 | private: 46 | virtual ui_element_config::ptr get_configuration() { 47 | ui_element_config_builder builder; 48 | return builder.finish(get_guid()); 49 | } 50 | 51 | virtual HWND get_wnd() { 52 | return m_hWnd; 53 | } 54 | 55 | virtual void notify(const GUID& what, t_size p_param1, const void * p_param2, t_size p_param2size) { 56 | if (ui_element_notify_colors_changed == what || ui_element_notify_font_changed == what) { 57 | OnUiChanged(); 58 | } 59 | } 60 | 61 | virtual void set_configuration(ui_element_config::ptr config) {} 62 | 63 | private: 64 | void OnStyleChanging(int nStyleType, LPSTYLESTRUCT lpStyleStruct) { 65 | if (GWL_EXSTYLE == nStyleType) { 66 | lpStyleStruct->styleNew &= ~WS_EX_STATICEDGE; 67 | } 68 | SetMsgHandled(FALSE); 69 | } 70 | 71 | virtual COLORREF GetUiColor(Color color) const { 72 | switch (color) { 73 | case kColorBackground: return callback_->query_std_color(ui_color_background); 74 | case kColorHighlight: return callback_->query_std_color(ui_color_highlight); 75 | } 76 | ATLASSERT(0); 77 | return 0; 78 | } 79 | 80 | virtual CFontHandle GetUiFont() const { 81 | return callback_->query_font_ex(ui_font_lists); 82 | } 83 | 84 | private: 85 | const ui_element_instance_callback_ptr callback_; 86 | }; 87 | 88 | class StatusDui: public ui_element_impl {}; 89 | 90 | static service_factory_single_t g_status_dui; 91 | -------------------------------------------------------------------------------- /foo_acfu/embedded/fb2k_core.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "embedded.h" 3 | #include "utils.h" 4 | 5 | namespace embedded { 6 | 7 | // {39733416-2AD5-4CA6-825E-FA6967977FFE} 8 | static const GUID guid_fb2k_core = 9 | {0x39733416, 0x2ad5, 0x4ca6, { 0x82, 0x5e, 0xfa, 0x69, 0x67, 0x97, 0x7f, 0xfe }}; 10 | 11 | // {DB46A577-9E0B-4ACF-9B59-DD546BDD1C0B} 12 | static const GUID guid_check_for_beta = 13 | {0xdb46a577, 0x9e0b, 0x4acf, { 0x9b, 0x59, 0xdd, 0x54, 0x6b, 0xdd, 0x1c, 0x0b }}; 14 | 15 | class Fb2kCore: public Component { 16 | class Request: public acfu::request { 17 | public: 18 | Request(const char* version): version_(version ? version : "") {} 19 | 20 | private: 21 | bool check_for_beta() const; 22 | virtual void run(file_info& info, abort_callback& abort) override; 23 | 24 | private: 25 | pfc::string8 version_; 26 | }; 27 | 28 | public: 29 | Fb2kCore(): Component(guid_fb2k_core, "Core") {} 30 | 31 | virtual void get_info(file_info& info) override { 32 | __super::get_info(info); 33 | info.meta_set("download_page", "https://foobar2000.org/download"); 34 | } 35 | 36 | virtual acfu::request::ptr create_request() override { 37 | auto version = GetInfo().meta_get("version", 0); 38 | acfu::request::ptr request = new service_impl_t(version); 39 | 40 | return request; 41 | } 42 | 43 | virtual bool is_newer(const file_info& info) override { 44 | auto checked_version = info.info_get("checked_version"); 45 | if (!checked_version) { 46 | return false; 47 | } 48 | auto current_version = GetInfo().meta_get("version", 0); 49 | if (0 != strcmp(current_version, checked_version)) { 50 | return false; 51 | } 52 | if (auto response = info.info_get("is_newer")) { 53 | return 0 == strcmp(response, "1"); 54 | } 55 | return false; 56 | } 57 | }; 58 | 59 | static service_factory_single_t g_fb2k_core; 60 | 61 | bool Fb2kCore::Request::check_for_beta() const { 62 | bool check = false; 63 | for_each_service([&check](auto ptr) { 64 | if (!ptr->is_radio()) { 65 | pfc::string8 name; 66 | ptr->get_name(name); 67 | if (0 == pfc::strcmp_partial(name.get_ptr(), "Check for beta versions of foobar2000") || 68 | guid_check_for_beta == ptr->get_guid()) { 69 | check = ptr->get_state(); 70 | } 71 | } 72 | }); 73 | return check; 74 | } 75 | 76 | void Fb2kCore::Request::run(file_info& info, abort_callback& abort) { 77 | pfc::string8 encoded; 78 | urlEncode(encoded, version_.c_str()); 79 | 80 | pfc::string8 url = "https://www.foobar2000.org/update-core?version="; 81 | url += encoded; 82 | if (check_for_beta()) { 83 | url += "&beta"; 84 | } 85 | 86 | auto request = static_api_ptr_t()->create_request("GET"); 87 | file::ptr response = request->run(url, abort); 88 | pfc::array_t data; 89 | response->read_till_eof(data, abort); 90 | data.append_fromptr("", 1); 91 | 92 | info.info_set("checked_version", version_.get_ptr()); 93 | 94 | if (0 == strcmp(data.get_ptr(), "1")) { 95 | info.info_set("is_newer", "1"); 96 | } 97 | else { 98 | info.meta_set("version", version_.get_ptr()); 99 | 100 | if (0 != strcmp(data.get_ptr(), "0")) { 101 | console::complain("unexpected response when checking foobar2000 core version", data.get_ptr()); 102 | } 103 | } 104 | } 105 | 106 | } // namespace embedded 107 | -------------------------------------------------------------------------------- /foo_acfu/foo_acfu.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "foo_acfu", "foo_acfu.vcxproj", "{2F23AC85-FE11-4C42-9635-C07EB8CD1F47}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "foobar2000_component_client", "..\foobar2000\foobar2000_component_client\foobar2000_component_client.vcxproj", "{71AD2674-065B-48F5-B8B0-E1F9D3892081}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "foobar2000_sdk_helpers", "..\foobar2000\helpers\foobar2000_sdk_helpers.vcxproj", "{EE47764E-A202-4F85-A767-ABDAB4AFF35F}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "foobar2000_SDK", "..\foobar2000\SDK\foobar2000_SDK.vcxproj", "{E8091321-D79D-4575-86EF-064EA1A4A20D}" 13 | EndProject 14 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfc", "..\pfc\pfc.vcxproj", "{EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}" 15 | EndProject 16 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "columns_ui_sdk", "..\columns_ui-sdk\columns_ui-sdk.vcxproj", "{93EC0EDE-01CD-4FB0-B8E8-4F2A027E026E}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Win32 = Debug|Win32 21 | Release|Win32 = Release|Win32 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {2F23AC85-FE11-4C42-9635-C07EB8CD1F47}.Debug|Win32.ActiveCfg = Debug|Win32 25 | {2F23AC85-FE11-4C42-9635-C07EB8CD1F47}.Debug|Win32.Build.0 = Debug|Win32 26 | {2F23AC85-FE11-4C42-9635-C07EB8CD1F47}.Release|Win32.ActiveCfg = Release|Win32 27 | {2F23AC85-FE11-4C42-9635-C07EB8CD1F47}.Release|Win32.Build.0 = Release|Win32 28 | {71AD2674-065B-48F5-B8B0-E1F9D3892081}.Debug|Win32.ActiveCfg = Debug|Win32 29 | {71AD2674-065B-48F5-B8B0-E1F9D3892081}.Debug|Win32.Build.0 = Debug|Win32 30 | {71AD2674-065B-48F5-B8B0-E1F9D3892081}.Release|Win32.ActiveCfg = Release|Win32 31 | {71AD2674-065B-48F5-B8B0-E1F9D3892081}.Release|Win32.Build.0 = Release|Win32 32 | {EE47764E-A202-4F85-A767-ABDAB4AFF35F}.Debug|Win32.ActiveCfg = Debug|Win32 33 | {EE47764E-A202-4F85-A767-ABDAB4AFF35F}.Debug|Win32.Build.0 = Debug|Win32 34 | {EE47764E-A202-4F85-A767-ABDAB4AFF35F}.Release|Win32.ActiveCfg = Release|Win32 35 | {EE47764E-A202-4F85-A767-ABDAB4AFF35F}.Release|Win32.Build.0 = Release|Win32 36 | {E8091321-D79D-4575-86EF-064EA1A4A20D}.Debug|Win32.ActiveCfg = Debug|Win32 37 | {E8091321-D79D-4575-86EF-064EA1A4A20D}.Debug|Win32.Build.0 = Debug|Win32 38 | {E8091321-D79D-4575-86EF-064EA1A4A20D}.Release|Win32.ActiveCfg = Release|Win32 39 | {E8091321-D79D-4575-86EF-064EA1A4A20D}.Release|Win32.Build.0 = Release|Win32 40 | {EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}.Debug|Win32.ActiveCfg = Debug|Win32 41 | {EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}.Debug|Win32.Build.0 = Debug|Win32 42 | {EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}.Release|Win32.ActiveCfg = Release|Win32 43 | {EBFFFB4E-261D-44D3-B89C-957B31A0BF9C}.Release|Win32.Build.0 = Release|Win32 44 | {93EC0EDE-01CD-4FB0-B8E8-4F2A027E026E}.Debug|Win32.ActiveCfg = Debug|Win32 45 | {93EC0EDE-01CD-4FB0-B8E8-4F2A027E026E}.Debug|Win32.Build.0 = Debug|Win32 46 | {93EC0EDE-01CD-4FB0-B8E8-4F2A027E026E}.Release|Win32.ActiveCfg = Release|Win32 47 | {93EC0EDE-01CD-4FB0-B8E8-4F2A027E026E}.Release|Win32.Build.0 = Release|Win32 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {DECC8A40-8CC5-478A-89DE-C275D939E1A1} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /foo_acfu/preferences_dlg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../acfu-sdk/acfu.h" 4 | #include "list_column_auto_size.h" 5 | #include "split_button.h" 6 | 7 | class SourcesList: public CListColumnAutoSizeImpl, 8 | public CCustomDraw { 9 | public: 10 | BEGIN_MSG_MAP_EX(SourcesList) 11 | CHAIN_MSG_MAP(CListColumnAutoSizeImpl) 12 | CHAIN_MSG_MAP_ALT(CCustomDraw, 1) 13 | DEFAULT_REFLECTION_HANDLER() 14 | END_MSG_MAP() 15 | 16 | DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/); 17 | DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/); 18 | DWORD OnSubItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw); 19 | 20 | private: 21 | CFont bold_font_; 22 | CFontHandle default_font_; 23 | }; 24 | 25 | class PreferencesDlg: public CDialogImpl, 26 | public preferences_page_instance, 27 | public acfu::updates::callback { 28 | public: 29 | enum { IDD = IDD_PROPERTIES }; 30 | 31 | BEGIN_MSG_MAP_EX(PreferencesDlg) 32 | MSG_WM_CONTEXTMENU(OnContextMenu) 33 | MSG_WM_DESTROY(OnDestroy) 34 | MSG_WM_INITDIALOG(OnInitDialog) 35 | COMMAND_ID_HANDLER_EX(IDC_CFU_ALL, OnCfuAll) 36 | COMMAND_ID_HANDLER_EX(ID_CFU_SINGLE, OnCfuSingle) 37 | COMMAND_ID_HANDLER_EX(ID_CLEAR_CACHE, OnClearCache) 38 | COMMAND_ID_HANDLER_EX(ID_MANAGE_EMBEDDED, OnManagedEmbedded) 39 | COMMAND_HANDLER_EX(IDC_PERIOD_EDIT, EN_CHANGE, OnPeriodEdit) 40 | NOTIFY_HANDLER_EX(IDC_SOURCE_LIST, LVN_COLUMNCLICK, OnListColunmClick) 41 | NOTIFY_HANDLER_EX(IDC_SOURCE_LIST, LVN_ITEMCHANGED, OnListItemChanged) 42 | NOTIFY_HANDLER_EX(IDC_CFU_ALL, BCN_DROPDOWN, OnSplitDropDown) 43 | NOTIFY_HANDLER_EX(IDC_WHY_LINK, NM_CLICK, OnWhy) 44 | NOTIFY_HANDLER_EX(IDC_WHY_LINK, NM_RETURN, OnWhy) 45 | REFLECT_NOTIFICATIONS_HWND_FILTERED(list_) 46 | END_MSG_MAP() 47 | 48 | PreferencesDlg(preferences_page_callback::ptr callback); 49 | ~PreferencesDlg(); 50 | 51 | private: 52 | void OnCfuAll(UINT uNotifyCode, int nID, CWindow wndCtl); 53 | void OnCfuSingle(UINT uNotifyCode, int nID, CWindow wndCtl); 54 | void OnClearCache(UINT uNotifyCode, int nID, CWindow wndCtl); 55 | void OnContextMenu(CWindow wnd, CPoint point); 56 | void OnDestroy(); 57 | BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam); 58 | void OnManagedEmbedded(UINT uNotifyCode, int nID, CWindow wndCtl); 59 | LRESULT OnListColunmClick(LPNMHDR pnmh); 60 | LRESULT OnListItemChanged(LPNMHDR pnmh); 61 | void OnPeriodEdit(UINT uNotifyCode, int nID, CWindow wndCtl); 62 | LRESULT OnSplitDropDown(LPNMHDR pnmh); 63 | LRESULT OnWhy(LPNMHDR pnmh); 64 | 65 | // preferences_page_instance 66 | virtual void apply(); 67 | virtual t_uint32 get_state(); 68 | HWND get_wnd() { return m_hWnd; } 69 | virtual void reset(); 70 | 71 | // acfu::cache::callback 72 | virtual void on_info_changed(const GUID& guid, const file_info& info); 73 | 74 | HMENU BuildContextMenu(const acfu::source::ptr& source, const file_info_impl& info) const; 75 | pfc::list_t GetCheckedSources() const; 76 | void FreeList(); 77 | void InitList(); 78 | void SortList(); 79 | void UpdateList(); 80 | void UpdateListItem(int index, const GUID& guid, const file_info& info); 81 | 82 | template 83 | void ForEachInList(TFunc func) const { 84 | for (int i = list_.GetItemCount() - 1; i >= 0; i --) { 85 | if (auto guid_ptr = reinterpret_cast(list_.GetItemData(i))) { 86 | func(*guid_ptr, i); 87 | } 88 | } 89 | } 90 | 91 | private: 92 | const preferences_page_callback::ptr callback_; 93 | CFont title_font_; 94 | SourcesList list_; 95 | CSplitButton split_; 96 | bool clear_cache_ = false; 97 | }; 98 | -------------------------------------------------------------------------------- /foo_acfu/status_wnd.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "resource.h" 3 | #include "status_wnd.h" 4 | #include "preferences_page.h" 5 | 6 | HBITMAP StatusWnd::CreateButtonBitmap(CDCHandle dc, CFontHandle font, CSize size) const { 7 | if (0 == updates_count_) { 8 | return NULL; 9 | } 10 | 11 | BITMAPINFO bmi = {{sizeof(bmi.bmiHeader), size.cx, size.cy, 1, 32, 0, DWORD(size.cx * size.cy)}}; 12 | HBITMAP bitmap = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, NULL, NULL, 0); 13 | CDC mem_dc(CreateCompatibleDC(dc)); 14 | SelectObjectScope bitmap_scope(mem_dc, bitmap); 15 | 16 | CRect rect(CPoint(), size); 17 | COLORREF fg_color = GetUiColor(kColorHighlight); 18 | mem_dc.FillSolidRect(&rect, fg_color); 19 | 20 | COLORREF bg_color = GetUiColor(kColorBackground); 21 | SelectObjectScope font_scope(mem_dc, font); 22 | mem_dc.SetBkColor(fg_color); 23 | mem_dc.SetTextColor(bg_color); 24 | 25 | wchar_t count[20]; 26 | _itow_s(updates_count_, count, _countof(count), 10); 27 | mem_dc.DrawText(count, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); 28 | 29 | return bitmap; 30 | } 31 | 32 | void StatusWnd::DrawHighlightedBackground(CDCHandle dc) { 33 | if (updates_count_ > 0) { 34 | CRect rect; 35 | toolbar_.GetWindowRect(&rect); 36 | ScreenToClient(&rect); 37 | dc.FillSolidRect(&rect, GetUiColor(kColorHighlight)); 38 | } 39 | } 40 | 41 | CSize StatusWnd::GetButtonSize(CDCHandle dc, CFontHandle font) const { 42 | SelectObjectScope font_scope(dc, font); 43 | CRect rect; 44 | dc.DrawText(L"99", -1, &rect, DT_CALCRECT); 45 | 46 | rect.right = LONG(1.5 * rect.right); 47 | const double FI = 1.62; 48 | if (FI * rect.bottom > rect.right) { 49 | rect.right = LONG(FI * rect.bottom); 50 | } 51 | else { 52 | rect.right = LONG(double(rect.right) / FI); 53 | } 54 | 55 | return rect.Size(); 56 | } 57 | 58 | void StatusWnd::on_updates_available(const pfc::list_base_const_t& ids) { 59 | if (ids.get_count() != updates_count_) { 60 | updates_count_ = ids.get_count(); 61 | ResetToolBar(); 62 | } 63 | } 64 | 65 | int StatusWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { 66 | static_api_ptr_t()->register_callback(this); 67 | 68 | // May already be created by on_updates_available due to register_callback 69 | if (!toolbar_) { 70 | ResetToolBar(); 71 | } 72 | 73 | SetMsgHandled(FALSE); 74 | return 0; 75 | } 76 | 77 | void StatusWnd::OnDestroy() { 78 | static_api_ptr_t()->unregister_callback(this); 79 | SetMsgHandled(FALSE); 80 | } 81 | 82 | BOOL StatusWnd::OnEraseBkgnd(CDCHandle dc) { 83 | CRect rect; 84 | GetClientRect(&rect); 85 | dc.FillSolidRect(&rect, GetUiColor(kColorBackground)); 86 | DrawHighlightedBackground(dc); 87 | 88 | return TRUE; 89 | } 90 | 91 | void StatusWnd::OnGetMinMaxInfo(LPMINMAXINFO lpMMI) { 92 | CSize size; 93 | toolbar_.GetMaxSize(&size); 94 | lpMMI->ptMinTrackSize = CPoint(size.cx, size.cy); 95 | } 96 | 97 | void StatusWnd::OnPreferences(UINT uNotifyCode, int nID, CWindow wndCtl) { 98 | static_api_ptr_t()->show_preferences(guid_preferences_page); 99 | } 100 | 101 | void StatusWnd::OnSize(UINT nType, CSize size) { 102 | UpdateToolBarSize(); 103 | SetMsgHandled(FALSE); 104 | } 105 | 106 | LRESULT StatusWnd::OnToolTipText(LPNMHDR pnmh) { 107 | LPNMTTDISPINFO info = (LPNMTTDISPINFO)pnmh; 108 | 109 | CString tooltip; 110 | if (0 == updates_count_) { 111 | tooltip.LoadString(IDS_NO_UPDATES); 112 | } 113 | else { 114 | CString format; 115 | format.LoadString(IDS_UPDATES_AVAILABLE); 116 | tooltip.Format(format, updates_count_); 117 | } 118 | wcsncpy_s(info->szText, tooltip, _TRUNCATE); 119 | 120 | return 0; 121 | } 122 | 123 | void StatusWnd::OnUiChanged() { 124 | ResetToolBar(); 125 | } 126 | 127 | void StatusWnd::ResetToolBar() { 128 | if (toolbar_) { 129 | toolbar_.DestroyWindow(); 130 | } 131 | 132 | CFont font(AtlCreateBoldFont(GetUiFont())); 133 | CClientDC dc(m_hWnd); 134 | CSize size = GetButtonSize(dc.m_hDC, font.m_hFont); 135 | HBITMAP bitmap = CreateButtonBitmap(dc.m_hDC, font.m_hFont, size); 136 | 137 | DWORD style = CControlWinTraits::GetWndStyle(0) | WS_TABSTOP | TBSTYLE_TOOLTIPS | TBSTYLE_TRANSPARENT | CCS_NODIVIDER | CCS_NORESIZE; 138 | toolbar_.Create(m_hWnd, CRect(), 0, style); 139 | toolbar_.SetWindowText(TEXT(APP_SHORT_NAME)); 140 | toolbar_.SetButtonStructSize(sizeof(TBBUTTON)); 141 | toolbar_.SetBitmapSize(size); 142 | toolbar_.AddBitmap(1, bitmap); 143 | toolbar_.InsertButton(0, ID_PREFERENCES, 0, TBSTATE_ENABLED, 0, 0, NULL); 144 | 145 | UpdateToolBarSize(); 146 | Invalidate(); 147 | } 148 | 149 | void StatusWnd::UpdateToolBarSize() { 150 | CSize size; 151 | toolbar_.GetMaxSize(&size); 152 | toolbar_.SetWindowPos(NULL, 0, 0, size.cx, size.cy, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); 153 | } 154 | -------------------------------------------------------------------------------- /foo_acfu/foo_acfu.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "atlres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""atlres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "#include ""foo_acfu.rc2"" // non-Microsoft Visual C++ edited resources\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Dialog 51 | // 52 | 53 | IDD_PROPERTIES DIALOGEX 0, 0, 333, 289 54 | STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD 55 | FONT 8, "MS Shell Dlg", 400, 0, 0x1 56 | BEGIN 57 | LTEXT "Available sources:",IDC_AVAILABLE_SOURCES,0,0,100,8 58 | RTEXT "Right-click a source for additional options.",IDC_STATIC,108,0,224,8 59 | CONTROL "",IDC_SOURCE_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,0,10,332,258 60 | CONTROL "Why this list differs from the list of components?",IDC_WHY_LINK, 61 | "SysLink",WS_TABSTOP,0,268,89,18 62 | RTEXT "Checking period (days):",IDC_STATIC,92,274,104,8 63 | EDITTEXT IDC_PERIOD_EDIT,200,272,27,16,ES_AUTOHSCROLL | ES_NUMBER 64 | CONTROL "",IDC_PERIOD_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,219,273,10,14 65 | PUSHBUTTON "Check for Updates",IDC_CFU_ALL,232,272,100,16 66 | END 67 | 68 | IDD_EMBEDDED_SOURCES DIALOGEX 0, 0, 183, 204 69 | STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU 70 | CAPTION "Embedded Sources" 71 | FONT 8, "MS Shell Dlg", 400, 0, 0x1 72 | BEGIN 73 | CONTROL "",IDC_EMBEDDED_SOURCES,"SysListView32",LVS_REPORT | LVS_SORTASCENDING | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,7,7,169,169 74 | DEFPUSHBUTTON "OK",IDOK,72,183,50,14 75 | PUSHBUTTON "Cancel",IDCANCEL,127,183,50,14 76 | END 77 | 78 | 79 | ///////////////////////////////////////////////////////////////////////////// 80 | // 81 | // DESIGNINFO 82 | // 83 | 84 | #ifdef APSTUDIO_INVOKED 85 | GUIDELINES DESIGNINFO 86 | BEGIN 87 | IDD_PROPERTIES, DIALOG 88 | BEGIN 89 | RIGHTMARGIN, 332 90 | BOTTOMMARGIN, 287 91 | END 92 | 93 | IDD_EMBEDDED_SOURCES, DIALOG 94 | BEGIN 95 | LEFTMARGIN, 7 96 | RIGHTMARGIN, 176 97 | TOPMARGIN, 7 98 | BOTTOMMARGIN, 197 99 | END 100 | END 101 | #endif // APSTUDIO_INVOKED 102 | 103 | 104 | ///////////////////////////////////////////////////////////////////////////// 105 | // 106 | // Menu 107 | // 108 | 109 | IDR_MAIN_POPUP MENU 110 | BEGIN 111 | POPUP "###" 112 | BEGIN 113 | MENUITEM "Clear Cache", ID_CLEAR_CACHE 114 | MENUITEM "Manage Embedded Sources", ID_MANAGE_EMBEDDED 115 | END 116 | END 117 | 118 | 119 | ///////////////////////////////////////////////////////////////////////////// 120 | // 121 | // String Table 122 | // 123 | 124 | STRINGTABLE 125 | BEGIN 126 | IDS_NEED_APPLY "%s (please apply changes to complete)" 127 | IDS_GREATER_VERSION "greater" 128 | IDS_NO_UPDATES "No updates are available" 129 | IDS_UPDATES_AVAILABLE "Updates are available (%d)" 130 | IDS_COL_NAME "Name" 131 | IDS_COL_AVAILABLE "Available" 132 | IDS_COL_INSTALLED "Installed" 133 | END 134 | 135 | STRINGTABLE 136 | BEGIN 137 | IDS_COL_MODULE "Module" 138 | IDS_SOURCE "Source" 139 | IDS_FOLLOW_PRERELEASES "Follow pre-releases" 140 | END 141 | 142 | STRINGTABLE 143 | BEGIN 144 | ID_CFU_SINGLE "Check for Updates..." 145 | ID_GOTO_DOWNLOAD_PAGE "Go to Download Page" 146 | ID_DOWNLOAD "Download in Browser" 147 | END 148 | 149 | #endif // English (United States) resources 150 | ///////////////////////////////////////////////////////////////////////////// 151 | 152 | 153 | 154 | #ifndef APSTUDIO_INVOKED 155 | ///////////////////////////////////////////////////////////////////////////// 156 | // 157 | // Generated from the TEXTINCLUDE 3 resource. 158 | // 159 | #include "foo_acfu.rc2" // non-Microsoft Visual C++ edited resources 160 | 161 | ///////////////////////////////////////////////////////////////////////////// 162 | #endif // not APSTUDIO_INVOKED 163 | 164 | -------------------------------------------------------------------------------- /foo_acfu/foo_acfu.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav 15 | 16 | 17 | {f937d24e-feaf-4f69-b11c-5ed723d3deac} 18 | 19 | 20 | {c7fee20e-972e-4a6e-86bc-afa3c335a4e3} 21 | 22 | 23 | {192adf1f-a08a-442f-8297-5668d47894be} 24 | 25 | 26 | {4add9f9f-38f7-4b38-8033-4435451cfeb5} 27 | 28 | 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | UI 44 | 45 | 46 | UI 47 | 48 | 49 | Preferences 50 | 51 | 52 | Preferences 53 | 54 | 55 | UI 56 | 57 | 58 | Source Files 59 | 60 | 61 | Embedded 62 | 63 | 64 | Embedded 65 | 66 | 67 | Embedded 68 | 69 | 70 | Embedded 71 | 72 | 73 | Embedded 74 | 75 | 76 | Embedded 77 | 78 | 79 | Source Files 80 | 81 | 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | SDK 97 | 98 | 99 | SDK 100 | 101 | 102 | SDK 103 | 104 | 105 | SDK 106 | 107 | 108 | Header Files 109 | 110 | 111 | UI 112 | 113 | 114 | Preferences 115 | 116 | 117 | Preferences 118 | 119 | 120 | Preferences 121 | 122 | 123 | Preferences 124 | 125 | 126 | Header Files 127 | 128 | 129 | Header Files 130 | 131 | 132 | SDK 133 | 134 | 135 | Header Files 136 | 137 | 138 | Embedded 139 | 140 | 141 | Embedded 142 | 143 | 144 | 145 | 146 | Resource Files 147 | 148 | 149 | 150 | 151 | Resource Files 152 | 153 | 154 | -------------------------------------------------------------------------------- /foo_acfu/status_cui.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "resource.h" 3 | #include "../columns_ui-sdk/ui_extension.h" 4 | #include "status_wnd.h" 5 | 6 | using namespace uie; 7 | 8 | class ResourceClient { 9 | public: 10 | void OnUiChanged() const { 11 | core_api::ensure_main_thread(); 12 | 13 | for (size_t i = 0; i < windows_.get_size(); i ++) { 14 | windows_[i]->OnUiChanged(); 15 | } 16 | } 17 | 18 | void RegisterWnd(StatusWnd* wnd) { 19 | core_api::ensure_main_thread(); 20 | windows_.add_item(wnd); 21 | } 22 | 23 | void UnregisterWnd(StatusWnd* wnd) { 24 | core_api::ensure_main_thread(); 25 | windows_.remove_item(wnd); 26 | } 27 | 28 | private: 29 | pfc::list_t windows_; 30 | }; 31 | 32 | class FontsClient: public cui::fonts::client, public ResourceClient { 33 | public: 34 | CFontHandle GetFont() { 35 | if (!font_) { 36 | cui::fonts::helper helper(get_client_guid()); 37 | font_.Attach(helper.get_font()); 38 | } 39 | return font_.m_hFont; 40 | } 41 | 42 | private: 43 | virtual const GUID& get_client_guid() const { 44 | // {7F2E0FAD-A9BE-40C8-8DA5-BE4EB8027C90} 45 | static const GUID guid = 46 | { 0x7f2e0fad, 0xa9be, 0x40c8, { 0x8d, 0xa5, 0xbe, 0x4e, 0xb8, 0x2, 0x7c, 0x90 } }; 47 | 48 | return guid; 49 | } 50 | 51 | virtual void get_name(pfc::string_base & p_out) const { 52 | p_out = APP_SHORT_NAME; 53 | } 54 | 55 | virtual cui::fonts::font_type_t get_default_font_type() const { 56 | return cui::fonts::font_type_labels; 57 | } 58 | 59 | virtual void on_font_changed() const { 60 | if (font_) { 61 | const_cast(font_).DeleteObject(); 62 | } 63 | OnUiChanged(); 64 | } 65 | 66 | private: 67 | CFont font_; 68 | }; 69 | 70 | static service_factory_single_t g_fonts; 71 | 72 | class ColorsClient: public cui::colours::client, public ResourceClient { 73 | public: 74 | COLORREF GetColor(StatusWnd::Color color) { 75 | cui::colours::helper helper(get_client_guid()); 76 | switch (color) { 77 | case StatusWnd::kColorBackground: return helper.get_colour(cui::colours::colour_background); 78 | case StatusWnd::kColorHighlight: return helper.get_colour(cui::colours::colour_selection_background); 79 | } 80 | ATLASSERT(0); 81 | return 0; 82 | } 83 | 84 | private: 85 | virtual const GUID& get_client_guid() const { 86 | // {7F2E0FAD-A9BE-40C8-8DA5-BE4EB8027C90} 87 | static const GUID guid = 88 | { 0x7f2e0fad, 0xa9be, 0x40c8, { 0x8d, 0xa5, 0xbe, 0x4e, 0xb8, 0x2, 0x7c, 0x90 } }; 89 | 90 | return guid; 91 | } 92 | 93 | virtual void get_name(pfc::string_base & p_out) const { p_out = APP_SHORT_NAME; } 94 | virtual t_size get_supported_colours() const { return cui::colours::colour_flag_background | cui::colours::colour_flag_selection_background; } 95 | virtual t_size get_supported_bools() const { return 0; } 96 | virtual bool get_themes_supported() const { return false; } 97 | virtual void on_colour_changed(t_size mask) const { OnUiChanged(); } 98 | virtual void on_bool_changed(t_size mask) const {} 99 | 100 | private: 101 | pfc::list_t windows_; 102 | }; 103 | 104 | static service_factory_single_t g_colors; 105 | 106 | class StatusCui: public StatusWnd, public window { 107 | public: 108 | BEGIN_MSG_MAP_EX(StatusCui) 109 | MSG_WM_CREATE(OnCreate) 110 | MSG_WM_DESTROY(OnDestroy) 111 | MSG_WM_ERASEBKGND(OnEraseBkgnd) 112 | CHAIN_MSG_MAP(StatusWnd) 113 | END_MSG_MAP() 114 | 115 | private: 116 | HWND create_or_transfer_window(HWND wnd_parent, const window_host_ptr& p_host, const ui_helpers::window_position_t& p_position) { 117 | if (NULL != m_hWnd) { 118 | ShowWindow(SW_HIDE); 119 | SetParent(wnd_parent); 120 | host_->relinquish_ownership(m_hWnd); 121 | SetWindowPos(0, p_position.x, p_position.y, p_position.cx, p_position.cy, SWP_NOZORDER); 122 | } 123 | else { 124 | CRect rect; 125 | p_position.convert_to_rect(rect); 126 | Create(wnd_parent, rect); 127 | } 128 | host_ = p_host; 129 | 130 | return m_hWnd; 131 | } 132 | 133 | virtual void destroy_window() { 134 | DestroyWindow(); 135 | host_.release(); 136 | } 137 | 138 | virtual void get_category(pfc::string_base& out) const { 139 | out = "Panels"; 140 | } 141 | 142 | virtual const GUID& get_extension_guid() const { 143 | // {15DCAFBB-757B-41F8-8481-1B0E13A3CE27} 144 | static const GUID guid = 145 | { 0x15dcafbb, 0x757b, 0x41f8, { 0x84, 0x81, 0x1b, 0xe, 0x13, 0xa3, 0xce, 0x27 } }; 146 | 147 | return guid; 148 | } 149 | 150 | virtual void get_name(pfc::string_base& out) const { 151 | out = APP_SHORT_NAME; 152 | } 153 | 154 | virtual unsigned get_type() const { 155 | return type_panel | type_toolbar; 156 | } 157 | 158 | virtual HWND get_wnd() const { 159 | return m_hWnd; 160 | } 161 | 162 | virtual bool is_available(const window_host_ptr& p) const { 163 | return true; 164 | } 165 | 166 | int OnCreate(LPCREATESTRUCT lpCreateStruct) { 167 | g_colors.get_static_instance().RegisterWnd(this); 168 | g_fonts.get_static_instance().RegisterWnd(this); 169 | 170 | SetMsgHandled(FALSE); 171 | return 0; 172 | } 173 | 174 | void OnDestroy() { 175 | g_colors.get_static_instance().UnregisterWnd(this); 176 | g_fonts.get_static_instance().UnregisterWnd(this); 177 | 178 | SetMsgHandled(FALSE); 179 | } 180 | 181 | BOOL OnEraseBkgnd(CDCHandle dc) { 182 | RelayEraseBkgnd(m_hWnd, GetParent(), dc); 183 | DrawHighlightedBackground(dc); 184 | 185 | return TRUE; 186 | } 187 | 188 | private: 189 | LRESULT on_message(HWND wnd, UINT msg, WPARAM wp, LPARAM lp) { 190 | return WindowProc(wnd, msg, wp, lp); 191 | } 192 | 193 | virtual COLORREF GetUiColor(Color color) const { 194 | return g_colors.get_static_instance().GetColor(color); 195 | } 196 | 197 | virtual CFontHandle GetUiFont() const { 198 | return g_fonts.get_static_instance().GetFont(); 199 | } 200 | 201 | private: 202 | window_host_ptr host_; 203 | }; 204 | 205 | window_factory g_status_cui; 206 | -------------------------------------------------------------------------------- /foo_acfu/split_button.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Split button has appeared in Vista so for XP it needs to be implemented 4 | // manually. This class wraps all that need to use real split button in 5 | // OS>=Vista and to create a control similar to split button in XP 6 | class CSplitButton: public CWindowImpl { 7 | public: 8 | BEGIN_MSG_MAP_EX(CSplitButton) 9 | MESSAGE_HANDLER_EX(WM_CREATE, OnCreate) 10 | if (!RunTimeHelper::IsVista()) { 11 | MSG_WM_GETDLGCODE(OnGetDlgCode) 12 | MSG_WM_KEYDOWN(OnKeyDown) 13 | MSG_WM_LBUTTONDBLCLK(OnLButtonDblClk) 14 | MSG_WM_LBUTTONDOWN(OnLButtonDown) 15 | MSG_WM_PAINT(OnPaint) 16 | MESSAGE_HANDLER_EX(WM_SETTEXT, OnSetText) 17 | MESSAGE_HANDLER_EX(WM_ENABLE, RedrawByMessage) 18 | MESSAGE_HANDLER_EX(WM_UPDATEUISTATE, RedrawByMessage) 19 | MESSAGE_HANDLER_EX(BCM_SETSPLITINFO, OnSetSplitInfo) 20 | } 21 | END_MSG_MAP() 22 | 23 | CSplitButton(): style_(0), ignore_set_sext_(false) {} 24 | 25 | // After return rc_draw contain arrow rectangle 26 | static void DrawArrow(CDCHandle dc, RECT& rc_draw, COLORREF clr_arrow) { 27 | CRect rc_arrow; 28 | rc_arrow.left = rc_draw.left + ::GetSystemMetrics(SM_CXEDGE); 29 | rc_arrow.top = (rc_draw.bottom + rc_draw.top) / 2 - kArrowSizeY / 2; 30 | rc_arrow.right = rc_arrow.left + kArrowSizeX; 31 | rc_arrow.bottom = (rc_draw.bottom + rc_draw.top) / 2 + kArrowSizeY / 2; 32 | 33 | POINT pt_arrow[3]; 34 | pt_arrow[0].x = rc_arrow.left; 35 | pt_arrow[0].y = rc_arrow.top; 36 | pt_arrow[1].x = rc_arrow.right; 37 | pt_arrow[1].y = rc_arrow.top; 38 | pt_arrow[2].x = (rc_arrow.left + rc_arrow.right) / 2; 39 | pt_arrow[2].y = rc_arrow.bottom; 40 | 41 | dc.SaveDC(); 42 | 43 | CBrush br_arrow; 44 | br_arrow.CreateSolidBrush(clr_arrow); 45 | CPen pen_arrow; 46 | pen_arrow.CreatePen(PS_SOLID, 0, clr_arrow); 47 | dc.SelectBrush(br_arrow); 48 | dc.SelectPen(pen_arrow); 49 | 50 | dc.SetPolyFillMode(WINDING); 51 | dc.Polygon(pt_arrow, 3); 52 | 53 | dc.RestoreDC(-1); 54 | rc_draw = rc_arrow; 55 | rc_draw.right += 1; 56 | rc_draw.bottom += 1; 57 | } 58 | 59 | // After return rc_draw contain splitter rectangle 60 | static void DrawSplitter(CDCHandle dc, RECT& rc_draw) { 61 | // Draw separator 62 | rc_draw.right = rc_draw.left; 63 | InflateRect(&rc_draw, 0, - 2 * ::GetSystemMetrics(SM_CXEDGE)); 64 | dc.DrawEdge(&rc_draw, EDGE_ETCHED, BF_RIGHT); 65 | rc_draw.left -= ::GetSystemMetrics(SM_CXEDGE); 66 | } 67 | 68 | BOOL SubclassWindow(HWND hwnd) { 69 | if (__super::SubclassWindow(hwnd)) { 70 | Init(); 71 | return TRUE; 72 | } 73 | return FALSE; 74 | } 75 | 76 | protected: 77 | void AppearanceFix() { 78 | ATLASSERT(!ignore_set_sext_); 79 | CString text; 80 | GetWindowText(text); 81 | if (!text.IsEmpty()) { 82 | text += L" "; 83 | ignore_set_sext_ = true; 84 | SetWindowText(text); 85 | ignore_set_sext_ = false; 86 | } 87 | } 88 | 89 | void DoPaint(CDCHandle dc) { 90 | DefWindowProc(WM_PAINT, (WPARAM)dc.m_hDC, 0); 91 | 92 | // Draw the arrow 93 | CRect rc_arrow = GetArrowArea(), rc_splitter = rc_arrow; 94 | COLORREF clr_arrow = GetSysColor(IsWindowEnabled() ? COLOR_BTNTEXT : COLOR_GRAYTEXT); 95 | 96 | DrawArrow(dc, rc_arrow, clr_arrow); 97 | if (0 == (BCSS_NOSPLIT & style_)) { 98 | DrawSplitter(dc, rc_splitter); 99 | } 100 | } 101 | 102 | CRect GetArrowArea() { 103 | CRect rc_draw; 104 | GetClientRect(&rc_draw); 105 | rc_draw.left = rc_draw.right - kArrowSizeX - 4 * ::GetSystemMetrics(SM_CXEDGE); 106 | 107 | return rc_draw; 108 | } 109 | 110 | void Init() { 111 | if (RunTimeHelper::IsVista()) { 112 | SetButtonStyle(BS_SPLITBUTTON); 113 | } 114 | else { 115 | AppearanceFix(); 116 | } 117 | } 118 | 119 | void NotifyDropDown() { 120 | if (0 == (BCSS_NOSPLIT & style_)) { 121 | NMBCDROPDOWN param = {0}; 122 | param.hdr.code = BCN_DROPDOWN; 123 | param.hdr.hwndFrom = m_hWnd; 124 | param.hdr.idFrom = GetDlgCtrlID(); 125 | param.rcButton = GetArrowArea(); // TODO: check what it really should contain 126 | GetParent().SendMessage(WM_NOTIFY, param.hdr.idFrom, reinterpret_cast(¶m)); 127 | } 128 | } 129 | 130 | LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam) { 131 | Init(); 132 | SetMsgHandled(FALSE); 133 | return 0; 134 | } 135 | 136 | UINT OnGetDlgCode(LPMSG lpMsg) { 137 | UINT code = (UINT)DefWindowProc(); 138 | return code | DLGC_WANTARROWS; 139 | } 140 | 141 | void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { 142 | if (VK_DOWN == nChar && 0 != (GetKeyState(VK_CONTROL) & KF_UP)) { 143 | NotifyDropDown(); 144 | } 145 | else { 146 | SetMsgHandled(FALSE); 147 | } 148 | } 149 | 150 | void OnLButtonDblClk(UINT nFlags, CPoint point) { 151 | // Nothing, just to block this message 152 | } 153 | 154 | void OnLButtonDown(UINT nFlags, CPoint point) { 155 | if (GetArrowArea().PtInRect(point)) { 156 | NotifyDropDown(); 157 | } 158 | else { 159 | SetMsgHandled(FALSE); 160 | } 161 | } 162 | 163 | void OnPaint(CDCHandle dc) { 164 | if (dc) { 165 | DoPaint(dc); 166 | } 167 | else { 168 | CPaintDC dc(m_hWnd); 169 | CMemoryDC mem_dc(dc.m_hDC, dc.m_ps.rcPaint); 170 | DoPaint(mem_dc.m_hDC); 171 | } 172 | } 173 | 174 | LRESULT OnSetSplitInfo(UINT uMsg, WPARAM wParam, LPARAM lParam) { 175 | PBUTTON_SPLITINFO info = (PBUTTON_SPLITINFO)lParam; 176 | if (BCSIF_STYLE & info->mask) { 177 | style_ = info->uSplitStyle; 178 | if (m_hWnd) { 179 | Invalidate(); 180 | } 181 | } 182 | return TRUE; 183 | } 184 | 185 | LRESULT OnSetText(UINT uMsg, WPARAM wParam, LPARAM lParam) { 186 | LRESULT res = DefWindowProc(uMsg, wParam, lParam); 187 | if (!ignore_set_sext_ && !RunTimeHelper::IsVista()) { 188 | AppearanceFix(); 189 | } 190 | return res; 191 | } 192 | 193 | LRESULT RedrawByMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { 194 | Invalidate(); 195 | SetMsgHandled(FALSE); 196 | return 0; 197 | } 198 | 199 | private: 200 | static const int kArrowSizeX = 8; 201 | static const int kArrowSizeY = 4; 202 | UINT style_; 203 | bool ignore_set_sext_; 204 | }; 205 | -------------------------------------------------------------------------------- /foo_acfu/updates.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "updates.h" 3 | 4 | namespace acfu { 5 | 6 | static const char* CLEANUP_FLAG = "." APP_BINARY_NAME "-clean-up"; 7 | 8 | static service_factory_single_t g_cache; 9 | 10 | // NOTE: should be in sync with embedded::EmbeddedInit (executed _after_ it) 11 | class CacheLoad: public init_stage_callback { 12 | virtual void on_init_stage(t_uint32 stage) { 13 | if (init_stages::after_ui_init == stage) { 14 | g_cache.get_static_instance().Load(); 15 | } 16 | } 17 | }; 18 | 19 | static service_factory_single_t g_cache_load; 20 | 21 | class CacheSave: public initquit { 22 | virtual void on_quit() { 23 | g_cache.get_static_instance().Save(); 24 | } 25 | }; 26 | 27 | static service_factory_single_t g_cache_save; 28 | 29 | ////////////////////////////////////////////////////////////////////////// 30 | 31 | pfc::string8 UpdatesImpl::GetCacheDir() { 32 | return core_api::pathInProfile(APP_BINARY_NAME "\\cache\\"); 33 | } 34 | 35 | bool UpdatesImpl::get_info(const GUID& guid, file_info& info) { 36 | pfc::mutexScope lock(mutex_); 37 | 38 | auto it = cache_.find(pfc::string8(pfc::print_guid(guid)).get_ptr()); 39 | if (it.is_empty()) { 40 | return false; 41 | } 42 | 43 | info.copy(it->m_value); 44 | 45 | return true; 46 | } 47 | 48 | void UpdatesImpl::Load() { 49 | abort_callback_dummy abort; 50 | pfc::list_t paths; 51 | bool clean_up = false; 52 | 53 | try { 54 | auto path = GetCacheDir(); 55 | foobar2000_io::listDirectory(path, abort, [&paths](const char* path, auto, bool is_subdirectory) { 56 | if (!is_subdirectory) { 57 | paths.add_item(path); 58 | } 59 | }); 60 | 61 | path += CLEANUP_FLAG; 62 | clean_up = filesystem::g_exists(path, abort); 63 | } 64 | catch (exception_io_not_found&) { 65 | } 66 | catch (std::exception& e) { 67 | console::formatter() << "[" APP_BINARY_NAME "] list cache directory: " << e.what(); 68 | } 69 | 70 | decltype(cache_) cache; 71 | decltype(updates_) updates; 72 | paths.enumerate([&](const auto& path) { 73 | auto filename = pfc::string_filename_ext(path); 74 | try { 75 | if (clean_up) { 76 | filesystem::g_remove(path, abort); 77 | } 78 | else { 79 | GUID guid = pfc::GUID_from_text(filename.get_ptr()); 80 | auto source = source::g_get(guid); 81 | file_ptr file; 82 | filesystem::g_open_read(file, path.c_str(), abort); 83 | file_info_impl info; 84 | info.from_stream(file.get_ptr(), abort); 85 | cache[filename.get_ptr()] = info; 86 | if (source->is_newer(info)) { 87 | updates.add_item(guid); 88 | } 89 | } 90 | } 91 | catch (std::exception& e) { 92 | console::formatter() << "[" APP_BINARY_NAME "] " 93 | << (clean_up ? "cleanup: error deleting " : "error loading cache entry ") 94 | << filename << ": " << e.what(); 95 | } 96 | }); 97 | 98 | { 99 | pfc::mutexScope lock(mutex_); 100 | cache_ = cache; 101 | updates_ = updates; 102 | } 103 | 104 | if (0 != updates.get_count()) { 105 | callbacks_.for_each([&updates](auto callback) { 106 | callback->on_updates_available(updates); 107 | }); 108 | } 109 | } 110 | 111 | void UpdatesImpl::register_callback(callback* callback) { 112 | core_api::ensure_main_thread(); 113 | callbacks_.add_item(callback); 114 | 115 | decltype(updates_) updates; 116 | { 117 | pfc::mutexScope lock(mutex_); 118 | if (0 == updates_.get_count()) { 119 | return; 120 | } 121 | updates = updates_; 122 | } 123 | callback->on_updates_available(updates); 124 | } 125 | 126 | void UpdatesImpl::Save() { 127 | decltype(cache_) cache; 128 | { 129 | pfc::mutexScope lock(mutex_); 130 | cache = cache_; 131 | } 132 | 133 | abort_callback_dummy abort; 134 | auto dir = GetCacheDir(); 135 | create_directory_helper::create_path(dir.c_str(), abort); 136 | 137 | cache.enumerate([&](const auto& key, const auto& value) { 138 | auto path = dir; 139 | path += key; 140 | file_ptr file; 141 | filesystem::g_open_write_new(file, path.c_str(), abort); 142 | value.to_stream(file.get_ptr(), abort); 143 | }); 144 | 145 | if (clean_up_) { 146 | auto path = dir; 147 | path += CLEANUP_FLAG; 148 | try { 149 | filesystem::g_open_write_new(file::ptr(), path, abort); 150 | } 151 | catch (...) { 152 | } 153 | } 154 | } 155 | 156 | void UpdatesImpl::ScheduleCleanup() { 157 | g_cache.get_static_instance().clean_up_ = true; 158 | } 159 | 160 | void UpdatesImpl::set_info(const GUID& guid, const file_info& info) { 161 | file_info_const_impl copy(info); 162 | fb2k::inMainThread([this, guid, copy = std::move(copy)] { 163 | SetInfoInMainThread(guid, copy); 164 | }); 165 | } 166 | 167 | void UpdatesImpl::SetInfoInMainThread(const GUID& guid, const file_info& info) { 168 | { 169 | pfc::mutexScope lock(mutex_); 170 | 171 | auto& existing = cache_[pfc::print_guid(guid).get_ptr()]; 172 | if (file_info::g_is_meta_equal(existing, info) && file_info::g_is_info_equal(existing, info)) { 173 | return; 174 | } 175 | existing = info; 176 | } 177 | 178 | callbacks_.for_each([&guid, &info](auto callback) { 179 | callback->on_info_changed(guid, info); 180 | }); 181 | 182 | bool is_newer = false; 183 | if (auto source = source::g_get(guid); source.is_valid()) { 184 | is_newer = source->is_newer(info); 185 | } 186 | 187 | bool notify_updates_available = false; 188 | decltype(updates_) updates; 189 | { 190 | pfc::mutexScope lock(mutex_); 191 | 192 | auto index = updates_.find_item(guid); 193 | bool known_newer = ~0 != index; 194 | if (known_newer == is_newer) { 195 | return; 196 | } 197 | if (is_newer) { 198 | updates_.add_item(guid); 199 | } 200 | else { 201 | updates_.remove_by_idx(index); 202 | } 203 | updates = updates_; 204 | } 205 | 206 | callbacks_.for_each([&updates](auto callback) { 207 | callback->on_updates_available(updates); 208 | }); 209 | } 210 | 211 | void UpdatesImpl::unregister_callback(callback* callback) { 212 | core_api::ensure_main_thread(); 213 | 214 | auto index = callbacks_.find_item(callback); 215 | if (~0 != index) { 216 | callbacks_.remove_by_idx(index); 217 | } 218 | } 219 | 220 | } // namespace acfu 221 | -------------------------------------------------------------------------------- /foo_acfu/foo_acfu.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {2F23AC85-FE11-4C42-9635-C07EB8CD1F47} 15 | foo_acfu 16 | Win32Proj 17 | 7.0 18 | 19 | 20 | 21 | DynamicLibrary 22 | v141_xp 23 | Unicode 24 | true 25 | 26 | 27 | DynamicLibrary 28 | v141_xp 29 | Unicode 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | <_ProjectFileVersion>15.0.26919.1 43 | 44 | 45 | $(SolutionDir)$(Configuration)\user-components\foo_acfu\ 46 | true 47 | $(SolutionDir)..\rapidjson\include;$(SolutionDir)..\wtl\include;$(IncludePath) 48 | 49 | 50 | $(SolutionDir)$(Configuration)\user-components\foo_acfu\ 51 | false 52 | $(SolutionDir)..\rapidjson\include;$(SolutionDir)..\wtl\include;$(IncludePath) 53 | 54 | 55 | 56 | Disabled 57 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 58 | true 59 | EnableFastChecks 60 | MultiThreadedDebug 61 | Use 62 | Level3 63 | ProgramDatabase 64 | /Zc:threadSafeInit- /std:c++latest %(AdditionalOptions) 65 | ./ 66 | 67 | 68 | ..\foobar2000\shared\shared.lib;%(AdditionalDependencies) 69 | true 70 | Windows 71 | MachineX86 72 | 73 | false 74 | 75 | 76 | 77 | 78 | MinSpace 79 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 80 | MultiThreaded 81 | Use 82 | Level3 83 | ProgramDatabase 84 | true 85 | NoExtensions 86 | /Zc:threadSafeInit- /std:c++latest %(AdditionalOptions) 87 | ./ 88 | 89 | 90 | ..\foobar2000\shared\shared.lib;%(AdditionalDependencies) 91 | true 92 | Windows 93 | true 94 | 95 | 96 | MachineX86 97 | 98 | UseLinkTimeCodeGeneration 99 | false 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Create 119 | Create 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | {93ec0ede-01cd-4fb0-b8e8-4f2a027e026e} 149 | 150 | 151 | {71ad2674-065b-48f5-b8b0-e1f9d3892081} 152 | 153 | 154 | {ee47764e-a202-4f85-a767-abdab4aff35f} 155 | 156 | 157 | {e8091321-d79d-4575-86ef-064ea1a4a20d} 158 | 159 | 160 | {ebfffb4e-261d-44d3-b89c-957b31a0bf9c} 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /foo_acfu/scheduler.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "scheduler.h" 3 | 4 | namespace acfu { 5 | 6 | // {EB73FF9D-D0AF-4232-BFA6-FA21A43C953B} 7 | static const GUID guid_cfg_period = 8 | { 0xeb73ff9d, 0xd0af, 0x4232, { 0xbf, 0xa6, 0xfa, 0x21, 0xa4, 0x3c, 0x95, 0x3b } }; 9 | 10 | cfg_uint cfg_period(guid_cfg_period, Scheduler::kPeriodDefault); 11 | 12 | // {264F8CC9-29A2-4DDE-934E-C8FFA666C6BB} 13 | static const GUID guid_cfg_last_run = 14 | { 0x264f8cc9, 0x29a2, 0x4dde, { 0x93, 0x4e, 0xc8, 0xff, 0xa6, 0x66, 0xc6, 0xbb } }; 15 | 16 | cfg_int_t cfg_last_run(guid_cfg_last_run, 0); 17 | 18 | // {26E16C98-32C8-440D-9E30-BD283FC41DB3} 19 | static const GUID guid_cfg_acfu_sources = 20 | { 0x26e16c98, 0x32c8, 0x440d, { 0x9e, 0x30, 0xbd, 0x28, 0x3f, 0xc4, 0x1d, 0xb3 } }; 21 | 22 | cfg_guidlist cfg_acfu_sources(guid_cfg_acfu_sources); 23 | 24 | // {EB228486-2B26-4705-8B04-28878E9753E7} 25 | FOOGUIDDECL const GUID Scheduler::class_guid = 26 | { 0xeb228486, 0x2b26, 0x4705, { 0x8b, 0x4, 0x28, 0x87, 0x8e, 0x97, 0x53, 0xe7 } }; 27 | 28 | class threaded_process_status_dummy: public threaded_process_status { 29 | public: 30 | virtual void set_progress(t_size p_state) {} 31 | virtual void set_progress_secondary(t_size p_state) {} 32 | virtual void set_item(const char * p_item,t_size p_item_len = ~0) {} 33 | virtual void set_item_path(const char * p_item,t_size p_item_len = ~0) {} 34 | virtual void set_title(const char * p_title,t_size p_title_len = ~0) {} 35 | virtual void force_update() {} 36 | virtual bool is_paused() { return false; } 37 | virtual bool process_pause() { return false; } 38 | }; 39 | 40 | class BackgroundAbort: public abort_callback { 41 | public: 42 | enum { ONE_MINUTE = 60 * 1000 * 10000 }; 43 | 44 | BackgroundAbort(abort_callback& abort): outer_abort_(abort.get_handle()) { 45 | timer_.Attach(CreateWaitableTimer(NULL, TRUE, NULL)); 46 | LARGE_INTEGER due_time; 47 | due_time.QuadPart = - ONE_MINUTE; 48 | SetWaitableTimer(timer_, &due_time, 0, NULL, NULL, FALSE); 49 | } 50 | 51 | virtual bool is_aborting() const override { 52 | HANDLE events[2] = {outer_abort_, timer_}; 53 | return WAIT_TIMEOUT != WaitForMultipleObjects(2, events, FALSE, 0); 54 | } 55 | 56 | virtual abort_callback_event get_abort_event() const override { 57 | return timer_; 58 | } 59 | 60 | bool is_timed_out() const { 61 | return WAIT_TIMEOUT != WaitForSingleObject(timer_, 0); 62 | } 63 | 64 | private: 65 | CHandle timer_; 66 | HANDLE outer_abort_; 67 | }; 68 | 69 | class Check: public threaded_process_callback { 70 | public: 71 | Check(const pfc::list_t& sources, bool background) 72 | : sources_(sources), background_(background) {} 73 | 74 | virtual void run(threaded_process_status& status, abort_callback& abort) { 75 | for (t_size i = 0; i < sources_.get_size(); i ++) { 76 | pfc::string8 name = pfc::print_guid(sources_[i]); 77 | try { 78 | auto source = source::g_get(sources_[i]); 79 | 80 | file_info_impl info; 81 | source->get_info(info); 82 | if (info.meta_exists("name")) { 83 | name = info.meta_get("name", 0); 84 | } 85 | status.set_item(name); 86 | 87 | auto request = source->create_request(); 88 | if (request.is_valid()) { 89 | file_info_impl info = perform(*request, abort); 90 | static_api_ptr_t()->set_info(source->get_guid(), info); 91 | } 92 | } 93 | catch (std::exception& e) { 94 | auto error = pfc::string8(name) << ": " << e.what(); 95 | console::formatter() << APP_SHORT_NAME << ": failed for source " << error; 96 | 97 | if (!background_) { 98 | if (!errors_.is_empty()) { 99 | errors_+= "\n\n"; 100 | } 101 | errors_ += error; 102 | } 103 | } 104 | 105 | status.set_progress_float((1. + i) / sources_.get_size()); 106 | } 107 | } 108 | 109 | virtual void on_done(ctx_t p_wnd, bool p_was_aborted) { 110 | if (!background_ && !errors_.is_empty()) { 111 | pfc::string8 message = "The following errors occurred during the checking for updates:\n\n"; 112 | message += errors_; 113 | popup_message_v2::g_show(GetParent(p_wnd), message.get_ptr()); 114 | } 115 | } 116 | 117 | private: 118 | file_info_impl perform(request& request, abort_callback& abort) { 119 | file_info_impl info; 120 | if (background_) { 121 | BackgroundAbort bg_abort(abort); 122 | try { 123 | request.run(info, bg_abort); 124 | } 125 | catch (exception_aborted&) { 126 | if (bg_abort.is_timed_out()) { 127 | throw std::logic_error("request timed out"); 128 | } 129 | throw; 130 | } 131 | } 132 | else { 133 | request.run(info, abort); 134 | } 135 | return info; 136 | } 137 | 138 | private: 139 | pfc::list_t sources_; 140 | bool background_; 141 | pfc::string8 errors_; 142 | }; 143 | 144 | void Scheduler::Check(HWND parent, const pfc::list_t& sources) { 145 | threaded_process::g_run_modal( 146 | new service_impl_t(sources, false), 147 | threaded_process::flag_show_abort | threaded_process::flag_show_progress | threaded_process::flag_show_item, 148 | parent, 149 | "Checking for Updates..." 150 | ); 151 | } 152 | 153 | class BackgroundCheck: private CSimpleThread { 154 | public: 155 | BackgroundCheck(std::function on_done): on_done_(on_done) {} 156 | 157 | void AbortThread() { 158 | __super::AbortThread(); 159 | } 160 | 161 | void StartThread(const pfc::list_t& sources) { 162 | AbortThread(); 163 | sources_ = sources; 164 | __super::StartThread(THREAD_PRIORITY_BELOW_NORMAL); 165 | } 166 | 167 | private: 168 | virtual void ThreadDone(unsigned p_code) { 169 | on_done_(); 170 | } 171 | 172 | virtual unsigned ThreadProc(abort_callback& abort) { 173 | console::formatter() << APP_SHORT_NAME << ": checking for updates..."; 174 | service_impl_t check(sources_, true); 175 | try { 176 | check.run(threaded_process_status_dummy(), abort); 177 | } 178 | catch (std::exception& e) { 179 | console::formatter() << APP_SHORT_NAME << ": stopped: " << e.what(); 180 | throw; 181 | } 182 | console::formatter() << APP_SHORT_NAME << ": completed"; 183 | 184 | return 0; 185 | } 186 | 187 | private: 188 | pfc::list_t sources_; 189 | std::function on_done_; 190 | }; 191 | 192 | class SchedulerImpl: public Scheduler, public CMessageMap { 193 | public: 194 | enum { 195 | kInitTimerId = 80561, 196 | kAcfuTimerId, 197 | 198 | kInitTimerDelay = 30 * 1000 199 | }; 200 | 201 | BEGIN_MSG_MAP_EX(SchedulerImpl) 202 | MSG_WM_TIMER(OnTimer) 203 | END_MSG_MAP() 204 | 205 | SchedulerImpl(): message_wnd_(L"Message", this, 0), worker_([&] { 206 | cfg_last_run = filetimestamp_from_system_timer(); 207 | SetInitTimer(); 208 | }) {} 209 | 210 | void Init() { 211 | message_wnd_.Create(HWND_MESSAGE, CRect()); 212 | SetInitTimer(); 213 | } 214 | 215 | void Free() { 216 | message_wnd_.DestroyWindow(); 217 | worker_.AbortThread(); 218 | } 219 | 220 | virtual t_uint32 GetPeriod() { 221 | return std::clamp(cfg_period.get_value(), (t_uint32)kPeriodMin, (t_uint32)kPeriodMax); 222 | } 223 | 224 | virtual void SetPeriod(t_uint32 period) { 225 | cfg_period = period; 226 | SetInitTimer(); 227 | } 228 | 229 | private: 230 | void OnTimer(UINT_PTR nIDEvent) { 231 | if (kInitTimerId == nIDEvent) { 232 | SetAcfuTimer(); 233 | } 234 | else if (kAcfuTimerId == nIDEvent) { 235 | SetInitTimer(); 236 | } 237 | } 238 | 239 | void SetAcfuTimer() { 240 | core_api::assert_main_thread(); 241 | 242 | message_wnd_.KillTimer(kInitTimerId); 243 | message_wnd_.KillTimer(kAcfuTimerId); 244 | 245 | auto next_run = GetPeriod() * system_time_periods::day + cfg_last_run; 246 | auto now = filetimestamp_from_system_timer(); 247 | if (0 == cfg_last_run || next_run < now) { 248 | if (0 != cfg_acfu_sources.get_size()) { 249 | worker_.StartThread(cfg_acfu_sources); 250 | return; 251 | } 252 | cfg_last_run = filetimestamp_from_system_timer(); 253 | } 254 | 255 | auto delay = std::min((next_run - now) / system_time_periods::second * 1000, (t_filetimestamp)USER_TIMER_MAXIMUM); 256 | message_wnd_.SetTimer(kAcfuTimerId, (UINT)delay); 257 | console::formatter() << APP_SHORT_NAME << ": next check is scheduled for " << format_filetimestamp(next_run); 258 | } 259 | 260 | void SetInitTimer() { 261 | core_api::assert_main_thread(); 262 | 263 | message_wnd_.KillTimer(kAcfuTimerId); 264 | message_wnd_.SetTimer(kInitTimerId, kInitTimerDelay); 265 | } 266 | 267 | private: 268 | CContainedWindow message_wnd_; 269 | BackgroundCheck worker_; 270 | }; 271 | 272 | static service_factory_single_t g_scheduler; 273 | 274 | class SchedulerInit: public initquit { 275 | virtual void on_init() { 276 | g_scheduler.get_static_instance().Init(); 277 | } 278 | virtual void on_quit() { 279 | g_scheduler.get_static_instance().Free(); 280 | } 281 | }; 282 | 283 | static service_factory_single_t g_scheduler_init; 284 | 285 | } // namespace acfu 286 | -------------------------------------------------------------------------------- /foo_acfu/list_column_auto_size.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /////////////////////////////////////////////////////////////////////////////// 4 | // List controls with columns auto sizing to use whole control width without 5 | // horizontal scroll 6 | // CListColumnAutoSize - size to content all columns except one and this one to 7 | // remaining space 8 | // CListColumnAutoSizeEx - to remaining space can be sized several columns 9 | // 10 | // Classes in this file: 11 | // 12 | // CListColumnAutoSizeImplBase 13 | // CListColumnAutoSizeImpl 14 | // CListColumnAutoSizeExImpl 15 | // CListColumnAutoSize 16 | // CListColumnAutoSizeEx 17 | 18 | #ifndef HDS_NOSIZING 19 | #define HDS_NOSIZING 0x0800 20 | #endif 21 | 22 | template 23 | class CListViewCtrlImplTraits: public ATL::CWinTraits { 24 | public: 25 | static DWORD GetExtendedListViewStyle() { 26 | return t_dwExListViewStyle; 27 | } 28 | }; 29 | 30 | typedef CListViewCtrlImplTraits CListColumnAutoSizeTraits; 31 | 32 | template 33 | class CListColumnAutoSizeImplBase: public ATL::CWindowImpl { 34 | public: 35 | DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName()) 36 | 37 | enum { kHeaderMsgMapId = 1 }; 38 | 39 | BEGIN_MSG_MAP_EX(CListColumnAutoSizeImplBase) 40 | MSG_WM_KEYDOWN(OnKeyDown) 41 | MESSAGE_HANDLER_EX(WM_CREATE, OnCreate) 42 | MESSAGE_HANDLER_EX(WM_NCCALCSIZE, OnNcCalcSize) 43 | MESSAGE_HANDLER_EX(WM_SIZE, OnSize) 44 | MESSAGE_HANDLER_EX(LVM_INSERTITEMA, OnItemChange) 45 | MESSAGE_HANDLER_EX(LVM_INSERTITEMW, OnItemChange) 46 | MESSAGE_HANDLER_EX(LVM_DELETEITEM, OnItemChange) 47 | MESSAGE_HANDLER_EX(LVM_DELETEALLITEMS, OnItemChange) 48 | MESSAGE_HANDLER_EX(LVM_SETITEMA, OnItemChange) 49 | MESSAGE_HANDLER_EX(LVM_SETITEMW, OnItemChange) 50 | MESSAGE_HANDLER_EX(LVM_SETITEMTEXTA, OnItemChange) 51 | MESSAGE_HANDLER_EX(LVM_SETITEMTEXTW, OnItemChange) 52 | NOTIFY_CODE_HANDLER_EX(HDN_BEGINTRACK, OnHeaderBeginTrack) 53 | NOTIFY_CODE_HANDLER_EX(HDN_DIVIDERDBLCLICK, OnHeaderDividerDblclick) 54 | ALT_MSG_MAP(T::kHeaderMsgMapId) // header control message map 55 | MSG_WM_SETCURSOR(OnHeaderSetCursor) 56 | END_MSG_MAP() 57 | 58 | static DWORD GetExtendedLVStyle() { 59 | return TWinTraits::GetExtendedListViewStyle(); 60 | } 61 | 62 | CListColumnAutoSizeImplBase(): header_(this, T::kHeaderMsgMapId), auto_update_(true) {} 63 | 64 | // Turn off auto update is helpful at work with large numbers of items 65 | void EnableAutoUpdate(bool enable) { 66 | auto_update_ = enable; 67 | if (auto_update_) { 68 | UpdateColumnsWidth(); 69 | } 70 | } 71 | 72 | bool IsAutoUpdateEnabled() const { 73 | return auto_update_; 74 | } 75 | 76 | bool IsVariableWidthColumn() const { 77 | ATLASSERT(0); // should be implemented in child class 78 | return false; 79 | } 80 | 81 | BOOL SubclassWindow(HWND hWnd) { 82 | BOOL res = __super::SubclassWindow(hWnd); 83 | if (res) { 84 | T* pT = static_cast(this); 85 | pT->PostInit(); 86 | if (pT->IsAutoUpdateEnabled()) { 87 | pT->UpdateColumnsWidth(); 88 | } 89 | } 90 | return res; 91 | } 92 | 93 | void UpdateColumnsWidth() { 94 | if (NULL != m_hWnd) { 95 | T* pT = static_cast(this); 96 | pT->SetRedraw(FALSE); 97 | pT->UpdateFixedWidthColumns(); 98 | pT->UpdateVariableWidthColumns(); 99 | pT->SetRedraw(TRUE); 100 | } 101 | } 102 | 103 | void UpdateFixedWidthColumns() { 104 | // The easiest way to not screw it up is left resizing to the system. But in 105 | // case of LVSCW_AUTOSIZE_USEHEADER it resizes last column to all remaining 106 | // space. Workaround - made column not last by adding fake column to the end 107 | int count = GetHeader().GetItemCount(); 108 | ATLVERIFY(count == InsertColumn(count, _T(""))); 109 | T* pT = static_cast(this); 110 | for (int i = 0; i < count; i ++) { 111 | if (0 != GetColumnWidth(i) && !pT->IsVariableWidthColumn(i)) { 112 | SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER); 113 | } 114 | } 115 | ATLVERIFY(DeleteColumn(count)); 116 | } 117 | 118 | void UpdateVariableWidthColumns() { 119 | ATLASSERT(0); // should be implemented in child class 120 | } 121 | 122 | protected: 123 | LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam) { 124 | LRESULT lr = DefWindowProc(uMsg, wParam, lParam); 125 | T* pT = static_cast(this); 126 | pT->PostInit(); 127 | 128 | return lr; 129 | } 130 | 131 | LRESULT OnHeaderBeginTrack(LPNMHDR pnmh) { 132 | SetMsgHandled(!WTL::RunTimeHelper::IsVista()); 133 | return TRUE; // prevent tracking 134 | } 135 | 136 | LRESULT OnHeaderDividerDblclick(LPNMHDR pnmh) { 137 | SetMsgHandled(!WTL::RunTimeHelper::IsVista()); 138 | return 0; // prevent reaction (header resizing to content) 139 | } 140 | 141 | BOOL OnHeaderSetCursor(ATL::CWindow wnd, UINT nHitTest, UINT message) { 142 | return TRUE; // prevent cursor change over dividers 143 | } 144 | 145 | LRESULT OnItemChange(UINT uMsg, WPARAM wParam, LPARAM lParam) { 146 | LRESULT lr = DefWindowProc(uMsg, wParam, lParam); 147 | T* pT = static_cast(this); 148 | if (pT->IsAutoUpdateEnabled()) { 149 | pT->UpdateColumnsWidth(); 150 | } 151 | return lr; 152 | } 153 | 154 | void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { 155 | // Block CTRL + Add to prevent columns width auto adjust 156 | SetMsgHandled(VK_ADD == nChar && (GetKeyState(VK_CONTROL) & KF_UP)); 157 | } 158 | 159 | LRESULT OnNcCalcSize(UINT uMsg, WPARAM wParam, LPARAM lParam) { 160 | LRESULT lr = DefWindowProc(uMsg, wParam, lParam); 161 | LPNCCALCSIZE_PARAMS params = (LPNCCALCSIZE_PARAMS)lParam; 162 | if (GetStyle() & WS_HSCROLL) { // to prevent flickering 163 | params->rgrc[0].bottom += GetSystemMetrics(SM_CYHSCROLL); 164 | } 165 | return lr; 166 | } 167 | 168 | LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam) { 169 | T* pT = static_cast(this); 170 | if (pT->IsAutoUpdateEnabled() && SIZE_MINIMIZED != wParam) { 171 | pT->UpdateVariableWidthColumns(); 172 | } 173 | SetMsgHandled(FALSE); 174 | return 0; 175 | } 176 | 177 | void PostInit() { 178 | T* pT = static_cast(this); 179 | SetExtendedListViewStyle(pT->GetExtendedLVStyle(), pT->GetExtendedLVStyle()); 180 | 181 | ATLASSERT(LVS_REPORT == (LVS_TYPEMASK & GetStyle())); 182 | if (WTL::RunTimeHelper::IsVista()) { 183 | GetHeader().ModifyStyle(0, HDS_NOSIZING); 184 | } 185 | else { 186 | ATLVERIFY(header_.SubclassWindow(GetHeader())); 187 | } 188 | } 189 | 190 | private: 191 | ATL::CContainedWindowT header_; 192 | bool auto_update_; 193 | }; 194 | 195 | template 196 | class CListColumnAutoSizeImpl: public CListColumnAutoSizeImplBase { 197 | public: 198 | CListColumnAutoSizeImpl(): variable_width_column_(0) {} 199 | 200 | bool IsVariableWidthColumn(int column) const { 201 | return variable_width_column_ == column; 202 | } 203 | 204 | void SetVariableWidthColumn(int index) { 205 | if (variable_width_column_ != index) { 206 | variable_width_column_ = index; 207 | T* pT = static_cast(this); 208 | if (pT->IsAutoUpdateEnabled() && NULL != m_hWnd) { 209 | pT->UpdateColumnsWidth(); 210 | } 211 | } 212 | } 213 | 214 | void UpdateVariableWidthColumns() { 215 | RECT rect = {0}; 216 | GetClientRect(&rect); 217 | 218 | T* pT = static_cast(this); 219 | int count = GetHeader().GetItemCount(); 220 | for (int i = 0; i < count; i ++) { 221 | if (!pT->IsVariableWidthColumn(i)) { 222 | rect.right -= GetColumnWidth(i); 223 | } 224 | } 225 | SetColumnWidth(variable_width_column_, rect.right - rect.left); 226 | } 227 | 228 | private: 229 | int variable_width_column_; 230 | }; 231 | 232 | template 233 | class CListColumnAutoSizeExImpl: public CListColumnAutoSizeImplBase { 234 | public: 235 | // Width in percents of available space, 0.0 - 0%, 1.0 - 100% 236 | void AddVariableWidthColumn(int index, double relative_width) { 237 | if (-1 != variable_width_columns_.FindKey(index)) { 238 | ATLASSERT(0); // already exist 239 | } 240 | else { 241 | variable_width_columns_.Add(index, relative_width); 242 | 243 | #ifdef _DEBUG // self-check, total width should be <= 100% 244 | double total_width = 0; 245 | for (int i = 0; i < variable_width_columns_.GetSize(); i ++) { 246 | total_width += variable_width_columns_.GetValueAt(i); 247 | } 248 | ATLASSERT(1.0 >= total_width); 249 | #endif 250 | 251 | T* pT = static_cast(this); 252 | if (pT->IsAutoUpdateEnabled()) { 253 | pT->UpdateColumnsWidth(); 254 | } 255 | } 256 | } 257 | 258 | void ClearVariableWidthColumns() { 259 | variable_width_columns_.RemoveAll(); 260 | } 261 | 262 | bool IsVariableWidthColumn(int column) const { 263 | return -1 != variable_width_columns_.FindKey(column); 264 | } 265 | 266 | void UpdateVariableWidthColumns() { 267 | if (0 != variable_width_columns_.GetSize()) { 268 | RECT rect = {0}; 269 | GetClientRect(&rect); 270 | 271 | T* pT = static_cast(this); 272 | int count = GetHeader().GetItemCount(); 273 | for (int i = 0; i < count; i ++) { 274 | if (!pT->IsVariableWidthColumn(i)) { 275 | rect.right -= GetColumnWidth(i); 276 | } 277 | } 278 | 279 | int total_width = rect.right - rect.left; 280 | for (int i = 0; i < variable_width_columns_.GetSize(); i ++) { 281 | int width = int(variable_width_columns_.GetValueAt(i) * total_width); 282 | SetColumnWidth(variable_width_columns_.GetKeyAt(i), width); 283 | } 284 | } 285 | } 286 | 287 | private: 288 | ATL::CSimpleMap variable_width_columns_; 289 | }; 290 | 291 | class CListColumnAutoSize: public CListColumnAutoSizeImpl { 292 | public: 293 | DECLARE_WND_SUPERCLASS(_T("ListColumnAutoSize"), GetWndClassName()) 294 | }; 295 | 296 | class CListColumnAutoSizeEx: public CListColumnAutoSizeExImpl { 297 | public: 298 | DECLARE_WND_SUPERCLASS(_T("ListColumnAutoSizeEx"), GetWndClassName()) 299 | }; 300 | -------------------------------------------------------------------------------- /foo_acfu/preferences_dlg.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "resource.h" 3 | #include "preferences_dlg.h" 4 | #include "embedded/sources_dlg.h" 5 | #include "scheduler.h" 6 | #include "updates.h" 7 | #include "urls.h" 8 | #include "utils.h" 9 | 10 | enum { 11 | kColName, 12 | kColAvailable, 13 | kColInstalled, 14 | kColModule, 15 | }; 16 | 17 | static int s_sort_column = kColName; 18 | static bool s_sort_up = true; 19 | 20 | static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { 21 | CListViewCtrl list((HWND)lParamSort); 22 | 23 | CString str1, str2; 24 | list.GetItemText(lParam1, s_sort_column, str1); 25 | list.GetItemText(lParam2, s_sort_column, str2); 26 | 27 | return str1.CompareNoCase(str2) * (s_sort_up ? 1 : -1); 28 | } 29 | 30 | DWORD SourcesList::OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw) { 31 | return CDRF_DODEFAULT | CDRF_NOTIFYSUBITEMDRAW; 32 | } 33 | 34 | DWORD SourcesList::OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw) { 35 | CDCHandle dc(lpNMCustomDraw->hdc); 36 | default_font_ = dc.GetCurrentFont(); 37 | 38 | if (bold_font_) { 39 | bold_font_.DeleteObject(); 40 | } 41 | CreateScaledFontEx(bold_font_, default_font_, 1, FW_BOLD); 42 | 43 | return CDRF_DODEFAULT | CDRF_NOTIFYITEMDRAW; 44 | } 45 | 46 | DWORD SourcesList::OnSubItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw) { 47 | LPNMLVCUSTOMDRAW info = (LPNMLVCUSTOMDRAW)lpNMCustomDraw; 48 | bool grayed = false; 49 | bool bold = false; 50 | 51 | if (kColAvailable == info->iSubItem || kColInstalled == info->iSubItem) { 52 | LVITEM item = {0}; 53 | item.mask = LVIF_IMAGE; 54 | item.iItem = info->nmcd.dwItemSpec; 55 | if (GetItem(&item)) { 56 | grayed = I_IMAGENONE == item.iImage; 57 | bold = !grayed && kColAvailable == info->iSubItem; 58 | } 59 | } 60 | 61 | // Do not care about themed colors for now 62 | info->clrText = GetSysColor(grayed ? COLOR_GRAYTEXT : COLOR_BTNTEXT); 63 | SelectObject(lpNMCustomDraw->hdc, bold ? bold_font_.m_hFont : default_font_.m_hFont); 64 | 65 | return CDRF_NEWFONT; 66 | } 67 | 68 | PreferencesDlg::PreferencesDlg(preferences_page_callback::ptr callback): callback_(callback) { 69 | acfu::cfg_acfu_sources.sort(); 70 | } 71 | 72 | PreferencesDlg::~PreferencesDlg() { 73 | } 74 | 75 | void PreferencesDlg::apply() { 76 | auto sources = GetCheckedSources(); 77 | pfc::list_t::g_swap(acfu::cfg_acfu_sources, sources); 78 | 79 | t_uint32 period = CUpDownCtrl(GetDlgItem(IDC_PERIOD_SPIN)).GetPos(NULL); 80 | static_api_ptr_t()->SetPeriod(period); 81 | 82 | if (clear_cache_ || embedded::Embedded::IsAnyModified()) { 83 | acfu::UpdatesImpl::ScheduleCleanup(); 84 | } 85 | 86 | callback_->on_state_changed(); 87 | } 88 | 89 | HMENU PreferencesDlg::BuildContextMenu(const acfu::source::ptr& source, const file_info_impl& info) const { 90 | CMenu popup(CreatePopupMenu()); 91 | popup.AppendMenu(MF_STRING, ID_CFU_SINGLE, Tr(ID_CFU_SINGLE)); 92 | if (info.meta_exists("download_page")) { 93 | popup.AppendMenu(MF_STRING, ID_GOTO_DOWNLOAD_PAGE, Tr(ID_GOTO_DOWNLOAD_PAGE)); 94 | } 95 | if (info.meta_exists("download_url")) { 96 | popup.AppendMenu(MF_STRING, ID_DOWNLOAD, Tr(ID_DOWNLOAD)); 97 | } 98 | source->context_menu_build(popup, ID_CONTEXT_MENU_BASE); 99 | 100 | return popup.Detach(); 101 | } 102 | 103 | void PreferencesDlg::FreeList() { 104 | for (int i = list_.GetItemCount() - 1; i >= 0; i --) { 105 | if (auto guid = reinterpret_cast(list_.GetItemData(i))) { 106 | delete guid; 107 | } 108 | } 109 | list_.DeleteAllItems(); 110 | } 111 | 112 | pfc::list_t PreferencesDlg::GetCheckedSources() const { 113 | pfc::list_t sources; 114 | ForEachInList([&](const auto& guid, auto index) { 115 | if (list_.GetCheckState(index)) { 116 | sources.add_item(guid); 117 | } 118 | }); 119 | 120 | sources.sort(); 121 | 122 | return sources; 123 | } 124 | 125 | t_uint32 PreferencesDlg::get_state() { 126 | bool resettable = false, changed = false, need_restart = false; 127 | 128 | auto sources = GetCheckedSources(); 129 | resettable = resettable || 0 != sources.get_count(); 130 | changed = changed || !decltype(sources)::g_equals(sources, acfu::cfg_acfu_sources); 131 | 132 | t_uint32 period = CUpDownCtrl(GetDlgItem(IDC_PERIOD_SPIN)).GetPos(NULL); 133 | resettable = resettable || acfu::Scheduler::kPeriodDefault != period; 134 | changed = changed || static_api_ptr_t()->GetPeriod() != period; 135 | 136 | resettable = resettable || clear_cache_; 137 | changed = changed || clear_cache_; 138 | need_restart = need_restart || clear_cache_; 139 | 140 | bool embedded_modified = embedded::Embedded::IsAnyModified(); 141 | changed = changed || embedded_modified; 142 | need_restart = need_restart || embedded_modified; 143 | 144 | t_uint32 state = resettable ? preferences_state::resettable : 0; 145 | state |= changed ? preferences_state::changed : 0; 146 | state |= need_restart ? preferences_state::needs_restart : 0; 147 | 148 | return state; 149 | } 150 | 151 | void PreferencesDlg::InitList() { 152 | for_each_service([&](auto ptr) { 153 | if (pfc::guid_null == ptr->get_guid()) { 154 | return; 155 | } 156 | 157 | file_info_impl info; 158 | ptr->get_info(info); 159 | 160 | pfc::string8 name; 161 | if (auto value = info.meta_get("name", 0)) { 162 | name = value; 163 | } 164 | else { 165 | name = pfc::print_guid(ptr->get_guid()); 166 | } 167 | 168 | int index = list_.AddItem(list_.GetItemCount(), 0, pfc::stringcvt::string_os_from_utf8(name)); 169 | list_.SetItemData(index, reinterpret_cast(new GUID(ptr->get_guid()))); 170 | if (auto value = info.meta_get("version", 0)) { 171 | list_.SetItemText(index, kColInstalled, pfc::stringcvt::string_os_from_utf8(value)); 172 | } 173 | if (auto value = info.meta_get("module", 0)) { 174 | list_.SetItemText(index, kColModule, pfc::stringcvt::string_os_from_utf8(value)); 175 | } 176 | if (~0 != acfu::cfg_acfu_sources.find_item(ptr->get_guid())) { 177 | list_.SetCheckState(index, TRUE); 178 | } 179 | }); 180 | 181 | SortList(); 182 | } 183 | 184 | void PreferencesDlg::on_info_changed(const GUID& guid, const file_info& info) { 185 | ForEachInList([&](auto item_guid, auto index) { 186 | if (item_guid == guid) { 187 | UpdateListItem(index, guid, info); 188 | } 189 | }); 190 | } 191 | 192 | void PreferencesDlg::OnCfuAll(UINT uNotifyCode, int nID, CWindow wndCtl) { 193 | acfu::Scheduler::Check(m_hWnd, GetCheckedSources()); 194 | } 195 | 196 | void PreferencesDlg::OnCfuSingle(UINT uNotifyCode, int nID, CWindow wndCtl) { 197 | if (auto guid_ptr = reinterpret_cast(list_.GetItemData(list_.GetSelectedIndex()))) { 198 | acfu::check::g_check(m_hWnd, *guid_ptr); 199 | } 200 | } 201 | 202 | void PreferencesDlg::OnClearCache(UINT uNotifyCode, int nID, CWindow wndCtl) { 203 | clear_cache_ = true; 204 | callback_->on_state_changed(); 205 | } 206 | 207 | void PreferencesDlg::OnContextMenu(CWindow wnd, _WTYPES_NS::CPoint point) { 208 | if (list_ != wnd) { 209 | return SetMsgHandled(FALSE); 210 | } 211 | 212 | auto guid_ptr = reinterpret_cast(list_.GetItemData(list_.GetSelectedIndex())); 213 | if (!guid_ptr) { 214 | return; 215 | } 216 | 217 | file_info_impl info; 218 | static_api_ptr_t()->get_info(*guid_ptr, info); 219 | auto source = acfu::source::g_get(*guid_ptr); 220 | file_info_impl source_info; 221 | source->get_info(source_info); 222 | info.merge_fallback(source_info); 223 | 224 | CMenu popup(BuildContextMenu(source, info)); 225 | ListView_FixContextMenuPoint(list_, point); 226 | if (unsigned cmd = popup.TrackPopupMenu(TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, m_hWnd)) { 227 | if (cmd >= ID_CONTEXT_MENU_BASE) { 228 | source->context_menu_command(cmd, ID_CONTEXT_MENU_BASE); 229 | } 230 | else if (ID_GOTO_DOWNLOAD_PAGE == cmd || ID_DOWNLOAD == cmd) { 231 | const char* url = info.meta_get(ID_DOWNLOAD == cmd ? "download_url" : "download_page", 0); 232 | ShellExecute(NULL, L"open", pfc::stringcvt::string_os_from_utf8(url), NULL, NULL, SW_SHOWNORMAL); 233 | } 234 | else { 235 | PostMessage(WM_COMMAND, cmd); 236 | } 237 | } 238 | } 239 | 240 | void PreferencesDlg::OnDestroy() { 241 | FreeList(); 242 | static_api_ptr_t()->unregister_callback(this); 243 | } 244 | 245 | BOOL PreferencesDlg::OnInitDialog(CWindow wndFocus, LPARAM lInitParam) { 246 | SetWindowText(TEXT(APP_SHORT_NAME)); 247 | 248 | CreateScaledFontEx(title_font_, GetFont(), 1, FW_BOLD); 249 | GetDlgItem(IDC_AVAILABLE_SOURCES).SetFont(title_font_); 250 | 251 | CUpDownCtrl spin(GetDlgItem(IDC_PERIOD_SPIN)); 252 | spin.SetRange(acfu::Scheduler::kPeriodMin, acfu::Scheduler::kPeriodMax); 253 | spin.SetPos(static_api_ptr_t()->GetPeriod()); 254 | 255 | list_.SubclassWindow(GetDlgItem(IDC_SOURCE_LIST)); 256 | ::SetWindowTheme(list_, L"explorer", NULL); 257 | DWORD style = LVS_EX_DOUBLEBUFFER | LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP; 258 | list_.SetExtendedListViewStyle(style, style); 259 | 260 | CImageListManaged il; 261 | il.Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32, 0, 1); 262 | il.AddIcon(LoadIcon(NULL, IDI_EXCLAMATION)); 263 | list_.SetImageList(il.Detach(), LVSIL_SMALL); 264 | 265 | ATLVERIFY(kColName == list_.InsertColumn(kColName, Tr(IDS_COL_NAME))); 266 | ATLVERIFY(kColAvailable == list_.InsertColumn(kColAvailable, Tr(IDS_COL_AVAILABLE))); 267 | ATLVERIFY(kColInstalled == list_.InsertColumn(kColInstalled, Tr(IDS_COL_INSTALLED))); 268 | ATLVERIFY(kColModule == list_.InsertColumn(kColModule, Tr(IDS_COL_MODULE))); 269 | 270 | InitList(); 271 | UpdateList(); 272 | 273 | split_.SubclassWindow(GetDlgItem(IDC_CFU_ALL)); 274 | 275 | static_api_ptr_t()->register_callback(this); 276 | 277 | return TRUE; 278 | } 279 | 280 | void PreferencesDlg::OnManagedEmbedded(UINT uNotifyCode, int nID, CWindow wndCtl) { 281 | embedded::SourceDlg dlg; 282 | if (IDOK == dlg.DoModal(m_hWnd)) { 283 | callback_->on_state_changed(); 284 | } 285 | } 286 | 287 | LRESULT PreferencesDlg::OnListColunmClick(LPNMHDR pnmh) { 288 | LPNMLISTVIEW info = (LPNMLISTVIEW)pnmh; 289 | if (info->iSubItem == s_sort_column) { 290 | s_sort_up = !s_sort_up; 291 | } 292 | else { 293 | s_sort_column = info->iSubItem; 294 | } 295 | SortList(); 296 | 297 | return 0; 298 | } 299 | 300 | LRESULT PreferencesDlg::OnListItemChanged(LPNMHDR pnmh) { 301 | LPNMLISTVIEW info = (LPNMLISTVIEW)pnmh; 302 | 303 | if (info->uChanged & LVIF_STATE) { 304 | UINT state_new = info->uNewState & LVIS_STATEIMAGEMASK; 305 | UINT state_old = info->uOldState & LVIS_STATEIMAGEMASK; 306 | 307 | if (0 != state_new && 0 != state_old && state_new != state_old) { 308 | callback_->on_state_changed(); 309 | } 310 | } 311 | 312 | SetMsgHandled(FALSE); 313 | return 0; 314 | } 315 | 316 | void PreferencesDlg::OnPeriodEdit(UINT uNotifyCode, int nID, CWindow wndCtl) { 317 | BOOL error = FALSE; 318 | if (0 != CUpDownCtrl(GetDlgItem(IDC_PERIOD_SPIN)).GetPos(&error) && !error) { 319 | callback_->on_state_changed(); 320 | } 321 | } 322 | 323 | LRESULT PreferencesDlg::OnSplitDropDown(LPNMHDR pnmh) { 324 | CMenu menu; 325 | ATLVERIFY(menu.LoadMenu(IDR_MAIN_POPUP)); 326 | CMenuHandle popup = menu.GetSubMenu(0); 327 | 328 | if (clear_cache_) { 329 | CString what, format, message; 330 | ATLVERIFY(popup.GetMenuString(ID_CLEAR_CACHE, what, MF_BYCOMMAND)); 331 | ATLVERIFY(format.LoadString(IDS_NEED_APPLY)); 332 | message.Format(format, static_cast(what)); 333 | 334 | MENUITEMINFO info = {sizeof(MENUITEMINFO)}; 335 | info.fState = MFS_GRAYED | MFS_CHECKED; 336 | info.dwTypeData = message.GetBuffer(); 337 | info.fMask = MIIM_STATE | MIIM_STRING; 338 | menu.SetMenuItemInfo(ID_CLEAR_CACHE, FALSE, &info); 339 | } 340 | 341 | CRect rect; 342 | GetDlgItem(IDC_CFU_ALL).GetWindowRect(&rect); 343 | popup.TrackPopupMenu(0, rect.left, rect.bottom, m_hWnd, &rect); 344 | 345 | return 0; 346 | } 347 | 348 | LRESULT PreferencesDlg::OnWhy(LPNMHDR pnmh) { 349 | ShellExecute(NULL, L"open", TEXT(APP_URL_WHY), NULL, NULL, SW_SHOWNORMAL); 350 | return 0; 351 | } 352 | 353 | void PreferencesDlg::reset() { 354 | for (int i = list_.GetItemCount() - 1; i >= 0; i --) { 355 | list_.SetCheckState(i, FALSE); 356 | } 357 | CUpDownCtrl(GetDlgItem(IDC_PERIOD_SPIN)).SetPos(acfu::Scheduler::kPeriodDefault); 358 | clear_cache_ = false; 359 | 360 | callback_->on_state_changed(); 361 | } 362 | 363 | void PreferencesDlg::SortList() { 364 | HeaderControl_SetSortIndicator(list_.GetHeader(), s_sort_column, s_sort_up); 365 | list_.SortItemsEx(CompareFunc, (LPARAM)list_.m_hWnd); 366 | } 367 | 368 | void PreferencesDlg::UpdateList() { 369 | static_api_ptr_t updates; 370 | ForEachInList([&](auto guid, auto index) { 371 | file_info_impl info; 372 | updates->get_info(guid, info); 373 | UpdateListItem(index, guid, info); 374 | }); 375 | } 376 | 377 | void PreferencesDlg::UpdateListItem(int index, const GUID& guid, const file_info& info) { 378 | bool is_newer = acfu::source::g_get(guid)->is_newer(info); 379 | 380 | CString last_version; 381 | if (auto value = info.meta_get("version", 0)) { 382 | last_version = pfc::stringcvt::string_os_from_utf8(value); 383 | } 384 | else if (is_newer) { 385 | ATLVERIFY(last_version.LoadString(IDS_GREATER_VERSION)); 386 | } 387 | list_.SetItemText(index, kColAvailable, last_version); 388 | 389 | LVITEM item = {0}; 390 | item.mask = LVIF_IMAGE; 391 | item.iItem = index; 392 | item.iImage = is_newer ? 0 : I_IMAGENONE; 393 | list_.SetItem(&item); 394 | } 395 | --------------------------------------------------------------------------------