diff --git a/doomsday/plugins/common/include/hu_lib.h b/doomsday/plugins/common/include/hu_lib.h index a79e1d59ab..a8045cd5b9 100644 --- a/doomsday/plugins/common/include/hu_lib.h +++ b/doomsday/plugins/common/include/hu_lib.h @@ -224,6 +224,7 @@ struct Widget Page *page() const; int flags() const; + bool isActive() { return (_flags & MNF_ACTIVE) != 0; } Widget &setFlags(flagop_t op, int flags); diff --git a/doomsday/plugins/common/include/m_ctrl.h b/doomsday/plugins/common/include/m_ctrl.h index da1b6c3769..62be431008 100644 --- a/doomsday/plugins/common/include/m_ctrl.h +++ b/doomsday/plugins/common/include/m_ctrl.h @@ -50,7 +50,8 @@ struct InputBindingWidget : public Widget int handleEvent_Privileged(event_t *ev); - char const *controlName(); + char const *controlName() const; + de::String bindContext() const; }; int InputBindingWidget_CommandResponder(Widget *ob, menucommand_e command); diff --git a/doomsday/plugins/common/src/m_ctrl.cpp b/doomsday/plugins/common/src/m_ctrl.cpp index ec3ad387b4..2afa7aadc7 100644 --- a/doomsday/plugins/common/src/m_ctrl.cpp +++ b/doomsday/plugins/common/src/m_ctrl.cpp @@ -619,133 +619,133 @@ void Hu_MenuControlGrabDrawer(char const *niceName, float alpha) DGL_Disable(DGL_TEXTURE_2D); } +/** + * Read the symbolic descriptor from the given @a event. + */ +static String symbolicDescriptor(event_t const &event) +{ + if(event.type == EV_SYMBOLIC) + { +#ifndef __64BIT__ + String symbol = (char const *) event.data1; +#else + String symbol = (char const *)( (duint32(event.data2) << 32) | duint32(event.data1) ); +#endif + if(symbol.beginsWith("echo-")) + { + return symbol.substr(5); + } + } + return ""; // No symbolic descriptor. +} + int InputBindingWidget::handleEvent_Privileged(event_t *ev) { DENG2_ASSERT(ev); LOG_AS("InputBindingWidget"); + // Only handle events when active. + if(!isActive()) return false; + + // We're only interested in events with an echoed, symbolic descriptor. + String symbol = symbolicDescriptor(*ev); + if(symbol.isEmpty()) return false; + // We're interested in key or button down events. - if((Widget::_flags & MNF_ACTIVE) && ev->type == EV_SYMBOLIC) + if(symbol.beginsWith("key-") && !symbol.endsWith("-down")) { - String symbol; - { -#ifndef __64BIT__ - char const *rawSymbol = (char const *) ev->data1; -#else - { - uint64_t address = (uint32_t) ev->data2; - address <<= 32; - address |= (uint32_t) ev->data1; - rawSymbol = (char const *) address; - } -#endif - symbol = rawSymbol; - } + return false; + } + + String const context = bindContext(); - if(!symbol.beginsWith("echo-")) + // The Delete key in the Menu context is reserved for deleting bindings + if((!context.compareWithCase("menu") || !context.compareWithCase("shortcut")) && + !symbol.compareWithCase("key-delete-down")) + { + return false; + } + + String cmd; + if(binds->command) + { + cmd = String("bindevent {%1:%2%3} {%4}") + .arg(context) + .arg(symbol) + .arg(binds->flags & CCF_MULTIPLAYER? " + multiplayer" : "") + .arg(binds->command); + + // Check for repeats. + if((binds->flags & CCF_REPEAT) && symbol.endsWith("-down")) { - return false; + cmd += String("; bindevent {%1:%2-repeat} {%3}") + .arg(context) + .arg(symbol.left(symbol.length() - 5)) + .arg(binds->command); } - if(symbol.beginsWith("echo-key-") && !symbol.endsWith("-down")) + } + else if(binds->controlName) + { + String stateFlags; + + // Extract the symbolic key/button name (exclude the state part). + int const endOfName = symbol.indexOf('-', symbol.indexOf('-') + 1); + if(endOfName < 0) { - return false; + throw Error("InputBindingWidget::handleEvent_Privileged", "Invalid symbol:" + symbol); } + String const name = symbol.left(endOfName); - String bindContext = "game"; - if(binds->bindContext) + // Staged? + if(binds->flags & CCF_STAGED) { - bindContext = binds->bindContext; - - // The Delete key in the Menu context is reserved for deleting bindings - if((!bindContext.compareWithCase("menu") || !bindContext.compareWithCase("shortcut")) && - !symbol.substr(5).compareWithCase("key-delete-down")) + // Staging is for keys and buttons. + if(name.beginsWith("key-") || name.indexOf("-button") >= 0 || + !name.compareWithCase("mouse-left") || !name.compareWithCase("mouse-middle") || + !name.compareWithCase("mouse-right")) { - return false; + stateFlags += "-staged"; } } - String cmd; - if(binds->command) + // Inverted? + bool inv = (binds->flags & CCF_INVERSE) != 0; + if(symbol.substr(endOfName).beginsWith("-neg")) { - cmd = String("bindevent {%1:%2%3} {%4}") - .arg(bindContext) - .arg(symbol.substr(5)) - .arg(binds->flags & CCF_MULTIPLAYER? " + multiplayer" : "") - .arg(binds->command); - - // Check for repeats. - if((binds->flags & CCF_REPEAT) && symbol.endsWith("-down")) - { - cmd += String("; bindevent {%1:%2-repeat} {%3}") - .arg(bindContext) - .arg(symbol.substr(5, symbol.length() - 5 - 5)) - .arg(binds->command); - } + inv = !inv; } - else if(binds->controlName) + if(inv) { - // Have to exclude the state part. - bool inv = (binds->flags & CCF_INVERSE) != 0; - bool isStaged = (binds->flags & CCF_STAGED) != 0; - - int end = symbol.indexOf('-', symbol.indexOf('-', 5) + 1); - if(end < 0) - { - throw Error("InputBindingWidget::handleEvent_Privileged", "Invalid symbol:" + symbol); - } - - String temp3 = symbol.substr(5, symbol.length() - 5 - 5); - - // Check for inverse. - if(symbol.substr(end).beginsWith("-neg")) - { - inv = !inv; - } - if(isStaged && - (temp3.beginsWith("key-") || temp3.indexOf("-button") >= 0 || - !temp3.compareWithCase("mouse-left") || !temp3.compareWithCase("mouse-middle") || - !temp3.compareWithCase("mouse-right"))) - { - // Staging is for keys and buttons. - temp3 += "-staged"; - } - if(inv) - { - temp3 += "-inverse"; - } - - String extra; - if(binds->flags & CCF_SIDESTEP_MODIFIER) - { - cmd = String("bindcontrol sidestep {%1 + modifier-1-down}").arg(temp3); - LOGDEV_INPUT_MSG("PrivilegedResponder: %s") << cmd; - DD_Execute(true, cmd.toUtf8().constData()); - - extra = " + modifier-1-up"; - } - - cmd = String("bindcontrol {%1} {%2%3}") - .arg(binds->controlName) - .arg(temp3) - .arg(extra); + stateFlags += "-inverse"; } - LOGDEV_INPUT_MSG("PrivilegedResponder: %s") << cmd; - DD_Execute(true, cmd.toUtf8().constData()); + cmd = String("bindcontrol {%1} {%2%3%4}") + .arg(binds->controlName) + .arg(name) + .arg(stateFlags) + .arg((binds->flags & CCF_SIDESTEP_MODIFIER)? " + modifier-1-up" : ""); - // We've finished the grab. - Widget::_flags &= ~MNF_ACTIVE; - DD_SetInteger(DD_SYMBOLIC_ECHO, false); - S_LocalSound(SFX_MENU_ACCEPT, nullptr); - return true; + if(binds->flags & CCF_SIDESTEP_MODIFIER) + { + cmd += String("; bindcontrol sidestep {%1%2 + modifier-1-down}") + .arg(name) + .arg(stateFlags); + } } - return false; + LOGDEV_INPUT_MSG("PrivilegedResponder: ") << cmd; + DD_Execute(true, cmd.toUtf8().constData()); + + // We've finished the grab. + Widget::_flags &= ~MNF_ACTIVE; + DD_SetInteger(DD_SYMBOLIC_ECHO, false); + S_LocalSound(SFX_MENU_ACCEPT, nullptr); + return true; } -char const *InputBindingWidget::controlName() +char const *InputBindingWidget::controlName() const { - DENG2_ASSERT(binds != 0); + DENG2_ASSERT(binds); // Map to a text definition? if(PTR2INT(binds->text) > 0 && PTR2INT(binds->text) < NUMTEXT) { @@ -754,5 +754,11 @@ char const *InputBindingWidget::controlName() return binds->text; } +String InputBindingWidget::bindContext() const +{ + DENG2_ASSERT(binds); + return (binds->bindContext? binds->bindContext : "game"); +} + } // namespace menu } // namespace common