diff --git a/projects/Makefile b/projects/Makefile index f735c9d7..9fc7e43d 100644 --- a/projects/Makefile +++ b/projects/Makefile @@ -252,7 +252,7 @@ COMMONFILES := \ View.o ModalView.o FieldView.o UIField.o UIIntField.o \ UIIntVarOffField.o UIIntVarField.o ViewEvent.o I_Action.o\ UITempoField.o UIActionField.o \ - MessageBox.o \ + MessageBox.o CommandSelectorModal.o \ GrooveView.o UINoteVarField.o UIBigHexVarField.o \ SRPUpdaters.o UIStaticField.o \ Song.o Chain.o Phrase.o Project.o Scale.o \ diff --git a/projects/lgpt.vcproj b/projects/lgpt.vcproj index d50935b8..f65adeb8 100644 --- a/projects/lgpt.vcproj +++ b/projects/lgpt.vcproj @@ -1051,6 +1051,14 @@ RelativePath="..\sources\Application\Views\ModalDialogs\SelectProjectDialog.h" > + + + + = startX && x <= endX && y >= startY && y <= endY; +} + +inline bool isCommandColumn(int col, int c1, int c2) { + return col == c1 || col == c2; +} + +inline bool isCommandColumn(int col, int c1, int c2, int c3) { + return col == c1 || col == c2 || col == c3; +} + +inline FourCC *getCommandPointerByCol(int col, int c1, FourCC *p1, int c2, + FourCC *p2) { + if (col == c1) { + return p1; + } + if (col == c2) { + return p2; + } + return 0; +} + +inline FourCC *getCommandPointerByCol(int col, int c1, FourCC *p1, int c2, + FourCC *p2, int c3, FourCC *p3) { + if (col == c1) { + return p1; + } + if (col == c2) { + return p2; + } + if (col == c3) { + return p3; + } + return 0; +} + +} // namespace CommandSelectorCommon + +#endif diff --git a/sources/Application/Views/ModalDialogs/CommandSelectorModal.cpp b/sources/Application/Views/ModalDialogs/CommandSelectorModal.cpp new file mode 100644 index 00000000..e74d170f --- /dev/null +++ b/sources/Application/Views/ModalDialogs/CommandSelectorModal.cpp @@ -0,0 +1,178 @@ +#include "CommandSelectorModal.h" +#include "Application/Utils/HelpLegend.h" +#include "Application/Utils/char.h" + +static int GetDisplayCommandCount() { + int count = CommandList::GetCount() - 1; + return (count > 0) ? count : 0; +} + +static FourCC GetDisplayCommandAt(int index) { + return CommandList::GetAt(index + 1); +} + +CommandSelectorModal::CommandSelectorModal(View &parentView, + FourCC *liveTarget, + ModalViewCallback previewCb): + ModalView(parentView), + selectedRow_(0), + selectedCol_(0), + selectedCommand_(I_CMD_NONE), + parentView_(parentView), + liveTarget_(liveTarget), + savedCmd_(liveTarget ? *liveTarget : I_CMD_NONE), + previewCb_(previewCb) { + FourCC initial = liveTarget_ ? *liveTarget_ : I_CMD_NONE; + if (initial != I_CMD_NONE) { + moveToCommand(initial); + } else { + moveToCommand(I_CMD_ARPG); + } +} + +CommandSelectorModal::~CommandSelectorModal() {} + +int CommandSelectorModal::commandToRow(FourCC command) const { + int idx = CommandList::IndexOf(command) - 1; + if (idx < 0) { + return 0; + } + return idx / GRID_COLUMNS; +} + +int CommandSelectorModal::commandToCol(FourCC command) const { + int idx = CommandList::IndexOf(command) - 1; + if (idx < 0) { + return 0; + } + return idx % GRID_COLUMNS; +} + +FourCC CommandSelectorModal::cellAtGridPos(int row, int col) const { + int count = GetDisplayCommandCount(); + int index = row * GRID_COLUMNS + col; + if (index >= count || index < 0) { + return I_CMD_NONE; + } + return GetDisplayCommandAt(index); +} + +void CommandSelectorModal::moveToCommand(FourCC command) { + int idx = CommandList::IndexOf(command); + if (idx < 0) { + command = I_CMD_ARPG; + } + selectedCommand_ = command; + selectedRow_ = commandToRow(command); + selectedCol_ = commandToCol(command); +} + +void CommandSelectorModal::navigateGrid(int deltaRow, int deltaCol) { + int count = GetDisplayCommandCount(); + int rows = (count + GRID_COLUMNS - 1) / GRID_COLUMNS; + + int newRow = selectedRow_; + int newCol = selectedCol_; + int tries = rows * GRID_COLUMNS; + + while (tries-- > 0) { + newRow += deltaRow; + newCol += deltaCol; + + if (newRow < 0) { + newRow = rows - 1; + } else if (newRow >= rows) { + newRow = 0; + } + + if (newCol < 0) { + newCol = GRID_COLUMNS - 1; + } else if (newCol >= GRID_COLUMNS) { + newCol = 0; + } + + FourCC candidate = cellAtGridPos(newRow, newCol); + if (candidate != I_CMD_NONE) { + selectedRow_ = newRow; + selectedCol_ = newCol; + selectedCommand_ = candidate; + if (liveTarget_) { + *liveTarget_ = selectedCommand_; + } + if (previewCb_) { + previewCb_(parentView_, *this); + } + return; + } + } +} + +void CommandSelectorModal::ProcessButtonMask(unsigned short mask, bool pressed) { + if (!pressed) { + return; + } + + if (mask & EPBM_UP) { + navigateGrid(-1, 0); + isDirty_ = true; + } else if (mask & EPBM_DOWN) { + navigateGrid(1, 0); + isDirty_ = true; + } else if (mask & EPBM_LEFT) { + navigateGrid(0, -1); + isDirty_ = true; + } else if (mask & EPBM_RIGHT) { + navigateGrid(0, 1); + isDirty_ = true; + } else if (mask & EPBM_A) { + EndModal(1); // Confirm selection + } else if (mask & EPBM_B) { + if (liveTarget_) { + *liveTarget_ = savedCmd_; + } + EndModal(0); // Cancel + } +} + +void CommandSelectorModal::DrawView() { + int count = GetDisplayCommandCount(); + int rows = (count + GRID_COLUMNS - 1) / GRID_COLUMNS; + int width = GRID_COLUMNS * 5; + + SetWindow(width, rows); + + GUITextProperties props; + + // Draw grid + char cellStr[6]; + for (int r = 0; r < rows; r++) { + for (int c = 0; c < GRID_COLUMNS; c++) { + FourCC cmd = cellAtGridPos(r, c); + if (cmd == I_CMD_NONE) { + continue; + } + bool isSelected = (r == selectedRow_ && c == selectedCol_); + + fourCC2char(cmd, cellStr); + cellStr[4] = 0; + + props.invert_ = isSelected; + SetColor(isSelected ? CD_HILITE2 : CD_NORMAL); + DrawString(c * 5, r, cellStr, props); + } + } + + props.invert_ = false; + SetColor(CD_NORMAL); + + std::string *cmdStr = getHelpLegend(selectedCommand_); + for (int i = 0; i < 3; i++) { + // Clear legend area first so shorter lines don't leave stale text. + View::DrawString(10, i, " ", props); + View::DrawString(10, i, cmdStr[i].c_str(), props); + + } +} + +void CommandSelectorModal::OnPlayerUpdate(PlayerEventType, unsigned int) {} +void CommandSelectorModal::OnFocus() {} diff --git a/sources/Application/Views/ModalDialogs/CommandSelectorModal.h b/sources/Application/Views/ModalDialogs/CommandSelectorModal.h new file mode 100644 index 00000000..f0e8d889 --- /dev/null +++ b/sources/Application/Views/ModalDialogs/CommandSelectorModal.h @@ -0,0 +1,38 @@ +#ifndef _COMMAND_SELECTOR_MODAL_H_ +#define _COMMAND_SELECTOR_MODAL_H_ + +#include "Application/Views/BaseClasses/ModalView.h" +#include "Application/Views/BaseClasses/View.h" +#include "Application/Instruments/CommandList.h" +#include "Application/Views/CommandSelectorCommon.h" + +class CommandSelectorModal : public ModalView { + public: + CommandSelectorModal(View &parentView, FourCC *liveTarget, + ModalViewCallback previewCb = 0); + virtual ~CommandSelectorModal(); + + virtual void ProcessButtonMask(unsigned short mask, bool pressed); + virtual void DrawView(); + virtual void OnPlayerUpdate(PlayerEventType, unsigned int tick = 0); + virtual void OnFocus(); + + private: + void navigateGrid(int deltaRow, int deltaCol); + void moveToCommand(FourCC command); + int commandToRow(FourCC command) const; + int commandToCol(FourCC command) const; + FourCC cellAtGridPos(int row, int col) const; + + int selectedRow_; + int selectedCol_; + FourCC selectedCommand_; + View &parentView_; + FourCC *liveTarget_; + FourCC savedCmd_; + ModalViewCallback previewCb_; + + static const int GRID_COLUMNS = CommandSelectorCommon::kColumns; +}; + +#endif diff --git a/sources/Application/Views/PhraseView.cpp b/sources/Application/Views/PhraseView.cpp index 75ac15bf..fcb0055c 100644 --- a/sources/Application/Views/PhraseView.cpp +++ b/sources/Application/Views/PhraseView.cpp @@ -4,6 +4,8 @@ #include "Application/Model/Table.h" #include "Application/Utils/HelpLegend.h" #include "Application/Utils/char.h" +#include "Application/Views/CommandSelectorCommon.h" +#include "Application/Views/ModalDialogs/CommandSelectorModal.h" #include "System/Console/Trace.h" #include "UIController.h" #include @@ -11,6 +13,14 @@ short PhraseView::offsets_[2][4] = {-1, 1, 12, -12, -1, 1, 16, -16}; +static void CommandSelectorCallback(View &v, ModalView &d) { + ((PhraseView &)v).onCommandSelectorResult(d); +} + +static void CommandSelectorPreviewCallback(View &v, ModalView &d) { + ((PhraseView &)v).onCommandSelectorPreview(d); +} + PhraseView::PhraseView(GUIWindow &w, ViewData *viewData) : View(w, viewData), cmdEdit_("edit", FCC_EDIT, 0) { phrase_ = viewData_->song_->phrase_; @@ -25,6 +35,7 @@ PhraseView::PhraseView(GUIWindow &w, ViewData *viewData) lastInstr_ = 0; lastCmd_ = I_CMD_NONE; lastParam_ = 0; + commandSelectorModalActive_ = false; clipboard_.active_ = false; clipboard_.width_ = 0; @@ -110,6 +121,43 @@ void PhraseView::stopAudition() { player->Stop(); } +bool PhraseView::isCommandColumn() const { return col_ == 2 || col_ == 4; } + +FourCC *PhraseView::getCurrentCommandPointer() { + return CommandSelectorCommon::getCommandPointerByCol( + col_, 2, phrase_->cmd1_ + (16 * viewData_->currentPhrase_ + row_), 4, + phrase_->cmd2_ + (16 * viewData_->currentPhrase_ + row_)); +} + +void PhraseView::enterCommandSelector() { + FourCC *cmdPtr = getCurrentCommandPointer(); + if (!cmdPtr) return; + commandSelectorModalActive_ = true; + DoModal(new CommandSelectorModal(*this, cmdPtr, CommandSelectorPreviewCallback), + CommandSelectorCallback); +} + +void PhraseView::onCommandSelectorResult(ModalView &d) { + commandSelectorModalActive_ = false; + CommandSelectorModal &modal = (CommandSelectorModal &)d; + if (modal.GetReturnCode() == 1) { + FourCC *cmd = getCurrentCommandPointer(); + if (cmd) { + lastCmd_ = *cmd; + } + } + isDirty_ = true; +} + +void PhraseView::onCommandSelectorPreview(ModalView &) { + isDirty_ = true; + Player *player = Player::GetInstance(); + if (!player->IsRunning()) { + player->OnStartButton(PM_AUDITION, viewData_->songX_, false, + viewData_->chainRow_); + } +} + void PhraseView::updateCursorValue(ViewUpdateDirection direction, int xOffset, int yOffset) { @@ -938,10 +986,19 @@ void PhraseView::processNormalButtonMask(unsigned short mask) { Player *player = Player::GetInstance(); player->OnStartButton(PM_AUDITION, viewData_->songX_, false, viewData_->chainRow_); } - if (mask & EPBM_DOWN) - updateCursorValue(VUD_DOWN); - if (mask & EPBM_UP) - updateCursorValue(VUD_UP); + + if (mask & EPBM_DOWN) { + if (isCommandColumn()) + enterCommandSelector(); + else + updateCursorValue(VUD_DOWN); + } + if (mask & EPBM_UP) { + if (isCommandColumn()) + enterCommandSelector(); + else + updateCursorValue(VUD_UP); + } if (mask & EPBM_LEFT) updateCursorValue(VUD_LEFT); if (mask & EPBM_RIGHT) @@ -1374,17 +1431,20 @@ void PhraseView::DrawView() { void PhraseView::OnPlayerUpdate(PlayerEventType eventType, unsigned int tick) { + GUITextProperties props; drawNotes(); GUIPoint anchor = GetAnchor(); GUIPoint pos = anchor; pos._x -= 1; - GUITextProperties props; SetColor(CD_NORMAL); pos._y = anchor._y + lastPlayingPos_; - DrawString(pos._x, pos._y, " ", props); + if (!commandSelectorModalActive_ || + !CommandSelectorCommon::popupContainsPoint(anchor, pos._x, pos._y)) { + DrawString(pos._x, pos._y, " ", props); + } Player *player = Player::GetInstance(); @@ -1394,17 +1454,20 @@ void PhraseView::OnPlayerUpdate(PlayerEventType eventType, unsigned int tick) { for (int i = 0; i < SONG_CHANNEL_COUNT; i++) { if (player->IsChannelPlaying(i)) { - if (viewData_->currentPlayPhrase_[i] == viewData_->currentPhrase_ && viewData_->playMode_ != PM_AUDITION) { pos._y = anchor._y + viewData_->phrasePlayPos_[i]; - if (!player->IsChannelMuted(i)) { - SetColor(CD_PLAY); - DrawString(pos._x, pos._y, ">", props); - } else { - SetColor(CD_MUTE); - DrawString(pos._x, pos._y, "-", props); + if (!commandSelectorModalActive_ || + !CommandSelectorCommon::popupContainsPoint( + anchor, pos._x, pos._y)) { + if (!player->IsChannelMuted(i)) { + SetColor(CD_PLAY); + DrawString(pos._x, pos._y, ">", props); + } else { + SetColor(CD_MUTE); + DrawString(pos._x, pos._y, "-", props); + } } SetColor(CD_NORMAL); lastPlayingPos_ = viewData_->phrasePlayPos_[i]; @@ -1414,9 +1477,7 @@ void PhraseView::OnPlayerUpdate(PlayerEventType eventType, unsigned int tick) { } // clear any live indicator - pos._y = anchor._y; - DrawString(pos._x, pos._y, " ", props); // Loop on all channels to see if one has queued current chain if (player->GetSequencerMode() == SM_LIVE) { @@ -1430,7 +1491,11 @@ void PhraseView::OnPlayerUpdate(PlayerEventType eventType, unsigned int tick) { viewData_->song_->data_ + i + 8 * songPos; if (*chain == viewData_->currentChain_) { char *indicator = player->GetLiveIndicator(i); - DrawString(pos._x, pos._y, indicator, props); + if (!commandSelectorModalActive_ || + !CommandSelectorCommon::popupContainsPoint( + anchor, pos._x, pos._y)) { + DrawString(pos._x, pos._y, indicator, props); + } break; } } diff --git a/sources/Application/Views/PhraseView.h b/sources/Application/Views/PhraseView.h index 1e266254..6afdd2e1 100644 --- a/sources/Application/Views/PhraseView.h +++ b/sources/Application/Views/PhraseView.h @@ -17,12 +17,16 @@ class PhraseView : public View { virtual void DrawView(); virtual void OnPlayerUpdate(PlayerEventType, unsigned int tick = 0); virtual void OnFocus(); + void onCommandSelectorResult(ModalView &d); + void onCommandSelectorPreview(ModalView &d); protected: void updateCursor(int dx, int dy); void stopAudition(); void updateCursorValue(ViewUpdateDirection offset, int xOffset = 0, int yOffset = 0); + bool isCommandColumn() const; + FourCC *getCurrentCommandPointer(); void updateSelectionValue(ViewUpdateDirection direction); void warpToNeighbour(int offset); void warpInChain(int offset); @@ -54,11 +58,13 @@ class PhraseView : public View { int lastInstr_; int lastCmd_; int lastParam_; + bool commandSelectorModalActive_; Phrase *phrase_; int lastPlayingPos_; Variable cmdEdit_; UIBigHexVarField *cmdEditField_; void printHelpLegend(FourCC command, GUITextProperties props); + void enterCommandSelector(); struct clipboard { bool active_; diff --git a/sources/Application/Views/TableView.cpp b/sources/Application/Views/TableView.cpp index 1fe50cad..970d30d0 100644 --- a/sources/Application/Views/TableView.cpp +++ b/sources/Application/Views/TableView.cpp @@ -3,9 +3,19 @@ #include "Application/Player/TablePlayback.h" #include "Application/Utils/HelpLegend.h" #include "Application/Utils/char.h" +#include "Application/Views/CommandSelectorCommon.h" +#include "Application/Views/ModalDialogs/CommandSelectorModal.h" #define FCC_EDIT MAKE_FOURCC('T', 'B', 'E', 'D') +static void CommandSelectorCallback(View &v, ModalView &d) { + ((TableView &)v).onCommandSelectorResult(d); +} + +static void CommandSelectorPreviewCallback(View &v, ModalView &d) { + ((TableView &)v).onCommandSelectorPreview(d); +} + TableView::TableView(GUIWindow &w, ViewData *viewData) : View(w, viewData), cmdEdit_("edit", FCC_EDIT, 0) { row_ = 0; @@ -19,6 +29,7 @@ TableView::TableView(GUIWindow &w, ViewData *viewData) lastTsp_ = 0; lastCmd_ = I_CMD_NONE; lastParam_ = 0; + commandSelectorModalActive_ = false; clipboard_.active_ = false; clipboard_.width_ = 0; @@ -309,6 +320,7 @@ void TableView::pasteClipboard() { }; void TableView::updateCursor(int dx, int dy) { + col_ += dx; row_ += dy; if (col_ > 5) @@ -503,6 +515,40 @@ void TableView::updateCursorValue(int offset) { isDirty_ = true; } +bool TableView::isCommandColumn() const { + return CommandSelectorCommon::isCommandColumn(col_, 0, 2, 4); +} + +FourCC *TableView::getCurrentCommandPointer() { + Table &table = + TableHolder::GetInstance()->GetTable(viewData_->currentTable_); + return CommandSelectorCommon::getCommandPointerByCol( + col_, 0, table.cmd1_ + row_, 2, table.cmd2_ + row_, 4, + table.cmd3_ + row_); +} + +void TableView::enterCommandSelector() { + FourCC *cmdPtr = getCurrentCommandPointer(); + if (!cmdPtr) return; + commandSelectorModalActive_ = true; + DoModal(new CommandSelectorModal(*this, cmdPtr, CommandSelectorPreviewCallback), + CommandSelectorCallback); +} + +void TableView::onCommandSelectorResult(ModalView &d) { + commandSelectorModalActive_ = false; + CommandSelectorModal &modal = (CommandSelectorModal &)d; + if (modal.GetReturnCode() == 1) { + FourCC *cmd = getCurrentCommandPointer(); + if (cmd) { + lastCmd_ = *cmd; + } + } + isDirty_ = true; +} + +void TableView::onCommandSelectorPreview(ModalView &) { isDirty_ = true; } + void TableView::pasteLast() { uint *i = 0; @@ -593,10 +639,18 @@ void TableView::processNormalButtonMask(unsigned short mask) { // A modifier if (mask & EPBM_A) { - if (mask & EPBM_DOWN) - updateCursorValue(-0x10); - if (mask & EPBM_UP) - updateCursorValue(0x10); + if (mask & EPBM_DOWN) { + if (isCommandColumn()) + enterCommandSelector(); + else + updateCursorValue(-0x10); + } + if (mask & EPBM_UP) { + if (isCommandColumn()) + enterCommandSelector(); + else + updateCursorValue(0x10); + } if (mask & EPBM_LEFT) updateCursorValue(-0x01); if (mask & EPBM_RIGHT) @@ -925,15 +979,24 @@ void TableView::OnPlayerUpdate(PlayerEventType eventType, unsigned int tick) { pos._x = anchor._x - 1; pos._y = anchor._y + lastPosition_[0]; - DrawString(pos._x, pos._y, " ", props); + if (!commandSelectorModalActive_ || + !CommandSelectorCommon::popupContainsPoint(anchor, pos._x, pos._y)) { + DrawString(pos._x, pos._y, " ", props); + } pos._x += 10; pos._y = anchor._y + lastPosition_[1]; - DrawString(pos._x, pos._y, " ", props); + if (!commandSelectorModalActive_ || + !CommandSelectorCommon::popupContainsPoint(anchor, pos._x, pos._y)) { + DrawString(pos._x, pos._y, " ", props); + } pos._x += 10; pos._y = anchor._y + lastPosition_[2]; - DrawString(pos._x, pos._y, " ", props); + if (!commandSelectorModalActive_ || + !CommandSelectorCommon::popupContainsPoint(anchor, pos._x, pos._y)) { + DrawString(pos._x, pos._y, " ", props); + } TableHolder *th = TableHolder::GetInstance(); // Get current channel @@ -951,16 +1014,28 @@ void TableView::OnPlayerUpdate(PlayerEventType eventType, unsigned int tick) { pos._x = anchor._x - 1; pos._y = anchor._y + lastPosition_[0]; - SetColor(CD_PLAY); - DrawString(pos._x, pos._y, ">", props); + if (!commandSelectorModalActive_ || + !CommandSelectorCommon::popupContainsPoint(anchor, pos._x, + pos._y)) { + SetColor(CD_PLAY); + DrawString(pos._x, pos._y, ">", props); + } pos._x += 10; pos._y = anchor._y + lastPosition_[1]; - DrawString(pos._x, pos._y, ">", props); + if (!commandSelectorModalActive_ || + !CommandSelectorCommon::popupContainsPoint(anchor, pos._x, + pos._y)) { + DrawString(pos._x, pos._y, ">", props); + } pos._x += 10; pos._y = anchor._y + lastPosition_[2]; - DrawString(pos._x, pos._y, ">", props); + if (!commandSelectorModalActive_ || + !CommandSelectorCommon::popupContainsPoint(anchor, pos._x, + pos._y)) { + DrawString(pos._x, pos._y, ">", props); + } }; drawNotes(); } diff --git a/sources/Application/Views/TableView.h b/sources/Application/Views/TableView.h index db11ca99..d71310ec 100644 --- a/sources/Application/Views/TableView.h +++ b/sources/Application/Views/TableView.h @@ -14,6 +14,8 @@ class TableView : public View { virtual void DrawView(); virtual void OnPlayerUpdate(PlayerEventType, unsigned int tick = 0); virtual void OnFocus(); + void onCommandSelectorResult(ModalView &d); + void onCommandSelectorPreview(ModalView &d); protected: void processNormalButtonMask(unsigned short mask); @@ -31,6 +33,9 @@ class TableView : public View { void updateCursor(int dx, int dy); void updateCursorValue(int offset); + bool isCommandColumn() const; + FourCC *getCurrentCommandPointer(); + void enterCommandSelector(); void setTextProps(GUITextProperties &props, int row, int col, bool restore); void warpToNeighbour(int dir); @@ -44,6 +49,7 @@ class TableView : public View { uchar lastTsp_; int lastCmd_; int lastParam_; + bool commandSelectorModalActive_; Variable cmdEdit_; UIBigHexVarField *cmdEditField_;