GXS.Console.pas 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.Console;
  5. (*
  6. The console is a popdown window that appears on a game for text output/input.
  7. What is different compared to the original component?
  8. 1) Can be added to any object, not just the root one
  9. 2) Has a *wide* range of built-in commands
  10. 3) TgxConsoleCommand.UnknownCommand added
  11. it is set to True, if no internal command recognized
  12. 4) Internal console help added
  13. 5) By default does not remove quotes ("), but this option can be
  14. turned on (property RemoveQuotes)
  15. 6) Command list added. All user commands are saved there
  16. 7) All previously typed commands can be accessed in a usual way (arrow up/down)
  17. 8) Commands can be auto-completed by pressing TConsoleControls.AutoCompleteCommand key,
  18. or setting AutoCompleteCommandsOnKeyPress, AutoCompleteCommandsOnEnter to True
  19. Dbl-pressing the key, defined in the TConsoleControls.AutoCompleteCommand
  20. property, gives you a list of all possible internal-external commands that
  21. start with your letters
  22. 9) Batch command execution support added
  23. 10) Short help is shown when user calls the global 'help' function
  24. Long help is shown elsewhere
  25. 11) Show command help by "/?", "-?", "--?" etc
  26. 12) Assign() added for every class
  27. TODO:
  28. [new command] Redirection with the | operator, like in any othe console (optional)
  29. [new command] File browser stuff... (this one's optional ;)
  30. Blinking cursor, "Delete" key support
  31. Allow long lines to continue on the next line
  32. May be SceneViewer should be a TControl to support the FullScreenViewer...
  33. *)
  34. interface
  35. {$I Stage.Defines.inc}
  36. uses
  37. Winapi.Windows,
  38. System.Classes,
  39. System.SysUtils,
  40. System.TypInfo,
  41. System.UITypes,
  42. System.UIConsts,
  43. FMX.Graphics,
  44. Stage.VectorTypes,
  45. GXS.PersistentClasses,
  46. Stage.Strings,
  47. Stage.Utils,
  48. GXS.Coordinates,
  49. GXS.Scene,
  50. GXS.Objects,
  51. GXS.HUDObjects,
  52. GXS.SceneViewer,
  53. GXS.BitmapFont,
  54. GXS.Context,
  55. GXS.Texture,
  56. GXS.Material,
  57. GXS.ImageUtils;
  58. const
  59. CONSOLE_MAX_COMMANDS = 120;
  60. type
  61. EGLConsoleException = class(Exception);
  62. TgxConsoleOption = (coAutoCompleteCommandsOnKeyPress,
  63. //commands are auto-completed as user types them
  64. coAutoCompleteCommandsOnEnter, //commands are auto-completed when user presses the "Enter" key
  65. coShowConsoleHelpIfUnknownCommand, //take a wild guess ;)
  66. coRemoveQuotes); //remove quotes when a command line is parsed
  67. TgxConsoleOptions = set of TgxConsoleOption;
  68. TgxCustomConsole = class;
  69. TgxConsoleCommandList = class;
  70. TgxConsoleCommand = class;
  71. (* Stores info on a command. A command is a parsed input line.
  72. Should be transformed into a class, I think...*)
  73. TgxUserInputCommand = record
  74. CommandCount: Integer;
  75. Strings: array of string;
  76. UnknownCommand: Boolean;
  77. //if user identifies a command, he must set this to "True"
  78. end;
  79. // Event called when used presses the "Enter"
  80. TgxlConsoleEvent = procedure(const ConsoleCommand: TgxConsoleCommand;
  81. const Console: TgxCustomConsole;
  82. var Command: TgxUserInputCommand) of object;
  83. TgxConsoleMatchList = set of 0..CONSOLE_MAX_COMMANDS {Byte};
  84. // A class that checks for duplicates.
  85. TgxConsoleStringList = class(TStringList)
  86. private
  87. FConsole: TgxCustomConsole;
  88. protected
  89. procedure Changed; override;
  90. function GetOwner: TPersistent; override;
  91. public
  92. function CommandExists(const Command: string): Boolean;
  93. constructor Create(const Owner: TgxCustomConsole);
  94. end;
  95. // A wrapper for a console command.
  96. TgxConsoleCommand = class(TCollectionItem)
  97. private
  98. FVisible: Boolean;
  99. FEnabled: Boolean;
  100. FSilentDisabled: Boolean;
  101. FCommandList: TgxConsoleCommandList;
  102. FCommandName: string;
  103. FShortHelp: string;
  104. FLongHelp: TStringList;
  105. FOnCommand: TgxlConsoleEvent;
  106. FOnHelp: TNotifyEvent;
  107. procedure SetCommandName(const Value: string);
  108. protected
  109. procedure ShowInvalidUseOfCommandError; virtual;
  110. procedure ShowInvalidNumberOfArgumentsError(const ShowHelpAfter: Boolean =
  111. True); virtual;
  112. procedure DoOnCommand(var UserInputCommand: TgxUserInputCommand); virtual;
  113. function GetDisplayName: string; override;
  114. public
  115. //procedures
  116. procedure ShowHelp; virtual;
  117. procedure ShowShortHelp; virtual;
  118. procedure Assign(Source: TPersistent); override;
  119. constructor Create(Collection: TCollection); override;
  120. destructor Destroy; override;
  121. published
  122. //properties
  123. property CommandName: string read FCommandName write SetCommandName;
  124. property ShortHelp: string read FShortHelp write FShortHelp;
  125. property LongHelp: TStringList read FLongHelp;
  126. property OnCommand: TgxlConsoleEvent read FOnCommand write FOnCommand;
  127. property OnHelp: TNotifyEvent read FOnHelp write FOnHelp;
  128. // Disabled commands won't execute
  129. property Enabled: Boolean read FEnabled write FEnabled default True;
  130. (* If command is disabled and user calls it, no error report will be
  131. generated if SilentDisabled is enabled *)
  132. property SilentDisabled: Boolean read FSilentDisabled write FSilentDisabled
  133. default False;
  134. (* Hidden commands won't show when user requests command list
  135. or uses auto-complete *)
  136. property Visible: Boolean read FVisible write FVisible default True;
  137. end;
  138. TgxConsoleCommandList = class(TCollection)
  139. private
  140. FConsole: TgxCustomConsole;
  141. function GetItems(const Index: Integer): TgxConsoleCommand;
  142. protected
  143. function GetOwner: TPersistent; override;
  144. public
  145. procedure SortCommands(const Ascending: Boolean = True);
  146. function CommandExists(const Command: string): Boolean;
  147. function GetCommandIndex(const Command: string): Integer;
  148. // General list stuff.
  149. function LastConsoleCommand: TgxConsoleCommand;
  150. function Add: TgxConsoleCommand; overload;
  151. // Standard stuff.
  152. constructor Create(const AOwner: TgxCustomConsole);
  153. destructor Destroy; override;
  154. property Items[const Index: Integer]: TgxConsoleCommand read GetItems;
  155. default;
  156. end;
  157. TgxConsoleControls = class(TPersistent)
  158. private
  159. FOwner: TPersistent;
  160. FNavigatePageUp: Byte;
  161. FAutoCompleteCommand: Byte;
  162. FPreviousCommand: Byte;
  163. FNextCommand: Byte;
  164. FNavigateUp: Byte;
  165. FNavigatePageDown: Byte;
  166. FNavigateDown: Byte;
  167. FDblClickDelay: Integer;
  168. protected
  169. function GetOwner: TPersistent; override;
  170. public
  171. constructor Create(AOwner: TPersistent);
  172. procedure Assign(Source: TPersistent); override;
  173. published
  174. property NavigateUp: Byte read FNavigateUp write FNavigateUp default
  175. VK_HOME;
  176. property NavigateDown: Byte read FNavigateDown write FNavigateDown default
  177. VK_END;
  178. property NavigatePageUp: Byte read FNavigatePageUp write FNavigatePageUp
  179. default VK_PRIOR;
  180. property NavigatePageDown: Byte read FNavigatePageDown write
  181. FNavigatePageDown default VK_NEXT;
  182. property NextCommand: Byte read FNextCommand write FNextCommand default
  183. VK_DOWN;
  184. property PreviousCommand: Byte read FPreviousCommand write FPreviousCommand
  185. default VK_UP;
  186. property AutoCompleteCommand: Byte read FAutoCompleteCommand write
  187. FAutoCompleteCommand default VK_CONTROL;
  188. property DblClickDelay: Integer read FDblClickDelay write FDblClickDelay
  189. default 300;
  190. end;
  191. // TgxCustomConsole
  192. TgxCustomConsole = class(TgxBaseSceneObject)
  193. private
  194. FHudSprite: TgxHudSprite;
  195. FHudText: TgxHudText;
  196. FSceneViewer: TgxSceneViewer;
  197. FInputLine: string;
  198. FStartLine: Integer;
  199. FCurrentCommand: Integer;
  200. FPreviousTickCount: Integer;
  201. FSize: Single;
  202. FColsoleLog: TStringList;
  203. FCommands: TgxConsoleCommandList;
  204. FAdditionalCommands: TgxConsoleStringList;
  205. FTypedCommands: TStringList;
  206. FControls: TgxConsoleControls;
  207. FOnCommandIssued: TgxlConsoleEvent;
  208. FOptions: TgxConsoleOptions;
  209. FHint: string;
  210. procedure SetSize(const Value: Single);
  211. procedure SetSceneViewer(const Value: TgxSceneViewer);
  212. function GetFont: TgxCustomBitmapFont;
  213. procedure SetFont(const Value: TgxCustomBitmapFont);
  214. protected
  215. procedure DoOnCommandIssued(var UserInputCommand: TgxUserInputCommand);
  216. virtual;
  217. procedure SetFontColor(const Color: TColor); virtual;
  218. function GetFontColor: TColor; virtual;
  219. procedure SetHUDSpriteColor(const Color: TColor); virtual;
  220. function GetHUDSpriteColor: TColor; virtual;
  221. function NumLines: Integer; virtual;
  222. procedure ShowConsoleHelp; virtual;
  223. procedure HandleUnknownCommand(const Command: string); virtual;
  224. // Auto Complete Command
  225. procedure AutoCompleteCommand; overload; virtual;
  226. procedure AutoCompleteCommand(var MatchCount: Integer; var
  227. AdditionalCommandsMatchList: TgxConsoleMatchList; var CommandsMatchList:
  228. TgxConsoleMatchList); overload;
  229. // Command interpreters
  230. procedure CommandIssued(var UserInputCommand: TgxUserInputCommand); virtual;
  231. procedure FixCommand(var UserInputCommand: TgxUserInputCommand); virtual;
  232. function ParseString(str, caract: string): TgxUserInputCommand; virtual;
  233. procedure ProcessInput; virtual;
  234. // Refreshes the Hud (clip lines outside the visible console).
  235. procedure RefreshHud; virtual;
  236. // Register built-in commands (onCreate)
  237. procedure RegisterBuiltInCommands; virtual;
  238. // Internal command handlers:
  239. procedure ProcessInternalCommandHelp(const ConsoleCommand:
  240. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  241. TgxUserInputCommand); virtual;
  242. procedure ProcessInternalCommandClearScreen(const ConsoleCommand:
  243. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  244. TgxUserInputCommand); virtual;
  245. procedure ProcessInternalCommandConsoleHide(const ConsoleCommand:
  246. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  247. TgxUserInputCommand); virtual;
  248. procedure ProcessInternalCommandConsoleColor(const ConsoleCommand:
  249. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  250. TgxUserInputCommand); virtual;
  251. procedure ProcessInternalCommandConsoleRename(const ConsoleCommand:
  252. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  253. TgxUserInputCommand); virtual;
  254. procedure ProcessInternalCommandConsoleClearTypedCommands(const
  255. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var
  256. Command: TgxUserInputCommand); virtual;
  257. procedure ProcessInternalCommandSystemTime(const ConsoleCommand:
  258. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  259. TgxUserInputCommand); virtual;
  260. procedure ProcessInternalCommandSystemDate(const ConsoleCommand:
  261. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  262. TgxUserInputCommand); virtual;
  263. procedure ProcessInternalCommandViewerFPS(const ConsoleCommand:
  264. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  265. TgxUserInputCommand); virtual;
  266. procedure ProcessInternalCommandViewerResetPerformanceMonitor(const
  267. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var
  268. Command: TgxUserInputCommand); virtual;
  269. procedure ProcessInternalCommandViewerVSync(const ConsoleCommand:
  270. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  271. TgxUserInputCommand); virtual;
  272. procedure ProcessInternalCommandViewerAntiAliasing(const ConsoleCommand:
  273. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  274. TgxUserInputCommand); virtual;
  275. // Internal command help handlers:
  276. procedure GetHelpInternalCommandRename(Sender: TObject); virtual;
  277. procedure Notification(AComponent: TComponent; Operation: TOperation);
  278. override;
  279. procedure SetName(const Value: TComponentName); override;
  280. public
  281. // Methods: User *must* call these methods in his code.
  282. procedure ProcessKeyPress(const c: Char); virtual;
  283. procedure ProcessKeyDown(const key: word); virtual;
  284. // Navigation through code from outside
  285. procedure NavigateUp;
  286. procedure NavigateDown;
  287. procedure NavigatePageUp;
  288. procedure NavigatePageDown;
  289. (* Refreshes the size of the hud to reflect changes on the viewer.
  290. Should be called whenever the viewer's size changes. *)
  291. procedure RefreshHudSize; virtual;
  292. // Adds a line (which is not treated as a command).
  293. procedure AddLine(const str: string);
  294. // TypedCommands are cleared and current command index is reset.
  295. procedure ClearTypedCommands;
  296. procedure ExecuteCommand(const Command: string);
  297. procedure ExecuteCommands(const Commands: TStrings);
  298. constructor Create(AOwner: TComponent); override;
  299. destructor Destroy; override;
  300. // Changes the console font color.
  301. property FontColor: TColor read GetFontColor write SetFontColor stored
  302. False;
  303. property HUDSpriteColor: TColor read GetHUDSpriteColor write
  304. SetHUDSpriteColor stored False;
  305. // Where user enters his commands.
  306. property InputLine: string read FInputLine write FInputLine;
  307. // List of commands that user typed.
  308. property TypedCommands: TStringList read FTypedCommands;
  309. // Commands have events that are called when user types a sertauin command
  310. property Commands: TgxConsoleCommandList read FCommands;
  311. (* Aditional commands can be registered to participate in command auto-completion.
  312. They can be interpreted in the global OnCommandIssued event handler. *)
  313. property AdditionalCommands: TgxConsoleStringList read FAdditionalCommands;
  314. // User controls.
  315. property Controls: TgxConsoleControls read FControls;
  316. // list of commands that user typed and console's responces.
  317. property ColsoleLog: TStringList read FColsoleLog;
  318. // Allows to change consol's height from 0 to 1.
  319. property Size: Single read FSize write SetSize;
  320. // Visual stuff.
  321. property SceneViewer: TgxSceneViewer read FSceneViewer write SetSceneViewer;
  322. property HudSprite: TgxHudSprite read FHudSprite;
  323. property HudText: TgxHudText read FHudText;
  324. property Font: TgxCustomBitmapFont read GetFont write SetFont stored False;
  325. property Options: TgxConsoleOptions read FOptions write FOptions;
  326. (* Main event of the console. Happens whenever the enter key is pressed.
  327. First the input line is compared to all registered commands, then everything
  328. is parsed into a TgxUserInputCommand record and sent to the event.
  329. Empty lines are not ignored (i.e. they also trigger events)*)
  330. property OnCommandIssued: TgxlConsoleEvent read FOnCommandIssued write
  331. FOnCommandIssued;
  332. // Standard stuff
  333. property Hint: string read FHint write FHint;
  334. property Visible default False;
  335. end;
  336. TgxConsole = class(TgxCustomConsole)
  337. published
  338. property FontColor;
  339. property HUDSpriteColor;
  340. property InputLine;
  341. property TypedCommands;
  342. property Commands;
  343. property AdditionalCommands;
  344. property Controls;
  345. property ColsoleLog;
  346. property SceneViewer;
  347. property HudSprite;
  348. property HudText;
  349. property Font;
  350. property Options;
  351. property OnCommandIssued;
  352. property Hint;
  353. property Tag;
  354. property ObjectsSorting;
  355. property Visible;
  356. property OnProgress;
  357. end;
  358. //-------------------------------------------
  359. implementation
  360. //-------------------------------------------
  361. const
  362. STR_NO_DUPLICATE_NAMES_ALLOWED = 'Duplicate names not allowed!';
  363. STR_UNRECOGNIZED_PARAMETER = 'Unrecognized parameter: ';
  364. conDefaultFontCharHeight = 15;
  365. conDefaultConsoleWidth = 400;
  366. conDefaultConsoleHeight = 100;
  367. //-------------------------------------------
  368. // TgxCustomConsole
  369. //-------------------------------------------
  370. procedure TgxCustomConsole.ProcessInternalCommandClearScreen(const
  371. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  372. TgxUserInputCommand);
  373. begin
  374. Console.FInputLine := '';
  375. Console.ColsoleLog.Clear;
  376. end;
  377. procedure TgxCustomConsole.ProcessInternalCommandConsoleHide(const
  378. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  379. TgxUserInputCommand);
  380. begin
  381. Console.Visible := False;
  382. AddLine(' - Console hidden');
  383. end;
  384. procedure TgxCustomConsole.ProcessInternalCommandConsoleColor(const
  385. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  386. TgxUserInputCommand);
  387. var
  388. NewColor: TColor;
  389. begin
  390. with Console, ConsoleCommand do
  391. begin
  392. if Command.CommandCount = 1 then
  393. AddLine(' - Current console font color is ' +
  394. ColorToString(FHudText.ModulateColor.AsWinColor))
  395. else if Command.CommandCount = 2 then
  396. begin
  397. if TryStringToColorAdvanced(Command.Strings[1], NewColor) then
  398. begin
  399. //color changed successfully
  400. SetFontColor(NewColor);
  401. AddLine(' - Current console font changed to ' +
  402. ColorToString(NewColor));
  403. end
  404. else
  405. begin
  406. //color unidentified!
  407. AddLine(' - Color unidentified!');
  408. end;
  409. end
  410. else
  411. ConsoleCommand.ShowInvalidNumberOfArgumentsError;
  412. end;
  413. end;
  414. procedure TgxCustomConsole.ProcessInternalCommandConsoleRename(const
  415. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  416. TgxUserInputCommand);
  417. var
  418. CommandIndex: Integer;
  419. begin
  420. if (Command.CommandCount <> 3) then
  421. ConsoleCommand.ShowInvalidNumberOfArgumentsError
  422. else
  423. begin
  424. CommandIndex :=
  425. ConsoleCommand.FCommandList.GetCommandIndex(Command.Strings[1]);
  426. if CommandIndex = -1 then
  427. begin
  428. AddLine(' - Could not rename command +"' + Command.Strings[1] + '" to "'
  429. +
  430. Command.Strings[2] + '": such command was not found!');
  431. ConsoleCommand.ShowHelp;
  432. end
  433. else if ConsoleCommand.FCommandList.CommandExists(Command.Strings[2]) or
  434. Console.FAdditionalCommands.CommandExists(Command.Strings[2]) then
  435. begin
  436. AddLine(' - Could not rename command +"' + Command.Strings[1] + '" to "'
  437. +
  438. Command.Strings[2] + '": command "' + Command.Strings[2] +
  439. '" already exists!');
  440. ConsoleCommand.ShowHelp;
  441. end
  442. else
  443. begin
  444. ConsoleCommand.FCommandName := Command.Strings[2];
  445. AddLine(' - Command "' + Command.Strings[1] + '" successfully renamed to "'
  446. +
  447. Command.Strings[2] + '"!');
  448. end;
  449. end;
  450. end;
  451. procedure TgxCustomConsole.ProcessInternalCommandConsoleClearTypedCommands(const
  452. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  453. TgxUserInputCommand);
  454. begin
  455. if (Command.CommandCount = 1) then
  456. Console.ClearTypedCommands
  457. else
  458. ConsoleCommand.ShowInvalidNumberOfArgumentsError;
  459. end;
  460. procedure TgxCustomConsole.ProcessInternalCommandSystemDate(const
  461. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  462. TgxUserInputCommand);
  463. begin
  464. if (Command.CommandCount = 1) then
  465. AddLine(' - Current system date is: ' + DateToStr(now))
  466. else
  467. ConsoleCommand.ShowInvalidNumberOfArgumentsError;
  468. end;
  469. procedure TgxCustomConsole.ProcessInternalCommandHelp(const ConsoleCommand:
  470. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  471. TgxUserInputCommand);
  472. var
  473. MainCommand: string;
  474. I: Integer;
  475. begin
  476. if Command.CommandCount = 1 then
  477. Console.ShowConsoleHelp
  478. else if (Command.CommandCount = 2) then
  479. begin
  480. MainCommand := LowerCase(Command.Strings[1]);
  481. if FCommands.Count <> 0 then
  482. for I := 0 to FCommands.Count - 1 do
  483. if MainCommand = LowerCase(FCommands[I].FCommandName) then
  484. begin
  485. FCommands[I].ShowHelp;
  486. Exit;
  487. end;
  488. if FAdditionalCommands.Count <> 0 then
  489. for I := 0 to FAdditionalCommands.Count - 1 do
  490. if MainCommand = LowerCase(FAdditionalCommands[I]) then
  491. begin
  492. AddLine(' - Command "' + Command.Strings[1] +
  493. '" exists, but help is unavaible,');
  494. AddLine(' - because it is an external command');
  495. Exit;
  496. end;
  497. HandleUnknownCommand(Command.Strings[1]);
  498. end
  499. else
  500. ConsoleCommand.ShowInvalidNumberOfArgumentsError;
  501. end;
  502. procedure TgxCustomConsole.ProcessInternalCommandSystemTime(const
  503. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  504. TgxUserInputCommand);
  505. begin
  506. if Command.CommandCount = 1 then
  507. AddLine(' - Current system time is: ' + TimeToStr(now))
  508. else
  509. ConsoleCommand.ShowInvalidNumberOfArgumentsError;
  510. end;
  511. procedure TgxCustomConsole.GetHelpInternalCommandRename(Sender: TObject);
  512. begin
  513. with TgxConsoleCommand(Sender) do
  514. begin
  515. Addline(' - The "' + FCommandName + '" command can rename any command');
  516. AddLine(' - Usage:');
  517. AddLine(' - ' + FCommandName + ' [old_command_name] [new_command_name]');
  518. end;
  519. end;
  520. procedure TgxCustomConsole.ProcessInternalCommandViewerFPS(const ConsoleCommand:
  521. TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  522. TgxUserInputCommand);
  523. begin
  524. if Command.CommandCount = 1 then
  525. begin
  526. if Console.FSceneViewer <> nil then
  527. AddLine(' - Current SceneViewer has ' +
  528. Console.FSceneViewer.FramesPerSecondText)
  529. else
  530. AddLine(' - ' + strErrorEx + strSceneViewerNotDefined);
  531. end
  532. else
  533. ConsoleCommand.ShowInvalidNumberOfArgumentsError;
  534. end;
  535. procedure
  536. TgxCustomConsole.ProcessInternalCommandViewerResetPerformanceMonitor(const
  537. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  538. TgxUserInputCommand);
  539. begin
  540. if Command.CommandCount = 1 then
  541. begin
  542. if Console.FSceneViewer <> nil then
  543. begin
  544. Console.FSceneViewer.ResetPerformanceMonitor;
  545. AddLine(' - ResetPerformanceMonitor for Current SceneViewer completed');
  546. end
  547. else
  548. AddLine(' - ' + strErrorEx + strSceneViewerNotDefined);
  549. end
  550. else
  551. ConsoleCommand.ShowInvalidNumberOfArgumentsError;
  552. end;
  553. procedure TgxCustomConsole.ProcessInternalCommandViewerVSync(const
  554. ConsoleCommand: TgxConsoleCommand; const Console: TgxCustomConsole; var Command:
  555. TgxUserInputCommand);
  556. const
  557. ON_OFF: array[Boolean] of string = ('Off', 'On');
  558. begin
  559. if Console.FSceneViewer <> nil then
  560. begin
  561. if Command.CommandCount = 1 then
  562. begin
  563. AddLine(' - Current SceneViewer VSync is ' +
  564. ON_OFF[Console.FSceneViewer.VSync = vsmSync]);
  565. end
  566. else if (Command.CommandCount = 2) then
  567. begin
  568. if Command.Strings[1] = ON_OFF[False] then
  569. Console.FSceneViewer.VSync := vsmNoSync
  570. else if Command.Strings[1] = ON_OFF[True] then
  571. Console.FSceneViewer.VSync := vsmSync
  572. else
  573. begin
  574. AddLine(' - ' + STR_UNRECOGNIZED_PARAMETER + Command.Strings[1]);
  575. Exit;
  576. end;
  577. AddLine(' - Current SceneViewer VSync was changed to ' +
  578. ON_OFF[Console.FSceneViewer.VSync = vsmSync]);
  579. end
  580. else
  581. HandleUnknownCommand(Command.Strings[1]);
  582. end
  583. else
  584. AddLine(' - ' + strErrorEx + strSceneViewerNotDefined);
  585. end;
  586. procedure TgxCustomConsole.ProcessInternalCommandViewerAntiAliasing(
  587. const ConsoleCommand: TgxConsoleCommand;
  588. const Console: TgxCustomConsole; var Command: TgxUserInputCommand);
  589. var
  590. Temp: Integer;
  591. begin
  592. if Console.FSceneViewer <> nil then
  593. begin
  594. if Command.CommandCount = 1 then
  595. AddLine(' - Current SceneViewer AntiAliasing = ' +
  596. GetEnumName(TypeInfo(TgxAntiAliasing),
  597. Ord(Console.FSceneViewer.Buffer.AntiAliasing)))
  598. else if (Command.CommandCount = 2) then
  599. begin
  600. Temp := GetEnumValue(TypeInfo(TgxAntiAliasing), Command.Strings[1]);
  601. if Temp = -1 then
  602. begin
  603. AddLine(' - ' + STR_UNRECOGNIZED_PARAMETER + Command.Strings[1]);
  604. end
  605. else
  606. begin
  607. Console.FSceneViewer.Buffer.AntiAliasing := TgxAntiAliasing(Temp);
  608. AddLine(' - Current SceneViewer AntiAliasing was changed to ' +
  609. GetEnumName(TypeInfo(TgxAntiAliasing),
  610. Ord(Console.FSceneViewer.Buffer.AntiAliasing)))
  611. end;
  612. end
  613. else
  614. ConsoleCommand.ShowInvalidNumberOfArgumentsError;
  615. end
  616. else
  617. AddLine(' - ' + strErrorEx + strSceneViewerNotDefined);
  618. end;
  619. function TgxCustomConsole.ParseString(str, caract: string): TgxUserInputCommand;
  620. var
  621. p1: Integer;
  622. begin
  623. Result.CommandCount := 0;
  624. while True do
  625. begin
  626. p1 := pos(caract, str);
  627. if (p1 = 0) or (p1 = -1) then
  628. break;
  629. SetLength(Result.Strings, Result.CommandCount + 1);
  630. Result.Strings[Result.CommandCount] := copy(str, 1, p1 - 1);
  631. str := copy(str, p1 + 1, Length(str));
  632. Result.CommandCount := Result.CommandCount + 1;
  633. end;
  634. if Length(str) > 0 then
  635. begin
  636. setlength(Result.Strings, Result.CommandCount + 1);
  637. Result.Strings[Result.CommandCount] := str;
  638. Result.CommandCount := Result.CommandCount + 1;
  639. end;
  640. end;
  641. procedure TgxCustomConsole.FixCommand(var UserInputCommand:
  642. TgxUserInputCommand);
  643. var
  644. nCount, I: Integer;
  645. openq: Boolean;
  646. begin
  647. for I := 0 to UserInputCommand.CommandCount - 1 do
  648. UserInputCommand.Strings[I] := trim(UserInputCommand.Strings[I]);
  649. nCount := 0;
  650. I := 0;
  651. openq := False;
  652. while nCount < UserInputCommand.CommandCount do
  653. begin
  654. if UserInputCommand.Strings[I] = '' then
  655. begin
  656. if UserInputCommand.Strings[nCount] <> '' then
  657. UserInputCommand.Strings[I] := UserInputCommand.Strings[nCount];
  658. end
  659. else if openq then
  660. UserInputCommand.Strings[I] := UserInputCommand.Strings[I] + ' ' +
  661. UserInputCommand.Strings[nCount];
  662. if (Length(UserInputCommand.Strings[I]) > 0) then
  663. begin
  664. if coRemoveQuotes in FOptions then
  665. begin
  666. if (UserInputCommand.Strings[I][1] = '"') and
  667. (UserInputCommand.Strings[I][Length(UserInputCommand.Strings[I])] =
  668. '"') then
  669. UserInputCommand.Strings[I] := copy(UserInputCommand.Strings[I], 2,
  670. Length(UserInputCommand.Strings[I]) - 2);
  671. if (UserInputCommand.Strings[I][1] = '"') and not openq then
  672. begin
  673. openq := True;
  674. UserInputCommand.Strings[I] := copy(UserInputCommand.Strings[I], 2,
  675. Length(UserInputCommand.Strings[I]));
  676. end;
  677. if (UserInputCommand.Strings[I][Length(UserInputCommand.Strings[I])] =
  678. '"') and openq then
  679. begin
  680. openq := False;
  681. UserInputCommand.Strings[I] := copy(UserInputCommand.Strings[I], 1,
  682. Length(UserInputCommand.Strings[I]) - 1);
  683. end;
  684. end;
  685. if not openq then
  686. Inc(I);
  687. end;
  688. Inc(nCount);
  689. end;
  690. if I < UserInputCommand.CommandCount then
  691. begin
  692. setLength(UserInputCommand.Strings, I);
  693. UserInputCommand.CommandCount := I;
  694. end;
  695. end;
  696. constructor TgxCustomConsole.Create(AOwner: TComponent);
  697. begin
  698. inherited;
  699. FColsoleLog := TStringList.Create;
  700. FTypedCommands := TStringList.Create;
  701. FCommands := TgxConsoleCommandList.Create(Self);
  702. FAdditionalCommands := TgxConsoleStringList.Create(Self);
  703. FControls := TgxConsoleControls.Create(Self);
  704. FHudSprite := TgxHudSprite.Create(Self);
  705. MakeSubComponent(FHudSprite, True);
  706. AddChild(FHudSprite);
  707. FHudSprite.FreeNotification(Self);
  708. with FHudSprite.Material do
  709. begin
  710. BlendingMode := bmTransparency;
  711. FrontProperties.Diffuse.Alpha := 0.5;
  712. Texture.TextureMode := tmModulate;
  713. Texture.Enabled := True;
  714. end;
  715. FHudText := TgxHudText.Create(Self);
  716. MakeSubComponent(FHudText, True);
  717. AddChild(FHUDText);
  718. FHudText.FreeNotification(Self);
  719. FHudText.Position.Y := 2;
  720. FHudText.Position.X := 3;
  721. FSize := 0.35;
  722. RegisterBuiltIncommands;
  723. SetVisible(False);
  724. SetHUDSpriteColor(TColorRec.White);
  725. SetFontColor(TColorRec.Blue);
  726. end;
  727. destructor TgxCustomConsole.Destroy;
  728. begin
  729. Controls.Destroy;
  730. FCommands.Destroy;
  731. FAdditionalCommands.Destroy;
  732. FTypedCommands.Destroy;
  733. FColsoleLog.Destroy;
  734. FreeAndNil(FHudSprite);
  735. FreeAndNil(FHudText);
  736. inherited;
  737. end;
  738. procedure TgxCustomConsole.ProcessKeyPress(const c: Char);
  739. begin
  740. if not Visible then
  741. Exit;
  742. if c = #8 then //glKey_BACK
  743. FInputLine := copy(FInputLine, 1, Length(FInputLine) - 1)
  744. else if c = #13 then //glKey_RETURN
  745. begin
  746. if coAutoCompleteCommandsOnEnter in FOptions then
  747. AutoCompleteCommand;
  748. //remmember the current entered command
  749. if (FInputLine <> '') and (FInputLine <> #13) then
  750. begin
  751. if FTypedCommands.Count = 0 then
  752. FCurrentCommand := FTypedCommands.Add(FInputLine) + 1
  753. else
  754. begin
  755. if FTypedCommands[FTypedCommands.Count - 1] <> FInputLine then
  756. FCurrentCommand := FTypedCommands.Add(FInputLine) + 1;
  757. end;
  758. end;
  759. ProcessInput;
  760. end
  761. else
  762. FInputLine := FinputLine + c;
  763. if coAutoCompleteCommandsOnKeyPress in FOptions then
  764. AutoCompleteCommand;
  765. RefreshHud;
  766. end;
  767. procedure TgxCustomConsole.ProcessKeyDown(const key: word);
  768. var
  769. MatchCount: Integer;
  770. AdditionalCommandsMatchList: TgxConsoleMatchList;
  771. CommandsMatchList: TgxConsoleMatchList;
  772. CurrentTickCount: Integer;
  773. I: Integer;
  774. begin
  775. if not Visible then
  776. Exit;
  777. if (key = FControls.NextCommand) then
  778. if FCurrentCommand <> FTypedCommands.Count then
  779. begin
  780. if FCurrentCommand <> FTypedCommands.Count - 1 then
  781. Inc(FCurrentCommand);
  782. FinputLine := FTypedCommands[FCurrentCommand];
  783. end;
  784. if (key = FControls.PreviousCommand) then
  785. if FTypedCommands.Count <> 0 then
  786. begin
  787. if FCurrentCommand <> 0 then
  788. Dec(FCurrentCommand);
  789. FinputLine := FTypedCommands[FCurrentCommand];
  790. end;
  791. if (key = FControls.AutoCompleteCommand) then
  792. begin
  793. CurrentTickCount := TThread.GetTickCount;
  794. AutoCompleteCommand(MatchCount, AdditionalCommandsMatchList,
  795. CommandsMatchList);
  796. if MatchCount = 0 then
  797. Beep;
  798. if CurrentTickCount - FPreviousTickCount < Controls.FDblClickDelay then
  799. if MatchCount > 1 then
  800. begin
  801. if CommandsMatchList <> [] then
  802. begin
  803. AddLine(' - Registered commands:');
  804. for I := 0 to CONSOLE_MAX_COMMANDS do
  805. if I in CommandsMatchList then
  806. AddLine(' - ' + FCommands[I].FCommandName);
  807. end;
  808. if AdditionalCommandsMatchList <> [] then
  809. begin
  810. AddLine(' - Additional registered commands:');
  811. for I := 0 to CONSOLE_MAX_COMMANDS do
  812. if I in AdditionalCommandsMatchList then
  813. AddLine(' - ' + FAdditionalCommands[I]);
  814. end;
  815. end;
  816. FPreviousTickCount := CurrentTickCount;
  817. end;
  818. if (key = FControls.NavigateUp) then
  819. Dec(FStartLine);
  820. if (key = FControls.NavigateDown) then
  821. Inc(FStartLine);
  822. if (key = FControls.NavigatePageUp) then
  823. Dec(FStartLine, NumLines);
  824. if key = FControls.NavigatePageDown then
  825. Inc(FStartLine, NumLines);
  826. RefreshHud;
  827. end;
  828. procedure TgxCustomConsole.RefreshHud;
  829. var
  830. outStr: string;
  831. endLine, I: Integer;
  832. begin
  833. //beware! This stuf is messy
  834. if FStartLine > FColsoleLog.Count - numlines then
  835. FStartLine := FColsoleLog.Count - numlines;
  836. if FStartLine < 0 then
  837. FStartLine := 0;
  838. endLine := FStartLine + numlines - 1;
  839. if FColsoleLog.Count < numLines then
  840. outStr := FColsoleLog.Text
  841. else
  842. begin
  843. for I := FStartLine to endLine do
  844. outStr := outStr + FColsoleLog[I] + #13;
  845. end;
  846. FHudText.Text := outStr + '> ' + FInputLine;
  847. end;
  848. function TgxCustomConsole.NumLines: Integer;
  849. begin
  850. if GetFont = nil then
  851. Result := Trunc(FHudSprite.Height / conDefaultFontCharHeight - 1.7)
  852. else
  853. Result := Trunc(FHudSprite.Height / GetFont.CharHeight - 1.7);
  854. end;
  855. procedure TgxCustomConsole.ProcessInput;
  856. var
  857. info: TgxUserInputCommand;
  858. begin
  859. //Add the current line
  860. AddLine(FInputLine);
  861. //Get everything between spaces
  862. info := ParseString(FInputLine, ' ');
  863. info.UnknownCommand := True;
  864. //Remove empty strings and " sequences
  865. FixCommand(info);
  866. //Execute the command
  867. CommandIssued(info);
  868. //Clear the current line
  869. FinputLine := '';
  870. end;
  871. procedure TgxCustomConsole.ExecuteCommands(const Commands: TStrings);
  872. var
  873. I: Integer;
  874. begin
  875. if Commands.Count = 0 then
  876. Exit;
  877. for I := 0 to Commands.Count - 1 do
  878. ExecuteCommand(Commands[I]);
  879. end;
  880. procedure TgxCustomConsole.ExecuteCommand(const Command: string);
  881. begin
  882. FInputLine := Command;
  883. ProcessInput;
  884. end;
  885. procedure TgxCustomConsole.AddLine(const str: string);
  886. begin
  887. FColsoleLog.Text := FColsoleLog.Text + str + #10;
  888. FStartLine := FColsoleLog.Count - numLines;
  889. RefreshHud;
  890. end;
  891. procedure TgxCustomConsole.CommandIssued(var UserInputCommand:
  892. TgxUserInputCommand);
  893. var
  894. MainCommand: string;
  895. I: Integer;
  896. begin
  897. if UserInputCommand.CommandCount = 0 then
  898. Exit;
  899. MainCommand := LowerCase(UserInputCommand.Strings[0]);
  900. if FCommands.Count <> 0 then
  901. for I := 0 to FCommands.Count - 1 do
  902. if MainCommand = LowerCase(FCommands[I].FCommandName) then
  903. begin
  904. //show help
  905. if UserInputCommand.CommandCount > 1 then
  906. begin
  907. //I hope I didn't forget anything ;)
  908. if (UserInputCommand.Strings[1] = '/?') or
  909. (UserInputCommand.Strings[1] = '\?') or
  910. (UserInputCommand.Strings[1] = '-?') or
  911. (UserInputCommand.Strings[1] = '--?') or
  912. (UserInputCommand.Strings[1] = '/help') or
  913. (UserInputCommand.Strings[1] = '\help') or
  914. (UserInputCommand.Strings[1] = '-help') or
  915. (UserInputCommand.Strings[1] = '--help') then
  916. FCommands[I].ShowHelp
  917. else
  918. //or execute the asosiated event
  919. FCommands[I].DoOnCommand(UserInputCommand);
  920. end
  921. else
  922. //or execute the asosiated event
  923. FCommands[I].DoOnCommand(UserInputCommand);
  924. //recognize the command
  925. UserInputCommand.UnknownCommand := False;
  926. break;
  927. end;
  928. //external command processing event
  929. DoOnCommandIssued(UserInputCommand);
  930. if UserInputCommand.UnknownCommand then
  931. HandleUnknownCommand(UserInputCommand.Strings[0]);
  932. end;
  933. procedure TgxCustomConsole.RefreshHudSize;
  934. begin
  935. if FSceneViewer <> nil then
  936. begin
  937. FHudSprite.Width := FSceneViewer.Width;
  938. FHudSprite.Height := FSceneViewer.Height * FSize;
  939. end
  940. else
  941. begin
  942. FHudSprite.Width := conDefaultConsoleWidth;
  943. FHudSprite.Height := conDefaultConsoleHeight;
  944. end;
  945. FHudSprite.Position.X := FHudSprite.Width / 2;
  946. FHudSprite.Position.Y := FHudSprite.Height / 2;
  947. RefreshHud;
  948. end;
  949. procedure TgxCustomConsole.SetFontColor(const Color: TColor);
  950. begin
  951. FHUDText.ModulateColor.AsWinColor := Color;
  952. FHUDText.Material.FrontProperties.Ambient.AsWinColor := Color;
  953. end;
  954. procedure TgxCustomConsole.ShowConsoleHelp;
  955. var
  956. I: Integer;
  957. begin
  958. if (FCommands.Count = 0) and (FAdditionalCommands.Count = 0) then
  959. AddLine(' - There are no registered commands!')
  960. else
  961. begin
  962. if FCommands.Count <> 0 then
  963. begin
  964. AddLine(' - List of registered console commands:');
  965. for I := 0 to FCommands.Count - 1 do
  966. FCommands[I].ShowShortHelp;
  967. end;
  968. if FAdditionalCommands.Count <> 0 then
  969. begin
  970. AddLine(' - List of additional console commands:');
  971. for I := 0 to FAdditionalCommands.Count - 1 do
  972. AddLine(' - ' + FAdditionalCommands[I]);
  973. end;
  974. end;
  975. end;
  976. procedure TgxCustomConsole.ClearTypedCommands;
  977. begin
  978. FTypedCommands.Clear;
  979. FCurrentCommand := 0;
  980. end;
  981. {$WARNINGS off}
  982. procedure TgxCustomConsole.AutoCompleteCommand(var MatchCount: Integer;
  983. var AdditionalCommandsMatchList: TgxConsoleMatchList;
  984. var CommandsMatchList: TgxConsoleMatchList);
  985. var
  986. I: Integer;
  987. HasEnterKey: Boolean;
  988. NewInputLine, FirstMatch: string;
  989. NewMatchCount, FirstMatchIndex: Integer;
  990. begin
  991. MatchCount := 0;
  992. AdditionalCommandsMatchList := [];
  993. CommandsMatchList := [];
  994. if FInputLine <> '' then
  995. begin
  996. //delete the last "Enter" key, if there is any
  997. if FInputLine[Length(FInputLine)] = #13 then
  998. begin
  999. Delete(FInputLine, Length(FInputLine), 1);
  1000. HasEnterKey := True;
  1001. end;
  1002. //find all the matches
  1003. if FAdditionalCommands.Count <> 0 then
  1004. for I := 0 to FAdditionalCommands.Count - 1 do
  1005. if AnsiStartsText(FInputLine, FAdditionalCommands[I]) then
  1006. begin
  1007. Inc(MatchCount);
  1008. AdditionalCommandsMatchList := AdditionalCommandsMatchList + [I];
  1009. end;
  1010. if FCommands.Count <> 0 then
  1011. for I := 0 to FCommands.Count - 1 do
  1012. if FCommands[I].FVisible then
  1013. if AnsiStartsText(FInputLine, FCommands[I].FCommandName) then
  1014. begin
  1015. Inc(MatchCount);
  1016. CommandsMatchList := CommandsMatchList + [I];
  1017. end;
  1018. //if there is only one, fill it up!
  1019. if MatchCount = 1 then
  1020. begin
  1021. if AdditionalCommandsMatchList <> [] then
  1022. for I := 0 to CONSOLE_MAX_COMMANDS do
  1023. if I in AdditionalCommandsMatchList then
  1024. begin
  1025. FInputLine := FAdditionalCommands[I];
  1026. break;
  1027. end;
  1028. if CommandsMatchList <> [] then
  1029. for I := 0 to CONSOLE_MAX_COMMANDS do
  1030. if I in CommandsMatchList then
  1031. begin
  1032. FInputLine := FCommands[I].FCommandName;
  1033. break;
  1034. end;
  1035. end
  1036. else
  1037. {//if more than 1, try to complete other letters} if MatchCount > 1 then
  1038. begin
  1039. NewInputLine := FInputLine;
  1040. //find 1st match
  1041. if AdditionalCommandsMatchList <> [] then
  1042. for I := 0 to CONSOLE_MAX_COMMANDS do
  1043. if I in AdditionalCommandsMatchList then
  1044. begin
  1045. FirstMatch := FAdditionalCommands[I];
  1046. FirstMatchIndex := I;
  1047. break;
  1048. end;
  1049. if AdditionalCommandsMatchList = [] then
  1050. for I := 0 to CONSOLE_MAX_COMMANDS do
  1051. if I in CommandsMatchList then
  1052. begin
  1053. FirstMatch := FCommands[I].FCommandName;
  1054. FirstMatchIndex := I;
  1055. break;
  1056. end;
  1057. NewMatchCount := MatchCount;
  1058. while (NewMatchCount = MatchCount) and (Length(NewInputLine) <>
  1059. Length(FirstMatch)) do
  1060. begin
  1061. NewInputLine := NewInputLine + FirstMatch[Length(NewInputLine) + 1];
  1062. NewMatchCount := 0;
  1063. if AdditionalCommandsMatchList <> [] then
  1064. for I := FirstMatchIndex to FAdditionalCommands.Count - 1 do
  1065. if AnsiStartsText(NewInputLine, FAdditionalCommands[I]) then
  1066. Inc(NewMatchCount);
  1067. if AdditionalCommandsMatchList = [] then
  1068. begin
  1069. for I := FirstMatchIndex to FCommands.Count - 1 do
  1070. if AnsiStartsText(NewInputLine, FCommands[I].FCommandName) then
  1071. Inc(NewMatchCount);
  1072. end
  1073. else if CommandsMatchList <> [] then
  1074. begin
  1075. for I := 0 to FCommands.Count - 1 do
  1076. if AnsiStartsText(NewInputLine, FCommands[I].FCommandName) then
  1077. Inc(NewMatchCount);
  1078. end;
  1079. end;
  1080. FInputLine := NewInputLine;
  1081. if NewMatchCount <> MatchCount then
  1082. Delete(FInputLine, Length(NewInputLine), 1);
  1083. end;
  1084. //Restore the #13 key
  1085. if HasEnterKey then
  1086. FInputLine := FInputLine + #13;
  1087. end;
  1088. end;
  1089. {$WARNINGS on}
  1090. procedure TgxCustomConsole.AutoCompleteCommand;
  1091. var
  1092. MatchCount: Integer;
  1093. AdditionalCommandsMatchList: TgxConsoleMatchList;
  1094. CommandsMatchList: TgxConsoleMatchList;
  1095. begin
  1096. AutoCompleteCommand(MatchCount, AdditionalCommandsMatchList,
  1097. CommandsMatchList);
  1098. end;
  1099. procedure TgxCustomConsole.RegisterBuiltInCommands;
  1100. begin
  1101. { Special commands }
  1102. with FCommands.Add do
  1103. begin
  1104. FCommandName := '?';
  1105. FShortHelp := 'displays help for a single command or all commands';
  1106. FLongHelp.Add(FShortHelp);
  1107. FOnCommand := ProcessInternalCommandHelp;
  1108. end;
  1109. with FCommands.Add do
  1110. begin
  1111. FCommandName := 'Help';
  1112. FShortHelp := 'displays help for a single command or all commands';
  1113. FLongHelp.Add(FShortHelp);
  1114. FOnCommand := ProcessInternalCommandHelp;
  1115. end;
  1116. with FCommands.Add do
  1117. begin
  1118. FCommandName := 'cls';
  1119. FShortHelp := 'clears screen';
  1120. FLongHelp.Add(FShortHelp);
  1121. FOnCommand := ProcessInternalCommandClearScreen;
  1122. end;
  1123. { Console commands }
  1124. with FCommands.Add do
  1125. begin
  1126. FCommandName := 'Console.Hide';
  1127. FShortHelp := 'hides the console';
  1128. FLongHelp.Add(FShortHelp);
  1129. FOnCommand := ProcessInternalCommandConsoleHide;
  1130. end;
  1131. with FCommands.Add do
  1132. begin
  1133. FCommandName := 'Console.Color';
  1134. FShortHelp := 'displays and allows to change the color of the console';
  1135. FLongHelp.Add(FShortHelp);
  1136. FOnCommand := ProcessInternalCommandConsoleColor;
  1137. end;
  1138. with FCommands.Add do
  1139. begin
  1140. FCommandName := 'Console.Ren';
  1141. FShortHelp := 'renames any command';
  1142. // FLongHelp.Add('') not needed here, because is has an OnHelp event
  1143. FOnCommand := ProcessInternalCommandConsoleRename;
  1144. FOnHelp := GetHelpInternalCommandRename;
  1145. end;
  1146. with FCommands.Add do
  1147. begin
  1148. FCommandName := 'Console.ClearTypedCommands';
  1149. FShortHelp := 'clears Typed Commands list';
  1150. FLongHelp.Add(FShortHelp);
  1151. FOnCommand := ProcessInternalCommandConsoleClearTypedCommands;
  1152. end;
  1153. { System commands }
  1154. with FCommands.Add do
  1155. begin
  1156. FCommandName := 'System.Time';
  1157. FShortHelp := 'displays current system time';
  1158. FLongHelp.Add(FShortHelp);
  1159. FOnCommand := ProcessInternalCommandSystemTime;
  1160. end;
  1161. with FCommands.Add do
  1162. begin
  1163. FCommandName := 'System.Date';
  1164. FShortHelp := 'displays current system date';
  1165. FLongHelp.Add(FShortHelp);
  1166. FOnCommand := ProcessInternalCommandSystemDate;
  1167. end;
  1168. { Viewer commands }
  1169. with FCommands.Add do
  1170. begin
  1171. FCommandName := 'Viewer.FPS';
  1172. FShortHelp := 'displays GLXceneViewer FPS';
  1173. FLongHelp.Add(FShortHelp);
  1174. FOnCommand := ProcessInternalCommandViewerFPS;
  1175. end;
  1176. with FCommands.Add do
  1177. begin
  1178. FCommandName := 'Viewer.ResetPerformanceMonitor';
  1179. FShortHelp := 'resets GLXceneViewer FPS monitor';
  1180. FLongHelp.Add(FShortHelp);
  1181. FOnCommand := ProcessInternalCommandViewerResetPerformanceMonitor;
  1182. end;
  1183. with FCommands.Add do
  1184. begin
  1185. FCommandName := 'Viewer.VSync';
  1186. FShortHelp := 'displays and allows to change GLXceneViewer VSync';
  1187. FLongHelp.Add(FShortHelp);
  1188. FOnCommand := ProcessInternalCommandViewerVSync;
  1189. end;
  1190. with FCommands.Add do
  1191. begin
  1192. FCommandName := 'Viewer.AntiAliasing';
  1193. FShortHelp := 'displays and allows to change GLXceneViewer AntiAliasing';
  1194. FLongHelp.Add(FShortHelp);
  1195. FOnCommand := ProcessInternalCommandViewerAntiAliasing;
  1196. end;
  1197. end;
  1198. procedure TgxCustomConsole.HandleUnknownCommand(const Command: string);
  1199. begin
  1200. AddLine(' - Command "' + Command + '" not recognized!');
  1201. if coShowConsoleHelpIfUnknownCommand in FOptions then
  1202. ShowConsoleHelp;
  1203. end;
  1204. procedure TgxCustomConsole.NavigateDown;
  1205. begin
  1206. Inc(FStartLine);
  1207. if FStartLine > FColsoleLog.Count - numlines then
  1208. FStartLine := FColsoleLog.Count - numlines;
  1209. if FStartLine < 0 then
  1210. FStartLine := 0;
  1211. end;
  1212. procedure TgxCustomConsole.NavigatePageDown;
  1213. begin
  1214. Inc(FStartLine, NumLines);
  1215. if FStartLine > FColsoleLog.Count - numlines then
  1216. FStartLine := FColsoleLog.Count - numlines;
  1217. if FStartLine < 0 then
  1218. FStartLine := 0;
  1219. end;
  1220. procedure TgxCustomConsole.NavigatePageUp;
  1221. begin
  1222. Dec(FStartLine, NumLines);
  1223. if FStartLine > FColsoleLog.Count - numlines then
  1224. FStartLine := FColsoleLog.Count - numlines;
  1225. if FStartLine < 0 then
  1226. FStartLine := 0;
  1227. end;
  1228. procedure TgxCustomConsole.NavigateUp;
  1229. begin
  1230. Dec(FStartLine);
  1231. if FStartLine > FColsoleLog.Count - numlines then
  1232. FStartLine := FColsoleLog.Count - numlines;
  1233. if FStartLine < 0 then
  1234. FStartLine := 0;
  1235. end;
  1236. function TgxCustomConsole.GetFontColor: TColor;
  1237. begin
  1238. Result := FHUDText.ModulateColor.AsWinColor;
  1239. end;
  1240. function TgxCustomConsole.GetHUDSpriteColor: TColor;
  1241. begin
  1242. if Assigned(HUDSprite.Material.MaterialLibrary)
  1243. and (HUDSprite.Material.MaterialLibrary is TgxMaterialLibrary)
  1244. and (HUDSprite.Material.LibMaterialName <> '') then
  1245. Result :=
  1246. TgxMaterialLibrary(HUDSprite.Material.MaterialLibrary).LibMaterialByName(HUDSprite.Material.LibMaterialName).Material.FrontProperties.Ambient.AsWinColor
  1247. else
  1248. Result := HUDSprite.Material.FrontProperties.Ambient.AsWinColor;
  1249. end;
  1250. procedure TgxCustomConsole.SetHUDSpriteColor(const Color: TColor);
  1251. begin
  1252. if Assigned(HUDSprite.Material.MaterialLibrary)
  1253. and (HUDSprite.Material.MaterialLibrary is TgxMaterialLibrary)
  1254. and (HUDSprite.Material.LibMaterialName <> '') then
  1255. TgxMaterialLibrary(HUDSprite.Material.MaterialLibrary).LibMaterialByName(HUDSprite.Material.LibMaterialName).Material.FrontProperties.Ambient.AsWinColor := Color
  1256. else
  1257. HUDSprite.Material.FrontProperties.Ambient.AsWinColor := Color;
  1258. end;
  1259. procedure TgxCustomConsole.SetSize(const Value: Single);
  1260. begin
  1261. if (Value <= 0) or (Value > 1) then
  1262. raise EGLConsoleException.Create('Size must be between 0 and 1!')
  1263. else
  1264. begin
  1265. FSize := Value;
  1266. RefreshHudSize;
  1267. end;
  1268. end;
  1269. procedure TgxCustomConsole.DoOnCommandIssued(var UserInputCommand:
  1270. TgxUserInputCommand);
  1271. begin
  1272. if Assigned(FOnCommandIssued) then
  1273. FOnCommandIssued(nil, Self, UserInputCommand);
  1274. end;
  1275. procedure TgxCustomConsole.Notification(AComponent: TComponent;
  1276. Operation: TOperation);
  1277. begin
  1278. inherited;
  1279. if Operation = opRemove then
  1280. begin
  1281. if AComponent = FSceneViewer then
  1282. FSceneViewer := nil;
  1283. if AComponent = FHudSprite then
  1284. FHudSprite := nil;
  1285. if AComponent = FHudText then
  1286. FHudText := nil;
  1287. end;
  1288. end;
  1289. procedure TgxCustomConsole.SetSceneViewer(
  1290. const Value: TgxSceneViewer);
  1291. begin
  1292. if FSceneViewer <> nil then
  1293. FSceneViewer.RemoveFreeNotification(Self);
  1294. FSceneViewer := Value;
  1295. if FSceneViewer <> nil then
  1296. begin
  1297. FSceneViewer.FreeNotification(Self);
  1298. RefreshHudSize;
  1299. end;
  1300. end;
  1301. function TgxCustomConsole.GetFont: TgxCustomBitmapFont;
  1302. begin
  1303. Result := FHudText.BitmapFont;
  1304. end;
  1305. procedure TgxCustomConsole.SetFont(const Value: TgxCustomBitmapFont);
  1306. begin
  1307. FHudText.BitmapFont := Value;
  1308. end;
  1309. procedure TgxCustomConsole.SetName(const Value: TComponentName);
  1310. begin
  1311. inherited;
  1312. FHudSprite.Name := Value + 'HudSprite';
  1313. FHudText.Name := Value + 'HudText';
  1314. end;
  1315. { TgxConsoleControls }
  1316. procedure TgxConsoleControls.Assign(Source: TPersistent);
  1317. begin
  1318. if Source is TgxConsoleControls then
  1319. begin
  1320. FNavigateUp := TgxConsoleControls(Source).FNavigateUp;
  1321. FNavigateDown := TgxConsoleControls(Source).FNavigateDown;
  1322. FNavigatePageUp := TgxConsoleControls(Source).FNavigatePageUp;
  1323. FNavigatePageDown := TgxConsoleControls(Source).FNavigatePageDown;
  1324. FNextCommand := TgxConsoleControls(Source).FNextCommand;
  1325. FPreviousCommand := TgxConsoleControls(Source).FPreviousCommand;
  1326. FAutoCompleteCommand := TgxConsoleControls(Source).FAutoCompleteCommand;
  1327. FDblClickDelay := TgxConsoleControls(Source).FDblClickDelay;
  1328. end;
  1329. end;
  1330. constructor TgxConsoleControls.Create(AOwner: TPersistent);
  1331. begin
  1332. FOwner := AOwner;
  1333. FNavigateUp := VK_HOME;
  1334. FNavigateDown := VK_END;
  1335. FNavigatePageUp := VK_PRIOR;
  1336. FNavigatePageDown := VK_NEXT;
  1337. FNextCommand := VK_DOWN;
  1338. FPreviousCommand := VK_UP;
  1339. FAutoCompleteCommand := VK_CONTROL;
  1340. FDblClickDelay := 300;
  1341. end;
  1342. function TgxConsoleControls.GetOwner: TPersistent;
  1343. begin
  1344. Result := FOwner;
  1345. end;
  1346. { TgxConsoleCommand }
  1347. procedure TgxConsoleCommand.Assign(Source: TPersistent);
  1348. begin
  1349. Assert(Source <> nil);
  1350. inherited;
  1351. SetCommandName(TgxConsoleCommand(Source).FCommandName);
  1352. FShortHelp := TgxConsoleCommand(Source).FShortHelp;
  1353. FLongHelp.Assign(TgxConsoleCommand(Source).FLongHelp);
  1354. FVisible := TgxConsoleCommand(Source).FVisible;
  1355. FEnabled := TgxConsoleCommand(Source).FEnabled;
  1356. FSilentDisabled := TgxConsoleCommand(Source).FSilentDisabled;
  1357. end;
  1358. constructor TgxConsoleCommand.Create(Collection: TCollection);
  1359. begin
  1360. inherited;
  1361. Assert((Collection is TgxConsoleCommandList) or (Collection = nil));
  1362. FCommandList := TgxConsoleCommandList(Collection);
  1363. FLongHelp := TStringList.Create;
  1364. FVisible := True;
  1365. FEnabled := True;
  1366. end;
  1367. destructor TgxConsoleCommand.Destroy;
  1368. begin
  1369. FLongHelp.Destroy;
  1370. inherited;
  1371. end;
  1372. procedure TgxConsoleCommand.ShowInvalidUseOfCommandError;
  1373. begin
  1374. FCommandList.FConsole.AddLine(' - Invalid use of command!');
  1375. end;
  1376. procedure TgxConsoleCommand.ShowInvalidNumberOfArgumentsError(const
  1377. ShowHelpAfter: Boolean);
  1378. begin
  1379. FCommandList.FConsole.AddLine(' - Invalid number of arguments!');
  1380. if ShowHelpAfter then
  1381. ShowHelp;
  1382. end;
  1383. procedure TgxConsoleCommand.SetCommandName(const Value: string);
  1384. begin
  1385. //the name must be unique
  1386. if FCommandList.CommandExists(Value) or
  1387. FCommandList.FConsole.FAdditionalCommands.CommandExists(Value) then
  1388. begin
  1389. raise EGLConsoleException.Create(STR_NO_DUPLICATE_NAMES_ALLOWED);
  1390. Exit;
  1391. end;
  1392. FCommandName := Value;
  1393. end;
  1394. procedure TgxConsoleCommand.ShowHelp;
  1395. var
  1396. I: Integer;
  1397. begin
  1398. if Assigned(FOnHelp) then
  1399. FOnHelp(Self)
  1400. else if FLongHelp.Count <> 0 then
  1401. for I := 0 to FLongHelp.Count - 1 do
  1402. FCommandList.FConsole.AddLine(' - ' + FLongHelp[I]);
  1403. end;
  1404. procedure TgxConsoleCommand.DoOnCommand(var UserInputCommand:
  1405. TgxUserInputCommand);
  1406. begin
  1407. Assert(Assigned(FOnCommand));
  1408. if FEnabled then
  1409. FOnCommand(Self, FCommandList.FConsole, UserInputCommand)
  1410. else
  1411. begin
  1412. if not FSilentDisabled then
  1413. FCommandList.FConsole.AddLine(' - Command "' + FCommandName +
  1414. '" has been disabled!');
  1415. end;
  1416. end;
  1417. procedure TgxConsoleCommand.ShowShortHelp;
  1418. begin
  1419. if FVisible then
  1420. FCommandList.FConsole.AddLine(' - ' + FCommandName + ' - ' + FShortHelp);
  1421. end;
  1422. function TgxConsoleCommand.GetDisplayName: string;
  1423. begin
  1424. if FCommandName = '' then
  1425. Result := inherited GetDisplayName
  1426. else
  1427. Result := FCommandName;
  1428. end;
  1429. { TgxConsoleCommandList }
  1430. function TgxConsoleCommandList.Add: TgxConsoleCommand;
  1431. begin
  1432. Result := TgxConsoleCommand(inherited Add);
  1433. end;
  1434. constructor TgxConsoleCommandList.Create(const AOwner: TgxCustomConsole);
  1435. begin
  1436. Assert(AOwner <> nil);
  1437. FConsole := TgxCustomConsole(AOwner);
  1438. inherited Create(TgxConsoleCommand);
  1439. end;
  1440. destructor TgxConsoleCommandList.Destroy;
  1441. begin
  1442. Clear;
  1443. inherited;
  1444. end;
  1445. function TgxConsoleCommandList.GetItems(const Index: Integer):
  1446. TgxConsoleCommand;
  1447. begin
  1448. Result := TgxConsoleCommand(inherited Items[Index]);
  1449. end;
  1450. function TgxConsoleCommandList.LastConsoleCommand: TgxConsoleCommand;
  1451. begin
  1452. Result := GetItems(Count - 1);
  1453. end;
  1454. procedure TgxConsoleCommandList.SortCommands(const Ascending: Boolean);
  1455. begin
  1456. Assert(False, 'Not implemented yet....');
  1457. end;
  1458. function TgxConsoleCommandList.CommandExists(const Command: string): Boolean;
  1459. var
  1460. I: Integer;
  1461. begin
  1462. Result := True;
  1463. if Count <> 0 then
  1464. for I := 0 to Count - 1 do
  1465. if GetItems(I).FCommandName = Command then
  1466. Exit;
  1467. Result := False;
  1468. end;
  1469. function TgxConsoleCommandList.GetCommandIndex(const Command: string): Integer;
  1470. begin
  1471. if Count <> 0 then
  1472. for Result := 0 to Count - 1 do
  1473. if GetItems(Result).FCommandName = Command then
  1474. Exit;
  1475. Result := -1;
  1476. end;
  1477. function TgxConsoleCommandList.GetOwner: TPersistent;
  1478. begin
  1479. Result := FConsole;
  1480. end;
  1481. { TgxConsoleStringList }
  1482. procedure TgxConsoleStringList.Changed;
  1483. begin
  1484. inherited;
  1485. //we'll just assume that user added a command and check it,
  1486. //other cases are not dealt with
  1487. if Count = 0 then
  1488. Exit;
  1489. //check if this command does not duplicate any existing
  1490. if FConsole.FCommands.CommandExists(Strings[Count - 1]) then
  1491. Delete(Count - 1);
  1492. end;
  1493. function TgxConsoleStringList.CommandExists(const Command: string): Boolean;
  1494. begin
  1495. Result := IndexOf(Command) <> -1;
  1496. end;
  1497. constructor TgxConsoleStringList.Create(const Owner: TgxCustomConsole);
  1498. begin
  1499. Assert(Owner <> nil);
  1500. Duplicates := dupError;
  1501. FConsole := Owner;
  1502. end;
  1503. function TgxConsoleStringList.GetOwner: TPersistent;
  1504. begin
  1505. Result := FConsole;
  1506. end;
  1507. initialization
  1508. RegisterClasses([TgxCustomConsole, TgxConsole, TgxConsoleStringList,
  1509. TgxConsoleCommand, TgxConsoleCommandList, TgxConsoleControls]);
  1510. end.