Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 6536 was 6536, checked in by rgrieder, 14 years ago

Merged revisions 6430-6440 from the gamestate branch to the trunk.
This adds keybindings merging functionality.

(from log of r6437)
When running development builds, the keybinder will merge the local file and the one from the data folder.
Catch: if you want to remove a binding, you'll have to write "NoBinding" (not case sensitive) to override the default command

The keybind command already does that for you though.

  • Property svn:eol-style set to native
File size: 9.2 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/Debug.h"
41#include "core/ConsoleCommand.h"
42#include "core/CommandEvaluation.h"
43#include "core/CommandExecutor.h"
44#include "core/ConfigFileManager.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
132                for (unsigned int iToken = 0; iToken < tokens.size(); ++iToken)
133                {
134                    const std::string& token = getLowercase(tokens[iToken]);
135
136                    if (token == "onpress")
137                        mode = KeybindMode::OnPress;
138                    else if (token == "onrelease")
139                        mode = KeybindMode::OnRelease;
140                    else if (token == "onhold")
141                        mode = KeybindMode::OnHold;
142                    else if (token == "buttonthreshold")
143                    {
144                        // for real axes, we can feed a ButtonThreshold argument
145                        ++iToken;
146                        if (iToken == tokens.size())
147                            continue;
148                        // may fail, but doesn't matter (default value already set)
149                        if (!convertValue(&buttonThreshold_, tokens[iToken + 1]))
150                            parseError("Could not parse 'ButtonThreshold' argument. \
151                                Switching to default value.", true);
152                        else
153                            this->bButtonThresholdUser_ = true;
154                    }
155                    else if (token == "scale")
156                    {
157                        ++iToken;
158                        if (iToken == tokens.size() || !convertValue(&paramModifier, tokens[iToken]))
159                            parseError("Could not parse 'scale' argument. Switching to default value.", true);
160                    }
161                    else
162                    {
163                        // no input related argument
164                        // we interpret everything from here as a command string
165                        while (iToken != tokens.size())
166                            commandStr += tokens[iToken++] + ' ';
167                    }
168                }
169
170                if (commandStr.empty())
171                {
172                    parseError("No command string given.", false);
173                    continue;
174                }
175
176                // evaluate the command
177                const CommandEvaluation& eval = CommandExecutor::evaluate(commandStr);
178                if (!eval.isValid())
179                {
180                    parseError("Command evaluation of \"" + commandStr + "\"failed.", true);
181                    continue;
182                }
183
184                // check for param command
185                int paramIndex = eval.getConsoleCommand()->getInputConfiguredParam_();
186                if (paramIndex >= 0)
187                {
188                    // parameter supported command
189                    ParamCommand* cmd = new ParamCommand();
190                    cmd->scale_ = paramModifier;
191
192                    // add command to the buffer if not yet existing
193                    for (unsigned int iParamCmd = 0; iParamCmd < paramCommandBuffer_->size(); iParamCmd++)
194                    {
195                        if ((*paramCommandBuffer_)[iParamCmd]->evaluation_.getConsoleCommand()
196                            == eval.getConsoleCommand())
197                        {
198                            // already in list
199                            cmd->paramCommand_ = (*paramCommandBuffer_)[iParamCmd];
200                            break;
201                        }
202                    }
203                    if (cmd->paramCommand_ == 0)
204                    {
205                        cmd->paramCommand_ = new BufferedParamCommand();
206                        paramCommandBuffer_->push_back(cmd->paramCommand_);
207                        cmd->paramCommand_->evaluation_ = eval;
208                        cmd->paramCommand_->paramIndex_ = paramIndex;
209                    }
210
211
212                    // we don't know whether this is an actual axis or just a button
213                    if (mode == KeybindMode::None)
214                    {
215                        if (!addParamCommand(cmd))
216                        {
217                            mode = eval.getConsoleCommand()->getKeybindMode();
218                            commands[mode].push_back(cmd);
219                        }
220                    }
221                }
222                else
223                {
224                    SimpleCommand* cmd = new SimpleCommand();
225                    cmd->evaluation_ = eval;
226
227                    if (mode == KeybindMode::None)
228                        mode = eval.getConsoleCommand()->getKeybindMode();
229
230                    commands[mode].push_back(cmd);
231                }
232            }
233        }
234
235        for (unsigned int j = 0; j < 3; j++)
236        {
237            nCommands_[j] = commands[j].size();
238            if (nCommands_[j])
239            {
240                commands_[j] = new BaseCommand*[nCommands_[j]];
241                for (unsigned int i = 0; i < commands[j].size(); i++)
242                    commands_[j][i] = commands[j][i];
243            }
244            else
245                commands_[j] = 0;
246        }
247    }
248
249    inline void Button::parseError(const std::string& message, bool serious)
250    {
251        if (serious)
252        {
253            COUT(2) << "Error while parsing binding for button/axis " << this->name_ << ". "
254                << message << std::endl;
255        }
256        else
257        {
258            COUT(3) << "Warning while parsing binding for button/axis " << this->name_ << ". "
259                << message << std::endl;
260        }
261    }
262}
Note: See TracBrowser for help on using the repository browser.