Merge pull request #650 from davidgiven/flags

Allow options to be set in the GUI.
This commit is contained in:
David Given
2023-03-31 23:37:17 +01:00
committed by GitHub
12 changed files with 583 additions and 130 deletions

View File

@@ -13,37 +13,44 @@ import "lib/common.proto";
import "lib/layout.proto";
// NEXT_TAG: 21
message ConfigProto {
optional string comment = 8;
optional bool is_extension = 13;
repeated string include = 19;
message ConfigProto
{
optional string comment = 8;
optional bool is_extension = 13;
repeated string include = 19;
optional LayoutProto layout = 18;
optional LayoutProto layout = 18;
optional ImageReaderProto image_reader = 12;
optional ImageWriterProto image_writer = 9;
optional FluxSourceProto flux_source = 10;
optional FluxSinkProto flux_sink = 11;
optional DriveProto drive = 15;
optional ImageReaderProto image_reader = 12;
optional ImageWriterProto image_writer = 9;
optional EncoderProto encoder = 3;
optional DecoderProto decoder = 4;
optional UsbProto usb = 5;
optional FluxSourceProto flux_source = 10;
optional FluxSinkProto flux_sink = 11;
optional DriveProto drive = 15;
optional RangeProto tracks = 6;
optional RangeProto heads = 7;
optional int32 tpi = 16 [ (help) = "TPI of image; if 0, use TPI of drive" ];
optional EncoderProto encoder = 3;
optional DecoderProto decoder = 4;
optional UsbProto usb = 5;
optional FilesystemProto filesystem = 17;
repeated OptionProto option = 20;
optional RangeProto tracks = 6;
optional RangeProto heads = 7;
optional int32 tpi = 16 [ (help) = "TPI of image; if 0, use TPI of drive" ];
optional FilesystemProto filesystem = 17;
repeated OptionProto option = 20;
}
message OptionProto {
optional string name = 1 [(help) = "Option name" ];
optional string comment = 2 [(help) = "Help text for option" ];
optional string message = 3 [(help) = "Message to display when option is in use" ];
optional ConfigProto config = 4 [(help) = "Option data", (recurse) = false ];
message OptionProto
{
optional string name = 1 [ (help) = "option name" ];
optional string comment = 2 [ (help) = "help text for option" ];
optional string message = 3
[ (help) = "message to display when option is in use" ];
optional string exclusivity_group = 5 [
(help) =
"options with the same group cannot be selected at the same time"
];
optional ConfigProto config = 4
[ (help) = "option data", (recurse) = false ];
}

View File

@@ -55,31 +55,39 @@ static bool setFallbackFlag(
}
else
{
for (const auto& configs : config.option())
{
if (path == configs.name())
{
if (configs.config().option_size() > 0)
Error() << fmt::format(
"option '{}' has an option inside it, which isn't "
"allowed",
path);
if (configs.config().include_size() > 0)
Error() << fmt::format(
"option '{}' is trying to include something, which "
"isn't allowed",
path);
Logger() << fmt::format("OPTION: {}", configs.message());
config.MergeFrom(configs.config());
return false;
}
}
if (FlagGroup::applyOption(path))
return false;
}
}
Error() << "unrecognised flag; try --help";
}
bool FlagGroup::applyOption(const std::string& option)
{
for (const auto& configs : config.option())
{
if (option == configs.name())
{
if (configs.config().option_size() > 0)
Error() << fmt::format(
"option '{}' has an option inside it, which isn't "
"allowed",
option);
if (configs.config().include_size() > 0)
Error() << fmt::format(
"option '{}' is trying to include something, which "
"isn't allowed",
option);
Logger() << fmt::format("OPTION: {}", configs.message());
config.MergeFrom(configs.config());
return true;
}
}
return false;
}
std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc,
const char* argv[],
std::function<bool(const std::string&)> callback)

View File

