Mega Code Archive

 
Categories / Delphi / Examples
 

Console Component

Title: Console Component Question: To Create Console, like dos prompt Answer: unit Console; interface uses Windows, Classes, StdCtrls, Controls, Graphics, Messages, Math, ClipBrd; const // Constants defining the default look of the console CONSOLE_DEFAULT_BACKGROUND = clBlack; CONSOLE_DEFAULT_FOREGROUND = clWhite; CONSOLE_DEFAULT_FONTNAME = 'Courier New'; CONSOLE_DEFAULT_FONTSIZE = 10; CONSOLE_DEFAULT_FONTSTYLE = [fsBold]; type // Forward TCustomConsole = class; // Events TBootEvent = procedure(Sender: TCustomConsole; var ABootFinished: boolean) of object; TShutDownEvent = procedure(Sender: TCustomConsole) of object; TCommandExecuteEvent = procedure(Sender: TCustomConsole; ACommand: string; var ACommandFinished: boolean) of object; TGetPromptEvent = procedure(Sender: TCustomConsole; var APrompt: string; var ADefaultText: string; var ADefaultCaretPos: Integer) of object; TCommandKeyPressEvent = procedure(Sender: TCustomConsole; var AKey: Char; var ATerminateCommand: boolean) of object; TPromptKeyPressEvent = procedure(Sender: TCustomConsole; var AKey: Char) of object; // TConsoleCaretType TConsoleCaretType = (ctVerticalLine, ctHorizontalLine, ctHalfBlock, ctBlock); // TCustomConsoleLine: Represents a single line PConsoleLine = ^TCustomConsoleLine; TCustomConsoleLine = record IsPromptLine: boolean; // True, if the line is a prompt line Prompt: string; // Contains the prompt string of the line, if IsPromptLine is True Text: string; // If IsPromptLine is True, Text contains the typed command string, else it containes the whole line string end; // TConsoleLines TConsoleLines = class(TObject) private fOwner: TCustomConsole; fLines: TList; function GetLines(Index: Integer): PConsoleLine; procedure SetLines(Index: Integer; const Value: PConsoleLine); function GetCount: Integer; function GetCurrLine: PConsoleLine; procedure SetCurrLine(const Value: PConsoleLine); function GetWrappedLines(Index: Integer): string; function GetWrappedLineCount: Integer; function GetWrapWidth: Integer; protected function AddLine(Force: boolean = False): PConsoleLine; procedure Writeln(ALine: string); procedure Write(AText: string); public constructor Create(AOwner: TCustomConsole); destructor Destroy; override; procedure Clear; function IsEmptyLine(ALine: PConsoleLine): boolean; function GetFullLineText(ALine: Integer): string; function LogicalToWrappedLineIndex(ALine: Integer): Integer; property CurrLine: PConsoleLine read GetCurrLine write SetCurrLine; property Lines[Index: Integer]: PConsoleLine read GetLines write SetLines; default; property Count: Integer read GetCount; property WrappedLines[Index: Integer]: string read GetWrappedLines; property WrappedLineCount: Integer read GetWrappedLineCount; property WrapWidth: Integer read GetWrapWidth; end; // TCustomConsole TCustomConsole = class(TCustomControl) private fPrompt: boolean; fCaretX: Integer; fLines: TConsoleLines; fCaretOffset: TPoint; fPaintLock: Integer; fScrollBars: TScrollStyle; fMouseWheelAccumulator: integer; fInsertMode: boolean; fOverwriteCaret: TConsoleCaretType; fInsertCaret: TConsoleCaretType; FActive: boolean; FOnBoot: TBootEvent; FOnCommandExecute: TCommandExecuteEvent; FOnShutDown: TShutDownEvent; FOnGetPrompt: TGetPromptEvent; FBorderSize: Integer; FExtraLineSpacing: Integer; FAutoUseInsertMode: boolean; FOnCommandKeyPress: TCommandKeyPressEvent; FMinLeftCaret: Integer; FOnPromptKeyPress: TPromptKeyPressEvent; function GetCanPaste: Boolean; function GetFont: TFont; procedure SetCaretX(Value: Integer); procedure SetFont(const Value: TFont); procedure SetScrollBars(const Value: TScrollStyle); procedure SizeOrFontChanged(bFont: boolean); procedure UpdateScrollBars; function CaretXYPix: TPoint; function GetAcceptInput: boolean; function GetTopLine: Integer; function GetTextHeight: Integer; function RowColumnToPixels(rowcol: TPoint): TPoint; function LogicalToPhysicalPos(p: TPoint): TPoint; procedure SetInsertCaret(const Value: TConsoleCaretType); procedure SetInsertMode(const Value: boolean); procedure SetOverwriteCaret(const Value: TConsoleCaretType); function GetCharWidth: Integer; function GetLinesInWindow: Integer; procedure SetActive(const Value: boolean); function GetLines: TConsoleLines; procedure SetLines(const Value: TConsoleLines); procedure SetOnBoot(const Value: TBootEvent); procedure SetOnCommandExecute(const Value: TCommandExecuteEvent); procedure SetOnShutDown(const Value: TShutDownEvent); procedure SetOnGetPrompt(const Value: TGetPromptEvent); function GetPrompt: boolean; procedure SetPrompt(const Value: boolean); function GetCurrLine: PConsoleLine; procedure SetCurrLine(const Value: PConsoleLine); procedure SetBorderSize(const Value: Integer); procedure SetExtraLineSpacing(const Value: Integer); procedure SetAutoUseInsertMode(const Value: boolean); procedure SetOnCommandKeyPress(const Value: TCommandKeyPressEvent); procedure SetMinLeftCaret(const Value: Integer); procedure SetOnPromptKeyPress(const Value: TPromptKeyPressEvent); protected // Status Flags sfLinesChanging: boolean; // Windows Events procedure WMEraseBkgnd(var Msg: TMessage); message WM_ERASEBKGND; procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE; procedure WMHScroll(var Msg: TWMScroll); message WM_HSCROLL; procedure WMPaste(var Message: TMessage); message WM_PASTE; procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS; procedure WMMouseWheel(var Msg: TMessage); message WM_MOUSEWHEEL; procedure WMSetFocus(var Msg: TWMSetFocus); message WM_SETFOCUS; procedure WMSize(var Msg: TWMSize); message WM_SIZE; procedure WMVScroll(var Msg: TWMScroll); message WM_VSCROLL; // More methods procedure InvalidateRect(const aRect: TRect; aErase: boolean); procedure DecPaintLock; procedure IncPaintLock; procedure InitializeCaret; procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; procedure KeyDown(var Key: Word; Shift: TShiftState); override; procedure KeyPress(var Key: Char); override; procedure Loaded; override; procedure Paint; override; procedure PaintTextLines(AClip: TRect; FirstLine, CurrLine: integer); virtual; procedure InvalidateLine(Line: integer); procedure InvalidateLines(FirstLine, LastLine: integer); procedure HideCaret; procedure ShowCaret; procedure DoOnPrompt(var APrompt: string; var DefaultText: string; var DefaultCaretPos: Integer); procedure KeyCommandHandler(AKey: Char); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; // Boot methods procedure Boot; procedure Shutdown; // Paintlock methods procedure BeginUpdate; procedure EndUpdate; // Various methods procedure UpdateCaret; procedure WndProc(var Msg: TMessage); override; procedure PasteFromClipboard; // Output methods procedure BeginExternalOutput; procedure EndExternalOutput; procedure Writeln(ALine: string = ''); procedure Write(AText: string; AUpdateMinLeftCaret: boolean = False); procedure Clear; // Active: To work with TCustomConsole, Active must be set to True property Active: boolean read FActive write SetActive; // AcceptInput: If False, key events are not regognized property AcceptInput: boolean read GetAcceptInput; // Automatically changes to Insert mode when a new prompt is activated property AutoUseInsertMode: boolean read FAutoUseInsertMode write SetAutoUseInsertMode; // CharWidth: Returns the width of a single char property CharWidth: Integer read GetCharWidth; // CaretX: CaretX stores the caret position in the current line property CaretX: Integer read fCaretX write SetCaretX; // Font: Wrapped to Canvas.Font property Font: TFont read GetFont write SetFont; // Lines: Provides access to the lines property Lines: TConsoleLines read GetLines write SetLines; // InsertMode: Switch between Insert and Overwrite mode property InsertMode: boolean read FInsertMode write SetInsertMode; // Insert Caret + Overwrite Caret property InsertCaret: TConsoleCaretType read FInsertCaret write SetInsertCaret; property OverwriteCaret: TConsoleCaretType read FOverwriteCaret write SetOverwriteCaret; // PaintLock: PaintLock Counter; Can be modified by BeginUpdate and EndUpdate property PaintLock: Integer read fPaintLock; // CurrLine: Easy access to the current line property CurrLine: PConsoleLine read GetCurrLine write SetCurrLine; // TextHeight: Returns the high of a single text line property TextHeight: Integer read GetTextHeight; // TopLine: Stores the index of the first line visible property TopLine: Integer read GetTopLine; // LinesInWindow: Returns the number of visible lines property LinesInWindow: Integer read GetLinesInWindow; // Prompt: Returns True, if currently in prompting mode property Prompt: boolean read GetPrompt write SetPrompt; // BorderSize: Specifies the border size in pixels property BorderSize: Integer read FBorderSize write SetBorderSize; // ExtraLineSpacing: Makes it possible to add change the space between the single lines property ExtraLineSpacing: Integer read FExtraLineSpacing write SetExtraLineSpacing; // MinLeftCaret: Only available in command mode property MinLeftCaret: Integer read FMinLeftCaret write SetMinLeftCaret; // Events property OnBoot: TBootEvent read FOnBoot write SetOnBoot; property OnShutDown: TShutDownEvent read FOnShutDown write SetOnShutDown; property OnCommandExecute: TCommandExecuteEvent read FOnCommandExecute write SetOnCommandExecute; property OnGetPrompt: TGetPromptEvent read FOnGetPrompt write SetOnGetPrompt; property OnCommandKeyPress: TCommandKeyPressEvent read FOnCommandKeyPress write SetOnCommandKeyPress; property OnPromptKeyPress: TPromptKeyPressEvent read FOnPromptKeyPress write SetOnPromptKeyPress; end; TConsole = class(TCustomConsole) published // TCustomConsole properties and events property AutoUseInsertMode; property InsertMode; property InsertCaret; property OverwriteCaret; property BorderSize; property ExtraLineSpacing; property OnBoot; property OnShutDown; property OnCommandExecute; property OnGetPrompt; property OnCommandKeyPress; property OnPromptKeyPress; // inherited properties property Align; property Anchors; property Constraints; property Color; property Ctl3D; property ParentCtl3D; property Enabled; property Font; property Height; property Name; property ParentColor; property ParentFont; property ParentShowHint; property PopupMenu; property ShowHint; property TabOrder; property TabStop default True; property Visible; property Width; // inherited events property OnClick; property OnDblClick; property OnDragDrop; property OnDragOver; property OnEndDock; property OnStartDock; property OnEndDrag; property OnEnter; property OnExit; property OnKeyDown; property OnKeyPress; property OnKeyUp; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnStartDrag; end; TCommandParser = class private FParamList: TStringList; FCommand: string; function GetParameters(Index: Integer): string; function GetParamCount: Integer; public constructor Create(ACommand: string); overload; constructor Create; overload; destructor Destroy; override; procedure ParseCommand(ACommand: string); property Command: string read FCommand; property Parameters[Index: Integer]: string read GetParameters; property ParamCount: Integer read GetParamCount; end; procedure Register; implementation procedure Register; Begin RegisterComponents('elsdoerfer.net', [TConsole]); end; { TCustomConsole } // IncPaintLock: prevents TCustomConsole from painting itself until EndUpdate is called procedure TCustomConsole.BeginUpdate; begin IncPaintLock; end; // CaretXYPix: Returns the caret position in pixels function TCustomConsole.CaretXYPix: TPoint; var p: TPoint; begin // If CurrLine is nil, then exit if CurrLine = nil then exit; // Y Caret position is always the last (logical) line. However, this line may // be wrapped. In this case it's possible that the carets phyiscal position is // above the last line. p.Y := fLines.WrappedLineCount - ( ((Length(CurrLine.Prompt + CurrLine.Text)) div (fLines.WrapWidth)) - ((fCaretX + Length(CurrLine.Prompt) - 1) div (fLines.WrapWidth)) ); // X Caret position is definied by fCaretX. Again, the line may be wrapped and // the caret phyiscal position differs from it's logical one. p.X := (fCaretX + Length(CurrLine.Prompt)) mod (fLines.WrapWidth); if p.X = 0 then p.X := fLines.WrapWidth; // Convert value to pixel and return Result := RowColumnToPixels(p); end; // Clear: Delete all lines procedure TCustomConsole.Clear; begin fLines.Clear; Invalidate; end; // Create: Constructor constructor TCustomConsole.Create(AOwner: TComponent); begin // Inherited inherited; ControlStyle := ControlStyle + [csOpaque]; // Defaults settings fActive := False; fPrompt := False; fInsertMode := True; fBorderSize := 3; fExtraLineSpacing := 0; FAutoUseInsertMode := True; // Font Font.Name := CONSOLE_DEFAULT_FONTNAME; Font.Style := CONSOLE_DEFAULT_FONTSTYLE; Font.Size := CONSOLE_DEFAULT_FONTSIZE; Font.Color := CONSOLE_DEFAULT_FOREGROUND; // Background Color Color := CONSOLE_DEFAULT_BACKGROUND; // Default carets InsertCaret := ctHorizontalLine; OverwriteCaret := ctHalfBlock; // Create Lines Object fLines := TConsoleLines.Create(Self); end; // DecPaintLock: Decrements the paint lock counter procedure TCustomConsole.DecPaintLock; begin Dec(fPaintLock); if fPaintLock = 0 then Invalidate; end; // Destroy: Destructor destructor TCustomConsole.Destroy; begin // Free Lines Object fLines.Free; // Inherited inherited; end; // LogicalToPhysicalPos: Takes a logical caret position (based on line index 0) // and makes a physical caret position out of it (based on first visible line) function TCustomConsole.LogicalToPhysicalPos(p: TPoint): TPoint; var s: string; i, L: integer; x: integer; begin if p.Y - 1 s := fLines.WrappedLines[p.Y - 1]; l := Length(s); x := 0; for i := 1 to p.x - 1 do begin inc(x); end; p.x := x + 1; end; Result := p; end; // RowColumnToPixels: Calculates the pixels for a certain caret position function TCustomConsole.RowColumnToPixels(RowCol: TPoint): TPoint; var P: TPoint; i: integer; lText: string; begin P := LogicalToPhysicalPos(RowCol); Result.X := BorderSize; if ((RowCol.Y - 1) for i := 1 to (P.X - 1) do Begin lText := fLines.WrappedLines[RowCol.Y - 1]; if lText '' then Result.X := Result.X + Canvas.TextWidth(lText[1]); end; Result.Y := (RowCol.Y - TopLine) * TextHeight; end; // EndUpdate: See DecPaintLock procedure TCustomConsole.EndUpdate; begin DecPaintLock; end; // GetCanPaste: Returns True if text from the clipboard can be pasted function TCustomConsole.GetCanPaste: Boolean; begin Result := not AcceptInput and (Clipboard.HasFormat(CF_TEXT)); end; // GetFont function TCustomConsole.GetFont: TFont; begin Result := Canvas.Font end; // GetAcceptInput: Only True, if fActive is also True function TCustomConsole.GetAcceptInput: boolean; begin Result := Active; end; // GetTextHeight: Calculates the height of a line function TCustomConsole.GetTextHeight: Integer; begin Result := Canvas.TextHeight('Ay%') + ExtraLineSpacing; end; // GetTopLine: Returns the physical line displayed on the very top of the control function TCustomConsole.GetTopLine: Integer; begin Result := Max(fLines.GetWrappedLineCount - LinesInWindow + 2, 1); end; // HideCaret procedure TCustomConsole.HideCaret; begin Windows.HideCaret(Handle); end; // IncPaintLock: Increments the Paint Lock Counter procedure TCustomConsole.IncPaintLock; begin Inc(fPaintLock); end; // InitializeCaret: Creates a caret object procedure TCustomConsole.InitializeCaret; var ct: TConsoleCaretType; cw, ch: integer; begin // Caret type depends on keyboard mode if InsertMode then ct := fInsertCaret else ct := fOverwriteCaret; // Set values depending on caret type case ct of ctHorizontalLine: begin cw := CharWidth; ch := 2; fCaretOffset := Point(0, TextHeight - ExtraLineSpacing - 2); end; ctHalfBlock: begin cw := CharWidth; ch := (TextHeight - 2) div 2; fCaretOffset := Point(0, ch); end; ctBlock: begin cw := CharWidth; ch := TextHeight - 2; FCaretOffset := Point(0, 0); end; else begin // Vertical Line cw := 2; ch := TextHeight - 2; FCaretOffset := Point(0, 0); end; end; // Create Caret CreateCaret(Handle, 0, cw, ch); // Show it UpdateCaret; end; // InvalidateLine: Calculates the rect of a certain line and uses InvalidateRect // to repaint it (reduces flickering) procedure TCustomConsole.InvalidateLine(Line: integer); var rcInval: TRect; begin if Visible and (Line = TopLine) and (Line (Line begin rcInval := Rect(0, TextHeight * (Line - TopLine), ClientWidth, 0); rcInval.Bottom := rcInval.Top + TextHeight; InvalidateRect(rcInval, False); end; end; // InvalidateLines: Calculates the rect of a certain line range und uses // InvalidateRect to repaint it procedure TCustomConsole.InvalidateLines(FirstLine, LastLine: integer); var rcInval: TRect; begin if Visible and HandleAllocated then if (FirstLine = -1) and (LastLine = -1) then begin rcInval := ClientRect; InvalidateRect(rcInval, false); end else begin FirstLine := Max(FirstLine, TopLine); LastLine := Min(LastLine, TopLine + LinesInWindow); if (LastLine = FirstLine) then begin rcInval := Rect(0, TextHeight * (FirstLine - TopLine), ClientWidth, TextHeight * (LastLine - TopLine + 1)); InvalidateRect(rcInval, false); end; end; end; // InvalidateRect procedure TCustomConsole.InvalidateRect(const aRect: TRect; aErase: boolean); begin Windows.InvalidateRect(Handle, @aRect, aErase); end; // KeyDown procedure TCustomConsole.KeyDown(var Key: Word; Shift: TShiftState); begin inherited; case Key of VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_TAB, VK_END, VK_HOME, VK_INSERT: KeyCommandHandler(Chr(Key)); end; end; // KeyPress procedure TCustomConsole.KeyPress(var Key: Char); begin inherited; KeyCommandHandler(Key); end; // Loaded procedure TCustomConsole.Loaded; begin inherited; UpdateScrollBars; Invalidate; end; // Paint procedure TCustomConsole.Paint; var rcClip, rcDraw: TRect; nL1, nL2: Integer; begin // Only paint if Paint Lock Counter is 0 if fPaintLock 0 then exit; // Get the invalidated rect rcClip := Canvas.ClipRect; // Calculate Line Range nL1 := Max(TopLine + ((rcClip.Top) div TextHeight), TopLine) - 1; nL2 := Min(TopLine + ((rcClip.Bottom + TextHeight - 1) div TextHeight), fLines.WrappedLineCount) - 1; // Now paint everything while the caret is hidden HideCaret; try rcDraw := rcClip; rcDraw.Left := 0; rcDraw.Right := ClientWidth; PaintTextLines(rcDraw, nL1, nL2); finally UpdateCaret; end; end; // PaintTextLines procedure TCustomConsole.PaintTextLines(AClip: TRect; FirstLine, CurrLine: integer); var rcLine, rcToken: TRect; function ColumnToXValue(Col: integer): integer; begin Result := Pred(Col) * CharWidth; end; procedure PaintLines; var nLine: integer; sLine: string; begin // Initialize rcLine for drawing. Note that Top and Bottom are updated // inside the loop. Get only the starting point for this. rcLine := AClip; rcLine.Bottom := (FirstLine - TopLine + 1) * TextHeight; // Now loop through all the lines for nLine := FirstLine to CurrLine do begin // Assign line sLine := Lines.WrappedLines[nLine]; // Update the rcLine rect to this line rcLine.Top := rcLine.Bottom; Inc(rcLine.Bottom, TextHeight); // We will need this later to fill the non-text area rcToken := rcLine; // Paint if not Canvas.HandleAllocated then exit; Brush.Color := Color; Brush.Style := bsSolid; Canvas.FillRect(Rect(0, rcLine.Top, BorderSize, rcLine.Bottom)); Canvas.TextOut(BorderSize, rcLine.Top, sLine); Canvas.FillRect(Rect(BorderSize + Canvas.TextWidth(sLine), rcLine.Top, rcLine.Right, rcLine.Bottom)); end; end; begin // Without this painting does not work until the first time FillRect is called - no clue where the problem is Canvas.Brush.Color := Color; Canvas.FillRect(Rect(0, 0, ClientWidth, 0)); try PaintLines; finally end; // If there is anything visible below the last line, then fill this as well rcToken := AClip; rcToken.Top := (CurrLine + 1 - TopLine + 1) * TextHeight; if (rcToken.Top begin Canvas.Brush.Color := Color; Canvas.FillRect(rcToken); end; end; // SetCaretX procedure TCustomConsole.SetCaretX(Value: Integer); begin if (Value else if (Value else if (CurrLine nil) and (Value length(CurrLine.Text) + 1) then Value := length(CurrLine.Text) + 1; if Value fCaretX then Begin fCaretX := Value; UpdateCaret; end; end; // SetFont procedure TCustomConsole.SetFont(const Value: TFont); begin Canvas.Font.Assign(Value); end; procedure TCustomConsole.SetScrollBars(const Value: TScrollStyle); begin { TODO : add scrollbar support } end; procedure TCustomConsole.ShowCaret; begin Windows.ShowCaret(Handle); end; procedure TCustomConsole.SizeOrFontChanged(bFont: boolean); begin Invalidate; end; procedure TCustomConsole.UpdateCaret; var CX, CY: Integer; iClientRect: TRect; begin CX := CaretXYPix.X + FCaretOffset.X; CY := CaretXYPix.Y + FCaretOffset.Y + 1; iClientRect := GetClientRect; if (CX = iClientRect.Left) and (CX (CY = iClientRect.Top) and (CY (AcceptInput) and (fActive) then begin SetCaretPos(CX, CY); ShowCaret; end else begin HideCaret; SetCaretPos(CX, CY); end; end; procedure TCustomConsole.UpdateScrollBars; begin { TODO : add scrollbar support } end; procedure TCustomConsole.WMEraseBkgnd(var Msg: TMessage); begin Msg.Result := 1; end; procedure TCustomConsole.WMGetDlgCode(var Msg: TWMGetDlgCode); begin inherited; Msg.Result := DLGC_WANTARROWS or DLGC_WANTCHARS or DLGC_WANTTAB or DLGC_WANTALLKEYS; end; procedure TCustomConsole.WMHScroll(var Msg: TWMScroll); begin { TODO : add scrolling support } end; procedure TCustomConsole.WMKillFocus(var Msg: TWMKillFocus); begin HideCaret; Windows.DestroyCaret; end; procedure TCustomConsole.WMMouseWheel(var Msg: TMessage); begin { TODO : add scrolling support } end; procedure TCustomConsole.WMPaste(var Message: TMessage); begin if AcceptInput then PasteFromClipboard; Message.Result := ord(True); end; procedure TCustomConsole.WMSetFocus(var Msg: TWMSetFocus); begin InitializeCaret; end; procedure TCustomConsole.WMSize(var Msg: TWMSize); begin SizeOrFontChanged(False); end; procedure TCustomConsole.WMVScroll(var Msg: TWMScroll); begin { TODO : add scrolling support } end; procedure TCustomConsole.WndProc(var Msg: TMessage); begin inherited; end; procedure TCustomConsole.SetInsertCaret(const Value: TConsoleCaretType); begin FInsertCaret := Value; end; procedure TCustomConsole.SetInsertMode(const Value: boolean); begin if fInsertMode Value then Begin fInsertMode := Value; InitializeCaret; end; end; procedure TCustomConsole.SetOverwriteCaret(const Value: TConsoleCaretType); begin FOverwriteCaret := Value; end; function TCustomConsole.GetCharWidth: Integer; begin Result := Canvas.TextWidth('a'); end; function TCustomConsole.GetLinesInWindow: Integer; begin Result := ClientHeight div TextHeight; end; // Set Active: Use this to boot or to shutdown the console procedure TCustomConsole.SetActive(const Value: boolean); var BootFinished: boolean; begin if (Value fActive) then Begin case Value of True: begin // Clear current lines Clear; // Add a first, empty line fLines.AddLine; // Call onBoot Event BootFinished := True; if Assigned(FOnBoot) then FOnBoot(Self, BootFinished); if BootFinished then Prompt := True; InitializeCaret; end; False: begin if Assigned(FOnShutDown) then FOnShutDown(Self); end; end; fActive := Value; end; end; procedure TCustomConsole.PasteFromClipboard; begin end; function TCustomConsole.GetLines: TConsoleLines; begin Result := fLines; end; procedure TCustomConsole.SetLines(const Value: TConsoleLines); begin fLines := Value; end; procedure TCustomConsole.Writeln(ALine: string); begin if not Prompt then Begin fLines.Writeln(ALine); fCaretX := length(ALine) + 1; Invalidate; end; end; procedure TCustomConsole.Boot; begin Active := True; end; procedure TCustomConsole.Shutdown; begin Active := False; end; procedure TCustomConsole.SetOnBoot(const Value: TBootEvent); begin FOnBoot := Value; end; procedure TCustomConsole.SetOnCommandExecute(const Value: TCommandExecuteEvent); begin FOnCommandExecute := Value; end; procedure TCustomConsole.SetOnShutDown(const Value: TShutDownEvent); begin FOnShutDown := Value; end; procedure TCustomConsole.SetOnGetPrompt(const Value: TGetPromptEvent); begin FOnGetPrompt := Value; end; procedure TCustomConsole.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited; Windows.SetFocus(Handle); end; function TCustomConsole.GetPrompt: boolean; begin Result := fPrompt; end; procedure TCustomConsole.SetPrompt(const Value: boolean); begin if Value fPrompt then Begin fPrompt := Value; if Value = True then Begin CaretX := 1; MinLeftCaret := 1; with fLines.AddLine^ do Begin IsPromptLine := True; DoOnPrompt(Prompt, Text, fCaretX); CaretX := fCaretX; // just for valid range checking end; if AutoUseInsertMode then InsertMode := True; ShowCaret; Invalidate; end else Begin fLines.AddLine; end; // end else end; // end if end; procedure TCustomConsole.Write(AText: string; AUpdateMinLeftCaret: boolean = False); begin if not Prompt then Begin fLines.Write(AText); CaretX := length(CurrLine.Text) + 1; if (AUpdateMinLeftCaret) then MinLeftCaret := length(CurrLine.Text) + 1; end; end; function TCustomConsole.GetCurrLine: PConsoleLine; begin Result := fLines.CurrLine; end; procedure TCustomConsole.SetCurrLine(const Value: PConsoleLine); begin fLines.CurrLine := Value; Invalidate; end; { TConsoleLines } function TConsoleLines.AddLine(Force: boolean = False): PConsoleLine; begin if (IsEmptyLine(CurrLine)) and (not Force) then Result := CurrLine else Begin New(Result); Result.IsPromptLine := False; Result.Prompt := ''; Result.Text := ''; fLines.Add(Result); end; end; procedure TConsoleLines.Clear; var i: integer; begin try for i := 0 to fLines.Count -1 do Dispose(fLines[i]); finally fLines.Clear; end; end; constructor TConsoleLines.Create(AOwner: TCustomConsole); begin fLines := TList.Create; fOwner := AOwner; end; destructor TConsoleLines.Destroy; begin try Clear; finally fLines.Free; end; inherited; end; function TConsoleLines.GetCount: Integer; begin Result := fLines.Count; end; function TConsoleLines.GetFullLineText(ALine: Integer): string; begin Result := Lines[ALine].Prompt + Lines[ALine].Text; end; function TConsoleLines.GetCurrLine: PConsoleLine; begin if fLines.Count = 0 then Result := nil else Result := fLines[fLines.Count - 1]; end; function TConsoleLines.GetLines(Index: Integer): PConsoleLine; begin Result := fLines[Index]; end; procedure TConsoleLines.SetCurrLine(const Value: PConsoleLine); begin fLines[fLines.Count - 1] := Value; end; procedure TConsoleLines.SetLines(Index: Integer; const Value: PConsoleLine); begin fLines[Index] := Value; end; procedure TConsoleLines.Write(AText: string); begin CurrLine.Text := CurrLine.Text + AText; end; procedure TConsoleLines.Writeln(ALine: string); begin CurrLine.Text := CurrLine.Text + ALine; AddLine(True); end; procedure TCustomConsole.SetBorderSize(const Value: Integer); begin FBorderSize := Value; end; procedure TCustomConsole.SetExtraLineSpacing(const Value: Integer); begin FExtraLineSpacing := Value; end; procedure TCustomConsole.BeginExternalOutput; begin Prompt := False; end; procedure TCustomConsole.EndExternalOutput; begin Prompt := True; end; function TConsoleLines.IsEmptyLine(ALine: PConsoleLine): boolean; begin Result := (ALine nil) and (not ALine.IsPromptLine) and (ALine.Text = ''); end; function TConsoleLines.GetWrappedLines(Index: Integer): string; var i, j, c: integer; FullLine: string; begin c := 0; Result := ''; for i := 0 to fLines.Count - 1 do Begin Inc(c, (Length(Lines[i].Prompt + Lines[i].Text) div WrapWidth) + 1); if c Index then Begin Dec(c, (Length(Lines[i].Prompt + Lines[i].Text) div WrapWidth) + 1); FullLine := Lines[i].Prompt + Lines[i].Text; j := c; while j Delete(FullLine, 1, WrapWidth); inc(j); end; // end while Result := Copy(FullLine, 1, WrapWidth); break; end; // end if end; // end for end; function TConsoleLines.GetWrappedLineCount: Integer; var i: integer; begin Result := 0; for i := 0 to fLines.Count - 1 do Result := Result + ((Length(Lines[i].Prompt + Lines[i].Text)) div WrapWidth ) + 1; end; function TConsoleLines.GetWrapWidth: Integer; begin Result := (TCustomConsole(fOwner).ClientWidth - TCustomConsole(fOwner).BorderSize * 2) div TCustomConsole(fOwner).CharWidth; end; function TConsoleLines.LogicalToWrappedLineIndex(ALine: Integer): Integer; begin Result := 0; end; procedure TCustomConsole.DoOnPrompt(var APrompt, DefaultText: string; var DefaultCaretPos: Integer); begin if Assigned(fOnGetPrompt) then fOnGetPrompt(Self, APrompt, DefaultText, DefaultCaretPos) else Begin APrompt := ' '; end; end; procedure TCustomConsole.KeyCommandHandler(AKey: Char); var CommandFinished: boolean; ATerminate: boolean; begin // If we are not in prompt mode, filter keys by user event ATerminate := False; if not (Prompt) and (Assigned(fOnCommandKeyPress)) then fOnCommandKeyPress(Self, AKey, ATerminate) else if (Assigned(fOnPromptKeyPress)) then fOnPromptKeyPress(Self, AKey); // Case Key case Ord(AKey) of VK_RETURN: Begin if Prompt then Begin // prompt mode BeginExternalOutput; CommandFinished := True; if Assigned(fOnCommandExecute) then fOnCommandExecute(Self, fLines[fLines.Count - 2].Text, CommandFinished); if CommandFinished and fActive then EndExternalOutput; Invalidate; end; end; VK_BACK: Begin if (Prompt) or (CaretX MinLeftCaret) then Begin if (CaretX length(CurrLine.Text)) then Delete(CurrLine.Text, Length(CurrLine.Text), 1) else Delete(CurrLine.Text, CaretX - 1, 1); CaretX := CaretX - 1; Invalidate; end; end; VK_LEFT: CaretX := CaretX - 1; VK_RIGHT: CaretX := CaretX + 1; VK_END: CaretX := length(CurrLine.Text) + 1; VK_HOME: CaretX := 1; VK_INSERT: Begin InsertMode := not InsertMode; InitializeCaret; end; VK_UP, VK_DOWN, VK_TAB: Begin // Should Be Handled By An Event { TODO : todo } end; else if (AKey #0) then // Insert char Begin if (CaretX length(CurrLine.Text)) then CurrLine.Text := CurrLine.Text + AKey else if InsertMode then Insert(AKey, CurrLine.Text, CaretX) // Insert Mode else Begin // Overwrite Mode Delete(CurrLine.Text, CaretX, 1); Insert(AKey, CurrLine.Text, CaretX); end; inc(fCaretX); Invalidate; end; end; if (ATerminate) then EndExternalOutput; end; procedure TCustomConsole.SetAutoUseInsertMode(const Value: boolean); begin FAutoUseInsertMode := Value; end; procedure TCustomConsole.SetOnCommandKeyPress( const Value: TCommandKeyPressEvent); begin FOnCommandKeyPress := Value; end; { TCommandParser } constructor TCommandParser.Create(ACommand: string); begin Create; ParseCommand(ACommand); end; constructor TCommandParser.Create; begin FParamList := TStringList.Create; end; destructor TCommandParser.Destroy; begin FParamList.Free; inherited; end; function TCommandParser.GetParamCount: Integer; begin Result := FParamList.Count; end; function TCommandParser.GetParameters(Index: Integer): string; begin if Index = 0 then Result := Command else Result := FParamList[Index - 1]; end; procedure TCommandParser.ParseCommand(ACommand: string); var t: string; Apost: boolean; i: integer; procedure StoreSubString(AString: string); Begin if (AString '') then // Store string (first substring as command, the others as parameters) if (FCommand = '') then FCommand := AString else FParamList.Add(AString); end; begin // Reset FParamList.Clear; FCommand := ''; Apost := False; // Parse i := 1; while (i case ACommand[i] of ' ': if not APost then Begin StoreSubString(t); t := ''; end else t := t + ACommand[i]; '"': Apost := not Apost; else t := t + ACommand[i]; end; inc(i); end; StoreSubString(t); end; procedure TCustomConsole.SetMinLeftCaret(const Value: Integer); begin FMinLeftCaret := Value; if CaretX FMinLeftCaret then CaretX := FMinLeftCaret; end; procedure TCustomConsole.SetOnPromptKeyPress( const Value: TPromptKeyPressEvent); begin FOnPromptKeyPress := Value; end; end.