OXYGENHUB
◆ main openmsx / src / settings / Setting.cc
4.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "Setting.hh"
2
3
#include "CliComm.hh"
4
#include "CommandController.hh"
5
#include "GlobalCommandController.hh"
6
#include "MSXCommandController.hh"
7
#include "MSXException.hh"
8
#include "SettingsConfig.hh"
9
#include "TclObject.hh"
10
11
#include "checked_cast.hh"
12
13
namespace openmsx {
14
15
// class BaseSetting
16
17
BaseSetting::BaseSetting(std::string_view name)
18
	: fullName(name)
19
	, baseName(fullName)
20
{
21
}
22
23
BaseSetting::BaseSetting(TclObject name)
24
	: fullName(std::move(name))
25
	, baseName(fullName)
26
{
27
}
28
29
void BaseSetting::info(TclObject& result) const
30
{
31
	result.addListElement(getTypeString(), getDefaultValue());
32
	additionalInfo(result);
33
}
34
35
36
// class Setting
37
38
Setting::Setting(CommandController& commandController_,
39
                 std::string_view name, static_string_view description_,
40
                 const TclObject& initialValue, Save save_)
41
	: BaseSetting(name)
42
	, commandController(commandController_)
43
	, description(description_)
44
	, value(initialValue)
45
	, defaultValue(initialValue)
46
	, save(save_)
47
{
48
	checkFunc = [](const TclObject&) { /* nothing */ };
49
}
50
51
void Setting::init()
52
{
53
	if (needLoadSave()) {
54
		if (const auto* savedValue =
55
		            getGlobalCommandController().getSettingsConfig()
56
		                    .getValueForSetting(getBaseName())) {
57
			try {
58
				setValueDirect(TclObject(*savedValue));
59
			} catch (MSXException&) {
60
				// saved value no longer valid, just keep default
61
			}
62
		}
63
	}
64
	getCommandController().registerSetting(*this);
65
66
	// This is needed to for example inform catapult of the new setting
67
	// value when a setting was destroyed/recreated (by a machine switch
68
	// for example).
69
	notify();
70
}
71
72
Setting::~Setting()
73
{
74
	getCommandController().unregisterSetting(*this);
75
}
76
77
78
std::string_view Setting::getDescription() const
79
{
80
	return description;
81
}
82
83
void Setting::setValue(const TclObject& newValue)
84
{
85
	getInterpreter().setVariable(getFullNameObj(), newValue);
86
}
87
88
void Setting::notify() const
89
{
90
	// Notify all subsystems of a change in the setting value. There
91
	// are actually quite a few subsystems involved with the settings:
92
	//  - the setting classes themselves
93
	//  - the Tcl variables (and possibly traces on those variables)
94
	//  - Subject/Observers
95
	//  - CliComm setting-change events (for external GUIs)
96
	//  - SettingsConfig (keeps values, also of not yet created settings)
97
	// This method takes care of the last 3 in this list.
98
	Subject<Setting>::notify();
99
	auto base = getBaseName();
100
	TclObject val = getValue();
101
	commandController.getCliComm().updateFiltered(
102
		CliComm::UpdateType::SETTING, base, val.getString());
103
104
	// Always keep SettingsConfig in sync.
105
	auto& config = getGlobalCommandController().getSettingsConfig();
106
	if (!needLoadSave() || (val == getDefaultValue())) {
107
		config.removeValueForSetting(base);
108
	} else {
109
		config.setValueForSetting(base, val.getString());
110
	}
111
}
112
113
void Setting::notifyPropertyChange() const
114
{
115
	TclObject result;
116
	info(result);
117
	commandController.getCliComm().updateFiltered(
118
		CliComm::UpdateType::SETTING_INFO, getBaseName(), result.getString());
119
}
120
121
bool Setting::needLoadSave() const
122
{
123
	return save == Save::YES;
124
}
125
bool Setting::needTransfer() const
126
{
127
	return save != Save::NO_AND_DONT_TRANSFER;
128
}
129
130
GlobalCommandController& Setting::getGlobalCommandController() const
131
{
132
	if (auto* globalCommandController =
133
	    dynamic_cast<GlobalCommandController*>(&commandController)) {
134
		return *globalCommandController;
135
	} else {
136
		return checked_cast<MSXCommandController*>(&commandController)
137
			->getGlobalCommandController();
138
	}
139
}
140
141
Interpreter& Setting::getInterpreter() const
142
{
143
	return commandController.getInterpreter();
144
}
145
146
void Setting::tabCompletion(std::vector<std::string>& /*tokens*/) const
147
{
148
	// nothing
149
}
150
151
void Setting::additionalInfo(TclObject& /*result*/) const
152
{
153
	// nothing
154
}
155
156
157
void Setting::setValueDirect(const TclObject& newValue_)
158
{
159
	TclObject newValue = newValue_;
160
	checkFunc(newValue);
161
	if (newValue != value) {
162
		value = newValue;
163
		notify();
164
	}
165
166
	// synchronize proxy
167
	const auto* controller = dynamic_cast<MSXCommandController*>(
168
		&getCommandController());
169
	if (!controller) {
170
		// This is not a machine specific setting.
171
		return;
172
	}
173
	if (!controller->isActive()) {
174
		// This setting does not belong to the active machine.
175
		return;
176
	}
177
178
	// Tcl already makes sure this doesn't result in an endless loop.
179
	try {
180
		getInterpreter().setVariable(getBaseNameObj(), getValue());
181
	} catch (MSXException&) {
182
		// ignore
183
	}
184
}
185
186
} // namespace openmsx