Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/input/Button.cc @ 9982

Last change on this file since 9982 was 9982, checked in by landauf, 10 years ago

added option to force the arguments which are defined in the keybindings file (instead of overwriting them with the input device's state)

  • Property svn:eol-style set to native
File size: 9.9 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30@file
31@brief
32    Implementation of the different input handlers.
33*/
34
35#include "Button.h"
36
37#include "util/Convert.h"
38#include "util/SubString.h"
39#include "util/StringUtils.h"
40#include "util/Output.h"
41#include "core/command/ConsoleCommand.h"
42#include "core/command/CommandEvaluation.h"
43#include "core/command/CommandExecutor.h"
44#include "core/config/ConfigFile.h"
45
46namespace orxonox
47{
48    /**
49    @note
50        bButtonThresholdUser_: We set it to true so that setConfigValues in KeyBinder sets the value
51        correctly the first time. It is then set to false first and changed later in Button::parse().
52    */
53    Button::Button()
54        : bButtonThresholdUser_(false)
55        , paramCommandBuffer_(0)
56    {
57        nCommands_[0]=0;
58        nCommands_[1]=0;
59        nCommands_[2]=0;
60    }
61
62    Button::~Button()
63    {
64        this->clear();
65    }
66
67    void Button::clear()
68    {
69        for (unsigned int j = 0; j < 3; j++)
70        {
71            if (nCommands_[j])
72            {
73                // delete all commands and the command pointer array
74                for (unsigned int i = 0; i < nCommands_[j]; i++)
75                    delete commands_[j][i];
76                delete[] commands_[j];
77                commands_[j] = 0;
78                nCommands_[j] = 0;
79            }
80        }
81        this->bindingString_.clear();
82    }
83
84    void Button::readBinding(ConfigFile* configFile, ConfigFile* fallbackFile)
85    {
86        std::string binding = configFile->getOrCreateValue(groupName_, name_, "", true);
87        if (binding.empty() && fallbackFile)
88            binding = fallbackFile->getValue(groupName_, name_, true);
89        this->parse(binding);
90    }
91
92    void Button::setBinding(ConfigFile* configFile, ConfigFile* fallbackFile, const std::string& binding, bool bTemporary)
93    {
94        if (!bTemporary)
95            configFile->setValue(groupName_, name_, binding, true);
96        this->parse(binding);
97    }
98
99    void Button::parse(const std::string& binding)
100    {
101        if (binding == this->bindingString_)
102            return;
103
104        // delete all commands
105        clear();
106        this->bindingString_ = binding;
107
108        if (isEmpty(bindingString_) || removeTrailingWhitespaces(getLowercase(binding)) == "nobinding")
109            return;
110
111        // reset this to false first when parsing (was true before when parsing for the first time)
112        bButtonThresholdUser_ = false;
113
114        // use std::vector for a temporary dynamic array
115        std::vector<BaseCommand*> commands[3];
116
117        // separate the commands
118        SubString commandStrings(bindingString_, "|", SubString::WhiteSpaces, false,
119            '\\', false, '"', false, '{', '}', false, '\0');
120
121        for (unsigned int iCommand = 0; iCommand < commandStrings.size(); iCommand++)
122        {
123            if (!commandStrings[iCommand].empty())
124            {
125                SubString tokens(commandStrings[iCommand], " ", SubString::WhiteSpaces, false,
126                    '\\', false, '"', false, '{', '}', false, '\0');
127
128                KeybindMode::Value mode = KeybindMode::None;
129                float paramModifier = 1.0f;
130                std::string commandStr;
131                bool forceArguments = false;
132
133                for (unsigned int iToken = 0; iToken < tokens.size(); ++iToken)
134                {
135                    const std::string& token = getLowercase(tokens[iToken]);
136
137                    if (token == "onpress")
138                        mode = KeybindMode::OnPress;
139                    else if (token == "onrelease")
140                        mode = KeybindMode::OnRelease;
141                    else if (token == "onpressandrelease")
142                        mode = KeybindMode::OnPressAndRelease;
143                    else if (token == "onhold")
144                        mode = KeybindMode::OnHold;
145                    else if (token == "forcearguments")
146                        forceArguments = true;
147                    else if (token == "buttonthreshold")
148                    {
149                        // for real axes, we can feed a ButtonThreshold argument
150                        ++iToken;
151                        if (iToken == tokens.size())
152                            continue;
153                        // may fail, but doesn't matter (default value already set)
154                        if (!convertValue(&buttonThreshold_, tokens[iToken + 1]))
155                            parseError("Could not parse 'ButtonThreshold' argument. \
156                                Switching to default value.", true);
157                        else
158                            this->bButtonThresholdUser_ = true;
159                    }
160                    else if (token == "scale")
161                    {
162                        ++iToken;
163                        if (iToken == tokens.size() || !convertValue(&paramModifier, tokens[iToken]))
164                            parseError("Could not parse 'scale' argument. Switching to default value.", true);
165                    }
166                    else
167                    {
168                        // no input related argument
169                        // we interpret everything from here as a command string
170                        while (iToken != tokens.size())
171                            commandStr += tokens[iToken++] + ' ';
172                    }
173                }
174
175                if (commandStr.empty())
176                {
177                    parseError("No command string given.", false);
178                    continue;
179                }
180
181                // evaluate the command
182                CommandEvaluation eval = CommandExecutor::evaluate(commandStr);
183                if (!eval.isValid() || eval.evaluateArguments(true))
184                {
185                    parseError("Command evaluation of \"" + commandStr + "\"failed.", true);
186                    continue;
187                }
188
189                // check for param command
190                int paramIndex = eval.getConsoleCommand()->getInputConfiguredParam_();
191                if (paramIndex >= 0 && !forceArguments)
192                {
193                    // parameter supported command
194                    ParamCommand* cmd = new ParamCommand();
195                    cmd->scale_ = paramModifier;
196
197                    // add command to the buffer if not yet existing
198                    for (unsigned int iParamCmd = 0; iParamCmd < paramCommandBuffer_->size(); iParamCmd++)
199                    {
200                        if ((*paramCommandBuffer_)[iParamCmd]->evaluation_.getConsoleCommand()
201                            == eval.getConsoleCommand())
202                        {
203                            // already in list
204                            cmd->paramCommand_ = (*paramCommandBuffer_)[iParamCmd];
205                            break;
206                        }
207                    }
208                    if (cmd->paramCommand_ == 0)
209                    {
210                        cmd->paramCommand_ = new BufferedParamCommand();
211                        paramCommandBuffer_->push_back(cmd->paramCommand_);
212                        cmd->paramCommand_->evaluation_ = eval;
213                        cmd->paramCommand_->paramIndex_ = paramIndex;
214                    }
215
216
217                    // we don't know whether this is an actual axis or just a button
218                    if (mode != KeybindMode::None || !addParamCommand(cmd))
219                        this->addCommand(cmd, mode, commands);
220                }
221                else
222                {
223                    SimpleCommand* cmd = new SimpleCommand();
224                    cmd->evaluation_ = eval;
225
226                    this->addCommand(cmd, mode, commands);
227                }
228            }
229        }
230
231        for (unsigned int j = 0; j < 3; j++)
232        {
233            nCommands_[j] = commands[j].size();
234            if (nCommands_[j])
235            {
236                commands_[j] = new BaseCommand*[nCommands_[j]];
237                for (unsigned int i = 0; i < commands[j].size(); i++)
238                    commands_[j][i] = commands[j][i];
239            }
240            else
241                commands_[j] = 0;
242        }
243    }
244
245    inline void Button::addCommand(BaseCommand* cmd, KeybindMode::Value mode, std::vector<BaseCommand*> commands[3])
246    {
247        if (mode == KeybindMode::None)
248            mode = cmd->getEvaluation()->getConsoleCommand()->getKeybindMode();
249        else
250            cmd->setFixedKeybindMode(true);
251
252        if (mode == KeybindMode::OnPressAndRelease)
253        {
254            BaseCommand* cmd2 = cmd->clone();
255
256            commands[KeybindMode::OnPress].push_back(cmd);
257            commands[KeybindMode::OnRelease].push_back(cmd2); // clone
258        }
259        else
260            commands[mode].push_back(cmd);
261    }
262
263    inline void Button::parseError(const std::string& message, bool serious)
264    {
265        if (serious)
266        {
267            orxout(internal_error, context::input) << "Error while parsing binding for button/axis " << this->name_ << ". "
268                << message << endl;
269        }
270        else
271        {
272            orxout(internal_warning, context::input) << "Warning while parsing binding for button/axis " << this->name_ << ". "
273                << message << endl;
274        }
275    }
276}
Note: See TracBrowser for help on using the repository browser.