Profile editor dialog

This commit is contained in:
Adam Honse
2026-02-16 10:45:45 -06:00
parent 91994d95e6
commit 012c6c45f1
11 changed files with 1075 additions and 102 deletions

View File

@@ -616,6 +616,32 @@ nlohmann::json PluginManager::OnProfileSave()
return(plugin_json);
}
nlohmann::json PluginManager::OnProfileSave(std::vector<std::string> 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;

View File

@@ -82,6 +82,7 @@ public:
void OnProfileAboutToLoad();
void OnProfileLoad(nlohmann::json profile_data);
nlohmann::json OnProfileSave();
nlohmann::json OnProfileSave(std::vector<std::string> enabled_plugins);
/*-----------------------------------------------------*\
| Plugin SDK Integration |

View File

@@ -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<RGBController *> GetRGBControllers() = 0;
virtual std::vector<RGBController *> 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<std::string> enabled_plugins) = 0;
/*-----------------------------------------------------*\
| Plugin SDK Integration |

View File

@@ -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<RGBController*> ProfileManager::GetControllerListFromProfile(nlohmann::json profile_json)
std::vector<RGBController*> ProfileManager::GetControllerListFromProfileJson(nlohmann::json profile_json)
{
std::vector<RGBController*> temp_controllers;
@@ -155,6 +275,11 @@ std::vector<RGBController*> ProfileManager::GetControllerListFromProfile(nlohman
return(temp_controllers);
}
std::vector<RGBController*> ProfileManager::GetControllerListFromProfileName(std::string profile_name)
{
return(GetControllerListFromProfileJson(ReadProfileJSON(profile_name)));
}
std::vector<RGBController*> ProfileManager::GetControllerListFromSizes()
{
/*-----------------------------------------------------*\
@@ -163,7 +288,7 @@ std::vector<RGBController*> 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<std::string> ProfileManager::GetProfileList()
@@ -429,6 +554,78 @@ bool ProfileManager::SaveProfile(std::string profile_name)
}
}
bool ProfileManager::SaveProfileCustom(std::string profile_name, std::vector<RGBController*> controllers, RGBColor base_color, bool base_color_enabled, std::vector<std::string> 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 |
\*-------------------------------------------------*/

View File

@@ -37,7 +37,8 @@ public:
virtual void DeleteProfile(std::string profile_name) = 0;
virtual std::string GetActiveProfile() = 0;
virtual std::vector<RGBController*> GetControllerListFromProfile(nlohmann::json profile_json) = 0;
virtual std::vector<RGBController*> GetControllerListFromProfileJson(nlohmann::json profile_json) = 0;
virtual std::vector<RGBController*> GetControllerListFromProfileName(std::string profile_name) = 0;
virtual std::vector<RGBController*> GetControllerListFromSizes() = 0;
virtual std::vector<std::string> 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<RGBController*> GetControllerListFromProfile(nlohmann::json profile_json);
std::vector<RGBController*> GetControllerListFromProfileJson(nlohmann::json profile_json);
std::vector<RGBController*> GetControllerListFromProfileName(std::string profile_name);
std::vector<RGBController*> GetControllerListFromSizes();
std::vector<std::string> 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<RGBController*> controllers, RGBColor base_color, bool base_color_enabled, std::vector<std::string> enabled_plugins);
bool SaveProfileFromJSON(nlohmann::json profile_json);
bool SaveSizes();

View File

@@ -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();
}
}
}

View File

@@ -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);

View File

@@ -11,11 +11,13 @@
#include <QFont>
#include <QString>
#include "RGBController.h"
class OpenRGBFont
{
public:
static OpenRGBFont* Get();
static int GetIconIDFromDeviceType(device_type type);
enum Glyph
{

View File

@@ -0,0 +1,453 @@
/*---------------------------------------------------------*\
| OpenRGBProfileEditorDialog.cpp |
| |
| User interface entry for OpenRGB profile editor dialog |
| |
| Adam Honse <calcprogrammer1@gmail.com> 16 Feb 2026 |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <QTreeWidgetItem>
#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<std::string> enabled_plugins;
std::vector<RGBController *> 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);
}
}

View File

@@ -0,0 +1,49 @@
/*---------------------------------------------------------*\
| OpenRGBProfileEditorDialog.h |
| |
| User interface entry for OpenRGB profile editor dialog |
| |
| Adam Honse <calcprogrammer1@gmail.com> 16 Feb 2026 |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <QDialog>
#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<RGBController*> old_controllers;
std::vector<RGBController*> new_controllers;
std::vector<RGBController*> 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();
};

View File

@@ -0,0 +1,211 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OpenRGBProfileEditorDialog</class>
<widget class="QDialog" name="OpenRGBProfileEditorDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>800</height>
</rect>
</property>
<property name="windowTitle">
<string>Profile Editor</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="GroupBoxBaseColor">
<property name="title">
<string>Base Color</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="3">
<widget class="ColorWheel" name="ColorWheelBaseColor" native="true"/>
</item>
<item row="1" column="1">
<widget class="QLabel" name="LabelBaseColorHex">
<property name="text">
<string>Hex:</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="LabelBaseColorDescription">
<property name="text">
<string>An optional static base color which will apply to all devices not otherwise covered by this profile.</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="CheckBoxBaseColor">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Enable Base Color</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="LineEditBaseColorHex">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="GroupBoxControllerStates">
<property name="title">
<string>Device States</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0">
<widget class="QPushButton" name="ButtonSelectAllControllerStates">
<property name="text">
<string>Select All</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="ButtonSelectNoneControllerStates">
<property name="text">
<string>Select None</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QTreeWidget" name="TreeWidgetControllerStates">
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="LabelControllerStatesDescription">
<property name="text">
<string>Select which devices should save their states (selected modes, mode settings, and colors) to this profile.</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QDialogButtonBox" name="ButtonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="GroupBoxPlugins">
<property name="title">
<string>Plugins</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0">
<widget class="QPushButton" name="ButtonSelectAllPlugins">
<property name="text">
<string>Select All</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="ButtonSelectNonePlugins">
<property name="text">
<string>Select None</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QTreeWidget" name="TreeWidgetPlugins">
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="LabelPluginsDescription">
<property name="text">
<string>Select which plugins should save their states (if supported) to this profile.</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ColorWheel</class>
<extends>QWidget</extends>
<header location="global">ColorWheel.h</header>
<container>1</container>
<slots>
<signal>colorChanged(QColor)</signal>
</slots>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>ButtonBox</sender>
<signal>accepted()</signal>
<receiver>OpenRGBProfileEditorDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>ButtonBox</sender>
<signal>rejected()</signal>
<receiver>OpenRGBProfileEditorDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>