From 012c6c45f1b30e55e9bf4259fcff6e63516ccfe3 Mon Sep 17 00:00:00 2001 From: Adam Honse Date: Mon, 16 Feb 2026 10:45:45 -0600 Subject: [PATCH] Profile editor dialog --- PluginManager.cpp | 26 + PluginManager.h | 1 + PluginManagerInterface.h | 23 +- ProfileManager.cpp | 227 ++++++++- ProfileManager.h | 8 +- qt/OpenRGBDialog/OpenRGBDialog.cpp | 96 +--- qt/OpenRGBFont.cpp | 81 ++++ qt/OpenRGBFont.h | 2 + .../OpenRGBProfileEditorDialog.cpp | 453 ++++++++++++++++++ .../OpenRGBProfileEditorDialog.h | 49 ++ .../OpenRGBProfileEditorDialog.ui | 211 ++++++++ 11 files changed, 1075 insertions(+), 102 deletions(-) create mode 100644 qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.cpp create mode 100644 qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.h create mode 100644 qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.ui diff --git a/PluginManager.cpp b/PluginManager.cpp index 7abad7fa8..7ac70e64e 100644 --- a/PluginManager.cpp +++ b/PluginManager.cpp @@ -616,6 +616,32 @@ nlohmann::json PluginManager::OnProfileSave() return(plugin_json); } +nlohmann::json PluginManager::OnProfileSave(std::vector enabled_plugins) +{ + nlohmann::json plugin_json; + + /*-----------------------------------------------------*\ + | Loop through all plugins and gather their profile | + | data if the plugin name is in the enabled list | + \*-----------------------------------------------------*/ + for(std::size_t plugin_idx = 0; plugin_idx < ActivePlugins.size(); plugin_idx++) + { + if(ActivePlugins[plugin_idx].enabled && ActivePlugins[plugin_idx].loader->isLoaded()) + { + for(std::size_t enabled_plugin_idx = 0; enabled_plugin_idx < enabled_plugins.size(); enabled_plugin_idx++) + { + if(enabled_plugins[enabled_plugin_idx] == ActivePlugins[plugin_idx].info.Name) + { + plugin_json[ActivePlugins[plugin_idx].plugin->GetPluginInfo().Name] = ActivePlugins[plugin_idx].plugin->OnProfileSave(); + break; + } + } + } + } + + return(plugin_json); +} + unsigned char * PluginManager::OnSDKCommand(unsigned int plugin_idx, unsigned int pkt_id, unsigned char * pkt_data, unsigned int * pkt_size) { unsigned char * out_data = NULL; diff --git a/PluginManager.h b/PluginManager.h index 7494438ef..0ddb2ef28 100644 --- a/PluginManager.h +++ b/PluginManager.h @@ -82,6 +82,7 @@ public: void OnProfileAboutToLoad(); void OnProfileLoad(nlohmann::json profile_data); nlohmann::json OnProfileSave(); + nlohmann::json OnProfileSave(std::vector enabled_plugins); /*-----------------------------------------------------*\ | Plugin SDK Integration | diff --git a/PluginManagerInterface.h b/PluginManagerInterface.h index 416479b50..2ee292c84 100644 --- a/PluginManagerInterface.h +++ b/PluginManagerInterface.h @@ -20,29 +20,30 @@ public: /*-----------------------------------------------------*\ | Plugin Information | \*-----------------------------------------------------*/ - virtual unsigned int GetPluginCount() = 0; - virtual std::string GetPluginDescription(unsigned int plugin_idx) = 0; - virtual std::string GetPluginName(unsigned int plugin_idx) = 0; - virtual unsigned int GetPluginProtocolVersion(unsigned int plugin_idx) = 0; - virtual std::string GetPluginVersion(unsigned int plugin_idx) = 0; + virtual unsigned int GetPluginCount() = 0; + virtual std::string GetPluginDescription(unsigned int plugin_idx) = 0; + virtual std::string GetPluginName(unsigned int plugin_idx) = 0; + virtual unsigned int GetPluginProtocolVersion(unsigned int plugin_idx) = 0; + virtual std::string GetPluginVersion(unsigned int plugin_idx) = 0; /*-----------------------------------------------------*\ | Plugin-Created RGBControllers | \*-----------------------------------------------------*/ - virtual std::vector GetRGBControllers() = 0; + virtual std::vector GetRGBControllers() = 0; /*-----------------------------------------------------*\ | Plugin Management | \*-----------------------------------------------------*/ - virtual void LoadPlugins() = 0; - virtual void UnloadPlugins() = 0; + virtual void LoadPlugins() = 0; + virtual void UnloadPlugins() = 0; /*-----------------------------------------------------*\ | Plugin Profile Integration | \*-----------------------------------------------------*/ - virtual void OnProfileAboutToLoad() = 0; - virtual void OnProfileLoad(nlohmann::json profile_data) = 0; - virtual nlohmann::json OnProfileSave() = 0; + virtual void OnProfileAboutToLoad() = 0; + virtual void OnProfileLoad(nlohmann::json profile_data) = 0; + virtual nlohmann::json OnProfileSave() = 0; + virtual nlohmann::json OnProfileSave(std::vector enabled_plugins) = 0; /*-----------------------------------------------------*\ | Plugin SDK Integration | diff --git a/ProfileManager.cpp b/ProfileManager.cpp index 2e6aeefae..599723540 100644 --- a/ProfileManager.cpp +++ b/ProfileManager.cpp @@ -111,6 +111,126 @@ void ProfileManager::ClearActiveProfile() SetActiveProfile(""); } +bool ProfileManager::CompareControllers(RGBController* controller_1, RGBController* controller_2) +{ + /*-----------------------------------------------------*\ + | Do not compare location string for HID devices, as | + | the location string may change between runs as | + | devices are connected and disconnected. Also do not | + | compare the I2C bus number, since it is not | + | persistent across reboots on Linux - strip the I2C | + | number and compare only address. | + \*-----------------------------------------------------*/ + bool location_check; + + if(controller_1->GetLocation().find("HID: ") == 0) + { + location_check = true; + } + else if(controller_1->GetLocation().find("I2C: ") == 0) + { + std::size_t loc = controller_1->GetLocation().rfind(", "); + if(loc == std::string::npos) + { + location_check = false; + } + else + { + std::string i2c_address = controller_1->GetLocation().substr(loc + 2); + location_check = controller_2->GetLocation().find(i2c_address) != std::string::npos; + } + } + else + { + location_check = controller_2->GetLocation() == controller_1->GetLocation(); + } + + /*-----------------------------------------------------*\ + | Compare top-level controller information | + \*-----------------------------------------------------*/ + if((controller_1->GetDeviceType() != controller_2->GetDeviceType() ) + || (controller_1->GetName() != controller_2->GetName() ) + || (controller_1->GetDescription() != controller_2->GetDescription()) + || (controller_1->GetVersion() != controller_2->GetVersion() ) + || (controller_1->GetSerial() != controller_2->GetSerial() ) + || (location_check != true )) + { + return(false); + } + + /*-----------------------------------------------------*\ + | Compare modes | + \*-----------------------------------------------------*/ + if(controller_1->modes.size() != controller_2->modes.size()) + { + return(false); + } + else + { + for(std::size_t mode_index = 0; mode_index < controller_1->modes.size(); mode_index++) + { + if((controller_1->GetModeName(mode_index) != controller_2->GetModeName(mode_index) ) + || (controller_1->GetModeValue(mode_index) != controller_2->GetModeValue(mode_index) ) + || (controller_1->GetModeFlags(mode_index) != controller_2->GetModeFlags(mode_index) ) + || (controller_1->GetModeSpeedMin(mode_index) != controller_2->GetModeSpeedMin(mode_index) ) + || (controller_1->GetModeSpeedMax(mode_index) != controller_2->GetModeSpeedMax(mode_index) ) + || (controller_1->GetModeBrightnessMin(mode_index) != controller_2->GetModeBrightnessMin(mode_index)) + || (controller_1->GetModeBrightnessMax(mode_index) != controller_2->GetModeBrightnessMax(mode_index)) + || (controller_1->GetModeColorsMin(mode_index) != controller_2->GetModeColorsMin(mode_index) ) + || (controller_1->GetModeColorsMax(mode_index) != controller_2->GetModeColorsMax(mode_index) )) + { + return(false); + } + } + } + /*-----------------------------------------------------*\ + | Compare zones | + \*-----------------------------------------------------*/ + if(controller_1->zones.size() != controller_2->zones.size()) + { + return(false); + } + else + { + for(std::size_t zone_index = 0; zone_index < controller_1->zones.size(); zone_index++) + { + if((controller_1->GetZoneName(zone_index) != controller_2->GetZoneName(zone_index) ) + || (controller_1->GetZoneType(zone_index) != controller_2->GetZoneType(zone_index) ) + || (controller_1->GetZoneLEDsMin(zone_index) != controller_2->GetZoneLEDsMin(zone_index) ) + || (controller_1->GetZoneLEDsMax(zone_index) != controller_2->GetZoneLEDsMax(zone_index) ) + || (controller_1->GetZoneModeCount(zone_index) != controller_2->GetZoneModeCount(zone_index))) + { + return(false); + } + + if(controller_1->GetZoneModeCount(zone_index) != controller_2->GetZoneModeCount(zone_index)) + { + return(false); + } + else + { + for(std::size_t mode_index = 0; mode_index < controller_1->GetZoneModeCount(zone_index); mode_index++) + { + if((controller_1->GetZoneModeName(zone_index, mode_index) != controller_2->GetZoneModeName(zone_index, mode_index) ) + || (controller_1->GetZoneModeValue(zone_index, mode_index) != controller_2->GetZoneModeValue(zone_index, mode_index) ) + || (controller_1->GetZoneModeFlags(zone_index, mode_index) != controller_2->GetZoneModeFlags(zone_index, mode_index) ) + || (controller_1->GetZoneModeSpeedMin(zone_index, mode_index) != controller_2->GetZoneModeSpeedMin(zone_index, mode_index) ) + || (controller_1->GetZoneModeSpeedMax(zone_index, mode_index) != controller_2->GetZoneModeSpeedMax(zone_index, mode_index) ) + || (controller_1->GetZoneModeBrightnessMin(zone_index, mode_index) != controller_2->GetZoneModeBrightnessMin(zone_index, mode_index)) + || (controller_1->GetZoneModeBrightnessMax(zone_index, mode_index) != controller_2->GetZoneModeBrightnessMax(zone_index, mode_index)) + || (controller_1->GetZoneModeColorsMin(zone_index, mode_index) != controller_2->GetZoneModeColorsMin(zone_index, mode_index) ) + || (controller_1->GetZoneModeColorsMax(zone_index, mode_index) != controller_2->GetZoneModeColorsMax(zone_index, mode_index) )) + { + return(false); + } + } + } + } + } + + return(true); +} + void ProfileManager::DeleteProfile(std::string profile_name) { if(ResourceManager::get()->IsLocalClient() && (ResourceManager::get()->GetLocalClient()->GetSupportsProfileManagerAPI())) @@ -133,7 +253,7 @@ std::string ProfileManager::GetActiveProfile() return(active_profile); } -std::vector ProfileManager::GetControllerListFromProfile(nlohmann::json profile_json) +std::vector ProfileManager::GetControllerListFromProfileJson(nlohmann::json profile_json) { std::vector temp_controllers; @@ -155,6 +275,11 @@ std::vector ProfileManager::GetControllerListFromProfile(nlohman return(temp_controllers); } +std::vector ProfileManager::GetControllerListFromProfileName(std::string profile_name) +{ + return(GetControllerListFromProfileJson(ReadProfileJSON(profile_name))); +} + std::vector ProfileManager::GetControllerListFromSizes() { /*-----------------------------------------------------*\ @@ -163,7 +288,7 @@ std::vector ProfileManager::GetControllerListFromSizes() filesystem::path filename = configuration_directory / "Sizes.json"; nlohmann::json sizes_json = ReadProfileFileJSON(filename); - return(GetControllerListFromProfile(sizes_json)); + return(GetControllerListFromProfileJson(sizes_json)); } std::vector ProfileManager::GetProfileList() @@ -429,6 +554,78 @@ bool ProfileManager::SaveProfile(std::string profile_name) } } +bool ProfileManager::SaveProfileCustom(std::string profile_name, std::vector controllers, RGBColor base_color, bool base_color_enabled, std::vector enabled_plugins) +{ + /*-----------------------------------------------------*\ + | If a name was entered, save the profile file | + \*-----------------------------------------------------*/ + if(profile_name != "") + { + /*-------------------------------------------------*\ + | Start filling in profile json data | + \*-------------------------------------------------*/ + nlohmann::json profile_json; + + profile_json["profile_version"] = OPENRGB_PROFILE_VERSION; + profile_json["profile_name"] = profile_name; + + /*-------------------------------------------------*\ + | Write base color data if enabled | + \*-------------------------------------------------*/ + if(base_color_enabled) + { + profile_json["base_color"] = base_color; + } + + /*-------------------------------------------------*\ + | Write controller data for each controller | + \*-------------------------------------------------*/ + for(std::size_t controller_index = 0; controller_index < controllers.size(); controller_index++) + { + /*---------------------------------------------*\ + | Read the controller data for this controller | + | into the profile json | + \*---------------------------------------------*/ + profile_json["controllers"][controller_index] = controllers[controller_index]->GetDeviceDescriptionJSON(); + } + + /*-------------------------------------------------*\ + | Get plugin profile data if the plugin manager is | + | available | + \*-------------------------------------------------*/ + if(enabled_plugins.size() > 0) + { + PluginManagerInterface* plugin_manager = ResourceManager::get()->GetPluginManager(); + + if(plugin_manager != NULL) + { + profile_json["plugins"] = plugin_manager->OnProfileSave(enabled_plugins); + } + } + + if(ResourceManager::get()->IsLocalClient() && (ResourceManager::get()->GetLocalClient()->GetSupportsProfileManagerAPI())) + { + /*---------------------------------------------*\ + | Upload the profile to the server | + \*---------------------------------------------*/ + ResourceManager::get()->GetLocalClient()->ProfileManager_UploadProfile(profile_json.dump()); + } + else + { + /*---------------------------------------------*\ + | Save the profile to file from the JSON | + \*---------------------------------------------*/ + SaveProfileFromJSON(profile_json); + } + + return(true); + } + else + { + return(false); + } +} + bool ProfileManager::SaveProfileFromJSON(nlohmann::json profile_json) { if(profile_json.contains("profile_name")) @@ -887,7 +1084,7 @@ bool ProfileManager::LoadProfileWithOptions /*-------------------------------------------------*\ | Open input file in binary mode | \*-------------------------------------------------*/ - temp_controllers = GetControllerListFromProfile(profile_json); + temp_controllers = GetControllerListFromProfileJson(profile_json); /*-------------------------------------------------*\ | Signal to plugins that a profile is about to load | @@ -901,6 +1098,30 @@ bool ProfileManager::LoadProfileWithOptions ResourceManager::get()->GetServer()->ProfileManager_ProfileAboutToLoad(); + /*-------------------------------------------------*\ + | If profile contains a base color, apply it | + \*-------------------------------------------------*/ + if(profile_json.contains("base_color")) + { + RGBColor base_color = profile_json["base_color"]; + + for(std::size_t controller_idx = 0; controller_idx < controllers.size(); controller_idx++) + { + controllers[controller_idx]->SetCustomMode(); + + if(controllers[controller_idx]->GetModeColorMode(controllers[controller_idx]->GetActiveMode()) == MODE_COLORS_PER_LED) + { + controllers[controller_idx]->SetAllColors(base_color); + controllers[controller_idx]->UpdateLEDs(); + } + else if(controllers[controller_idx]->GetModeColorMode(controllers[controller_idx]->GetActiveMode()) == MODE_COLORS_MODE_SPECIFIC) + { + controllers[controller_idx]->SetModeColor(controllers[controller_idx]->GetActiveMode(), 0, base_color); + controllers[controller_idx]->UpdateMode(); + } + } + } + /*-------------------------------------------------*\ | Set up used flag vector | \*-------------------------------------------------*/ diff --git a/ProfileManager.h b/ProfileManager.h index ae4b7fbaa..2b235f443 100644 --- a/ProfileManager.h +++ b/ProfileManager.h @@ -37,7 +37,8 @@ public: virtual void DeleteProfile(std::string profile_name) = 0; virtual std::string GetActiveProfile() = 0; - virtual std::vector GetControllerListFromProfile(nlohmann::json profile_json) = 0; + virtual std::vector GetControllerListFromProfileJson(nlohmann::json profile_json) = 0; + virtual std::vector GetControllerListFromProfileName(std::string profile_name) = 0; virtual std::vector GetControllerListFromSizes() = 0; virtual std::vector GetProfileList() = 0; virtual unsigned char * GetProfileListDescription() = 0; @@ -76,11 +77,13 @@ public: ~ProfileManager(); void ClearActiveProfile(); + static bool CompareControllers(RGBController* controller_1, RGBController* controller_2); void DeleteProfile(std::string profile_name); std::string GetActiveProfile(); - std::vector GetControllerListFromProfile(nlohmann::json profile_json); + std::vector GetControllerListFromProfileJson(nlohmann::json profile_json); + std::vector GetControllerListFromProfileName(std::string profile_name); std::vector GetControllerListFromSizes(); std::vector GetProfileList(); unsigned char * GetProfileListDescription(); @@ -114,6 +117,7 @@ public: nlohmann::json ReadProfileJSON(std::string profile_name); bool SaveProfile(std::string profile_name); + bool SaveProfileCustom(std::string profile_name, std::vector controllers, RGBColor base_color, bool base_color_enabled, std::vector enabled_plugins); bool SaveProfileFromJSON(nlohmann::json profile_json); bool SaveSizes(); diff --git a/qt/OpenRGBDialog/OpenRGBDialog.cpp b/qt/OpenRGBDialog/OpenRGBDialog.cpp index 903ed3235..170cc73c2 100644 --- a/qt/OpenRGBDialog/OpenRGBDialog.cpp +++ b/qt/OpenRGBDialog/OpenRGBDialog.cpp @@ -15,6 +15,7 @@ #include "OpenRGBServerInfoPage.h" #include "OpenRGBConsolePage.h" #include "OpenRGBPluginContainer.h" +#include "OpenRGBProfileEditorDialog.h" #include "OpenRGBProfileListDialog.h" #include "ResourceManager.h" #include "SettingsManager.h" @@ -41,87 +42,6 @@ #include "macutils.h" #endif -static int GetIcon(device_type type) -{ - /*-----------------------------------------------------*\ - | Return the icon int value for the given device | - | type value | - \*-----------------------------------------------------*/ - int icon; - - switch(type) - { - case DEVICE_TYPE_ACCESSORY: - icon = OpenRGBFont::usb; - break; - case DEVICE_TYPE_MOTHERBOARD: - icon = OpenRGBFont::mainboard; - break; - case DEVICE_TYPE_DRAM: - icon = OpenRGBFont::dram; - break; - case DEVICE_TYPE_GPU: - icon = OpenRGBFont::gpu; - break; - case DEVICE_TYPE_COOLER: - icon = OpenRGBFont::cooler; - break; - case DEVICE_TYPE_LEDSTRIP: - icon = OpenRGBFont::ledstrip; - break; - case DEVICE_TYPE_KEYBOARD: - icon = OpenRGBFont::keyboard; - break; - case DEVICE_TYPE_MICROPHONE: - icon = OpenRGBFont::mic; - break; - case DEVICE_TYPE_MOUSE: - icon = OpenRGBFont::mouse; - break; - case DEVICE_TYPE_MOUSEMAT: - icon = OpenRGBFont::mousemat; - break; - case DEVICE_TYPE_HEADSET: - icon = OpenRGBFont::headset; - break; - case DEVICE_TYPE_HEADSET_STAND: - icon = OpenRGBFont::headsetstand; - break; - case DEVICE_TYPE_GAMEPAD: - icon = OpenRGBFont::gamepad; - break; - case DEVICE_TYPE_LIGHT: - icon = OpenRGBFont::bulb; - break; - case DEVICE_TYPE_SPEAKER: - icon = OpenRGBFont::music_speaker; - break; - case DEVICE_TYPE_VIRTUAL: - icon = OpenRGBFont::virtual_controller; - break; - case DEVICE_TYPE_STORAGE: - icon = OpenRGBFont::drive; - break; - case DEVICE_TYPE_CASE: - icon = OpenRGBFont::pc_case; - break; - case DEVICE_TYPE_KEYPAD: - icon = OpenRGBFont::keypad; - break; - case DEVICE_TYPE_LAPTOP: - icon = OpenRGBFont::laptop; - break; - case DEVICE_TYPE_MONITOR: - icon = OpenRGBFont::monitor; - break; - default: - icon = OpenRGBFont::unknown; - break; - } - - return icon; -} - static void OpenRGBDialogProfileManagerCallback(void * this_ptr, unsigned int update_reason) { OpenRGBDialog * this_obj = (OpenRGBDialog *)this_ptr; @@ -1062,7 +982,7 @@ void OpenRGBDialog::UpdateDevicesList() /*---------------------------------*\ | Create the tab label | \*---------------------------------*/ - TabLabel* NewTabLabel = new TabLabel(GetIcon(controllers[controller_idx]->GetDeviceType()), QString::fromStdString(controllers[controller_idx]->GetName()), (char *)controllers[controller_idx]->GetName().c_str(), (char *)context); + TabLabel* NewTabLabel = new TabLabel(OpenRGBFont::GetIconIDFromDeviceType(controllers[controller_idx]->GetDeviceType()), QString::fromStdString(controllers[controller_idx]->GetName()), (char *)controllers[controller_idx]->GetName().c_str(), (char *)context); ui->DevicesTabBar->tabBar()->setTabButton(ui->DevicesTabBar->count() - 1, QTabBar::LeftSide, NewTabLabel); ui->DevicesTabBar->tabBar()->setTabToolTip(ui->DevicesTabBar->count() - 1, QString::fromStdString(controllers[controller_idx]->GetName())); @@ -1139,7 +1059,7 @@ void OpenRGBDialog::UpdateDevicesList() /*-----------------------------------------*\ | Create the tab label | \*-----------------------------------------*/ - TabLabel* NewTabLabel = new TabLabel(GetIcon(controllers[controller_idx]->GetDeviceType()), QString::fromStdString(controllers[controller_idx]->GetName()), (char *)controllers[controller_idx]->GetName().c_str(), (char *)context); + TabLabel* NewTabLabel = new TabLabel(OpenRGBFont::GetIconIDFromDeviceType(controllers[controller_idx]->GetDeviceType()), QString::fromStdString(controllers[controller_idx]->GetName()), (char *)controllers[controller_idx]->GetName().c_str(), (char *)context); ui->DevicesTabBar->tabBar()->setTabButton(ui->DevicesTabBar->count() - 1, QTabBar::LeftSide, NewTabLabel); ui->DevicesTabBar->tabBar()->setTabToolTip(ui->DevicesTabBar->count() - 1, QString::fromStdString(controllers[controller_idx]->GetName())); @@ -1199,7 +1119,7 @@ void OpenRGBDialog::UpdateDevicesList() /*---------------------------------------------*\ | Create the tab label | \*---------------------------------------------*/ - TabLabel* NewTabLabel = new TabLabel(GetIcon(controllers[controller_idx]->GetDeviceType()), QString::fromStdString(controllers[controller_idx]->GetName()), (char *)controllers[controller_idx]->GetName().c_str(), (char *)context); + TabLabel* NewTabLabel = new TabLabel(OpenRGBFont::GetIconIDFromDeviceType(controllers[controller_idx]->GetDeviceType()), QString::fromStdString(controllers[controller_idx]->GetName()), (char *)controllers[controller_idx]->GetName().c_str(), (char *)context); ui->InformationTabBar->tabBar()->setTabButton(ui->InformationTabBar->count() - 1, QTabBar::LeftSide, NewTabLabel); ui->InformationTabBar->tabBar()->setTabToolTip(ui->InformationTabBar->count() - 1, QString::fromStdString(controllers[controller_idx]->GetName())); @@ -1817,7 +1737,9 @@ void OpenRGBDialog::SaveProfile() /*-------------------------------------------------*\ | Save the profile | \*-------------------------------------------------*/ - profile_manager->SaveProfile(filename); + OpenRGBProfileEditorDialog editor(filename); + + editor.show(); } } @@ -1844,7 +1766,9 @@ void OpenRGBDialog::SaveProfileAs() /*---------------------------------------------*\ | Save the profile | \*---------------------------------------------*/ - profile_manager->SaveProfile(filename); + OpenRGBProfileEditorDialog editor(filename); + + editor.show(); } } } diff --git a/qt/OpenRGBFont.cpp b/qt/OpenRGBFont.cpp index e123cee32..dd482bbcd 100644 --- a/qt/OpenRGBFont.cpp +++ b/qt/OpenRGBFont.cpp @@ -39,6 +39,87 @@ OpenRGBFont *OpenRGBFont::Get() return instance; } +int OpenRGBFont::GetIconIDFromDeviceType(device_type type) +{ + /*-----------------------------------------------------*\ + | Return the icon int value for the given device | + | type value | + \*-----------------------------------------------------*/ + int icon; + + switch(type) + { + case DEVICE_TYPE_ACCESSORY: + icon = OpenRGBFont::usb; + break; + case DEVICE_TYPE_MOTHERBOARD: + icon = OpenRGBFont::mainboard; + break; + case DEVICE_TYPE_DRAM: + icon = OpenRGBFont::dram; + break; + case DEVICE_TYPE_GPU: + icon = OpenRGBFont::gpu; + break; + case DEVICE_TYPE_COOLER: + icon = OpenRGBFont::cooler; + break; + case DEVICE_TYPE_LEDSTRIP: + icon = OpenRGBFont::ledstrip; + break; + case DEVICE_TYPE_KEYBOARD: + icon = OpenRGBFont::keyboard; + break; + case DEVICE_TYPE_MICROPHONE: + icon = OpenRGBFont::mic; + break; + case DEVICE_TYPE_MOUSE: + icon = OpenRGBFont::mouse; + break; + case DEVICE_TYPE_MOUSEMAT: + icon = OpenRGBFont::mousemat; + break; + case DEVICE_TYPE_HEADSET: + icon = OpenRGBFont::headset; + break; + case DEVICE_TYPE_HEADSET_STAND: + icon = OpenRGBFont::headsetstand; + break; + case DEVICE_TYPE_GAMEPAD: + icon = OpenRGBFont::gamepad; + break; + case DEVICE_TYPE_LIGHT: + icon = OpenRGBFont::bulb; + break; + case DEVICE_TYPE_SPEAKER: + icon = OpenRGBFont::music_speaker; + break; + case DEVICE_TYPE_VIRTUAL: + icon = OpenRGBFont::virtual_controller; + break; + case DEVICE_TYPE_STORAGE: + icon = OpenRGBFont::drive; + break; + case DEVICE_TYPE_CASE: + icon = OpenRGBFont::pc_case; + break; + case DEVICE_TYPE_KEYPAD: + icon = OpenRGBFont::keypad; + break; + case DEVICE_TYPE_LAPTOP: + icon = OpenRGBFont::laptop; + break; + case DEVICE_TYPE_MONITOR: + icon = OpenRGBFont::monitor; + break; + default: + icon = OpenRGBFont::unknown; + break; + } + + return icon; +} + QString OpenRGBFont::icon(int glyph) { return QChar(glyph); diff --git a/qt/OpenRGBFont.h b/qt/OpenRGBFont.h index 8a258f6a8..077454400 100644 --- a/qt/OpenRGBFont.h +++ b/qt/OpenRGBFont.h @@ -11,11 +11,13 @@ #include #include +#include "RGBController.h" class OpenRGBFont { public: static OpenRGBFont* Get(); + static int GetIconIDFromDeviceType(device_type type); enum Glyph { diff --git a/qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.cpp b/qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.cpp new file mode 100644 index 000000000..eb27c152c --- /dev/null +++ b/qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.cpp @@ -0,0 +1,453 @@ +/*---------------------------------------------------------*\ +| OpenRGBProfileEditorDialog.cpp | +| | +| User interface entry for OpenRGB profile editor dialog | +| | +| Adam Honse 16 Feb 2026 | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "OpenRGBFont.h" +#include "OpenRGBProfileEditorDialog.h" +#include "PluginManager.h" +#include "ProfileManager.h" +#include "ResourceManager.h" +#include "SettingsManager.h" + +OpenRGBProfileEditorDialog::OpenRGBProfileEditorDialog(std::string name, QWidget* parent) : QDialog(parent), ui(new Ui::OpenRGBProfileEditorDialog) +{ + ui->setupUi(this); + + /*-----------------------------------------------------*\ + | Save profile name | + \*-----------------------------------------------------*/ + profile_name = name; + + /*-----------------------------------------------------*\ + | Get the existing profile JSON data | + \*-----------------------------------------------------*/ + nlohmann::json profile_json = ResourceManager::get()->GetProfileManager()->ReadProfileJSON(profile_name); + + /*-----------------------------------------------------*\ + | Append profile name to window title | + \*-----------------------------------------------------*/ + QString currentTitle = windowTitle(); + + QString newTitle = currentTitle + " - " + QString::fromStdString(profile_name); + + setWindowTitle(newTitle); + + /*-----------------------------------------------------*\ + | Get the UserInterface settings hex format settings | + \*-----------------------------------------------------*/ + SettingsManager* settings_manager = ResourceManager::get()->GetSettingsManager(); + std::string ui_string = "UserInterface"; + json ui_settings; + + ui_settings = settings_manager->GetSettings(ui_string); + + if(ui_settings.contains("hex_format")) + { + if(ui_settings["hex_format"] == "RGB") + { + HexFormatRGB = true; + } + else if(ui_settings["hex_format"] == "BGR") + { + HexFormatRGB = false; + } + } + + /*-----------------------------------------------------*\ + | Fill in the base color if it exists | + \*-----------------------------------------------------*/ + RGBColor base_color = 0; + bool base_color_enabled = false; + + if(profile_json.contains("base_color")) + { + base_color = profile_json["base_color"]; + base_color_enabled = true; + } + + /*-----------------------------------------------------*\ + | If the hex format is BGR, swap R and B before | + | displaying as hex | + \*-----------------------------------------------------*/ + if(HexFormatRGB) + { + base_color = RGBGetRValue(base_color) << 16 + | RGBGetGValue(base_color) << 8 + | RGBGetBValue(base_color); + } + + ui->CheckBoxBaseColor->setChecked(base_color_enabled); + ui->ColorWheelBaseColor->setColor(base_color); + + ui->LineEditBaseColorHex->blockSignals(true); + ui->LineEditBaseColorHex->setText(QString().asprintf("%06X", base_color)); + ui->LineEditBaseColorHex->blockSignals(false); + + /*-----------------------------------------------------*\ + | Create 3 controller lists: | + | * Updated controllers | + | Present in both the active controllers list and | + | in the existing profile. | + | * New controllers | + | Present in the active controllers list but not | + | in the existing profile. | + | * Old controllers | + | Not present in the active controllersl list but | + | present in the existing profile. | + \*-----------------------------------------------------*/ + old_controllers = ResourceManager::get()->GetProfileManager()->GetControllerListFromProfileJson(profile_json); + new_controllers = ResourceManager::get()->GetRGBControllers(); + + + Qt::CheckState state = Qt::Checked; + + if(old_controllers.size() == 0) + { + state = Qt::Unchecked; + } + + /*-----------------------------------------------------*\ + | Search new and old lists for matches, move these | + | matches to the updated list | + \*-----------------------------------------------------*/ + for(std::size_t new_controller_idx = 0; new_controller_idx < new_controllers.size(); new_controller_idx++) + { + for(std::size_t old_controller_idx = 0; old_controller_idx < old_controllers.size(); old_controller_idx++) + { + if(ProfileManager::CompareControllers(new_controllers[new_controller_idx], old_controllers[old_controller_idx])) + { + /*-----------------------------------------*\ + | Controllers match, copy from new list to | + | updated list, then remove from new and | + | old lists | + \*-----------------------------------------*/ + updated_controllers.push_back(new_controllers[new_controller_idx]); + + new_controllers.erase(new_controllers.begin() + new_controller_idx); + old_controllers.erase(old_controllers.begin() + old_controller_idx); + + /*-----------------------------------------*\ + | Decrement new controller index as to | + | account for the entry being removed | + \*-----------------------------------------*/ + new_controller_idx--; + + break; + } + } + } + + /*-----------------------------------------------------*\ + | Set up the controller states tree view | + \*-----------------------------------------------------*/ + ui->TreeWidgetControllerStates->setColumnCount(4); + ui->TreeWidgetControllerStates->setRootIsDecorated(false); + ui->TreeWidgetControllerStates->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->TreeWidgetControllerStates->header()->setSectionResizeMode(1, QHeaderView::Stretch); + ui->TreeWidgetControllerStates->header()->setStretchLastSection(false); + + QStringList controller_header_labels; + controller_header_labels << "" << "Controller Name" << "Action" << "Include In Profile"; + ui->TreeWidgetControllerStates->setHeaderLabels(controller_header_labels); + + /*-----------------------------------------------------*\ + | Add updated controllers to controller states list | + \*-----------------------------------------------------*/ + for(std::size_t controller_idx = 0; controller_idx < updated_controllers.size(); controller_idx++) + { + QTreeWidgetItem* new_item = new QTreeWidgetItem(); + QFont font = OpenRGBFont::GetFont(); + + Qt::ItemFlags flags = new_item->flags(); + flags &= ~Qt::ItemIsSelectable; + new_item->setFlags(flags); + + font.setPointSize(18); + + new_item->setFont(0, font); + new_item->setText(0, OpenRGBFont::icon(OpenRGBFont::GetIconIDFromDeviceType(updated_controllers[controller_idx]->GetDeviceType()))); + new_item->setText(1, QString::fromStdString(updated_controllers[controller_idx]->GetName())); + new_item->setText(2, "Update"); + new_item->setCheckState(3, state); + + ui->TreeWidgetControllerStates->addTopLevelItem(new_item); + } + + /*-----------------------------------------------------*\ + | Add new controllers to controller states list | + \*-----------------------------------------------------*/ + for(std::size_t controller_idx = 0; controller_idx < new_controllers.size(); controller_idx++) + { + QTreeWidgetItem* new_item = new QTreeWidgetItem(); + QFont font = OpenRGBFont::GetFont(); + + Qt::ItemFlags flags = new_item->flags(); + flags &= ~Qt::ItemIsSelectable; + new_item->setFlags(flags); + + font.setPointSize(18); + + new_item->setFont(0, font); + new_item->setText(0, OpenRGBFont::icon(OpenRGBFont::GetIconIDFromDeviceType(new_controllers[controller_idx]->GetDeviceType()))); + new_item->setText(1, QString::fromStdString(new_controllers[controller_idx]->GetName())); + new_item->setText(2, "Add"); + new_item->setCheckState(3, state); + + ui->TreeWidgetControllerStates->addTopLevelItem(new_item); + } + + /*-----------------------------------------------------*\ + | Add old controllers to controller states list | + \*-----------------------------------------------------*/ + for(std::size_t controller_idx = 0; controller_idx < old_controllers.size(); controller_idx++) + { + QTreeWidgetItem* new_item = new QTreeWidgetItem(); + QFont font = OpenRGBFont::GetFont(); + + Qt::ItemFlags flags = new_item->flags(); + flags &= ~Qt::ItemIsSelectable; + new_item->setFlags(flags); + + font.setPointSize(18); + + new_item->setFont(0, font); + new_item->setText(0, OpenRGBFont::icon(OpenRGBFont::GetIconIDFromDeviceType(old_controllers[controller_idx]->GetDeviceType()))); + new_item->setText(1, QString::fromStdString(old_controllers[controller_idx]->GetName())); + new_item->setText(2, "Keep"); + new_item->setCheckState(3, state); + + ui->TreeWidgetControllerStates->addTopLevelItem(new_item); + } + + /*-----------------------------------------------------*\ + | Add plugins to plugins list | + \*-----------------------------------------------------*/ + PluginManager* plugin_manager = (PluginManager*)ResourceManager::get()->GetPluginManager(); + + ui->TreeWidgetPlugins->setColumnCount(3); + ui->TreeWidgetPlugins->setRootIsDecorated(false); + ui->TreeWidgetPlugins->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->TreeWidgetPlugins->header()->setSectionResizeMode(1, QHeaderView::Stretch); + ui->TreeWidgetPlugins->header()->setStretchLastSection(false); + + QStringList plugin_header_labels; + plugin_header_labels << "" << "Plugin Name" << "Include In Profile"; + ui->TreeWidgetPlugins->setHeaderLabels(plugin_header_labels); + + if(plugin_manager) + { + for(std::size_t plugin_idx = 0; plugin_idx < plugin_manager->GetPluginCount(); plugin_idx++) + { + QTreeWidgetItem* new_item = new QTreeWidgetItem(); + + Qt::ItemFlags flags = new_item->flags(); + flags &= ~Qt::ItemIsSelectable; + new_item->setFlags(flags); + + state = Qt::Unchecked; + + if(profile_json.contains("plugins")) + { + if(profile_json["plugins"].contains(plugin_manager->GetPluginName(plugin_idx))) + { + state = Qt::Checked; + } + } + + new_item->setIcon(0, QIcon(QPixmap::fromImage(plugin_manager->ActivePlugins[plugin_idx].info.Icon))); + new_item->setText(1, QString::fromStdString(plugin_manager->GetPluginName(plugin_idx))); + new_item->setCheckState(2, state); + + ui->TreeWidgetPlugins->addTopLevelItem(new_item); + } + } +} + +OpenRGBProfileEditorDialog::~OpenRGBProfileEditorDialog() +{ + +} + +bool OpenRGBProfileEditorDialog::show() +{ + int result = this->exec(); + + if(result == QDialog::Rejected) + { + return(false); + } + else + { + bool base_color_enabled = ui->CheckBoxBaseColor->isChecked(); + std::vector enabled_plugins; + std::vector rgb_controllers; + QTreeWidgetItem* ControllersRootItem = ui->TreeWidgetControllerStates->invisibleRootItem(); + QTreeWidgetItem* PluginsRootItem = ui->TreeWidgetPlugins->invisibleRootItem(); + + for(int item_idx = 0; item_idx < ControllersRootItem->childCount(); item_idx++) + { + QTreeWidgetItem* item = ControllersRootItem->child(item_idx); + + if(item->checkState(3) == Qt::Checked) + { + if(item_idx < (int)updated_controllers.size()) + { + rgb_controllers.push_back(updated_controllers[item_idx]); + } + else if(item_idx < (int)(updated_controllers.size() + new_controllers.size())) + { + rgb_controllers.push_back(new_controllers[item_idx - updated_controllers.size()]); + } + else if(item_idx < (int)(updated_controllers.size() + new_controllers.size() + old_controllers.size())) + { + rgb_controllers.push_back(old_controllers[item_idx - updated_controllers.size() - new_controllers.size()]); + } + } + } + + for(int item_idx = 0; item_idx < PluginsRootItem->childCount(); item_idx++) + { + QTreeWidgetItem* item = PluginsRootItem->child(item_idx); + + if(item->checkState(2) == Qt::Checked) + { + enabled_plugins.push_back(item->text(1).toStdString()); + } + } + + RGBColor rgb_color = (0x00FFFFFF & ui->ColorWheelBaseColor->color().rgb()); + + /*-------------------------------------------------*\ + | If the hex format is BGR, swap R and B before | + | displaying as hex | + \*-------------------------------------------------*/ + if(HexFormatRGB) + { + rgb_color = RGBGetRValue(rgb_color) << 16 + | RGBGetGValue(rgb_color) << 8 + | RGBGetBValue(rgb_color); + } + + + ResourceManager::get()->GetProfileManager()->SaveProfileCustom(profile_name, rgb_controllers, rgb_color, base_color_enabled, enabled_plugins); + return(true); + } +} + +void OpenRGBProfileEditorDialog::changeEvent(QEvent* event) +{ + if(event->type() == QEvent::LanguageChange) + { + ui->retranslateUi(this); + } +} + +void OpenRGBProfileEditorDialog::on_ColorWheelBaseColor_colorChanged(const QColor& color) +{ + RGBColor rgb_color = (0x00FFFFFF & color.rgb()); + + /*-----------------------------------------------------*\ + | If the hex format is BGR, swap R and B before | + | displaying as hex | + \*-----------------------------------------------------*/ + if(!HexFormatRGB) + { + rgb_color = RGBGetRValue(rgb_color) << 16 + | RGBGetGValue(rgb_color) << 8 + | RGBGetBValue(rgb_color); + } + + ui->LineEditBaseColorHex->blockSignals(true); + ui->LineEditBaseColorHex->setText(QString().asprintf("%06X", rgb_color)); + ui->LineEditBaseColorHex->blockSignals(false); +} + +void OpenRGBProfileEditorDialog::on_LineEditBaseColorHex_textChanged(const QString& text) +{ + /*-----------------------------------------------------*\ + | Make an editable copy of the string | + \*-----------------------------------------------------*/ + QString temp = text; + + /*-----------------------------------------------------*\ + | Remove # character so that #XXXXXX color codes are | + | acceptable. 0xXXXXXX codes are already accepted by | + | toInt(). Convert into an RGBColor. Mask off the | + | unused bits. | + \*-----------------------------------------------------*/ + QColor color; + RGBColor rgb_color = (RGBColor)(0x00FFFFFF & temp.replace("#", "").toInt(NULL, 16)); + + /*-----------------------------------------------------*\ + | Store new color into the current color QColor | + | Because RGBColor stores color in BGR format, we have | + | to reverse the R and B channels if the hex format is | + | RGB. | + \*-----------------------------------------------------*/ + if(HexFormatRGB) + { + color.setRed(RGBGetBValue(rgb_color)); + color.setGreen(RGBGetGValue(rgb_color)); + color.setBlue(RGBGetRValue(rgb_color)); + } + else + { + color.setRed(RGBGetRValue(rgb_color)); + color.setGreen(RGBGetGValue(rgb_color)); + color.setBlue(RGBGetBValue(rgb_color)); + } + + /*-----------------------------------------------------*\ + | Update the color wheel | + \*-----------------------------------------------------*/ + ui->LineEditBaseColorHex->blockSignals(true); + ui->ColorWheelBaseColor->setColor(color); + ui->LineEditBaseColorHex->blockSignals(false); +} + +void OpenRGBProfileEditorDialog::on_ButtonSelectAllControllerStates_clicked() +{ + QTreeWidgetItem* ControllersRootItem = ui->TreeWidgetControllerStates->invisibleRootItem(); + + for(int item_idx = 0; item_idx < ControllersRootItem->childCount(); item_idx++) + { + ControllersRootItem->child(item_idx)->setCheckState(3, Qt::Checked); + } +} + +void OpenRGBProfileEditorDialog::on_ButtonSelectNoneControllerStates_clicked() +{ + QTreeWidgetItem* ControllersRootItem = ui->TreeWidgetControllerStates->invisibleRootItem(); + + for(int item_idx = 0; item_idx < ControllersRootItem->childCount(); item_idx++) + { + ControllersRootItem->child(item_idx)->setCheckState(3, Qt::Unchecked); + } +} + +void OpenRGBProfileEditorDialog::on_ButtonSelectAllPlugins_clicked() +{ + QTreeWidgetItem* PluginsRootItem = ui->TreeWidgetPlugins->invisibleRootItem(); + + for(int item_idx = 0; item_idx < PluginsRootItem->childCount(); item_idx++) + { + PluginsRootItem->child(item_idx)->setCheckState(2, Qt::Checked); + } +} + +void OpenRGBProfileEditorDialog::on_ButtonSelectNonePlugins_clicked() +{ + QTreeWidgetItem* PluginsRootItem = ui->TreeWidgetPlugins->invisibleRootItem(); + + for(int item_idx = 0; item_idx < PluginsRootItem->childCount(); item_idx++) + { + PluginsRootItem->child(item_idx)->setCheckState(2, Qt::Unchecked); + } +} diff --git a/qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.h b/qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.h new file mode 100644 index 000000000..79cea7221 --- /dev/null +++ b/qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.h @@ -0,0 +1,49 @@ +/*---------------------------------------------------------*\ +| OpenRGBProfileEditorDialog.h | +| | +| User interface entry for OpenRGB profile editor dialog | +| | +| Adam Honse 16 Feb 2026 | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include "RGBController.h" +#include "ui_OpenRGBProfileEditorDialog.h" + +namespace Ui +{ + class OpenRGBProfileEditorDialog; +} + +class OpenRGBProfileEditorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit OpenRGBProfileEditorDialog(std::string name, QWidget* parent = nullptr); + ~OpenRGBProfileEditorDialog(); + + bool show(); + +private: + Ui::OpenRGBProfileEditorDialog* ui; + bool HexFormatRGB; + std::string profile_name; + + std::vector old_controllers; + std::vector new_controllers; + std::vector updated_controllers; + +private slots: + void changeEvent(QEvent* event); + void on_ColorWheelBaseColor_colorChanged(const QColor& color); + void on_LineEditBaseColorHex_textChanged(const QString& text); + void on_ButtonSelectAllControllerStates_clicked(); + void on_ButtonSelectNoneControllerStates_clicked(); + void on_ButtonSelectAllPlugins_clicked(); + void on_ButtonSelectNonePlugins_clicked(); +}; diff --git a/qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.ui b/qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.ui new file mode 100644 index 000000000..da2ff3f68 --- /dev/null +++ b/qt/OpenRGBProfileEditorDialog/OpenRGBProfileEditorDialog.ui @@ -0,0 +1,211 @@ + + + OpenRGBProfileEditorDialog + + + + 0 + 0 + 800 + 800 + + + + Profile Editor + + + + + + Base Color + + + + + + + + + Hex: + + + + + + + An optional static base color which will apply to all devices not otherwise covered by this profile. + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + 0 + 0 + + + + Enable Base Color + + + + + + + + 0 + 0 + + + + + + + + + + + Device States + + + + + + Select All + + + + + + + Select None + + + + + + + + 1 + + + + + + + + Select which devices should save their states (selected modes, mode settings, and colors) to this profile. + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + + + + + Plugins + + + + + + Select All + + + + + + + Select None + + + + + + + + 1 + + + + + + + + Select which plugins should save their states (if supported) to this profile. + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + + ColorWheel + QWidget +
ColorWheel.h
+ 1 + + colorChanged(QColor) + +
+
+ + + + ButtonBox + accepted() + OpenRGBProfileEditorDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + ButtonBox + rejected() + OpenRGBProfileEditorDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +