1 (* Copyright (C) Doom 2D: Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 // ////////////////////////////////////////////////////////////////////////// //
17 TBindArgLessCB = procedure ();
18 TBindToggleCB = procedure (arg: Integer); // -1: no arg
19 TBindStringCB = procedure (s: AnsiString);
21 PHolmesCommand = ^THolmesCommand;
22 THolmesCommand = record
24 type TType = (TArgLess, TToggle, TString);
34 // command name already taken
35 procedure execute (pr: TTextParser);
38 PHolmesBinding = ^THolmesBinding;
39 THolmesBinding = record
40 key: AnsiString; // or mouse
43 function cmdName (): AnsiString;
46 TCmdHash = specialize THashBase<AnsiString, PHolmesCommand, THashKeyStr>;
49 // ////////////////////////////////////////////////////////////////////////// //
50 function THolmesBinding.cmdName (): AnsiString;
52 pr: TTextParser = nil;
55 pr := TStrTextParser.Create(cmd);
56 if (pr.tokType = pr.TTStr)
57 then Result := pr.expectStr(false)
58 else Result := pr.expectId();
66 // ////////////////////////////////////////////////////////////////////////// //
67 // command name already taken
68 procedure THolmesCommand.execute (pr: TTextParser);
73 if not assigned(cb) then exit;
77 if (pr.tokType <> pr.TTEOF) then
79 if pr.eatId('true') or pr.eatId('yes') then a := 1
80 else if pr.eatId('false') or pr.eatId('no') then a := 0
81 else begin conwritefln('%s: invalid argument', [name]); exit; end;
82 if (pr.tokType <> pr.TTEOF) then begin conwritefln('%s: too many arguments', [name]); exit; end;
88 if (pr.tokType <> pr.TTEOF) then begin conwritefln('%s: too many arguments', [name]); exit; end;
93 if (pr.tokType <> pr.TTEOF) then
95 if (pr.tokType = pr.TTStr) then s := pr.expectStr(false) else s := pr.expectId;
96 if (pr.tokType <> pr.TTEOF) then begin conwritefln('%s: too many arguments', [name]); exit; end;
100 conwritefln('%s: string argument expected', [name]);
103 TBindStringCB(cb)(s);
110 // ////////////////////////////////////////////////////////////////////////// //
111 function hashNewCommand (): TCmdHash;
113 result := TCmdHash.Create();
117 // ////////////////////////////////////////////////////////////////////////// //
120 THBA = array of THolmesBinding;
124 cmdlist: TCmdHash = nil;
125 keybinds: THBA = nil;
127 keybindsInited: Boolean = false;
130 // ////////////////////////////////////////////////////////////////////////// //
131 function cmdNewInternal (const aname: AnsiString; const ahelp: AnsiString; const asection: AnsiString): PHolmesCommand;
133 if (cmdlist = nil) then cmdlist := hashNewCommand();
134 if not cmdlist.get(aname, result) then
136 GetMem(result, sizeof(THolmesCommand));
137 FillChar(result^, sizeof(THolmesCommand), 0);
138 result.name := aname;
139 result.help := ahelp;
140 result.section := asection;
142 result.ctype := result.TType.TArgLess;
143 cmdlist.put(aname, result);
147 result.help := ahelp;
148 result.section := asection;
153 // ////////////////////////////////////////////////////////////////////////// //
154 procedure cmdAdd (const aname: AnsiString; cb: TBindArgLessCB; const ahelp: AnsiString; const asection: AnsiString); overload;
158 if (Length(aname) = 0) or not assigned(cb) then exit;
159 cmd := cmdNewInternal(aname, ahelp, asection);
160 cmd.cb := Pointer(@cb);
161 cmd.ctype := cmd.TType.TArgLess;
165 procedure cmdAdd (const aname: AnsiString; cb: TBindToggleCB; const ahelp: AnsiString; const asection: AnsiString); overload;
169 if (Length(aname) = 0) or not assigned(cb) then exit;
170 cmd := cmdNewInternal(aname, ahelp, asection);
171 cmd.cb := Pointer(@cb);
172 cmd.ctype := cmd.TType.TToggle;
176 procedure cmdAdd (const aname: AnsiString; cb: TBindStringCB; const ahelp: AnsiString; const asection: AnsiString); overload;
180 if (Length(aname) = 0) or not assigned(cb) then exit;
181 cmd := cmdNewInternal(aname, ahelp, asection);
182 cmd.cb := Pointer(@cb);
183 cmd.ctype := cmd.TType.TString;
187 // ////////////////////////////////////////////////////////////////////////// //
188 function getCommandHelp (const aname: AnsiString): AnsiString;
190 cmd: PHolmesCommand = nil;
192 if not cmdlist.get(aname, cmd) then result := '' else result := cmd.help;
196 function getCommandSection (const aname: AnsiString): AnsiString;
198 cmd: PHolmesCommand = nil;
200 if not cmdlist.get(aname, cmd) then result := '' else result := cmd.section;
204 // ////////////////////////////////////////////////////////////////////////// //
205 function internalBindAdd (ba: PHBA; const akey: AnsiString; const acmd: AnsiString): Boolean;
209 for f := 0 to High(ba^) do
211 if (CompareText(ba^[f].key, akey) = 0) then
213 if (Length(acmd) = 0) then
217 for c := f+1 to High(ba^) do ba^[c-1] := ba^[c];
218 SetLength(ba^, Length(ba^)-1);
229 if (Length(acmd) > 0) then
232 SetLength(ba^, Length(ba^)+1);
233 ba^[High(ba^)].key := akey;
234 ba^[High(ba^)].cmd := acmd;
243 procedure keybindAdd (const akey: AnsiString; const acmd: AnsiString);
245 internalBindAdd(@keybinds, akey, acmd);
246 keybindsInited := true;
249 procedure msbindAdd (const akey: AnsiString; const acmd: AnsiString);
251 internalBindAdd(@msbinds, akey, acmd);
252 keybindsInited := true;
256 procedure execCommand (const s: AnsiString);
258 pr: TTextParser = nil;
262 if (cmdlist = nil) then
264 conwriteln('holmes command system is not initialized!');
268 pr := TStrTextParser.Create(s);
269 if (pr.tokType = pr.TTStr) then cmd := pr.expectStr(false) else cmd := pr.expectId();
270 if cmdlist.get(cmd, cc) then cc.execute(pr) else conwritefln('holmes command ''%s'' not found', [cmd]);
274 conwritefln('error executing holmes command: [%s]', [s]);
275 //conwritefln('* [%s] [%s]', [Integer(pr.tokType), E.message]);
282 function keybindExecute (var ev: TFUIEvent): Boolean;
287 for f := 0 to High(keybinds) do
289 if (ev = keybinds[f].key) then
292 //conwritefln('found command [%s] for keybind <%s>', [keybinds[f].cmd, keybinds[f].key]);
293 execCommand(keybinds[f].cmd);
300 function msbindExecute (var ev: TFUIEvent): Boolean;
305 for f := 0 to High(msbinds) do
307 if (ev = msbinds[f].key) then
310 execCommand(msbinds[f].cmd);