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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
35 | MinUICommandDialog::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
122 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
139 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
154 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
270 | void MinUICommandDialog::processCancel() |
271 | { |
272 | instance().eventHandler().leaveMenuMode(); |
273 | } |
274 | |
275 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
276 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
287 | void MinUICommandDialog::updateTVFormat() |
288 | { |
289 | myTVFormatButton->setLabel(instance().console().getFormatString() + " Mode" ); |
290 | } |
291 | |
292 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
293 | void MinUICommandDialog::updateWinds() |
294 | { |
295 | RewindManager& r = instance().state().rewindManager(); |
296 | |
297 | myRewindButton->setEnabled(!r.atFirst()); |
298 | myUnwindButton->setEnabled(!r.atLast()); |
299 | } |
300 | |
301 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
302 | void 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 | |