1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include "Console.hxx"
19#include "TIA.hxx"
20#include "Switches.hxx"
21#include "Dialog.hxx"
22#include "Font.hxx"
23#include "EventHandler.hxx"
24#include "StateManager.hxx"
25#include "RewindManager.hxx"
26#include "OSystem.hxx"
27#include "Widget.hxx"
28#include "StellaSettingsDialog.hxx"
29#include "OptionsDialog.hxx"
30#include "TIASurface.hxx"
31
32#include "MinUICommandDialog.hxx"
33
34// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
35MinUICommandDialog::MinUICommandDialog(OSystem& osystem, DialogContainer& parent)
36 : Dialog(osystem, parent, osystem.frameBuffer().font(), "Commands"),
37 myStellaSettingsDialog(nullptr),
38 myOptionsDialog(nullptr)
39{
40 const int HBORDER = 10;
41 const int VBORDER = 10;
42 const int HGAP = 8;
43 const int VGAP = 5;
44 const int buttonWidth = _font.getStringWidth(" Load State 0") + 20,
45 buttonHeight = _font.getLineHeight() + 8,
46 rowHeight = buttonHeight + VGAP;
47
48 // Set real dimensions
49 _w = 3 * (buttonWidth + 5) + HBORDER * 2;
50 _h = 6 * rowHeight - VGAP + VBORDER * 2 + _th;
51 ButtonWidget* bw = nullptr;
52 WidgetArray wid;
53 int xoffset = HBORDER, yoffset = VBORDER + _th;
54
55 auto ADD_CD_BUTTON = [&](const string& label, int cmd)
56 {
57 ButtonWidget* b = new ButtonWidget(this, _font, xoffset, yoffset,
58 buttonWidth, buttonHeight, label, cmd);
59 yoffset += buttonHeight + VGAP;
60 return b;
61 };
62
63 // Column 1
64 bw = ADD_CD_BUTTON(GUI::SELECT, kSelectCmd);
65 wid.push_back(bw);
66 bw = ADD_CD_BUTTON("Reset", kResetCmd);
67 wid.push_back(bw);
68 myColorButton = ADD_CD_BUTTON("", kColorCmd);
69 wid.push_back(myColorButton);
70 myLeftDiffButton = ADD_CD_BUTTON("", kLeftDiffCmd);
71 wid.push_back(myLeftDiffButton);
72 myRightDiffButton = ADD_CD_BUTTON("", kLeftDiffCmd);
73 wid.push_back(myRightDiffButton);
74
75 // Column 2
76 xoffset += buttonWidth + HGAP;
77 yoffset = VBORDER + _th;
78
79 mySaveStateButton = ADD_CD_BUTTON("", kSaveStateCmd);
80 wid.push_back(mySaveStateButton);
81 myStateSlotButton = ADD_CD_BUTTON("", kStateSlotCmd);
82 wid.push_back(myStateSlotButton);
83 myLoadStateButton = ADD_CD_BUTTON("", kLoadStateCmd);
84 wid.push_back(myLoadStateButton);
85 myRewindButton = ADD_CD_BUTTON("Rewind", kRewindCmd);
86 wid.push_back(myRewindButton);
87 myUnwindButton = ADD_CD_BUTTON("Unwind", kUnwindCmd);
88 wid.push_back(myUnwindButton);
89
90 // Column 3
91 xoffset += buttonWidth + HGAP;
92 yoffset = VBORDER + _th;
93
94 myTVFormatButton = ADD_CD_BUTTON("", kFormatCmd);
95 wid.push_back(myTVFormatButton);
96 myStretchButton = ADD_CD_BUTTON("", kStretchCmd);
97 wid.push_back(myStretchButton);
98 myPhosphorButton = ADD_CD_BUTTON("", kPhosphorCmd);
99 wid.push_back(myPhosphorButton);
100 bw = ADD_CD_BUTTON("Fry", kFry);
101 wid.push_back(bw);
102 bw = ADD_CD_BUTTON("Settings" + ELLIPSIS, kSettings);
103 wid.push_back(bw);
104
105 // Bottom row
106 xoffset = HBORDER + (buttonWidth + HGAP) / 2;
107 bw = ADD_CD_BUTTON("Exit Game", kExitGameCmd);
108 wid.push_back(bw);
109 xoffset += buttonWidth + HGAP;
110 yoffset -= buttonHeight + VGAP;
111 bw = ADD_CD_BUTTON("Close", GuiObject::kCloseCmd);
112 wid.push_back(bw);
113
114 addToFocusList(wid);
115
116 // We don't have a close/cancel button, but we still want the cancel
117 // event to be processed
118 processCancelWithoutWidget(true);
119}
120
121// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
122void MinUICommandDialog::loadConfig()
123{
124 // Column 1
125 myColorButton->setLabel(instance().console().switches().tvColor() ? "Color Mode" : "B/W Mode");
126 myLeftDiffButton->setLabel(GUI::LEFT_DIFF + (instance().console().switches().leftDifficultyA() ? " A" : " B"));
127 myRightDiffButton->setLabel(GUI::RIGHT_DIFF + (instance().console().switches().rightDifficultyA() ? " A" : " B"));
128 // Column 2
129 updateSlot(instance().state().currentSlot());
130 updateWinds();
131
132 // Column 3
133 updateTVFormat();
134 myStretchButton->setLabel(instance().settings().getBool("tia.fs_stretch") ? "Stretched" : "4:3 Format");
135 myPhosphorButton->setLabel(instance().frameBuffer().tiaSurface().phosphorEnabled() ? "Phosphor On" : "Phosphor Off");
136}
137
138// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
139void MinUICommandDialog::handleKeyDown(StellaKey key, StellaMod mod, bool repeated)
140{
141 switch (key)
142 {
143 case KBDK_F8: // front ("Skill P2")
144 instance().eventHandler().leaveMenuMode();
145 break;
146
147 default:
148 Dialog::handleKeyDown(key, mod);
149 break;
150 }
151}
152
153// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
154void MinUICommandDialog::handleCommand(CommandSender* sender, int cmd,
155 int data, int id)
156{
157 bool consoleCmd = false, stateCmd = false;
158 Event::Type event = Event::NoType;
159
160 switch(cmd)
161 {
162 // Column 1
163 case kSelectCmd:
164 event = Event::ConsoleSelect;
165 consoleCmd = true;
166 break;
167
168 case kResetCmd:
169 event = Event::ConsoleReset;
170 consoleCmd = true;
171 break;
172
173 case kColorCmd:
174 event = Event::ConsoleColorToggle;
175 consoleCmd = true;
176 break;
177
178 case kLeftDiffCmd:
179 event = Event::ConsoleLeftDiffToggle;
180 consoleCmd = true;
181 break;
182
183 case kRightDiffCmd:
184 event = Event::ConsoleRightDiffToggle;
185 consoleCmd = true;
186 break;
187
188 // Column 2
189 case kSaveStateCmd:
190 event = Event::SaveState;
191 consoleCmd = true;
192 break;
193
194 case kStateSlotCmd:
195 {
196 event = Event::ChangeState;
197 stateCmd = true;
198 int slot = (instance().state().currentSlot() + 1) % 10;
199 updateSlot(slot);
200 break;
201 }
202
203 case kLoadStateCmd:
204 event = Event::LoadState;
205 consoleCmd = true;
206 break;
207
208 case kRewindCmd:
209 // rewind 5s
210 instance().state().rewindStates(5);
211 updateWinds();
212 break;
213
214 case kUnwindCmd:
215 // unwind 5s
216 instance().state().unwindStates(5);
217 updateWinds();
218 break;
219
220 // Column 3
221 case kFormatCmd:
222 instance().console().toggleFormat();
223 updateTVFormat();
224 break;
225
226 case kStretchCmd:
227 instance().eventHandler().leaveMenuMode();
228 instance().eventHandler().handleEvent(Event::VidmodeIncrease);
229 break;
230
231 case kPhosphorCmd:
232 instance().eventHandler().leaveMenuMode();
233 instance().console().togglePhosphor();
234 break;
235
236 case kFry:
237 instance().eventHandler().leaveMenuMode();
238 instance().console().fry();
239 break;
240
241 case kSettings:
242 openSettings();
243 break;
244
245 // Bottom row
246 case GuiObject::kCloseCmd:
247 instance().eventHandler().leaveMenuMode();
248 break;
249
250 case kExitGameCmd:
251 instance().eventHandler().handleEvent(Event::ExitMode);
252 break;
253 }
254
255 // Console commands should be performed right away, after leaving the menu
256 // State commands require you to exit the menu manually
257 if(consoleCmd)
258 {
259 instance().eventHandler().leaveMenuMode();
260 instance().eventHandler().handleEvent(event);
261 instance().console().switches().update();
262 instance().console().tia().update();
263 instance().eventHandler().handleEvent(event, false);
264 }
265 else if(stateCmd)
266 instance().eventHandler().handleEvent(event);
267}
268
269// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
270void MinUICommandDialog::processCancel()
271{
272 instance().eventHandler().leaveMenuMode();
273}
274
275// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
276void MinUICommandDialog::updateSlot(int slot)
277{
278 ostringstream buf;
279 buf << " " << slot;
280
281 mySaveStateButton->setLabel("Save State" + buf.str());
282 myStateSlotButton->setLabel("State Slot" + buf.str());
283 myLoadStateButton->setLabel("Load State" + buf.str());
284}
285
286// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
287void MinUICommandDialog::updateTVFormat()
288{
289 myTVFormatButton->setLabel(instance().console().getFormatString() + " Mode");
290}
291
292// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
293void MinUICommandDialog::updateWinds()
294{
295 RewindManager& r = instance().state().rewindManager();
296
297 myRewindButton->setEnabled(!r.atFirst());
298 myUnwindButton->setEnabled(!r.atLast());
299}
300
301// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
302void MinUICommandDialog::openSettings()
303{
304 // Create an options dialog, similar to the in-game one
305 if (instance().settings().getBool("basic_settings"))
306 {
307 if (myStellaSettingsDialog == nullptr)
308 myStellaSettingsDialog = make_unique<StellaSettingsDialog>(instance(), parent(),
309 instance().frameBuffer().launcherFont(), FBMinimum::Width, FBMinimum::Height, Menu::AppMode::launcher);
310 myStellaSettingsDialog->open();
311 }
312 else
313 {
314 if (myOptionsDialog == nullptr)
315 myOptionsDialog = make_unique<OptionsDialog>(instance(), parent(), this,
316 FBMinimum::Width, FBMinimum::Height, Menu::AppMode::launcher);
317 myOptionsDialog->open();
318 }
319}
320
321