Componentes.Terceros.jvcl/official/3.39/run/JvJanTreeView.pas
2010-01-18 16:55:50 +00:00

1505 lines
37 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvJanTreeView.PAS, released on 2002-06-15.
The Initial Developer of the Original Code is Jan Verhoeven [jan1 dott verhoeven att wxs dott nl]
Portions created by Jan Verhoeven are Copyright (C) 2002 Jan Verhoeven.
All Rights Reserved.
Contributor(s): Robert Love [rlove att slcdug dott org].
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.delphi-jedi.org
Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvJanTreeView.pas 12461 2009-08-14 17:21:33Z obones $
unit JvJanTreeView;
{$I jvcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
Windows,
ShellAPI, Messages,
SysUtils, Classes,
Graphics, Controls, Forms, Dialogs, ComCtrls, Menus;
type
TGetVarEvent = procedure(Sender: TObject; VarName: string;
var Value: Extended; var Found: Boolean) of object;
TParseErrorEvent = procedure(Sender: TObject; ParseError: Integer) of object;
const
ParserStackSize = 15;
MaxFuncNameLen = 5;
ExpLimit = 11356;
SqrLimit = 1E2466;
MaxExpLen = 4;
TotalErrors = 7;
ErrParserStack = 1;
ErrBadRange = 2;
ErrExpression = 3;
ErrOperator = 4;
ErrOpenParen = 5;
ErrOpCloseParen = 6;
ErrInvalidNum = 7;
type
ErrorRange = 0..TotalErrors;
TokenTypes = (ttPlus, ttMinus, ttTimes, ttDivide, ttExpo, ttOParen,
ttCParen, ttNum, ttFunc, ttEol, ttBad, ttErr, ttModu);
TokenRec = record
State: Byte;
case Byte of
0:
(Value: Extended);
2:
(FuncName: string[MaxFuncNameLen]);
end;
type
TStack = array [1..ParserStackSize] of TokenRec;
TStackTop = 0..ParserStackSize;
TJvMathParser = class(TComponent)
private
FInput: string;
FOnGetVar: TGetVarEvent;
FOnParseError: TParseErrorEvent;
FPosition: Word;
FParseError: Boolean;
FParseValue: Extended;
protected
CurrToken: TokenRec;
MathError: Boolean;
Stack: TStack;
StackTop: TStackTop;
TokenError: ErrorRange;
TokenLen: Word;
TokenType: TokenTypes;
function GotoState(Production: Word): Word;
function IsFunc(S: string): Boolean;
function IsVar(var Value: Extended): Boolean;
function NextToken: TokenTypes;
procedure Push(Token: TokenRec);
procedure Pop(var Token: TokenRec);
procedure Reduce(Reduction: Word);
procedure Shift(State: Word);
public
constructor Create(AOwner: TComponent); override;
procedure Parse;
property Position: Word read FPosition write FPosition;
property ParseError: Boolean read FParseError write FParseError;
property ParseValue: Extended read FParseValue write FParseValue;
published
property OnGetVar: TGetVarEvent read FOnGetVar write FOnGetVar;
property OnParseError: TParseErrorEvent read FOnParseError write FOnParseError;
property ParseString: string read FInput write FInput;
end;
TTreeKeyMappings = class(TPersistent)
private
FAddNode: TShortCut;
FInsertNode: TShortCut;
FAddChildNode: TShortCut;
FDeleteNode: TShortCut;
FDuplicateNode: TShortCut;
FEditNode: TShortCut;
FSaveTree: TShortCut;
FLoadTree: TShortCut;
FCloseTree: TShortCut;
FSaveTreeAs: TShortCut;
FFindNode: TShortCut;
procedure SetAddNode(const Value: TShortCut);
procedure SetInsertNode(const Value: TShortCut);
procedure SetDeleteNode(const Value: TShortCut);
procedure SetAddChildNode(const Value: TShortCut);
procedure SetDuplicateNode(const Value: TShortCut);
procedure SetEditNode(const Value: TShortCut);
procedure SetLoadTree(const Value: TShortCut);
procedure SetSaveTree(const Value: TShortCut);
procedure SetCloseTree(const Value: TShortCut);
procedure SetSaveTreeAs(const Value: TShortCut);
procedure SetFindNode(const Value: TShortCut);
published
property AddNode: TShortCut read FAddNode write SetAddNode;
property DeleteNode: TShortCut read FDeleteNode write SetDeleteNode;
property InsertNode: TShortCut read FInsertNode write SetInsertNode;
property AddChildNode: TShortCut read FAddChildNode write SetAddChildNode;
property DuplicateNode: TShortCut read FDuplicateNode write SetDuplicateNode;
property EditNode: TShortCut read FEditNode write SetEditNode;
property FindNode: TShortCut read FFindNode write SetFindNode;
property LoadTree: TShortCut read FLoadTree write SetLoadTree;
property SaveTree: TShortCut read FSaveTree write SetSaveTree;
property SaveTreeAs: TShortCut read FSaveTreeAs write SetSaveTreeAs;
property CloseTree: TShortCut read FCloseTree write SetCloseTree;
end;
TJvJanTreeView = class(TTreeView)
private
FParser: TJvMathParser;
FParseError: Boolean;
FKeyMappings: TTreeKeyMappings;
FKeyMappingsEnabled: Boolean;
FVarList: TStringList;
FColorFormulas: Boolean;
FFormuleColor: TColor;
FDefaultExt: string;
FFileName: TFileName;
FSearchText: string;
procedure ParseVariables;
procedure NodeDuplicate(ATree: TJvJanTreeView; FromNode, ToNode: TTreeNode);
procedure SetKeyMappings(const Value: TTreeKeyMappings);
procedure SetKeyMappingsEnabled(const Value: Boolean);
procedure SetupKeyMappings;
procedure ParserGetVar(Sender: TObject; VarName: string; var Value: Extended; var Found: Boolean);
procedure ParserParseError(Sender: TObject; ParseError: Integer);
procedure DoCustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
procedure SetColorFormulas(const Value: Boolean);
procedure SetFormuleColor(const Value: TColor);
procedure SetDefaultExt(const Value: string);
procedure SetFileName(const Value: TFileName);
protected
procedure DragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure DblClick; override;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
procedure KeyPress(var Key: Char); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure DuplicateNode;
procedure DragDrop(Source: TObject; X, Y: Integer); override;
procedure DoAddNode;
procedure DoAddChildNode;
procedure DoDeleteNode;
procedure DoInsertNode;
procedure DoEditNode;
procedure DoFindNode;
procedure DoLoadTree;
procedure DoSaveTree;
procedure DoSaveTreeAs;
procedure DoCloseTree;
procedure Recalculate;
published
property KeyMappings: TTreeKeyMappings read FKeyMappings write SetKeyMappings;
property KeyMappingsEnabled: Boolean read FKeyMappingsEnabled write SetKeyMappingsEnabled default True;
property ColorFormulas: Boolean read FColorFormulas write SetColorFormulas default True;
property FormuleColor: TColor read FFormuleColor write SetFormuleColor;
property FileName: TFileName read FFileName write SetFileName;
property DefaultExt: string read FDefaultExt write SetDefaultExt;
end;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_39/run/JvJanTreeView.pas $';
Revision: '$Revision: 12461 $';
Date: '$Date: 2009-08-14 19:21:33 +0200 (ven., 14 août 2009) $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
uses
{$IFNDEF COMPILER12_UP}
JvJCLUtils,
{$ENDIF ~COMPILER12_UP}
JvConsts, JvResources, JvTypes;
//=== { TJvJanTreeView } =====================================================
constructor TJvJanTreeView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
DragMode := dmAutomatic;
FDefaultExt := 'txt';
FKeyMappings := TTreeKeyMappings.Create;
SetupKeyMappings;
FColorFormulas := True;
FKeyMappingsEnabled := True;
FParser := TJvMathParser.Create(Self);
FParser.OnGetVar := ParserGetVar;
FParser.OnParseError := ParserParseError;
FVarList := TStringList.Create;
OnCustomDrawItem := DoCustomDrawItem;
end;
destructor TJvJanTreeView.Destroy;
begin
FParser.Free;
FKeyMappings.Free;
FVarList.Free;
inherited Destroy;
end;
procedure TJvJanTreeView.SetupKeyMappings;
begin
with FKeyMappings do
begin
AddChildNode := TextToShortCut('Ctrl+Ins');
AddNode := TextToShortCut('Ctrl+Shift+Ins');
InsertNode := TextToShortCut('Shift+Ins');
DeleteNode := TextToShortCut('Shift+Del');
DuplicateNode := TextToShortCut('Ctrl+D');
EditNode := TextToShortCut('F2');
FindNode := TextToShortCut('Ctrl+F');
LoadTree := TextToShortCut('Ctrl+O');
SaveTree := TextToShortCut('Ctrl+S');
CloseTree := TextToShortCut('Ctrl+Alt+C');
SaveTreeAs := TextToShortCut('Ctrl+Alt+S');
end;
end;
procedure TJvJanTreeView.DblClick;
var
N: TTreeNode;
S: string;
begin
if Selected <> nil then
begin
N := Selected;
S := N.Text;
if (Copy(S, 1, 7) = 'http://') or (Copy(S, 1, 7) = 'mailto:') then
ShellExecute(Handle, 'open', PChar(S), nil, nil, SW_SHOWNORMAL);
end;
if Assigned(OnDblClick) then
OnDblClick(Self);
end;
procedure TJvJanTreeView.DoAddChildNode;
var
N: TTreeNode;
begin
if Selected <> nil then
begin
N := Selected;
N := Items.AddChild(N, RsNewNode);
Selected := N;
end;
end;
procedure TJvJanTreeView.DoAddNode;
var
N: TTreeNode;
begin
Items.BeginUpdate;
N := Items.Add(Selected, RsNewNode);
Items.EndUpdate;
Selected := N;
end;
procedure TJvJanTreeView.DoDeleteNode;
begin
if Selected <> nil then
Items.Delete(Selected);
end;
procedure TJvJanTreeView.DoEditNode;
var
N: TTreeNode;
begin
if Selected <> nil then
begin
N := Selected;
N.EditText;
end;
end;
procedure TJvJanTreeView.DoInsertNode;
var
N: TTreeNode;
begin
if Selected <> nil then
begin
N := Selected;
Items.BeginUpdate;
N := Items.Insert(N, RsNewNode);
Items.EndUpdate;
Selected := N;
end;
end;
procedure TJvJanTreeView.DragDrop(Source: TObject; X, Y: Integer);
var
HitTest: THitTests;
N: TTreeNode;
begin
inherited DragDrop(Source, X, Y);
HitTest := Self.GetHitTestInfoAt(X, Y);
if htOnLabel in HitTest then
begin
N := Self.GetNodeAt(X, Y);
if Source = Self then
begin
if Selected = nil then
Exit;
Selected.MoveTo(N, naInsert);
end;
end;
if Assigned(OnDragDrop) then
OnDragDrop(Self, Source, X, Y);
end;
procedure TJvJanTreeView.DragOver(Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
inherited DragOver(Source, X, Y, State, Accept);
Accept := (Source = Self);
if Assigned(OnDragOver) then
OnDragOver(Self, Source, X, Y, State, Accept);
end;
procedure TJvJanTreeView.DuplicateNode;
var
Node, NewNode: TTreeNode;
begin
if Selected <> nil then
begin
Node := Selected;
NewNode := Items.Add(Node, Node.Text);
NodeDuplicate(Self, Node, NewNode);
end;
end;
procedure TJvJanTreeView.KeyUp(var Key: Word; Shift: TShiftState);
var
MKey: Word;
MShift: TShiftState;
function MLoadTree: Boolean;
begin
ShortCutToKey(KeyMappings.LoadTree, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MSaveTree: Boolean;
begin
ShortCutToKey(KeyMappings.SaveTree, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MSaveTreeAs: Boolean;
begin
ShortCutToKey(KeyMappings.SaveTreeAs, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MCloseTree: Boolean;
begin
ShortCutToKey(KeyMappings.CloseTree, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MAddNode: Boolean;
begin
ShortCutToKey(KeyMappings.AddNode, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MDeleteNode: Boolean;
begin
ShortCutToKey(KeyMappings.DeleteNode, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MInsertNode: Boolean;
begin
ShortCutToKey(KeyMappings.InsertNode, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MAddChildNode: Boolean;
begin
ShortCutToKey(KeyMappings.AddChildNode, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MDuplicateNode: Boolean;
begin
ShortCutToKey(KeyMappings.DuplicateNode, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MEditNode: Boolean;
begin
ShortCutToKey(KeyMappings.EditNode, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
function MFindNode: Boolean;
begin
ShortCutToKey(KeyMappings.FindNode, MKey, MShift);
Result := ((MKey = Key) and (MShift = Shift));
end;
begin
inherited KeyUp(Key, Shift);
if KeyMappingsEnabled then
begin
if MAddNode then
DoAddNode
else
if MDeleteNode then
DoDeleteNode
else
if MInsertNode then
DoInsertNode
else
if MAddChildNode then
DoAddChildNode
else
if MDuplicateNode then
DuplicateNode
else
if MEditNode then
DoEditNode
else
if MFindNode then
DoFindNode
else
if MLoadTree then
DoLoadTree
else
if MSaveTree then
DoSaveTree
else
if MSaveTreeAs then
DoSaveTreeAs
else
if MCloseTree then
DoCloseTree;
end;
if Assigned(OnKeyDown) then
OnKeyDown(Self, Key, Shift);
end;
procedure TJvJanTreeView.SetKeyMappings(const Value: TTreeKeyMappings);
begin
FKeyMappings := Value;
end;
procedure TJvJanTreeView.SetKeyMappingsEnabled(const Value: Boolean);
begin
FKeyMappingsEnabled := Value;
end;
procedure TJvJanTreeView.MouseMove(Shift: TShiftState; X, Y: Integer);
var
HitTest: THitTests;
N: TTreeNode;
S: string;
begin
HitTest := GetHitTestInfoAt(X, Y);
if htOnLabel in HitTest then
begin
N := GetNodeAt(X, Y);
S := N.Text;
if (Copy(S, 1, 7) = 'http://') or (Copy(S, 1, 7) = 'mailto:') then
Cursor := crHandPoint
else
Cursor := crDefault;
end
else
Cursor := crDefault;
if Assigned(OnMouseMove) then
OnMouseMove(Self, Shift, X, Y);
end;
procedure TJvJanTreeView.NodeDuplicate(ATree: TJvJanTreeView;
FromNode, ToNode: TTreeNode);
var
I: Integer;
begin
if FromNode.Count > 0 then
for I := 1 to FromNode.Count do
begin
ATree.Items.AddChild(ToNode, FromNode.Item[I - 1].Text);
if FromNode.Item[I - 1].Count > 0 then
NodeDuplicate(ATree, FromNode.Item[I - 1], ToNode.Item[I - 1]);
end;
end;
procedure TJvJanTreeView.ParserGetVar(Sender: TObject; VarName: string;
var Value: Extended; var Found: Boolean);
var
N: TTreeNode;
Index: Integer;
begin
Found := False;
Index := FVarList.IndexOf(VarName);
if Index <> -1 then
begin
N := TTreeNode(FVarList.Objects[Index]);
if N.Count > 0 then
try
Value := StrToFloat(N.Item[0].Text);
Found := True;
except
end;
end
else
if LowerCase(VarName) = 'pi' then
begin
Value := Pi;
Found := True;
end;
end;
procedure TJvJanTreeView.ParserParseError(Sender: TObject; ParseError: Integer);
begin
FParseError := True;
end;
procedure TJvJanTreeView.Recalculate;
var
N, NV: TTreeNode;
S: string;
I, P: Integer;
begin
if Items.Count = 0 then
Exit;
ParseVariables;
for I := 0 to Items.Count - 1 do
begin
N := Items[I];
S := N.Text;
P := Pos('=', S);
if P = 0 then
Continue;
S := Copy(S, P + 1, Length(S));
if S = '' then
Continue;
FParser.ParseString := S;
FParseError := False;
FParser.Parse;
if not FParseError then
begin
if N.Count = 0 then
Items.AddChild(N, RsNew);
NV := N.Item[0];
NV.Text := FloatToStr(FParser.ParseValue);
end
else
begin
ShowMessageFmt(RsRecalculateErr, [S]);
Exit;
end;
end;
end;
procedure TJvJanTreeView.ParseVariables;
var
I, P: Integer;
N: TTreeNode;
S: string;
begin
FVarList.Clear;
if Items.Count = 0 then
Exit;
for I := 0 to Items.Count - 1 do
begin
N := Items[I];
S := N.Text;
P := Pos('=', S);
if P = 0 then
Continue;
S := Copy(S, 1, P - 1);
if S <> '' then
FVarList.AddObject(S, TObject(N));
end;
end;
procedure TJvJanTreeView.DoCustomDrawItem(Sender: TCustomTreeView;
Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
var
S: string;
R: TRect;
begin
S := Node.Text;
if (cdsSelected in State) or (cdsFocused in State) then
begin
DefaultDraw := True;
Exit;
end;
if (Copy(S, 1, 7) = 'http://') or (Copy(S, 1, 7) = 'mailto:') then
with Canvas do
begin
R := Node.DisplayRect(True);
Font := Self.Font;
Font.Style := Font.Style + [fsUnderline];
Font.Color := clBlue;
TextRect(R, R.Left, R.Top, S);
DefaultDraw := False;
end
else
if FColorFormulas and (Pos('=', S) > 0) then
with Canvas do
begin
R := Node.DisplayRect(True);
Font := Self.Font;
Font.Color := FFormuleColor;
TextRect(R, R.Left, R.Top, S);
DefaultDraw := False;
end
else
DefaultDraw := True;
end;
procedure TJvJanTreeView.SetColorFormulas(const Value: Boolean);
begin
FColorFormulas := Value;
end;
procedure TJvJanTreeView.SetFormuleColor(const Value: TColor);
begin
FFormuleColor := Value;
end;
procedure TTreeKeyMappings.SetLoadTree(const Value: TShortCut);
begin
FLoadTree := Value;
end;
procedure TTreeKeyMappings.SetSaveTree(const Value: TShortCut);
begin
FSaveTree := Value;
end;
procedure TJvJanTreeView.DoLoadTree;
var
Dlg: TOpenDialog;
S: string;
begin
Dlg := TOpenDialog.Create(Self);
try
Dlg.DefaultExt := FDefaultExt;
S := FDefaultExt;
if S = '' then
S := '*';
Dlg.Filter := RsTreeViewFiles + '|*.' + S;
if Dlg.Execute then
begin
LoadFromFile(Dlg.FileName);
FFileName := Dlg.FileName;
Recalculate;
end;
finally
Dlg.Free;
end;
end;
procedure TJvJanTreeView.DoSaveTreeAs;
var
Dlg: TSaveDialog;
S: string;
begin
Dlg := TSaveDialog.Create(Self);
try
Dlg.DefaultExt := FDefaultExt;
S := FDefaultExt;
if S = '' then
S := '*';
Dlg.Filter := RsTreeViewFiles + '|*.' + S;
if Dlg.Execute then
begin
SaveToFile(Dlg.FileName);
FFileName := Dlg.FileName;
end;
finally
Dlg.Free;
end;
end;
procedure TJvJanTreeView.SetDefaultExt(const Value: string);
begin
FDefaultExt := Value;
end;
procedure TJvJanTreeView.SetFileName(const Value: TFileName);
begin
FFileName := Value;
end;
procedure TJvJanTreeView.DoCloseTree;
begin
if MessageDlg(RsSaveCurrentTree, mtConfirmation, [mbYes, mbNo], 0) = mrYes then
begin
if FFileName <> '' then
SaveToFile(FFileName)
else
DoSaveTreeAs;
end;
Items.BeginUpdate;
Items.Clear;
Items.EndUpdate;
FFileName := '';
end;
procedure TTreeKeyMappings.SetCloseTree(const Value: TShortCut);
begin
FCloseTree := Value;
end;
procedure TTreeKeyMappings.SetSaveTreeAs(const Value: TShortCut);
begin
FSaveTreeAs := Value;
end;
procedure TJvJanTreeView.DoSaveTree;
begin
if FFileName <> '' then
SaveToFile(FFileName)
else
DoSaveTreeAs;
end;
procedure TTreeKeyMappings.SetFindNode(const Value: TShortCut);
begin
FFindNode := Value;
end;
procedure TJvJanTreeView.DoFindNode;
var
N: TTreeNode;
I, FR: Integer;
S: string;
begin
N := Selected;
if N = nil then
Exit;
S := InputBox(RsSearch, RsSearchFor, FSearchText);
if S = '' then
Exit;
FSearchText := S;
S := LowerCase(S);
FR := N.AbsoluteIndex;
if FR < Items.Count - 1 then
for I := FR + 1 to Items.Count - 1 do
if Pos(S, LowerCase(Items[I].Text)) > 0 then
begin
Selected := Items[I];
Exit;
end;
ShowMessage(Format(RsNoMoresFound, [S]));
end;
//=== { TJvMathParser } ======================================================
constructor TJvMathParser.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
{ defaults }
FInput := '';
end;
{ Finds the new state based on the just-completed production and the
top state. }
function TJvMathParser.GotoState(Production: Word): Word;
var
State: Word;
begin
Result := 0; // removes warning
State := Stack[StackTop].State;
if Production <= 3 then
case State of
0:
GotoState := 1;
9:
GotoState := 19;
20:
GotoState := 28;
end
else
if Production <= 6 then
case State of
0, 9, 20:
GotoState := 2;
12:
GotoState := 21;
13:
GotoState := 22;
end
else
if (Production <= 8) or (Production = 100) then
case State of
0, 9, 12, 13, 20:
GotoState := 3;
14:
GotoState := 23;
15:
GotoState := 24;
16:
GotoState := 25;
40:
GotoState := 80;
end
else
if Production <= 10 then
case State of
0, 9, 12..16, 20, 40:
GotoState := 4;
end
else
if Production <= 12 then
case State of
0, 9, 12..16, 20, 40:
GotoState := 6;
5:
GotoState := 17;
end
else
case State of
0, 5, 9, 12..16, 20, 40:
GotoState := 8;
end;
end;
{ Checks to see if the parser is about to read a function }
function TJvMathParser.IsFunc(S: string): Boolean;
var
P, SLen: Word;
FuncName: string;
begin
P := Position;
FuncName := '';
while (P <= Length(FInput)) and CharInSet(FInput[P], IdentifierSymbols) do
begin
FuncName := FuncName + FInput[P];
Inc(P);
end;
if UpperCase(FuncName) = S then
begin
SLen := Length(S);
CurrToken.FuncName := {$IFDEF SUPPORTS_UNICODE}UTF8Encode{$ENDIF SUPPORTS_UNICODE}(UpperCase(Copy(FInput, Position, SLen)));
Position := Position + SLen;
IsFunc := True;
end
else
IsFunc := False;
end;
function TJvMathParser.IsVar(var Value: Extended): Boolean;
var
VarName: string;
VarFound: Boolean;
begin
VarFound := False;
VarName := '';
while (Position <= Length(FInput)) and CharInSet(FInput[Position], IdentifierSymbols) do
begin
VarName := VarName + FInput[Position];
Position := Position + 1;
end;
if Assigned(FOnGetVar) then
FOnGetVar(Self, VarName, Value, VarFound);
IsVar := VarFound;
end;
{ Gets the next Token from the Input stream }
function TJvMathParser.NextToken: TokenTypes;
var
NumString: string;
TLen, NumLen: Word;
Check: Integer;
Ch: Char;
Decimal: Boolean;
begin
NextToken := ttBad;
while (Position <= Length(FInput)) and (FInput[Position] = ' ') do
Position := Position + 1;
TokenLen := Position;
if Position > Length(FInput) then
begin
NextToken := ttEol;
TokenLen := 0;
Exit;
end;
Ch := UpCase(FInput[Position]);
if Ch = '!' then
begin
NextToken := ttErr;
TokenLen := 0;
Exit;
end;
if CharInSet(Ch, ['0'..'9', '.']) then
begin
NumString := '';
TLen := Position;
Decimal := False;
while (TLen <= Length(FInput)) and
(CharInSet(FInput[TLen], DigitSymbols) or
((FInput[TLen] = '.') and (not Decimal))) do
begin
NumString := NumString + FInput[TLen];
if Ch = '.' then
Decimal := True;
Inc(TLen);
end;
if (TLen = 2) and (Ch = '.') then
begin
NextToken := ttBad;
TokenLen := 0;
Exit;
end;
if (TLen <= Length(FInput)) and (UpCase(FInput[TLen]) = 'E') then
begin
NumString := NumString + 'E';
Inc(TLen);
if CharInSet(FInput[TLen], ['+', '-']) then
begin
NumString := NumString + FInput[TLen];
Inc(TLen);
end;
NumLen := 1;
while (TLen <= Length(FInput)) and CharInSet(FInput[TLen], DigitSymbols) and
(NumLen <= MaxExpLen) do
begin
NumString := NumString + FInput[TLen];
Inc(NumLen);
Inc(TLen);
end;
end;
if NumString[1] = '.' then
NumString := '0' + NumString;
Val(NumString, CurrToken.Value, Check);
if Check <> 0 then
begin
MathError := True;
TokenError := ErrInvalidNum;
Position := Position + Pred(Check);
end
else
begin
NextToken := ttNum;
Position := Position + System.Length(NumString);
TokenLen := Position - TokenLen;
end;
Exit;
end
else
if CharInSet(Ch, IdentifierLetters) then
begin
if IsFunc('ABS') or IsFunc('ATAN') or IsFunc('COS') or
IsFunc('EXP') or IsFunc('LN') or IsFunc('ROUND') or
IsFunc('SIN') or IsFunc('SQRT') or IsFunc('SQR') or IsFunc('TRUNC') then
begin
NextToken := ttFunc;
TokenLen := Position - TokenLen;
Exit;
end;
if IsFunc('MOD') then
begin
NextToken := ttModu;
TokenLen := Position - TokenLen;
Exit;
end;
if IsVar(CurrToken.Value) then
begin
NextToken := ttNum;
TokenLen := Position - TokenLen;
Exit;
end
else
begin
NextToken := ttBad;
TokenLen := 0;
Exit;
end;
end
else
begin
case Ch of
'+':
NextToken := ttPlus;
'-':
NextToken := ttMinus;
'*':
NextToken := ttTimes;
'/':
NextToken := ttDivide;
'^':
NextToken := ttExpo;
'(':
NextToken := ttOParen;
')':
NextToken := ttCParen;
else
begin
NextToken := ttBad;
TokenLen := 0;
Exit;
end;
end;
Position := Position + 1;
TokenLen := Position - TokenLen;
Exit;
end;
end;
{ Pops the top Token off of the stack }
procedure TJvMathParser.Pop(var Token: TokenRec);
begin
Token := Stack[StackTop];
Dec(StackTop);
end;
{ Pushes a new Token onto the stack }
procedure TJvMathParser.Push(Token: TokenRec);
begin
if StackTop = ParserStackSize then
TokenError := ErrParserStack
else
begin
Inc(StackTop);
Stack[StackTop] := Token;
end;
end;
{ Parses an input stream }
procedure TJvMathParser.Parse;
var
FirstToken: TokenRec;
Accepted: Boolean;
begin
Position := 1;
StackTop := 0;
TokenError := 0;
MathError := False;
ParseError := False;
Accepted := False;
FirstToken.State := 0;
FirstToken.Value := 0;
Push(FirstToken);
TokenType := NextToken;
repeat
case Stack[StackTop].State of
0, 9, 12..16, 20, 40:
begin
if TokenType = ttNum then
Shift(10)
else
if TokenType = ttFunc then
Shift(11)
else
if TokenType = ttMinus then
Shift(5)
else
if TokenType = ttOParen then
Shift(9)
else
if TokenType = ttErr then
begin
MathError := True;
Accepted := True;
end
else
begin
TokenError := ErrExpression;
Position := Position - TokenLen;
end;
end;
1:
begin
if TokenType = ttEol then
Accepted := True
else
if TokenType = ttPlus then
Shift(12)
else
if TokenType = ttMinus then
Shift(13)
else
begin
TokenError := ErrOperator;
Position := Position - TokenLen;
end;
end;
2:
begin
if TokenType = ttTimes then
Shift(14)
else
if TokenType = ttDivide then
Shift(15)
else
Reduce(3);
end;
3:
begin
if TokenType = ttModu then
Shift(40)
else
Reduce(6);
end;
4:
begin
if TokenType = ttExpo then
Shift(16)
else
Reduce(8);
end;
5:
begin
if TokenType = ttNum then
Shift(10)
else
if TokenType = ttFunc then
Shift(11)
else
if TokenType = ttOParen then
Shift(9)
else
begin
TokenError := ErrExpression;
Position := Position - TokenLen;
end;
end;
6:
Reduce(10);
7:
Reduce(13);
8:
Reduce(12);
10:
Reduce(15);
11:
if TokenType = ttOParen then
Shift(20)
else
begin
TokenError := ErrOpenParen;
Position := Position - TokenLen;
end;
17:
Reduce(9);
18:
raise EJVCLException.CreateRes(@RsEBadTokenState);
19:
if TokenType = ttPlus then
Shift(12)
else
if TokenType = ttMinus then
Shift(13)
else
if TokenType = ttCParen then
Shift(27)
else
begin
TokenError := ErrOpCloseParen;
Position := Position - TokenLen;
end;
21:
if TokenType = ttTimes then
Shift(14)
else
if TokenType = ttDivide then
Shift(15)
else
Reduce(1);
22:
if TokenType = ttTimes then
Shift(14)
else
if TokenType = ttDivide then
Shift(15)
else
Reduce(2);
23:
Reduce(4);
24:
Reduce(5);
25:
Reduce(7);
26:
Reduce(11);
27:
Reduce(14);
28:
if TokenType = ttPlus then
Shift(12)
else
if TokenType = ttMinus then
Shift(13)
else
if TokenType = ttCParen then
Shift(29)
else
begin
TokenError := ErrOpCloseParen;
Position := Position - TokenLen;
end;
29:
Reduce(16);
80:
Reduce(100);
end;
until Accepted or (TokenError <> 0);
if TokenError <> 0 then
begin
if TokenError = ErrBadRange then
Position := Position - TokenLen;
if Assigned(FOnParseError) then
FOnParseError(Self, TokenError);
end;
if MathError or (TokenError <> 0) then
begin
ParseError := True;
ParseValue := 0;
Exit;
end;
ParseError := False;
ParseValue := Stack[StackTop].Value;
end;
{ Completes a reduction }
procedure TJvMathParser.Reduce(Reduction: Word);
var
Token1, Token2: TokenRec;
begin
case Reduction of
1:
begin
Pop(Token1);
Pop(Token2);
Pop(Token2);
CurrToken.Value := Token1.Value + Token2.Value;
end;
2:
begin
Pop(Token1);
Pop(Token2);
Pop(Token2);
CurrToken.Value := Token2.Value - Token1.Value;
end;
4:
begin
Pop(Token1);
Pop(Token2);
Pop(Token2);
CurrToken.Value := Token1.Value * Token2.Value;
end;
5:
begin
Pop(Token1);
Pop(Token2);
Pop(Token2);
if Token1.Value = 0 then
MathError := True
else
CurrToken.Value := Token2.Value / Token1.Value;
end;
{ MOD operator }
100:
begin
Pop(Token1);
Pop(Token2);
Pop(Token2);
if Token1.Value = 0 then
MathError := True
else
CurrToken.Value := Round(Token2.Value) mod Round(Token1.Value);
end;
7:
begin
Pop(Token1);
Pop(Token2);
Pop(Token2);
if Token2.Value <= 0 then
MathError := True
else
if (Token1.Value * Ln(Token2.Value) < -ExpLimit) or
(Token1.Value * Ln(Token2.Value) > ExpLimit) then
MathError := True
else
CurrToken.Value := Exp(Token1.Value * Ln(Token2.Value));
end;
9:
begin
Pop(Token1);
Pop(Token2);
CurrToken.Value := -Token1.Value;
end;
11:
raise EJVCLException.CreateRes(@RsEInvalidReduction);
13:
raise EJVCLException.CreateRes(@RsEInvalidReduction);
14:
begin
Pop(Token1);
Pop(CurrToken);
Pop(Token1);
end;
16:
begin
Pop(Token1);
Pop(CurrToken);
Pop(Token1);
Pop(Token1);
if Token1.FuncName = 'ABS' then
CurrToken.Value := Abs(CurrToken.Value)
else
if Token1.FuncName = 'ATAN' then
CurrToken.Value := ArcTan(CurrToken.Value)
else
if Token1.FuncName = 'COS' then
begin
if (CurrToken.Value < -9E18) or (CurrToken.Value > 9E18) then
MathError := True
else
CurrToken.Value := Cos(CurrToken.Value)
end
else
if Token1.FuncName = 'EXP' then
begin
if (CurrToken.Value < -ExpLimit) or (CurrToken.Value > ExpLimit) then
MathError := True
else
CurrToken.Value := Exp(CurrToken.Value);
end
else
if Token1.FuncName = 'LN' then
begin
if CurrToken.Value <= 0 then
MathError := True
else
CurrToken.Value := Ln(CurrToken.Value);
end
else
if Token1.FuncName = 'ROUND' then
begin
if (CurrToken.Value < -1E9) or (CurrToken.Value > 1E9) then
MathError := True
else
CurrToken.Value := Round(CurrToken.Value);
end
else
if Token1.FuncName = 'SIN' then
begin
if (CurrToken.Value < -9E18) or (CurrToken.Value > 9E18) then
MathError := True
else
CurrToken.Value := Sin(CurrToken.Value)
end
else
if Token1.FuncName = 'SQRT' then
begin
if CurrToken.Value < 0 then
MathError := True
else
CurrToken.Value := Sqrt(CurrToken.Value);
end
else
if Token1.FuncName = 'SQR' then
begin
if (CurrToken.Value < -SqrLimit) or (CurrToken.Value > SqrLimit) then
MathError := True
else
CurrToken.Value := Sqr(CurrToken.Value);
end
else
if Token1.FuncName = 'TRUNC' then
begin
if (CurrToken.Value < -1E9) or (CurrToken.Value > 1E9) then
MathError := True
else
CurrToken.Value := Trunc(CurrToken.Value);
end;
end;
3, 6, 8, 10, 12, 15:
Pop(CurrToken);
end;
CurrToken.State := GotoState(Reduction);
Push(CurrToken);
end;
{ Shifts a Token onto the stack }
procedure TJvMathParser.Shift(State: Word);
begin
CurrToken.State := State;
Push(CurrToken);
TokenType := NextToken;
end;
//=== { TTreeKeyMappings } ===================================================
procedure TTreeKeyMappings.SetAddNode(const Value: TShortCut);
begin
FAddNode := Value;
end;
procedure TTreeKeyMappings.SetDeleteNode(const Value: TShortCut);
begin
FDeleteNode := Value;
end;
procedure TTreeKeyMappings.SetInsertNode(const Value: TShortCut);
begin
FInsertNode := Value;
end;
procedure TTreeKeyMappings.SetAddChildNode(const Value: TShortCut);
begin
FAddChildNode := Value;
end;
procedure TTreeKeyMappings.SetDuplicateNode(const Value: TShortCut);
begin
FDuplicateNode := Value;
end;
procedure TTreeKeyMappings.SetEditNode(const Value: TShortCut);
begin
FEditNode := Value;
end;
procedure TJvJanTreeView.KeyPress(var Key: Char);
begin
if Key = Cr then
Recalculate;
if Assigned(OnKeyPress) then
OnKeyPress(Self, Key);
end;
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.