@@ -12,21 +12,42 @@ public:
FlagGroup(std::initializer_list<FlagGroup*> groups);
public:
void parseFlags(int argc, const char* argv[],
std::function<bool(const std::string&)> callback =
[](const auto&){ return false; });
void parseFlags(
int argc,
const char* argv[],
std::function<bool(const std::string&)> callback =
[](const auto&)
{
return false;
});
std::vector<std::string> parseFlagsWithFilenames(
int argc, const char* argv[],
std::function<bool(const std::string&)> callback =
[](const auto&){ return false; });
void parseFlagsWithConfigFiles(int argc, const char* argv[],
const std::map<std::string, std::string>& configFiles);
static ConfigProto parseSingleConfigFile(
const std::string& filename,
const std::map<std::string, std::string>& configFiles);
static void parseConfigFile(
const std::string& filename,
const std::map<std::string, std::string>& configFiles);
int argc,
const char* argv[],
std::function<bool(const std::string&)> callback =
[](const auto&)
{
return false;
});
void parseFlagsWithConfigFiles(int argc,
const char* argv[],
const std::map<std::string, std::string>& configFiles);
/* Load one config file (or internal config file), without expanding
* includes. */
static ConfigProto parseSingleConfigFile(const std::string& filename,
const std::map<std::string, std::string>& configFiles);
/* Load a top-level config file (or internal config file), expanding
* includes. */
static void parseConfigFile(const std::string& filename,
const std::map<std::string, std::string>& configFiles);
/* Modify the current config to engage the named option. */
static bool applyOption(const std::string& option);
void addFlag(Flag* flag);
void checkInitialised() const;
@@ -40,14 +61,25 @@ class Flag
{
public:
Flag(const std::vector<std::string>& names, const std::string helptext);
virtual ~Flag() {};
virtual ~Flag(){};
void checkInitialised() const
{ _group.checkInitialised(); }
{
_group.checkInitialised();
}
const std::string& name() const { return _names[0]; }
const std::vector<std::string> names() const { return _names; }
const std::string& helptext() const { return _helptext; }
const std::string& name() const
{
return _names[0];
}
const std::vector<std::string> names() const
{
return _names;
}
const std::string& helptext() const
{
return _helptext;
}
virtual bool hasArgument() const = 0;
virtual const std::string defaultValueAsString() const = 0;
@@ -62,15 +94,26 @@ private:
class ActionFlag : Flag
{
public:
ActionFlag(const std::vector<std::string>& names, const std::string helptext,
std::function<void(void)> callback):
ActionFlag(const std::vector<std::string>& names,
const std::string helptext,
std::function<void(void)> callback):
Flag(names, helptext),
_callback(callback)
{}
{
}
bool hasArgument() const { return false; }
const std::string defaultValueAsString() const { return ""; }
void set(const std::string& value) { _callback(); }
bool hasArgument() const
{
return false;
}
const std::string defaultValueAsString() const
{
return "";
}
void set(const std::string& value)
{
_callback();
}
private:
const std::function<void(void)> _callback;
@@ -79,16 +122,30 @@ private:
class SettableFlag : public Flag
{
public:
SettableFlag(const std::vector<std::string>& names, const std::string helptext):
SettableFlag(
const std::vector<std::string>& names, const std::string helptext):
Flag(names, helptext)
{}
{
}
operator bool() const
{ checkInitialised(); return _value; }
{
checkInitialised();
return _value;
}
bool hasArgument() const { return false; }
const std::string defaultValueAsString() const { return "false"; }
void set(const std::string& value) { _value = true; }
bool hasArgument() const
{
return false;
}
const std::string defaultValueAsString() const
{
return "false";
}
void set(const std::string& value)
{
_value = true;
}
private:
bool _value = false;
@@ -98,72 +155,122 @@ template <typename T>
class ValueFlag : public Flag
{
public:
ValueFlag(const std::vector<std::string>& names, const std::string helptext,
const T defaultValue,
std::function<void(const T&)> callback = [](const T&) {}):
ValueFlag(
const std::vector<std::string>& names,
const std::string helptext,
const T defaultValue,
std::function<void(const T&)> callback =
[](const T&)
{
}):
Flag(names, helptext),
_defaultValue(defaultValue),
_value(defaultValue),
_callback(callback)
{}
_callback(callback)
{
}
const T& get() const
{ checkInitialised(); return _value; }
{
checkInitialised();
return _value;
}
operator const T& () const
{ return get(); }
operator const T&() const
{
return get();
}
bool isSet() const
{ return _isSet; }
bool isSet() const
{
return _isSet;
}
void setDefaultValue(T value)
{
_value = _defaultValue = value;
}
bool hasArgument() const { return true; }
bool hasArgument() const
{
return true;
}
protected:
T _defaultValue;
T _value;
bool _isSet = false;
std::function<void(const T&)> _callback;
bool _isSet = false;
std::function<void(const T&)> _callback;
};
class StringFlag : public ValueFlag<std::string>
{
public:
StringFlag(const std::vector<std::string>& names, const std::string helptext,
const std::string defaultValue = "",
std::function<void(const std::string&)> callback = [](const std::string&) {}):
StringFlag(
const std::vector<std::string>& names,
const std::string helptext,
const std::string defaultValue = "",
std::function<void(const std::string&)> callback =
[](const std::string&)
{
}):
ValueFlag(names, helptext, defaultValue, callback)
{}
{
}
const std::string defaultValueAsString() const { return _defaultValue; }
void set(const std::string& value) { _value = value; _callback(_value); _isSet = true; }
const std::string defaultValueAsString() const
{
return _defaultValue;
}
void set(const std::string& value)
{
_value = value;
_callback(_value);
_isSet = true;
}
};
class IntFlag : public ValueFlag<int>
{
public:
IntFlag(const std::vector<std::string>& names, const std::string helptext,
int defaultValue = 0,
std::function<void(const int&)> callback = [](const int&) {}):
IntFlag(
const std::vector<std::string>& names,
const std::string helptext,
int defaultValue = 0,
std::function<void(const int&)> callback =
[](const int&)
{
}):
ValueFlag(names, helptext, defaultValue, callback)
{}
{
}
const std::string defaultValueAsString() const { return std::to_string(_defaultValue); }
void set(const std::string& value) { _value = std::stoi(value); _callback(_value); _isSet = true; }
const std::string defaultValueAsString() const
{
return std::to_string(_defaultValue);
}
void set(const std::string& value)
{
_value = std::stoi(value);
_callback(_value);
_isSet = true;
}
};
class HexIntFlag : public IntFlag
{
public:
HexIntFlag(const std::vector<std::string>& names, const std::string helptext,
int defaultValue = 0,
std::function<void(const int&)> callback = [](const int&) {}):
HexIntFlag(
const std::vector<std::string>& names,
const std::string helptext,
int defaultValue = 0,
std::function<void(const int&)> callback =
[](const int&)
{
}):
IntFlag(names, helptext, defaultValue, callback)
{}
{
}
const std::string defaultValueAsString() const;
};
@@ -171,26 +278,49 @@ public:
class DoubleFlag : public ValueFlag<double>
{
public:
DoubleFlag(const std::vector<std::string>& names, const std::string helptext,
double defaultValue = 1.0,
std::function<void(const double&)> callback = [](const double&) {}):
DoubleFlag(
const std::vector<std::string>& names,
const std::string helptext,
double defaultValue = 1.0,
std::function<void(const double&)> callback =
[](const double&)
{
}):
ValueFlag(names, helptext, defaultValue, callback)
{}
{
}
const std::string defaultValueAsString() const { return std::to_string(_defaultValue); }
void set(const std::string& value) { _value = std::stod(value); _callback(_value); _isSet = true; }
const std::string defaultValueAsString() const
{
return std::to_string(_defaultValue);
}
void set(const std::string& value)
{
_value = std::stod(value);
_callback(_value);
_isSet = true;
}
};
class BoolFlag : public ValueFlag<bool>
{
public:
BoolFlag(const std::vector<std::string>& names, const std::string helptext,
bool defaultValue = false,
std::function<void(const bool&)> callback = [](const bool&) {}):
BoolFlag(
const std::vector<std::string>& names,
const std::string helptext,
bool defaultValue = false,
std::function<void(const bool&)> callback =
[](const bool&)
{
}):
ValueFlag(names, helptext, defaultValue, callback)
{}
{
}
const std::string defaultValueAsString() const { return _defaultValue ? "true" : "false"; }
const std::string defaultValueAsString() const
{
return _defaultValue ? "true" : "false";
}
void set(const std::string& value);
};

View File

@@ -28,6 +28,7 @@ class AppledosFilesystem : public Filesystem
for (char& c : filename)
c &= 0x7f;
filename = rightTrimWhitespace(filename);
path = {filename};
file_type = TYPE_FILE;

View File

@@ -15,8 +15,9 @@ encoder {
option {
name: "appledos"
comment: "specifies AppleDOS soft sector skew for filesystem access and images"
comment: "use AppleDOS soft sector skew for filesystem access and images"
message: "compensating for AppleDOS soft sector skew"
exclusivity_group: "format"
config {
image_reader {
@@ -58,8 +59,9 @@ option {
option {
name: "prodos"
comment: "specifies ProDOS soft sector skew for filesystem access and images"
comment: "use ProDOS soft sector skew for filesystem access and images"
message: "compensating for ProDOS soft sector skew"
exclusivity_group: "format"
config {
image_reader {
@@ -101,8 +103,9 @@ option {
option {
name: "cpm"
comment: "specifies CP/M soft sector skew for filesystem access and images"
comment: "use CP/M soft sector skew for filesystem access and images"
message: "compensating for CP/M soft sector skew"
exclusivity_group: "format"
config {
image_reader {

View File

@@ -28,7 +28,7 @@ tpi: 96
option {
name: "side1"
comment: "reads the volume on side 1 of a disk"
comment: "read the volume on side 1 of a disk (AppleDOS only)"
message: "accessing volume on side 1"
config {

View File

@@ -9,6 +9,7 @@ class MainWindow;
extern void postToUiThread(std::function<void()> callback);
extern void runOnUiThread(std::function<void()> callback);
extern void runOnWorkerThread(std::function<void()> callback);
extern bool isWorkerThread();
wxDECLARE_EVENT(UPDATE_STATE_EVENT, wxCommandEvent);
@@ -34,7 +35,8 @@ private:
void OnExec(const ExecEvent& event);
public:
bool IsWorkerThreadRunning() const;
bool IsWorkerThread();
bool IsWorkerThreadRunning();
protected:
virtual wxThread::ExitCode Entry();

View File

@@ -169,6 +169,13 @@ MainWindowGen::MainWindowGen( wxWindow* parent, wxWindowID id, const wxString& t
fgSizer6->Fit( m_panel11 );
fgSizer8->Add( m_panel11, 1, wxEXPAND | wxALL, 5 );
m_staticText232 = new wxStaticText( idlePanel, wxID_ANY, wxT("then select some options (if there are any):"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL );
m_staticText232->Wrap( -1 );
fgSizer8->Add( m_staticText232, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
formatOptionsContainer = new wxPanel( idlePanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
fgSizer8->Add( formatOptionsContainer, 1, wxALL|wxEXPAND, 5 );
m_staticText19 = new wxStaticText( idlePanel, wxID_ANY, wxT("and press one of:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText19->Wrap( -1 );
fgSizer8->Add( m_staticText19, 0, wxALIGN_CENTER|wxALL, 5 );

View File

@@ -1479,6 +1479,126 @@
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_HORIZONTAL|wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">then select some options (if there are any):</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText232</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxALIGN_CENTER_HORIZONTAL</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxPanel" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">formatOptionsContainer</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
</object>
</object>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER|wxALL</property>

View File

@@ -77,6 +77,8 @@ class MainWindowGen : public wxFrame
wxPanel* m_panel11;
wxChoice* formatChoice;
wxButton* customConfigurationButton;
wxStaticText* m_staticText232;
wxPanel* formatOptionsContainer;
wxStaticText* m_staticText19;
wxButton* readButton;
wxButton* writeButton;

View File

@@ -115,7 +115,17 @@ void runOnWorkerThread(std::function<void()> callback)
wxGetApp().RunOnWorkerThread(callback);
}
bool FluxEngineApp::IsWorkerThreadRunning() const
bool isWorkerThread()
{
return wxGetApp().IsWorkerThread();
}
bool FluxEngineApp::IsWorkerThread()
{
return wxThread::GetCurrentId() != wxThread::GetMainId();
}
bool FluxEngineApp::IsWorkerThreadRunning()
{
return !!_callback;
}

View File

@@ -34,6 +34,7 @@ extern const std::map<std::string, std::string> formats;
#define CONFIG_FORTYTRACK "FortyTrack"
#define CONFIG_HIGHDENSITY "HighDensity"
#define CONFIG_FORMAT "Format"
#define CONFIG_FORMATOPTIONS "FormatOptions"
#define CONFIG_EXTRACONFIG "ExtraConfig"
#define CONFIG_FLUXIMAGE "FluxImage"
#define CONFIG_DISKIMAGE "DiskImage"
@@ -95,11 +96,16 @@ public:
Logger::setLogger(
[&](std::shared_ptr<const AnyLogMessage> message)
{
runOnUiThread(
[message, this]()
{
OnLogMessage(message);
});
if (isWorkerThread())
{
runOnUiThread(
[message, this]()
{
OnLogMessage(message);
});
}
else
OnLogMessage(message);
});
_logWindow.reset(
@@ -174,6 +180,7 @@ public:
LoadConfig();
UpdateDevices();
UpdateFormatOptions();
}
void OnShowLogWindow(wxCommandEvent& event) override
@@ -258,6 +265,7 @@ public:
{
SaveConfig();
UpdateState();
UpdateFormatOptions();
}
void OnControlsChanged(wxFileDirPickerEvent& event) override
@@ -1220,8 +1228,18 @@ public:
Error() << "no format selected";
config.Clear();
FlagGroup::parseConfigFile(
_formatNames[formatChoice->GetSelection()], formats);
auto formatName = _formatNames[formatChoice->GetSelection()];
FlagGroup::parseConfigFile(formatName, formats);
/* Apply any format options. */
for (const auto& e : _formatOptions)
{
if (e.first == formatName)
FlagGroup::applyOption(e.second);
}
/* Merge in any custom config. */
for (auto setting : split(_extraConfiguration, '\n'))
{
@@ -1242,6 +1260,8 @@ public:
FlagGroup::parseConfigFile(setting, formats);
}
/* Locate the device, if any. */
auto serial = deviceCombo->GetValue().ToStdString();
if (!serial.empty() && (serial[0] == '/'))
setProtoByString(&config, "usb.greaseweazle.port", serial);
@@ -1250,6 +1270,8 @@ public:
_logWindow->GetTextControl()->Clear();
/* Apply the source/destination. */
switch (_selectedSource)
{
case SELECTEDSOURCE_REAL:
@@ -1476,6 +1498,24 @@ public:
_config.Read(CONFIG_EXTRACONFIG, &s);
_extraConfiguration = s;
/* Format options. */
_formatOptions.clear();
s = "";
_config.Read(CONFIG_FORMATOPTIONS, &s);
for (auto combined : split(std::string(s), ','))
{
auto pair = split(combined, ':');
try
{
auto key = std::make_pair(pair.at(0), pair.at(1));
_formatOptions.insert(key);
}
catch (std::exception&)
{
}
}
/* Triggers SaveConfig */
_dontSaveConfig = false;
@@ -1514,6 +1554,16 @@ public:
_config.Write(CONFIG_FORMAT,
formatChoice->GetString(formatChoice->GetSelection()));
_config.Write(CONFIG_EXTRACONFIG, wxString(_extraConfiguration));
/* Format options. */
{
std::vector<std::string> options;
for (auto& e : _formatOptions)
options.push_back(fmt::format("{}:{}", e.first, e.second));
_config.Write(CONFIG_FORMATOPTIONS, wxString(join(options, ",")));
}
}
void UpdateState()
@@ -1615,6 +1665,117 @@ public:
}
}
void UpdateFormatOptions()
{
assert(!wxGetApp().IsWorkerThreadRunning());
int formatSelection = formatChoice->GetSelection();
if (formatSelection != _currentlyDisplayedFormat)
{
_currentlyDisplayedFormat = formatSelection;
formatOptionsContainer->DestroyChildren();
auto* sizer = new wxBoxSizer(wxVERTICAL);
if (formatSelection == wxNOT_FOUND)
sizer->Add(new wxStaticText(
formatOptionsContainer, wxID_ANY, "(no format selected)"));
else
{
config.Clear();
std::string formatName =
_formatNames[formatChoice->GetSelection()];
FlagGroup::parseConfigFile(formatName, formats);
std::set<std::string> exclusivityGroups;
for (auto& option : config.option())
{
if (option.has_exclusivity_group())
exclusivityGroups.insert(option.exclusivity_group());
}
if (config.option().empty())
sizer->Add(new wxStaticText(formatOptionsContainer,
wxID_ANY,
"(no options for this format)"));
else
{
/* Add grouped radiobuttons for anything in an exclusivity
* group. */
for (auto& group : exclusivityGroups)
{
bool first = true;
for (auto& option : config.option())
{
if (option.exclusivity_group() != group)
continue;
auto* rb = new wxRadioButton(formatOptionsContainer,
wxID_ANY,
option.comment());
auto key =
std::make_pair(formatName, option.name());
sizer->Add(rb);
rb->Bind(wxEVT_RADIOBUTTON,
[=](wxCommandEvent& e)
{
for (auto& option : config.option())
{
if (option.exclusivity_group() == group)
_formatOptions.erase(std::make_pair(
formatName, option.name()));
}
_formatOptions.insert(key);
OnControlsChanged(e);
});
if (_formatOptions.find(key) !=
_formatOptions.end())
rb->SetValue(true);
if (first)
rb->SetExtraStyle(wxRB_GROUP);
first = false;
}
}
/* Anything that's _not_ in a group gets a checkbox. */
for (auto& option : config.option())
{
if (option.has_exclusivity_group())
continue;
auto* choice = new wxCheckBox(
formatOptionsContainer, wxID_ANY, option.comment());
auto key = std::make_pair(formatName, option.name());
sizer->Add(choice);
if (_formatOptions.find(key) != _formatOptions.end())
choice->SetValue(true);
choice->Bind(wxEVT_CHECKBOX,
[=](wxCommandEvent& e)
{
if (choice->GetValue())
_formatOptions.insert(key);
else
_formatOptions.erase(key);
OnControlsChanged(e);
});
}
}
}
formatOptionsContainer->SetSizerAndFit(sizer);
idlePanel->Layout();
}
}
void OnTrackSelection(TrackSelectionEvent& event)
{
(new FluxViewerWindow(this, event.trackFlux))->Show(true);
@@ -1713,6 +1874,8 @@ private:
int _explorerSide;
bool _explorerUpdatePending;
std::unique_ptr<const Fluxmap> _explorerFluxmap;
std::set<std::pair<std::string, std::string>> _formatOptions;
int _currentlyDisplayedFormat = wxNOT_FOUND - 1;
};
wxWindow* FluxEngineApp::CreateMainWindow()