diff --git a/known.json b/settings.json similarity index 89% rename from known.json rename to settings.json index 2d655c3..8f15c36 100644 --- a/known.json +++ b/settings.json @@ -5,6 +5,7 @@ "process_names": [ "AppleMusic.exe" ], + "enabled": true, "search_endpoint": "https://music.apple.com/search?term=", "client_id": "1245257240890310686" }, @@ -13,6 +14,7 @@ "process_names": [ "Spotify.exe" ], + "enabled": true, "search_endpoint": "https://open.spotify.com/search/", "client_id": "1245257414715113573" }, @@ -21,6 +23,7 @@ "process_names": [ "TIDAL.exe" ], + "enabled": true, "search_endpoint": "https://listen.tidal.com/search?q=", "client_id": "1245257493966225488" } diff --git a/src/main.cpp b/src/main.cpp index 5b4b4ae..54516c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -102,51 +103,134 @@ void handleMediaTasks() { Discord_UpdatePresence(&activity); } } -class MyTaskBarIcon : public wxTaskBarIcon { +class PlayerLinkIcon : public wxTaskBarIcon { public: - MyTaskBarIcon(wxFrame* mainFrame) : m_mainFrame(mainFrame) {} + PlayerLinkIcon(wxFrame* s) : settingsFrame(s) {} - void OnMenuOpen(wxCommandEvent& evt) { m_mainFrame->Show(true); } + void OnMenuOpen(wxCommandEvent& evt) { settingsFrame->Show(true); } - void OnMenuExit(wxCommandEvent& evt) { m_mainFrame->Close(true); } + void OnMenuExit(wxCommandEvent& evt) { settingsFrame->Close(true); } protected: virtual wxMenu* CreatePopupMenu() override { wxMenu* menu = new wxMenu; - menu->Append(10001, "Open"); + menu->Append(10004, _("Not Playing")); // TODO: make this dynamic + menu->Enable(10004, false); menu->AppendSeparator(); - menu->Append(10002, "Quit"); - Bind(wxEVT_MENU, &MyTaskBarIcon::OnMenuOpen, this, 10001); - Bind(wxEVT_MENU, &MyTaskBarIcon::OnMenuExit, this, 10002); + menu->Append(10001, _("Settings")); + menu->Append(10003, _("About PlayerLink")); + menu->AppendSeparator(); + menu->Append(10002, _("Quit PlayerLink...")); + Bind(wxEVT_MENU, &PlayerLinkIcon::OnMenuOpen, this, 10001); + Bind(wxEVT_MENU, &PlayerLinkIcon::OnMenuExit, this, 10002); return menu; } private: - wxFrame* m_mainFrame; + wxFrame* settingsFrame; }; -class MyApp : public wxApp { + +class PlayerLinkFrame : public wxFrame { +protected: + wxStaticText* settingsText; + wxStaticLine* settingsDivider; + wxStaticText* enabledAppsText; + wxCheckBox* anyOtherCheckbox; + wxStaticLine* appsDivider; + wxStaticText* startupText; + wxCheckBox* autostartCheckbox; + +public: + PlayerLinkFrame(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString, + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(300, 200), + long style = wxDEFAULT_FRAME_STYLE & ~wxRESIZE_BORDER & ~wxMAXIMIZE_BOX) + : wxFrame(parent, id, title, pos, size, style) { + this->SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* mainContainer; + mainContainer = new wxBoxSizer(wxVERTICAL); + + settingsText = new wxStaticText(this, wxID_ANY, _("Settings"), wxDefaultPosition, wxDefaultSize, 0); + settingsText->Wrap(-1); + mainContainer->Add(settingsText, 0, wxALIGN_CENTER | wxALL, 5); + + settingsDivider = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL); + mainContainer->Add(settingsDivider, 0, wxEXPAND | wxALL, 5); + + wxBoxSizer* enabledAppsContainer; + enabledAppsContainer = new wxBoxSizer(wxHORIZONTAL); + + enabledAppsText = new wxStaticText(this, wxID_ANY, _("Enabled Apps:"), wxDefaultPosition, wxDefaultSize, 0); + enabledAppsText->Wrap(-1); + enabledAppsContainer->Add(enabledAppsText, 0, wxALL, 5); + + wxBoxSizer* appCheckboxContainer; + appCheckboxContainer = new wxBoxSizer(wxVERTICAL); + + auto apps = utils::getAllApps(); + + for (auto app : apps) { + auto checkbox = new wxCheckBox(this, wxID_ANY, _(app.appName), wxDefaultPosition, wxDefaultSize, 0); + appCheckboxContainer->Add(checkbox, 0, wxALL, 5); + } + + anyOtherCheckbox = new wxCheckBox(this, wxID_ANY, _("Any other"), wxDefaultPosition, wxDefaultSize, 0); + appCheckboxContainer->Add(anyOtherCheckbox, 0, wxALL, 5); + + enabledAppsContainer->Add(appCheckboxContainer, 1, wxEXPAND, 5); + + mainContainer->Add(enabledAppsContainer, 0, 0, 5); + + appsDivider = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL); + mainContainer->Add(appsDivider, 0, wxEXPAND | wxALL, 5); + + wxBoxSizer* settingsContainer; + settingsContainer = new wxBoxSizer(wxHORIZONTAL); + + startupText = new wxStaticText(this, wxID_ANY, _("Startup:"), wxDefaultPosition, wxDefaultSize, 0); + startupText->Wrap(-1); + settingsContainer->Add(startupText, 0, wxALL, 5); + + autostartCheckbox = new wxCheckBox(this, wxID_ANY, _("Launch at login"), wxDefaultPosition, wxDefaultSize, 0); + settingsContainer->Add(autostartCheckbox, 0, wxALL, 5); + + mainContainer->Add(settingsContainer, 0, wxEXPAND, 5); + + this->SetSizerAndFit(mainContainer); + + wxSize currentSize = this->GetSize(); + this->SetSize(size.GetWidth(), currentSize.GetHeight()); + this->Layout(); + + this->Centre(wxBOTH); + } +}; +class PlayerLink : public wxApp { public: virtual bool OnInit() override { + if (wxSystemSettings::GetAppearance().IsSystemDark()) // To support the native dark mode on windows 10 and up + this->SetAppearance(wxAppBase::Appearance::Dark); + wxInitAllImageHandlers(); - wxFrame* frame = new wxFrame(nullptr, wxID_ANY, "Hello wxWidgets", wxDefaultPosition, wxSize(400, 300)); - trayIcon = new MyTaskBarIcon(frame); + PlayerLinkFrame* frame = new PlayerLinkFrame(nullptr, wxID_ANY, _("PlayerLink")); + trayIcon = new PlayerLinkIcon(frame); frame->Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent& event) { - trayIcon->RemoveIcon(); - trayIcon->Destroy(); - event.Skip(); + if (event.CanVeto()) { + frame->Hide(); + event.Veto(); + } else + this->ExitMainLoop(); }); wxIcon icon = utils::loadIconFromMemory(icon_png, icon_png_size); - trayIcon->SetIcon(icon, "My App"); - wxStaticText* text = new wxStaticText(frame, wxID_ANY, "Hello World", wxPoint(150, 130)); - frame->Show(true); + trayIcon->SetIcon(icon, _("PlayerLink")); return true; } private: - MyTaskBarIcon* trayIcon; + PlayerLinkIcon* trayIcon; }; -wxIMPLEMENT_APP_NO_MAIN(MyApp); +wxIMPLEMENT_APP_NO_MAIN(PlayerLink); int main(int argc, char** argv) { std::thread rpcThread(handleRPCTasks); diff --git a/src/utils.hpp b/src/utils.hpp index 770c232..812cd7d 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -9,15 +9,24 @@ #include #include #include +#include #define DEFAULT_CLIENT_ID "1301849203378622545" #define DEFAULT_APP_NAME "Music" -#define CONFIG_FILENAME "known.json" +#define CONFIG_FILENAME "settings.json" namespace utils { + struct App { + bool enabled; + std::string appName; + std::string clientId; + std::string searchEndpoint; + std::vector processNames; + }; + inline wxIcon loadIconFromMemory(const unsigned char* data, size_t size) { wxMemoryInputStream stream(data, size); - wxImage img(stream, wxBITMAP_TYPE_PNG); + wxImage img(stream, wxBITMAP_TYPE_PNG); if (img.IsOk()) { wxBitmap bmp(img); wxIcon icon; @@ -26,6 +35,7 @@ namespace utils { } return wxNullIcon; } + inline std::string ltrim(std::string& s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); return s; @@ -35,11 +45,13 @@ namespace utils { s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end()); return s; } + inline std::string trim(std::string& s) { ltrim(s); rtrim(s); return s; } + inline std::string urlEncode(std::string str) { std::string new_str = ""; char c; @@ -101,8 +113,12 @@ namespace utils { return ""; } - inline nlohmann::json getApp(std::string processName) { - std::ifstream i("known.json"); + inline std::vector getAllApps() { + std::vector results; + if (!std::filesystem::exists(CONFIG_FILENAME)) + return results; + + std::ifstream i(CONFIG_FILENAME); std::stringstream s; s << i.rdbuf(); i.close(); @@ -111,36 +127,49 @@ namespace utils { nlohmann::json j = nlohmann::json::parse(s.str()); auto apps = j["apps"]; for (auto app : apps) { + App a; + a.appName = app["name"].get(); + a.clientId = app["client_id"].get(); + a.searchEndpoint = app["search_endpoint"].get(); + a.enabled = app["enabled"].get(); auto processNames = app["process_names"]; - for (auto process : processNames) { - if (process.get() == processName) - return app; - } + for (auto process : processNames) a.processNames.push_back(process.get()); + results.push_back(a); } } catch (nlohmann::json::parse_error& ex) { } // TODO: handle parse errors - return nlohmann::json(); + return results; + } + + inline App getApp(std::string processName) { + auto apps = getAllApps(); + for (auto app : apps) { + for(auto procName : app.processNames) { + if(procName == processName) + return app; + } + } + App a; + a.clientId = DEFAULT_CLIENT_ID; + a.appName = DEFAULT_APP_NAME; + a.enabled = true; + a.searchEndpoint = ""; + return a; } inline std::string getClientID(std::string processName) { - if (!std::filesystem::exists(CONFIG_FILENAME)) - return DEFAULT_CLIENT_ID; auto app = getApp(processName); - return app.contains("client_id") ? app["client_id"].get() : DEFAULT_CLIENT_ID; + return app.clientId; } inline std::string getAppName(std::string processName) { - if (!std::filesystem::exists(CONFIG_FILENAME)) - return DEFAULT_APP_NAME; auto app = getApp(processName); - return app.contains("name") ? app["name"].get() : DEFAULT_APP_NAME; + return app.appName; } inline std::string getSearchEndpoint(std::string processName) { - if (!std::filesystem::exists(CONFIG_FILENAME)) - return ""; auto app = getApp(processName); - return app.contains("search_endpoint") ? app["search_endpoint"].get() : ""; + return app.searchEndpoint; } } // namespace utils