dlgTextEntry_Keyboard: rename to TouchTextEntry
[xcsoar.git] / src / Input / InputParser.cpp
blobb956cb5504c7a167a4f90e176f2cd3e07ed5d906
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "InputParser.hpp"
25 #include "InputConfig.hpp"
26 #include "InputKeys.hpp"
27 #include "InputLookup.hpp"
28 #include "IO/LineReader.hpp"
29 #include "Util/StringUtil.hpp"
30 #include "Util/StaticString.hpp"
31 #include "Util/EscapeBackslash.hpp"
32 #include "Util/NumberParser.hpp"
33 #include "LogFile.hpp"
35 #include <string.h>
36 #include <tchar.h>
37 #include <stdio.h>
39 static bool
40 parse_assignment(TCHAR *buffer, const TCHAR *&key, const TCHAR *&value)
42 TCHAR *separator = _tcschr(buffer, '=');
43 if (separator == NULL || separator == buffer)
44 return false;
46 *separator = _T('\0');
48 key = buffer;
49 value = separator + 1;
51 return true;
54 struct EventBuilder {
55 unsigned event_id, location;
56 StaticString<1024> mode;
57 StaticString<256> type, data, label;
59 void clear() {
60 event_id = 0;
61 location = 0;
62 mode.clear();
63 type.clear();
64 data.clear();
65 label.clear();
68 bool empty() const {
69 return mode.empty();
72 void commit(InputConfig &config, unsigned line) {
73 if (empty())
74 return;
76 TCHAR *token;
78 // For each mode
79 token = mode.first_token(_T(" "));
81 // General errors - these should be true
82 assert(location < 1024);
84 const TCHAR *new_label = NULL;
85 while (token != NULL) {
87 // All modes are valid at this point
88 int mode_id = config.MakeMode(token);
89 assert(mode_id >= 0);
91 // Make label event
92 // TODO code: Consider Reuse existing entries...
93 if (location > 0) {
94 // Only copy this once per object - save string space
95 if (!new_label) {
96 new_label = UnescapeBackslash(label);
99 config.AppendMenu(mode_id, new_label, location, event_id);
102 // Make key (Keyboard input)
103 // key - Hardware key or keyboard
104 if (type.equals(_T("key"))) {
105 // Get the int key (eg: APP1 vs 'a')
106 unsigned key = ParseKeyCode(data);
107 if (key > 0)
108 config.Key2Event[mode_id][key] = event_id;
109 else
110 LogFormat(_T("Invalid key data: %s at %u"), data.c_str(), line);
112 // Make gce (Glide Computer Event)
113 // GCE - Glide Computer Event
114 } else if (type.equals(_T("gce"))) {
115 // Get the int key (eg: APP1 vs 'a')
116 int key = InputEvents::findGCE(data);
117 if (key >= 0)
118 config.GC2Event[key] = event_id;
119 else
120 LogFormat(_T("Invalid GCE data: %s at %u"), data.c_str(), line);
122 // Make gesture (Gesture Event)
123 // Key - Key Event
124 } else if (type.equals(_T("gesture"))) {
125 // Check data for invalid characters:
126 bool valid = true;
127 for (const TCHAR* c = data; *c; c++)
128 if (*c != _T('U') &&
129 *c != _T('D') &&
130 *c != _T('R') &&
131 *c != _T('L'))
132 valid = false;
134 if (valid) {
135 // One entry per key: delete old, create new
136 config.Gesture2Event.Remove(data.c_str());
137 config.Gesture2Event.Add(data.c_str(), event_id);
138 } else
139 LogFormat(_T("Invalid gesture data: %s at %u"), data.c_str(), line);
141 // Make ne (NMEA Event)
142 // NE - NMEA Event
143 } else if (type.equals(_T("ne"))) {
144 // Get the int key (eg: APP1 vs 'a')
145 int key = InputEvents::findNE(data);
146 if (key >= 0)
147 config.N2Event[key] = event_id;
148 else
149 LogFormat(_T("Invalid GCE data: %s at %u"), data.c_str(), line);
151 // label only - no key associated (label can still be touch screen)
152 } else if (type.equals(_T("label"))) {
153 // Nothing to do here...
155 } else {
156 LogFormat(_T("Invalid type: %s at %u"), type.c_str(), line);
159 token = mode.next_token(_T(" "));
164 void
165 ParseInputFile(InputConfig &config, TLineReader &reader)
167 // TODO code - Safer sizes, strings etc - use C++ (can scanf restrict length?)
169 // Multiple modes (so large string)
170 EventBuilder current;
171 current.clear();
173 int line = 0;
175 // Read from the file
176 TCHAR *buffer;
177 while ((buffer = reader.ReadLine()) != NULL) {
178 TrimRight(buffer);
179 line++;
181 const TCHAR *key, *value;
183 // experimental: if the first line is "#CLEAR" then the whole default config is cleared
184 // and can be overwritten by file
185 if (line == 1 && StringIsEqual(buffer, _T("#CLEAR"))) {
186 config.SetDefaults();
187 } else if (buffer[0] == _T('\0')) {
188 // Check valid line? If not valid, assume next record (primative, but works ok!)
189 // General checks before continue...
190 current.commit(config, line);
192 // Clear all data.
193 current.clear();
195 } else if (StringIsEmpty(buffer) || buffer[0] == _T('#')) {
196 // Do nothing - we probably just have a comment line
197 // NOTE: Do NOT display buffer to user as it may contain an invalid stirng !
199 } else if (parse_assignment(buffer, key, value)) {
200 if (StringIsEqual(key, _T("mode"))) {
201 current.mode = value;
202 } else if (StringIsEqual(key, _T("type"))) {
203 current.type = value;
204 } else if (StringIsEqual(key, _T("data"))) {
205 current.data = value;
206 } else if (StringIsEqual(key, _T("event"))) {
207 if (_tcslen(value) < 256) {
208 TCHAR d_event[256] = _T("");
209 TCHAR d_misc[256] = _T("");
210 int ef;
212 #if defined(__BORLANDC__)
213 memset(d_event, 0, sizeof(d_event));
214 memset(d_misc, 0, sizeof(d_event));
215 if (_tcschr(value, ' ') == NULL) {
216 _tcscpy(d_event, value);
217 } else {
218 #endif
220 ef = _stscanf(value, _T("%[^ ] %[A-Za-z0-9 \\/().,]"), d_event,
221 d_misc);
223 #if defined(__BORLANDC__)
225 #endif
227 if ((ef == 1) || (ef == 2)) {
229 // TODO code: Consider reusing existing identical events
231 pt2Event event = InputEvents::findEvent(d_event);
232 if (event) {
233 TCHAR *allocated = UnescapeBackslash(d_misc);
234 current.event_id = config.AppendEvent(event, allocated,
235 current.event_id);
237 /* not freeing the string, because
238 InputConfig::AppendEvent() stores the string point
239 without duplicating it; strictly speaking, this is a
240 memory leak, but the input file is only loaded once
241 at startup, so this is acceptable; in return, we
242 don't have to duplicate the hard-coded defaults,
243 which saves some memory */
244 //free(allocated);
246 } else {
247 LogFormat(_T("Invalid event type: %s at %i"), d_event, line);
249 } else {
250 LogFormat("Invalid event type at %i", line);
253 } else if (StringIsEqual(key, _T("label"))) {
254 current.label = value;
255 } else if (StringIsEqual(key, _T("location"))) {
256 current.location = ParseUnsigned(value);
258 } else {
259 LogFormat(_T("Invalid key/value pair %s=%s at %i"), key, value, line);
261 } else {
262 LogFormat("Invalid line at %i", line);
267 current.commit(config, line);