| 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 |  | 
|---|