summaryrefslogtreecommitdiff
path: root/examples/apps/ide
diff options
context:
space:
mode:
authorGraeme Geldenhuys <graeme@mastermaths.co.za>2011-07-16 19:56:28 +0200
committerGraeme Geldenhuys <graeme@mastermaths.co.za>2011-07-16 19:56:28 +0200
commit7eee7a7d0ffb55fbee2b7d2e3f2903ad1f7711fc (patch)
tree94417b26c048f55849efb1fc92ecfdff0d1a58a0 /examples/apps/ide
parent2122524e4d56618197e4f0ddd69db49f3552bbfb (diff)
parent0a6e1179e7f192f4350a01074de86f77f0e927ca (diff)
downloadfpGUI-7eee7a7d0ffb55fbee2b7d2e3f2903ad1f7711fc.tar.xz
Merged fpgIDE project as a subdirectory examples/apps/ide/
Diffstat (limited to 'examples/apps/ide')
-rw-r--r--examples/apps/ide/TODO51
-rw-r--r--examples/apps/ide/images/constructor_16.bmpbin0 -> 822 bytes
-rw-r--r--examples/apps/ide/images/destructor_16.bmpbin0 -> 822 bytes
-rw-r--r--examples/apps/ide/images/function_16.bmpbin0 -> 822 bytes
-rw-r--r--examples/apps/ide/images/gears_16.bmpbin0 -> 822 bytes
-rw-r--r--examples/apps/ide/images/gutter_vertical.bmpbin0 -> 438 bytes
-rw-r--r--examples/apps/ide/src/builderthread.pas128
-rw-r--r--examples/apps/ide/src/filemonitor.pas32
-rw-r--r--examples/apps/ide/src/fpg_textedit.pas1881
-rw-r--r--examples/apps/ide/src/fpgide.lpi150
-rw-r--r--examples/apps/ide/src/fpgide.lpr33
-rw-r--r--examples/apps/ide/src/fpgide.prj1387
-rw-r--r--examples/apps/ide/src/fpgide.project55
-rw-r--r--examples/apps/ide/src/frm_configureide.pas741
-rw-r--r--examples/apps/ide/src/frm_debug.pas170
-rw-r--r--examples/apps/ide/src/frm_main.pas1277
-rw-r--r--examples/apps/ide/src/frm_procedurelist.pas1333
-rw-r--r--examples/apps/ide/src/frm_projectoptions.pas1164
-rw-r--r--examples/apps/ide/src/ideconst.pas83
-rw-r--r--examples/apps/ide/src/ideimages.pas64
-rw-r--r--examples/apps/ide/src/idemacros.pas318
-rw-r--r--examples/apps/ide/src/ideutils.pas395
-rw-r--r--examples/apps/ide/src/mPasLex.pas1442
-rw-r--r--examples/apps/ide/src/proclistimages.inc208
-rw-r--r--examples/apps/ide/src/project.pas368
-rw-r--r--examples/apps/ide/src/stringhelpers.pas390
-rw-r--r--examples/apps/ide/src/synregexpr.pas4141
-rw-r--r--examples/apps/ide/src/templates/default.project39
-rw-r--r--examples/apps/ide/src/templates/empty.project29
-rw-r--r--examples/apps/ide/src/unitlist.pas170
30 files changed, 16049 insertions, 0 deletions
diff --git a/examples/apps/ide/TODO b/examples/apps/ide/TODO
new file mode 100644
index 00000000..bbd18ddf
--- /dev/null
+++ b/examples/apps/ide/TODO
@@ -0,0 +1,51 @@
+
+ Personal todo list for fpGUI IDE project
+
+
+Legend
+======
+[ ] - not started yet
+[o] - started but not complete
+[x] - completed task.
+
+
+fpGUI IDE
+=========
+[ ] Double click on lines in Messages window jumps to code line.
+[ ] add a Console Output window to the IDE.
+[ ] integrate GDB/MI into the IDE.
+ [ ] porting of basic unit to fpGUI
+ [ ] integration of Compiler Settings Dialog
+ [ ] Watches window implementation
+ [ ] Call Stack window
+ [ ] Watchpoints window
+ [ ] Assembly window
+ [ ] CPU window
+[ ] Syntax highlighting with descent speed.
+[ ] Basic Search dialog
+[ ] Find in Files dialog
+[ ] Regex support in all search dialogs
+[ ] External Tools setup and usage
+[ ] Keyboard Shortcuts dialog
+[ ] Refactoring Tools
+[ ] Interface/Implementation jumping within the editor using the normal
+ Ctrl+Shift+[up|down] key combo.
+[ ] Variable tab stop support in the editor
+[ ] Elastic Tabstops implementation.
+[ ] Global Macro support. These macros exist in the IDE across projects.
+[ ] Unit Testing framework integration (with DUnit2 project)
+[ ] Code Templates support
+[ ] File Browser tabsheet implementation. File navigation and opening of files.
+[ ] GoTo Line Number dialog
+[ ] Converting all UI to MiG Layout Manager based dialogs.
+
+
+Completed
+=========
+[x] Project management.
+[x] Project based marco support with hard-coded system macros
+[x] pipe compiler messages out to the Messages window.
+[x] Procedure List dialog
+
+
+
diff --git a/examples/apps/ide/images/constructor_16.bmp b/examples/apps/ide/images/constructor_16.bmp
new file mode 100644
index 00000000..992fe262
--- /dev/null
+++ b/examples/apps/ide/images/constructor_16.bmp
Binary files differ
diff --git a/examples/apps/ide/images/destructor_16.bmp b/examples/apps/ide/images/destructor_16.bmp
new file mode 100644
index 00000000..0f0d0c2b
--- /dev/null
+++ b/examples/apps/ide/images/destructor_16.bmp
Binary files differ
diff --git a/examples/apps/ide/images/function_16.bmp b/examples/apps/ide/images/function_16.bmp
new file mode 100644
index 00000000..8929a90b
--- /dev/null
+++ b/examples/apps/ide/images/function_16.bmp
Binary files differ
diff --git a/examples/apps/ide/images/gears_16.bmp b/examples/apps/ide/images/gears_16.bmp
new file mode 100644
index 00000000..c9633504
--- /dev/null
+++ b/examples/apps/ide/images/gears_16.bmp
Binary files differ
diff --git a/examples/apps/ide/images/gutter_vertical.bmp b/examples/apps/ide/images/gutter_vertical.bmp
new file mode 100644
index 00000000..882e2f34
--- /dev/null
+++ b/examples/apps/ide/images/gutter_vertical.bmp
Binary files differ
diff --git a/examples/apps/ide/src/builderthread.pas b/examples/apps/ide/src/builderthread.pas
new file mode 100644
index 00000000..bfdc48b1
--- /dev/null
+++ b/examples/apps/ide/src/builderthread.pas
@@ -0,0 +1,128 @@
+unit BuilderThread;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+type
+ TOutputLineEvent = procedure(Sender: TObject; const ALine: string) of object;
+
+ TBuilderThread = class(TThread)
+ private
+ FBuildMode: integer;
+ FOnAvailableOutput: TOutputLineEvent;
+ OutputLine: string;
+ procedure DoOutputLine;
+ protected
+ procedure Execute; override;
+ public
+ procedure AfterConstruction; override;
+ property BuildMode: integer read FBuildMode write FBuildMode;
+ property OnAvailableOutput: TOutputLineEvent read FOnAvailableOutput write FOnAvailableOutput;
+ end;
+
+implementation
+
+uses
+ project
+ ,process
+ ,fpg_base
+ ,fpg_iniutils
+ ,fpg_utils
+ ,ideconst
+ ,idemacros
+ ;
+
+{ TBuilderThread }
+
+procedure TBuilderThread.AfterConstruction;
+begin
+ inherited AfterConstruction;
+ FBuildMode := -1; // signals use of project's default build mode
+ FreeOnTerminate := True;
+end;
+
+procedure TBuilderThread.Execute;
+const
+ BufSize = 1024; //4096;
+var
+ p: TProcess;
+ c: TfpgString;
+ unitdir: TfpgString;
+ Buf: string;
+ Count: integer;
+ i: integer;
+ LineStart: integer;
+begin
+ unitdir := GProject.ProjectDir + GProject.UnitOutputDir;
+ unitdir := GMacroList.ExpandMacro(unitdir);
+ if not fpgDirectoryExists(unitdir) then
+ begin
+ {$IFDEF DEBUG}
+ writeln('DEBUG: TBuilderThread.Execute - Creating dir: ' + unitdir);
+ {$ENDIF}
+ fpgForceDirectories(unitDir);
+ end;
+
+ p := TProcess.Create(nil);
+ p.Options := [poUsePipes, poStdErrToOutPut];
+ p.ShowWindow := swoShowNormal;
+ p.CurrentDirectory := GProject.ProjectDir;
+
+ // build compilation string
+ c := gINI.ReadString(cEnvironment, 'Compiler', '');
+ c := c + GProject.GenerateCmdLine(False, BuildMode);
+ c := GMacroList.ExpandMacro(c);
+
+// AddMessage('Compile command: ' + c);
+ p.CommandLine := c;
+ try
+ p.Execute;
+
+ { Now process the output }
+ OutputLine:='';
+ SetLength(Buf,BufSize);
+ repeat
+ if (p.Output<>nil) then
+ begin
+ Count:=p.Output.Read(Buf[1],Length(Buf));
+ end
+ else
+ Count:=0;
+ LineStart:=1;
+ i:=1;
+ while i<=Count do
+ begin
+ if Buf[i] in [#10,#13] then
+ begin
+ OutputLine:=OutputLine+Copy(Buf,LineStart,i-LineStart);
+ Synchronize(@DoOutputLine);
+ OutputLine:='';
+ if (i<Count) and (Buf[i+1] in [#10,#13]) and (Buf[i]<>Buf[i+1]) then
+ inc(i);
+ LineStart:=i+1;
+ end;
+ inc(i);
+ end;
+ OutputLine:=Copy(Buf,LineStart,Count-LineStart+1);
+ until Count=0;
+ if OutputLine <> '' then
+ Synchronize(@DoOutputLine);
+ p.WaitOnExit;
+ finally
+ FreeAndNil(p);
+ end;
+
+end;
+
+procedure TBuilderThread.DoOutputLine;
+begin
+ if Assigned(FOnAvailableOutput) then
+ FOnAvailableOutput(self, OutputLine);
+end;
+
+end.
+
diff --git a/examples/apps/ide/src/filemonitor.pas b/examples/apps/ide/src/filemonitor.pas
new file mode 100644
index 00000000..0e0fa7a8
--- /dev/null
+++ b/examples/apps/ide/src/filemonitor.pas
@@ -0,0 +1,32 @@
+unit filemonitor;
+
+{$mode objfpc}
+
+interface
+
+uses
+ Classes, SysUtils, fpg_main;
+
+type
+ TFileMonitor = class(TThread)
+ private
+ FInterval: integer;
+ public
+ procedure Execute; override;
+ property Interval: integer read FInterval write FInterval;
+ end;
+
+implementation
+
+{ TFileMonitor }
+
+procedure TFileMonitor.Execute;
+begin
+ while not Terminated do
+ begin
+
+ end;
+end;
+
+end.
+
diff --git a/examples/apps/ide/src/fpg_textedit.pas b/examples/apps/ide/src/fpg_textedit.pas
new file mode 100644
index 00000000..02aa5285
--- /dev/null
+++ b/examples/apps/ide/src/fpg_textedit.pas
@@ -0,0 +1,1881 @@
+{
+ fpGUI - Free Pascal GUI Toolkit
+
+ Copyright (C) 2006 - 2010 See the file AUTHORS.txt, included in this
+ distribution, for details of the copyright.
+
+ See the file COPYING.modifiedLGPL, included in this distribution,
+ for details about redistributing fpGUI.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ Description:
+ A new Memo component. It's actually more a TextEdit or MulitLineEdit
+ component because it has a lot more features than simply a Memo. Features
+ include: gutter, line numbers in gutter, right edge margin, syntax
+ highlighting, much more optimised etc...
+}
+
+unit fpg_textedit;
+
+{$mode objfpc}{$H+}
+{.$Define gDEBUG}
+
+interface
+
+uses
+ Classes, SysUtils, fpg_base, fpg_main, fpg_widget,
+ fpg_scrollbar;
+
+type
+ // forward declaration
+ TfpgBaseTextEdit = class;
+
+ TfpgGutter = class(TfpgWidget)
+ private
+ FOwner: TfpgBaseTextEdit; // convenience reference variable
+ FDigits: Integer;
+ FShowNum: Boolean;
+ FSpace: Integer;
+ FStartNum: Integer;
+ FZeroStart: Boolean;
+ procedure SetDigits(const AValue: Integer);
+ procedure SetShowNum(const AValue: Boolean);
+ procedure SetSpace(const AValue: Integer);
+ procedure SetStartNum(const AValue: Integer);
+ procedure DrawLineNums;
+ procedure SetZeroStart(const AValue: Boolean);
+ protected
+ procedure HandlePaint; override;
+ procedure HandleMouseScroll(x, y: integer; shiftstate: TShiftState; delta: smallint); override;
+ public
+ constructor CreateGutter(AOwner: TfpgBaseTextEdit);
+ function GetClientRect: TfpgRect; override;
+ property LeadingDigits: Integer read FDigits write SetDigits default 0;
+ property ShowNum: Boolean read FShowNum write SetShowNum default True;
+ property Space: Integer read FSpace write SetSpace default 2;
+ property StartNum: Integer read FStartNum write SetStartNum default 1;
+ property Width default 35;
+ property ZeroStart: Boolean read FZeroStart write SetZeroStart default False;
+ end;
+
+
+ TfpgDrawLineEvent = procedure(Sender: TObject; ALineText: TfpgString;
+ ALineIndex: Integer; ACanvas: TfpgCanvas; ATextRect: TfpgRect;
+ var AllowSelfDraw: Boolean) of object;
+
+
+ TfpgBaseTextEdit = class(TfpgWidget)
+ private
+ FFont: TfpgFont;
+ FFullRedraw: Boolean;
+ FLines: TStrings;
+ CaretPos: TPoint;
+ FOnDrawLine: TfpgDrawLineEvent;
+ FScrollBarStyle: TfpgScrollStyle;
+ MousePos: TPoint;
+ FChrW: Integer;
+ FChrH: Integer;
+ FTopLine: Integer;
+ FVisLines: Integer;
+ FVisCols: Integer;
+ StartNo, EndNo, StartOffs, EndOffs: Integer;
+ // Selection start and end line number
+ FSelStartNo, FSelEndNo: Integer;
+ // Selection start and end column
+ FSelStartOffs, FSelEndOffs: Integer;
+ FTabWidth: Integer;
+ HPos, VPos, XSize, YSize: Integer;
+ FMaxScrollH: Integer;
+ FVScrollBar: TfpgScrollBar;
+ FHScrollBar: TfpgScrollBar;
+ FTracking: Boolean;
+ FSelDrag: Boolean;
+ FSelected, FSelMouseDwn: Boolean;
+ FGutterPan: TfpgGutter;
+ FRightEdge: Boolean;
+ FRightEdgeCol: Integer;
+ FLineChanged: Integer; // force only one line to repaint if greater than -1
+
+ FLastScrollEventTime: TTime; // in milliseconds
+ FLastScrollEventTimeBefore: TTime; // in milliseconds
+ fmousewheelfrequmin: double;
+ fmousewheelfrequmax: double;
+ fmousewheeldeltamin: double;
+ fmousewheeldeltamax: double;
+ fmousewheelaccelerationmax: double;
+
+ fwheelsensitivity: double;
+ function GetFontDesc: string;
+ function GetGutterShowLineNumbers: Boolean;
+ function GetGutterVisible: Boolean;
+ function GetHScrollPos: Integer;
+ function GetVScrollPos: Integer;
+ procedure SetFontDesc(const AValue: string);
+ procedure SetGutterShowLineNumbers(const AValue: Boolean);
+ procedure SetGutterVisible(const AValue: Boolean);
+ procedure SetHScrollPos(const AValue: Integer);
+ procedure SetLines(const AValue: TStrings);
+ procedure SetScrollBarStyle(const AValue: TfpgScrollStyle);
+ procedure SetTabWidth(const AValue: Integer);
+ procedure SetVScrollPos(const AValue: Integer);
+ procedure UpdateCharBounds;
+ procedure GetSelBounds(var AStartNo, AEndNo, AStartOffs, AEndOffs: Integer);
+ procedure UpdateScrollBars;
+ procedure VScrollBarMove(Sender: TObject; position: integer);
+ procedure HScrollBarMove(Sender: TObject; position: integer);
+ procedure SetVPos(p: Integer);
+ procedure SetHPos(p: Integer);
+ procedure UpdateScrollBarCoords;
+ procedure UpdateGutterCoords;
+ procedure KeyboardCaretNav(const ShiftState: TShiftState; const AKeyCode: Word);
+ procedure InitMemoObjects;
+ procedure SetRightEdge(const AValue: Boolean);
+ procedure SetRightEdgeCol(const AValue: Integer);
+ function calcmousewheeldelta(var info: TfpgMsgParmMouse; const fmin,fmax,deltamin,deltamax: double): double;
+ function mousewheelacceleration(const avalue: double): double;
+ function mousewheelacceleration(const avalue: integer): integer;
+ protected
+ { -- internal events -- }
+ procedure HandleShow; override;
+ procedure HandleResize(AWidth, AHeight: TfpgCoord); override;
+ procedure HandlePaint; override;
+ procedure HandleMouseEnter; override;
+ procedure HandleMouseExit; override;
+ procedure HandleLMouseDown(x, y: integer; shiftstate: TShiftState); override;
+ procedure HandleMouseScroll(x, y: integer; shiftstate: TShiftState; delta: smallint); override;
+ procedure HandleKeyPress(var keycode: word; var shiftstate: TShiftState; var consumed: boolean); override;
+ procedure HandleKeyChar(var AText: TfpgChar; var shiftstate: TShiftState; var consumed: boolean); override;
+ { -- local widget functions -- }
+ procedure DrawVisible; virtual;
+ procedure DrawLine(const ALineIndex, Y: Integer); virtual;
+ procedure FormatLine(const ALineIndex, X, Y: Integer);
+ procedure DrawCaret(const X, Y: Integer); virtual;
+ { -- to be published --}
+ property FontDesc: string read GetFontDesc write SetFontDesc;
+ property FullRedraw: Boolean read FFullRedraw write FFullRedraw default False;
+ property GutterVisible: Boolean read GetGutterVisible write SetGutterVisible default False;
+ property GutterShowLineNumbers: Boolean read GetGutterShowLineNumbers write SetGutterShowLineNumbers default True;
+ property Lines: TStrings read FLines write SetLines;
+ property ScrollBarStyle: TfpgScrollStyle read FScrollBarStyle write SetScrollBarStyle default ssAutoBoth;
+ property TabWidth: Integer read FTabWidth write SetTabWidth default 8;
+ property Tracking: Boolean read FTracking write FTracking default True;
+ property OnDrawLine: TfpgDrawLineEvent read FOnDrawLine write FOnDrawLine;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ function GetClientRect: TfpgRect; override;
+ function GetWordAtPos(const X, Y: Integer; out XBegin: Integer): TfpgString;
+ procedure GetRowColAtPos(const X, Y: Integer; out Row, Col: Integer);
+ procedure Clear;
+ procedure ScrollTo(X, Y: Integer);
+ procedure GotoLine(ALine: integer);
+ procedure DeleteSelection;
+ procedure SaveToFile(const AFileName: TfpgString);
+ procedure LoadFromFile(const AFileName: TfpgString);
+ property FontHeight: Integer read FChrH;
+ property FontWidth: Integer read FChrW;
+ property ScrollPos_H: Integer read GetHScrollPos write SetHScrollPos;
+ property ScrollPos_V: Integer read GetVScrollPos write SetVScrollPos;
+ property TopLine: Integer read FTopLine;
+ property VisibleLines: Integer read FVisLines;
+ property RightEdge: Boolean read FRightEdge write SetRightEdge default False;
+ property RightEdgeCol: Integer read FRightEdgeCol write SetRightEdgeCol default 80;
+ end;
+
+
+ TfpgTextEdit = class(TfpgBaseTextEdit)
+ published
+ property FontDesc;
+ property FullRedraw;
+ property GutterVisible;
+ property GutterShowLineNumbers;
+ property Lines;
+ property ScrollBarStyle;
+ property TabWidth;
+ property Tracking;
+ property OnDrawLine;
+ end;
+
+
+implementation
+
+uses
+ fpg_dialogs{, fpg_constants}, fpg_stringutils, fpg_utils, math;
+
+
+function GetNextWord(SLine: TfpgString; var PosX: Integer): Boolean;
+const
+ ValidChars = ['a'..'z', 'A'..'Z', '0'..'9', '#'];
+var
+ I, RetX: Integer;
+ FindNext: Boolean;
+ c: TfpgChar;
+begin
+ Result := False;
+ if PosX > UTF8Length(SLine) then Exit;
+ FindNext := False;
+ RetX := 0;
+ for I := PosX to UTF8Length(SLine) do
+ begin
+ c := fpgCharAt(SLine, I);
+ { TODO -cUnicode Error : We need to fix c[i] usage. Also improve ValidChars definition. }
+ if not FindNext and not (c[1] in ValidChars) then
+ begin
+ FindNext := True;
+ Continue;
+ end;
+ if FindNext and (c[1] in ValidChars) then
+ begin
+ RetX := I;
+ Result := True;
+ Break;
+ end;
+ end;
+ if RetX < 1 then
+ Result := False;
+ PosX := RetX;
+end;
+
+
+{ TfpgGutter }
+
+procedure TfpgGutter.SetDigits(const AValue: Integer);
+begin
+ if FDigits=AValue then exit;
+ FDigits:=AValue;
+end;
+
+procedure TfpgGutter.SetShowNum(const AValue: Boolean);
+begin
+ if FShowNum=AValue then exit;
+ FShowNum:=AValue;
+ Invalidate;
+end;
+
+procedure TfpgGutter.SetSpace(const AValue: Integer);
+begin
+ if FSpace=AValue then exit;
+ FSpace:=AValue;
+end;
+
+procedure TfpgGutter.SetStartNum(const AValue: Integer);
+begin
+ if FStartNum=AValue then exit;
+ FStartNum:=AValue;
+end;
+
+procedure TfpgGutter.DrawLineNums;
+var
+ r: TfpgRect;
+ I, MaxI, W, H, ZeroL: Integer;
+ s: TfpgString;
+ ltxtflags: TfpgTextFlags;
+begin
+ if not FShowNum then
+ Exit; //==>
+ w := GetClientRect.Width - FSpace - 1;
+ H := FOwner.FChrH;
+ MaxI := FOwner.FVisLines;
+ ltxtflags := [txtRight, txtVCenter];
+ Canvas.SetFont(FOwner.FFont);
+ r.SetRect(2, 0, W, H);
+
+ for i := 0 to MaxI do
+ begin
+// writeln('i=', i);
+ if FZeroStart then
+ S := IntToStr(FStartNum + i - 1)
+ else
+ S := IntToStr(FStartNum + i);
+ for ZeroL := Length(S) to FDigits do
+ S := '0' + S;
+ r.Top := i * h;
+ Canvas.DrawText(r, S, ltxtflags);
+ end;
+end;
+
+procedure TfpgGutter.SetZeroStart(const AValue: Boolean);
+begin
+ if FZeroStart=AValue then exit;
+ FZeroStart:=AValue;
+end;
+
+procedure TfpgGutter.HandlePaint;
+begin
+ inherited HandlePaint;
+ Canvas.Clear(clWindowBackground);
+ // Gutter right border
+ Canvas.SetColor(clHilite2);
+ Canvas.DrawLine(Width - 2, 0, Width - 2, Height - 1);
+ Canvas.SetColor(clShadow1);
+ Canvas.DrawLine(Width - 1, 0, Width - 1, Height - 1);
+ DrawLineNums;
+end;
+
+procedure TfpgGutter.HandleMouseScroll(x, y: integer; shiftstate: TShiftState;
+ delta: smallint);
+var
+ msg: TfpgMessageParams;
+begin
+ inherited HandleMouseScroll(x, y, shiftstate, delta);
+ fillchar(msg, sizeof(msg), 0); // zero out the record - initialize it
+ msg.mouse.x := x;
+ msg.mouse.y := y;
+ msg.mouse.shiftstate := shiftstate;
+ msg.mouse.delta := delta;
+ fpgPostMessage(self, FOwner.FVScrollBar, FPGM_SCROLL, msg);
+end;
+
+constructor TfpgGutter.CreateGutter(AOwner: TfpgBaseTextEdit);
+begin
+ inherited Create(AOwner);
+ FOwner := AOwner;
+ FDigits := 0;
+ FShowNum := True;
+ FSpace := 2;
+ FStartNum := 1;
+ FZeroStart := False;
+ Width := 35;
+end;
+
+function TfpgGutter.GetClientRect: TfpgRect;
+begin
+ Result := inherited GetClientRect;
+ Result.Width := Result.Width - 2; // border right line takes up two pixels
+end;
+
+{ TfpgBaseTextEdit }
+
+procedure TfpgBaseTextEdit.SetLines(const AValue: TStrings);
+begin
+ FLines.Assign(AValue);
+ Invalidate;
+end;
+
+procedure TfpgBaseTextEdit.SetScrollBarStyle(const AValue: TfpgScrollStyle);
+begin
+ if FScrollBarStyle = AValue then
+ Exit; //==>
+ FScrollBarStyle := AValue;
+ UpdateScrollBarCoords;
+end;
+
+function TfpgBaseTextEdit.GetFontDesc: string;
+begin
+ Result := FFont.FontDesc;
+end;
+
+function TfpgBaseTextEdit.GetGutterShowLineNumbers: Boolean;
+begin
+ Result := FGutterPan.ShowNum;
+end;
+
+function TfpgBaseTextEdit.GetGutterVisible: Boolean;
+begin
+ Result := FGutterPan.Visible;
+end;
+
+function TfpgBaseTextEdit.GetHScrollPos: Integer;
+begin
+ Result := HPos;
+end;
+
+function TfpgBaseTextEdit.GetVScrollPos: Integer;
+begin
+ Result := VPos;
+end;
+
+procedure TfpgBaseTextEdit.SetFontDesc(const AValue: string);
+begin
+ FFont.Free;
+ FFont := fpgGetFont(AValue);
+ Invalidate;
+end;
+
+procedure TfpgBaseTextEdit.SetGutterShowLineNumbers(const AValue: Boolean);
+begin
+ FGutterPan.ShowNum := AValue;
+end;
+
+procedure TfpgBaseTextEdit.SetGutterVisible(const AValue: Boolean);
+begin
+ FGutterPan.Visible := AValue;
+ if FGutterPan.Visible then
+ UpdateGutterCoords;
+ Invalidate;
+end;
+
+procedure TfpgBaseTextEdit.SetHScrollPos(const AValue: Integer);
+begin
+ SetHPos(AValue);
+end;
+
+procedure TfpgBaseTextEdit.SetTabWidth(const AValue: Integer);
+begin
+ if AValue < 1 then
+ begin
+ { todo: add these to resourcestring section }
+ if csDesigning in ComponentState then
+ TfpgMessageDialog.Information(ClassName + ' Tip', 'Value for TabWidth must be greater than 0.');
+ Exit; //==>
+ end;
+ FTabWidth := AValue;
+end;
+
+procedure TfpgBaseTextEdit.SetVScrollPos(const AValue: Integer);
+begin
+ SetVPos(AValue);
+end;
+
+procedure TfpgBaseTextEdit.UpdateCharBounds;
+begin
+ FChrW := FFont.TextWidth('W');
+ FChrH := FFont.Height;
+ FVisLines := (GetClientRect.Height div FChrH) + 1;
+ if FGutterPan.Visible then
+ FVisCols := (GetClientRect.Width - FGutterPan.Width) div FChrW
+ else
+ FVisCols := GetClientRect.Width div FChrW;
+end;
+
+{ Re-order StartXXX and EndXXX if user is selecting backwards }
+procedure TfpgBaseTextEdit.GetSelBounds(var AStartNo, AEndNo, AStartOffs,
+ AEndOffs: Integer);
+begin
+ if FSelStartNo <= FSelEndNo then
+ begin
+ AStartNo := FSelStartNo;
+ AEndNo := FSelEndNo;
+ if not ((AStartNo = AEndNo) and (FSelStartOffs > FSelEndOffs)) then
+ begin
+ AStartOffs := FSelStartOffs;
+ AEndOffs := FSelEndOffs;
+ end
+ else
+ begin
+ AStartOffs := FSelEndOffs;
+ AEndOffs := FSelStartOffs;
+ end;
+ end
+ else
+ begin
+ AStartNo := FSelEndNo;
+ AEndNo := FSelStartNo;
+ AStartOffs := FSelEndOffs;
+ AEndOffs := FSelStartOffs;
+ end;
+end;
+
+procedure TfpgBaseTextEdit.UpdateScrollBars;
+begin
+ FVScrollBar.Min := 0;
+ FVScrollBar.PageSize := FVisLines - 4;
+ FVScrollBar.Max := FLines.Count - FVisLines + 1; // +1 is so the last line is completely visible
+ FVScrollBar.Position := VPos;
+ if FLines.Count > 0 then
+ FVScrollBar.SliderSize := FVisLines / FLines.Count;
+ FVScrollBar.Visible := FLines.Count > FVisLines;
+ if FVScrollBar.Visible then
+ FVScrollBar.RepaintSlider;
+
+ FHScrollBar.Min := 0;
+ FHScrollBar.PageSize := FVisCols div 2; //FMaxScrollH div 4;
+ FHScrollBar.Max := FMaxScrollH - FVisCols + 1;// div 2;
+ FHScrollBar.Position := HPos;
+ FHScrollBar.SliderSize := FVisCols / FMaxScrollH;
+ FHScrollBar.Visible := FMaxScrollH > FVisCols;
+ if FHScrollBar.Visible then
+ FHScrollBar.RepaintSlider;
+
+ UpdateScrollBarCoords;
+ UpdateCharBounds;
+end;
+
+procedure TfpgBaseTextEdit.VScrollBarMove(Sender: TObject; position: integer);
+begin
+ //if FDropList.Visible then
+ //FDropList.Visible := False;
+ //FDropTimeCount := 0;
+ //FLastDropPos.x := -1;
+ //FLastDropPos.y := -1;
+ if FTracking then
+ SetVPos(position);
+ //case ScrollCode of
+ //SB_LINEUP: SetVPos(VPos - 1);
+ //SB_LINEDOWN: SetVPos(VPos + 1);
+ //SB_PAGEUP: SetVPos(VPos - FVisLines);
+ //SB_PAGEDOWN: SetVPos(VPos + FVisLines);
+ //SB_THUMBPOSITION: SetVPos(Pos);
+ //SB_THUMBTRACK: if FTracking then SetVPos(Pos);
+ //SB_TOP: SetVPos(0);
+ //SB_BOTTOM: SetVPos(YSize);
+ //end;
+end;
+
+procedure TfpgBaseTextEdit.HScrollBarMove(Sender: TObject; position: integer);
+begin
+ //if FDropList.Visible then
+ //FDropList.Visible := False;
+ //FDropTimeCount := 0;
+ //FLastDropPos.x := -1;
+ //FLastDropPos.y := -1;
+
+ if FTracking then
+ SetHPos(position);
+
+ //case ScrollCode of
+ //SB_LINERIGHT: SetHPos(HPos + 1);
+ //SB_LINELEFT: SetHPos(HPos - 1);
+ //SB_PAGEUP: SetHPos(HPos - FVisLines);
+ //SB_PAGEDOWN: SetHPos(HPos + FVisLines);
+ //SB_THUMBPOSITION: SetHPos(Pos);
+ //SB_THUMBTRACK: if FTracking then SetHPos(Pos);
+ //SB_TOP: SetHPos(0);
+ //SB_BOTTOM: SetHPos(XSize);
+ //end;
+end;
+
+procedure TfpgBaseTextEdit.SetVPos(p: Integer);
+var
+ OldPos: Integer;
+// R: TfpgRect;
+begin
+ OldPos := VPos;
+ VPos := p;
+
+ {$IFDEF gDEBUG}
+ writeln('OldPos:', OldPos, ' NewPos:', VPos, ' SB.Max:', FVScrollBar.Max);
+ {$ENDIF}
+
+// FVScrollBar.Position := VPos;
+
+// R := GetClientRect;
+ if OldPos - VPos <> 0 then
+ begin
+ { todo: implement scrolling children }
+// ScrollChildren(0, (OldPos - VPos) * FChrH);
+ FTopLine := VPos;
+
+ if FFullRedraw then
+ Invalidate
+ else
+ if (FTopLine + (FVisLines-1)) <= FLines.Count then
+ Invalidate;
+ { TODO : Implement scrolling events }
+ //if Assigned(FOnScrolled_V) then
+ //FOnScrolled_V(Self);
+ //if Assigned(FOnTextScrolled) then
+ //FOnTextScrolled(Self, FTopLine, FTopLine + FVisLines + 1,HPos, FMaxScrollH);
+ end;
+end;
+
+procedure TfpgBaseTextEdit.SetHPos(p: Integer);
+var
+ OldPos: Integer;
+// R: TfpgRect;
+begin
+ OldPos := HPos;
+ HPos := p;
+
+ {$IFDEF gDEBUG}
+ writeln('OldPos:', OldPos, ' NewPos:', HPos, ' SB.Max:', FHScrollBar.Max);
+ {$ENDIF}
+
+// R := GetClientRect;
+ if OldPos - HPos <> 0 then
+ begin
+ { TODO : Implemente scrolling children }
+// ScrollChildren((OldPos - HPos), 0);
+ //if FFullRedraw then
+ Invalidate;
+ //else
+ //DrawVisible;
+ { TODO : Implement scrolling events }
+ //if Assigned(FOnScrolled_H) then
+ //FOnScrolled_H(Self);
+ //if Assigned(FOnTextScrolled) then
+ //FOnTextScrolled(Self, FTopLine, FTopLine + FVisLines, HPos, FMaxScrollH);
+ end;
+end;
+
+procedure TfpgBaseTextEdit.UpdateScrollBarCoords;
+var
+ HWidth: integer;
+ VHeight: integer;
+ r: TfpgRect;
+begin
+ r := GetClientRect;
+ VHeight := r.Height;
+ HWidth := r.Width;
+
+ FHScrollBar.Top := Height - FHScrollBar.Height - r.Top;
+ FHScrollBar.Left := r.Top;
+ FHScrollBar.Width := HWidth;
+
+ FVScrollBar.Top := r.Top;
+ FVScrollBar.Left := Width - FVScrollBar.Width - r.Top;
+ FVScrollBar.Height := VHeight;
+
+ FVScrollBar.UpdateWindowPosition;
+ FHScrollBar.UpdateWindowPosition;
+end;
+
+procedure TfpgBaseTextEdit.UpdateGutterCoords;
+var
+ r: TfpgRect;
+begin
+ r := GetClientRect;
+ if FGutterPan.Visible then
+ FGutterPan.SetPosition(r.Left, r.Top, FGutterPan.Width, r.Height);
+end;
+
+{ This procedure is used to set caret position on keyboard navigation and
+ to set selection if Shift key is pressed. }
+procedure TfpgBaseTextEdit.KeyboardCaretNav(const ShiftState: TShiftState; const AKeyCode: Word);
+var
+ SaveXCaret: Integer;
+
+ procedure CtrlKeyLeftKey;
+ var
+ S: TfpgString;
+ XB: Integer;
+ begin
+ S := GetWordAtPos(CaretPos.X, CaretPos.Y, XB);
+ if (S <> '') and (XB > -1) then
+ begin
+ if FSelected then
+ FSelEndOffs := XB;
+ CaretPos.X := XB;
+ end
+ else
+ begin
+ if FSelected then
+ FSelEndOffs := 0;
+ CaretPos.X := 0;
+ end;
+ end;
+
+ procedure CtrlKeyRightKey;
+ var
+ S: TfpgString;
+ I: Integer;
+ NotFindIt: Boolean;
+ begin
+ if CaretPos.Y <= pred(FLines.Count) then
+ begin
+ NotFindIt := True;
+ while NotFindIt do
+ begin
+ S := FLines[CaretPos.Y];
+ I := CaretPos.X;
+ if GetNextWord(S, I) then
+ begin
+ CaretPos.X := I - 1;
+ NotFindIt := False;
+ end
+ else
+ begin
+ CaretPos.Y := CaretPos.Y + 1;
+ CaretPos.X := 0;
+ NotFindIt := False;
+ end;
+ if CaretPos.Y > pred(FLines.Count) then
+ begin
+ NotFindIt := False;
+ CaretPos.X := 0;
+ end;
+ end;
+ end
+ else
+ CaretPos.X := 0;
+ end;
+
+begin
+ case AKeyCode of
+ keyLeft:
+ begin
+ CaretPos.X := CaretPos.X - 1;
+ if CaretPos.X < 0 then
+ begin
+ if CaretPos.Y > 0 then
+ begin
+ if CaretPos.Y <= (FLines.Count-1) then
+ begin
+ if (ssCtrl in ShiftState) and (CaretPos.Y > 0) then
+ begin
+ CaretPos.Y := CaretPos.Y - 1;
+ CaretPos.X := UTF8Length(FLines[CaretPos.Y]);
+ if FSelected then
+ begin
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := CaretPos.X;
+ end;
+ Exit;
+ end;
+ end;
+ CaretPos.Y := CaretPos.Y - 1;
+ CaretPos.X := UTF8Length(FLines[CaretPos.Y]);
+ end
+ else
+ begin
+ CaretPos.X := 0;
+ end;
+ end;
+ if ssShift in ShiftState then
+ begin
+ if not FSelected then
+ begin
+ if CaretPos.Y <= (FLines.Count-1) then
+ if CaretPos.X > UTF8Length(FLines[CaretPos.Y]) then
+ CaretPos.Y := UTF8Length(FLines[CaretPos.Y]) - 1;
+ FSelected := True;
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X + 1;
+ FSelEndNo := CaretPos.Y;
+ if ssCtrl in ShiftState then
+ CtrlKeyLeftKey
+ else
+ FSelEndOffs := CaretPos.X;
+ end
+ else
+ begin
+ FSelEndNo := CaretPos.Y;
+ if ssCtrl in ShiftState then
+ CtrlKeyLeftKey
+ else
+ FSelEndOffs := CaretPos.X;
+ if FSelEndNo <= (FLines.Count-1) then
+ begin
+ if FSelEndOffs > UTF8Length(FLines[FSelEndNo]) then
+ begin
+ FSelEndOffs := UTF8Length(FLines[FSelEndNo]) - 1;
+ CaretPos.X := FSelEndOffs;
+ end;
+ end
+ else
+ begin
+ FSelEndOffs := 0;
+ CaretPos.X := 0;
+ end;
+ end;
+ FSelected := (FSelStartNo <> FSelEndNo) or (FSelStartOffs <> FSelEndOffs);
+ Exit;
+ end;
+ if FSelected then
+ begin
+ FSelected := False;
+ end;
+ if ssCtrl in ShiftState then
+ begin
+ CtrlKeyLeftKey;
+ end;
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ end;
+
+ keyRight:
+ begin
+ CaretPos.X := CaretPos.X + 1;
+ if CaretPos.X > FMaxScrollH then
+ begin
+ FMaxScrollH := FMaxScrollH + 2;
+ UpdateScrollBars;
+ end;
+ if ssShift in ShiftState then
+ begin
+ if not FSelected then
+ begin
+ FSelected := True;
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X - 1;
+ if ssCtrl in ShiftState then
+ CtrlKeyRightKey;
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := CaretPos.X;
+ end
+ else
+ begin
+ if ssCtrl in ShiftState then
+ CtrlKeyRightKey;
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := CaretPos.X;
+ end;
+ FSelected := (FSelStartNo <> FSelEndNo) or (FSelStartOffs <> FSelEndOffs);
+ Exit;
+ end;
+ if FSelected then
+ begin
+ FSelected := False;
+ end;
+ if ssCtrl in ShiftState then
+ begin
+ CtrlKeyRightKey;
+ end;
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ end;
+
+ keyUp:
+ begin
+ if CaretPos.Y = 0 then
+ Exit;
+ if not (ssShift in ShiftState) and not (ssCtrl in ShiftState) then
+ begin
+ CaretPos.Y := CaretPos.Y - 1;
+ // scroll text
+ if FVScrollBar.Visible and (CaretPos.Y < FTopLine) then
+ FVScrollBar.LineUp;
+ if FSelected then
+ begin
+ FSelected := False;
+ Exit;
+ end;
+ FSelStartNo := CaretPos.Y;
+ Exit;
+ end
+ else if (ssCtrl in ShiftState) and not (ssShift in ShiftState) then
+ begin
+ CaretPos.Y := CaretPos.Y - 1;
+ if FVScrollBar.Visible then
+ FVScrollBar.LineUp; // VScrollBarMove(self, FVScrollBar.Position-1);
+ FSelStartNo := CaretPos.Y;
+ Exit;
+ end
+ else if not (ssCtrl in ShiftState) and (ssShift in ShiftState) then
+ begin
+ CaretPos.Y := CaretPos.Y - 1;
+ if not FSelected then
+ begin
+ FSelStartNo := CaretPos.Y + 1;
+ FSelStartOffs := CaretPos.X;
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := CaretPos.X;
+ FSelected := True;
+ end
+ else
+ begin
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := CaretPos.X;
+ FSelected := (FSelStartNo <> FSelEndNo) or (FSelStartOffs <> FSelEndOffs);
+ end;
+ end;
+ end;
+
+ keyDown:
+ begin
+ if CaretPos.Y >= FLines.Count then
+ Exit;
+ if not (ssShift in ShiftState) and not (ssCtrl in ShiftState) then
+ begin
+ CaretPos.Y := CaretPos.Y + 1;
+ // scroll text
+ if FVScrollBar.Visible and (CaretPos.Y > FTopLine+FVisLines-2) then
+ FVScrollBar.LineDown;
+ if FSelected then
+ begin
+ FSelected := False;
+ Exit;
+ end;
+ FSelStartNo := CaretPos.Y;
+ Exit;
+ end
+ else if (ssCtrl in ShiftState) and not (ssShift in ShiftState) then
+ begin
+ CaretPos.Y := CaretPos.Y + 1;
+ if FVScrollBar.Visible then
+ FVScrollBar.LineDown; // VScrollBarMove(self, FVScrollBar.Position+1);
+ FSelStartNo := CaretPos.Y;
+ Exit;
+ end
+ else if not (ssCtrl in ShiftState) and (ssShift in ShiftState) then
+ begin
+ CaretPos.Y := CaretPos.Y + 1;
+ if not FSelected then
+ begin
+ FSelStartNo := CaretPos.Y - 1;
+ FSelStartOffs := CaretPos.X;
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := CaretPos.X;
+ FSelected := True;
+ end
+ else
+ begin
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := CaretPos.X;
+ FSelected := (FSelStartNo <> FSelEndNo) or (FSelStartOffs <> FSelEndOffs);
+ end;
+ end;
+ end;
+
+ keyHome:
+ begin
+ if not (ssCtrl in ShiftState) and not (ssShift in ShiftState) then
+ begin
+ CaretPos.X := 0;
+ if FSelected then
+ begin
+ FSelected := False;
+ Exit;
+ end;
+ end;
+ if ssCtrl in ShiftState then
+ begin
+ if ssShift in ShiftState then
+ begin
+ if not FSelected then
+ begin
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ FSelected := True;
+ end;
+ CaretPos.Y := 0;
+ CaretPos.X := 0;
+ FSelEndNo := 0;
+ FSelEndOffs := 0;
+ end
+ else
+ begin
+ CaretPos.Y := 0;
+ CaretPos.X := 0;
+ end;
+ Exit;
+ end;
+ if ssShift in ShiftState then
+ begin
+ if not FSelected then
+ begin
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ FSelected := True;
+ end;
+ CaretPos.X := 0;
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := 0;
+ if FSelEndNo = FSelStartNo then
+ FSelected := (FSelStartOffs <> FSelEndOffs);
+ end;
+ end;
+
+ keyEnd:
+ begin
+ if not (ssCtrl in ShiftState) and not (ssShift in ShiftState) then
+ begin
+ if CaretPos.Y <= pred(FLines.Count) then
+ CaretPos.X := Length(FLines[CaretPos.Y])
+ else
+ CaretPos.X := 0;
+ end;
+ if ssCtrl in ShiftState then
+ begin
+ if ssShift in ShiftState then
+ begin
+ if not FSelected then
+ begin
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ FSelected := True;
+ end;
+ CaretPos.Y := pred(FLines.Count);
+ CaretPos.X := Length(FLines[CaretPos.Y]);
+ FSelEndNo := pred(FLines.Count);
+ FSelEndOffs := Length(FLines[CaretPos.Y]);
+ end else
+ begin
+ CaretPos.Y := pred(FLines.Count);
+ CaretPos.X := Length(FLines[CaretPos.Y]);
+ end;
+ Exit;
+ end;
+ if ssShift in ShiftState then
+ begin
+ if not FSelected then
+ begin
+ FSelStartNo := CaretPos.Y;
+ if CaretPos.Y <= pred(FLines.Count) then
+ if CaretPos.X > Length(FLines[CaretPos.Y]) then
+ CaretPos.X := Length(FLines[CaretPos.Y]);
+ FSelStartOffs := CaretPos.X;
+ FSelected := True;
+ end;
+ if CaretPos.Y <= pred(FLines.Count) then
+ CaretPos.X := Length(FLines[CaretPos.Y])
+ else
+ CaretPos.X := 0;
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := CaretPos.X;
+ if FSelEndNo = FSelStartNo then
+ FSelected := (FSelStartOffs <> FSelEndOffs);
+ end;
+ end;
+
+ keyPageUp, keyPageDown:
+ begin
+ if not FSelected then
+ begin
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ end;
+ SaveXCaret := CaretPos.Y - FTopLine;
+ if AKeyCode = keyPageUp then
+ begin
+ if VPos = 0 then
+ begin
+ CaretPos.Y := 0;
+ CaretPos.X := 0;
+ end
+ else
+ begin
+ // scroll text
+ if FVScrollBar.Visible then
+ FVScrollBar.PageUp;
+ // restore caret at same line offset as before
+ CaretPos.Y := FTopLine + SaveXCaret;
+ end;
+ end
+ else
+ begin
+ if VPos > (FLines.Count - FVisLines) then
+ begin
+ CaretPos.Y := FLines.Count-1;
+ CaretPos.X := Length(FLines[CaretPos.Y]);
+ end
+ else
+ begin
+ // scroll text
+ if FVScrollBar.Visible then
+ FVScrollBar.PageDown;
+ // restore caret at same line offset as before
+ CaretPos.Y := FTopLine + SaveXCaret;
+ end;
+ end;
+ if ssShift in ShiftState then
+ begin
+ FSelEndNo := CaretPos.Y;
+ FSelEndOffs := CaretPos.X;
+ if not FSelected then
+ FSelected := True;
+ end;
+ end;
+ end;
+end;
+
+procedure TfpgBaseTextEdit.InitMemoObjects;
+begin
+ FGutterPan := TfpgGutter.CreateGutter(Self);
+ with FGutterPan do
+ begin
+ Left := -Width - 1;
+ Visible := False;
+ end;
+end;
+
+procedure TfpgBaseTextEdit.SetRightEdge(const AValue: Boolean);
+begin
+ if FRightEdge <> AValue then
+ begin
+ FRightEdge := AValue;
+ Invalidate;
+ end;
+end;
+
+procedure TfpgBaseTextEdit.SetRightEdgeCol(const AValue: Integer);
+var
+ v: Integer;
+begin
+ v := AValue;
+ if v < 20 then v := 20;
+ if v > 160 then v := 160;
+ if FRightEdgeCol <> v then
+ begin
+ FRightEdgeCol := v;
+ if FRightEdge then
+ Invalidate;
+ end;
+end;
+
+procedure TfpgBaseTextEdit.HandleShow;
+begin
+ inherited HandleShow;
+ HandleResize(Width, Height);
+end;
+
+procedure TfpgBaseTextEdit.HandleResize(AWidth, AHeight: TfpgCoord);
+begin
+ inherited HandleResize(AWidth, AHeight);
+ if HasHandle then
+ begin
+ UpdateCharBounds;
+ UpdateScrollBars;
+ UpdateGutterCoords;
+ end;
+end;
+
+procedure TfpgBaseTextEdit.HandlePaint;
+begin
+ Canvas.ClearClipRect;
+ if FLineChanged > -1 then
+ begin
+ { TODO: We would like Vertical and Underline cursor painting at some point }
+ DrawLine(FLineChanged, CaretPos.Y * FChrH);
+ FLineChanged := -1;
+ Exit;
+ end;
+
+ // normal house keeping
+ Canvas.Clear(clBoxColor);
+ fpgStyle.DrawControlFrame(Canvas, 0, 0, Width, Height);
+ Canvas.Font := FFont;
+ Canvas.SetClipRect(GetClientRect);
+
+ // do the actual drawing
+ DrawVisible;
+ DrawCaret(CaretPos.X, CaretPos.Y);
+ Canvas.ClearClipRect;
+
+ // The little square in the bottom right corner
+ if FHScrollBar.Visible and FVScrollBar.Visible then
+ begin
+ Canvas.SetColor(clButtonFace);
+ Canvas.FillRectangle(FHScrollBar.Left+FHScrollBar.Width,
+ FVScrollBar.Top+FVScrollBar.Height,
+ FVScrollBar.Width,
+ FHScrollBar.Height);
+ end;
+end;
+
+procedure TfpgBaseTextEdit.HandleMouseEnter;
+begin
+ inherited HandleMouseEnter;
+ MouseCursor := mcIBeam;
+end;
+
+procedure TfpgBaseTextEdit.HandleMouseExit;
+begin
+ inherited HandleMouseExit;
+ MouseCursor := mcDefault;
+end;
+
+procedure TfpgBaseTextEdit.HandleLMouseDown(x, y: integer; shiftstate: TShiftState);
+var
+ RNo: Integer;
+ CNo: Integer;
+begin
+ inherited HandleLMouseDown(x, y, shiftstate);
+ if FGutterPan.Visible and (X <= FGutterPan.Width) then Exit; //==>
+
+ GetRowColAtPos(X + HPos * FChrW, Y + VPos * FChrH, RNo, CNo);
+ CaretPos.X := CNo;
+ CaretPos.Y := RNo;
+ FSelDrag := False;
+ if (RNo in [FSelStartNo..FSelEndNo]) or (RNo in [FSelEndNo..FSelStartNo]) then
+ begin
+ if (FSelStartNo = FSelEndNo) and ((CNo in [FSelStartOffs..FSelEndOffs]) or
+ (CNo in [FSelEndOffs..FSelStartOffs])) then FSelDrag := True;
+ if FSelStartNo <> FSelEndNo then
+ begin
+ FSelDrag := True;
+ if (RNo = FSelStartNo) and (FSelStartNo < FSelEndNo) and (CNo < FSelStartOffs) then
+ FSelDrag := False;
+ if (RNo = FSelStartNo) and (FSelStartNo > FSelEndNo) and (CNo > FSelStartOffs) then
+ FSelDrag := False;
+ if (RNo = FSelEndNo) and (FSelStartNo < FSelEndNo) and (CNo > FSelStartOffs) then
+ FSelDrag := False;
+ if (RNo = FSelEndNo) and (FSelStartNo > FSelEndNo) and (CNo < FSelStartOffs) then
+ FSelDrag := False;
+ end;
+ end;
+ if FSelDrag then
+ begin
+// writeln(' SelDrag is True!!!!');
+// Exit; //==>
+ end;
+ if not (ssShift in ShiftState) then
+ begin
+ if FSelected then
+ begin
+ { Erase old selection, if any... }
+ FSelected := False;
+ end;
+ FSelStartNo := RNo;
+ FSelEndNo := FSelStartNo;
+ FSelStartOffs := CNo;
+ FSelEndOffs := FSelStartOffs;
+// FSelected := True;
+ FSelMouseDwn := True;
+ end
+ else
+ begin
+ FSelEndNo := RNo;
+ FSelEndOffs := CNo;
+ FSelected := True;
+ end;
+ Invalidate;
+end;
+
+function TfpgBaseTextEdit.calcmousewheeldelta(var info: TfpgMsgParmMouse;
+ const fmin,fmax,deltamin,deltamax: double): double;
+var
+ frequ: double;
+begin
+ if (FLastScrollEventTime <> 0) and (FLastScrollEventTime <> info.timestamp) then
+ begin
+ frequ := 0.00003 /(info.timestamp-FLastScrollEventTime); // Hz
+ {$IFDEF gDEBUG}
+ writeln('frequ = ', Format('%3.9f', [frequ]));
+ {$ENDIF}
+ if frequ > fmax then
+ begin
+ frequ := fmax;
+ end;
+ if frequ < fmin then
+ begin
+ frequ := fmin;
+ end;
+ result := (frequ*(deltamax-deltamin)+(deltamin*fmax-deltamax*fmin)) / (fmax-fmin);
+ end
+ else
+ begin
+ result := deltamin;
+ end;
+{
+ if d > 0 then // down
+ begin
+ d := - d;
+ end;
+}
+ {$IFDEF gDEBUG}
+ writeln('result = ', Format('%3.6f', [result]));
+ {$ENDIF}
+end;
+
+function TfpgBaseTextEdit.mousewheelacceleration(const avalue: double): double;
+var
+ info: TfpgMsgParmMouse;
+ d: double;
+begin
+ info.timestamp := FLastScrollEventTime + FLastScrollEventTime -
+ FLastScrollEventTimeBefore;
+ d := calcmousewheeldelta(info,fmousewheelfrequmin,fmousewheelfrequmax,1,
+ fmousewheelaccelerationmax);
+ result := avalue * d;
+end;
+
+function TfpgBaseTextEdit.mousewheelacceleration(const avalue: integer): integer;
+begin
+ result:= round(mousewheelacceleration(avalue*1.0));
+end;
+
+procedure TfpgBaseTextEdit.HandleMouseScroll(x, y: integer; shiftstate: TShiftState;
+ delta: smallint);
+var
+ msg: TfpgMessageParams;
+ ldelta: integer;
+begin
+ inherited HandleMouseScroll(x, y, shiftstate, delta);
+ fillchar(msg, sizeof(msg), 0); // zero out the record - initialize it
+ msg.mouse.x := x;
+ msg.mouse.y := y;
+ msg.mouse.shiftstate := shiftstate;
+
+ FLastScrollEventTimeBefore := FLastScrollEventTime;
+ FLastScrollEventTime := Now;
+
+ { calculate a modified delta based on mouse scroll sensitivity setting }
+ ldelta := round(mousewheelacceleration(delta*fwheelsensitivity));
+
+ msg.mouse.delta := ldelta;
+
+ fpgPostMessage(self, FVScrollBar, FPGM_SCROLL, msg);
+end;
+
+procedure TfpgBaseTextEdit.HandleKeyPress(var keycode: word;
+ var shiftstate: TShiftState; var consumed: boolean);
+var
+ SLine: TfpgString;
+ AddS: TfpgString;
+ Y: Integer;
+ X: Integer;
+ CaretScroll: Boolean;
+begin
+ {$IFDEF gDEBUG}
+ writeln('>> TfpgBaseTextEdit.HandleKeyPress');
+ {$ENDIF}
+// inherited HandleKeyPress(keycode, shiftstate, consumed);
+
+ { Add lines as we go, so we can cursor past EOF. }
+ { todo: This behaviour should be optional }
+ if CaretPos.Y > pred(FLines.Count) then
+ begin
+ FLines.Add('');
+ FVScrollBar.Max := FVScrollBar.Max + 1;
+ Exit; //==>
+ end;
+// if (keycode = keyEscape) or (ssCtrl in ShiftState) then
+// Exit; //==>
+
+ if FSelected then
+ begin
+ DeleteSelection;
+ if keycode = keyBackSpace then
+ Exit; //==>
+ end;
+ SLine := FLines[CaretPos.Y];
+
+ case keycode of
+ keyBackspace:
+ begin
+ if UTF8Length(SLine) >= CaretPos.X then
+ X := CaretPos.X
+ else
+ begin
+ X := UTF8Length(SLine);
+ CaretPos.X := X;
+ end;
+ UTF8Delete(SLine, X, 1);
+ FLines[CaretPos.Y] := SLine;
+ CaretPos.X := CaretPos.X - 1;
+ if CaretPos.X < 0 then
+ begin
+ if CaretPos.Y > 0 then
+ begin
+ AddS := FLines[CaretPos.Y]; { store any text from current line }
+ FLines.Delete(CaretPos.Y);
+ CaretPos.Y := CaretPos.Y - 1;
+ CaretPos.X := UTF8Length(FLines[CaretPos.Y]); { reposition cursor }
+ if AddS <> '' then
+ FLines[CaretPos.Y] := FLines[CaretPos.Y] + AddS; { add stored text to new current line }
+ end
+ else
+ begin
+ CaretPos.X := 0;
+ end;
+ end;
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ end;
+
+ keyTab:
+ begin
+ end;
+
+ keyReturn:
+ begin
+ AddS := '';
+ if UTF8Length(SLine) > CaretPos.X then
+ begin
+ AddS := Copy(SLine, CaretPos.X + 1, Length(SLine) - CaretPos.X + 1);
+ Delete(SLine, CaretPos.X + 1, Length(SLine) - CaretPos.X);
+ FLines[CaretPos.Y] := SLine;
+ end;
+ if CaretPos.Y = pred(FLines.Count) then
+ FLines.Add(AddS)
+ else
+ if CaretPos.Y < pred(FLines.Count) then
+ FLines.Insert(CaretPos.Y + 1, AddS)
+ else
+ if CaretPos.Y > FLines.Count then
+ FLines.Add(''); { ??? }
+ CaretPos.Y := CaretPos.Y + 1;
+ CaretPos.X := 0;
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ end;
+
+ keyLeft, keyRight, keyUp, keyDown, keyHome, keyEnd, keyPrior, keyNext:
+ begin
+ KeyboardCaretNav(ShiftState, keycode);
+ CaretScroll := True;
+ end;
+ end;
+
+ if CaretPos.X > HPos + FVisCols then
+ ScrollPos_H := CaretPos.X - FVisCols
+ else if CaretPos.X < HPos then
+ ScrollPos_H := CaretPos.X;
+
+ if CaretPos.Y < (FTopLine+1) then
+ ScrollPos_V := CaretPos.Y
+ else if CaretPos.Y > (FTopLine + FVisLines - 2) then
+ ScrollPos_V := CaretPos.Y - FVisLines + 2;
+
+ Invalidate;
+ {$IFDEF gDEBUG}
+ writeln('<< TfpgBaseTextEdit.HandleKeyPress');
+ {$ENDIF}
+end;
+
+procedure TfpgBaseTextEdit.HandleKeyChar(var AText: TfpgChar;
+ var shiftstate: TShiftState; var consumed: boolean);
+var
+ SLine: TfpgString;
+ Fill: Integer;
+begin
+ {$IFDEF gDEBUG}
+ writeln('>> TfpgBaseTextEdit.HandleKeyChar');
+ {$ENDIF}
+ if not consumed then
+ begin
+ // Handle only printable characters
+ // UTF-8 characters beyond ANSI range are supposed to be printable
+ if ((Ord(AText[1]) > 31) and (Ord(AText[1]) < 127)) or (Length(AText) > 1) then
+ begin
+ SLine := FLines[CaretPos.Y];
+
+ { cursor was somewhere in whitespace, so we need to fill up the spaces }
+ if UTF8Length(SLine) < CaretPos.X + 1 then
+ for Fill := Length(SLine) to CaretPos.X + 1 do
+ SLine := SLine + ' ';
+
+ UTF8Insert(AText, SLine, CaretPos.X + 1);
+ FLines[CaretPos.Y] := SLine;
+ CaretPos.X := CaretPos.X + 1;
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ consumed := True;
+ end;
+ end;
+
+ if consumed then
+ RePaint
+ else
+ inherited HandleKeyChar(AText, shiftstate, consumed);
+ {$IFDEF gDEBUG}
+ writeln('<< TfpgBaseTextEdit.HandleKeyChar');
+ {$ENDIF}
+end;
+
+procedure TfpgBaseTextEdit.DrawVisible;
+var
+ I, Y, cntVis: Integer;
+begin
+ Y := 0;
+ cntVis := 1;
+ GetSelBounds(StartNo, EndNo, StartOffs, EndOffs);
+
+ // Draw right edge line first, so text can draw over it.
+ if FRightEdge then
+ begin
+ if not FGutterPan.Visible then
+ begin
+ with Canvas do
+ begin
+ Canvas.Color := clShadow1; // FEnvironment.RightEdgeColor;
+ Canvas.DrawLine((FRightEdgeCol * FChrW) - (HPos * FChrW), GetClientRect.Top, (FRightEdgeCol * FChrW) - (HPos * FChrW), GetClientRect.Height);
+ end;
+ end else
+ with Canvas do
+ begin
+ Canvas.Color := clShadow1; // FEnvironment.RightEdgeColor;
+ Canvas.DrawLine((FRightEdgeCol * FChrW) - (HPos * FChrW) + FGutterPan.Width, GetClientRect.Top, (FRightEdgeCol * FChrW) - (HPos * FChrW) + FGutterPan.Width, GetClientRect.Height);
+ end;
+ end;
+
+ // Draw lines of text
+ for I := FTopLine to FTopLine + FVisLines do
+ begin
+ DrawLine(I, Y);
+ Y := Y + FChrH;
+ cntVis := cntVis + 1;
+ if cntVis > FVisLines then
+ Break; //==>
+ end;
+end;
+
+procedure TfpgBaseTextEdit.DrawLine(const ALineIndex, Y: Integer);
+var
+ X: Integer;
+ GSz: Integer;
+begin
+ if FGutterPan.Visible then
+ begin
+ GSz := FGutterPan.Width + GetClientRect.Left + 1;
+ if FGutterPan.ShowNum and (FGutterPan.StartNum <> FTopLine + 1) then
+ begin
+ FGutterPan.StartNum := FTopLine + 1;
+ FGutterPan.Invalidate;
+ end;
+ end
+ else
+ GSz := GetClientRect.Left + 1; // gutter size if no gutter panel
+
+ if ALineIndex < FLines.Count then
+ begin
+ X := -(HPos * FChrW) + GSz;
+ FormatLine(ALineIndex, X, Y);
+ end;
+end;
+
+procedure TfpgBaseTextEdit.FormatLine(const ALineIndex, X, Y: Integer);
+var
+ S, CorrectS, SS: TfpgString;
+ TI, Si, Ei, T: Integer;
+ R: TfpgRect;
+ AllowDraw: Boolean;
+begin
+ if FLines.Count = 0 then
+ Exit; //==>
+ if (ALineIndex < 0) or (ALineIndex > FLines.Count-1) then
+ Exit; //==>
+ S := FLines[ALineIndex];
+ if Pos(#9, S) > 0 then
+ begin
+ CorrectS := '';
+ for TI := 1 to Length(S) do // no need to use utf8 version here
+ begin
+ if S[TI] = #9 then
+ begin
+ for T := 1 to FTabWidth do
+ CorrectS := CorrectS + ' ';
+ end
+ else
+ CorrectS := CorrectS + S[TI];
+ end;
+ S := CorrectS;
+ end; { if }
+
+ { start drawing formatted text }
+ R.SetRect(X, Y, UTF8Length(S) * FChrW, FChrH);
+ AllowDraw := True;
+ { end-user can hook in here to do syntax highlighting and other custom drawing }
+ if Assigned(FOnDrawLine) then
+ FOnDrawLine(self, S, ALineIndex, Canvas, R, AllowDraw);
+ { Draw simple text line... }
+ if AllowDraw then
+ begin
+ Canvas.DrawText(R, S);
+ end;
+ if FSelected then
+ begin
+ Canvas.TextColor := clWhite;
+ Canvas.Color := fpgColorToRGB(clSelection);
+ if (ALineIndex > StartNo) and (ALineIndex < EndNo) then // whole line is selected
+ begin
+ R.SetRect(X, Y, UTF8Length(S) * FChrW, FChrH);
+ Canvas.FillRectangle(R);
+ Canvas.DrawText(R, S);
+ end
+ else
+ begin
+ Ei := EndOffs;
+ Si := StartOffs;
+ if (ALineIndex = StartNo) and (ALineIndex = EndNo) then // start/end selection on same line
+ begin
+ SS := UTF8Copy(S, Si + 1, UTF8Length(S) - Si);
+ if Ei > UTF8Length(S) then
+ SS := UTF8Copy(S, Si + 1, UTF8Length(S) - Si)
+ else
+ SS := UTF8Copy(S, Si + 1, Ei - Si);
+ R.SetRect(X+(Si * FChrW), Y, (UTF8Length(SS) * FChrW), FChrH);
+ Canvas.FillRectangle(R);
+ Canvas.DrawText(R, SS);
+ end
+ else
+ begin
+ if (ALineIndex = StartNo) and (ALineIndex < EndNo) then
+ begin
+ SS := UTF8Copy(S, Si + 1, UTF8Length(S) - Si);
+ R.SetRect(X+(Si * FChrW), Y, (UTF8Length(SS) * FChrW), FChrH);
+ Canvas.FillRectangle(R);
+ Canvas.DrawText(R, SS);
+ end
+ else
+ begin
+ if (ALineIndex > StartNo) and (ALineIndex = EndNo) then
+ begin
+ if Ei > UTF8Length(S) then
+ Ei := UTF8Length(S);
+ SS := UTF8Copy(S, 1, Ei);
+ R.SetRect(X, Y, (UTF8Length(SS) * FChrW), FChrH);
+ Canvas.FillRectangle(R);
+ Canvas.DrawText(R, SS);
+ end;
+ end;
+ end;
+ end;
+ end; { if FSelected... }
+// end; { if AllowDraw... }
+
+ if UTF8Length(S) > FMaxScrollH then
+ begin
+ FMaxScrollH := UTF8Length(S);
+ UpdateScrollBars;
+ end;
+end;
+
+procedure TfpgBaseTextEdit.DrawCaret(const X, Y: Integer);
+var
+ Xp, Yp: Integer;
+begin
+ if csDesigning in ComponentState then
+ Exit; //==>
+
+ {$IFDEF gDEBUG}
+ writeln('X:', X, ' Y:', Y, ' FTopLine:', FTopLine, ' HPos:', HPos, ' VPos:', VPos);
+ {$ENDIF}
+
+ if (Y < FTopLine) or (Y > FTopLine + FVisLines) then
+ begin
+ fpgCaret.UnSetCaret(Canvas);
+ Exit; //==>
+ end;
+ Yp := ((Y - FTopLine) * FChrH) + 1;
+ Xp := ((X - HPos) * FChrW) + GetClientRect.Left;
+
+ if FGutterPan.Visible then
+ Xp := Xp + FGutterPan.Width;
+ if (Xp < 0) or (Xp > GetClientRect.Width) then
+ begin
+ fpgCaret.UnSetCaret(Canvas);
+ Exit; //==>
+ end;
+ //with Canvas do
+ //begin
+ //if ShowCaret then
+ //begin
+ //Pen.Mode := pmNotMerge;
+ //Pen.Color := Font.Color;
+ //end else
+ //begin
+ //if not FSelected then
+ //Pen.Color := Self.Color
+ //else
+ //Pen.Color := FEnvironment.SelectionBackground;
+ //Pen.Mode := pmCopy;
+ //end;
+ //MoveTo(Xp, Yp);
+ //LineTo(Xp, Yp + FChrH);
+ //Pen.Mode := pmCopy;
+ //end;
+ if Focused then
+ fpgCaret.SetCaret(Canvas, Xp, Yp, fpgCaret.Width, FFont.Height)
+ else
+ fpgCaret.UnSetCaret(Canvas);
+
+ if not FSelected then
+ begin
+ FSelStartNo := CaretPos.Y;
+ FSelStartOffs := CaretPos.X;
+ end;
+end;
+
+constructor TfpgBaseTextEdit.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ Focusable := True;
+ FFont := fpgGetFont('#Edit1');
+ Width := 320;
+ Height := 240;
+ FLines := TStringList.Create;
+ CaretPos.x := 0;
+ CaretPos.y := 0;
+ FTopLine := 0;
+ FTabWidth := 8;
+ FMaxScrollH := 1;
+ VPos := 0;
+ HPos := 0;
+ FTracking := True;
+ FFullRedraw := False;
+ FSelected := False;
+ FRightEdge := False;
+ FRightEdgeCol := 80;
+ FLineChanged := -1;
+
+ fmousewheelfrequmin := 1;
+ fmousewheelfrequmax := 100;
+ fmousewheeldeltamin := 0.05;
+ fmousewheeldeltamax := 30;
+ fmousewheelaccelerationmax := 30;
+ fwheelsensitivity := 1.5;
+
+ FLastScrollEventTime := 0;
+ FLastScrollEventTimeBefore := 0;
+
+ FVScrollBar := TfpgScrollBar.Create(self);
+ FVScrollBar.Orientation := orVertical;
+ FVScrollBar.OnScroll := @VScrollBarMove;
+ FVScrollBar.Visible := False;
+
+ FHScrollBar := TfpgScrollBar.Create(self);
+ FHScrollBar.Orientation := orHorizontal;
+ FHScrollBar.OnScroll := @HScrollBarMove;
+// FHScrollBar.ScrollStep := 5;
+ FHScrollBar.Visible := False;
+
+ InitMemoObjects;
+end;
+
+destructor TfpgBaseTextEdit.Destroy;
+begin
+ FLines.Free;
+ FFont.Free;
+ inherited Destroy;
+end;
+
+function TfpgBaseTextEdit.GetClientRect: TfpgRect;
+begin
+ // widget has a 2 pixel 3D border
+ Result.SetRect(2, 2, Width-4, Height-4);
+ if FVScrollBar.Visible then
+ Result.Width := Result.Width - FVScrollBar.Width;
+ if FHScrollBar.Visible then
+ Result.Height := Result.Height - FHScrollBar.Height;
+end;
+
+function TfpgBaseTextEdit.GetWordAtPos(const X, Y: Integer; out XBegin: Integer): TfpgString;
+{ todo: This needs to be made UTF8 compliant! It currently is not. }
+const
+ ValidChars = ['a'..'z', 'A'..'Z', '0'..'9', '#'];
+var
+ S: TfpgString;
+ C: Char;
+ I, Si, Ei, CrX: Integer;
+ lX: integer;
+begin
+ Result := '';
+ XBegin := -1;
+ Si := 0;
+ Ei := 0;
+ lX := X;
+ if Y > pred(FLines.Count) then Exit; //==>
+ S := FLines[Y];
+ if S = '' then Exit; //==>
+ if lX > UTF8Length(S) - 1 then
+ lX := UTF8Length(S) - 1;
+ if not (S[lX + 1] in ValidChars) then
+ begin
+ CrX := lX - 1;
+ for I := CrX downto 1 do
+ begin
+ C := S[I + 1];
+ if (C in ValidChars) then
+ begin
+ lX := I;
+ Break;
+ end;
+ end;
+ if lX = 0 then Exit; //==>
+ end;
+ for I := (lX + 1) downto 1 do
+ if S[I] in ValidChars then
+ Si := I
+ else
+ Break;
+ for I := (lX + 1) to Length(S) do
+ if S[I] in ValidChars then
+ Ei := I + 1
+ else
+ Break;
+ if Ei >= Si then
+ begin
+ Result := UTF8Copy(S, Si, Ei - Si);
+ XBegin := Si - 1;
+ end;
+end;
+
+procedure TfpgBaseTextEdit.GetRowColAtPos(const X, Y: Integer; out Row, Col: Integer);
+var
+ Fine: Integer;
+ lX: Integer;
+begin
+ Row := Y div FChrH;
+ if Row > Flines.Count then
+ Row := FLines.Count;
+
+ lX := X - GetClientRect.Left;
+ if FGutterPan.Visible then
+ begin
+ if lX < FGutterPan.Width then
+ lX := FGutterPan.Width;
+ Col := (lX - FGutterPan.Width) div FChrW;
+ Fine := (lX - FGutterPan.Width) mod FChrW;
+ end
+ else
+ begin
+ if lX < 0 then
+ lX := 0;
+ Col := lX div FChrW;
+ Fine := lX mod FChrW;
+ end;
+ if Fine > (FChrW div 2) - 1 then
+ Col := Col + 1;
+end;
+
+procedure TfpgBaseTextEdit.Clear;
+begin
+ CaretPos.x := 0;
+ CaretPos.y := 0;
+ ScrollTo(0, 0);
+ FSelStartNo := 0;
+ FSelStartOffs := 0;
+ FSelEndNo := 0;
+ FSelEndOffs := 0;
+ FLines.Clear;
+ FSelected := False;
+ Invalidate;
+end;
+
+procedure TfpgBaseTextEdit.ScrollTo(X, Y: Integer);
+begin
+ SetVPos(Y div FChrH);
+ SetHPos(X div FChrW);
+end;
+
+procedure TfpgBaseTextEdit.GotoLine(ALine: integer);
+begin
+ CaretPos.X := 0;
+ CaretPos.Y := ALine;
+ ScrollPos_V := ALine-5; // scrolling a few lines short so cursor is not on top line
+end;
+
+procedure TfpgBaseTextEdit.DeleteSelection;
+begin
+ { TODO : Implement DeleteSelection }
+end;
+
+procedure TfpgBaseTextEdit.SaveToFile(const AFileName: TfpgString);
+var
+ BuffList: TStringList;
+ SLine: TfpgString;
+ I, P: Integer;
+ Replace: Boolean;
+begin
+ BuffList := TStringList.Create;
+ try
+ BuffList.Assign(FLines);
+ for I := 0 to pred(BuffList.Count) do
+ begin
+ SLine := BuffList[I];
+ P := UTF8Length(SLine);
+ Replace := (P > 0) and (SLine <> '');
+ if Replace then
+ begin
+ while (fpgCharAt(SLine, P) = ' ') do
+ begin
+ UTF8Delete(SLine, P, 1);
+ P := UTF8Length(SLine);
+ end;
+ BuffList[I] := SLine;
+ end;
+ end;
+ BuffList.SaveToFile(AFileName);
+ finally
+ BuffList.Free;
+ end;
+end;
+
+procedure TfpgBaseTextEdit.LoadFromFile(const AFileName: TfpgString);
+begin
+ if not fpgFileExists(AFileName) then
+ Exit; //==>
+ Clear;
+ FLines.LoadFromFile(AFileName);
+ HandleResize(Width, Height);
+ Invalidate;
+end;
+
+
+end.
+
diff --git a/examples/apps/ide/src/fpgide.lpi b/examples/apps/ide/src/fpgide.lpi
new file mode 100644
index 00000000..2e877727
--- /dev/null
+++ b/examples/apps/ide/src/fpgide.lpi
@@ -0,0 +1,150 @@
+<?xml version="1.0"?>
+<CONFIG>
+ <ProjectOptions>
+ <Version Value="9"/>
+ <General>
+ <Flags>
+ <SaveOnlyProjectUnits Value="True"/>
+ <MainUnitHasCreateFormStatements Value="False"/>
+ <MainUnitHasTitleStatement Value="False"/>
+ </Flags>
+ <SessionStorage Value="InProjectDir"/>
+ <MainUnit Value="0"/>
+ <UseAppBundle Value="False"/>
+ </General>
+ <VersionInfo>
+ <Language Value=""/>
+ <CharSet Value=""/>
+ </VersionInfo>
+ <BuildModes Count="1">
+ <Item1 Name="default" Default="True"/>
+ </BuildModes>
+ <PublishOptions>
+ <Version Value="2"/>
+ <IgnoreBinaries Value="False"/>
+ <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
+ <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
+ </PublishOptions>
+ <RunParams>
+ <local>
+ <FormatVersion Value="1"/>
+ <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
+ </local>
+ </RunParams>
+ <RequiredPackages Count="1">
+ <Item1>
+ <PackageName Value="fpgui_toolkit"/>
+ </Item1>
+ </RequiredPackages>
+ <Units Count="16">
+ <Unit0>
+ <Filename Value="fpgide.lpr"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="fpgide"/>
+ </Unit0>
+ <Unit1>
+ <Filename Value="frm_main.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="frm_main"/>
+ </Unit1>
+ <Unit2>
+ <Filename Value="frm_configureide.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="frm_configureide"/>
+ </Unit2>
+ <Unit3>
+ <Filename Value="ideconst.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="ideconst"/>
+ </Unit3>
+ <Unit4>
+ <Filename Value="idemacros.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="idemacros"/>
+ </Unit4>
+ <Unit5>
+ <Filename Value="frm_debug.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="frm_debug"/>
+ </Unit5>
+ <Unit6>
+ <Filename Value="project.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="Project"/>
+ </Unit6>
+ <Unit7>
+ <Filename Value="unitlist.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="UnitList"/>
+ </Unit7>
+ <Unit8>
+ <Filename Value="frm_projectoptions.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="frm_projectoptions"/>
+ </Unit8>
+ <Unit9>
+ <Filename Value="ideutils.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="ideutils"/>
+ </Unit9>
+ <Unit10>
+ <Filename Value="builderthread.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="BuilderThread"/>
+ </Unit10>
+ <Unit11>
+ <Filename Value="ideimages.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="ideimages"/>
+ </Unit11>
+ <Unit12>
+ <Filename Value="stringhelpers.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="stringhelpers"/>
+ </Unit12>
+ <Unit13>
+ <Filename Value="frm_procedurelist.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="frm_procedurelist"/>
+ </Unit13>
+ <Unit14>
+ <Filename Value="mpaslex.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="mPasLex"/>
+ </Unit14>
+ <Unit15>
+ <Filename Value="filemonitor.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="filemonitor"/>
+ </Unit15>
+ </Units>
+ </ProjectOptions>
+ <CompilerOptions>
+ <Version Value="9"/>
+ <Target>
+ <Filename Value="fpgide.elf32"/>
+ </Target>
+ <SearchPaths>
+ <IncludeFiles Value="$(ProjOutDir)"/>
+ <UnitOutputDirectory Value="units/$(TargetCPU)-$(TargetOS)"/>
+ </SearchPaths>
+ <Parsing>
+ <SyntaxOptions>
+ <AllowLabel Value="False"/>
+ <CPPInline Value="False"/>
+ <UseAnsiStrings Value="False"/>
+ </SyntaxOptions>
+ </Parsing>
+ <Linking>
+ <Debugging>
+ <UseHeaptrc Value="True"/>
+ </Debugging>
+ </Linking>
+ <Other>
+ <CompilerMessages>
+ <UseMsgFile Value="True"/>
+ </CompilerMessages>
+ <CompilerPath Value="$(CompPath)"/>
+ </Other>
+ </CompilerOptions>
+</CONFIG>
diff --git a/examples/apps/ide/src/fpgide.lpr b/examples/apps/ide/src/fpgide.lpr
new file mode 100644
index 00000000..ad9827d1
--- /dev/null
+++ b/examples/apps/ide/src/fpgide.lpr
@@ -0,0 +1,33 @@
+program fpgide;
+
+{$mode objfpc}{$H+}
+
+uses
+ {$IFDEF UNIX}
+ cthreads,
+ {$ENDIF}
+ Classes, fpg_base, fpg_main, frm_main, frm_configureide, ideconst, idemacros,
+ frm_debug, project, unitlist, frm_projectoptions, ideutils, builderthread,
+ ideimages, stringhelpers, frm_procedurelist, mPasLex, filemonitor;
+
+
+procedure MainProc;
+var
+ frm: TMainForm;
+begin
+// FPG_DEFAULT_FONT_DESC := 'DejaVu Sans-9';
+ fpgApplication.Initialize;
+ RegisterIDEImages;
+ frm := TMainForm.Create(nil);
+ try
+ frm.Show;
+ fpgApplication.Run;
+ finally
+ frm.Free;
+ end;
+end;
+
+begin
+ MainProc;
+end.
+
diff --git a/examples/apps/ide/src/fpgide.prj b/examples/apps/ide/src/fpgide.prj
new file mode 100644
index 00000000..5c6e06a5
--- /dev/null
+++ b/examples/apps/ide/src/fpgide.prj
@@ -0,0 +1,1387 @@
+[componentpalette]
+order0=0
+order1=0
+order2=0
+order3=0
+order4=0
+order5=0
+order6=0
+order7=0
+order8=0
+order9=0
+order10=0
+order11=0
+order12=0
+order13=0
+[projectoptions]
+projectdir=/media/flash16gig/programming/fpgide/src
+projectfilename=/media/flash16gig/programming/fpgide/src/fpgide.prj
+findinfiledialog=40
+ [findinfileadialogfo.subdirs]
+ value=1
+ [findinfileadialogfo.inopenfiles]
+ value=0
+ [findinfileadialogfo.wholeword]
+ value=0
+ [findinfileadialogfo.casesensitive]
+ value=0
+ [findinfileadialogfo.indirectories]
+ value=1
+ [findinfileadialogfo.mask]
+ value="*.pas" "*.pp" "*.inc"
+ history=1
+ "*.pas" "*.pp" "*.inc"
+ [findinfileadialogfo.dir]
+ filenames=1
+ /media/flash16gig/programming/msegui_svn/lib/common/
+ filehistory=4
+ /media/flash16gig/programming/msegui_svn/lib/common/
+ /media/flash16gig/programming/Kylix_addons/
+ /media/flash16gig/programming/fpgide/src/
+ /home/graemeg/programming/lazarus/ide/
+ filefilterindex=0
+ filefilter="*.pas" "*.pp" "*.inc"
+ filecolwidth=174
+ [findinfileadialogfo.findtext]
+ value=mousewheelacceleration
+ history=6
+ mousewheelacceleration
+ CLK_TCK
+ TBuilderThread
+ Output.Read
+ LoadSettings
+
+ [findinfileadialogfo]
+ stackedunder=
+ x=268
+ y=415
+ cx=406
+ cy=296
+finddialog=8
+ [finddialogfo.selectedonly]
+ value=0
+ [finddialogfo]
+ stackedunder=
+ x=319
+ y=205
+ cx=363
+ cy=118
+options=99
+ [projectoptionsfo.twidgetgrid3]
+ propcolwidthref=732
+ sortdescent0=0
+ sortdescent1=0
+ sortdescent2=0
+ width3=128
+ sortdescent3=0
+ width4=352
+ sortdescent4=0
+ width5=202
+ sortdescent5=0
+ [projectoptionsfo.twidgetgrid4]
+ propcolwidthref=543
+ width0=96
+ sortdescent0=0
+ width1=13
+ sortdescent1=0
+ width2=73
+ sortdescent2=0
+ width3=260
+ sortdescent3=0
+ width4=273
+ sortdescent4=0
+ sortcol=-1
+ col=-1073741823
+ row=-1073741823
+ rowheight=19
+ [projectoptionsfo.newfile]
+ firsttab=0
+ index=2
+ [projectoptionsfo.fontaliasgrid]
+ propcolwidthref=402
+ width0=98
+ sortdescent0=0
+ width1=391
+ sortdescent1=0
+ width2=30
+ sortdescent2=0
+ width3=50
+ sortdescent3=0
+ width4=50
+ sortdescent4=0
+ width5=50
+ sortdescent5=0
+ width6=70
+ sortdescent6=0
+ [projectoptionsfo.macrosplitter]
+ x=0
+ y=182
+ xprop=1
+ yprop=0.35922330097087
+ [projectoptionsfo.macrogrid]
+ propcolwidthref=526
+ sortdescent0=0
+ sortdescent1=0
+ sortdescent2=0
+ sortdescent3=0
+ sortdescent4=0
+ sortdescent5=0
+ width6=146
+ sortdescent6=0
+ width7=514
+ sortdescent7=0
+ [projectoptionsfo.makegroupbox]
+ firsttab=0
+ index=2
+ [projectoptionsfo.exceptionsgrid]
+ propcolwidthref=703
+ width0=47
+ sortdescent0=0
+ width1=697
+ sortdescent1=0
+ [projectoptionsfo.ttabwidget1]
+ firsttab=0
+ index=0
+ [projectoptionsfo.grid]
+ propcolwidthref=504
+ width0=219
+ sortdescent0=0
+ width1=497
+ sortdescent1=0
+ [projectoptionsfo.filefiltergrid]
+ propcolwidthref=611
+ width0=112
+ sortdescent0=0
+ width1=604
+ sortdescent1=0
+ [projectoptionsfo.ttabwidget2]
+ firsttab=0
+ index=0
+ [projectoptionsfo.tabwidget]
+ firsttab=0
+ index=0
+ [projectoptionsfo]
+ stackedunder=
+ x=151
+ y=68
+ cx=752
+ cy=572
+settings=8
+ [settingsfo]
+ x=88
+ y=108
+ cx=415
+ cy=604
+ wsize=0
+ active=1
+ visible=1
+copymessages=0
+closemessages=1
+checkmethods=1
+showgrid=1
+snaptogrid=1
+moveonfirstclick=1
+gridsizex=8
+gridsizey=8
+autoindent=1
+blockindent=2
+rightmarginon=1
+rightmarginchars=80
+scrollheight=0
+tabstops=2
+spacetabs=1
+tabindent=0
+editfontname=DejaVu Sans Mono
+editfontheight=15
+editfontwidth=0
+editfontextraspace=0
+editfontcolor=-1879048183
+editbkcolor=-1879048186
+statementcolor=14745599
+editfontantialiased=1
+editmarkbrackets=1
+backupfilecount=0
+encoding=0
+codetemplatedirs=1
+ ${TEMPLATEDIR}
+usercolors=30
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+usercolorcomment=30
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+stoponexception=0
+valuehints=1
+activateonbreak=1
+showconsole=1
+externalconsole=0
+gdbdownload=0
+downloadalways=0
+startupbkpt=0
+startupbkpton=0
+gdbsimulator=0
+gdbserverwait=0
+nogdbserverexit=0
+exceptclassnames=1
+ EconvertError
+exceptignore=1
+ 0
+settingsfile=/media/flash16gig/programming/fpgide/src/fpgide_settings.prj
+sigsettings=27
+ 1,1,T,F
+ 3,3,T,F
+ 4,4,T,F
+ 6,6,T,F
+ 7,7,T,F
+ 8,8,T,F
+ 9,9,T,F
+ 10,10,T,F
+ 11,11,T,F
+ 12,12,T,F
+ 13,13,T,F
+ 15,15,T,F
+ 16,16,T,F
+ 17,17,F,F
+ 18,18,T,F
+ 19,19,T,F
+ 20,20,T,F
+ 21,21,T,F
+ 22,22,T,F
+ 23,23,T,F
+ 24,24,T,F
+ 25,25,T,F
+ 26,26,T,F
+ 27,27,T,F
+ 28,28,T,F
+ 29,29,T,F
+ 30,30,T,F
+modulenames=0
+moduletypes=0
+modulefiles=0
+mainfile=${PROJECTNAME}.lpr
+targetfile=${PROJECTNAME}${EXEEXT}
+messageoutputfile=
+makecommand=${COMPILER}
+makedir=
+debugcommand=${DEBUGGER}
+debugoptions=
+debugtarget=
+runcommand=
+remoteconnection=
+uploadcommand=
+gdbprocessor=i386
+gdbservercommand=
+gdbservercommandattach=
+beforeload=
+afterload=
+beforerun=
+defaultmake=4
+befcommand=0
+befcommandon=0
+aftcommand=0
+aftcommandon=0
+makeoptions=5
+ -l -Mobjfpc -Sh
+ -gl -O-
+ -B
+ -O2 -XX -Xs -CX
+ -FUunits/${TargetCPU}-${TargetOS}/
+makeoptionson=5
+ 63
+ 31
+ 34
+ 32
+ 63
+macroon=6
+ 1
+ 3
+ 6
+ 4
+ 63
+ 63
+macronames=6
+ TargetCPU
+ TargetOS
+ TargetCPU
+ TargetOS
+ FPGUI_DIR
+ HelpPath
+macrovalues=6
+ x86_64
+ linux
+ i386
+ win32
+ /home/graemeg/programming/fpgui
+ /home/graemeg/programming/FPC_Docs/inf/
+macrogroup=1
+groupcomments=6
+ Linux 64-bit
+ Linux 32-bit
+ Windows 32-bit
+
+
+
+sourcedirs=4
+ ${FPGUI_DIR}/src/corelib/gdi/X/
+ ${FPGUI_DIR}/src/corelib/x11/
+ ${FPGUI_DIR}/src/*/
+ ./
+defines=0
+defineson=0
+unitdirs=6
+ ${FPGUI_DIR}/lib/${TargetCPU}-${TargetOS}/
+ ${FPGUI_DIR}/src/
+ ${FPGUI_DIR}/src/corelib/gdi/
+ ${FPGUI_DIR}/src/corelib/x11/
+ ${FPGUI_DIR}/src/gui/
+ ${FPGUI_DIR}/src/corelib/
+unitdirson=6
+ 65537
+ 131102
+ 196616
+ 196614
+ 65566
+ 65566
+unitpref=-Fu
+incpref=-Fi
+libpref=-Fl
+objpref=-Fo
+targpref=-o
+sourcefilemasks=5
+ "*.pas" "*.dpr" "*.pp" "*.inc" "*.lpr"
+ "*.c" "*.cc" "*.h"
+ "*.mfm"
+ "*.ipf"
+ "*.sql"
+syntaxdeffiles=5
+ ${SYNTAXDEFDIR}pascal_dark.sdef
+ ${SYNTAXDEFDIR}cpp.sdef
+ ${SYNTAXDEFDIR}objecttext.sdef
+ ${SYNTAXDEFDIR}ipf2.sdef
+ ${SYNTAXDEFDIR}sql.sdef
+filemasknames=5
+ Source
+ Forms
+ Text
+ IPF help
+ All Files
+filemasks=5
+ "*.pp" "*.pas" "*.inc" "*.dpr" "*.lpr"
+ *.mfm
+ *.txt
+ *.ipf
+ *
+toolsave=2
+ -1
+ 0
+toolhide=2
+ 0
+ 0
+toolparse=2
+ 0
+ 0
+toolmenus=2
+ fpGUI &UI Designer
+ fpGUI &DocView
+toolfiles=2
+ ${FPGUI_DIR}/uidesigner/units/${TargetCPU}-${TargetOS}/uidesigner
+ ${FPGUI_DIR}/docview/src/units/${TargetCPU}-${TargetOS}/docview
+toolparams=2
+ ${CURSOURCEFILE}
+ ${HelpPath} -k ${cursword}
+fontalias=0
+fontancestors=0
+fontnames=0
+fontheights=0
+fontwidths=0
+fontoptions=0
+fontxscales=0
+scriptbeforecopy=
+scriptaftercopy=
+newprojectfiles=4
+ ${TEMPLATEDIR}/fpgui_single_unit/project.pas
+ ${TEMPLATEDIR}fpgui/units/i386-linux/placeholder.txt
+ ${TEMPLATEDIR}fpgui/units/i386-win32/placeholder.txt
+ ${TEMPLATEDIR}fpgui/units/x86_64-linux/placeholder.txt
+newprojectfilesdest=4
+ ${%PROJECTNAME%}.pas
+ ${%PROJECTDIR%}units/i386-linux/placeholder.txt
+ ${%PROJECTDIR%}units/i386-win32/placeholder.txt
+ ${%PROJECTDIR%}units/x86_64-linux/placeholder.txt
+expandprojectfilemacros=4
+ 1
+ -1
+ -1
+ -1
+loadprojectfile=4
+ 1
+ 0
+ 0
+ 0
+newfinames=3
+ Program
+ Unit
+ Textfile
+newfinfilters=3
+ "*.pas" "*.pp"
+ "*.pas" "*.pp"
+
+newfiexts=3
+ pas
+ pas
+
+newfisources=3
+ ${TEMPLATEDIR}default/program.pas
+ ${TEMPLATEDIR}default/unit.pas
+
+newfonames=2
+ Mainform
+ Simple Form
+newfonamebases=2
+
+
+newinheritedforms=2
+ 0
+ 0
+newfosources=2
+ ${TEMPLATEDIR}fpgui/mainform.pas
+ ${TEMPLATEDIR}fpgui/simpleform.pas
+newfoforms=2
+
+
+[breakpoints]
+on=1
+ -1
+path=1
+ /media/flash16gig/programming/fpgide/src/fpg_textedit.pas
+line=1
+ 1257
+address=1
+ 135547161
+addbkpt=1
+ 0
+ignore=1
+ 2
+condition=1
+
+panels=0
+units=
+ (
+ modulefilenames=0
+ modulenames=0
+ moduleclassnames=0
+ a=0,4149,0,Pascal Units
+ c=19
+ (
+ file=/media/flash16gig/programming/fpgide/src/builderthread.pas
+ kind=1
+ a=0,4100,0,builderthread.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/debugger.pas
+ kind=1
+ a=0,4100,0,debugger.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/fpgide.lpr
+ kind=1
+ a=0,4100,0,fpgide.lpr
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/fpg_textedit.pas
+ kind=1
+ a=0,4100,0,fpg_textedit.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/frm_configureide.pas
+ kind=1
+ a=0,4100,0,frm_configureide.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/frm_debug.pas
+ kind=1
+ a=0,4100,0,frm_debug.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/frm_main.pas
+ kind=1
+ a=0,4100,0,frm_main.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/frm_procedurelist.pas
+ kind=1
+ a=0,4100,0,frm_procedurelist.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/frm_projectoptions.pas
+ kind=1
+ a=0,4100,0,frm_projectoptions.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/ideconst.pas
+ kind=1
+ a=0,4100,0,ideconst.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/ideimages.pas
+ kind=1
+ a=0,4100,0,ideimages.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/idemacros.pas
+ kind=1
+ a=0,4100,0,idemacros.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/ideutils.pas
+ kind=1
+ a=0,4100,0,ideutils.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/mPasLex.pas
+ kind=1
+ a=0,4100,0,mPasLex.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/msetypes.pas
+ kind=1
+ a=0,4100,0,msetypes.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/proclistimages.inc
+ kind=1
+ a=0,4100,0,proclistimages.inc
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/project.pas
+ kind=1
+ a=0,4100,0,project.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/stringhelpers.pas
+ kind=1
+ a=0,4100,0,stringhelpers.pas
+ )
+ (
+ file=/media/flash16gig/programming/fpgide/src/unitlist.pas
+ kind=1
+ a=0,4100,0,unitlist.pas
+ )
+ )
+cmodules=
+ (
+ a=0,4132,0,C Modules
+ )
+files=
+ (
+ a=0,4132,0,Text Files
+ )
+[componentstore]
+storedir=/home/graemeg/programming/msegui_svn/apps/ide/compstore/
+filename=
+[layout]
+windowlayout=474
+ [mainfo.openfile]
+ filenames=0
+ filehistory=10
+ /media/flash16gig/programming/msegui_svn/lib/common/kernel/msescrollbar.pas
+ /media/flash16gig/programming/msegui_svn/lib/common/kernel/i386-linux/mseguiintf.pas
+ /media/flash16gig/programming/fpgide/src/synregexpr.pas
+ /home/graemeg/programming/msegui_svn/README.TXT
+ /media/flash16gig/programming/fpgide/src/fpg_textedit.pas
+ /media/flash16gig/programming/Kylix_addons/Sort_Code/GenericUtils.pas
+ /media/flash16gig/programming/Kylix_addons/Sort_Code/ProcedureList.pas
+ /media/flash16gig/programming/fpgide/src/mPasLex.pas
+ /media/flash16gig/programming/fpgide/src/frm_procedurelist.pas
+ /home/graemeg/programming/fpc-2.5.1/src/packages/gdbm/examples/testgdbm.pp
+ filefilterindex=4
+ filefilter=*
+ filecolwidth=219
+ [mainfo.basedock]
+ splitdir=1
+ useroptions=15488
+ [mainfo]
+ stackedunder=
+ x=66
+ y=49
+ cx=693
+ cy=87
+ wsize=0
+ active=1
+ visible=1
+ [targetconsolefo]
+ splitdir=0
+ useroptions=16489
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=889
+ y=594
+ cx=551
+ cy=306
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=1
+ [threadsfo]
+ splitdir=0
+ useroptions=16489
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=37
+ y=270
+ cx=349
+ cy=276
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+ [memoryfo]
+ splitdir=0
+ useroptions=16507
+ stackedunder=componentpalettefo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=100
+ y=100
+ cx=453
+ cy=354
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+ [memoryfo.add]
+ value=0
+ [memoryfo.memon]
+ value=0
+ [memoryfo.bitwidth]
+ value=0
+ [memoryfo.cnt]
+ value=0
+ [disassfo]
+ splitdir=0
+ useroptions=16491
+ stackedunder=stackfo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=162
+ y=502
+ cx=564
+ cy=210
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+ [findinfilefo]
+ splitdir=0
+ useroptions=16491
+ stackedunder=targetconsolefo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=351
+ y=53
+ cx=1089
+ cy=363
+ rcx=0
+ rcy=0
+ [projecttreefo]
+ splitdir=0
+ useroptions=16491
+ stackedunder=breakpointsfo
+ parent=
+ mdistate=3
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=6
+ y=186
+ cx=233
+ cy=892
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=1
+ [projecttreefo.grid]
+ propcolwidthref=35
+ width0=188
+ sortdescent0=0
+ width1=29
+ sortdescent1=0
+ sortcol=-1
+ col=0
+ row=9
+ rowheight=19
+ [stackfo]
+ splitdir=0
+ useroptions=16489
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=407
+ y=349
+ cx=254
+ cy=180
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+ [watchpointsfo]
+ splitdir=0
+ useroptions=16489
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=537
+ y=26
+ cx=483
+ cy=210
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+ [watchpointsfo.grid]
+ propcolwidthref=352
+ width0=16
+ sortdescent0=0
+ values1=0
+ values1_ci=-1
+ width1=33
+ sortdescent1=0
+ values2=0
+ width2=158
+ sortdescent2=0
+ width4=34
+ sortdescent4=0
+ values5=0
+ values5_ci=-1
+ width5=38
+ sortdescent5=0
+ values6=0
+ width6=184
+ sortdescent6=0
+ [breakpointsfo]
+ splitdir=0
+ useroptions=16491
+ stackedunder=findinfilefo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=107
+ y=404
+ cx=660
+ cy=128
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=1
+ [breakpointsfo.bkptson]
+ value=1
+ [objectinspectorfo]
+ splitdir=0
+ useroptions=16491
+ stackedunder=symbolfo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=665
+ y=156
+ cx=355
+ cy=484
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+ [objectinspectorfo.grid]
+ propcolwidthref=345
+ width0=83
+ sortdescent0=0
+ width1=256
+ sortdescent1=0
+ [symbolfo]
+ splitdir=0
+ useroptions=16511
+ stackedunder=componentstorefo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=1079
+ y=763
+ cx=361
+ cy=137
+ rcx=0
+ rcy=0
+ children=1
+ container,0,0,351,137
+ wsize=0
+ active=0
+ visible=0
+ [symbolfo.grid]
+ propcolwidthref=222
+ values0=0
+ width0=111
+ sortdescent0=0
+ width1=135
+ sortdescent1=0
+ [symbolfo.symbol]
+ [watchfo]
+ splitdir=0
+ useroptions=24681
+ parent=
+ mdistate=3
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=713
+ y=600
+ cx=727
+ cy=300
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+ [watchfo.grid]
+ propcolwidthref=555
+ values0=0
+ values0_ci=-1
+ width0=13
+ sortdescent0=0
+ values1=0
+ width1=119
+ sortdescent1=0
+ values3=0
+ values3_ci=-1
+ width3=12
+ sortdescent3=0
+ values4=0
+ values4_ci=-1
+ width4=13
+ sortdescent4=0
+ [watchfo.watchon]
+ [watchfo.expression]
+ [watchfo.watcheson]
+ value=0
+ [messagefo]
+ splitdir=0
+ useroptions=49259
+ stackedunder=projecttreefo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=5
+ y=24
+ cx=1046
+ cy=193
+ rcx=1046
+ rcy=754
+ wsize=0
+ active=0
+ visible=0
+ [componentstorefo]
+ splitdir=0
+ useroptions=24675
+ stackedunder=messagefo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=100
+ y=100
+ cx=445
+ cy=354
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+ [componentstorefo.grid]
+ propcolwidthref=435
+ width0=111
+ sortdescent0=0
+ sortdescent1=0
+ width2=112
+ sortdescent2=0
+ width3=185
+ sortdescent3=0
+ [componentstorefo.storefiledialog]
+ filenames=1
+ /home/mse/test/msegui/apps/ide/compstore/
+ lastdir=/home/mse/test/msegui/apps/ide/compstore/
+ filehistory=0
+ filefilterindex=0
+ filefilter=
+ filecolwidth=0
+ [componentstorefo.groupfiledialog]
+ filenames=1
+ /home/mse/test/msegui/apps/ide/compstore/
+ lastdir=/home/mse/test/msegui/apps/ide/compstore/
+ filehistory=0
+ filefilterindex=0
+ filefilter=
+ filecolwidth=0
+ [componentstorefo.compfiledialog]
+ filenames=0
+ lastdir=
+ filehistory=0
+ filefilterindex=0
+ filefilter=
+ filecolwidth=0
+ [componentpalettefo]
+ splitdir=0
+ useroptions=24683
+ stackedunder=cpui386fo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=340
+ y=26
+ cx=457
+ cy=129
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+ [componentpalettefo.componentpages]
+ activetab=4
+ [debuggerfo]
+ splitdir=0
+ useroptions=24683
+ parent=mainfo.basedock
+ visible=1
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=0
+ y=0
+ cx=693
+ cy=45
+ rcx=0
+ rcy=0
+ [sourcefo]
+ splitdir=0
+ useroptions=16489
+ parent=
+ mdistate=3
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=249
+ y=186
+ cx=1034
+ cy=891
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=1
+ [actionsmo.watchesonact]
+ checked=0
+ [actionsmo.bluedotsonact]
+ checked=1
+ [cpui386fo]
+ irqoff=0
+ splitdir=0
+ useroptions=16491
+ stackedunder=threadsfo
+ parent=
+ mdistate=0
+ nx=0
+ ny=0
+ ncx=0
+ ncy=0
+ x=312
+ y=341
+ cx=283
+ cy=210
+ rcx=0
+ rcy=0
+ wsize=0
+ active=0
+ visible=0
+[edit]
+hintwidth=414
+hintheight=19
+finddtext=fwheelsensitivity
+findhistory=10
+ fwheelsensitivity
+ deltamin
+ fmousewheelfrequmin
+ flastmousewheeltimestampbefore
+ lasteventtime
+ buttonpress
+ FPGM_SCROLL
+ cbDefaultMakeCol
+ tpLeft
+ left
+findoptions=1
+editpos=38
+ 0,0
+ 39,779
+ 0,0
+ 0,1
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 27,230
+ 0,-1073741823
+ 26,1684
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 22,166
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 57,823
+ 0,-1073741823
+ 0,-1073741823
+ 0,-1073741823
+ 0,0
+ 14,843
+ 57,1200
+ 6,16460
+ 51,611
+bookmarks0=0
+bookmarks1=0
+bookmarks2=0
+bookmarks3=2
+ 84,4
+ 1052,5
+bookmarks4=0
+bookmarks5=0
+bookmarks6=0
+bookmarks7=0
+bookmarks8=0
+bookmarks9=0
+bookmarks10=0
+bookmarks11=0
+bookmarks12=0
+bookmarks13=2
+ 1224,2
+ 1683,3
+bookmarks14=0
+bookmarks15=0
+bookmarks16=0
+bookmarks17=0
+bookmarks18=0
+bookmarks19=0
+bookmarks20=0
+bookmarks21=0
+bookmarks22=0
+bookmarks23=0
+bookmarks24=0
+bookmarks25=0
+bookmarks26=0
+bookmarks27=0
+bookmarks28=0
+bookmarks29=0
+bookmarks30=0
+bookmarks31=0
+bookmarks32=0
+bookmarks33=0
+bookmarks34=0
+bookmarks35=1
+ 1197,1
+bookmarks36=0
+bookmarks37=0
+sourcefiles=38
+ /media/flash16gig/programming/fpgide/src/synregexpr.pas
+ /media/flash16gig/programming/fpgide/src/frm_projectoptions.pas
+ /media/flash16gig/programming/fpgide/src/ideutils.pas
+ /media/flash16gig/programming/fpgide/src/frm_main.pas
+ /media/flash16gig/programming/fpgide/src/unitlist.pas
+ /media/flash16gig/programming/fpgide/src/project.pas
+ /media/flash16gig/programming/fpgide/src/stringhelpers.pas
+ /media/flash16gig/programming/fpgide/src/ideimages.pas
+ /media/flash16gig/programming/fpgide/src/frm_debug.pas
+ /media/flash16gig/programming/fpgide/src/idemacros.pas
+ /media/flash16gig/programming/fpgide/src/ideconst.pas
+ /media/flash16gig/programming/fpgide/src/frm_configureide.pas
+ /home/graemeg/programming/fpgui/src/gui/fpg_form.pas
+ /media/flash16gig/programming/fpgide/src/fpg_textedit.pas
+ /home/graemeg/programming/fpgui/src/corelib/fpg_main.pas
+ /media/flash16gig/programming/fpgide/src/builderthread.pas
+ /home/graemeg/programming/lazarus/ide/checkcompileropts.pas
+ /media/flash16gig/programming/fpgide/src/frm_procedurelist.pas
+ /media/flash16gig/programming/Kylix_addons/Sort_Code/ProcedureList.pas
+ /media/flash16gig/programming/Kylix_addons/Sort_Code/GenericUtils.pas
+ /home/graemeg/programming/fpgui/src/corelib/fpg_base.pas
+ /home/graemeg/programming/fpgui/src/gui/fpg_grid.pas
+ /home/graemeg/programming/fpgui/src/gui/fpg_customgrid.pas
+ /home/graemeg/programming/fpgui/src/gui/fpg_basegrid.pas
+ /home/graemeg/programming/fpgui/src/corelib/fpg_imagelist.pas
+ /media/flash16gig/programming/fpgide/src/proclistimages.inc
+ /home/graemeg/programming/fpgui/src/corelib/fpg_imgfmt_bmp.pas
+ /home/graemeg/programming/fpgui/src/corelib/fpg_stdimages.pas
+ /home/graemeg/programming/fpgui/src/gui/fpg_edit.pas
+ /home/graemeg/programming/fpgui/src/corelib/fpg_widget.pas
+ /home/graemeg/programming/fpgui/src/corelib/keys.inc
+ /media/flash16gig/programming/fpgide/src/debugger.pas
+ /media/flash16gig/programming/fpgide/src/mPasLex.pas
+ /media/flash16gig/programming/fpgide/src/msetypes.pas
+ /media/flash16gig/programming/msegui_svn/lib/common/kernel/i386-linux/mseguiintf.pas
+ /media/flash16gig/programming/msegui_svn/lib/common/kernel/msescrollbar.pas
+ /media/flash16gig/programming/msegui_svn/lib/common/kernel/msegui.pas
+ /home/graemeg/programming/fpgui/src/gui/fpg_scrollbar.pas
+relpaths=38
+ synregexpr.pas
+ frm_projectoptions.pas
+ ideutils.pas
+ frm_main.pas
+ unitlist.pas
+ project.pas
+ stringhelpers.pas
+ ideimages.pas
+ frm_debug.pas
+ idemacros.pas
+ ideconst.pas
+ frm_configureide.pas
+ ../../../../../home/graemeg/programming/fpgui/src/gui/fpg_form.pas
+ fpg_textedit.pas
+ ../../../../../home/graemeg/programming/fpgui/src/corelib/fpg_main.pas
+ builderthread.pas
+ ../../../../../home/graemeg/programming/lazarus/ide/checkcompileropts.pas
+ frm_procedurelist.pas
+ ../../Kylix_addons/Sort_Code/ProcedureList.pas
+ ../../Kylix_addons/Sort_Code/GenericUtils.pas
+ ../../../../../home/graemeg/programming/fpgui/src/corelib/fpg_base.pas
+ ../../../../../home/graemeg/programming/fpgui/src/gui/fpg_grid.pas
+ ../../../../../home/graemeg/programming/fpgui/src/gui/fpg_customgrid.pas
+ ../../../../../home/graemeg/programming/fpgui/src/gui/fpg_basegrid.pas
+ ../../../../../home/graemeg/programming/fpgui/src/corelib/fpg_imagelist.pas
+ proclistimages.inc
+ ../../../../../home/graemeg/programming/fpgui/src/corelib/fpg_imgfmt_bmp.pas
+ ../../../../../home/graemeg/programming/fpgui/src/corelib/fpg_stdimages.pas
+ ../../../../../home/graemeg/programming/fpgui/src/gui/fpg_edit.pas
+ ../../../../../home/graemeg/programming/fpgui/src/corelib/fpg_widget.pas
+ ../../../../../home/graemeg/programming/fpgui/src/corelib/keys.inc
+ debugger.pas
+ mPasLex.pas
+ msetypes.pas
+ ../../msegui_svn/lib/common/kernel/i386-linux/mseguiintf.pas
+ ../../msegui_svn/lib/common/kernel/msescrollbar.pas
+ ../../msegui_svn/lib/common/kernel/msegui.pas
+ ../../../../../home/graemeg/programming/fpgui/src/gui/fpg_scrollbar.pas
+ismoduletexts=38
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+modules=0
+visiblemodules=0
+[sourcefo.tabwidget]
+order=38
+ 21
+ 2
+ 3
+ 0
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 1
+ 12
+ 13
+ 37
+ 20
+ 35
+ 36
+ 34
+ 14
+ 15
+ 16
+ 17
+ 31
+ 32
+ 33
+ 28
+ 29
+ 18
+ 19
+ 30
+ 22
+ 23
+ 24
+ 25
+ 26
+ 27
+tabsize=169
+firsttab=0
+index=14
+[components]
+[selecteditpage]
+colwidth=156
+x=160
+y=124
+cx=704
+cy=473
+[progparams]
+parameters=
+progparamhistory=0
+workingdirectory=
+envvarons=0
+envvarnames=0
+envvarvalues=0
diff --git a/examples/apps/ide/src/fpgide.project b/examples/apps/ide/src/fpgide.project
new file mode 100644
index 00000000..ac5e3635
--- /dev/null
+++ b/examples/apps/ide/src/fpgide.project
@@ -0,0 +1,55 @@
+[ProjectOptions]
+ProjectName=fpgide.project
+MainUnit=fpgide.lpr
+TargetFile=fpgide${EXEEXT}
+DefaultMake=0
+MakeOptionsCount=5
+MakeOptionEnabled1=1,1,1,1,1,1
+MakeOptionEnabled2=1,1,1,1,1,0
+MakeOptionEnabled3=1,1,0,0,0,1
+MakeOptionEnabled4=0,0,0,0,1,1
+MacroCount=6
+Macro1=TargetCPU=x86_64
+Macro2=TargetOS=linux
+Macro3=TargetCPU=i386
+Macro4=TargetOS=win32
+Macro5=FPGUI_DIR=/home/graemeg/programming/fpgui/
+Macro6=tiOPF_fpGUI_Dir=/home/graemeg/programming/3rdParty/tiOPF2/src/
+UnitDirsCount=7
+UnitDirEnabled1=1,1,1,1,0,0,0,1,0,0
+UnitDirEnabled2=1,1,1,1,0,0,1,1,0,0
+UnitDirEnabled3=1,1,1,0,0,0,1,1,0,0
+UnitDirEnabled4=0,0,0,1,0,0,1,1,0,0
+UnitDirEnabled5=1,1,1,1,0,0,1,0,0,0
+UnitDirEnabled6=1,1,1,1,0,0,1,0,0,0
+UnitDirEnabled7=0,0,0,0,1,1,1,0,0,0
+UnitDirEnabled8=0,0,0,0,0,1,1,0,0,0
+UnitOutputDir=units/${TARGET}/
+MakeOptionEnabled5=1,0,0,0,0,0
+MakeOption1=-l -Mobjfpc -Sch
+MakeOption2=-gl -O-
+MakeOption3=-B
+MakeOption4=-O2 -XX -Xs -CX
+MakeOption5=-veiw
+UnitDir1=${FPGUIDIR}src/
+UnitDir2=${FPGUIDIR}src/corelib/
+UnitDir3=${FPGUIDIR}src/corelib/x11/
+UnitDir4=${FPGUIDIR}src/corelib/gdi/
+UnitDir5=${FPGUIDIR}src/gui/
+UnitDir6=${FPGUIDIR}src/gui/db/
+UnitDir7=${FPGUILIBDIR}
+
+[Units]
+UnitCount=12
+Unit1=fpg_textedit.pas,-1
+Unit2=fpgide.lpr,0
+Unit3=frm_configureide.pas,0
+Unit4=frm_debug.pas,0
+Unit5=frm_main.pas,-1
+Unit6=frm_procedurelist.pas,0
+Unit7=frm_projectoptions.pas,0
+Unit8=ideconst.pas,-1
+Unit9=idemacros.pas,0
+Unit10=ideutils.pas,-1
+Unit11=project.pas,-1
+Unit12=unitlist.pas,-1
diff --git a/examples/apps/ide/src/frm_configureide.pas b/examples/apps/ide/src/frm_configureide.pas
new file mode 100644
index 00000000..b3fb02da
--- /dev/null
+++ b/examples/apps/ide/src/frm_configureide.pas
@@ -0,0 +1,741 @@
+unit frm_configureide;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ SysUtils, Classes, fpg_base, fpg_main, fpg_form, fpg_button, fpg_editbtn,
+ fpg_label, fpg_tab, fpg_edit, fpg_grid, fpg_listbox, idemacros, fpg_combobox,
+ fpg_checkbox;
+
+type
+ TConfigureIDEForm = class(TfpgForm)
+ private
+ {@VFD_HEAD_BEGIN: ConfigureIDEForm}
+ btnCancel: TfpgButton;
+ btnOK: TfpgButton;
+ pcSettings: TfpgPageControl;
+ tsEnvironment: TfpgTabSheet;
+ tsEditor: TfpgTabSheet;
+ tsShortcuts: TfpgTabSheet;
+ Label1: TfpgLabel;
+ edtFPCSrcDir: TfpgDirectoryEdit;
+ edtFPGuiDir: TfpgDirectoryEdit;
+ edtFPGuiLibDir: TfpgDirectoryEdit;
+ edtSyntaxDefDir: TfpgDirectoryEdit;
+ edtTempateDir: TfpgDirectoryEdit;
+ edtCompiler: TfpgFileNameEdit;
+ edtDebugger: TfpgFileNameEdit;
+ Label2: TfpgLabel;
+ Label3: TfpgLabel;
+ Label4: TfpgLabel;
+ Label5: TfpgLabel;
+ Label6: TfpgLabel;
+ Label7: TfpgLabel;
+ Label8: TfpgLabel;
+ Label9: TfpgLabel;
+ Label11: TfpgLabel;
+ edtEditorFont: TfpgFontEdit;
+ edtExeExt: TfpgEdit;
+ edtTarget: TfpgEdit;
+ grdShortcuts: TfpgStringGrid;
+ tsSyntaxDefs: TfpgTabSheet;
+ tsFileFilters: TfpgTabSheet;
+ grdSyntaxDefs: TfpgStringGrid;
+ grdFileFilters: TfpgStringGrid;
+ tsExtTools: TfpgTabSheet;
+ Label10: TfpgLabel;
+ edtExtToolMenu: TfpgEdit;
+ Label12: TfpgLabel;
+ edtExtToolFile: TfpgFileNameEdit;
+ Label13: TfpgLabel;
+ edtExtToolParams: TfpgEdit;
+ btnExtToolAdd: TfpgButton;
+ btnExtToolDel: TfpgButton;
+ lbExtTools: TfpgListBox;
+ cbTabPosition: TfpgComboBox;
+ Label14: TfpgLabel;
+ cbSyntaxHighlighting: TfpgCheckBox;
+ {@VFD_HEAD_END: ConfigureIDEForm}
+ // so we can get correct hints, but still undo with the Cancel button
+ FInternalMacroList: TIDEMacroList;
+ procedure BeforeShowHint(Sender: TObject; var AHint: TfpgString);
+ procedure LoadSettings;
+ procedure SaveSettings;
+ procedure SaveToMacroList(AList: TIDEMacroList);
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure AfterCreate; override;
+ end;
+
+{@VFD_NEWFORM_DECL}
+
+procedure DisplayConfigureIDE;
+
+
+implementation
+
+uses
+ fpg_dialogs
+ ,fpg_iniutils
+ ,fpg_widget
+ ,ideconst
+ ;
+
+type
+ // Used to get access to the Protected properties
+ TDirectoryEditFriend = class(TfpgDirectoryEdit);
+
+
+procedure DisplayConfigureIDE;
+var
+ frm: TConfigureIDEForm;
+ Result: Boolean;
+begin
+ frm := TConfigureIDEForm.Create(nil);
+ try
+ frm.LoadSettings;
+ Result := frm.ShowModal = mrOK;
+ if Result then
+ begin
+ frm.SaveSettings;
+ end;
+ finally
+ frm.Free;
+ end;
+end;
+
+{@VFD_NEWFORM_IMPL}
+
+procedure TConfigureIDEForm.BeforeShowHint(Sender: TObject; var AHint: TfpgString);
+var
+ s: TfpgString;
+ c: TfpgWidget;
+begin
+ if Sender is TfpgWidget then
+ c := TfpgWidget(Sender)
+ else
+ Exit; // should never occur, but lets just be safe
+
+ if (c.Name = 'FEdit') and ((c.Parent is TfpgDirectoryEdit) or (c.Parent is TfpgFileNameEdit)) then
+ begin
+ if c.Parent <> nil then
+ c := c.Parent
+ else
+ Exit; // lets just be safe again
+ end;
+
+ // controls that may contain macros
+ if c is TfpgDirectoryEdit then
+ s := TfpgDirectoryEdit(c).Directory
+ else if c is TfpgFileNameEdit then
+ s := TfpgFileNameEdit(c).FileName
+ else if c is TfpgEdit then
+ s := TfpgEdit(c).Text;
+
+ AHint := s;
+
+ if FInternalMacroList.StrHasMacros(s) then
+ begin
+ SaveToMacroList(FInternalMacroList);
+ AHint := FInternalMacroList.ExpandMacro(s);
+ end;
+end;
+
+procedure TConfigureIDEForm.LoadSettings;
+begin
+ edtFPCSrcDir.Directory := gINI.ReadString(cEnvironment, 'FPCSrcDir', '');
+ edtFPGuiDir.Directory := gINI.ReadString(cEnvironment, 'FPGuiDir', '');
+ edtFPGuiLibDir.Directory := gINI.ReadString(cEnvironment, 'FPGuiLibDir', GMacroList.FindByName(cMacro_FPGuiLibDir).Value);
+ edtSyntaxDefDir.Directory := gINI.ReadString(cEnvironment, 'SyntaxDefDir', GMacroList.FindByName(cMacro_SyntaxDefDir).Value);
+ edtTempateDir.Directory := gINI.ReadString(cEnvironment, 'TemplateDir', GMacroList.FindByName(cMacro_TemplateDir).Value);
+ edtCompiler.Filename := gINI.ReadString(cEnvironment, 'Compiler', '');
+ edtDebugger.Filename := gINI.ReadString(cEnvironment, 'Debugger', 'gdb');
+ edtExeExt.Text := gINI.ReadString(cEnvironment, 'ExeExt', '');
+ edtTarget.Text := gINI.ReadString(cEnvironment, 'Target', GMacroList.FindByName(cMacro_Target).Value);
+ edtEditorFont.FontDesc := gINI.ReadString(cEditor, 'Font', '#Edit2');
+ cbTabPosition.FocusItem := gINI.ReadInteger(cEditor, 'TabPosition', 0);
+ cbSyntaxHighlighting.Checked := gINI.ReadBool(cEditor, 'SyntaxHighlighting', True);
+end;
+
+procedure TConfigureIDEForm.SaveSettings;
+begin
+ gINI.WriteString(cEnvironment, 'FPCSrcDir', edtFPCSrcDir.Directory);
+ gINI.WriteString(cEnvironment, 'FPGuiDir', edtFPGuiDir.Directory);
+ gINI.WriteString(cEnvironment, 'FPGuiLibDir', edtFPGuiLibDir.Directory);
+ gINI.WriteString(cEnvironment, 'SyntaxDefDir', edtSyntaxDefDir.Directory);
+ gINI.WriteString(cEnvironment, 'TemplateDir', edtTempateDir.Directory);
+ gINI.WriteString(cEnvironment, 'Compiler', edtCompiler.Filename);
+ gINI.WriteString(cEnvironment, 'Debugger', edtDebugger.Filename);
+ gINI.WriteString(cEnvironment, 'ExeExt', edtExeExt.Text);
+ gINI.WriteString(cEnvironment, 'Target', edtTarget.Text);
+ gINI.WriteString(cEditor, 'Font', edtEditorFont.FontDesc);
+ gINI.WriteInteger(cEditor, 'TabPosition', cbTabPosition.FocusItem);
+ gINI.WriteBool(cEditor, 'SyntaxHighlighting', cbSyntaxHighlighting.Checked);
+
+ SaveToMacroList(GMacroList);
+end;
+
+procedure TConfigureIDEForm.SaveToMacroList(AList: TIDEMacroList);
+begin
+ AList.SetValue(cMacro_FPCSrcDir, edtFPCSrcDir.Directory);
+ AList.SetValue(cMacro_FPGuiDir, edtFPGuiDir.Directory);
+ AList.SetValue(cMacro_FPGuiLibDir, edtFPGuiLibDir.Directory);
+ AList.SetValue(cMacro_SyntaxDefDir, edtSyntaxDefDir.Directory);
+ AList.SetValue(cMacro_TemplateDir, edtTempateDir.Directory);
+ AList.SetValue(cMacro_Compiler, edtCompiler.FileName);
+ AList.SetValue(cMacro_Debugger, edtDebugger.FileName);
+ AList.SetValue(cMacro_ExeExt, edtExeExt.Text);
+ AList.SetValue(cMacro_Target, edtTarget.Text);
+end;
+
+constructor TConfigureIDEForm.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ FInternalMacroList := TIDEMacroList.Create;
+end;
+
+destructor TConfigureIDEForm.Destroy;
+begin
+ FInternalMacroList.Free;
+ inherited Destroy;
+end;
+
+procedure TConfigureIDEForm.AfterCreate;
+begin
+ {%region 'Auto-generated GUI code' -fold}
+ {@VFD_BODY_BEGIN: ConfigureIDEForm}
+ Name := 'ConfigureIDEForm';
+ SetPosition(332, 190, 578, 480);
+ WindowTitle := 'Configure IDE';
+ Hint := '';
+ ShowHint := True;
+ WindowPosition := wpOneThirdDown;
+
+ btnCancel := TfpgButton.Create(self);
+ with btnCancel do
+ begin
+ Name := 'btnCancel';
+ SetPosition(494, 450, 80, 24);
+ Anchors := [anRight,anBottom];
+ Text := 'Cancel';
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageName := '';
+ ModalResult := mrCancel;
+ TabOrder := 1;
+ end;
+
+ btnOK := TfpgButton.Create(self);
+ with btnOK do
+ begin
+ Name := 'btnOK';
+ SetPosition(410, 450, 80, 24);
+ Anchors := [anRight,anBottom];
+ Text := 'OK';
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageName := '';
+ ModalResult := mrOK;
+ TabOrder := 2;
+ end;
+
+ pcSettings := TfpgPageControl.Create(self);
+ with pcSettings do
+ begin
+ Name := 'pcSettings';
+ SetPosition(4, 4, 570, 430);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ ActivePageIndex := 0;
+ Hint := '';
+ TabOrder := 3;
+ TabPosition := tpRight;
+ end;
+
+ tsEnvironment := TfpgTabSheet.Create(pcSettings);
+ with tsEnvironment do
+ begin
+ Name := 'tsEnvironment';
+ SetPosition(125, 3, 442, 424);
+ Text := 'Environment';
+ end;
+
+ tsEditor := TfpgTabSheet.Create(pcSettings);
+ with tsEditor do
+ begin
+ Name := 'tsEditor';
+ SetPosition(125, 3, 442, 424);
+ Text := 'Editor';
+ end;
+
+ tsShortcuts := TfpgTabSheet.Create(pcSettings);
+ with tsShortcuts do
+ begin
+ Name := 'tsShortcuts';
+ SetPosition(125, 3, 442, 424);
+ Text := 'Shortcuts';
+ end;
+
+ Label1 := TfpgLabel.Create(tsEnvironment);
+ with Label1 do
+ begin
+ Name := 'Label1';
+ SetPosition(8, 4, 340, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'FPC Source Directory ${FPCSRCDIR}';
+ end;
+
+ edtFPCSrcDir := TfpgDirectoryEdit.Create(tsEnvironment);
+ with edtFPCSrcDir do
+ begin
+ Name := 'edtFPCSrcDir';
+ SetPosition(8, 22, 424, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ Directory := '';
+ RootDirectory := '';
+ TabOrder := 3;
+ Hint := '*';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ edtFPGuiDir := TfpgDirectoryEdit.Create(tsEnvironment);
+ with edtFPGuiDir do
+ begin
+ Name := 'edtFPGuiDir';
+ SetPosition(8, 74, 424, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ Directory := '';
+ RootDirectory := '';
+ TabOrder := 6;
+ Hint := '*';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ edtFPGuiLibDir := TfpgDirectoryEdit.Create(tsEnvironment);
+ with edtFPGuiLibDir do
+ begin
+ Name := 'edtFPGuiLibDir';
+ SetPosition(8, 122, 424, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ Directory := '${FPGUIDIR}lib/';
+ RootDirectory := '';
+ TabOrder := 7;
+ Hint := '*';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ edtSyntaxDefDir := TfpgDirectoryEdit.Create(tsEnvironment);
+ with edtSyntaxDefDir do
+ begin
+ Name := 'edtSyntaxDefDir';
+ SetPosition(8, 170, 424, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ Directory := '${FPGUIDIR}apps/ide/syntaxdefs/';
+ RootDirectory := '';
+ TabOrder := 8;
+ Hint := '*';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ edtTempateDir := TfpgDirectoryEdit.Create(tsEnvironment);
+ with edtTempateDir do
+ begin
+ Name := 'edtTempateDir';
+ SetPosition(8, 218, 424, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ Directory := '{FPGUIDIR}apps/ide/templates/';
+ RootDirectory := '';
+ TabOrder := 9;
+ Hint := '*';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ edtCompiler := TfpgFileNameEdit.Create(tsEnvironment);
+ with edtCompiler do
+ begin
+ Name := 'edtCompiler';
+ SetPosition(8, 266, 424, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ FileName := '/opt/fpc_2.4.1/${TARGET}/bin/fpc';
+ InitialDir := '';
+ Filter := '';
+ TabOrder := 10;
+ Hint := '*';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ edtDebugger := TfpgFileNameEdit.Create(tsEnvironment);
+ with edtDebugger do
+ begin
+ Name := 'edtDebugger';
+ SetPosition(8, 314, 426, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ FileName := 'gdb';
+ InitialDir := '';
+ Filter := '';
+ TabOrder := 11;
+ Hint := '*';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ Label2 := TfpgLabel.Create(tsEnvironment);
+ with Label2 do
+ begin
+ Name := 'Label2';
+ SetPosition(8, 56, 340, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'fpGUI Root Directory ${FPGUIDIR}';
+ end;
+
+ Label3 := TfpgLabel.Create(tsEnvironment);
+ with Label3 do
+ begin
+ Name := 'Label3';
+ SetPosition(8, 104, 340, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := '${FPGUILIBDIR}';
+ end;
+
+ Label4 := TfpgLabel.Create(tsEnvironment);
+ with Label4 do
+ begin
+ Name := 'Label4';
+ SetPosition(8, 152, 344, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := '${SYNTAXDEFDIR}';
+ end;
+
+ Label5 := TfpgLabel.Create(tsEnvironment);
+ with Label5 do
+ begin
+ Name := 'Label5';
+ SetPosition(8, 200, 340, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := '${TEMPLATEDIR}';
+ end;
+
+ Label6 := TfpgLabel.Create(tsEnvironment);
+ with Label6 do
+ begin
+ Name := 'Label6';
+ SetPosition(8, 248, 340, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := '${COMPILER}';
+ end;
+
+ Label7 := TfpgLabel.Create(tsEnvironment);
+ with Label7 do
+ begin
+ Name := 'Label7';
+ SetPosition(8, 296, 340, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := '${DEBUGGER}';
+ end;
+
+ Label8 := TfpgLabel.Create(tsEnvironment);
+ with Label8 do
+ begin
+ Name := 'Label8';
+ SetPosition(8, 344, 144, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := '${EXEEXT}';
+ end;
+
+ Label9 := TfpgLabel.Create(tsEnvironment);
+ with Label9 do
+ begin
+ Name := 'Label9';
+ SetPosition(164, 344, 188, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := '${TARGET}';
+ end;
+
+ Label11 := TfpgLabel.Create(tsEditor);
+ with Label11 do
+ begin
+ Name := 'Label11';
+ SetPosition(8, 4, 224, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Font';
+ end;
+
+ edtEditorFont := TfpgFontEdit.Create(tsEditor);
+ with edtEditorFont do
+ begin
+ Name := 'edtEditorFont';
+ SetPosition(8, 22, 424, 24);
+ Anchors := [anLeft,anRight,anTop];
+ FontDesc := '#Edit1';
+ TabOrder := 2;
+ end;
+
+ edtExeExt := TfpgEdit.Create(tsEnvironment);
+ with edtExeExt do
+ begin
+ Name := 'edtExeExt';
+ SetPosition(8, 362, 144, 24);
+ ExtraHint := '';
+ Hint := '*';
+ TabOrder := 21;
+ Text := '';
+ FontDesc := '#Edit1';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ edtTarget := TfpgEdit.Create(tsEnvironment);
+ with edtTarget do
+ begin
+ Name := 'edtTarget';
+ SetPosition(164, 362, 192, 24);
+ ExtraHint := '';
+ Hint := '*';
+ TabOrder := 22;
+ Text := 'i386-linux';
+ FontDesc := '#Edit1';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ grdShortcuts := TfpgStringGrid.Create(tsShortcuts);
+ with grdShortcuts do
+ begin
+ Name := 'grdShortcuts';
+ SetPosition(8, 8, 428, 408);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ AddColumn('Action', 180, taLeftJustify);
+ AddColumn('Shortcut', 110, taLeftJustify);
+ AddColumn('Alternative', 110, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 10;
+ RowSelect := False;
+ TabOrder := 1;
+ end;
+
+ tsSyntaxDefs := TfpgTabSheet.Create(pcSettings);
+ with tsSyntaxDefs do
+ begin
+ Name := 'tsSyntaxDefs';
+ SetPosition(125, 3, 442, 424);
+ Text := 'Syntax Highlighting';
+ end;
+
+ tsFileFilters := TfpgTabSheet.Create(pcSettings);
+ with tsFileFilters do
+ begin
+ Name := 'tsFileFilters';
+ SetPosition(125, 3, 442, 424);
+ Text := 'File Filters';
+ end;
+
+ grdSyntaxDefs := TfpgStringGrid.Create(tsSyntaxDefs);
+ with grdSyntaxDefs do
+ begin
+ Name := 'grdSyntaxDefs';
+ SetPosition(8, 8, 428, 408);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ AddColumn('Syntax Definition File', 200, taLeftJustify);
+ AddColumn('File Mask', 200, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 5;
+ RowSelect := False;
+ TabOrder := 1;
+ end;
+
+ grdFileFilters := TfpgStringGrid.Create(tsFileFilters);
+ with grdFileFilters do
+ begin
+ Name := 'grdFileFilters';
+ SetPosition(8, 8, 428, 408);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ AddColumn('Name', 150, taLeftJustify);
+ AddColumn('File Mask', 200, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 5;
+ RowSelect := False;
+ TabOrder := 1;
+ end;
+
+ tsExtTools := TfpgTabSheet.Create(pcSettings);
+ with tsExtTools do
+ begin
+ Name := 'tsExtTools';
+ SetPosition(125, 3, 442, 424);
+ Text := 'External Tools';
+ end;
+
+ Label10 := TfpgLabel.Create(tsExtTools);
+ with Label10 do
+ begin
+ Name := 'Label10';
+ SetPosition(8, 234, 212, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Menu Caption';
+ end;
+
+ edtExtToolMenu := TfpgEdit.Create(tsExtTools);
+ with edtExtToolMenu do
+ begin
+ Name := 'edtExtToolMenu';
+ SetPosition(8, 252, 428, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ Hint := '';
+ TabOrder := 3;
+ Text := '';
+ FontDesc := '#Edit1';
+ end;
+
+ Label12 := TfpgLabel.Create(tsExtTools);
+ with Label12 do
+ begin
+ Name := 'Label12';
+ SetPosition(8, 282, 428, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Program';
+ end;
+
+ edtExtToolFile := TfpgFileNameEdit.Create(tsExtTools);
+ with edtExtToolFile do
+ begin
+ Name := 'edtExtToolFile';
+ SetPosition(8, 300, 428, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ FileName := '';
+ InitialDir := '';
+ Filter := '';
+ TabOrder := 5;
+ end;
+
+ Label13 := TfpgLabel.Create(tsExtTools);
+ with Label13 do
+ begin
+ Name := 'Label13';
+ SetPosition(8, 330, 420, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Parameters';
+ end;
+
+ edtExtToolParams := TfpgEdit.Create(tsExtTools);
+ with edtExtToolParams do
+ begin
+ Name := 'edtExtToolParams';
+ SetPosition(8, 348, 428, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ Hint := '';
+ TabOrder := 7;
+ Text := '';
+ FontDesc := '#Edit1';
+ end;
+
+ btnExtToolAdd := TfpgButton.Create(tsExtTools);
+ with btnExtToolAdd do
+ begin
+ Name := 'btnExtToolAdd';
+ SetPosition(8, 8, 24, 24);
+ Text := '';
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageMargin := -1;
+ ImageName := 'stdimg.add';
+ ImageSpacing := 0;
+ TabOrder := 8;
+ end;
+
+ btnExtToolDel := TfpgButton.Create(tsExtTools);
+ with btnExtToolDel do
+ begin
+ Name := 'btnExtToolDel';
+ SetPosition(34, 8, 24, 24);
+ Text := '';
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageMargin := -1;
+ ImageName := 'stdimg.remove';
+ ImageSpacing := 0;
+ TabOrder := 9;
+ end;
+
+ lbExtTools := TfpgListBox.Create(tsExtTools);
+ with lbExtTools do
+ begin
+ Name := 'lbExtTools';
+ SetPosition(8, 33, 428, 192);
+ Anchors := [anLeft,anRight,anTop];
+ FontDesc := '#List';
+ Hint := '';
+ HotTrack := False;
+ PopupFrame := False;
+ TabOrder := 10;
+ end;
+
+ cbTabPosition := TfpgComboBox.Create(tsEditor);
+ with cbTabPosition do
+ begin
+ Name := 'cbTabPosition';
+ SetPosition(8, 68, 144, 22);
+ FontDesc := '#List';
+ Hint := '';
+ Items.Add('top');
+ Items.Add('bottom');
+ Items.Add('left');
+ Items.Add('right');
+ TabOrder := 3;
+ end;
+
+ Label14 := TfpgLabel.Create(tsEditor);
+ with Label14 do
+ begin
+ Name := 'Label14';
+ SetPosition(8, 50, 404, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Tab position';
+ end;
+
+ cbSyntaxHighlighting := TfpgCheckBox.Create(tsEditor);
+ with cbSyntaxHighlighting do
+ begin
+ Name := 'cbSyntaxHighlighting';
+ SetPosition(4, 100, 404, 20);
+ Anchors := [anLeft,anRight,anTop];
+ FontDesc := '#Label1';
+ Hint := '';
+ TabOrder := 5;
+ Text := 'Syntax Highlighting';
+ end;
+
+ {@VFD_BODY_END: ConfigureIDEForm}
+ {%endregion}
+end;
+
+
+end.
diff --git a/examples/apps/ide/src/frm_debug.pas b/examples/apps/ide/src/frm_debug.pas
new file mode 100644
index 00000000..267871c2
--- /dev/null
+++ b/examples/apps/ide/src/frm_debug.pas
@@ -0,0 +1,170 @@
+unit frm_debug;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ SysUtils, Classes, fpg_base, fpg_main, fpg_form, fpg_tab, fpg_grid;
+
+type
+
+ TDebugForm = class(TfpgForm)
+ private
+ {@VFD_HEAD_BEGIN: DebugForm}
+ PageControl1: TfpgPageControl;
+ TabSheet1: TfpgTabSheet;
+ TabSheet2: TfpgTabSheet;
+ TabSheet3: TfpgTabSheet;
+ TabSheet4: TfpgTabSheet;
+ TabSheet5: TfpgTabSheet;
+ TabSheet6: TfpgTabSheet;
+ TabSheet7: TfpgTabSheet;
+ Grid1: TfpgStringGrid;
+ Grid2: TfpgStringGrid;
+ Grid3: TfpgStringGrid;
+ {@VFD_HEAD_END: DebugForm}
+ public
+ procedure AfterCreate; override;
+ end;
+
+{@VFD_NEWFORM_DECL}
+
+var
+ DebugForm: TDebugForm;
+
+implementation
+
+{@VFD_NEWFORM_IMPL}
+
+procedure TDebugForm.AfterCreate;
+begin
+ {%region 'Auto-generated GUI code' -fold}
+ {@VFD_BODY_BEGIN: DebugForm}
+ Name := 'DebugForm';
+ SetPosition(690, 193, 512, 247);
+ WindowTitle := 'Debug Window';
+ Hint := '';
+
+ PageControl1 := TfpgPageControl.Create(self);
+ with PageControl1 do
+ begin
+ Name := 'PageControl1';
+ SetPosition(4, 4, 506, 240);
+ ActivePageIndex := 0;
+ Hint := '';
+ TabOrder := 0;
+ end;
+
+ TabSheet1 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet1 do
+ begin
+ Name := 'TabSheet1';
+ SetPosition(3, 24, 500, 213);
+ Text := 'Watches';
+ end;
+
+ TabSheet2 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet2 do
+ begin
+ Name := 'TabSheet2';
+ SetPosition(3, 24, 500, 213);
+ Text := 'BreakPoints';
+ end;
+
+ TabSheet3 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet3 do
+ begin
+ Name := 'TabSheet3';
+ SetPosition(3, 24, 500, 213);
+ Text := 'Local Vars';
+ end;
+
+ TabSheet4 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet4 do
+ begin
+ Name := 'TabSheet4';
+ SetPosition(3, 24, 500, 213);
+ Text := 'Call Stack';
+ end;
+
+ TabSheet5 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet5 do
+ begin
+ Name := 'TabSheet5';
+ SetPosition(3, 24, 500, 213);
+ Text := 'Registers';
+ end;
+
+ TabSheet6 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet6 do
+ begin
+ Name := 'TabSheet6';
+ SetPosition(3, 24, 500, 213);
+ Text := 'Asm';
+ end;
+
+ TabSheet7 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet7 do
+ begin
+ Name := 'TabSheet7';
+ SetPosition(3, 24, 500, 213);
+ Text := 'GDB output';
+ end;
+
+ Grid1 := TfpgStringGrid.Create(TabSheet1);
+ with Grid1 do
+ begin
+ Name := 'Grid1';
+ SetPosition(0, 4, 496, 204);
+ AddColumn('Expression', 100, taLeftJustify);
+ AddColumn('Value', 350, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 5;
+ RowSelect := False;
+ TabOrder := 0;
+ end;
+
+ Grid2 := TfpgStringGrid.Create(TabSheet2);
+ with Grid2 do
+ begin
+ Name := 'Grid2';
+ SetPosition(0, 4, 496, 204);
+ AddColumn('State', 50, taLeftJustify);
+ AddColumn('Filename/Addres', 120, taLeftJustify);
+ AddColumn('Line/Length', 85, taLeftJustify);
+ AddColumn('Condition', 70, taLeftJustify);
+ AddColumn('Action', 50, taLeftJustify);
+ AddColumn('Count', 50, taLeftJustify);
+ AddColumn('Group', 80, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 5;
+ RowSelect := False;
+ TabOrder := 0;
+ end;
+
+ Grid3 := TfpgStringGrid.Create(TabSheet3);
+ with Grid3 do
+ begin
+ Name := 'Grid3';
+ SetPosition(0, 4, 496, 204);
+ AddColumn('Name', 150, taLeftJustify);
+ AddColumn('Value', 250, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 5;
+ RowSelect := False;
+ TabOrder := 0;
+ end;
+
+ {@VFD_BODY_END: DebugForm}
+ {%endregion}
+end;
+
+
+end.
diff --git a/examples/apps/ide/src/frm_main.pas b/examples/apps/ide/src/frm_main.pas
new file mode 100644
index 00000000..85b1258b
--- /dev/null
+++ b/examples/apps/ide/src/frm_main.pas
@@ -0,0 +1,1277 @@
+unit frm_main;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ SysUtils, Classes, fpg_base, fpg_main, fpg_form, fpg_menu, fpg_panel,
+ fpg_button, fpg_splitter, fpg_tab, fpg_memo, fpg_label, fpg_grid,
+ fpg_tree, fpg_textedit, fpg_mru, synregexpr;
+
+type
+
+ TMainForm = class(TfpgForm)
+ private
+ {@VFD_HEAD_BEGIN: MainForm}
+ pnlMenu: TfpgBevel;
+ mainmenu: TfpgMenuBar;
+ Toolbar: TfpgBevel;
+ btnQuit: TfpgButton;
+ btnOpen: TfpgButton;
+ btnSave: TfpgButton;
+ btnSaveAll: TfpgButton;
+ btnTest: TfpgButton;
+ pnlStatusBar: TfpgBevel;
+ lblStatus: TfpgLabel;
+ pnlClientArea: TfpgBevel;
+ pnlWindow: TfpgPageControl;
+ tsMessages: TfpgTabSheet;
+ grdMessages: TfpgStringGrid;
+ tsScribble: TfpgTabSheet;
+ memScribble: TfpgMemo;
+ tsTerminal: TfpgTabSheet;
+ Splitter1: TfpgSplitter;
+ pnlTool: TfpgPageControl;
+ tsProject: TfpgTabSheet;
+ tvProject: TfpgTreeView;
+ tsFiles: TfpgTabSheet;
+ grdFiles: TfpgFileGrid;
+ Splitter2: TfpgSplitter;
+ grdOpenFiles: TfpgStringGrid;
+ Splitter3: TfpgSplitter;
+ pcEditor: TfpgPageControl;
+ tseditor: TfpgTabSheet;
+ TextEditor: TfpgTextEdit;
+ mnuFile: TfpgPopupMenu;
+ mnuEdit: TfpgPopupMenu;
+ mnuSearch: TfpgPopupMenu;
+ mnuView: TfpgPopupMenu;
+ mnuProject: TfpgPopupMenu;
+ mnuRun: TfpgPopupMenu;
+ mnuTools: TfpgPopupMenu;
+ mnuSettings: TfpgPopupMenu;
+ mnuHelp: TfpgPopupMenu;
+ {@VFD_HEAD_END: MainForm}
+ pmOpenRecentMenu: TfpgPopupMenu;
+ miRecentProjects: TfpgMenuItem;
+ FRecentFiles: TfpgMRU;
+ FRegex: TRegExpr;
+ procedure FormShow(Sender: TObject);
+ procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
+ procedure btnQuitClicked(Sender: TObject);
+ procedure btnOpenFileClicked(Sender: TObject);
+ procedure miFileSave(Sender: TObject);
+ procedure miFileSaveAs(Sender: TObject);
+ procedure miSearchProcedureList(Sender: TObject);
+ procedure miAboutFPGuiClicked(Sender: TObject);
+ procedure miAboutIDE(Sender: TObject);
+ procedure miRunMake(Sender: TObject);
+ procedure miRunBuild(Sender: TObject);
+ procedure miRunMake1(Sender: TObject);
+ procedure miRunMake2(Sender: TObject);
+ procedure miRunMake3(Sender: TObject);
+ procedure miRunMake4(Sender: TObject);
+ procedure miConfigureIDE(Sender: TObject);
+ procedure miViewDebug(Sender: TObject);
+ procedure miProjectNew(Sender: TObject);
+ procedure miProjectNewFromTemplate(Sender: TObject);
+ procedure miProjectOptions(Sender: TObject);
+ procedure miProjectOpen(Sender: TObject);
+ procedure miRecentProjectsClick(Sender: TObject; const FileName: String);
+ procedure miProjectSave(Sender: TObject);
+ procedure miProjectSaveAs(Sender: TObject);
+ procedure miProjectAddUnitToProject(Sender: TObject);
+ procedure tvProjectDoubleClick(Sender: TObject; AButton: TMouseButton; AShift: TShiftState; const AMousePos: TPoint);
+ procedure TabSheetClosing(Sender: TObject; ATabSheet: TfpgTabSheet);
+ procedure BuildTerminated(Sender: TObject);
+ procedure BuildOutput(Sender: TObject; const ALine: string);
+ procedure UpdateStatus(const AText: TfpgString);
+ procedure SetupProjectTree;
+ procedure PopuplateProjectTree;
+ procedure SetupFilesGrid;
+ procedure AddMessage(const AMsg: TfpgString);
+ procedure ClearMessagesWindow;
+ procedure CloseAllTabs;
+ procedure LoadProject(const AFilename: TfpgString);
+ function OpenEditorPage(const AFilename: TfpgString): TfpgTabSheet;
+ procedure miTest(Sender: TObject);
+ function GetUnitsNode: TfpgTreeNode;
+ procedure UpdateWindowTitle;
+ procedure TextEditDrawLine(Sender: TObject; ALineText: TfpgString; ALineIndex: Integer; ACanvas: TfpgCanvas; ATextRect: TfpgRect; var AllowSelfDraw: Boolean);
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure AfterCreate; override;
+ end;
+
+{@VFD_NEWFORM_DECL}
+
+implementation
+
+uses
+ process
+ ,fpg_iniutils
+ ,fpg_dialogs
+ ,fpg_utils
+ ,fpg_stringutils
+ ,fpg_constants
+ ,fpg_widget
+ ,frm_configureide
+ ,frm_projectoptions
+ ,frm_debug
+ ,frm_procedurelist
+ ,fpg_basegrid
+ ,ideconst
+ ,idemacros
+ ,Project
+ ,UnitList
+ ,BuilderThread
+ ,dbugintf
+ ,ideutils
+ ;
+
+
+const
+ cTitle = 'fpGUI IDE - %s';
+ cFileFilterTemplate = '%s (%s)|%s';
+ cSourceFiles = '*.pas;*.pp;*.lpr;*.dpr;*.inc';
+ cProjectFiles = '*.project';
+
+ { nicely working so far }
+ cKeywords = '\s*(begin|end|read|write|try|finally|except|uses|interface'
+ + '|implementation|procedure|function|constructor|destructor|property'
+ + '|private|public|published|type|class|unit|program|if|then|for|downto|to'
+ + '|do|else|while|and|inherited|const|var|initialization|finalization)[^0-9a-zA-Z:=;\)\( ]*';
+
+ cComments1 = '\s*\/\/.*$';
+
+{@VFD_NEWFORM_IMPL}
+
+procedure TMainForm.btnQuitClicked(Sender: TObject);
+begin
+ Close;
+end;
+
+procedure TMainForm.btnOpenFileClicked(Sender: TObject);
+var
+ s: TfpgString;
+begin
+ s := SelectFileDialog(sfdOpen, Format(cFileFilterTemplate, ['Source Files', cSourceFiles, cSourceFiles]));
+ if s <> '' then
+ begin
+ OpenEditorPage(s);
+ end;
+end;
+
+procedure TMainForm.miFileSave(Sender: TObject);
+var
+ s: TfpgString;
+begin
+ s := pcEditor.ActivePage.Hint;
+ if s <> '' then
+ TfpgTextEdit(pcEditor.ActivePage.Components[0]).SaveToFile(s);
+end;
+
+procedure TMainForm.miFileSaveAs(Sender: TObject);
+var
+ s: TfpgString;
+begin
+ s := SelectFileDialog(sfdSave);
+ if s <> '' then
+ TfpgTextEdit(pcEditor.ActivePage.Components[0]).SaveToFile(s);
+end;
+
+procedure TMainForm.miSearchProcedureList(Sender: TObject);
+var
+ s: TfpgString;
+ edt: TfpgTextEdit;
+begin
+ s := pcEditor.ActivePage.Hint;
+ if s <> '' then
+ begin
+ edt := TfpgTextEdit(pcEditor.ActivePage.Components[0]);
+ DisplayProcedureList(s, edt);
+ end;
+end;
+
+procedure TMainForm.miAboutFPGuiClicked(Sender: TObject);
+begin
+ TfpgMessageDialog.AboutFPGui;
+end;
+
+procedure TMainForm.miAboutIDE(Sender: TObject);
+begin
+ TfpgMessageDialog.Information('About fpGUI IDE',
+ 'fpGUI IDE version ' + FPGUI_VERSION + LineEnding + LineEnding
+ + 'Created by Graeme Geldenhuys' + LineEnding
+ + 'Compiled with FPC ' + FPCVersion);
+end;
+
+procedure TMainForm.miRunMake(Sender: TObject);
+var
+ thd: TBuilderThread;
+begin
+ ClearMessagesWindow;
+ thd := TBuilderThread.Create(True);
+ thd.OnTerminate := @BuildTerminated;
+ thd.OnAvailableOutput := @BuildOutput;
+ thd.Resume;
+end;
+
+procedure TMainForm.miRunBuild(Sender: TObject);
+var
+ thd: TBuilderThread;
+begin
+ ClearMessagesWindow;
+ thd := TBuilderThread.Create(True);
+ thd.BuildMode := 1;
+ thd.OnTerminate := @BuildTerminated;
+ thd.OnAvailableOutput := @BuildOutput;
+ thd.Resume;
+end;
+
+procedure TMainForm.miRunMake1(Sender: TObject);
+var
+ thd: TBuilderThread;
+begin
+ ClearMessagesWindow;
+ thd := TBuilderThread.Create(True);
+ thd.BuildMode := 2;
+ thd.OnTerminate := @BuildTerminated;
+ thd.OnAvailableOutput := @BuildOutput;
+ thd.Resume;
+end;
+
+procedure TMainForm.miRunMake2(Sender: TObject);
+var
+ thd: TBuilderThread;
+begin
+ ClearMessagesWindow;
+ thd := TBuilderThread.Create(True);
+ thd.BuildMode := 3;
+ thd.OnTerminate := @BuildTerminated;
+ thd.OnAvailableOutput := @BuildOutput;
+ thd.Resume;
+end;
+
+procedure TMainForm.miRunMake3(Sender: TObject);
+var
+ thd: TBuilderThread;
+begin
+ ClearMessagesWindow;
+ thd := TBuilderThread.Create(True);
+ thd.BuildMode := 4;
+ thd.OnTerminate := @BuildTerminated;
+ thd.OnAvailableOutput := @BuildOutput;
+ thd.Resume;
+end;
+
+procedure TMainForm.miRunMake4(Sender: TObject);
+var
+ thd: TBuilderThread;
+begin
+ ClearMessagesWindow;
+ thd := TBuilderThread.Create(True);
+ thd.BuildMode := 5;
+ thd.OnTerminate := @BuildTerminated;
+ thd.OnAvailableOutput := @BuildOutput;
+ thd.Resume;
+end;
+
+procedure TMainForm.miConfigureIDE(Sender: TObject);
+begin
+ DisplayConfigureIDE;
+ pcEditor.TabPosition := TfpgTabPosition(gINI.ReadInteger(cEditor, 'TabPosition', 0));
+end;
+
+procedure TMainForm.miViewDebug(Sender: TObject);
+begin
+ if not Assigned(DebugForm) then
+ fpgApplication.CreateForm(TDebugForm, TfpgWindowBase(DebugForm));
+ DebugForm.Show;
+end;
+
+procedure TMainForm.miProjectNew(Sender: TObject);
+begin
+ CloseAllTabs;
+ FreeProject;
+ GProject.ProjectName := 'empty.project';
+ GProject.MainUnit := 'empty.pas';
+ OpenEditorPage(GProject.MainUnit);
+ miProjectSaveAs(nil);
+end;
+
+procedure TMainForm.miProjectNewFromTemplate(Sender: TObject);
+var
+ dlg: TfpgFileDialog;
+ lFilename: TfpgString;
+begin
+ CloseAllTabs;
+ FreeProject;
+ dlg := TfpgFileDialog.Create(nil);
+ try
+ dlg.InitialDir := GMacroList.ExpandMacro(cMacro_TemplateDir);
+ dlg.Filter := Format(cFileFilterTemplate, ['Project Files', cProjectFiles, cProjectFiles])
+ + '|' + rsAllFiles+' ('+AllFilesMask+')'+'|'+AllFilesMask;
+ if dlg.RunOpenFile then
+ begin
+ lFilename := dlg.FileName;
+ GProject.Load(lFilename);
+ end;
+ finally
+ dlg.Free;
+ end;
+end;
+
+procedure TMainForm.miProjectOptions(Sender: TObject);
+begin
+ DisplayProjectOptions;
+end;
+
+procedure TMainForm.miProjectOpen(Sender: TObject);
+var
+ s: TfpgString;
+begin
+ s := SelectFileDialog(sfdOpen, Format(cFileFilterTemplate, ['Project Files', cProjectFiles, cProjectFiles]));
+ if s <> '' then
+ begin
+ LoadProject(s);
+ end;
+end;
+
+procedure TMainForm.miRecentProjectsClick(Sender: TObject; const FileName: String);
+begin
+ LoadProject(Filename);
+end;
+
+procedure TMainForm.miProjectSave(Sender: TObject);
+begin
+ try
+ GProject.Save;
+ except
+ on E: Exception do
+ begin
+ TfpgMessageDialog.Critical('', E.Message);
+ end;
+ end;
+ AddMessage('Project saved.');
+end;
+
+procedure TMainForm.miProjectSaveAs(Sender: TObject);
+var
+ s: TfpgString;
+begin
+ s := SelectFileDialog(sfdSave, Format(cFileFilterTemplate, ['Project Files', cProjectFiles, cProjectFiles]));
+ if s <> '' then
+ begin
+ if fpgExtractFileExt(s) = '' then
+ s := s + cProjectExt;
+ try
+ GProject.Save(s);
+ except
+ on E: Exception do
+ begin
+ TfpgMessageDialog.Critical('', E.Message);
+ end;
+ end;
+ UpdateWindowTitle;
+ AddMessage(Format('Project saved as <%s>.', [s]));
+ end;
+end;
+
+procedure TMainForm.miProjectAddUnitToProject(Sender: TObject);
+var
+ u: TUnit;
+ s: TfpgString;
+ r: TfpgTreeNode;
+ n: TfpgTreeNode;
+begin
+ s := pcEditor.ActivePage.Hint;
+// writeln('adding unit: ', s);
+ if s = '' then
+ Exit;
+ if GProject.UnitList.FileExists(s) then
+ Exit;
+ u := TUnit.Create;
+ u.FileName := s;
+ u.Opened := True;
+ GProject.UnitList.Add(u);
+ // add reference to tabsheet
+ pcEditor.ActivePage.TagPointer := u;
+ s := ExtractRelativepath(GProject.ProjectDir, u.FileName);
+ r := GetUnitsNode;
+ n := r.AppendText(s);
+ // add reference to treenode
+ n.Data := u;
+ tvProject.Invalidate;
+end;
+
+procedure TMainForm.tvProjectDoubleClick(Sender: TObject; AButton: TMouseButton; AShift: TShiftState; const AMousePos: TPoint);
+var
+ r: TfpgTreeNode;
+ n: TfpgTreeNode;
+ ts: TfpgTabSheet;
+ u: TUnit;
+begin
+ r := GetUnitsNode;
+ n := tvProject.Selection;
+ if n.Data <> nil then
+ u := TUnit(n.Data);
+ if u <> nil then
+ begin
+ ts := OpenEditorPage(u.FileName);
+ u.Opened := True;
+ ts.TagPointer := u; // add reference to tabsheet
+ end;
+end;
+
+procedure TMainForm.TabSheetClosing(Sender: TObject; ATabSheet: TfpgTabSheet);
+var
+ u: TUnit;
+begin
+ u := TUnit(ATabSheet.TagPointer);
+ if Assigned(u) then
+ u.Opened := False;
+end;
+
+procedure TMainForm.BuildTerminated(Sender: TObject);
+begin
+ AddMessage('Compilation complete');
+end;
+
+procedure TMainForm.BuildOutput(Sender: TObject; const ALine: string);
+begin
+ AddMessage(ALine);
+end;
+
+procedure TMainForm.UpdateStatus(const AText: TfpgString);
+begin
+ lblStatus.Text := AText;
+end;
+
+procedure TMainForm.SetupProjectTree;
+begin
+ tvProject.RootNode.Clear;
+ tvProject.RootNode.AppendText('Units');
+ tvProject.RootNode.AppendText('Images');
+ tvProject.RootNode.AppendText('Help Files');
+ tvProject.RootNode.AppendText('Text');
+ tvProject.RootNode.AppendText('Other');
+end;
+
+procedure TMainForm.PopuplateProjectTree;
+var
+ r: TfpgTreeNode;
+ n: TfpgTreeNode;
+ i: integer;
+ s: TfpgString;
+begin
+ r := GetUnitsNode;
+ tvProject.Selection := r;
+ if Assigned(r) then // just to be safe, but 'Units' should always exist
+ begin
+ for i := 0 to GProject.UnitList.Count-1 do
+ begin
+ {$Note ExtractRelativePath still needs a fpGUI wrapper }
+ s := ExtractRelativepath(GProject.ProjectDir, GProject.UnitList[i].FileName);
+ n := r.AppendText(s);
+ n.Data := GProject.UnitList[i];
+ end;
+ end;
+ r.Expand;
+ tvProject.Invalidate;
+end;
+
+procedure TMainForm.SetupFilesGrid;
+begin
+ grdFiles.FileList.FileMask := AllFilesMask;
+ grdFiles.FileList.ShowHidden := False;
+ grdFiles.FileList.ReadDirectory;
+ grdFiles.FileList.Sort(soFileName);
+ grdFiles.Invalidate;
+end;
+
+procedure TMainForm.AddMessage(const AMsg: TfpgString);
+begin
+ grdMessages.BeginUpdate;
+ grdMessages.RowCount := grdMessages.RowCount + 1;
+ grdMessages.Cells[0,grdMessages.RowCount-1] := AMsg;
+ grdMessages.FocusRow := grdMessages.RowCount;
+ grdMessages.EndUpdate;
+// fpgApplication.ProcessMessages;
+end;
+
+procedure TMainForm.ClearMessagesWindow;
+begin
+ grdMessages.RowCount := 0;
+end;
+
+procedure TMainForm.CloseAllTabs;
+var
+ ts: TfpgTabSheet;
+ i: integer;
+begin
+ for i := 0 to pcEditor.PageCount-1 do
+ begin
+ ts := pcEditor.Pages[0];
+ pcEditor.RemoveTabSheet(ts);
+ ts.Free;
+ end;
+end;
+
+procedure TMainForm.LoadProject(const AFilename: TfpgString);
+var
+ i: integer;
+ ts: TfpgTabSheet;
+begin
+ // remove all project info
+ CloseAllTabs;
+ SetupProjectTree;
+ FreeProject;
+ // now load new project info
+ GProject.Load(AFilename);
+ FRecentFiles.AddItem(AFilename);
+ for i := 0 to GProject.UnitList.Count-1 do
+ begin
+ if GProject.UnitList[i].Opened then
+ begin
+ ts := OpenEditorPage(GProject.UnitList[i].FileName);
+ ts.TagPointer := GProject.UnitList[i];
+ end;
+ end;
+ PopuplateProjectTree;
+ UpdateWindowTitle;
+ AddMessage('Project loaded');
+end;
+
+function TMainForm.OpenEditorPage(const AFilename: TfpgString): TfpgTabSheet;
+var
+ s: TfpgString;
+ f: TfpgString;
+ i: integer;
+ found: Boolean;
+ ts: TfpgTabSheet;
+ m: TfpgTextEdit;
+ ext: TfpgString;
+begin
+ s := AFilename;
+ f := fpgExtractFileName(s);
+ found := False;
+ for i := 0 to pcEditor.PageCount-1 do
+ begin
+ if pcEditor.Pages[i].Text = f then
+ found := True;
+ if found then
+ break;
+ end;
+ if found then
+ begin
+ // reuse existing tab
+ TfpgTextEdit(pcEditor.Pages[i].Components[0]).Lines.BeginUpdate;
+ TfpgTextEdit(pcEditor.Pages[i].Components[0]).LoadFromFile(s);
+ TfpgTextEdit(pcEditor.Pages[i].Components[0]).Lines.EndUpdate;
+ pcEditor.ActivePageIndex := i;
+ ts := pcEditor.ActivePage;
+ end
+ else
+ begin
+ // we need a new tabsheet
+ ts := pcEditor.AppendTabSheet(f);
+ m := TfpgTextEdit.Create(ts);
+ m.SetPosition(1, 1, 200, 20);
+ m.Align := alClient;
+ m.FontDesc := gINI.ReadString(cEditor, 'Font', '#Edit2');
+ m.GutterVisible := True;
+ m.GutterShowLineNumbers := True;
+ m.RightEdge := True;
+ TfpgTextEdit(ts.Components[0]).Lines.BeginUpdate;
+ TfpgTextEdit(ts.Components[0]).Lines.LoadFromFile(s);
+ TfpgTextEdit(ts.Components[0]).Lines.EndUpdate;
+ if gINI.ReadBool(cEditor, 'SyntaxHighlighting', True) then
+ begin
+ ext := fpgExtractFileExt(AFilename);
+ if (ext = '.pas') or (ext = '.pp') or (ext = '.inc') then
+ TfpgTextEdit(ts.Components[0]).OnDrawLine := @TextEditDrawLine;
+ end;
+ ts.Realign;
+ pcEditor.ActivePage := ts;
+ end;
+ ts.Hint := s;
+ Result := ts;
+ UpdateStatus(s);
+end;
+
+procedure TMainForm.miTest(Sender: TObject);
+var
+ s: TfpgString;
+ r: TfpgString;
+begin
+ TempHourGlassCursor(TfpgWidget(self));
+ s := cMacro_Compiler + ' -FU' +cMacro_Target+' -Fu' + cMacro_FPGuiLibDir;
+// writeln('source string = ', s);
+ r := GMacroList.ExpandMacro(s);
+// writeln('expanded string = ', r);
+ sleep(5000);
+end;
+
+function TMainForm.GetUnitsNode: TfpgTreeNode;
+begin
+ Result := tvProject.RootNode.FindSubNode('Units', True);
+end;
+
+procedure TMainForm.UpdateWindowTitle;
+begin
+ WindowTitle := Format(cTitle, [GProject.ProjectName]);
+end;
+
+const
+ cReservedWords: array[1..50] of string =
+ ('begin', 'end', 'program', 'procedure', 'var',
+ 'uses', 'type', 'const', 'if', 'then',
+ 'for', 'do', 'unit', 'interface', 'implementation',
+ 'initialization', 'finalization', 'with', 'case', 'private',
+ 'protected', 'public', 'published', 'override', 'virtual',
+ 'class', 'record', 'function', 'property', 'to',
+ 'else', 'finally', 'while', 'except', 'try',
+ 'constructor', 'destructor', 'read', 'write', 'out',
+ 'default', 'not', 'and', 'in', 'raise',
+ 'of', 'resourcestring', 'operator', 'inherited', 'array');
+
+procedure TMainForm.TextEditDrawLine(Sender: TObject; ALineText: TfpgString;
+ ALineIndex: Integer; ACanvas: TfpgCanvas; ATextRect: TfpgRect;
+ var AllowSelfDraw: Boolean);
+var
+ oldfont, newfont: TfpgFont;
+ s: TfpgString; // copy of ALineText we work with
+ i, j, c: integer; // i = position of reserved word; c = last character pos
+ iLength: integer; // length of reserved word
+ w: integer; // reserved word loop variable
+ r: TfpgRect; // string rectangle to draw in
+ edt: TfpgTextEdit;
+ lMatchPos, lOffset: integer; // user for regex
+
+ procedure TestFurther(var AIndex: integer);
+ begin
+ if AIndex = 0 then
+ begin
+ AIndex := UTF8Pos(cReservedWords[w], s);
+ if (AIndex > 0) then
+ begin
+ if (AIndex+iLength-1 <> Length(s)) and not (s[AIndex+iLength] in [';', '.', '(', #10, #13]) then
+ AIndex := 0;
+ end;
+ end;
+ end;
+
+begin
+// writeln('syntax highlight line: ', ALineIndex);
+ edt := TfpgTextEdit(Sender);
+ AllowSelfDraw := False;
+
+ oldfont := TfpgFont(ACanvas.Font);
+ ACanvas.Color := clWhite;
+
+ { draw the plain text first }
+ ACanvas.TextColor := clBlack;
+ ACanvas.DrawText(ATextRect, ALineText);
+
+ lMatchPos := 0;
+ lOffset := 0;
+
+ { syntax highlighting for: keywords }
+ newfont := fpgGetFont(edt.FontDesc + ':bold');
+ ACanvas.Font := newfont;
+ FRegex.Expression := cKeywords;
+ if FRegex.Exec(ALineText) then
+ begin
+ for i := 1 to FRegex.SubExprMatchCount do
+ begin
+ lMatchPos := FRegex.MatchPos[i];
+ lOffset := FRegex.MatchLen[i];
+ s := FRegex.Match[i];
+ j := Length(s);
+ r.SetRect(ATextRect.Left + (edt.FontWidth * (lMatchPos-1)), ATextRect.Top,
+ (edt.FontWidth * j), ATextRect.Height);
+ ACanvas.FillRectangle(r);
+ ACanvas.DrawText(r, s);
+ end;
+ end;
+
+ { syntax highlighting for: comments }
+ ACanvas.Font := oldfont;
+ ACanvas.TextColor := clDarkCyan;
+ FRegex.Expression := cComments1;
+ if FRegex.Exec(ALineText) then
+ begin
+ if FRegex.SubExprMatchCount = 0 then
+ begin
+ i := 0;
+ lMatchPos := FRegex.MatchPos[i];
+ lOffset := FRegex.MatchLen[i];
+ s := FRegex.Match[i];
+ j := Length(s);
+ r.SetRect(ATextRect.Left + (edt.FontWidth * (lMatchPos-1)), ATextRect.Top,
+ (edt.FontWidth * j), ATextRect.Height);
+ ACanvas.FillRectangle(r);
+ ACanvas.DrawText(r, s);
+ end
+ else
+ begin
+ for i := 1 to FRegex.SubExprMatchCount do
+ begin
+ lMatchPos := FRegex.MatchPos[i];
+ lOffset := FRegex.MatchLen[i];
+ s := FRegex.Match[i];
+ j := Length(s);
+ r.SetRect(ATextRect.Left + (edt.FontWidth * (lMatchPos-1)), ATextRect.Top,
+ (edt.FontWidth * j), ATextRect.Height);
+ ACanvas.FillRectangle(r);
+ ACanvas.DrawText(r, s);
+ end;
+ end;
+ end;
+
+ ACanvas.Font := oldfont;
+ newfont.Free;
+// writeln('------');
+end;
+
+procedure TMainForm.FormShow(Sender: TObject);
+var
+ lErrPos: integer;
+begin
+ Left := gINI.ReadInteger(Name + 'State', 'Left', Left);
+ Top := gINI.ReadInteger(Name + 'State', 'Top', Top);
+ Width := gINI.ReadInteger(Name + 'State', 'Width', Width);
+ Height := gINI.ReadInteger(Name + 'State', 'Height', Height);
+ UpdateWindowPosition;
+
+ SetupProjectTree;
+ SetupFilesGrid;
+
+ // apply editor settings
+ pcEditor.TabPosition := TfpgTabPosition(gINI.ReadInteger(cEditor, 'TabPosition', 0));
+ FRegex := TRegExpr.Create;
+ FRegex.Expression := cKeywords;
+end;
+
+procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
+begin
+ CloseAction := caFree;
+ gINI.WriteInteger(Name + 'State', 'Left', Left);
+ gINI.WriteInteger(Name + 'State', 'Top', Top);
+ gINI.WriteInteger(Name + 'State', 'Width', Width);
+ gINI.WriteInteger(Name + 'State', 'Height', Height);
+end;
+
+constructor TMainForm.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ OnShow := @FormShow;
+ OnClose := @FormClose;
+ SendDebug('TMainForm.Create');
+end;
+
+destructor TMainForm.Destroy;
+begin
+ FRegex.Free;
+ inherited Destroy;
+end;
+
+procedure TMainForm.AfterCreate;
+begin
+ SendMethodEnter('TMainForm.AfterCreate');
+ {%region 'Auto-generated GUI code' -fold}
+ {@VFD_BODY_BEGIN: MainForm}
+ Name := 'MainForm';
+ SetPosition(310, 206, 638, 428);
+ WindowTitle := 'fpGUI IDE - %s';
+ Hint := '';
+ WindowPosition := wpOneThirdDown;
+
+ pnlMenu := TfpgBevel.Create(self);
+ with pnlMenu do
+ begin
+ Name := 'pnlMenu';
+ SetPosition(0, 0, 638, 54);
+ Align := alTop;
+ Hint := '';
+ Shape := bsSpacer;
+ end;
+
+ mainmenu := TfpgMenuBar.Create(pnlMenu);
+ with mainmenu do
+ begin
+ Name := 'mainmenu';
+ SetPosition(0, 0, 638, 24);
+ Anchors := [anLeft,anRight,anTop];
+ Align := alTop;
+ end;
+
+ Toolbar := TfpgBevel.Create(pnlMenu);
+ with Toolbar do
+ begin
+ Name := 'Toolbar';
+ SetPosition(2, 2, 634, 50);
+ Anchors := [anLeft,anRight,anTop];
+ Align := alClient;
+ Hint := '';
+ Shape := bsSpacer;
+ end;
+
+ btnQuit := TfpgButton.Create(Toolbar);
+ with btnQuit do
+ begin
+ Name := 'btnQuit';
+ SetPosition(4, 2, 24, 24);
+ Text := '';
+ Down := False;
+ Embedded := True;
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageMargin := 0;
+ ImageName := 'stdimg.quit';
+ TabOrder := 3;
+ OnClick := @btnQuitClicked;
+ end;
+
+ btnOpen := TfpgButton.Create(Toolbar);
+ with btnOpen do
+ begin
+ Name := 'btnOpen';
+ SetPosition(28, 2, 24, 24);
+ Text := '';
+ Down := False;
+ Embedded := True;
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageMargin := 0;
+ ImageName := 'stdimg.open';
+ TabOrder := 4;
+ OnClick := @btnOpenFileClicked;
+ end;
+
+ btnSave := TfpgButton.Create(Toolbar);
+ with btnSave do
+ begin
+ Name := 'btnSave';
+ SetPosition(56, 2, 24, 24);
+ Text := '';
+ Down := False;
+ Embedded := True;
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageMargin := 0;
+ ImageName := 'stdimg.save';
+ TabOrder := 5;
+ OnClick := @miFileSave;
+ end;
+
+ btnSaveAll := TfpgButton.Create(Toolbar);
+ with btnSaveAll do
+ begin
+ Name := 'btnSaveAll';
+ SetPosition(80, 2, 24, 24);
+ Text := '';
+ Down := False;
+ Embedded := True;
+ Enabled := False;
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageMargin := 0;
+ ImageName := 'stdimg.saveall';
+ TabOrder := 6;
+ end;
+
+ btnTest := TfpgButton.Create(Toolbar);
+ with btnTest do
+ begin
+ Name := 'btnTest';
+ SetPosition(168, 2, 80, 24);
+ Text := 'test';
+ Down := False;
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageName := '';
+ TabOrder := 7;
+ OnClick := @miTest;
+ end;
+
+ pnlStatusBar := TfpgBevel.Create(self);
+ with pnlStatusBar do
+ begin
+ Name := 'pnlStatusBar';
+ SetPosition(0, 408, 636, 20);
+ Anchors := [anLeft,anRight,anBottom];
+ Hint := '';
+ Style := bsLowered;
+ end;
+
+ lblStatus := TfpgLabel.Create(pnlStatusBar);
+ with lblStatus do
+ begin
+ Name := 'lblStatus';
+ SetPosition(2, 2, 632, 16);
+ Anchors := [anLeft,anRight,anTop];
+ Align := alBottom;
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := '';
+ end;
+
+ pnlClientArea := TfpgBevel.Create(self);
+ with pnlClientArea do
+ begin
+ Name := 'pnlClientArea';
+ SetPosition(0, 54, 638, 374);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ Align := alClient;
+ Hint := '';
+ Shape := bsSpacer;
+ end;
+
+ pnlWindow := TfpgPageControl.Create(pnlClientArea);
+ with pnlWindow do
+ begin
+ Name := 'pnlWindow';
+ SetPosition(2, 288, 634, 84);
+ ActivePageIndex := 0;
+ Align := alBottom;
+ Hint := '';
+ TabOrder := 11;
+ TabPosition := tpRight;
+ end;
+
+ tsMessages := TfpgTabSheet.Create(pnlWindow);
+ with tsMessages do
+ begin
+ Name := 'tsMessages';
+ SetPosition(73, 3, 558, 78);
+ Text := 'Messages';
+ end;
+
+ grdMessages := TfpgStringGrid.Create(tsMessages);
+ with grdMessages do
+ begin
+ Name := 'grdMessages';
+ SetPosition(0, 4, 558, 73);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ BackgroundColor := TfpgColor($80000002);
+ AddColumn('New', 800, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 0;
+ RowSelect := True;
+ ShowHeader := False;
+ TabOrder := 13;
+ end;
+
+ tsScribble := TfpgTabSheet.Create(pnlWindow);
+ with tsScribble do
+ begin
+ Name := 'tsScribble';
+ SetPosition(73, 3, 188, 78);
+ Text := 'Scribble';
+ end;
+
+ memScribble := TfpgMemo.Create(tsScribble);
+ with memScribble do
+ begin
+ Name := 'memScribble';
+ SetPosition(0, 4, 187, 73);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ FontDesc := '#Edit2';
+ Hint := '';
+ Lines.Add('Make notes, use it as a clipboard');
+ Lines.Add('or type whatever you want...');
+ TabOrder := 15;
+ end;
+
+ tsTerminal := TfpgTabSheet.Create(pnlWindow);
+ with tsTerminal do
+ begin
+ Name := 'tsTerminal';
+ SetPosition(73, 3, 188, 78);
+ Text := 'Terminal';
+ end;
+
+ Splitter1 := TfpgSplitter.Create(pnlClientArea);
+ with Splitter1 do
+ begin
+ Name := 'Splitter1';
+ SetPosition(2, 281, 634, 7);
+ Align := alBottom;
+ end;
+
+ pnlTool := TfpgPageControl.Create(pnlClientArea);
+ with pnlTool do
+ begin
+ Name := 'pnlTool';
+ SetPosition(2, 2, 140, 279);
+ ActivePageIndex := 0;
+ Align := alLeft;
+ Hint := '';
+ TabOrder := 18;
+ end;
+
+ tsProject := TfpgTabSheet.Create(pnlTool);
+ with tsProject do
+ begin
+ Name := 'tsProject';
+ SetPosition(3, 24, 134, 252);
+ Text := 'Project';
+ end;
+
+ tvProject := TfpgTreeView.Create(tsProject);
+ with tvProject do
+ begin
+ Name := 'tvProject';
+ SetPosition(1, 1, 132, 250);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ FontDesc := '#Label1';
+ Hint := '';
+ TabOrder := 20;
+ OnDoubleClick := @tvProjectDoubleClick;
+ end;
+
+ tsFiles := TfpgTabSheet.Create(pnlTool);
+ with tsFiles do
+ begin
+ Name := 'tsFiles';
+ SetPosition(3, 24, 134, 193);
+ Text := 'Files';
+ end;
+
+ grdFiles := TfpgFileGrid.Create(tsFiles);
+ with grdFiles do
+ begin
+ Name := 'grdFiles';
+ SetPosition(1, 1, 131, 190);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ Options := Options + [go_SmoothScroll];
+ end;
+
+ Splitter2 := TfpgSplitter.Create(pnlClientArea);
+ with Splitter2 do
+ begin
+ Name := 'Splitter2';
+ SetPosition(142, 2, 8, 279);
+ Align := alLeft;
+ end;
+
+ grdOpenFiles := TfpgStringGrid.Create(pnlClientArea);
+ with grdOpenFiles do
+ begin
+ Name := 'grdOpenFiles';
+ SetPosition(516, 2, 120, 279);
+ Align := alRight;
+ BackgroundColor := TfpgColor($80000002);
+ AddColumn('File', 100, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 0;
+ RowSelect := True;
+ ShowHeader := False;
+ TabOrder := 24;
+ end;
+
+ Splitter3 := TfpgSplitter.Create(pnlClientArea);
+ with Splitter3 do
+ begin
+ Name := 'Splitter3';
+ SetPosition(508, 2, 8, 279);
+ Align := alRight;
+ end;
+
+ pcEditor := TfpgPageControl.Create(pnlClientArea);
+ with pcEditor do
+ begin
+ Name := 'pcEditor';
+ SetPosition(150, 2, 358, 279);
+ ActivePageIndex := 0;
+ Align := alClient;
+ Hint := '';
+ TabOrder := 18;
+ TabPosition := tpRight;
+ Options := Options + [to_PMenuClose];
+ OnClosingTabSheet := @TabSheetClosing;
+ end;
+
+ tseditor := TfpgTabSheet.Create(pcEditor);
+ with tseditor do
+ begin
+ Name := 'tseditor';
+ SetPosition(3, 3, 282, 273);
+ Text := 'Tabsheet1';
+ end;
+
+ TextEditor := TfpgTextEdit.Create(tseditor);
+ with TextEditor do
+ begin
+ Name := 'TextEditor';
+ SetPosition(0, 0, 130, 200);
+ Align := alClient;
+ end;
+
+ mnuFile := TfpgPopupMenu.Create(self);
+ with mnuFile do
+ begin
+ Name := 'mnuFile';
+ SetPosition(476, 61, 172, 20);
+ AddMenuItem('New...', '', nil).Enabled := False;
+ AddMenuItem('Open...', '', nil).Enabled := False;
+ AddMenuItem('Open Recent', '', nil).Enabled := False;
+ AddMenuItem('Save', '', @miFileSave);
+ AddMenuItem('Save As...', '', @miFileSaveAs);
+ AddMenuItem('-', '', nil);
+ AddMenuItem('Quit', '', @btnQuitClicked);
+ end;
+
+ mnuEdit := TfpgPopupMenu.Create(self);
+ with mnuEdit do
+ begin
+ Name := 'mnuEdit';
+ SetPosition(476, 80, 172, 20);
+ AddMenuItem('Cut', '', nil).Enabled := False;
+ AddMenuItem('Copy', '', nil).Enabled := False;
+ AddMenuItem('Paste', '', nil).Enabled := False;
+ end;
+
+ mnuSearch := TfpgPopupMenu.Create(self);
+ with mnuSearch do
+ begin
+ Name := 'mnuSearch';
+ SetPosition(476, 98, 172, 20);
+ AddMenuItem('Find...', '', nil).Enabled := False;
+ AddMenuItem('Find in Files...', '', nil).Enabled := False;
+ AddMenuItem('Procedure List...', '', @miSearchProcedureList);
+ end;
+
+ mnuView := TfpgPopupMenu.Create(self);
+ with mnuView do
+ begin
+ Name := 'mnuView';
+ SetPosition(476, 119, 172, 20);
+ AddMenuItem('Todo List...', '', nil).Enabled := False;
+ AddMenuItem('Debug Windows', '', @miViewDebug);
+ end;
+
+ mnuProject := TfpgPopupMenu.Create(self);
+ with mnuProject do
+ begin
+ Name := 'mnuProject';
+ SetPosition(476, 140, 172, 20);
+ AddMenuItem('Options...', '', @miProjectOptions);
+ AddMenuItem('-', '', nil);
+ AddMenuItem('New (empty)...', '', @miProjectNew);
+ AddMenuItem('New from Template...', '', @miProjectNewFromTemplate);
+ AddMenuItem('Open...', '', @miProjectOpen);
+ miRecentProjects := AddMenuItem('Open Recent', '', nil);
+ AddMenuItem('Save', '', @miProjectSave);
+ AddMenuItem('Save As...', '', @miProjectSaveAs);
+ AddMenuItem('-', '', nil);
+ AddMenuItem('View Source', '', nil);
+ AddMenuItem('Add editor file to Project', '', @miProjectAddUnitToProject);
+ end;
+
+ mnuRun := TfpgPopupMenu.Create(self);
+ with mnuRun do
+ begin
+ Name := 'mnuRun';
+ SetPosition(476, 161, 172, 20);
+ AddMenuItem('Make', 'Ctrl+F9', @miRunMake);
+ AddMenuItem('Build All', 'Ctrl+Shift+F9', @miRunBuild);
+ AddMenuItem('Make 1', 'Ctrl+Alt+1', @miRunMake1);
+ AddMenuItem('Make 2', 'Ctrl+Alt+2', @miRunMake2);
+ AddMenuItem('Make 3', 'Ctrl+Alt+3', @miRunMake3);
+ AddMenuItem('Make 4', 'Ctrl+Alt+4', @miRunMake4);
+ AddMenuItem('-', '', nil);
+ AddMenuItem('Run', 'F9', nil);
+ AddMenuItem('Run Parameters...', '', nil);
+ end;
+
+ mnuTools := TfpgPopupMenu.Create(self);
+ with mnuTools do
+ begin
+ Name := 'mnuTools';
+ SetPosition(476, 182, 172, 20);
+ AddMenuItem('fpGUI UI Designer...', '', nil);
+ AddMenuItem('fpGUI DocView...', '', nil);
+ end;
+
+ mnuSettings := TfpgPopupMenu.Create(self);
+ with mnuSettings do
+ begin
+ Name := 'mnuSettings';
+ SetPosition(476, 203, 172, 20);
+ AddMenuItem('Configure IDE...', '', @miConfigureIDE);
+ end;
+
+ mnuHelp := TfpgPopupMenu.Create(self);
+ with mnuHelp do
+ begin
+ Name := 'mnuHelp';
+ SetPosition(476, 224, 172, 20);
+ AddMenuItem('Contents...', '', nil);
+ AddMenuItem('-', '', nil);
+ AddMenuItem('About fpGUI Toolkit...', '', @miAboutFPGuiClicked);
+ AddMenuItem('About fpGUI IDE...', '', @miAboutIDE);
+ end;
+
+ {@VFD_BODY_END: MainForm}
+ {%endregion}
+
+{
+ pcEditor.AppendTabSheet('Five');
+ pcEditor.AppendTabSheet('Six');
+ pcEditor.AppendTabSheet('Seven');
+ pcEditor.AppendTabSheet('Eight');
+ pcEditor.AppendTabSheet('Nine');
+ pcEditor.AppendTabSheet('Ten');
+ pcEditor.AppendTabSheet('11');
+ pcEditor.AppendTabSheet('12');
+ pcEditor.AppendTabSheet('13');
+ pcEditor.AppendTabSheet('14');
+ pcEditor.AppendTabSheet('15');
+ pcEditor.AppendTabSheet('16');
+ pcEditor.AppendTabSheet('17');
+ pcEditor.AppendTabSheet('18');
+ pcEditor.AppendTabSheet('19');
+ pcEditor.AppendTabSheet('20');
+}
+
+ mainmenu.AddMenuItem('&File', nil).SubMenu := mnuFile;
+ mainmenu.AddMenuItem('&Edit', nil).SubMenu := mnuEdit;
+ mainmenu.AddMenuItem('&Search', nil).SubMenu := mnuSearch;
+ mainmenu.AddMenuItem('&View', nil).SubMenu := mnuView;
+ mainmenu.AddMenuItem('&Project', nil).SubMenu := mnuProject;
+ mainmenu.AddMenuItem('&Run', nil).SubMenu := mnuRun;
+ mainmenu.AddMenuItem('&Tools', nil).SubMenu := mnuTools;
+ mainmenu.AddMenuItem('&Settings', nil).SubMenu := mnuSettings;
+ mainmenu.AddMenuItem('&Help', nil).SubMenu := mnuHelp;
+
+ pmOpenRecentMenu := TfpgPopupMenu.Create(self);
+ with pmOpenRecentMenu do
+ begin
+ Name := 'pmOpenRecentMenu';
+ SetPosition(336, 68, 128, 20);
+ end;
+
+ miRecentProjects.SubMenu := pmOpenRecentMenu;
+
+ FRecentFiles := TfpgMRU.Create(self);
+ FRecentFiles.ParentMenuItem := pmOpenRecentMenu;
+ FRecentFiles.OnClick :=@miRecentProjectsClick;
+ FRecentFiles.MaxItems := gINI.ReadInteger('Options', 'MRUProjectCount', 10);
+ FRecentFiles.ShowFullPath := gINI.ReadBool('Options', 'ShowFullPath', True);
+ FRecentFiles.LoadMRU;
+
+
+ SendMethodExit('TMainForm.AfterCreate');
+end;
+
+
+end.
diff --git a/examples/apps/ide/src/frm_procedurelist.pas b/examples/apps/ide/src/frm_procedurelist.pas
new file mode 100644
index 00000000..4b5b7548
--- /dev/null
+++ b/examples/apps/ide/src/frm_procedurelist.pas
@@ -0,0 +1,1333 @@
+unit frm_procedurelist;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ SysUtils, Classes, fpg_base, fpg_main, fpg_form, fpg_panel, fpg_label,
+ fpg_edit, fpg_combobox, fpg_basegrid, fpg_grid, fpg_imagelist,
+ pparser, pastree, fpg_textedit;
+
+type
+
+ TSimpleEngine = class(TPasTreeContainer)
+ public
+ function CreateElement(AClass: TPTreeElement; const AName: String;
+ AParent: TPasElement; AVisibility: TPasMemberVisibility;
+ const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override;
+ function FindElement(const AName: String): TPasElement; override;
+ end;
+
+ TSourceLanguage = (ltPas, ltCpp);
+
+ TProcInfo = class(TObject)
+ private
+ FLineNo: Integer;
+ FName: string;
+ FDisplayName: string;
+ FProcedureType: string;
+ FProcArgs: string;
+ FProcClass: string;
+ FProcReturnType: string;
+ FProcName: string;
+ FProcIndex: Integer;
+ public
+ property LineNo: Integer read FLineNo write FLineNo;
+ property Name: string read FName write FName;
+ property DisplayName: string read FDisplayName write FDisplayName;
+ property ProcedureType: string read FProcedureType write FProcedureType;
+ property ProcArgs: string read FProcArgs write FProcArgs;
+ property ProcName: string read FProcName write FProcName;
+ property ProcClass: string read FProcClass write FProcClass;
+ property ProcReturnType: string read FProcReturnType write FProcReturnType;
+ property ProcIndex: Integer read FProcIndex write FProcIndex;
+ end;
+
+
+ TProcedureListForm = class(TfpgForm)
+ private
+ procedure SearchTextChanged(Sender: TObject);
+ procedure SearchEditKeyPressed(Sender: TObject; var KeyCode: word; var ShiftState: TShiftState; var Consumed: boolean);
+ procedure SetFilename(const AValue: string);
+ private
+ {@VFD_HEAD_BEGIN: ProcedureListForm}
+ Bevel1: TfpgBevel;
+ Bevel2: TfpgBevel;
+ lblSearch: TfpgLabel;
+ edtSearch: TfpgEdit;
+ cbObjects: TfpgComboBox;
+ lblObjects: TfpgLabel;
+ grdProcedures: TfpgStringGrid;
+ StatusBar: TfpgPanel;
+ {@VFD_HEAD_END: ProcedureListForm}
+ FFilename: TfpgString;
+ FLanguage: TSourceLanguage;
+ FSortOnColumn: Integer;
+ FSearchAll: Boolean;
+ FProcList: TStringList;
+ FObjectStrings: TStringList;
+ FEditor: TfpgTextEdit;
+// FImageList: TfpgImageList;
+ procedure FormShow(Sender: TObject);
+ procedure LoadProcs;
+ procedure AddProcedure(ProcedureInfo: TProcInfo);
+ procedure ClearObjectStrings;
+ procedure LoadObjectCombobox;
+ procedure QuickSort(L, R: Integer);
+ procedure InitializeForm;
+ procedure FillListBox;
+ function GetMethodName(const ProcName: string): string;
+ function GetImageIndex(const ProcName, ProcClass: string): Integer;
+ procedure GridDrawCell(Sender: TObject; const ARow, ACol: Integer; const ARect: TfpgRect; const AFlags: TfpgGridDrawState; var ADefaultDrawing: boolean);
+ property Filename: string read FFilename write SetFilename;
+ property Editor: TfpgTextEdit read FEditor write FEditor;
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure AfterCreate; override;
+ property Language: TSourceLanguage read FLanguage write FLanguage default ltPas;
+ end;
+
+{@VFD_NEWFORM_DECL}
+
+function DisplayProcedureList(const AFilename: TfpgString; var AEditor: TfpgTextEdit): boolean;
+
+
+implementation
+
+uses
+ ideconst
+ ,mPasLex
+ ,ideutils
+ ,dbugintf
+ ,fpg_utils
+ ,fpg_imgfmt_bmp
+ ;
+
+const
+ SAllString = '<All>';
+ SNoneString = '<None>';
+ SUnknown = 'Unknown';
+ SImplementationNotFound = 'Implementation section not found (parser error?)';
+ SInvalidIndex = 'Invalid index number';
+ SParseStatistics = 'Procedures processed in %g seconds';
+
+
+{$I proclistimages.inc}
+
+
+function DisplayProcedureList(const AFilename: TfpgString; var AEditor: TfpgTextEdit): boolean;
+var
+ frm: TProcedureListForm;
+begin
+ try
+ frm := TProcedureListForm.Create(nil);
+ frm.Filename := AFilename;
+ frm.Editor := AEditor;
+ frm.ShowModal;
+ finally
+ frm.Free;
+ AEditor.SetFocus;
+ end;
+end;
+
+{@VFD_NEWFORM_IMPL}
+
+procedure TProcedureListForm.SearchTextChanged(Sender: TObject);
+begin
+ FillListBox;
+end;
+
+procedure TProcedureListForm.SearchEditKeyPressed(Sender: TObject; var KeyCode: word; var ShiftState: TShiftState; var Consumed: boolean);
+begin
+ case KeyCode of
+ KeyUp:
+ begin
+ grdProcedures.FocusRow := grdProcedures.FocusRow-1;
+ consumed := True;
+ end;
+ KeyDown:
+ begin
+ grdProcedures.FocusRow := grdProcedures.FocusRow+1;
+ consumed := True;
+ end;
+ KeyEnter:
+ begin
+ Editor.GotoLine(StrToInt(grdProcedures.Cells[3, grdProcedures.FocusRow]));
+ consumed := True;
+ Close;
+ end;
+ KeyEscape:
+ begin
+ consumed := True;
+ Close;
+ end;
+ end;
+end;
+
+procedure TProcedureListForm.SetFilename(const AValue: string);
+var
+ LoadTime: DWord;
+begin
+ if FFilename=AValue then exit;
+ FFilename:=AValue;
+// if IsCpp(FileName) or IsC(FileName) or IsH(FileName) then
+// Language := ltCpp
+// else
+ Language := ltPas;
+
+ LoadTime := fpgGetTickCount;
+ InitializeForm;
+ LoadTime := fpgGetTickCount - LoadTime;
+ StatusBar.Text := Format(SParseStatistics, [LoadTime / 1000]);
+end;
+
+procedure TProcedureListForm.FormShow(Sender: TObject);
+var
+ M: TPasModule;
+ E: TPasTreeContainer;
+ I: Integer;
+ Decls: TList;
+ p: TPasElement;
+begin
+{
+ E := TSimpleEngine.Create;
+ try
+ writeln(Format('Parsing file <%s> for OS <%s> and CPU <%s>', [FFilename, OSTarget, CPUTarget]));
+ M := ParseSource(E, FFilename, OSTarget, CPUTarget);
+
+ { Cool, we successfully parsed the unit.
+ Now output some info about it. }
+ Decls := M.InterfaceSection.Declarations;
+ for I := 0 to Decls.Count - 1 do
+ begin
+ p := TObject(Decls[I]) as TPasElement;
+ Writeln('Interface item ', I, ': ' + p.Name + ' [line ' + IntToStr(p.SourceLinenumber) + ']');
+
+ end;
+ FreeAndNil(M);
+ finally
+ FreeAndNil(E)
+ end;
+}
+end;
+
+procedure TProcedureListForm.InitializeForm;
+begin
+ FObjectStrings := TStringList.Create;
+ FObjectStrings.Sorted := True;
+ FObjectStrings.Duplicates := dupIgnore;
+ ClearObjectStrings;
+
+ FSortOnColumn := 1;
+
+ FProcList := TStringList.Create;
+
+// LoadSettings;
+ LoadProcs;
+
+ FillListBox;
+ edtSearch.SetFocus;
+end;
+
+procedure TProcedureListForm.FillListBox;
+var
+ i: Integer;
+ ProcName: string;
+ IsObject: Boolean;
+ ProcInfo: TProcInfo;
+
+ procedure AddListItem(ProcInfo: TProcInfo);
+ var
+ r: integer;
+ begin
+ r := grdProcedures.RowCount;
+ grdProcedures.RowCount := grdProcedures.RowCount + 1;
+ grdProcedures.Objects[0, r] := ProcInfo;
+// case Language of
+// ltPas: ListItem.ImageIndex := GetPasImageIndex(ProcInfo.Name);
+// ltCpp: ListItem.ImageIndex := ProcInfo.ProcIndex;
+// end;
+ grdProcedures.Cells[1, r] := ProcInfo.DisplayName;
+ grdProcedures.Cells[2, r] := ProcInfo.ProcedureType;
+ grdProcedures.Cells[3, r] := IntToStr(ProcInfo.LineNo);
+ end;
+
+ procedure FocusAndSelectFirstItem;
+ begin
+ if grdProcedures.RowCount > 0 then
+ begin
+ grdProcedures.FocusRow := 0;
+// lvProcs.ItemFocused := lvProcs.Selected;
+ end;
+ end;
+
+begin
+ grdProcedures.BeginUpdate;
+ try
+ grdProcedures.RowCount := 0;
+ if (Length(edtSearch.Text) = 0) and (cbObjects.Text = SAllString) then
+ begin
+ for i := 0 to FProcList.Count - 1 do
+ AddListItem(TProcInfo(FProcList.Objects[i]));
+ FocusAndSelectFirstItem;
+ Exit;
+ end;
+
+ for i := 0 to FProcList.Count - 1 do
+ begin
+ ProcInfo := TProcInfo(FProcList.Objects[i]);
+ case Language of
+ ltPas: ProcName := ProcInfo.Name;
+ ltCpp: ProcName := ProcInfo.ProcClass;
+ end;
+ IsObject := Length(ProcInfo.ProcClass) > 0;
+
+ // Is it the object we want?
+ if cbObjects.Text <> SAllString then
+ begin
+ if cbObjects.Text = SNoneString then
+ begin
+ if IsObject then // Does it have an object?
+ Continue;
+ if Length(edtSearch.Text) = 0 then // If no filter is active, add
+ begin
+ AddListItem(ProcInfo);
+ Continue;
+ end;
+ end // if/then
+ else if not SameText(cbObjects.Text, ProcInfo.ProcClass) then
+ Continue;
+ end;
+
+ case Language of
+ ltPas: ProcName := GetMethodName(ProcName);
+ ltCpp: ProcName := ProcInfo.ProcName;
+ end;
+
+ if Length(edtSearch.Text) = 0 then
+ AddListItem(ProcInfo)
+ else if not FSearchAll and SameText(edtSearch.Text, Copy(ProcName, 1, Length(edtSearch.Text))) then
+ AddListItem(ProcInfo)
+ else if FSearchAll and StrContains(edtSearch.Text, ProcName, False) then
+ AddListItem(ProcInfo);
+ end;
+ FocusAndSelectFirstItem;
+ finally
+ grdProcedures.EndUpdate;
+ end;
+// ResizeCols;
+end;
+
+function TProcedureListForm.GetMethodName(const ProcName: string): string;
+var
+ CharPos: Integer;
+begin
+ Result := ProcName;
+ Delete(Result, 1, 1);
+
+ CharPos := Pos(#9, Result);
+ if CharPos <> 0 then
+ Delete(Result, CharPos, Length(Result));
+
+ CharPos := Pos(' ', Result);
+ Result := Copy(Result, CharPos + 1, Length(Result));
+
+ CharPos := Pos('(', Result);
+ if CharPos > 0 then
+ Result := Copy(Result, 1, CharPos - 1);
+
+ CharPos := Pos('.', Result);
+ if CharPos > 0 then
+ Result := Copy(Result, CharPos + 1, Length(Result));
+
+ Result := Trim(Result);
+end;
+
+function TProcedureListForm.GetImageIndex(const ProcName, ProcClass: string): Integer;
+const
+ ImageIndexNew = 1;
+ ImageIndexTrash = 2;
+ ImageIndexGear = 3;
+ ImageIndexFunction = 4;
+begin
+ if StrContains('constructor', ProcName, False) then // Do not localize.
+ Result := ImageIndexNew
+ else if StrContains('destructor', ProcName, False) then // Do not localize.
+ Result := ImageIndexTrash
+ else if StrBeginsWith('class proc', ProcName, False) // Do not localize.
+ or StrContains('class func', ProcName, False)
+ or (ProcClass <> '') then
+ Result := ImageIndexGear
+ else
+ Result := ImageIndexFunction;
+end;
+
+procedure TProcedureListForm.GridDrawCell(Sender: TObject; const ARow, ACol: Integer; const ARect: TfpgRect; const AFlags: TfpgGridDrawState; var ADefaultDrawing: boolean);
+var
+ img: TfpgImage;
+ i: integer;
+ ProcInfo: TProcInfo;
+begin
+ ADefaultDrawing := True;
+ if ACol = 0 then
+ begin
+ ProcInfo := grdProcedures.Objects[ACol, ARow] as TProcInfo;
+ i := GetImageIndex(ProcInfo.ProcedureType, ProcInfo.ProcClass);
+{
+ ImageIndexNew = 1;
+ ImageIndexTrash = 2;
+ ImageIndexGear = 3;
+ ImageIndexFunction = 4;
+}
+ case i of
+ 1: img := fpgImages.GetImage('ide.grid.constr');
+ 2: img := fpgImages.GetImage('ide.grid.destr');
+ 3: img := fpgImages.GetImage('ide.grid.gears');
+ 4: img := fpgImages.GetImage('ide.grid.func');
+ end;
+ if Assigned(img) then
+ grdProcedures.Canvas.DrawImage((ARect.Width-16) div 2, ARect.Top, img);
+ end;
+end;
+
+procedure TProcedureListForm.LoadProcs;
+var
+ Parser: TmwPasLex;
+// CParser: TBCBTokenList;
+ BeginBracePosition: Longint;
+ BraceCount, PreviousBraceCount: Integer;
+
+ function MoveToImplementation: Boolean;
+ begin
+ if IsProgram(FFileName) or (IsInc(FFileName)) then
+ begin
+ Result := True;
+ Exit;
+ end;
+ Result := False;
+ while Parser.TokenID <> tkNull do
+ begin
+ if Parser.TokenID = tkImplementation then
+ Result := True;
+ Parser.Next;
+ if Result then
+ Break;
+ end;
+ end;
+
+ procedure FindProcs;
+
+ function GetProperProcName(ProcType: TTokenKind; IsClass: Boolean): string;
+ begin
+ Result := SUnknown;
+ if IsClass then
+ begin
+ if ProcType = tkFunction then
+ Result := 'Class Func' // Do not localize.
+ else if ProcType = tkProcedure then
+ Result := 'Class Proc'; // Do not localize.
+ end
+ else
+ begin
+ case ProcType of
+ // Do not localize.
+ tkFunction: Result := 'Function';
+ tkProcedure: Result := 'Procedure';
+ tkConstructor: Result := 'Constructor';
+ tkDestructor: Result := 'Destructor';
+ end;
+ end;
+ end;
+
+(*
+ procedure FindBeginningBrace;
+ begin
+ repeat
+ CParser.NextNonJunk;
+ case CParser.RunID of
+ ctkbraceopen: Inc(BraceCount);
+ ctkbraceclose: Dec(BraceCount);
+ ctknull: Exit;
+ end;
+ until (CParser.RunID = ctkbraceopen) or
+ (CParser.RunID = ctkbracepair) or
+ (CParser.RunID = ctknull);
+ end;
+
+ // This procedure does two things. It looks for procedures and it
+ // looks for named scopes (like class/struct definitions & namespaces)
+ // If it finds a named scope it returns the non-blank name. If it finds
+ // a procedure it returns a blank name.
+ procedure FindBeginningProcedureBrace(var Name: string); // Used for CPP
+ var
+ InitialPosition: Integer;
+ RestorePosition: Integer;
+ FoundClass: Boolean;
+ begin
+ BeginBracePosition := 0;
+ InitialPosition := CParser.RunPosition;
+ // Skip these: enum {a, b, c}; or int a[] = {0, 3, 5}; and find foo () {
+ FindBeginningBrace;
+ if CParser.RunID = ctknull then
+ Exit;
+ CParser.PreviousNonJunk;
+ // Check for a namespace or a class name
+ if CParser.RunID = ctkidentifier then
+ begin
+ Name := CParser.RunToken; // The name
+ // This might be a derived class so search backward
+ // no further than InitialPosition to see
+ RestorePosition := CParser.RunPosition;
+ FoundClass := False;
+ while CParser.RunPosition >= InitialPosition do begin
+ if CParser.RunID in [ctkclass, ctkstruct, ctknamespace] then
+ begin
+ FoundClass := True;
+ Break;
+ end;
+ if CParser.RunPosition = InitialPosition then
+ Break;
+ CParser.PreviousNonJunk;
+ end;
+ // The class name is the last token before a : or {
+ if FoundClass then
+ begin
+ while not (CParser.RunID in [ctkcolon, ctkbraceopen, ctknull]) do begin
+ Name := CParser.RunToken;
+ CParser.NextNonJunk;
+ end;
+ // Back up a bit if we are on a brace open so empty enums don't get treated as namespaces
+ if CParser.RunID = ctkbraceopen then
+ CParser.PreviousNonJunk;
+ end;
+ // Now get back to where you belong
+ while CParser.RunPosition < RestorePosition do
+ CParser.NextNonJunk;
+ CParser.NextNonJunk;
+ BeginBracePosition := CParser.RunPosition;
+ end
+ else
+ begin
+ if CParser.RunID in [ctkroundclose, ctkroundpair, ctkconst, ctkvolatile, ctknull] then
+ begin
+ // Return an empty name to indicate that a procedure was found
+ Name := '';
+ CParser.NextNonJunk;
+ BeginBracePosition := CParser.RunPosition;
+ end
+ else
+ begin
+ while not (CParser.RunID in [ctkroundclose, ctkroundpair, ctkconst, ctkvolatile, ctknull]) do
+ begin
+ CParser.NextNonJunk;
+ if CParser.RunID = ctknull then
+ Exit;
+ // Recurse
+ FindBeginningProcedureBrace(Name);
+ CParser.PreviousNonJunk;
+ if Name <> '' then Break;
+ end;
+ CParser.NextNonJunk;
+ end;
+ end;
+ end;
+
+ // This function searches backward from the current parser position
+ // trying to find the procedure name - it returns all of the text
+ // between the starting position and the position where it thinks a
+ // procedure name has been found.
+ function SearchForProcedureName: string;
+ var
+ ParenCount: Integer;
+ begin
+ ParenCount := 0;
+ Result := '';
+ repeat
+ CParser.Previous;
+ if CParser.RunID <> ctkcrlf then
+ if (CParser.RunID = ctkspace) and (CParser.RunToken = #9) then
+ Result := #32 + Result
+ else
+ Result := CParser.RunToken + Result;
+ case CParser.RunID of
+ ctkroundclose: Inc(ParenCount);
+ ctkroundopen: Dec(ParenCount);
+ ctknull: Exit;
+ end;
+ until ((ParenCount = 0) and ((CParser.RunID = ctkroundopen) or (CParser.RunID = ctkroundpair)));
+ CParser.PreviousNonJunk; // This is the procedure name
+ end;
+
+ function SearchForTemplateArgs: string;
+ var
+ AngleCount: Integer;
+ begin
+ Result := '';
+ if CParser.RunID <> ctkGreater then
+ Exit; // only use if we are on a '>'
+ AngleCount := 1;
+ Result := CParser.RunToken;
+ repeat
+ CParser.Previous;
+ if CParser.RunID <> ctkcrlf then
+ if (CParser.RunID = ctkspace) and (CParser.RunToken = #9) then
+ Result := #32 + Result
+ else
+ Result := CParser.RunToken + Result;
+ case CParser.RunID of
+ ctkgreater: Inc(AngleCount);
+ ctklower: Dec(AngleCount);
+ ctknull: Exit;
+ end;
+ until (((AngleCount = 0) and (CParser.RunID = ctklower)) or (CParser.RunIndex = 0));
+ CParser.PreviousNonJunk; // This is the token before the template args
+ end;
+*)
+
+ var
+ ProcLine: string;
+ ProcType: TTokenKind;
+ Line: Integer;
+ ClassLast: Boolean;
+ InParenthesis: Boolean;
+ InTypeDeclaration: Boolean;
+ FoundNonEmptyType: Boolean;
+ IdentifierNeeded: Boolean;
+ ProcedureInfo: TProcInfo;
+ BeginProcHeaderPosition: Longint;
+ i, j: Integer;
+ LineNo: Integer;
+ ProcName, ProcReturnType: string;
+ ProcedureType, ProcClass, ProcArgs: string;
+ ProcIndex: Integer;
+ NameList: TStringList;
+ NewName, TmpName, ProcClassAdd, ClassName: string;
+ BraceCountDelta: Integer;
+ TemplateArgs: string;
+
+ procedure EraseName(Index: Integer);
+ var
+ NameIndex: Integer;
+ begin
+ NameIndex := NameList.IndexOfName(IntToStr(Index));
+ if NameIndex <> -1 then
+ NameList.Delete(NameIndex);
+ end;
+
+ begin
+ FProcList.Capacity := 200;
+ FProcList.BeginUpdate;
+ try
+ case Language of
+ ltPas:
+ begin
+ if not MoveToImplementation then
+ raise Exception.Create(SImplementationNotFound);
+ ClassLast := False;
+ InParenthesis := False;
+ InTypeDeclaration := False;
+ FoundNonEmptyType := False;
+
+ while Parser.TokenID <> tkNull do
+ begin
+ if not InTypeDeclaration and
+ (Parser.TokenID in [tkFunction, tkProcedure, tkConstructor, tkDestructor]) then
+ begin
+ IdentifierNeeded := True;
+ ProcType := Parser.TokenID;
+ Line := Parser.LineNumber + 1;
+ ProcLine := '';
+ while not (Parser.TokenId in [tkNull]) do
+ begin
+ //{$IFOPT D+} SendDebug('Found Inner Token: '+ Parser.Token+ ' '+BTS(ClassLast)); {$ENDIF}
+ case Parser.TokenID of
+ tkIdentifier, tkRegister:
+ IdentifierNeeded := False;
+
+ tkRoundOpen:
+ begin
+ // Did we run into an identifier already?
+ // This prevents
+ // AProcedure = procedure() of object
+ // from being recognised as a procedure
+ if IdentifierNeeded then
+ Break;
+ InParenthesis := True;
+ end;
+
+ tkRoundClose:
+ InParenthesis := False;
+
+ else
+ // nothing
+ end; // case
+
+ if (not InParenthesis) and (Parser.TokenID = tkSemiColon) then
+ Break;
+
+ if not (Parser.TokenID in [tkCRLF, tkCRLFCo]) then
+ ProcLine := ProcLine + Parser.Token;
+ Parser.Next;
+ end; // while
+ if Parser.TokenID = tkSemicolon then
+ ProcLine := ProcLine + ';';
+ if ClassLast then
+ ProcLine := 'class ' + ProcLine; // Do not localize.
+ //{$IFOPT D+} SendDebug('FoundProc: ' + ProcLine); {$ENDIF}
+ if not IdentifierNeeded then
+ begin
+ ProcedureInfo := TProcInfo.Create;
+ ProcedureInfo.Name := ProcLine;
+ ProcedureInfo.ProcedureType := GetProperProcName(ProcType, ClassLast);
+ ProcedureInfo.LineNo := Line;
+ AddProcedure(ProcedureInfo);
+ end;
+ end;
+ if (Parser.TokenID = tkClass) and Parser.IsClass then
+ begin
+ InTypeDeclaration := True;
+ FoundNonEmptyType := False;
+ end
+ else if InTypeDeclaration and
+ (Parser.TokenID in [tkProcedure, tkFunction, tkProperty,
+ tkPrivate, tkProtected, tkPublic, tkPublished]) then
+ begin
+ FoundNonEmptyType := True;
+ end
+ else if InTypeDeclaration and
+ ((Parser.TokenID = tkEnd) or
+ ((Parser.TokenID = tkSemiColon) and not FoundNonEmptyType)) then
+ begin
+ InTypeDeclaration := False;
+ end;
+ //{$IFOPT D+} SendDebug('Found Token: '+ Parser.Token+ ' '+BTS(ClassLast)); {$ENDIF}
+ ClassLast := (Parser.TokenID = tkClass);
+ if ClassLast then
+ begin
+ Parser.NextNoJunk;
+ //{$IFOPT D+} SendDebug('Found Class Token'+ ' '+BTS(ClassLast)); {$ENDIF}
+ end
+ else
+ Parser.Next;
+ end;
+ end; //ltPas
+
+(*
+ ltCpp:
+ begin
+ NameList := TStringList.Create;
+ try
+ BraceCount := 0;
+ NameList.Add('0='); // empty enclosure name
+ j := CParser.TokenPositionsList[CParser.TokenPositionsList.Count - 1];
+ PreviousBraceCount := BraceCount;
+ FindBeginningProcedureBrace(NewName);
+
+ while (CParser.RunPosition <= j - 1) or (CParser.RunID <> ctknull) do
+ begin
+ // If NewName = '' then we are looking at a real procedure - otherwise
+ // we've just found a new enclosure name to add to our list
+ if NewName = '' then
+ begin
+ // If we found a brace pair then special handling is necessary
+ // for the bracecounting stuff (it is off by one)
+ if CParser.RunID = ctkbracepair then
+ BraceCountDelta := 0
+ else
+ BraceCountDelta := 1;
+ if (BraceCountDelta > 0) and (PreviousBraceCount >= BraceCount) then
+ EraseName(PreviousBraceCount);
+ // Back up a tiny bit so that we are "in front of" the
+ // ctkbraceopen or ctkbracepair we just found
+ CParser.Previous;
+
+ while not ((CParser.RunID in [ctksemicolon, ctkbraceclose, ctkbraceopen, ctkbracepair]) or
+ (CParser.RunID in IdentDirect) or
+ (CParser.RunIndex = 0)) do
+ begin
+ CParser.PreviousNonJunk;
+ // Handle the case where a colon is part of a valid procedure definition
+ if CParser.RunID = ctkcolon then
+ begin
+ // A colon is valid in a procedure definition only if it is immediately
+ // following a close parenthesis (possibly separated by "junk")
+ CParser.PreviousNonJunk;
+ if CParser.RunID in [ctkroundclose, ctkroundpair] then
+ CParser.NextNonJunk
+ else
+ begin
+ // Restore position and stop backtracking
+ CParser.NextNonJunk;
+ Break;
+ end;
+ end;
+ end;
+
+ if CParser.RunID in [ctkcolon, ctksemicolon, ctkbraceclose, ctkbraceopen, ctkbracepair] then
+ CParser.NextNonComment
+ else if CParser.RunIndex = 0 then
+ begin
+ if CParser.IsJunk then
+ CParser.NextNonJunk;
+ end
+ else // IdentDirect
+ begin
+ while CParser.RunID <> ctkcrlf do
+ begin
+ if (CParser.RunID = ctknull) then
+ Exit;
+ CParser.Next;
+ end;
+ CParser.NextNonJunk;
+ end;
+ // We are at the beginning of procedure header
+ BeginProcHeaderPosition := CParser.RunPosition;
+
+ ProcLine := '';
+ while (CParser.RunPosition < BeginBracePosition) and (CParser.RunID <> ctkcolon) do
+ begin
+ if (CParser.RunID = ctknull) then
+ Exit
+ else if (CParser.RunID <> ctkcrlf) then
+ if (CParser.RunID = ctkspace) and (CParser.RunToken = #9) then
+ ProcLine := ProcLine + #32
+ else
+ ProcLine := ProcLine + CParser.RunToken;
+ CParser.NextNonComment;
+ end;
+ // We are at the end of a procedure header
+ // Go back and skip parenthesis to find the procedure name
+ ProcName := '';
+ ProcClass := '';
+ ProcReturnType := '';
+ ProcArgs := SearchForProcedureName;
+ // We have to check for ctknull and exit since we moved the
+ // code to a nested procedure (if we exit SearchForProcedureName
+ // early due to RunID = ctknull we exit this procedure early as well)
+ if CParser.RunID = ctknull then
+ Exit;
+ if CParser.RunID = ctkthrow then
+ begin
+ ProcArgs := CParser.RunToken + ProcArgs;
+ ProcArgs := SearchForProcedureName + ProcArgs;
+ end;
+ // Since we've enabled nested procedures it is now possible
+ // that we think we've found a procedure but what we've really found
+ // is a standard C or C++ construct (like if or for, etc...)
+ // To guard against this we require that our procedures be of type
+ // ctkidentifier. If not, then skip this step.
+ if (CParser.RunID = ctkidentifier) and not InProcedureBlacklist(CParser.RunToken) then
+ begin
+ ProcName := CParser.RunToken;
+ LineNo := CParser.PositionAtLine(CParser.RunPosition);
+ CParser.PreviousNonJunk;
+ if CParser.RunID = ctkcoloncolon then // The object/method delimiter
+ begin
+ // There may be multiple name::name::name:: sets here
+ // so loop until no more are found
+ ClassName := '';
+ while CParser.RunID = ctkcoloncolon do begin
+ CParser.PreviousNonJunk; // The object name?
+ // It is possible that we are looking at a templatized class and
+ // what we have in front of the :: is the end of a specialization:
+ // ClassName<x, y, z>::Function
+ if CParser.RunID = ctkgreater then
+ TemplateArgs := SearchForTemplateArgs;
+ ProcClass := CParser.RunToken + ProcClass;
+ if ClassName = '' then
+ ClassName := CParser.RunToken;
+ CParser.PreviousNonJunk; // look for another ::
+ if CParser.RunID = ctkcoloncolon then
+ ProcClass := CParser.RunToken + ProcClass;
+ end;
+ // We went back one step too far so go ahead one
+ CParser.NextNonJunk;
+ ProcIndex := ImageIndexFunction;
+ if ProcName = ClassName then // A constructor
+ ProcIndex := ImageIndexNew;
+ if ProcName = '~' + ClassName then // A destructor
+ ProcIndex := ImageIndexTrash;
+ end
+ else
+ begin
+ ProcIndex := ImageIndexFunction;
+ // If ProcIndex is 1 then we have backed up too far already
+ // so restore our previous position in order to correctly
+ // get the return type information for non-class methods
+ CParser.NextNonJunk;
+ end;
+
+ while CParser.RunPosition > BeginProcHeaderPosition do // Find the return type of the procedure
+ begin
+ CParser.PreviousNonComment;
+ // Handle the possibility of template specifications and
+ // do not include them in the return type
+ if CParser.RunID = ctkGreater then
+ TemplateArgs := SearchForTemplateArgs;
+ if CParser.RunID = ctktemplate then
+ Continue;
+ if CParser.RunID in [ctkcrlf, ctkspace] then
+ ProcReturnType := ' ' + ProcReturnType
+ else
+ ProcReturnType := CParser.RunToken + ProcReturnType
+ end;
+
+ // If the return type is an empty string then it must be a constructor
+ // or a destructor (depending on the presence of a ~ in the name
+ if (Trim(ProcReturnType) = '') or (Trim(ProcReturnType) = 'virtual') then
+ begin
+ if StrBeginsWith('~', ProcName) then
+ ProcIndex := ImageIndexTrash // a destructor
+ else
+ ProcIndex := ImageIndexNew; // a constructor
+ end;
+
+ ProcLine := Trim(ProcReturnType) + ' ';
+
+ // This code sticks enclosure names in front of
+ // methods (namespaces & classes with in-line definitions)
+ ProcClassAdd := '';
+ for i := 0 to BraceCount - BraceCountDelta do begin
+ if i < NameList.Count then
+ begin
+ TmpName := NameList.Values[IntToStr(i)];
+ if TmpName <> '' then
+ begin
+ if ProcClassAdd <> '' then
+ ProcClassAdd := ProcClassAdd + '::';
+ ProcClassAdd := ProcClassAdd + TmpName;
+ end;
+ end;
+ end;
+
+ if Length(ProcClassAdd) > 0 then
+ begin
+ if Length(ProcClass) > 0 then
+ ProcClassAdd := ProcClassAdd + '::';
+ ProcClass := ProcClassAdd + ProcClass;
+ end;
+ if Length(ProcClass) > 0 then
+ ProcLine := ProcLine + ' ' + ProcClass + '::';
+ ProcLine := ProcLine + ProcName + ' ' + ProcArgs;
+
+ // We need to double check the ProcIndex if it is = 0
+ // if it isn't a "static" method it should be 1
+ if (ProcIndex in [ImageIndexFunction, ImageIndexGear]) then
+ if StrBeginsWith('static ', Trim(ProcReturnType)) and
+ (Length(ProcClass) > 0) then
+ ProcIndex := ImageIndexGear
+ else
+ ProcIndex := ImageIndexFunction;
+
+ case ProcIndex of
+ ImageIndexFunction: if StrContains('void', ProcReturnType) then
+ ProcedureType := 'Procedure'
+ else
+ ProcedureType := 'Function';
+ ImageIndexGear: if StrContains('void', ProcReturnType) then
+ ProcedureType := 'Class Proc'
+ else
+ ProcedureType := 'Class Func';
+ ImageIndexNew: ProcedureType := 'Constructor';
+ ImageIndexTrash: ProcedureType := 'Destructor';
+ end;
+
+ ProcedureInfo := TProcInfo.Create;
+ ProcedureInfo.Name := ProcLine;
+ ProcedureInfo.ProcedureType := ProcedureType;
+ ProcedureInfo.LineNo := LineNo;
+ ProcedureInfo.ProcClass := ProcClass;
+ ProcedureInfo.ProcArgs := ProcArgs;
+ ProcedureInfo.ProcReturnType := ProcReturnType;
+ ProcedureInfo.ProcIndex := ProcIndex;
+ ProcedureInfo.ProcName := ProcName;
+ AddProcedure(ProcedureInfo);
+ end;
+ while (CParser.RunPosition < BeginBracePosition) do
+ CParser.Next;
+ end
+ else begin
+ // Insert enclosure name into our list (delete the old one if found)
+ EraseName(BraceCount);
+ NameList.Add(IntToStr(BraceCount) + '=' + NewName);
+ end;
+ PreviousBraceCount := BraceCount;
+ FindBeginningProcedureBrace(NewName);
+ end; //while (RunPosition <= j-1) ...
+ finally
+ NameList.Free;
+ end;
+ end; //Cpp
+*)
+ end; //case Language
+ finally
+ FProcList.EndUpdate;
+ end;
+ end;
+
+var
+ SFile: TFileStream;
+ MemStream: TMemoryStream;
+ Pos: Integer;
+ Size: Integer;
+const
+ TheEnd: Char = #0; // Leave typed constant as is - needed for streaming code
+begin
+ case Language of
+ ltPas: Parser := TmwPasLex.Create;
+// ltCpp: CParser := TBCBTokenList.Create;
+ end;
+ try
+ MemStream := TMemoryStream.Create;
+ try
+ // Read from file on disk and store in a memory stream
+ SFile := TFileStream.Create(FFilename, fmOpenRead or fmShareDenyWrite);
+ try
+ SFile.Position := 0;
+ MemStream.CopyFrom(SFile, SFile.Size);
+ MemStream.Write(TheEnd, 1);
+ finally
+ SFile.Free;
+ end;
+ SendDebug('Procedure List: Starting Parse');
+ case Language of
+ ltPas: Parser.Origin := MemStream.Memory;
+// ltCpp: CParser.SetOrigin(MemStream.Memory, MemStream.Size);
+ end;
+ WindowTitle := WindowTitle + ' - ' + fpgExtractFileName(FFileName);
+
+ ClearObjectStrings;
+ try
+ FindProcs;
+ finally
+ LoadObjectCombobox;
+ end;
+ SendDebug('Procedure List: QuickSorting procedures');
+ QuickSort(0, FProcList.Count - 1);
+// StatusBar.Panels[1].Text := Trim(IntToStr(lvProcs.Items.Count));
+ finally
+ MemStream.Free;
+ end;
+ finally
+ case Language of
+ ltPas: Parser.Free;
+// ltCpp: CParser.Free;
+ end;
+ end;
+end;
+
+procedure TProcedureListForm.AddProcedure(ProcedureInfo: TProcInfo);
+var
+ TempStr: string;
+ i: Integer;
+begin
+ ProcedureInfo.Name := CompressWhiteSpace(ProcedureInfo.Name);
+ case Language of
+ ltPas:
+ begin
+ TempStr := ProcedureInfo.Name;
+ // Remove the class reserved word
+ if StrBeginsWith('CLASS ', TempStr, False) then // Do not localize.
+ Delete(TempStr, 1, 6); // Do not localize.
+ // Remove 'function' or 'procedure'
+ i := Pos(' ', TempStr);
+ if i > 0 then
+ TempStr := Copy(TempStr, i + 1, Length(TempStr));
+ // Remove the paramater list
+ i := Pos('(', TempStr);
+ if i > 0 then
+ TempStr := Copy(TempStr, 1, i - 1);
+ // Remove the function return type
+ i := Pos(':', TempStr);
+ if i > 0 then
+ TempStr := Copy(TempStr, 1, i - 1);
+ // Check for an implementation procedural type
+ if Length(TempStr) = 0 then
+ begin
+ ProcedureInfo.Free;
+ Exit;
+ end;
+ // Remove any trailing ';'
+ if TempStr[Length(TempStr)] = ';' then
+ Delete(TempStr, Length(TempStr), 1);
+ TempStr := Trim(TempStr);
+ ProcedureInfo.DisplayName := TempStr;
+ // Add to the object combobox and set the object name in ProcedureInfo
+ i := Pos('.', TempStr);
+ if i = 0 then
+ FObjectStrings.Add(SNoneString)
+ else
+ begin
+ ProcedureInfo.ProcClass := Copy(TempStr, 1, i - 1);
+ FObjectStrings.Add(ProcedureInfo.ProcClass);
+ end;
+ FProcList.AddObject(#9 + TempStr + #9 + ProcedureInfo.ProcedureType + #9 + IntToStr(ProcedureInfo.LineNo), ProcedureInfo);
+ end; //ltPas
+
+(*
+ ltCpp:
+ begin
+ if Length(ProcedureInfo.ProcClass) > 0 then
+ ProcedureInfo.DisplayName := ProcedureInfo.ProcClass + '::';
+ // Should be the return type and args displayed, they are now in the status bar ?
+ ProcedureInfo.DisplayName := ProcedureInfo.DisplayName + ProcedureInfo.ProcName;
+ FProcList.AddObject(#9 + ProcedureInfo.DisplayName + #9 + ProcedureInfo.ProcedureType + #9 + IntToStr(ProcedureInfo.LineNo), ProcedureInfo);
+ if Length(ProcedureInfo.ProcClass) = 0 then
+ FObjectStrings.Add(SNoneString)
+ else
+ FObjectStrings.Add(ProcedureInfo.ProcClass);
+ end; //ltCpp
+*)
+ end; //case Language
+end;
+
+procedure TProcedureListForm.ClearObjectStrings;
+begin
+ FObjectStrings.Clear;
+ FObjectStrings.Add(SAllString);
+end;
+
+procedure TProcedureListForm.LoadObjectCombobox;
+begin
+ cbObjects.Items.Assign(FObjectStrings);
+ cbObjects.FocusItem := cbObjects.Items.IndexOf(SAllString);
+end;
+
+procedure TProcedureListForm.QuickSort(L, R: Integer);
+
+ function GetValue(idx: Integer): string;
+ var
+ i: Integer;
+ TabPos: Integer;
+ begin
+ if idx >= FProcList.Count then
+ raise Exception.Create(SInvalidIndex);
+ Result := FProcList.Strings[idx];
+ for i := 0 to FSortOnColumn - 1 do
+ begin
+ TabPos := Pos(#9, Result);
+ if TabPos > 0 then
+ Delete(Result, 1, TabPos)
+ else
+ Exit;
+ end;
+ if FSortOnColumn = 3 then
+ begin
+ for i := Length(Result) to 5 do
+ Result := ' ' + Result;
+ end;
+ end;
+
+var
+ I, J: Integer;
+ P: string;
+begin
+ if FProcList.Count = 0 then
+ Exit;
+ repeat
+ I := L;
+ J := R;
+ P := GetValue((L + R) shr 1);
+ repeat
+ while AnsiCompareText(GetValue(I), P) < 0 do
+ Inc(I);
+ while AnsiCompareText(GetValue(J), P) > 0 do
+ Dec(J);
+ if I <= J then
+ begin
+ FProcList.Exchange(I, J);
+ Inc(I);
+ Dec(J);
+ end;
+ until I > J;
+ if L < J then
+ QuickSort(L, J);
+ L := I;
+ until I >= R;
+end;
+
+constructor TProcedureListForm.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ OnShow := @FormShow;
+ FLanguage := ltPas;
+ FSearchAll := True; // search anywhere in a method name
+// FImageList := TfpgImageList.Create;
+
+// CreateImage_BMP(@grdimg_destructor_16, SizeOf(grdimg_destructor_16));
+
+ fpgImages.AddMaskedBMP( // 16x16 image
+ 'ide.grid.destr',
+ @grdimg_destructor_16,
+ sizeof(grdimg_destructor_16), 0, 0);
+
+ fpgImages.AddMaskedBMP( // 16x16 image
+ 'ide.grid.constr',
+ @grdimg_constructor_16,
+ sizeof(grdimg_constructor_16), 0, 0);
+
+ fpgImages.AddMaskedBMP( // 16x16 image
+ 'ide.grid.func',
+ @grdimg_function_16,
+ sizeof(grdimg_function_16), 0, 0);
+
+ fpgImages.AddMaskedBMP( // 16x16 image
+ 'ide.grid.gears',
+ @grdimg_gears_16,
+ sizeof(grdimg_gears_16), 0, 0);
+end;
+
+destructor TProcedureListForm.Destroy;
+var
+ i: Integer;
+begin
+ FreeAndNil(FObjectStrings);
+
+ if FProcList <> nil then
+ begin
+ for i := 0 to FProcList.Count - 1 do
+ FProcList.Objects[i].Free;
+ FreeAndNil(FProcList);
+ end;
+ inherited Destroy;
+end;
+
+procedure TProcedureListForm.AfterCreate;
+begin
+ {%region 'Auto-generated GUI code' -fold}
+ {@VFD_BODY_BEGIN: ProcedureListForm}
+ Name := 'ProcedureListForm';
+ SetPosition(332, 253, 564, 310);
+ WindowTitle := 'Procedure List';
+ Hint := '';
+ ShowHint := True;
+ WindowPosition := wpOneThirdDown;
+
+ Bevel1 := TfpgBevel.Create(self);
+ with Bevel1 do
+ begin
+ Name := 'Bevel1';
+ SetPosition(0, 0, 564, 32);
+ Anchors := [anLeft,anRight,anTop];
+ Hint := '';
+ Shape := bsSpacer;
+ end;
+
+ Bevel2 := TfpgBevel.Create(self);
+ with Bevel2 do
+ begin
+ Name := 'Bevel2';
+ SetPosition(0, 33, 564, 32);
+ Anchors := [anLeft,anRight,anTop];
+ Hint := '';
+ Shape := bsSpacer;
+ end;
+
+ lblSearch := TfpgLabel.Create(Bevel2);
+ with lblSearch do
+ begin
+ Name := 'lblSearch';
+ SetPosition(4, 8, 47, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Search';
+ end;
+
+ edtSearch := TfpgEdit.Create(Bevel2);
+ with edtSearch do
+ begin
+ Name := 'edtSearch';
+ SetPosition(52, 4, 264, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ FontDesc := '#Edit1';
+ Hint := '';
+ TabOrder := 2;
+ Text := '';
+ OnChange := @SearchTextChanged;
+ OnKeyPress := @SearchEditKeyPressed;
+ end;
+
+ cbObjects := TfpgComboBox.Create(Bevel2);
+ with cbObjects do
+ begin
+ Name := 'cbObjects';
+ SetPosition(376, 4, 184, 22);
+ Anchors := [anRight,anTop];
+ FontDesc := '#List';
+ Hint := '';
+ TabOrder := 3;
+ OnChange := @SearchTextChanged;
+ end;
+
+ lblObjects := TfpgLabel.Create(Bevel2);
+ with lblObjects do
+ begin
+ Name := 'lblObjects';
+ SetPosition(324, 8, 51, 16);
+ Anchors := [anRight,anTop];
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Objects';
+ end;
+
+ grdProcedures := TfpgStringGrid.Create(self);
+ with grdProcedures do
+ begin
+ Name := 'grdProcedures';
+ SetPosition(4, 68, 556, 216);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ BackgroundColor := TfpgColor($80000002);
+ AddColumn('', 30, taLeftJustify);
+ AddColumn('Procedure', 300, taLeftJustify);
+ AddColumn('Type', 130, taLeftJustify);
+ AddColumn('Line', 70, taRightJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 0;
+ RowSelect := True;
+ TabOrder := 3;
+ HeaderStyle := ghsFlat;
+ OnDrawCell := @GridDrawCell;
+ end;
+
+ StatusBar := TfpgPanel.Create(self);
+ with StatusBar do
+ begin
+ Name := 'StatusBar';
+ SetPosition(0, 290, 564, 20);
+ Align := alBottom;
+ Alignment := taLeftJustify;
+ FontDesc := '#Label1';
+ Hint := '';
+ Style := bsLowered;
+ Text := 'Panel';
+ end;
+
+ {@VFD_BODY_END: ProcedureListForm}
+ {%endregion}
+end;
+
+
+{ TSimpleEngine }
+
+function TSimpleEngine.CreateElement(AClass: TPTreeElement;
+ const AName: String; AParent: TPasElement; AVisibility: TPasMemberVisibility;
+ const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
+begin
+ Result := AClass.Create(AName, AParent);
+ Result.Visibility := AVisibility;
+ Result.SourceFilename := ASourceFilename;
+ Result.SourceLinenumber := ASourceLinenumber;
+end;
+
+function TSimpleEngine.FindElement(const AName: String): TPasElement;
+begin
+ { dummy implementation, see TFPDocEngine.FindElement for a real example }
+ Result := nil;
+end;
+
+end.
diff --git a/examples/apps/ide/src/frm_projectoptions.pas b/examples/apps/ide/src/frm_projectoptions.pas
new file mode 100644
index 00000000..7ece019d
--- /dev/null
+++ b/examples/apps/ide/src/frm_projectoptions.pas
@@ -0,0 +1,1164 @@
+unit frm_projectoptions;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ SysUtils, Classes, fpg_base, fpg_main, fpg_form, fpg_button, fpg_label,
+ fpg_tab, fpg_editbtn, fpg_checkbox, fpg_grid, fpg_basegrid,
+ fpg_combobox, fpg_edit, idemacros;
+
+type
+
+ TProjectOptionsForm = class(TfpgForm)
+ private
+ {@VFD_HEAD_BEGIN: ProjectOptionsForm}
+ btnCancel: TfpgButton;
+ btnOK: TfpgButton;
+ pcOptions: TfpgPageControl;
+ tsCompiler: TfpgTabSheet;
+ tsDebugger: TfpgTabSheet;
+ tsMacros: TfpgTabSheet;
+ tsOther: TfpgTabSheet;
+ Label1: TfpgLabel;
+ edtMainFile: TfpgFileNameEdit;
+ Label2: TfpgLabel;
+ edtTargetFile: TfpgFileNameEdit;
+ edtMakeCommand: TfpgFileNameEdit;
+ Label3: TfpgLabel;
+ edtMakeDir: TfpgDirectoryEdit;
+ Label4: TfpgLabel;
+ Label5: TfpgLabel;
+ FilenameEdit4: TfpgFileNameEdit;
+ CheckBox1: TfpgCheckBox;
+ Label6: TfpgLabel;
+ cbDefaultMakeCol: TfpgComboBox;
+ Button1: TfpgButton;
+ pcCompiler: TfpgPageControl;
+ TabSheet1: TfpgTabSheet;
+ TabSheet2: TfpgTabSheet;
+ grdCompilerMakeOptions: TfpgStringGrid;
+ grdCompilerDirs: TfpgStringGrid;
+ Label11: TfpgLabel;
+ Label7: TfpgLabel;
+ FilenameEdit5: TfpgFileNameEdit;
+ Label8: TfpgLabel;
+ Edit1: TfpgEdit;
+ CheckBox2: TfpgCheckBox;
+ CheckBox3: TfpgCheckBox;
+ CheckBox4: TfpgCheckBox;
+ PageControl1: TfpgPageControl;
+ TabSheet3: TfpgTabSheet;
+ TabSheet4: TfpgTabSheet;
+ TabSheet5: TfpgTabSheet;
+ TabSheet6: TfpgTabSheet;
+ TabSheet7: TfpgTabSheet;
+ grdDebugSrcDirs: TfpgStringGrid;
+ btnShowCmdLine: TfpgButton;
+ edtUnitOutputDir: TfpgDirectoryEdit;
+ Label9: TfpgLabel;
+ Label10: TfpgLabel;
+ grdMacroGroup: TfpgStringGrid;
+ Label12: TfpgLabel;
+ grdUserMacros: TfpgStringGrid;
+ {@VFD_HEAD_END: ProjectOptionsForm}
+ FCellEdit: TfpgEdit;
+ FFocusRect: TfpgRect;
+ FLastGrid: TfpgStringGrid; // reference only
+ // so we can get correct hints, but still undo with the Cancel button
+ FInternalMacroList: TIDEMacroList;
+ procedure btnShowCmdLineClicked(Sender: TObject);
+ procedure CellEditExit(Sender: TObject);
+ procedure CellEditKeypressed(Sender: TObject; var KeyCode: word; var ShiftState: TShiftState; var Consumed: boolean);
+ procedure grdCompilerDirsDrawCell(Sender: TObject; const ARow, ACol: Integer; const ARect: TfpgRect; const AFlags: TfpgGridDrawState; var ADefaultDrawing: boolean);
+ procedure grdCompilerDirsKeyPressed(Sender: TObject; var KeyCode: word; var ShiftState: TShiftState; var Consumed: boolean);
+ procedure grdCompilerMakeOptionsKeyPressed(Sender: TObject; var KeyCode: word; var ShiftState: TShiftState; var Consumed: boolean);
+ procedure grdCompilerMakeOptionsDrawCell(Sender: TObject; const ARow, ACol: Integer; const ARect: TfpgRect; const AFlags: TfpgGridDrawState; var ADefaultDrawing: boolean);
+ procedure grdCompilerMakeOptionsClicked(Sender: TObject);
+ procedure grdCompilerDirsClicked(Sender: TObject);
+ procedure grdUserMacrosClicked(Sender: TObject);
+ procedure grdUserMacrosDrawCell(Sender: TObject; const ARow, ACol: Integer; const ARect: TfpgRect; const AFlags: TfpgGridDrawState; var ADefaultDrawing: boolean);
+ procedure grdMacroGroupCanSelectCell(Sender: TObject; const ARow, ACol: Integer; var ACanSelect: boolean);
+ procedure BeforeShowHint(Sender: TObject; var AHint: TfpgString);
+ procedure LoadSettings;
+ procedure SaveSettings;
+ procedure SetupCellEdit(AGrid: TfpgStringGrid);
+ procedure CleanupCompilerMakeOptionsGrid;
+ procedure CleanupCompilerDirs;
+ procedure SaveToMacroList(AList: TIDEMacroList);
+
+ public
+ constructor Create(AOwner: TComponent); override;
+ destructor Destroy; override;
+ procedure AfterCreate; override;
+ end;
+
+{@VFD_NEWFORM_DECL}
+
+procedure DisplayProjectOptions;
+
+
+implementation
+
+uses
+ fpg_iniutils
+ ,fpg_dialogs
+ ,fpg_widget
+ ,Project
+ ,ideconst
+ ,ideutils
+ ;
+
+type
+ // Used to get access to the Protected properties
+ TDirectoryEditFriend = class(TfpgDirectoryEdit);
+
+
+procedure DisplayProjectOptions;
+var
+ frm: TProjectOptionsForm;
+ Result: Boolean;
+begin
+ frm := TProjectOptionsForm.Create(nil);
+ try
+ frm.LoadSettings;
+ Result := frm.ShowModal = mrOK;
+ if Result then
+ begin
+ frm.SaveSettings;
+ end;
+ finally
+ frm.Free;
+ end;
+end;
+
+{@VFD_NEWFORM_IMPL}
+
+procedure TProjectOptionsForm.btnShowCmdLineClicked(Sender: TObject);
+var
+ c: TfpgString;
+ b: integer;
+begin
+ // build compilation string
+ c := gINI.ReadString(cEnvironment, 'Compiler', '') + LineEnding;
+ b := cbDefaultMakeCol.FocusItem;
+
+ c := c + GProject.GenerateCmdLine(True, b);
+ try
+ c := GMacroList.ExpandMacro(c);
+ except
+ on E: Exception do
+ begin
+ TfpgMessageDialog.Critical('', E.Message);
+ Exit;
+ end;
+ end;
+ ShowString(c, 'Compile command');
+end;
+
+procedure TProjectOptionsForm.CellEditExit(Sender: TObject);
+begin
+ FCellEdit.Visible := False;
+end;
+
+procedure TProjectOptionsForm.CellEditKeypressed(Sender: TObject;
+ var KeyCode: word; var ShiftState: TShiftState; var Consumed: boolean);
+begin
+ if KeyCode = keyReturn then
+ begin
+ FLastGrid.Cells[FLastGrid.FocusCol, FLastGrid.FocusRow] := FCellEdit.Text;
+ FCellEdit.Visible := False;
+ FLastGrid.SetFocus;
+ end;
+end;
+
+procedure TProjectOptionsForm.grdCompilerDirsDrawCell(Sender: TObject;
+ const ARow, ACol: Integer; const ARect: TfpgRect;
+ const AFlags: TfpgGridDrawState; var ADefaultDrawing: boolean);
+var
+ img: TfpgImage;
+begin
+ if ACol = 5 then
+ begin
+ grdCompilerDirs.Canvas.Color := clMedGray;
+ grdCompilerDirs.Canvas.DrawLine(ARect.Right-1, ARect.Top, ARect.Right-1, ARect.Bottom);
+ end
+ else if ACol = 6 then
+ begin
+ grdCompilerDirs.Canvas.Color := clMedGray;
+ grdCompilerDirs.Canvas.DrawLine(ARect.Left, ARect.Top, ARect.Left, ARect.Bottom);
+ end;
+
+ if (gdSelected in AFlags) and (ACol = 10) then
+ begin
+ FFocusRect := ARect;
+ end;
+
+ if ACol < 10 then
+ begin
+ if grdCompilerDirs.Cells[ACol, ARow] = cCheck then
+ begin
+ img := fpgImages.GetImage('stdimg.check');
+ if (gdSelected in AFlags) and (gdFocused in AFlags) then
+ img.Invert;
+ grdCompilerDirs.Canvas.DrawImage(ARect.Left, ARect.Top, img);
+ if (gdSelected in AFlags) and (gdFocused in AFlags) then
+ img.Invert; // restore image to original state
+ ADefaultDrawing := False;
+ end;
+ end
+ else
+ grdCompilerDirs.Canvas.Setfont(grdCompilerDirs.Font);
+end;
+
+procedure TProjectOptionsForm.grdCompilerDirsKeyPressed(Sender: TObject;
+ var KeyCode: word; var ShiftState: TShiftState; var Consumed: boolean);
+begin
+ if (KeyCode = keyInsert) and (ssCtrl in ShiftState) then
+ begin
+ TfpgStringGrid(Sender).RowCount := TfpgStringGrid(Sender).RowCount + 1;
+ Consumed := True;
+ Exit;
+ end;
+
+ if TfpgStringGrid(Sender).FocusCol < 10 then
+ begin
+ if (KeyCode = keySpace) then
+ begin
+ grdCompilerDirsClicked(Sender);
+ Consumed := True;
+ end;
+ end
+ else if TfpgStringGrid(Sender).FocusCol = 10 then
+ begin
+ if (KeyCode = keyF2) or (KeyCode = keyReturn) then
+ begin
+ // we need to edit the cell contents
+ SetupCellEdit(TfpgStringGrid(Sender));
+ Consumed := True;
+ end;
+ end;
+end;
+
+procedure TProjectOptionsForm.grdCompilerMakeOptionsKeyPressed(Sender: TObject;
+ var KeyCode: word; var ShiftState: TShiftState; var Consumed: boolean);
+begin
+ if (KeyCode = keyInsert) and (ssCtrl in ShiftState) then
+ begin
+ TfpgStringGrid(Sender).RowCount := TfpgStringGrid(Sender).RowCount + 1;
+ Consumed := True;
+ Exit;
+ end;
+
+ if TfpgStringGrid(Sender).FocusCol < 6 then
+ begin
+ if (KeyCode = keySpace) then
+ begin
+ grdCompilerMakeOptionsClicked(Sender);
+ Consumed := True;
+ end;
+ end
+ else if TfpgStringGrid(Sender).FocusCol = 6 then
+ begin
+ if (KeyCode = keyF2) or (KeyCode = keyReturn) then
+ begin
+ // we need to edit the cell contents
+ SetupCellEdit(TfpgStringGrid(Sender));
+ Consumed := True;
+ end;
+ end;
+end;
+
+procedure TProjectOptionsForm.grdCompilerMakeOptionsDrawCell(Sender: TObject;
+ const ARow, ACol: Integer; const ARect: TfpgRect;
+ const AFlags: TfpgGridDrawState; var ADefaultDrawing: boolean);
+var
+ img: TfpgImage;
+begin
+ if (gdSelected in AFlags) and (ACol = 6) then
+ begin
+ FFocusRect := ARect;
+ end;
+
+ if ACol < 6 then
+ begin
+ if grdCompilerMakeOptions.Cells[ACol, ARow] = cCheck then
+ begin
+ img := fpgImages.GetImage('stdimg.check');
+ if (gdSelected in AFlags) and (gdFocused in AFlags) then
+ img.Invert;
+ grdCompilerMakeOptions.Canvas.DrawImage(ARect.Left, ARect.Top, img);
+ if (gdSelected in AFlags) and (gdFocused in AFlags) then
+ img.Invert; // restore image to original state
+ ADefaultDrawing := False;
+ end;
+ end
+ else
+ grdCompilerMakeOptions.Canvas.Setfont(grdCompilerMakeOptions.Font);
+end;
+
+procedure TProjectOptionsForm.grdCompilerMakeOptionsClicked(Sender: TObject);
+var
+ r, c: integer;
+begin
+ if TfpgStringGrid(Sender).RowCount = 0 then
+ TfpgStringGrid(Sender).RowCount := 1;
+ r := TfpgStringGrid(Sender).FocusRow;
+ c := TfpgStringGrid(Sender).FocusCol;
+ if c < 6 then // checkbox area
+ begin
+ if TfpgStringGrid(Sender).Cells[c, r] = '' then
+ TfpgStringGrid(Sender).Cells[c, r] := cCheck
+ else
+ TfpgStringGrid(Sender).Cells[c, r] := '';
+ end;
+end;
+
+procedure TProjectOptionsForm.grdCompilerDirsClicked(Sender: TObject);
+var
+ r, c: integer;
+begin
+ if TfpgStringGrid(Sender).RowCount = 0 then
+ TfpgStringGrid(Sender).RowCount := 1;
+ r := TfpgStringGrid(Sender).FocusRow;
+ c := TfpgStringGrid(Sender).FocusCol;
+ if c < 10 then // checkbox area
+ begin
+ if TfpgStringGrid(Sender).Cells[c, r] = '' then
+ TfpgStringGrid(Sender).Cells[c, r] := cCheck
+ else
+ TfpgStringGrid(Sender).Cells[c, r] := '';
+ end;
+end;
+
+procedure TProjectOptionsForm.grdUserMacrosClicked(Sender: TObject);
+var
+ r, c: integer;
+begin
+ if TfpgStringGrid(Sender).RowCount = 0 then
+ TfpgStringGrid(Sender).RowCount := 1;
+ r := TfpgStringGrid(Sender).FocusRow;
+ c := TfpgStringGrid(Sender).FocusCol;
+ if c < 6 then // checkbox area
+ begin
+ if TfpgStringGrid(Sender).Cells[c, r] = '' then
+ TfpgStringGrid(Sender).Cells[c, r] := cCheck
+ else
+ TfpgStringGrid(Sender).Cells[c, r] := '';
+ end;
+end;
+
+procedure TProjectOptionsForm.grdUserMacrosDrawCell(Sender: TObject;
+ const ARow, ACol: Integer; const ARect: TfpgRect;
+ const AFlags: TfpgGridDrawState; var ADefaultDrawing: boolean);
+var
+ img: TfpgImage;
+begin
+ if (gdSelected in AFlags) and (ACol = 6) then
+ begin
+ FFocusRect := ARect;
+ end;
+
+ if ACol < 6 then
+ begin
+ if grdUserMacros.Cells[ACol, ARow] = cCheck then
+ begin
+ img := fpgImages.GetImage('stdimg.check');
+ if (gdSelected in AFlags) and (gdFocused in AFlags) then
+ img.Invert;
+ grdUserMacros.Canvas.DrawImage(ARect.Left, ARect.Top, img);
+ if (gdSelected in AFlags) and (gdFocused in AFlags) then
+ img.Invert; // restore image to original state
+ ADefaultDrawing := False;
+ end;
+ end
+ else
+ grdUserMacros.Canvas.Setfont(grdUserMacros.Font);
+end;
+
+procedure TProjectOptionsForm.grdMacroGroupCanSelectCell(Sender: TObject;
+ const ARow, ACol: Integer; var ACanSelect: boolean);
+begin
+ ACanSelect := ACol > 0;
+end;
+
+procedure TProjectOptionsForm.BeforeShowHint(Sender: TObject; var AHint: TfpgString);
+var
+ s: TfpgString;
+ c: TfpgWidget;
+begin
+ if Sender is TfpgWidget then
+ c := TfpgWidget(Sender)
+ else
+ Exit; // should never occur, but lets just be safe
+
+ if (c.Name = 'FEdit') and ((c.Parent is TfpgDirectoryEdit) or (c.Parent is TfpgFileNameEdit)) then
+ begin
+ if c.Parent <> nil then
+ c := c.Parent
+ else
+ Exit; // lets just be safe again
+ end;
+
+ // controls that may contain macros
+ if c is TfpgDirectoryEdit then
+ s := TfpgDirectoryEdit(c).Directory
+ else if c is TfpgFileNameEdit then
+ s := TfpgFileNameEdit(c).FileName
+ else if c is TfpgEdit then
+ s := TfpgEdit(c).Text
+ else if c is TfpgStringGrid then
+ begin
+ s := TfpgStringGrid(c).Cells[TfpgStringGrid(c).FocusCol, TfpgStringGrid(c).FocusRow];
+ if s = cCheck then
+ s := '';
+ end;
+
+ AHint := s;
+
+ if FInternalMacroList.StrHasMacros(s) then
+ begin
+ SaveToMacroList(FInternalMacroList);
+ AHint := FInternalMacroList.ExpandMacro(s);
+ end;
+end;
+
+procedure TProjectOptionsForm.LoadSettings;
+var
+ i, j: integer;
+begin
+ edtMainFile.FileName := GProject.MainUnit;
+ edtTargetFile.FileName := GProject.TargetFile;
+ edtMakeDir.Directory := GProject.ProjectDir;
+ edtUnitOutputDir.Directory := GProject.UnitOutputDir;
+ cbDefaultMakeCol.FocusItem := GProject.DefaultMake;
+ grdCompilerMakeOptions.RowCount := GProject.MakeOptions.Count;
+
+ for i := 0 to GProject.MakeOptions.Count-1 do
+ begin
+ grdCompilerMakeOptions.Cells[6, i] := GProject.MakeOptions[i];
+ for j := 0 to 5 do // we know there is only 6 boolean columns
+ begin
+ if GProject.MakeOptionsGrid[j, i] then
+ grdCompilerMakeOptions.Cells[j, i] := cCheck;
+ end;
+ end;
+
+ grdCompilerDirs.RowCount := GProject.UnitDirs.Count;
+ for i := 0 to GProject.UnitDirs.Count-1 do
+ begin
+ grdCompilerDirs.Cells[10, i] := GProject.UnitDirs[i];
+ for j := 0 to 9 do // we know there is only 10 boolean columns
+ begin
+ if GProject.UnitDirsGrid[j, i] then
+ grdCompilerDirs.Cells[j, i] := cCheck;
+ end;
+ end;
+end;
+
+procedure TProjectOptionsForm.SaveSettings;
+var
+ i, j: integer;
+begin
+ GProject.MainUnit := edtMainFile.FileName;
+ GProject.TargetFile := edtTargetFile.FileName;
+ GProject.ProjectDir := edtMakeDir.Directory;
+ GProject.DefaultMake := cbDefaultMakeCol.FocusItem;
+ GProject.UnitOutputDir := edtUnitOutputDir.Directory;
+
+ CleanupCompilerMakeOptionsGrid;
+ GProject.ClearAndInitMakeOptions(grdCompilerMakeOptions.RowCount);
+ for i := 0 to grdCompilerMakeOptions.RowCount-1 do
+ begin
+ if grdCompilerMakeOptions.Cells[6, i] = '' then
+ Continue;
+ GProject.MakeOptions.Add(grdCompilerMakeOptions.Cells[6, i]);
+ for j := 0 to 5 do // we know there is only 6 boolean columns
+ begin
+ if grdCompilerMakeOptions.Cells[j, i] = cCheck then
+ GProject.MakeOptionsGrid[j, i] := True;
+ end;
+ end;
+
+ CleanupCompilerDirs;
+ GProject.ClearAndInitUnitDirsGrid(grdCompilerDirs.RowCount);
+ for i := 0 to grdCompilerDirs.RowCount-1 do
+ begin
+ GProject.UnitDirs.Add(grdCompilerDirs.Cells[10, i]);
+ for j := 0 to 9 do // we know there is only 10 boolean columns
+ begin
+ if grdCompilerDirs.Cells[j, i] = cCheck then
+ GProject.UnitDirsGrid[j, i] := True;
+ end;
+ end;
+end;
+
+procedure TProjectOptionsForm.SetupCellEdit(AGrid: TfpgStringGrid);
+var
+ pt: TPoint;
+begin
+ if Assigned(FCellEdit) then
+ FCellEdit.Free;
+
+ FLastGrid := AGrid;
+ FCellEdit := TfpgEdit.Create(FLastGrid.Parent);
+ pt.X := FLastGrid.Left + FFocusRect.Left;
+ pt.Y := FLastGrid.Top + FFocusRect.Top;
+ with FCellEdit do
+ begin
+ Name := 'FCellEdit';
+ SetPosition(pt.X, pt.Y, FFocusRect.Width, FFocusRect.Height);
+ BorderStyle := ebsSingle;
+ FontDesc := '#Grid';
+ Text := AGrid.Cells[AGrid.FocusCol, AGrid.FocusRow];
+ OnKeyPress := @CellEditKeypressed;
+ OnExit := @CellEditExit;
+ SetFocus;
+ end;
+end;
+
+// Remove all rows that have empty grid options (text)
+procedure TProjectOptionsForm.CleanupCompilerMakeOptionsGrid;
+var
+ i: integer;
+begin
+ for i := grdCompilerMakeOptions.RowCount-1 downto 0 do
+ begin
+ if Trim(grdCompilerMakeOptions.Cells[6, i]) = '' then
+ grdCompilerMakeOptions.DeleteRow(i);
+ end;
+end;
+
+// Remove all rows that have empty grid options (text)
+procedure TProjectOptionsForm.CleanupCompilerDirs;
+var
+ i: integer;
+begin
+ for i := grdCompilerDirs.RowCount-1 downto 0 do
+ begin
+ if Trim(grdCompilerDirs.Cells[10, i]) = '' then
+ grdCompilerDirs.DeleteRow(i);
+ end;
+end;
+
+procedure TProjectOptionsForm.SaveToMacroList(AList: TIDEMacroList);
+begin
+// AList.SetValue(cMacro_FPCSrcDir, edtFPCSrcDir.Directory);
+end;
+
+constructor TProjectOptionsForm.Create(AOwner: TComponent);
+begin
+ inherited Create(AOwner);
+ FInternalMacroList := TIDEMacroList.Create;
+end;
+
+destructor TProjectOptionsForm.Destroy;
+begin
+ FInternalMacroList.Free;
+ inherited Destroy;
+end;
+
+procedure TProjectOptionsForm.AfterCreate;
+begin
+ {%region 'Auto-generated GUI code' -fold}
+ {@VFD_BODY_BEGIN: ProjectOptionsForm}
+ Name := 'ProjectOptionsForm';
+ SetPosition(317, 177, 609, 570);
+ WindowTitle := 'Project Options';
+ Hint := '';
+ ShowHint := True;
+
+ btnCancel := TfpgButton.Create(self);
+ with btnCancel do
+ begin
+ Name := 'btnCancel';
+ SetPosition(514, 540, 88, 24);
+ Anchors := [anRight,anBottom];
+ Text := 'Cancel';
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageName := '';
+ ModalResult := mrCancel;
+ TabOrder := 1;
+ end;
+
+ btnOK := TfpgButton.Create(self);
+ with btnOK do
+ begin
+ Name := 'btnOK';
+ SetPosition(422, 540, 88, 24);
+ Anchors := [anRight,anBottom];
+ Text := 'OK';
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageName := '';
+ ModalResult := mrOK;
+ TabOrder := 2;
+ end;
+
+ pcOptions := TfpgPageControl.Create(self);
+ with pcOptions do
+ begin
+ Name := 'pcOptions';
+ SetPosition(4, 4, 600, 524);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ ActivePageIndex := 0;
+ Hint := '';
+ TabOrder := 3;
+ end;
+
+ tsCompiler := TfpgTabSheet.Create(pcOptions);
+ with tsCompiler do
+ begin
+ Name := 'tsCompiler';
+ SetPosition(3, 24, 594, 497);
+ Text := 'Compiler';
+ end;
+
+ tsDebugger := TfpgTabSheet.Create(pcOptions);
+ with tsDebugger do
+ begin
+ Name := 'tsDebugger';
+ SetPosition(3, 24, 594, 497);
+ Text := 'Debugger';
+ end;
+
+ tsMacros := TfpgTabSheet.Create(pcOptions);
+ with tsMacros do
+ begin
+ Name := 'tsMacros';
+ SetPosition(3, 24, 594, 497);
+ Text := 'Macros';
+ end;
+
+ tsOther := TfpgTabSheet.Create(pcOptions);
+ with tsOther do
+ begin
+ Name := 'tsOther';
+ SetPosition(3, 24, 456, 217);
+ Text := 'Other';
+ end;
+
+ Label1 := TfpgLabel.Create(tsCompiler);
+ with Label1 do
+ begin
+ Name := 'Label1';
+ SetPosition(4, 10, 284, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Main file';
+ end;
+
+ edtMainFile := TfpgFileNameEdit.Create(tsCompiler);
+ with edtMainFile do
+ begin
+ Name := 'edtMainFile';
+ SetPosition(4, 28, 288, 24);
+ ExtraHint := '';
+ FileName := '${PROJECTNAME}.pas';
+ InitialDir := '';
+ Filter := '';
+ TabOrder := 0;
+ Hint := ' ';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ Label2 := TfpgLabel.Create(tsCompiler);
+ with Label2 do
+ begin
+ Name := 'Label2';
+ SetPosition(300, 10, 288, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Target file (-o)';
+ end;
+
+ edtTargetFile := TfpgFileNameEdit.Create(tsCompiler);
+ with edtTargetFile do
+ begin
+ Name := 'edtTargetFile';
+ SetPosition(300, 28, 288, 24);
+ ExtraHint := '';
+ FileName := '${PROJECTNAME}${EXEEXT}';
+ InitialDir := '';
+ Filter := '';
+ TabOrder := 1;
+ Hint := ' ';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ edtMakeCommand := TfpgFileNameEdit.Create(tsCompiler);
+ with edtMakeCommand do
+ begin
+ Name := 'edtMakeCommand';
+ SetPosition(4, 76, 288, 24);
+ ExtraHint := '';
+ FileName := '${COMPILER}';
+ InitialDir := '';
+ Filter := '';
+ TabOrder := 2;
+ Hint := ' ';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ Label3 := TfpgLabel.Create(tsCompiler);
+ with Label3 do
+ begin
+ Name := 'Label3';
+ SetPosition(4, 58, 284, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Make command';
+ end;
+
+ edtMakeDir := TfpgDirectoryEdit.Create(tsCompiler);
+ with edtMakeDir do
+ begin
+ Name := 'edtMakeDir';
+ SetPosition(300, 76, 288, 24);
+ ExtraHint := '';
+ Directory := '';
+ RootDirectory := '';
+ TabOrder := 3;
+ Hint := ' ';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ Label4 := TfpgLabel.Create(tsCompiler);
+ with Label4 do
+ begin
+ Name := 'Label4';
+ SetPosition(300, 58, 284, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Make directory';
+ end;
+
+ Label5 := TfpgLabel.Create(tsCompiler);
+ with Label5 do
+ begin
+ Name := 'Label5';
+ SetPosition(4, 106, 284, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Message output file';
+ Enabled := False;
+ end;
+
+ FilenameEdit4 := TfpgFileNameEdit.Create(tsCompiler);
+ with FilenameEdit4 do
+ begin
+ Name := 'FilenameEdit4';
+ SetPosition(4, 124, 288, 24);
+ ExtraHint := '';
+ FileName := '';
+ InitialDir := '';
+ Filter := '';
+ TabOrder := 4;
+ Enabled := False;
+ end;
+
+ CheckBox1 := TfpgCheckBox.Create(tsCompiler);
+ with CheckBox1 do
+ begin
+ Name := 'CheckBox1';
+ SetPosition(300, 128, 280, 20);
+ FontDesc := '#Label1';
+ Hint := '';
+ TabOrder := 5;
+ Text := 'Copy messages to file';
+ end;
+
+ Label6 := TfpgLabel.Create(tsCompiler);
+ with Label6 do
+ begin
+ Name := 'Label6';
+ SetPosition(4, 154, 144, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Default make column on Run';
+ end;
+
+ cbDefaultMakeCol := TfpgComboBox.Create(tsCompiler);
+ with cbDefaultMakeCol do
+ begin
+ Name := 'cbDefaultMakeCol';
+ SetPosition(4, 172, 132, 24);
+ FontDesc := '#List';
+ Hint := '';
+ Items.Add('M (Make)');
+ Items.Add('B (Build)');
+ Items.Add('1 (Make 1)');
+ Items.Add('2 (Make 2)');
+ Items.Add('3 (Make 3)');
+ Items.Add('4 (Make 4)');
+ TabOrder := 6;
+ FocusItem := 0;
+ end;
+
+ Button1 := TfpgButton.Create(tsCompiler);
+ with Button1 do
+ begin
+ Name := 'Button1';
+ SetPosition(148, 290, 144, 24);
+ Anchors := [anLeft,anBottom];
+ Text := 'Show command line';
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageName := '';
+ TabOrder := 14;
+ end;
+
+ pcCompiler := TfpgPageControl.Create(tsCompiler);
+ with pcCompiler do
+ begin
+ Name := 'pcCompiler';
+ SetPosition(4, 209, 584, 282);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ ActivePageIndex := 0;
+ Hint := '';
+ TabOrder := 9;
+ end;
+
+ TabSheet1 := TfpgTabSheet.Create(pcCompiler);
+ with TabSheet1 do
+ begin
+ Name := 'TabSheet1';
+ SetPosition(3, 24, 578, 255);
+ Text := 'Make options';
+ end;
+
+ TabSheet2 := TfpgTabSheet.Create(pcCompiler);
+ with TabSheet2 do
+ begin
+ Name := 'TabSheet2';
+ SetPosition(3, 24, 578, 255);
+ Text := 'Directories';
+ end;
+
+ grdCompilerMakeOptions := TfpgStringGrid.Create(TabSheet1);
+ with grdCompilerMakeOptions do
+ begin
+ Name := 'grdCompilerMakeOptions';
+ SetPosition(2, 2, 574, 251);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ AddColumn('M', 20, taCenter);
+ AddColumn('B', 20, taCenter);
+ AddColumn('1', 20, taCenter);
+ AddColumn('2', 20, taCenter);
+ AddColumn('3', 20, taCenter);
+ AddColumn('4', 20, taCenter);
+ AddColumn('Command line options', 430, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := ' ';
+ RowCount := 0;
+ RowSelect := False;
+ TabOrder := 1;
+ OnClick := @grdCompilerMakeOptionsClicked;
+ OnDrawCell := @grdCompilerMakeOptionsDrawCell;
+ OnKeyPress := @grdCompilerMakeOptionsKeyPressed;
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ grdCompilerDirs := TfpgStringGrid.Create(TabSheet2);
+ with grdCompilerDirs do
+ begin
+ Name := 'grdCompilerDirs';
+ SetPosition(2, 22, 574, 231);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ AddColumn('M', 20, taCenter);
+ AddColumn('B', 20, taCenter);
+ AddColumn('1', 20, taCenter);
+ AddColumn('2', 20, taCenter);
+ AddColumn('3', 20, taCenter);
+ AddColumn('4', 20, taCenter);
+ AddColumn('U', 20, taCenter);
+ AddColumn('I', 20, taCenter);
+ AddColumn('L', 20, taCenter);
+ AddColumn('O', 20, taCenter);
+ AddColumn('Directories', 350, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := ' ';
+ ParentShowHint := False;
+ RowCount := 0;
+ RowSelect := False;
+ ShowHint := True;
+ TabOrder := 1;
+ OnClick := @grdCompilerDirsClicked;
+ OnDrawCell := @grdCompilerDirsDrawCell;
+ OnKeyPress := @grdCompilerDirsKeyPressed;
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ Label11 := TfpgLabel.Create(TabSheet2);
+ with Label11 do
+ begin
+ Name := 'Label11';
+ SetPosition(4, 4, 560, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Unit (-Fu), Include (-Fi), Library (-Fl) and Object (-Fo) directories';
+ end;
+
+ Label7 := TfpgLabel.Create(tsDebugger);
+ with Label7 do
+ begin
+ Name := 'Label7';
+ SetPosition(4, 10, 296, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Debug command';
+ end;
+
+ FilenameEdit5 := TfpgFileNameEdit.Create(tsDebugger);
+ with FilenameEdit5 do
+ begin
+ Name := 'FilenameEdit5';
+ SetPosition(4, 28, 584, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ FileName := '';
+ InitialDir := '';
+ Filter := '';
+ TabOrder := 2;
+ end;
+
+ Label8 := TfpgLabel.Create(tsDebugger);
+ with Label8 do
+ begin
+ Name := 'Label8';
+ SetPosition(4, 58, 420, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Debug options';
+ end;
+
+ Edit1 := TfpgEdit.Create(tsDebugger);
+ with Edit1 do
+ begin
+ Name := 'Edit1';
+ SetPosition(4, 76, 584, 24);
+ Anchors := [anLeft,anRight,anTop];
+ ExtraHint := '';
+ Hint := '';
+ TabOrder := 4;
+ Text := '';
+ FontDesc := '#Edit1';
+ end;
+
+ CheckBox2 := TfpgCheckBox.Create(tsDebugger);
+ with CheckBox2 do
+ begin
+ Name := 'CheckBox2';
+ SetPosition(4, 124, 152, 20);
+ FontDesc := '#Label1';
+ Hint := '';
+ TabOrder := 5;
+ Text := 'Activate on Break';
+ end;
+
+ CheckBox3 := TfpgCheckBox.Create(tsDebugger);
+ with CheckBox3 do
+ begin
+ Name := 'CheckBox3';
+ SetPosition(160, 124, 152, 20);
+ FontDesc := '#Label1';
+ Hint := '';
+ TabOrder := 6;
+ Text := 'Stop on Exception';
+ end;
+
+ CheckBox4 := TfpgCheckBox.Create(tsDebugger);
+ with CheckBox4 do
+ begin
+ Name := 'CheckBox4';
+ SetPosition(320, 124, 160, 20);
+ FontDesc := '#Label1';
+ Hint := '';
+ TabOrder := 7;
+ Text := 'Show Console on Run';
+ end;
+
+ PageControl1 := TfpgPageControl.Create(tsDebugger);
+ with PageControl1 do
+ begin
+ Name := 'PageControl1';
+ SetPosition(4, 153, 584, 338);
+ ActivePageIndex := 0;
+ Hint := '';
+ TabOrder := 8;
+ end;
+
+ TabSheet3 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet3 do
+ begin
+ Name := 'TabSheet3';
+ SetPosition(3, 24, 578, 311);
+ Text := 'Source directories';
+ end;
+
+ TabSheet4 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet4 do
+ begin
+ Name := 'TabSheet4';
+ SetPosition(3, 24, 578, 311);
+ Text := 'Defines';
+ end;
+
+ TabSheet5 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet5 do
+ begin
+ Name := 'TabSheet5';
+ SetPosition(3, 24, 578, 311);
+ Text := 'Signals';
+ end;
+
+ TabSheet6 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet6 do
+ begin
+ Name := 'TabSheet6';
+ SetPosition(3, 24, 578, 311);
+ Text := 'Exceptions';
+ end;
+
+ TabSheet7 := TfpgTabSheet.Create(PageControl1);
+ with TabSheet7 do
+ begin
+ Name := 'TabSheet7';
+ SetPosition(3, 24, 578, 311);
+ Text := 'Target';
+ end;
+
+ grdDebugSrcDirs := TfpgStringGrid.Create(TabSheet3);
+ with grdDebugSrcDirs do
+ begin
+ Name := 'grdDebugSrcDirs';
+ SetPosition(2, 2, 574, 307);
+ AddColumn('New', 550, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 5;
+ RowSelect := False;
+ ShowHeader := False;
+ TabOrder := 1;
+ end;
+
+ btnShowCmdLine := TfpgButton.Create(tsCompiler);
+ with btnShowCmdLine do
+ begin
+ Name := 'btnShowCmdLine';
+ SetPosition(160, 172, 132, 24);
+ Text := 'Show command line';
+ FontDesc := '#Label1';
+ Hint := '';
+ ImageName := '';
+ TabOrder := 7;
+ OnClick :=@btnShowCmdLineClicked;
+ end;
+
+ edtUnitOutputDir := TfpgDirectoryEdit.Create(tsCompiler);
+ with edtUnitOutputDir do
+ begin
+ Name := 'edtUnitOutputDir';
+ SetPosition(300, 172, 288, 24);
+ ExtraHint := '';
+ Directory := '';
+ RootDirectory := '';
+ TabOrder := 8;
+ Hint := ' ';
+ OnShowHint := @BeforeShowHint;
+ end;
+
+ Label9 := TfpgLabel.Create(tsCompiler);
+ with Label9 do
+ begin
+ Name := 'Label9';
+ SetPosition(300, 154, 280, 16);
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Unit output directory (-FU)';
+ end;
+
+ Label10 := TfpgLabel.Create(tsMacros);
+ with Label10 do
+ begin
+ Name := 'Label10';
+ SetPosition(4, 10, 580, 16);
+ Anchors := [anLeft,anRight,anTop];
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'Active Group';
+ end;
+
+ grdMacroGroup := TfpgStringGrid.Create(tsMacros);
+ with grdMacroGroup do
+ begin
+ Name := 'grdMacroGroup';
+ SetPosition(4, 28, 584, 104);
+ Anchors := [anLeft,anRight,anTop];
+ AddColumn('#', 20, taCenter);
+ AddColumn('', 20, taCenter);
+ AddColumn('Name', 520, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 6;
+ RowSelect := False;
+ ShowHeader := False;
+ TabOrder := 0;
+ Columns[0].BackgroundColor := clLightGray;
+ Cells[0,0] := '1';
+ Cells[0,1] := '2';
+ Cells[0,2] := '3';
+ Cells[0,3] := '4';
+ Cells[0,4] := '5';
+ Cells[0,5] := '6';
+ FocusCol := 2;
+ OnCanSelectCell := @grdMacroGroupCanSelectCell;
+ end;
+
+ Label12 := TfpgLabel.Create(tsMacros);
+ with Label12 do
+ begin
+ Name := 'Label12';
+ SetPosition(4, 144, 572, 16);
+ Anchors := [anLeft,anRight,anTop];
+ FontDesc := '#Label1';
+ Hint := '';
+ Text := 'User defined macros';
+ end;
+
+ grdUserMacros := TfpgStringGrid.Create(tsMacros);
+ with grdUserMacros do
+ begin
+ Name := 'grdUserMacros';
+ SetPosition(4, 162, 584, 328);
+ Anchors := [anLeft,anRight,anTop,anBottom];
+ AddColumn('1', 20, taCenter);
+ AddColumn('2', 20, taCenter);
+ AddColumn('3', 20, taCenter);
+ AddColumn('4', 20, taCenter);
+ AddColumn('5', 20, taCenter);
+ AddColumn('6', 20, taCenter);
+ AddColumn('Name', 150, taLeftJustify);
+ AddColumn('Value', 290, taLeftJustify);
+ FontDesc := '#Grid';
+ HeaderFontDesc := '#GridHeader';
+ Hint := '';
+ RowCount := 0;
+ RowSelect := False;
+ TabOrder := 1;
+ OnClick := @grdUserMacrosClicked;
+ OnDrawCell := @grdUserMacrosDrawCell;
+ end;
+
+ {@VFD_BODY_END: ProjectOptionsForm}
+ {%endregion}
+end;
+
+
+end.
diff --git a/examples/apps/ide/src/ideconst.pas b/examples/apps/ide/src/ideconst.pas
new file mode 100644
index 00000000..3570bcbf
--- /dev/null
+++ b/examples/apps/ide/src/ideconst.pas
@@ -0,0 +1,83 @@
+unit ideconst;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+const
+ cProjectExt = '.project';
+
+ // Project Options
+ cProjectOptions = 'ProjectOptions';
+ cOpenedUnits = 'OpenedUnits';
+ cUnits = 'Units';
+
+ // INI Environment
+ cEnvironment = 'Environment';
+ cEditor = 'Editor';
+ cShortcuts = 'Shortcuts';
+ cINIMakeOption = 'MakeOption';
+ cINIMakeOptionGrid = 'MakeOptionEnabled';
+ cINIUnitDir = 'UnitDir';
+ cINIUnitDirGrid = 'UnitDirEnabled';
+
+
+ // Predefined Macros
+ cMacro_FPCSrcDir = '${FPCSRCDIR}';
+ cMacro_FPGuiDir = '${FPGUIDIR}';
+ cMacro_FPGuiLibDir = '${FPGUILIBDIR}';
+ cMacro_SyntaxDefDir = '${SYNTAXDEFDIR}';
+ cMacro_TemplateDir = '${TEMPLATEDIR}';
+ cMacro_Compiler = '${COMPILER}';
+ cMacro_Debugger = '${DEBUGGER}';
+ cMacro_ExeExt = '${EXEEXT}';
+ cMacro_Target = '${TARGET}';
+ cMacro_ProjectDir = '${PROJDIR}';
+
+
+ OSTarget: String = {$I %FPCTARGETOS%};
+ CPUTarget: String = {$I %FPCTARGETCPU%};
+ FPCVersion: String = {$I %FPCVERSION%};
+ FPCDate: String = {$I %FPCDATE%};
+
+
+ // Unicode character used as grid check mark
+ cMultiplicationX = #$E2#$9C#$95;
+ cNormCheck = #$E2#$9C#$93;
+ cHeavyCheck = #$E2#$9C#$94;
+ cHeavyX = #$E2#$9C#$96;
+ cMedCircle = #$E2#$9A#$AB;
+ cCheck = cHeavyCheck;
+{
+ U+2715 MULTIPLICATION X
+ UTF-8: 0xE2 0x9C 0x95
+ UTF-16: 0x2715
+
+ U+2713 CHECK MARK
+ UTF-8: 0xE2 0x9C 0x93
+ UTF-16: 0x2713
+
+ U+2714 HEAVY CHECK MARK
+ UTF-8: 0xE2 0x9C 0x94
+ UTF-16: 0x2714
+
+ U+2716 HEAVY MULTIPLICATION X
+ UTF-8: 0xE2 0x9C 0x96
+ UTF-16: 0x2716
+
+ U+26AB MEDIUM BLACK CIRCLE
+ UTF-8: 0xE2 0x9A 0xAB
+ UTF-16: 0x26AB
+}
+
+
+implementation
+
+initialization
+ OSTarget := Lowercase(OSTarget);
+
+end.
+
diff --git a/examples/apps/ide/src/ideimages.pas b/examples/apps/ide/src/ideimages.pas
new file mode 100644
index 00000000..6963e6e9
--- /dev/null
+++ b/examples/apps/ide/src/ideimages.pas
@@ -0,0 +1,64 @@
+unit ideimages;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+procedure RegisterIDEImages;
+
+
+implementation
+
+uses
+ fpg_main;
+
+const
+ ide_gutter_vertical : Array[0..437] of byte = (
+ 66, 77,182, 1, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 40, 0, 0,
+ 0, 15, 0, 0, 0, 8, 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0,
+ 128, 1, 0, 0,196, 14, 0, 0,196, 14, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,187,160,131,243,220,191,247,227,200,243,221,194,239,215,
+ 187,235,210,183,231,206,179,226,198,167,224,196,162,226,197,166,224,
+ 199,168,224,199,168,226,204,175,203,175,144,255,255,255, 0, 0, 0,
+ 187,160,131,243,220,191,247,227,200,243,221,194,239,215,187,235,210,
+ 183,231,206,179,226,198,167,224,196,162,226,197,166,224,199,168,224,
+ 199,168,226,204,175,203,175,144,255,255,255, 0, 0, 0,187,160,131,
+ 243,220,191,247,227,200,243,221,194,239,215,187,235,210,183,231,206,
+ 179,226,198,167,224,196,162,226,197,166,224,199,168,224,199,168,226,
+ 204,175,203,175,144,255,255,255, 0, 0, 0,187,160,131,243,220,191,
+ 247,227,200,243,221,194,239,215,187,235,210,183,231,206,179,226,198,
+ 167,224,196,162,226,197,166,224,199,168,224,199,168,226,204,175,203,
+ 175,144,255,255,255, 0, 0, 0,187,160,131,243,220,191,247,227,200,
+ 243,221,194,239,215,187,235,210,183,231,206,179,226,198,167,224,196,
+ 162,226,197,166,224,199,168,224,199,168,226,204,175,203,175,144,255,
+ 255,255, 0, 0, 0,187,160,131,243,220,191,247,227,200,243,221,194,
+ 239,215,187,235,210,183,231,206,179,226,198,167,224,196,162,226,197,
+ 166,224,199,168,224,199,168,226,204,175,203,175,144,255,255,255, 0,
+ 0, 0,187,160,131,243,220,191,247,227,200,243,221,194,239,215,187,
+ 235,210,183,231,206,179,226,198,167,224,196,162,226,197,166,224,199,
+ 168,224,199,168,226,204,175,203,175,144,255,255,255, 0, 0, 0,187,
+ 160,131,243,220,191,247,227,200,243,221,194,239,215,187,235,210,183,
+ 231,206,179,226,198,167,224,196,162,226,197,166,224,199,168,224,199,
+ 168,226,204,175,203,175,144,255,255,255, 0, 0, 0);
+
+
+procedure RegisterIDEImages;
+begin
+ // system images. Change these to the composite arrow bmp that includes
+ // disabled state
+ //fpgImages.AddBMP(
+ // 'sys.sb.up',
+ // @stdimg_arrow_up,
+ // sizeof(stdimg_arrow_up));
+
+ fpgImages.AddMaskedBMP( // 60x12 in total. 5 images of 12x12 each.
+ 'ide.guttervertical',
+ @ide_gutter_vertical,
+ sizeof(ide_gutter_vertical), 0,0);
+end;
+
+end.
+
diff --git a/examples/apps/ide/src/idemacros.pas b/examples/apps/ide/src/idemacros.pas
new file mode 100644
index 00000000..deee6507
--- /dev/null
+++ b/examples/apps/ide/src/idemacros.pas
@@ -0,0 +1,318 @@
+unit idemacros;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes
+ ,SysUtils
+ ,fpg_base
+ ,fpg_main
+ ;
+
+type
+ TIDEMacro = class(TObject)
+ public
+ Name: string;
+ Value: string;
+ Description: string;
+ constructor Create(AName, AValue, ADescription: TfpgString);
+ end;
+
+
+ TIDEMacroList = class(TObject)
+ private
+ FItems: TList;
+ function GetItems(AIndex: integer): TIDEMacro;
+ procedure SetItems(AIndex: integer; const AValue: TIDEMacro);
+ procedure AddDefaults;
+ procedure LoadSavedValues;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ function Count: integer;
+ function FindByName(const MacroName: TfpgString): TIDEMacro;
+ function StrHasMacros(const s: TfpgString): boolean;
+ function ExpandMacro(const s: TfpgString): TfpgString;
+ procedure Add(NewMacro: TIDEMacro);
+ procedure Clear;
+ procedure Delete(AIndex: integer);
+ procedure SetValue(const MacroName, NewValue: TfpgString);
+ procedure ResetToDefaults;
+ property Items[AIndex: integer]: TIDEMacro read GetItems write SetItems; default;
+ end;
+
+
+// lazy-man singleton of IDE Macros
+function GMacroList: TIDEMacroList;
+
+
+implementation
+
+uses
+ ideconst
+ ,fpg_iniutils;
+
+var
+ uIDEMacroList: TIDEMacroList;
+
+
+function GMacroList: TIDEMacroList;
+begin
+ if not Assigned(uIDEMacroList) then
+ uIDEMacroList := TIDEMacroList.Create;
+ Result := uIDEMacroList;
+end;
+
+
+{ TIDEMacro }
+
+constructor TIDEMacro.Create(AName, AValue, ADescription: TfpgString);
+begin
+ Name := AName;
+ Value := AValue;
+ Description := ADescription;
+end;
+
+
+{ TIDEMacroList }
+
+function TIDEMacroList.GetItems(AIndex: integer): TIDEMacro;
+begin
+ Result := TIDEMacro(FItems[AIndex]);
+end;
+
+procedure TIDEMacroList.SetItems(AIndex: integer; const AValue: TIDEMacro);
+begin
+ FItems[AIndex] := AValue;
+end;
+
+procedure TIDEMacroList.AddDefaults;
+var
+ o: TIDEMacro;
+begin
+ o := TIDEMacro.Create(cMacro_FPCSrcDir, '', 'FPC source directory');
+ Add(o);
+ o := TIDEMacro.Create(cMacro_FPGuiDir, '', 'fpGUI root directory');
+ Add(o);
+ o := TIDEMacro.Create(cMacro_FPGuiLibDir, cMacro_FPGuiDir+'lib/'+cMacro_Target+'/', 'fpGUI compiled library directory');
+ Add(o);
+ o := TIDEMacro.Create(cMacro_SyntaxDefDir, cMacro_FPGuiDir+'examples/apps/fpgide/syntaxdefs/', 'Editor syntax highlighter definitions');
+ Add(o);
+ o := TIDEMacro.Create(cMacro_TemplateDir, cMacro_FPGuiDir+'examples/apps/fpgide/templates/', 'Project template directory');
+ Add(o);
+ o := TIDEMacro.Create(cMacro_Compiler, '', 'FPC Compiler to use');
+ Add(o);
+ o := TIDEMacro.Create(cMacro_Debugger, 'gdb', 'Location of GDB debugger');
+ Add(o);
+ o := TIDEMacro.Create(cMacro_ExeExt, {$IFDEF MSWINDOWS} '.exe' {$ENDIF} {$IFDEF UNIX} '' {$ENDIF}, 'Default executable extension');
+ Add(o);
+ o := TIDEMacro.Create(cMacro_Target, CPUTarget+'-'+OSTarget, 'Default target');
+ Add(o);
+end;
+
+procedure TIDEMacroList.LoadSavedValues;
+var
+ s: TfpgString;
+begin
+ // we don't unnecessarily override the defaults setup in AddDefaults()
+ SetValue(cMacro_FPCSrcDir, gINI.ReadString(cEnvironment, 'FPCSrcDir', ''));
+ SetValue(cMacro_FPGuiDir, gINI.ReadString(cEnvironment, 'FPGuiDir', ''));
+ s := gINI.ReadString(cEnvironment, 'FPGuiLibDir', '');
+ if s <> '' then
+ SetValue(cMacro_FPGuiLibDir, s);
+ s := gINI.ReadString(cEnvironment, 'SyntaxDefDir', '');
+ if s <> '' then
+ SetValue(cMacro_SyntaxDefDir, s);
+ s := gINI.ReadString(cEnvironment, 'TemplateDir', '');
+ if s <> '' then
+ SetValue(cMacro_TemplateDir, s);
+ SetValue(cMacro_Compiler, gINI.ReadString(cEnvironment, 'Compiler', ''));
+ s := gINI.ReadString(cEnvironment, 'Debugger', '');
+ if s <> '' then
+ SetValue(cMacro_Debugger, s);
+ s := gINI.ReadString(cEnvironment, 'ExeExt', '');
+ if s <> '' then
+ SetValue(cMacro_ExeExt, s);
+ s := gINI.ReadString(cEnvironment, 'Target', '');
+ if s <> '' then
+ SetValue(cMacro_Target, s);
+end;
+
+constructor TIDEMacroList.Create;
+begin
+ inherited Create;
+ FItems := TList.Create;
+ AddDefaults;
+ LoadSavedValues;
+end;
+
+destructor TIDEMacroList.Destroy;
+begin
+ Clear;
+ FItems.Free;
+ inherited Destroy;
+end;
+
+function TIDEMacroList.Count: integer;
+begin
+ Result := FItems.Count;
+end;
+
+function TIDEMacroList.FindByName(const MacroName: TfpgString): TIDEMacro;
+var
+ l: Integer;
+ r: Integer;
+ m: Integer;
+ cmp: Integer;
+begin
+ l := 0;
+ r := FItems.Count-1;
+ m := 0;
+ while l <= r do
+ begin
+ m := (l+r) shr 1;
+ Result := Items[m];
+ cmp := AnsiCompareText(MacroName,Result.Name);
+ if cmp < 0 then
+ r := m-1
+ else if cmp > 0 then
+ l := m+1
+ else
+ exit;
+ end;
+ Result := nil;
+end;
+
+function TIDEMacroList.StrHasMacros(const s: TfpgString): boolean;
+// search for ${
+var
+ p: Integer;
+ Len: Integer;
+begin
+ Result := false;
+ p := 1;
+ Len := length(s);
+ while (p < Len) do
+ begin
+ if s[p] = '$' then
+ begin
+ inc(p);
+ if (p<Len) and (s[p]<>'$') then
+ begin
+ // skip macro function name
+ while (p<Len) and (s[p]<>'{') do inc(p);
+ if (p<Len) then
+ begin
+ Result:=true;
+ exit;
+ end;
+ end
+ else
+ begin
+ // $$ is not a macro
+ inc(p);
+ end;
+ end else
+ inc(p);
+ end;
+end;
+
+function TIDEMacroList.ExpandMacro(const s: TfpgString): TfpgString;
+var
+ sub: TfpgString;
+ pstart: integer;
+ pend: integer;
+ len: integer;
+ m: TIDEMacro;
+ r: TfpgString;
+begin
+ r := s;
+ pstart := Pos('${', r);
+ while (pstart > 0) do
+ begin
+ len := Length(r);
+ pend := pstart + 2;
+ while pend < len do
+ begin
+ if r[pend] = '}' then
+ break
+ else
+ inc(pend);
+ end;
+ sub := Copy(r, pstart, (pend-pstart)+1);
+ m := FindByName(sub);
+ if not Assigned(m) then
+ raise Exception.CreateFmt('The macro <%s> is not defined.', [sub]);
+ r := StringReplace(r, sub, m.Value, [rfReplaceAll, rfIgnoreCase]);
+ pstart := Pos('${', r);
+ end;
+ Result := r;
+end;
+
+procedure TIDEMacroList.Add(NewMacro: TIDEMacro);
+var
+ l: Integer;
+ r: Integer;
+ m: Integer;
+ cmp: Integer;
+begin
+ l := 0;
+ r := FItems.Count-1;
+ m := 0;
+ while l <= r do
+ begin
+ m := (l+r) shr 1;
+ cmp := AnsiCompareText(NewMacro.Name, Items[m].Name);
+ if cmp < 0 then
+ r := m-1
+ else if cmp > 0 then
+ l := m + 1
+ else
+ break;
+ end;
+ if (m < FItems.Count) and (AnsiCompareText(NewMacro.Name, Items[m].Name) > 0) then
+ inc(m);
+ FItems.Insert(m, NewMacro);
+end;
+
+procedure TIDEMacroList.Clear;
+var
+ i: integer;
+begin
+ for i := 0 to FItems.Count-1 do Items[i].Free;
+ FItems.Clear;
+end;
+
+procedure TIDEMacroList.Delete(AIndex: integer);
+begin
+ Items[AIndex].Free;
+ FItems.Delete(AIndex);
+end;
+
+procedure TIDEMacroList.SetValue(const MacroName, NewValue: TfpgString);
+var
+ lMacro: TIDEMacro;
+begin
+ lMacro := FindByName(MacroName);
+ if lMacro <> nil then
+ lMacro.Value := NewValue;
+end;
+
+procedure TIDEMacroList.ResetToDefaults;
+begin
+ Clear;
+ AddDefaults;
+ LoadSavedValues;
+end;
+
+
+initialization
+ uIDEMacroList := nil;
+
+finalization
+ uIDEMacroList.Free;
+
+end.
+
diff --git a/examples/apps/ide/src/ideutils.pas b/examples/apps/ide/src/ideutils.pas
new file mode 100644
index 00000000..f0b54648
--- /dev/null
+++ b/examples/apps/ide/src/ideutils.pas
@@ -0,0 +1,395 @@
+unit ideutils;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes
+ ,SysUtils
+ ,fpg_base
+ ,fpg_widget
+ ;
+
+
+
+// Set the Screen.Cursor to crHourGlass until the interface goes out of scope
+function TempHourGlassCursor(var AWidget: TfpgWidget): IInterface;
+
+
+function tiNumToken(const AValue, AToken: string): integer;
+function tiToken(const AValue, AToken: string; const APos: integer): string;
+procedure ShowString(const AString: TfpgString; const AHeading: TfpgString);
+
+// Extract the file extension from FileName and return it in all UPPERCASE.
+function ExtractUpperFileExt(const FileName: string): string;
+
+// Transforms all consecutive sequences of #10, #13, #32, and #9 in Str
+// into a single space, and strips off whitespace at the beginning and
+// end of the string
+function CompressWhiteSpace(const Str: string): string;
+
+// See if a string begins/ends with a specific substring
+function StrBeginsWith(const SubStr, Str: string; CaseSensitive: Boolean = True): Boolean;
+function StrEndsWith(const SubStr, Str: string; CaseSensitive: Boolean = True): Boolean;
+// See is a string contains another substring
+function StrContains(const SubStr, Str: string; CaseSensitive: Boolean = True): Boolean;
+
+// Find SubString in S; do not consider case;
+// this works exactly the same as the Pos function,
+// except for case-INsensitivity.
+function CaseInsensitivePos(Pat, Text: PChar): Integer; overload;
+function CaseInsensitivePos(const Pat, Text: string): Integer; overload;
+function AnsiCaseInsensitivePos(const SubString, S: string): Integer;
+
+procedure MakeASCIICharTable;
+procedure Initialize;
+
+function IsCharAlpha(ch: Char): Boolean;
+function IsCharUpper(ch: Char): Boolean;
+function IsCharLower(ch: Char): Boolean;
+function IsCharAlphaNumeric(ch: Char): Boolean;
+
+// Emulates the VB $Right function to obtain up to n of the
+// rightmost characters in a string.
+function RightString(const Value: string; NumChars: Integer): string;
+
+function IsPas(const FileName: string): Boolean;
+function IsInc(const FileName: string): Boolean;
+function IsProgram(const FileName: string): Boolean;
+
+
+
+
+implementation
+
+uses
+ fpg_form
+ ,fpg_memo
+ ,fpg_main
+ ,fpg_utils
+ ;
+
+var
+ LocaleIdentifierChars: set of Char;
+ ASCIICharTable: array [#0..#255] of Byte;
+
+const
+ EmptyString = '';
+ GxIdentChars = ['A'..'Z', 'a'..'z', '0'..'9', '_'];
+ GxIdentStartChars = ['A'..'Z', 'a'..'z', '0'..'9'];
+ GxAlphaChars = ['A'..'Z', 'a'..'z'];
+ GxUpperAlphaChars = ['A'..'Z'];
+ GxLowerAlphaChars = ['a'..'z'];
+ GxSentenceEndChars = ['.', '!', '?'];
+
+ SAllAlphaNumericChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
+
+
+type
+ { once it goes out of scope, it restores the mouse cursor }
+ TTempHourClassCursor = class(TInterfacedObject, IInterface)
+ private
+ FOldCursor: TMouseCursor;
+ FWidget: TfpgWidget;
+ public
+ constructor Create(var AWidget: TfpgWidget);
+ destructor Destroy; override;
+ end;
+
+constructor TTempHourClassCursor.Create(var AWidget: TfpgWidget);
+begin
+ inherited Create;
+ FOldCursor := AWidget.MouseCursor;
+ FWidget := AWidget;
+ AWidget.MouseCursor := mcHourGlass;
+end;
+
+destructor TTempHourClassCursor.Destroy;
+begin
+ FWidget.MouseCursor := FOldCursor;
+ inherited Destroy;
+end;
+
+
+
+function TempHourGlassCursor(var AWidget: TfpgWidget): IInterface;
+begin
+ Result := TTempHourClassCursor.Create(AWidget) as IInterface;
+end;
+
+function tiNumToken(const AValue, AToken : string): integer;
+var
+ i, iCount : integer;
+ lsValue : string;
+begin
+ Result := 0;
+ if AValue = '' then
+ Exit; //==>
+
+ iCount := 0;
+ lsValue := AValue;
+ i := pos(AToken, lsValue);
+ while i <> 0 do begin
+ delete(lsValue, i, length(AToken));
+ inc(iCount);
+ i := pos(AToken, lsValue);
+ end;
+ Result := iCount + 1;
+end;
+
+
+function tiToken(const AValue, AToken : string; const APos : integer): string;
+var
+ i, iCount, iNumToken : integer;
+ lsValue : string;
+begin
+ result := '';
+
+ iNumToken := tiNumToken(AValue, AToken);
+ if APos = 1 then
+ begin
+ if pos(AToken, AValue) = 0 then
+ result := AValue
+ else
+ result := copy(AValue, 1, pos(AToken, AValue)-1);
+ end
+ else if (iNumToken < APos-1) or (APos<1) then
+ begin
+ result := '';
+ end
+ else
+ begin
+ { Remove leading blocks }
+ iCount := 1;
+ lsValue := AValue;
+ i := pos(AToken, lsValue);
+ while (i<>0) and (iCount<APos) do
+ begin
+ delete(lsValue, 1, i + length(AToken) - 1);
+ inc(iCount);
+ i := pos(AToken, lsValue);
+ end;
+
+ if (i=0) and (iCount=APos) then
+ result := lsValue
+ else if (i=0) and (iCount<>APos) then
+ result := ''
+ else
+ result := copy(lsValue, 1, i-1);
+ end;
+end;
+
+procedure ShowString(const AString: TfpgString; const AHeading: TfpgString);
+var
+ lForm: TfpgForm;
+ lMemo: TfpgMemo;
+begin
+ lForm := TfpgForm.Create(nil);
+ lMemo := TfpgMemo.Create(lForm);
+ try
+ lForm.WindowTitle := AHeading;
+ lForm.Width := 450;
+ lForm.Height := 250;
+ lForm.WindowPosition := wpOneThirdDown;
+ lForm.Name := 'FormShowStrings';
+ lMemo.Lines.Text := AString;
+ lMemo.FontDesc := '#Edit2';
+ lMemo.SetPosition(0, 0, lForm.Width, lForm.Height);
+ lMemo.Align := alClient;
+ lForm.ShowModal;
+ finally
+ lForm.free;
+ end;
+end;
+
+function ExtractUpperFileExt(const FileName: string): string;
+begin
+ Result := UpperCase(fpgExtractFileExt(FileName));
+end;
+
+function CompressWhiteSpace(const Str: string): string;
+var
+ i: Integer;
+ Len: Integer;
+ NextResultChar: Integer;
+ CheckChar: Char;
+ NextChar: Char;
+begin
+ Len := Length(Str);
+ NextResultChar := 1;
+ SetLength(Result, Len);
+
+ for i := 1 to Len do
+ begin
+ CheckChar := Str[i];
+ {$RANGECHECKS OFF}
+ NextChar := Str[i + 1];
+ {$RANGECHECKS ON}
+ case CheckChar of
+ #9, #10, #13, #32:
+ begin
+ if (NextChar in [#0, #9, #10, #13, #32]) or (NextResultChar = 1) then
+ Continue
+ else
+ begin
+ Result[NextResultChar] := #32;
+ Inc(NextResultChar);
+ end;
+ end;
+ else
+ begin
+ Result[NextResultChar] := Str[i];
+ Inc(NextResultChar);
+ end;
+ end;
+ end;
+ if Len = 0 then
+ Exit;
+ SetLength(Result, NextResultChar - 1);
+end;
+
+function StrBeginsWith(const SubStr, Str: string; CaseSensitive: Boolean): Boolean;
+begin
+ if CaseSensitive then
+ Result := Pos(SubStr, Str) = 1
+ else
+ Result := CaseInsensitivePos(SubStr, Str) = 1;
+end;
+
+function StrEndsWith(const SubStr, Str: string; CaseSensitive: Boolean): Boolean;
+begin
+ if CaseSensitive then
+ Result := RightString(Str, Length(SubStr)) = SubStr
+ else
+ Result := SameText(RightString(Str, Length(SubStr)), SubStr);
+end;
+
+function StrContains(const SubStr, Str: string; CaseSensitive: Boolean): Boolean;
+begin
+ if CaseSensitive then
+ Result := Pos(SubStr, Str) > 0
+ else
+ Result := CaseInsensitivePos(SubStr, Str) > 0;
+end;
+
+function CaseInsensitivePos(Pat, Text: PChar): Integer;
+var
+ RunPat, RunText, PosPtr: PChar;
+begin
+ Result := 0;
+ RunPat := Pat;
+ RunText := Text;
+ while RunText^ <> #0 do
+ begin
+ if (ASCIICharTable[RunPat^] = ASCIICharTable[RunText^]) then
+ begin
+ PosPtr := RunText;
+ while RunPat^ <> #0 do
+ begin
+ if ASCIICharTable[RunPat^] <> ASCIICharTable[RunText^] then
+ Break;
+ Inc(RunPat);
+ Inc(RunText);
+ end;
+ if RunPat^ = #0 then
+ begin
+ Result := PosPtr - Text + 1;
+ Break;
+ end;
+ end
+ else
+ Inc(RunText);
+ RunPat := Pat;
+ end;
+end;
+
+function CaseInsensitivePos(const Pat, Text: string): Integer; overload;
+begin
+ Result := CaseInsensitivePos(PChar(Pat), PChar(Text));
+end;
+
+function AnsiCaseInsensitivePos(const SubString, S: string): Integer;
+begin
+ Result := AnsiPos(AnsiUpperCase(SubString), AnsiUpperCase(S));
+end;
+
+procedure MakeASCIICharTable;
+var
+ i: Integer;
+begin
+ for i := 0 to 255 do
+ begin
+ If (I > 64) and (I < 91) then
+ ASCIICharTable[Char(I)] := i + 32
+ else
+ ASCIICharTable[Char(I)] := i;
+ end;
+end;
+
+procedure Initialize;
+var
+ i: Char;
+begin
+ for i := Low(Char) to High(Char) do
+ if IsCharAlphaNumeric(i) then
+ Include(LocaleIdentifierChars, i);
+ Include(LocaleIdentifierChars, '_');
+ MakeASCIICharTable;
+end;
+
+function IsCharAlpha(ch: Char): Boolean;
+begin
+ Result := (ch in GxAlphaChars);
+end;
+
+function IsCharUpper(ch: Char): Boolean;
+begin
+ Result := (ch in GxUpperAlphaChars);
+end;
+
+function IsCharLower(ch: Char): Boolean;
+begin
+ Result := (ch in GxLowerAlphaChars);
+end;
+
+function IsCharAlphaNumeric(ch: Char): Boolean;
+begin
+ Result := (ch in GxIdentStartChars);
+end;
+
+function RightString(const Value: string; NumChars: Integer): string;
+begin
+ Result := Copy(Value, (Length(Value) - NumChars) + 1, NumChars);
+end;
+
+function IsPas(const FileName: string): Boolean;
+var
+ FileExt: string;
+begin
+ FileExt := ExtractUpperFileExt(FileName);
+ Result := (FileExt = '.PAS');
+end;
+
+function IsInc(const FileName: string): Boolean;
+var
+ FileExt: string;
+begin
+ FileExt := ExtractUpperFileExt(FileName);
+ Result := (FileExt = '.INC');
+end;
+
+function IsProgram(const FileName: string): Boolean;
+var
+ FileExt: string;
+begin
+ FileExt := ExtractUpperFileExt(FileName);
+ Result := (FileExt = '.LPR') or (FileExt = '.DPR');
+end;
+
+
+initialization
+ Initialize;
+
+end.
+
diff --git a/examples/apps/ide/src/mPasLex.pas b/examples/apps/ide/src/mPasLex.pas
new file mode 100644
index 00000000..a9f452c9
--- /dev/null
+++ b/examples/apps/ide/src/mPasLex.pas
@@ -0,0 +1,1442 @@
+{+--------------------------------------------------------------------------+
+ | Class: TmwPasLex
+ | Created: 07.98 - 10.98
+ | Author: Martin Waldenburg
+ | Description: A very fast Pascal tokenizer.
+ | Version: 1.32
+ | Copyright (c) 1998, 1999 Martin Waldenburg
+ | All rights reserved.
+ |
+ | LICENCE CONDITIONS
+ |
+ | USE OF THE ENCLOSED SOFTWARE
+ | INDICATES YOUR ASSENT TO THE
+ | FOLLOWING LICENCE CONDITIONS.
+ |
+ |
+ |
+ | These Licence Conditions are exlusively
+ | governed by the Law and Rules of the
+ | Federal Republic of Germany.
+ |
+ | Redistribution and use in source and binary form, with or without
+ | modification, are permitted provided that the following conditions
+ | are met:
+ |
+ | 1. Redistributions of source code must retain the above copyright
+ | notice, this list of conditions and the following disclaimer.
+ | If the source is modified, the complete original and unmodified
+ | source code has to distributed with the modified version.
+ |
+ | 2. Redistributions in binary form must reproduce the above
+ | copyright notice, these licence conditions and the disclaimer
+ | found at the end of this licence agreement in the documentation
+ | and/or other materials provided with the distribution.
+ |
+ | 3. Software using this code must contain a visible line of credit.
+ |
+ | 4. If my code is used in a "for profit" product, you have to donate
+ | to a registered charity in an amount that you feel is fair.
+ | You may use it in as many of your products as you like.
+ | Proof of this donation must be provided to the author of
+ | this software.
+ |
+ | 5. If you for some reasons don't want to give public credit to the
+ | author, you have to donate three times the price of your software
+ | product, or any other product including this component in any way,
+ | but no more than $500 US and not less than $200 US, or the
+ | equivalent thereof in other currency, to a registered charity.
+ | You have to do this for every of your products, which uses this
+ | code separately.
+ | Proof of this donations must be provided to the author of
+ | this software.
+ |
+ |
+ | DISCLAIMER:
+ |
+ | THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS'.
+ |
+ | ALL EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ | PARTICULAR PURPOSE ARE DISCLAIMED.
+ |
+ | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ |
+ | Martin.Waldenburg@T-Online.de
+ +--------------------------------------------------------------------------+}
+
+unit mPasLex;
+
+{$mode delphi}{$H+}
+
+interface
+
+uses
+ SysUtils;
+
+var
+ Identifiers: array[#0..#255]of ByteBool;
+ mHashTable: array[#0..#255]of Integer;
+
+type
+ TTokenKind=(tkAbsolute, tkAbstract, tkAddressOp, tkAnd, tkAnsiComment,
+ tkArray, tkAs, tkAt, tkAsciiChar, tkAsm, tkAssembler, tkAssign, tkAutomated,
+ tkBegin, tkBadString, tkBorComment, tkCase, tkCdecl, tkClass, tkColon,
+ tkComma, tkCompDirect, tkConst, tkConstructor, tkCRLF, tkCRLFCo, tkDefault,
+ tkDestructor, tkDispid, tkDispinterface, tkDiv, tkDo, tkDoubleAddressOp,
+ tkDotDot, tkDownto, tkDynamic, tkElse, tkEnd, tkEqual, tkError, tkExcept,
+ tkExport, tkExports, tkExternal, tkFar, tkFile, tkFinalization, tkFinally,
+ tkFloat, tkFor, tkForward, tkFunction, tkGoto, tkGreater, tkGreaterEqual,
+ tkIdentifier, tkIf, tkImplementation, tkImplements, tkIn, tkIndex,
+ tkInherited, tkInitialization, tkInline, tkInteger, tkInterface, tkIs,
+ tkKeyString, tkLabel, tkLibrary, tkLower, tkLowerEqual, tkMessage, tkMinus,
+ tkMod, tkName, tkNear, tkNil, tkNodefault, tkNone, tkNot, tkNotEqual, tkNull,
+ tkNumber, tkObject, tkOf, tkOn, tkOr, tkOut, tkOverload, tkOverride,
+ tkPacked, tkPascal, tkPlus, tkPoint, tkPointerSymbol, tkPrivate, tkProcedure,
+ tkProgram, tkProperty, tkProtected, tkPublic, tkPublished, tkRaise, tkRead,
+ tkReadonly, tkRecord, tkRegister, tkReintroduce, tkRepeat, tkResident,
+ tkResourcestring, tkRoundClose, tkRoundOpen, tkSafecall, tkSemiColon, tkSet,
+ tkShl, tkShr, tkSlash, tkSlashesComment, tkSquareClose, tkSquareOpen,
+ tkSpace, tkStar, tkStdcall, tkStored, tkString, tkStringresource, tkSymbol,
+ tkThen, tkThreadvar, tkTo, tkTry, tkType, tkUnit, tkUnknown, tkUntil, tkUses,
+ tkVar, tkVirtual, tkWhile, tkWith, tkWrite, tkWriteonly, tkXor);
+
+ TCommentState=(csAnsi, csBor, csNo);
+
+ TmwPasLex=class(TObject)
+ private
+ fComment: TCommentState;
+ fOrigin: PChar;
+ fProcTable: array[#0..#255]of procedure of Object;
+ Run: Longint;
+ Temp: PChar;
+ FRoundCount: Integer;
+ FSquareCount: Integer;
+ fStringLen: Integer;
+ fToIdent: PChar;
+ fIdentFuncTable: array[0..191]of function: TTokenKind of Object;
+ fTokenPos: Integer;
+ fLineNumber: Integer;
+ FTokenID: TTokenKind;
+ fLastIdentPos: Integer;
+ fLastNoSpace: TTokenKind;
+ fLastNoSpacePos: Integer;
+ fLinePos: Integer;
+ fIsInterface: Boolean;
+ fIsClass: Boolean;
+ function KeyHash(ToHash: PChar): Integer;
+ function KeyComp(const aKey: string): Boolean;
+ function Func15: TTokenKind;
+ function Func19: TTokenKind;
+ function Func20: TTokenKind;
+ function Func21: TTokenKind;
+ function Func23: TTokenKind;
+ function Func25: TTokenKind;
+ function Func27: TTokenKind;
+ function Func28: TTokenKind;
+ function Func29: TTokenKind;
+ function Func32: TTokenKind;
+ function Func33: TTokenKind;
+ function Func35: TTokenKind;
+ function Func37: TTokenKind;
+ function Func38: TTokenKind;
+ function Func39: TTokenKind;
+ function Func40: TTokenKind;
+ function Func41: TTokenKind;
+ function Func44: TTokenKind;
+ function Func45: TTokenKind;
+ function Func47: TTokenKind;
+ function Func49: TTokenKind;
+ function Func52: TTokenKind;
+ function Func54: TTokenKind;
+ function Func55: TTokenKind;
+ function Func56: TTokenKind;
+ function Func57: TTokenKind;
+ function Func59: TTokenKind;
+ function Func60: TTokenKind;
+ function Func61: TTokenKind;
+ function Func63: TTokenKind;
+ function Func64: TTokenKind;
+ function Func65: TTokenKind;
+ function Func66: TTokenKind;
+ function Func69: TTokenKind;
+ function Func71: TTokenKind;
+ function Func73: TTokenKind;
+ function Func75: TTokenKind;
+ function Func76: TTokenKind;
+ function Func79: TTokenKind;
+ function Func81: TTokenKind;
+ function Func84: TTokenKind;
+ function Func85: TTokenKind;
+ function Func87: TTokenKind;
+ function Func88: TTokenKind;
+ function Func91: TTokenKind;
+ function Func92: TTokenKind;
+ function Func94: TTokenKind;
+ function Func95: TTokenKind;
+ function Func96: TTokenKind;
+ function Func97: TTokenKind;
+ function Func98: TTokenKind;
+ function Func99: TTokenKind;
+ function Func100: TTokenKind;
+ function Func101: TTokenKind;
+ function Func102: TTokenKind;
+ function Func103: TTokenKind;
+ function Func105: TTokenKind;
+ function Func106: TTokenKind;
+ function Func117: TTokenKind;
+ function Func126: TTokenKind;
+ function Func129: TTokenKind;
+ function Func132: TTokenKind;
+ function Func133: TTokenKind;
+ function Func136: TTokenKind;
+ function Func141: TTokenKind;
+ function Func143: TTokenKind;
+ function Func166: TTokenKind;
+ function Func168: TTokenKind;
+ function Func191: TTokenKind;
+ function AltFunc: TTokenKind;
+ procedure InitIdent;
+ function IdentKind(MayBe: PChar): TTokenKind;
+ procedure SetOrigin(NewValue: PChar);
+ procedure SetRunPos(Value: Integer);
+ procedure MakeMethodTables;
+ procedure AddressOpProc;
+ procedure AsciiCharProc;
+ procedure AnsiProc;
+ procedure BorProc;
+ procedure BraceCloseProc;
+ procedure BraceOpenProc;
+ procedure ColonProc;
+ procedure CommaProc;
+ procedure CRProc;
+ procedure EqualProc;
+ procedure GreaterProc;
+ procedure IdentProc;
+ procedure IntegerProc;
+ procedure LFProc;
+ procedure LowerProc;
+ procedure MinusProc;
+ procedure NullProc;
+ procedure NumberProc;
+ procedure PlusProc;
+ procedure PointerSymbolProc;
+ procedure PointProc;
+ procedure RoundCloseProc;
+ procedure RoundOpenProc;
+ procedure SemiColonProc;
+ procedure SlashProc;
+ procedure SpaceProc;
+ procedure SquareCloseProc;
+ procedure SquareOpenProc;
+ procedure StarProc;
+ procedure StringProc;
+ procedure SymbolProc;
+ procedure UnknownProc;
+ function GetToken: string;
+ function InSymbols(aChar: Char): Boolean;
+ protected
+ public
+ constructor Create;
+ destructor Destroy; override;
+ function CharAhead(Count: Integer): Char;
+ function NextChar: Char;
+ procedure Next;
+ procedure NextID(ID: TTokenKind);
+ procedure NextNoJunk;
+ procedure NextClass;
+ property IsClass: Boolean read fIsClass;
+ property IsInterface: Boolean read fIsInterface;
+ property LastIdentPos: Integer read fLastIdentPos;
+ property LastNoSpace: TTokenKind read fLastNoSpace;
+ property LastNoSpacePos: Integer read fLastNoSpacePos;
+ property LineNumber: Integer read fLineNumber;
+ property LinePos: Integer read fLinePos;
+ property Origin: PChar read fOrigin write SetOrigin;
+ property RunPos: Integer read Run write SetRunPos;
+ property TokenPos: Integer read fTokenPos;
+ property Token: string read GetToken;
+ property TokenID: TTokenKind read FTokenID;
+ published
+ end;
+
+implementation
+
+procedure MakeIdentTable;
+var
+ I, J: Char;
+begin
+ for I:=#0 to #255 do
+ begin
+ Case I of
+ '_', '0'..'9', 'a'..'z', 'A'..'Z': Identifiers[I]:=True;
+ else Identifiers[I]:=False;
+ end;
+ J:=UpperCase(I)[1];
+ Case I of
+ 'a'..'z', 'A'..'Z', '_': mHashTable[I]:=Ord(J)-64;
+ else mHashTable[Char(I)]:=0;
+ end;
+ end;
+end;
+
+procedure TmwPasLex.InitIdent;
+var
+ I: Integer;
+begin
+ for I:=0 to 191 do
+ Case I of
+ 15: fIdentFuncTable[I]:=Func15;
+ 19: fIdentFuncTable[I]:=Func19;
+ 20: fIdentFuncTable[I]:=Func20;
+ 21: fIdentFuncTable[I]:=Func21;
+ 23: fIdentFuncTable[I]:=Func23;
+ 25: fIdentFuncTable[I]:=Func25;
+ 27: fIdentFuncTable[I]:=Func27;
+ 28: fIdentFuncTable[I]:=Func28;
+ 29: fIdentFuncTable[I]:=Func29;
+ 32: fIdentFuncTable[I]:=Func32;
+ 33: fIdentFuncTable[I]:=Func33;
+ 35: fIdentFuncTable[I]:=Func35;
+ 37: fIdentFuncTable[I]:=Func37;
+ 38: fIdentFuncTable[I]:=Func38;
+ 39: fIdentFuncTable[I]:=Func39;
+ 40: fIdentFuncTable[I]:=Func40;
+ 41: fIdentFuncTable[I]:=Func41;
+ 44: fIdentFuncTable[I]:=Func44;
+ 45: fIdentFuncTable[I]:=Func45;
+ 47: fIdentFuncTable[I]:=Func47;
+ 49: fIdentFuncTable[I]:=Func49;
+ 52: fIdentFuncTable[I]:=Func52;
+ 54: fIdentFuncTable[I]:=Func54;
+ 55: fIdentFuncTable[I]:=Func55;
+ 56: fIdentFuncTable[I]:=Func56;
+ 57: fIdentFuncTable[I]:=Func57;
+ 59: fIdentFuncTable[I]:=Func59;
+ 60: fIdentFuncTable[I]:=Func60;
+ 61: fIdentFuncTable[I]:=Func61;
+ 63: fIdentFuncTable[I]:=Func63;
+ 64: fIdentFuncTable[I]:=Func64;
+ 65: fIdentFuncTable[I]:=Func65;
+ 66: fIdentFuncTable[I]:=Func66;
+ 69: fIdentFuncTable[I]:=Func69;
+ 71: fIdentFuncTable[I]:=Func71;
+ 73: fIdentFuncTable[I]:=Func73;
+ 75: fIdentFuncTable[I]:=Func75;
+ 76: fIdentFuncTable[I]:=Func76;
+ 79: fIdentFuncTable[I]:=Func79;
+ 81: fIdentFuncTable[I]:=Func81;
+ 84: fIdentFuncTable[I]:=Func84;
+ 85: fIdentFuncTable[I]:=Func85;
+ 87: fIdentFuncTable[I]:=Func87;
+ 88: fIdentFuncTable[I]:=Func88;
+ 91: fIdentFuncTable[I]:=Func91;
+ 92: fIdentFuncTable[I]:=Func92;
+ 94: fIdentFuncTable[I]:=Func94;
+ 95: fIdentFuncTable[I]:=Func95;
+ 96: fIdentFuncTable[I]:=Func96;
+ 97: fIdentFuncTable[I]:=Func97;
+ 98: fIdentFuncTable[I]:=Func98;
+ 99: fIdentFuncTable[I]:=Func99;
+ 100: fIdentFuncTable[I]:=Func100;
+ 101: fIdentFuncTable[I]:=Func101;
+ 102: fIdentFuncTable[I]:=Func102;
+ 103: fIdentFuncTable[I]:=Func103;
+ 105: fIdentFuncTable[I]:=Func105;
+ 106: fIdentFuncTable[I]:=Func106;
+ 117: fIdentFuncTable[I]:=Func117;
+ 126: fIdentFuncTable[I]:=Func126;
+ 129: fIdentFuncTable[I]:=Func129;
+ 132: fIdentFuncTable[I]:=Func132;
+ 133: fIdentFuncTable[I]:=Func133;
+ 136: fIdentFuncTable[I]:=Func136;
+ 141: fIdentFuncTable[I]:=Func141;
+ 143: fIdentFuncTable[I]:=Func143;
+ 166: fIdentFuncTable[I]:=Func166;
+ 168: fIdentFuncTable[I]:=Func168;
+ 191: fIdentFuncTable[I]:=Func191;
+ else fIdentFuncTable[I]:=AltFunc;
+ end;
+end;
+
+function TmwPasLex.KeyHash(ToHash: PChar): Integer;
+begin
+ Result:=0;
+ while ToHash^in ['a'..'z', 'A'..'Z']do
+ begin
+ Inc(Result, mHashTable[ToHash^]);
+ Inc(ToHash);
+ end;
+ if ToHash^in ['_', '0'..'9']then Inc(ToHash);
+ fStringLen:=ToHash-fToIdent;
+end; { KeyHash }
+
+function TmwPasLex.KeyComp(const aKey: string): Boolean;
+var
+ I: Integer;
+begin
+ Temp:=fToIdent;
+ if Length(aKey)=fStringLen then
+ begin
+ Result:=True;
+ for i:=1 to fStringLen do
+ begin
+ if mHashTable[Temp^]<>mHashTable[aKey[i]]then
+ begin
+ Result:=False;
+ Break;
+ end;
+ Inc(Temp);
+ end;
+ end else Result:=False;
+end; { KeyComp }
+
+function TmwPasLex.Func15: TTokenKind;
+begin
+ if KeyComp('If')then Result:=tkIf else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func19: TTokenKind;
+begin
+ if KeyComp('Do')then Result:=tkDo else
+ if KeyComp('And')then Result:=tkAnd else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func20: TTokenKind;
+begin
+ if KeyComp('As')then Result:=tkAs else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func21: TTokenKind;
+begin
+ if KeyComp('Of')then Result:=tkOf else
+ if KeyComp('At')then Result:=tkAt else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func23: TTokenKind;
+begin
+ if KeyComp('End')then Result:=tkEnd else
+ if KeyComp('In')then Result:=tkIn else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func25: TTokenKind;
+begin
+ if KeyComp('Far')then Result:=tkFar else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func27: TTokenKind;
+begin
+ if KeyComp('Cdecl')then Result:=tkCdecl else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func28: TTokenKind;
+begin
+ if KeyComp('Read')then
+ begin
+ if inSymbols(CharAhead(fStringLen))then Result:=tkIdentifier else
+ Result:=tkRead
+ end else
+ if KeyComp('Case')then Result:=tkCase else
+ if KeyComp('Is')then Result:=tkIs else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func29: TTokenKind;
+begin
+ if KeyComp('On')then Result:=tkOn else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func32: TTokenKind;
+begin
+ if KeyComp('File')then Result:=tkFile else
+ if KeyComp('Label')then Result:=tkLabel else
+ if KeyComp('Mod')then Result:=tkMod else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func33: TTokenKind;
+begin
+ if KeyComp('Or')then Result:=tkOr else
+ if KeyComp('Name')then
+ begin
+ if inSymbols(CharAhead(fStringLen))then Result:=tkIdentifier else
+ Result:=tkName
+ end else
+ if KeyComp('Asm')then Result:=tkAsm else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func35: TTokenKind;
+begin
+ if KeyComp('To')then Result:=tkTo else
+ if KeyComp('Nil')then Result:=tkNil else
+ if KeyComp('Div')then Result:=tkDiv else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func37: TTokenKind;
+begin
+ if KeyComp('Begin')then Result:=tkBegin else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func38: TTokenKind;
+begin
+ if KeyComp('Near')then Result:=tkNear else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func39: TTokenKind;
+begin
+ if KeyComp('For')then Result:=tkFor else
+ if KeyComp('Shl')then Result:=tkShl else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func40: TTokenKind;
+begin
+ if KeyComp('Packed')then Result:=tkPacked else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func41: TTokenKind;
+begin
+ if KeyComp('Else')then Result:=tkElse else
+ if KeyComp('Var')then Result:=tkVar else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func44: TTokenKind;
+begin
+ if KeyComp('Set')then Result:=tkSet else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func45: TTokenKind;
+begin
+ if KeyComp('Shr')then Result:=tkShr else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func47: TTokenKind;
+begin
+ if KeyComp('Then')then Result:=tkThen else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func49: TTokenKind;
+begin
+ if KeyComp('Not')then Result:=tkNot else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func52: TTokenKind;
+begin
+ if KeyComp('Raise')then Result:=tkRaise else
+ if KeyComp('Pascal')then Result:=tkPascal else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func54: TTokenKind;
+begin
+ if KeyComp('Class')then
+ begin
+ Result:=tkClass;
+ if fLastNoSpace=tkEqual then
+ begin
+ fIsClass:=True;
+ if Identifiers[CharAhead(fStringLen)]then fIsClass:=False;
+ end else fIsClass:=False;
+ end else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func55: TTokenKind;
+begin
+ if KeyComp('Object')then Result:=tkObject else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func56: TTokenKind;
+begin
+ if KeyComp('Index')then
+ begin
+ if inSymbols(CharAhead(fStringLen))then Result:=tkIdentifier else
+ Result:=tkIndex
+ end else
+ if KeyComp('Out')then Result:=tkOut else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func57: TTokenKind;
+begin
+ if KeyComp('While')then Result:=tkWhile else
+ if KeyComp('Goto')then Result:=tkGoto else
+ if KeyComp('Xor')then Result:=tkXor else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func59: TTokenKind;
+begin
+ if KeyComp('Safecall')then Result:=tkSafecall else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func60: TTokenKind;
+begin
+ if KeyComp('With')then Result:=tkWith else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func61: TTokenKind;
+begin
+ if KeyComp('Dispid')then Result:=tkDispid else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func63: TTokenKind;
+begin
+ if KeyComp('Public')then
+ begin
+ if inSymbols(CharAhead(fStringLen))then Result:=tkIdentifier else
+ Result:=tkPublic
+ end else
+ if KeyComp('Record')then Result:=tkRecord else
+ if KeyComp('Try')then Result:=tkTry else
+ if KeyComp('Array')then Result:=tkArray else
+ if KeyComp('Inline')then Result:=tkInline else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func64: TTokenKind;
+begin
+ if KeyComp('Uses')then Result:=tkUses else
+ if KeyComp('Unit')then Result:=tkUnit else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func65: TTokenKind;
+begin
+ if KeyComp('Repeat')then Result:=tkRepeat else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func66: TTokenKind;
+begin
+ if KeyComp('Type')then Result:=tkType else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func69: TTokenKind;
+begin
+ if KeyComp('Dynamic')then Result:=tkDynamic else
+ if KeyComp('Default')then Result:=tkDefault else
+ if KeyComp('Message')then Result:=tkMessage else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func71: TTokenKind;
+begin
+ if KeyComp('Stdcall')then Result:=tkStdcall else
+ if KeyComp('Const')then Result:=tkConst else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func73: TTokenKind;
+begin
+ if KeyComp('Except')then Result:=tkExcept else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func75: TTokenKind;
+begin
+ if KeyComp('Write')then
+ begin
+ if inSymbols(CharAhead(fStringLen))then Result:=tkIdentifier else
+ Result:=tkWrite
+ end else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func76: TTokenKind;
+begin
+ if KeyComp('Until')then Result:=tkUntil else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func79: TTokenKind;
+begin
+ if KeyComp('Finally')then Result:=tkFinally else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func81: TTokenKind;
+begin
+ if KeyComp('Interface')then
+ begin
+ Result:=tkInterface;
+ if fLastNoSpace=tkEqual then
+ fIsInterface:=True else fIsInterface:=False;
+ end else
+ if KeyComp('Stored')then Result:=tkStored else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func84: TTokenKind;
+begin
+ if KeyComp('Abstract')then Result:=tkAbstract else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func85: TTokenKind;
+begin
+ if KeyComp('Library')then Result:=tkLibrary else
+ if KeyComp('Forward')then Result:=tkForward else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func87: TTokenKind;
+begin
+ if KeyComp('String')then Result:=tkString else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func88: TTokenKind;
+begin
+ if KeyComp('Program')then Result:=tkProgram else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func91: TTokenKind;
+begin
+ if KeyComp('Private')then
+ begin
+ if inSymbols(CharAhead(fStringLen))then Result:=tkIdentifier else
+ Result:=tkPrivate
+ end else
+ if KeyComp('Downto')then Result:=tkDownto else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func92: TTokenKind;
+begin
+ if KeyComp('overload') then
+ Result:=tkOverload
+ else
+ if KeyComp('Inherited') then
+ Result:=tkInherited
+ else
+ Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func94: TTokenKind;
+begin
+ if KeyComp('Resident')then Result:=tkResident else
+ if KeyComp('Readonly')then Result:=tkReadonly else
+ if KeyComp('Assembler')then Result:=tkAssembler else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func95: TTokenKind;
+begin
+ if KeyComp('Absolute')then Result:=tkAbsolute else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func96: TTokenKind;
+begin
+ if KeyComp('Published')then
+ begin
+ if inSymbols(CharAhead(fStringLen))then Result:=tkIdentifier else
+ Result:=tkPublished
+ end else
+ if KeyComp('Override')then Result:=tkOverride else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func97: TTokenKind;
+begin
+ if KeyComp('Threadvar')then Result:=tkThreadvar else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func98: TTokenKind;
+begin
+ if KeyComp('Export')then Result:=tkExport else
+ if KeyComp('Nodefault')then Result:=tkNodefault else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func99: TTokenKind;
+begin
+ if KeyComp('External')then Result:=tkExternal else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func100: TTokenKind;
+begin
+ if KeyComp('Automated')then
+ begin
+ if inSymbols(CharAhead(fStringLen))then Result:=tkIdentifier else
+ Result:=tkAutomated
+ end else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func101: TTokenKind;
+begin
+ if KeyComp('Register')then Result:=tkRegister else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func102: TTokenKind;
+begin
+ if KeyComp('Function')then Result:=tkFunction else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func103: TTokenKind;
+begin
+ if KeyComp('Virtual')then Result:=tkVirtual else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func105: TTokenKind;
+begin
+ if KeyComp('Procedure')then Result:=tkProcedure else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func106: TTokenKind;
+begin
+ if KeyComp('Protected')then
+ begin
+ if inSymbols(CharAhead(fStringLen))then Result:=tkIdentifier else
+ Result:=tkProtected
+ end else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func117: TTokenKind;
+begin
+ if KeyComp('Exports')then Result:=tkExports else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func126: TTokenKind;
+begin
+ if KeyComp('Implements') then
+ Result:=tkImplements
+ else
+ Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func129: TTokenKind;
+begin
+ if KeyComp('Dispinterface')then Result:=tkDispinterface else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func132: TTokenKind;
+begin
+ if KeyComp('Reintroduce') then
+ Result:=tkReintroduce
+ else
+ Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func133: TTokenKind;
+begin
+ if KeyComp('Property')then Result:=tkProperty else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func136: TTokenKind;
+begin
+ if KeyComp('Finalization')then Result:=tkFinalization else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func141: TTokenKind;
+begin
+ if KeyComp('Writeonly')then Result:=tkWriteonly else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func143: TTokenKind;
+begin
+ if KeyComp('Destructor')then Result:=tkDestructor else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func166: TTokenKind;
+begin
+ if KeyComp('Constructor')then Result:=tkConstructor else
+ if KeyComp('Implementation')then Result:=tkImplementation else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func168: TTokenKind;
+begin
+ if KeyComp('Initialization')then Result:=tkInitialization else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.Func191: TTokenKind;
+begin
+ if KeyComp('Resourcestring')then Result:=tkResourcestring else
+ if KeyComp('Stringresource')then Result:=tkStringresource else Result:=tkIdentifier;
+end;
+
+function TmwPasLex.AltFunc: TTokenKind;
+begin
+ Result:=tkIdentifier
+end;
+
+function TmwPasLex.IdentKind(MayBe: PChar): TTokenKind;
+var
+ HashKey: Integer;
+begin
+ fToIdent:=MayBe;
+ HashKey:=KeyHash(MayBe);
+ if HashKey<192 then Result:=fIdentFuncTable[HashKey]else Result:=tkIdentifier;
+end;
+
+procedure TmwPasLex.MakeMethodTables;
+var
+ I: Char;
+begin
+ for I:=#0 to #255 do
+ case I of
+ #0: fProcTable[I]:=NullProc;
+ #10: fProcTable[I]:=LFProc;
+ #13: fProcTable[I]:=CRProc;
+ #1..#9, #11, #12, #14..#32:
+ fProcTable[I]:=SpaceProc;
+ '#': fProcTable[I]:=AsciiCharProc;
+ '$': fProcTable[I]:=IntegerProc;
+ #39: fProcTable[I]:=StringProc;
+ '0'..'9': fProcTable[I]:=NumberProc;
+ 'A'..'Z', 'a'..'z', '_':
+ fProcTable[I]:=IdentProc;
+ '{': fProcTable[I]:=BraceOpenProc;
+ '}': fProcTable[I]:=BraceCloseProc;
+ '!', '"', '%', '&', '('..'/', ':'..'@', '['..'^', '`', '~':
+ begin
+ case I of
+ '(': fProcTable[I]:=RoundOpenProc;
+ ')': fProcTable[I]:=RoundCloseProc;
+ '*': fProcTable[I]:=StarProc;
+ '+': fProcTable[I]:=PlusProc;
+ ',': fProcTable[I]:=CommaProc;
+ '-': fProcTable[I]:=MinusProc;
+ '.': fProcTable[I]:=PointProc;
+ '/': fProcTable[I]:=SlashProc;
+ ':': fProcTable[I]:=ColonProc;
+ ';': fProcTable[I]:=SemiColonProc;
+ '<': fProcTable[I]:=LowerProc;
+ '=': fProcTable[I]:=EqualProc;
+ '>': fProcTable[I]:=GreaterProc;
+ '@': fProcTable[I]:=AddressOpProc;
+ '[': fProcTable[I]:=SquareOpenProc;
+ ']': fProcTable[I]:=SquareCloseProc;
+ '^': fProcTable[I]:=PointerSymbolProc;
+ else fProcTable[I]:=SymbolProc;
+ end;
+ end;
+ else fProcTable[I]:=UnknownProc;
+ end;
+end;
+
+constructor TmwPasLex.Create;
+begin
+ inherited Create;
+ InitIdent;
+ MakeMethodTables;
+end; { Create }
+
+destructor TmwPasLex.Destroy;
+begin
+ inherited Destroy;
+end; { Destroy }
+
+procedure TmwPasLex.SetOrigin(NewValue: PChar);
+begin
+ fOrigin:=NewValue;
+ fComment:=csNo;
+ fLineNumber:=0;
+ fLinePos:=0;
+ Run:=0;
+ Next;
+end; { SetOrigin }
+
+procedure TmwPasLex.SetRunPos(Value: Integer);
+begin
+ Run:=Value;
+ Next;
+end;
+
+procedure TmwPasLex.AddressOpProc;
+begin
+ Case FOrigin[Run+1]of
+ '@':
+ begin
+ fTokenID:=tkDoubleAddressOp;
+ Inc(Run, 2);
+ end;
+ else
+ begin
+ fTokenID:=tkAddressOp;
+ Inc(Run);
+ end;
+ end;
+end;
+
+procedure TmwPasLex.AsciiCharProc;
+begin
+ fTokenID:=tkAsciiChar;
+ Inc(Run);
+ while FOrigin[Run]in ['0'..'9']do Inc(Run);
+end;
+
+procedure TmwPasLex.BraceCloseProc;
+begin
+ Inc(Run);
+ fTokenId:=tkError;
+end;
+
+procedure TmwPasLex.BorProc;
+begin
+ fTokenID:=tkBorComment;
+ case FOrigin[Run]of
+ #0:
+ begin
+ NullProc;
+ Exit;
+ end;
+
+ #10:
+ begin
+ LFProc;
+ Exit;
+ end;
+
+ #13:
+ begin
+ CRProc;
+ Exit;
+ end;
+ end;
+
+ while FOrigin[Run]<>#0 do
+ case FOrigin[Run]of
+ '}':
+ begin
+ fComment:=csNo;
+ Inc(Run);
+ Break;
+ end;
+ #10: Break;
+
+ #13: Break;
+ else Inc(Run);
+ end;
+end;
+
+procedure TmwPasLex.BraceOpenProc;
+begin
+ Case FOrigin[Run+1]of
+ '$': fTokenID:=tkCompDirect;
+ else
+ begin
+ fTokenID:=tkBorComment;
+ fComment:=csBor;
+ end;
+ end;
+ Inc(Run);
+ while FOrigin[Run]<>#0 do
+ case FOrigin[Run]of
+ '}':
+ begin
+ fComment:=csNo;
+ Inc(Run);
+ Break;
+ end;
+ #10: Break;
+
+ #13: Break;
+ else Inc(Run);
+ end;
+end;
+
+procedure TmwPasLex.ColonProc;
+begin
+ Case FOrigin[Run+1]of
+ '=':
+ begin
+ Inc(Run, 2);
+ fTokenID:=tkAssign;
+ end;
+ else
+ begin
+ Inc(Run);
+ fTokenID:=tkColon;
+ end;
+ end;
+end;
+
+procedure TmwPasLex.CommaProc;
+begin
+ Inc(Run);
+ fTokenID:=tkComma;
+end;
+
+procedure TmwPasLex.CRProc;
+begin
+ Case fComment of
+ csBor: fTokenID:=tkCRLFCo;
+ csAnsi: fTokenID:=tkCRLFCo;
+ else fTokenID:=tkCRLF;
+ end;
+
+ Case FOrigin[Run+1]of
+ #10: Inc(Run, 2);
+ else Inc(Run);
+ end;
+ Inc(fLineNumber);
+ fLinePos:=Run;
+end;
+
+procedure TmwPasLex.EqualProc;
+begin
+ Inc(Run);
+ fTokenID:=tkEqual;
+end;
+
+procedure TmwPasLex.GreaterProc;
+begin
+ Case FOrigin[Run+1]of
+ '=':
+ begin
+ Inc(Run, 2);
+ fTokenID:=tkGreaterEqual;
+ end;
+ else
+ begin
+ Inc(Run);
+ fTokenID:=tkGreater;
+ end;
+ end;
+end;
+
+function TmwPasLex.InSymbols(aChar: Char): Boolean;
+begin
+ if aChar in ['#', '$', '&', #39, '(', ')', '*', '+', ',', #150, '.', '/', ':',
+ ';', '<', '=', '>', '@', '[', ']', '^']then Result:=True else Result:=False;
+end;
+
+function TmwPasLex.CharAhead(Count: Integer): Char;
+begin
+ Temp:=fOrigin+Run+Count;
+ while Temp^in [#1..#9, #11, #12, #14..#32]do Inc(Temp);
+ Result:=Temp^;
+end;
+
+function TmwPasLex.NextChar: Char;
+begin
+ Temp:=fOrigin+Run;
+ Result:=Temp^;
+end;
+
+procedure TmwPasLex.IdentProc;
+begin
+ fTokenID:=IdentKind((fOrigin+Run));
+ Inc(Run, fStringLen);
+ while Identifiers[fOrigin[Run]]do Inc(Run);
+end;
+
+procedure TmwPasLex.IntegerProc;
+begin
+ Inc(Run);
+ fTokenID:=tkInteger;
+ while FOrigin[Run]in ['0'..'9', 'A'..'F', 'a'..'f']do Inc(Run);
+end;
+
+procedure TmwPasLex.LFProc;
+begin
+ Case fComment of
+ csBor: fTokenID:=tkCRLFCo;
+ csAnsi: fTokenID:=tkCRLFCo;
+ else fTokenID:=tkCRLF;
+ end;
+ Inc(Run);
+ Inc(fLineNumber);
+ fLinePos:=Run;
+end;
+
+procedure TmwPasLex.LowerProc;
+begin
+ case FOrigin[Run+1]of
+ '=':
+ begin
+ Inc(Run, 2);
+ fTokenID:=tkLowerEqual;
+ end;
+ '>':
+ begin
+ Inc(Run, 2);
+ fTokenID:=tkNotEqual;
+ end
+ else
+ begin
+ Inc(Run);
+ fTokenID:=tkLower;
+ end;
+ end;
+end;
+
+procedure TmwPasLex.MinusProc;
+begin
+ Inc(Run);
+ fTokenID:=tkMinus;
+end;
+
+procedure TmwPasLex.NullProc;
+begin
+ fTokenID:=tkNull;
+end;
+
+procedure TmwPasLex.NumberProc;
+begin
+ Inc(Run);
+ fTokenID:=tkNumber;
+ while FOrigin[Run]in ['0'..'9', '.', 'e', 'E']do
+ begin
+ case FOrigin[Run]of
+ '.':
+ if FOrigin[Run+1]='.' then Break else fTokenID:=tkFloat
+ end;
+ Inc(Run);
+ end;
+end;
+
+procedure TmwPasLex.PlusProc;
+begin
+ Inc(Run);
+ fTokenID:=tkPlus;
+end;
+
+procedure TmwPasLex.PointerSymbolProc;
+begin
+ Inc(Run);
+ fTokenID:=tkPointerSymbol;
+end;
+
+procedure TmwPasLex.PointProc;
+begin
+ case FOrigin[Run+1]of
+ '.':
+ begin
+ Inc(Run, 2);
+ fTokenID:=tkDotDot;
+ end;
+ ')':
+ begin
+ Inc(Run, 2);
+ fTokenID:=tkSquareClose;
+ Dec(FSquareCount);
+ end;
+ else
+ begin
+ Inc(Run);
+ fTokenID:=tkPoint;
+ end;
+ end;
+end;
+
+procedure TmwPasLex.RoundCloseProc;
+begin
+ Inc(Run);
+ fTokenID:=tkRoundClose;
+ Dec(FRoundCount);
+end;
+
+procedure TmwPasLex.AnsiProc;
+begin
+ fTokenID:=tkAnsiComment;
+ case FOrigin[Run]of
+ #0:
+ begin
+ NullProc;
+ Exit;
+ end;
+
+ #10:
+ begin
+ LFProc;
+ Exit;
+ end;
+
+ #13:
+ begin
+ CRProc;
+ Exit;
+ end;
+ end;
+
+ while fOrigin[Run]<>#0 do
+ case fOrigin[Run]of
+ '*':
+ if fOrigin[Run+1]=')' then
+ begin
+ fComment:=csNo;
+ Inc(Run, 2);
+ Break;
+ end else Inc(Run);
+ #10: Break;
+
+ #13: Break;
+ else Inc(Run);
+ end;
+end;
+
+procedure TmwPasLex.RoundOpenProc;
+begin
+ Inc(Run);
+ case fOrigin[Run]of
+ '*':
+ begin
+ fTokenID:=tkAnsiComment;
+ if FOrigin[Run+1]='$' then fTokenID:=tkCompDirect else fComment:=csAnsi;
+ Inc(Run);
+ while fOrigin[Run]<>#0 do
+ case fOrigin[Run]of
+ '*':
+ if fOrigin[Run+1]=')' then
+ begin
+ fComment:=csNo;
+ Inc(Run, 2);
+ Break;
+ end else Inc(Run);
+ #10: Break;
+ #13: Break;
+ else Inc(Run);
+ end;
+ end;
+ '.':
+ begin
+ Inc(Run);
+ fTokenID:=tkSquareOpen;
+ Inc(FSquareCount);
+ end;
+ else
+ begin
+ FTokenID:=tkRoundOpen;
+ Inc(FRoundCount);
+ end;
+ end;
+end;
+
+procedure TmwPasLex.SemiColonProc;
+begin
+ Inc(Run);
+ fTokenID:=tkSemiColon;
+end;
+
+procedure TmwPasLex.SlashProc;
+begin
+ case FOrigin[Run+1]of
+ '/':
+ begin
+ Inc(Run, 2);
+ fTokenID:=tkSlashesComment;
+ while FOrigin[Run]<>#0 do
+ begin
+ case FOrigin[Run]of
+ #10, #13: Break;
+ end;
+ Inc(Run);
+ end;
+ end;
+ else
+ begin
+ Inc(Run);
+ fTokenID:=tkSlash;
+ end;
+ end;
+end;
+
+procedure TmwPasLex.SpaceProc;
+begin
+ Inc(Run);
+ fTokenID:=tkSpace;
+ while FOrigin[Run]in [#1..#9, #11, #12, #14..#32]do Inc(Run);
+end;
+
+procedure TmwPasLex.SquareCloseProc;
+begin
+ Inc(Run);
+ fTokenID:=tkSquareClose;
+ Dec(FSquareCount);
+end;
+
+procedure TmwPasLex.SquareOpenProc;
+begin
+ Inc(Run);
+ fTokenID:=tkSquareOpen;
+ Inc(FSquareCount);
+end;
+
+procedure TmwPasLex.StarProc;
+begin
+ Inc(Run);
+ fTokenID:=tkStar;
+end;
+
+procedure TmwPasLex.StringProc;
+begin
+ fTokenID:=tkString;
+ if(FOrigin[Run+1]=#39)and(FOrigin[Run+2]=#39)then Inc(Run, 2);
+ repeat
+ case FOrigin[Run]of
+ #0, #10, #13: Break;
+ end;
+ Inc(Run);
+ until FOrigin[Run]=#39;
+ if FOrigin[Run]<>#0 then Inc(Run);
+end;
+
+procedure TmwPasLex.SymbolProc;
+begin
+ Inc(Run);
+ fTokenID:=tkSymbol;
+end;
+
+procedure TmwPasLex.UnknownProc;
+begin
+ Inc(Run);
+ fTokenID:=tkUnknown;
+end;
+
+procedure TmwPasLex.Next;
+begin
+ Case fTokenID of
+ tkIdentifier:
+ begin
+ fLastIdentPos:=fTokenPos;
+ fLastNoSpace:=fTokenID;
+ fLastNoSpacePos:=fTokenPos;
+ end;
+ tkSpace: ;
+ else
+ begin
+ fLastNoSpace:=fTokenID;
+ fLastNoSpacePos:=fTokenPos;
+ end;
+ end;
+ fTokenPos:=Run;
+ Case fComment of
+ csNo: fProcTable[fOrigin[Run]];
+ else
+ Case fComment of
+ csBor: BorProc;
+ csAnsi: AnsiProc;
+ end;
+ end;
+end;
+
+function TmwPasLex.GetToken: string;
+var
+ Len: Longint;
+begin
+ Len:=Run-fTokenPos;
+ SetString(Result, (FOrigin+fTokenPos), Len);
+end;
+
+procedure TmwPasLex.NextID(ID: TTokenKind);
+begin
+ repeat
+ Case fTokenID of
+ tkNull: Break;
+ else Next;
+ end;
+ until fTokenID=ID;
+end;
+
+procedure TmwPasLex.NextNoJunk;
+begin
+ repeat
+ Next;
+ until not(fTokenID in [tkSlashesComment, tkAnsiComment, tkBorComment, tkCRLF, tkCRLFCo, tkSpace]);
+end;
+
+procedure TmwPasLex.NextClass;
+begin
+ if fTokenID<>tkNull then next;
+ repeat
+ Case fTokenID of
+ tkNull: Break;
+ else Next;
+ end;
+ until(fTokenID=tkClass)and(IsClass);
+end;
+
+initialization
+ MakeIdentTable;
+
+end.
+
+
+
+
+
diff --git a/examples/apps/ide/src/proclistimages.inc b/examples/apps/ide/src/proclistimages.inc
new file mode 100644
index 00000000..0b6454db
--- /dev/null
+++ b/examples/apps/ide/src/proclistimages.inc
@@ -0,0 +1,208 @@
+
+Const
+ grdimg_destructor_16 : Array[0..821] of byte = (
+ 66, 77, 54, 3, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 40, 0, 0,
+ 0, 16, 0, 0, 0, 16, 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 19, 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255, 0, 0, 0, 0, 0,
+ 0,132,132,132,132,132,132,132,132,132,132,132,132, 0, 0, 0, 0,
+ 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255, 0, 0, 0,255, 0,255,132,132,132, 0, 0,
+ 0,132,132,132, 0, 0, 0, 0, 0, 0,132,132,132,255, 0,255, 0,
+ 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255, 0, 0, 0, 0, 0, 0,255, 0,255,255, 0,255, 0, 0,
+ 0,255, 0,255,255, 0,255,255, 0,255, 0, 0, 0, 0, 0, 0,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 0, 0, 0,255, 0,255, 0, 0, 0, 0, 0, 0,255,255,255, 0, 0,
+ 0,255, 0,255, 0, 0, 0,255, 0,255, 0, 0, 0,255, 0,255,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255, 0, 0, 0,255, 0,255,
+ 0, 0, 0,255,255,255,255,255,255,255,255,255,255, 0,255, 0, 0,
+ 0,255, 0,255,132,132,132,255, 0,255, 0, 0, 0,255, 0,255,255,
+ 0,255,255, 0,255,255, 0,255, 0, 0, 0, 0, 0, 0,255,255,255,
+ 0, 0, 0,255,255,255, 0, 0, 0, 0, 0, 0,255, 0,255, 0, 0,
+ 0,255, 0,255, 0, 0, 0, 0, 0, 0,255, 0,255,255, 0,255,255,
+ 0,255,255, 0,255, 0, 0, 0,255,255,255,255,255,255,255,255,255,
+ 0, 0, 0,255,255,255,255,255,255,132,132,132,255,255,255, 0, 0,
+ 0,255, 0,255, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,132,
+ 132,132, 0, 0, 0, 0, 0, 0,255,255,255, 0, 0, 0,255,255,255,
+ 0, 0, 0,255,255,255, 0, 0, 0, 0, 0, 0,255, 0,255, 0, 0,
+ 0, 0, 0, 0,132,132,132,255, 0,255,255, 0,255, 0, 0, 0,132,
+ 132,132,255,255,255, 0, 0, 0,255,255,255,255,255,255,255,255,255,
+ 0, 0, 0,255,255,255,255,255,255, 0, 0, 0,255,255,255,132,132,
+ 132, 0, 0, 0,255, 0,255,255, 0,255, 0, 0, 0,255, 0,255,132,
+ 132,132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,132,132,132,255,255,255, 0, 0,
+ 0,255, 0,255,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,
+ 0,255,132,132,132,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,
+ 255,255, 0,255, 0, 0, 0,255, 0,255,255, 0,255,132,132,132,255,
+ 0,255,132,132,132,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,198,198,198, 0, 0, 0, 0, 0, 0,255, 0,255,255, 0,
+ 255,255, 0,255, 0, 0, 0, 0, 0, 0,132,132,132,255, 0,255,132,
+ 132,132,255,255,255,255,255,255,255,255,255,255,255,255,198,198,198,
+ 0, 0, 0, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0,132,
+ 132,132,255,255,255,255,255,255,198,198,198, 0, 0, 0,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,132,
+ 132,132,198,198,198, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255);
+
+Const
+ grdimg_function_16 : Array[0..821] of byte = (
+ 66, 77, 54, 3, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 40, 0, 0,
+ 0, 16, 0, 0, 0, 16, 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 19, 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,132, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255,
+ 0,255,255, 0,255,132, 0, 0,255, 0,255,255, 0,255,255, 0,255,
+ 132, 0, 0,132, 0, 0,255, 0,255,255, 0,255,132, 0, 0,255, 0,
+ 255,255, 0,255,132, 0, 0,255, 0,255,255, 0,255,132, 0, 0,255,
+ 0,255,255, 0,255,132, 0, 0,255, 0,255,255, 0,255,132, 0, 0,
+ 132, 0, 0,255, 0,255,255, 0,255,132, 0, 0,255, 0,255,255, 0,
+ 255,132, 0, 0,255, 0,255,255, 0,255,132, 0, 0,255, 0,255,255,
+ 0,255,132, 0, 0,255, 0,255,255, 0,255,132, 0, 0,132, 0, 0,
+ 255, 0,255,255, 0,255,132, 0, 0,255, 0,255,255, 0,255,132, 0,
+ 0,255, 0,255,255, 0,255,132, 0, 0,255, 0,255,255, 0,255,132,
+ 0, 0,255, 0,255,255, 0,255,132, 0, 0,132, 0, 0,255, 0,255,
+ 255, 0,255,132, 0, 0,255, 0,255,255, 0,255,132, 0, 0,132, 0,
+ 0,132, 0, 0,255, 0,255,255, 0,255,255, 0,255,132, 0, 0,255,
+ 0,255,255, 0,255,132, 0, 0,132, 0, 0,255, 0,255,255, 0,255,
+ 132, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,132, 0, 0,255, 0,255,132,
+ 0, 0,132, 0, 0,132, 0, 0,132, 0, 0,255, 0,255,255, 0,255,
+ 132, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,132, 0, 0,255, 0,255,255, 0,255,255, 0,255,132,
+ 0, 0,132, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,132, 0, 0,132,
+ 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,132, 0, 0,132, 0, 0,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,132, 0, 0,132, 0, 0,255, 0,255,132,
+ 0, 0,132, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,132, 0, 0,132, 0, 0,132, 0, 0,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255);
+
+Const
+ grdimg_constructor_16 : Array[0..821] of byte = (
+ 66, 77, 54, 3, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 40, 0, 0,
+ 0, 16, 0, 0, 0, 16, 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 19, 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,132,132,
+ 132,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,198,
+ 198,198,198,198,198,198,198,198, 0, 0, 0,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,132,132,132,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,198,198,198, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,132,132,132,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,198,
+ 198,198, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,132,132,132,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,198,198,198, 0,
+ 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,132,132,132,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,198,198,198, 0, 0, 0,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 132,132,132,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,198,198,198, 0, 0, 0,255, 0,255,255,
+ 0,255,255, 0,255,255,255,255,255, 0,255,255, 0,255,132,132,132,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,198,198,198, 0, 0, 0,255, 0,255,255, 0,255,255,
+ 0,255,132,132,132,255,255,255,132,132,132,132,132,132,255,255,255,
+ 255,255,255,132,132,132,255,255,255,255,255,255, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255,
+ 0,255,132,132,132, 0,255,255,132,132,132,255,255,255,132,132,132,
+ 255,255,255,255,255,255,255,255,255,198,198,198,255,255,255,132,132,
+ 132,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255,255,255,255,
+ 255,255,132,132,132,255,255,255, 0,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,198,198,198,132,132,132,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,132,132,132,132,
+ 132,132, 0,255,255,255,255,255,255,255,255,132,132,132,132,132,132,
+ 132,132,132,132,132,132,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,132,132,132, 0,255,255,132,
+ 132,132, 0,255,255,132,132,132,255,255,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,132,132,132,255,255,255,255, 0,255,132,132,132,255,
+ 255,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255,255,255,255, 0,255,255, 0,255,132,132,132,255,255,255,255,
+ 0,255,255, 0,255,132,132,132,255,255,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,132,132,132,255,255,255,255, 0,255,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255);
+
+Const
+ grdimg_gears_16 : Array[0..821] of byte = (
+ 66, 77, 54, 3, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 40, 0, 0,
+ 0, 16, 0, 0, 0, 16, 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 19, 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,255, 0,255, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255, 0, 0, 0, 0,132,132, 0, 0, 0, 0,255,255, 0,
+ 0, 0, 0,132,132, 0, 0, 0, 0, 0, 0,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255, 0,132,132, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,132,132, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255, 0,132,132, 0,132,
+ 132, 0,255,255, 0,132,132, 0, 0, 0, 0,132,132, 0,255,255, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255, 0,132,132, 0,255,255, 0,255,
+ 255, 0, 0, 0,132,132,132, 0, 0, 0, 0,255,255, 0,255,255, 0,
+ 132,132, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255, 0,132,132, 0,132,132, 0,255,255, 0,132,
+ 132, 0, 0, 0, 0,132,132, 0,255,255, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255, 0,132,132, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,132,132, 0, 0, 0,255, 0,255,255,
+ 0,255,255, 0,255,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,132,132, 0,132,132, 0, 0, 0, 0,255,255, 0, 0,
+ 0, 0,132,132, 0, 0, 0, 0, 0, 0,255, 0,255,255, 0,255, 0,
+ 0, 0, 0,132,132, 0, 0, 0, 0,255,255, 0, 0, 0, 0,132,132,
+ 0, 0, 0, 0, 0, 0, 0,132,132, 0,132,132, 0,132,132, 0,132,
+ 132,255, 0,255,255, 0,255,255, 0,255,255, 0,255, 0,132,132, 0,
+ 255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,132,132,
+ 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255,255, 0,255, 0,132,132, 0,132,132, 0,255,255, 0,
+ 132,132, 0, 0, 0, 0,132,132, 0,255,255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,255, 0,132,132, 0,255,255, 0,255,255, 0, 0, 0,132,
+ 132,132, 0, 0, 0, 0,255,255, 0,255,255, 0,132,132, 0, 0, 0,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255, 0,132,132, 0,132,132, 0,255,255, 0,132,132, 0, 0, 0, 0,
+ 132,132, 0,255,255, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255, 0,132,132, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255,255, 0,132,132, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255, 0,132,
+ 132, 0,132,132, 0, 0, 0, 0,255,255, 0, 0, 0, 0,132,132, 0,
+ 0, 0, 0, 0, 0,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,
+ 255, 0,132,132, 0,132,132, 0,132,132, 0,132,132,255, 0,255,255,
+ 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,255, 0,255,
+ 255, 0,255,255, 0,255);
diff --git a/examples/apps/ide/src/project.pas b/examples/apps/ide/src/project.pas
new file mode 100644
index 00000000..699c0957
--- /dev/null
+++ b/examples/apps/ide/src/project.pas
@@ -0,0 +1,368 @@
+unit Project;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, UnitList, fpg_base, fpg_iniutils;
+
+type
+ TBooleanGrid = array of array of Boolean;
+
+ TProject = class(TObject)
+ private
+ FMakeOptionsGrid: TBooleanGrid;
+ FProjectName: TfpgString;
+ FMainUnit: TfpgString;
+ FUnitDirs: TStringList;
+ FUnitDirsGrid: TBooleanGrid;
+ FUnitList: TUnitList;
+ FIniFile: TfpgINIFile;
+ FProjectDir: TfpgString;
+ FTargetFile: TfpgString;
+ FDefaultMake: integer;
+ FMakeOptions: TStringList;
+ FMacroNames: TStringList;
+ FUnitOutputDir: TfpgString;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ function Save(const AFile: TfpgString = ''): Boolean;
+ function Load(AProjectFile: TfpgString): Boolean;
+ function GenerateCmdLine(const AShowOnly: Boolean = False; const ABuildMode: integer = -1): TfpgString;
+ procedure ClearAndInitMakeOptions(const ASize: integer);
+ procedure ClearAndInitUnitDirsGrid(const ASize: integer);
+ property ProjectDir: TfpgString read FProjectDir write FProjectDir;
+ property ProjectName: TfpgString read FProjectName write FProjectName;
+ property MainUnit: TfpgString read FMainUnit write FMainUnit;
+ property TargetFile: TfpgString read FTargetFile write FTargetFile;
+ property UnitList: TUnitList read FUnitList;
+ property DefaultMake: integer read FDefaultMake write FDefaultMake;
+ property MakeOptions: TStringList read FMakeOptions;
+ property MakeOptionsGrid: TBooleanGrid read FMakeOptionsGrid write FMakeOptionsGrid;
+ property MacroNames: TStringList read FMacroNames;
+ property UnitDirs: TStringList read FUnitDirs;
+ property UnitOutputDir: TfpgString read FUnitOutputDir write FUnitOutputDir;
+ property UnitDirsGrid: TBooleanGrid read FUnitDirsGrid write FUnitDirsGrid;
+ end;
+
+
+// lazy-mans singleton
+function GProject: TProject;
+
+procedure FreeProject;
+
+
+implementation
+
+uses
+ ideconst
+ ,ideutils
+ ,fpg_utils
+ ;
+
+
+var
+ uProject: TProject;
+
+function GProject: TProject;
+begin
+ if not Assigned(uProject) then
+ uProject := TProject.Create;
+ Result := uProject;
+end;
+
+procedure FreeProject;
+begin
+ uProject.Free;
+ uProject := nil;
+end;
+
+
+{ TProject }
+
+constructor TProject.Create;
+begin
+ inherited Create;
+ FUnitList := TUnitList.Create;
+ FMakeOptions := TStringList.Create;
+ FMacroNames := TStringList.Create;
+ FUnitDirs := TStringList.Create;
+end;
+
+destructor TProject.Destroy;
+begin
+ FUnitDirs.Free;
+ FMacroNames.Free;
+ FMakeOptions.Free;
+ FUnitList.Free;
+ FIniFile.Free;
+ inherited Destroy;
+end;
+
+function TProject.Save(const AFile: TfpgString = ''): Boolean;
+var
+ c, j: integer;
+ s: TfpgString;
+ lDelim: TfpgString;
+
+ procedure SaveList(AList: TStringList; const CName, IName: TfpgString);
+ var
+ i: integer;
+ begin
+ FIniFile.WriteInteger(cProjectOptions, CName, AList.Count);
+ for i := 0 to AList.Count-1 do
+ FIniFile.WriteString(cProjectOptions, IName + IntToStr(i+1), AList[i]);
+ end;
+
+begin
+ Result := False;
+ if (AFile = '') and (ProjectName = '') then
+ raise Exception.Create('Project name has not been specified yet');
+
+ if not Assigned(FIniFile) then
+ begin
+ if AFile = '' then
+ FIniFile := TfpgINIFile.CreateExt(ProjectDir + ProjectName + cProjectExt)
+ else
+ FIniFile := TfpgINIFile.CreateExt(AFile);
+ end
+ else
+ begin
+ if AFile <> '' then
+ begin
+ FIniFile.Free;
+ FIniFile := TfpgINIFile.CreateExt(AFile);
+ end;
+ end;
+
+ if AFile <> '' then
+ ProjectName := fpgExtractFileName(AFile);
+
+ FIniFile.WriteString(cProjectOptions, 'ProjectName', ProjectName);
+ FIniFile.WriteString(cProjectOptions, 'MainUnit', MainUnit);
+ FIniFile.WriteString(cProjectOptions, 'TargetFile', TargetFile);
+ FIniFile.WriteInteger(cProjectOptions, 'DefaultMake', DefaultMake);
+ FIniFile.WriteString(cProjectOptions, 'UnitOutputDir', UnitOutputDir);
+
+ // Process the Make (compiler param) options
+ { first delete old items in ini file }
+ c := FIniFile.ReadInteger(cProjectOptions, 'MakeOptionsCount', 0);
+ for j := 1 to c do
+ FIniFile.DeleteKey(cProjectOptions, cINIMakeOption + IntToStr(j));
+ { no lets save new info }
+ SaveList(MakeOptions, 'MakeOptionsCount', cINIMakeOption);
+ for j := 0 to MakeOptions.Count-1 do
+ begin
+ s := '';
+ lDelim := '';
+ for c := 0 to 5 do
+ begin
+ if MakeOptionsGrid[c, j] then // True = 1, False = 0
+ s := s + lDelim + '1'
+ else
+ s := s + lDelim + '0';
+ lDelim := ',';
+ end;
+ FIniFile.WriteString(cProjectOptions, cINIMakeOptionGrid + IntToStr(j+1), s);
+ end;
+
+ // macros definitions
+ SaveList(MacroNames, 'MacroCount', 'Macro');
+
+ // unit search directories
+ { first delete old items in ini file }
+ c := FIniFile.ReadInteger(cProjectOptions, 'UnitDirsCount', 0);
+ for j := 1 to c do
+ FIniFile.DeleteKey(cProjectOptions, cINIUnitDir + IntToStr(j));
+ SaveList(UnitDirs, 'UnitDirsCount', cINIUnitDir);
+ for j := 0 to UnitDirs.Count-1 do
+ begin
+ s := '';
+ lDelim := '';
+ for c := 0 to 9 do
+ begin
+ if UnitDirsGrid[c, j] then // True = 1, False = 0
+ s := s + lDelim + '1'
+ else
+ s := s + lDelim + '0';
+ lDelim := ',';
+ end;
+ FIniFile.WriteString(cProjectOptions, cINIUnitDirGrid + IntToStr(j+1), s);
+ end;
+
+ // Unit file list
+ FIniFile.WriteInteger(cUnits, 'UnitCount', UnitList.Count);
+ for j := 0 to UnitList.Count-1 do
+ begin
+ s := UnitList[j].FileName;
+ FIniFile.WriteString(cUnits, 'Unit' + IntToStr(j+1),
+ Format('%s,%s', [ExtractRelativepath(ProjectDir, s), BoolToStr(UnitList[j].Opened, False)]));
+ end;
+
+ Result := True;
+end;
+
+function TProject.Load(AProjectFile: TfpgString): Boolean;
+var
+ a: string;
+ s: TfpgString;
+ j: integer;
+ l: integer;
+ sl: TStringList;
+ u: TUnit;
+
+ // CName = xxxCount & IName is the Item name
+ procedure LoadList(ASection: TfpgString; AList: TStringList; const CName, IName: TfpgString);
+ var
+ c: integer;
+ i: integer;
+ begin
+ c := FIniFile.ReadInteger(ASection, CName, 0);
+ for i := 0 to c-1 do
+ begin
+ s := FIniFile.ReadString(ASection, IName + IntToStr(i+1), '');
+ if s <> '' then
+ AList.Add(s);
+ end;
+ end;
+
+begin
+ Result := False;
+ if AProjectFile = '' then
+ raise Exception.Create('You need to specify a Project filename');
+
+ if not Assigned(FIniFile) then
+ FIniFile := TfpgINIFile.CreateExt(AProjectFile);
+
+ ProjectDir := fpgExtractFilePath(AProjectFile);
+ ProjectName := FIniFile.ReadString(cProjectOptions, 'ProjectName', ChangeFileExt(fpgExtractFileName(AProjectFile), ''));
+ MainUnit := FIniFile.ReadString(cProjectOptions, 'MainUnit', '');
+ TargetFile := FIniFile.ReadString(cProjectOptions, 'TargetFile', '');
+ DefaultMake := FIniFile.ReadInteger(cProjectOptions, 'DefaultMake', 0);
+ UnitOutputDir := FIniFile.ReadString(cProjectOptions, 'UnitOutputDir', 'units/'+cMacro_Target+'/');
+
+ // Load make options
+ LoadList(cProjectOptions, MakeOptions, 'MakeOptionsCount', 'MakeOption');
+ sl := TStringList.Create;
+ try
+ LoadList(cProjectOptions, sl, 'MakeOptionsCount', cINIMakeOptionGrid);
+ SetLength(FMakeOptionsGrid, 6, MakeOptions.Count); // 6 columns by X rows
+ for j := 0 to sl.Count-1 do
+ begin
+ s := sl[j];
+ for l := 0 to 5 do // we know we only have 6 columns
+ begin
+ a := tiToken(s, ',', l+1);
+ MakeOptionsGrid[l, j] := Boolean(StrToInt(a)); // 1 = True, 0 = False
+ end;
+ end;
+ finally
+ sl.Free;
+ end;
+
+ // Load Macro definitions
+ LoadList(cProjectOptions, MacroNames, 'MacroCount', 'Macro');
+
+ // Load Unit search dirs
+ LoadList(cProjectOptions, UnitDirs, 'UnitDirsCount', 'UnitDir');
+ sl := TStringList.Create;
+ try
+ LoadList(cProjectOptions, sl, 'UnitDirsCount', 'UnitDirEnabled');
+ SetLength(FUnitDirsGrid, 10, UnitDirs.Count); // 10 columns by X rows
+ for j := 0 to sl.Count-1 do
+ begin
+ s := sl[j];
+ for l := 0 to 9 do // we know we only have 10 columns
+ begin
+ a := tiToken(s, ',', l+1);
+ UnitDirsGrid[l, j] := Boolean(StrToInt(a)); // 1 = True, 0 = False
+ end;
+ end;
+ finally
+ sl.Free;
+ end;
+
+ // Load Unit file list
+ sl := TStringList.Create;
+ try
+ LoadList(cUnits, sl, 'UnitCount', 'Unit');
+ for j := 0 to sl.Count-1 do
+ begin
+ u := TUnit.Create;
+ s := tiToken(sl[j], ',', 1);
+ u.FileName := fpgExpandFileName(ProjectDir + s);
+ u.Opened := Boolean(StrToInt(tiToken(sl[j], ',', 2)));
+ UnitList.Add(u);
+ end;
+ finally
+ sl.Free;
+ end;
+
+ Result := True;
+end;
+
+function TProject.GenerateCmdLine(const AShowOnly: Boolean; const ABuildMode: integer): TfpgString;
+var
+ c: TfpgString;
+ b: integer;
+ eol: TfpgString;
+ i: integer;
+begin
+ if AShowOnly then
+ eol := LineEnding
+ else
+ eol := '';
+ if ABuildMode = -1 then
+ b := DefaultMake
+ else
+ b := ABuildMode;
+
+ // include dirs
+ for i := 0 to UnitDirs.Count-1 do
+ if UnitDirsGrid[b, i] and UnitDirsGrid[7, i] then
+ c := c + ' -Fi' + UnitDirs[i] + eol;
+ // unit dirs
+ for i := 0 to UnitDirs.Count-1 do
+ if UnitDirsGrid[b, i] and UnitDirsGrid[6, i] then
+ c := c + ' -Fu' + UnitDirs[i] + eol;
+ // unit output dir
+ if UnitOutputDir <> '' then
+ c := c + ' -FU' + UnitOutputDir + eol;
+ // make option - compiler flags
+ for i := 0 to MakeOptions.Count-1 do
+ if MakeOptionsGrid[b, i] then
+ c := c + ' ' + MakeOptions[i];
+ // target output file
+ if TargetFile <> '' then
+ c := c + ' -o' + TargetFile;
+ // unit to start compilation
+ c := c + ' ' + MainUnit;
+
+ Result := c;
+end;
+
+procedure TProject.ClearAndInitMakeOptions(const ASize: integer);
+begin
+ FMakeOptions.Clear;
+ SetLength(FMakeOptionsGrid, 0, 0); // free items
+ SetLength(FMakeOptionsGrid, 6, ASize); // 6 columns by X rows
+end;
+
+procedure TProject.ClearAndInitUnitDirsGrid(const ASize: integer);
+begin
+ FUnitDirs.Clear;
+ SetLength(FUnitDirsGrid, 0, 0); // free items
+ SetLength(FUnitDirsGrid, 10, ASize); // 10 columns by X rows
+end;
+
+
+initialization
+ uProject := nil;
+
+finalization
+ FreeProject;
+
+end.
+
diff --git a/examples/apps/ide/src/stringhelpers.pas b/examples/apps/ide/src/stringhelpers.pas
new file mode 100644
index 00000000..35fb9060
--- /dev/null
+++ b/examples/apps/ide/src/stringhelpers.pas
@@ -0,0 +1,390 @@
+unit stringhelpers;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+function psubstr(const start,stop: pchar): string;
+function cstringtostringvar(var inp: pchar): string;
+function trystrtooct(const inp: string; out value: longword): boolean;
+function strtooct(const inp: string): longword;
+function trystrtobin(const inp: string; out value: longword): boolean;
+function strtobin(const inp: string): longword;
+function trystrtodec(const inp: string; out value: longword): boolean;
+function strtodec(const inp: string): longword;
+function trystrtohex(const inp: string; out value: longword): boolean;
+function strtohex(const inp: string): longword;
+function trystrtooct64(const inp: string; out value: qword): boolean;
+
+function trystrtointvalue(const inp: string; out value: longword): boolean; overload;
+function trystrtointvalue64(const inp: string; out value: qword): boolean; overload;
+
+
+implementation
+
+
+procedure formaterror(const value: string);
+begin
+ raise exception.Create('Invalid number '''+value+'''.');
+end;
+
+function strtooct(const inp: string): longword;
+ //wandelt 1..0-string in longword)
+begin
+ if not trystrtooct(inp,result) then begin
+ formaterror(inp);
+ end;
+end;
+
+function strtooct64(const inp: string): qword;
+ //wandelt 1..0-string in longword)
+begin
+ if not trystrtooct64(inp,result) then begin
+ formaterror(inp);
+ end;
+end;
+
+function strtodec1(const inp: string; out value: longword): boolean;
+begin
+ result:= trystrtoint(inp,integer(value));
+end;
+
+function strtodec164(const inp: string; out value: qword): boolean;
+begin
+ result:= trystrtoint64(inp,int64(value));
+end;
+
+function strtohex1(const inp: string; out value: longword): boolean;
+begin
+ result:= trystrtoint('$'+inp,integer(value));
+end;
+
+function strtohex164(const inp: string; out value: qword): boolean;
+begin
+ result:= trystrtoint64('$'+inp,int64(value));
+end;
+
+
+function strtooct1(const inp: string; out value: longword): boolean;
+var
+ int1: integer;
+ ca1: longword;
+ ch1: char;
+begin
+ result:= false;
+ if inp <> '' then begin
+ value:= 0;
+ ca1:= 0;
+ for int1:= length(inp) downto 1 do begin
+ ch1:= inp[int1];
+ if (ch1 < '0') or (ch1 > '7') then begin
+ exit;
+ end;
+ value:= value + longword(((ord(ch1) - ord('0'))) shl ca1);
+ inc(ca1,3);
+ end;
+ result:= true;
+ end;
+end;
+
+function strtobin1(const inp: string; out value: longword): boolean;
+ //wandelt 1..0-string in longword)
+var
+ int1: integer;
+ lwo1: longword;
+begin
+ result:= false;
+ if inp <> '' then begin
+ value:= 0;
+ lwo1:= 1;
+ for int1:= length(inp) downto 1 do begin
+ if inp[int1] = '1' then begin
+ value:= value + lwo1;
+ end
+ else begin
+ if inp[int1] <> '0' then begin
+ exit;
+ end;
+ end;
+ lwo1:= lwo1 shl 1;
+ end;
+ result:= true;
+ end;
+end;
+
+function strtobin164(const inp: string; out value: qword): boolean;
+ //wandelt 1..0-string in longword)
+var
+ int1: integer;
+ lwo1: qword;
+begin
+ result:= false;
+ if inp <> '' then begin
+ value:= 0;
+ lwo1:= 1;
+ for int1:= length(inp) downto 1 do begin
+ if inp[int1] = '1' then begin
+ value:= value + lwo1;
+ end
+ else begin
+ if inp[int1] <> '0' then begin
+ exit;
+ end;
+ end;
+ lwo1:= lwo1 shl 1;
+ end;
+ result:= true;
+ end;
+end;
+
+
+
+function psubstr(const start,stop: pchar): string;
+var
+ int1: integer;
+begin
+ if (start = nil) or (stop = nil) then begin
+ result:= '';
+ end
+ else begin
+ int1:= stop-start;
+ setlength(result,int1);
+ move(start^,result[1],int1);
+ end;
+end;
+
+function cstringtostringvar(var inp: pchar): string;
+
+const
+ quotechar = '"';
+ escapechar = '\';
+
+var
+ po1,po2: pchar;
+ int1,int2: integer;
+ ch1: char;
+
+begin
+ result:= '';
+ if inp <> nil then begin
+ po1:= inp;
+ while true do begin
+ while (po1^ = ' ') do begin //first quote
+ inc(po1);
+ end;
+ if (po1^ <> quotechar) then begin
+ break; //end or no start quote
+ end;
+ inc(po1);
+ po2:= po1; //text
+ while true do begin
+ while (po1^ <> quotechar) and (po1^ <> escapechar) do begin
+ if (po1^ = #0) then begin
+ result:= '';
+ inp:= nil;
+ exit; //error: no end quote
+ end;
+ inc(po1);
+ end;
+ int1:= po1-po2; //text length
+ int2:= length(result)+1;
+ setlength(result,length(result) + int1);
+ move(po2^,result[int2],int1); //add text
+ if po1^ = escapechar then begin
+ inc(po1);
+ case po1^ of
+ 'a': ch1:= #$07;
+ 'b': ch1:= #$08;
+ 'f': ch1:= #$0c;
+ 'n': ch1:= #$0a;
+ 'r': ch1:= #$0d;
+ 't': ch1:= #$09;
+ 'v': ch1:= #$0b;
+ '\': ch1:= '\';
+ '''': ch1:= '''';
+ '"': ch1:= '"';
+ '?': ch1:= '?';
+ '0'..'7': begin
+ po2:= po1;
+ for int1:= 0 to 2 do begin
+ if (po1^ < '0') or (po1^ > '7') then begin
+ break;
+ end;
+ inc(po1);
+ end;
+ ch1:= char(strtooct(psubstr(po2,po1)));
+ dec(po1);
+ end;
+ 'x','X': begin
+ inc(po1);
+ po2:= po1;
+ while (po1^ >= '0') and (po1^ <= '9') or
+ (po1^ >= 'a') and (po1^ <= 'f') or
+ (po1^ >= 'A') and (po1^ <= 'F') do begin
+ inc(po1);
+ end;
+ ch1:= char(strtohex(psubstr(po2,po1)));
+ dec(po1);
+ end;
+ else begin
+ ch1:= ' ';
+ end;
+ end;
+ result:= result + ch1;
+ inc(po1);
+ end
+ else begin
+ inc(po1);
+ break;
+ end;
+ po2:= po1; //past quote
+ end;
+ end;
+ inp:= po1;
+ end;
+end;
+
+function strtooct164(const inp: string; out value: qword): boolean;
+var
+ int1: integer;
+ ca1: longword;
+ ch1: char;
+begin
+ result:= false;
+ if inp <> '' then begin
+ value:= 0;
+ ca1:= 0;
+ for int1:= length(inp) downto 1 do begin
+ ch1:= inp[int1];
+ if (ch1 < '0') or (ch1 > '7') then begin
+ exit;
+ end;
+ value:= value + qword(((ord(ch1) - ord('0'))) shl ca1);
+ inc(ca1,3);
+ end;
+ result:= true;
+ end;
+end;
+
+function trystrtooct(const inp: string; out value: longword): boolean;
+begin
+ result:= strtooct1(inp,value);
+ if not result then begin
+ result:= trystrtointvalue(inp,value);
+ end;
+end;
+
+function trystrtobin(const inp: string; out value: longword): boolean;
+begin
+ result:= strtobin1(inp,value);
+ if not result then begin
+ result:= trystrtointvalue(inp,value);
+ end;
+end;
+
+function strtobin(const inp: string): longword;
+ //wandelt 0..1-string in longword)
+begin
+ if not trystrtobin(inp,result) then begin
+ formaterror(inp);
+ end;
+end;
+
+function trystrtodec(const inp: string; out value: longword): boolean;
+begin
+ result:= strtodec1(inp,value);
+ if not result then begin
+ result:= trystrtointvalue(inp,value);
+ end;
+end;
+
+function strtodec(const inp: string): longword;
+ //wandelt 0..9-string in longword)
+begin
+ if not trystrtodec(inp,result) then begin
+ formaterror(inp);
+ end;
+end;
+
+function trystrtohex(const inp: string; out value: longword): boolean;
+begin
+ result:= strtohex1(inp,value);
+ if not result then begin
+ result:= trystrtointvalue(inp,value);
+ end;
+end;
+
+function strtohex(const inp: string): longword;
+begin
+ if not trystrtohex(inp,result) then begin
+ formaterror(inp);
+ end;
+end;
+
+function trystrtooct64(const inp: string; out value: qword): boolean;
+begin
+ result:= strtooct164(inp,value);
+ if not result then begin
+ result:= trystrtointvalue64(inp,value);
+ end;
+end;
+
+function trystrtointvalue(const inp: string; out value: longword): boolean;
+var
+ lint1: int64;
+begin
+ result:= false;
+ if length(inp) > 0 then begin
+ case inp[1] of
+ '%': result:= strtobin1(copy(inp,2,length(inp)-1),value);
+ '&': result:= strtooct1(copy(inp,2,length(inp)-1),value);
+ '#': result:= strtodec1(copy(inp,2,length(inp)-1),value);
+ '$': result:= strtohex1(copy(inp,2,length(inp)-1),value);
+ else begin
+ if (length(inp) > 2) and
+ ((inp[2] = 'x') or (inp[2] = 'X')) and (inp[1] = '0') then begin
+ result:= strtohex1(copy(inp,3,length(inp)-2),value);
+ end
+ else begin
+ result:= trystrtoint64(inp,lint1);
+ if result then begin
+ value:= lint1;
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+function trystrtointvalue64(const inp: string; out value: qword): boolean; overload;
+var
+ lint1: int64;
+begin
+ result:= false;
+ if length(inp) > 0 then begin
+ case inp[1] of
+ '%': result:= strtobin164(copy(inp,2,length(inp)-1),value);
+ '&': result:= strtooct164(copy(inp,2,length(inp)-1),value);
+ '#': result:= strtodec164(copy(inp,2,length(inp)-1),value);
+ '$': result:= strtohex164(copy(inp,2,length(inp)-1),value);
+ else begin
+ if (length(inp) > 2) and
+ ((inp[2] = 'x') or (inp[2] = 'X')) and (inp[1] = '0') then begin
+ result:= strtohex164(copy(inp,3,length(inp)-2),value);
+ end
+ else begin
+ result:= trystrtoint64(inp,lint1);
+ if result then begin
+ value:= lint1;
+ end;
+ end;
+ end;
+ end;
+ end;
+end;
+
+end.
+
diff --git a/examples/apps/ide/src/synregexpr.pas b/examples/apps/ide/src/synregexpr.pas
new file mode 100644
index 00000000..b88d9c2d
--- /dev/null
+++ b/examples/apps/ide/src/synregexpr.pas
@@ -0,0 +1,4141 @@
+{$IFNDEF QSYNREGEXPR}
+unit SynRegExpr;
+{$ENDIF}
+
+{
+ TRegExpr class library
+ Delphi Regular Expressions
+
+ Copyright (c) 1999-2004 Andrey V. Sorokin, St.Petersburg, Russia
+
+ You may use this software in any kind of development,
+ including comercial, redistribute, and modify it freely,
+ under the following restrictions :
+ 1. This software is provided as it is, without any kind of
+ warranty given. Use it at Your own risk.The author is not
+ responsible for any consequences of use of this software.
+ 2. The origin of this software may not be mispresented, You
+ must not claim that You wrote the original software. If
+ You use this software in any kind of product, it would be
+ appreciated that there in a information box, or in the
+ documentation would be an acknowledgement like
+
+ Partial Copyright (c) 2004 Andrey V. Sorokin
+ http://RegExpStudio.com
+ mailto:anso@mail.ru
+
+ 3. You may not have any income from distributing this source
+ (or altered version of it) to other developers. When You
+ use this product in a comercial package, the source may
+ not be charged seperatly.
+ 4. Altered versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+ 5. RegExp Studio application and all the visual components as
+ well as documentation is not part of the TRegExpr library
+ and is not free for usage.
+
+ mailto:anso@mail.ru
+ http://RegExpStudio.com
+ http://anso.da.ru/
+}
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+ {$DEFINE SYN_COMPILER_1_UP}
+ {$DEFINE SYN_COMPILER_2_UP}
+ {$DEFINE SYN_COMPILER_3_UP}
+ {$DEFINE SYN_COMPILER_4_UP}
+ {$DEFINE SYN_DELPHI_2_UP}
+ {$DEFINE SYN_DELPHI_3_UP}
+ {$DEFINE SYN_DELPHI_4_UP}
+ {$DEFINE SYN_DELPHI_5_UP}
+ {$DEFINE SYN_LAZARUS}
+{$ENDIF}
+
+// ======== Determine compiler
+{$IFDEF VER80} Sorry, TRegExpr is for 32-bits Delphi only. Delphi 1 is not supported (and whos really care today?!). {$ENDIF}
+{$IFDEF VER90} {$DEFINE D2} {$ENDIF} // D2
+{$IFDEF VER93} {$DEFINE D2} {$ENDIF} // CPPB 1
+{$IFDEF VER100} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D3
+{$IFDEF VER110} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // CPPB 3
+{$IFDEF VER120} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D4
+{$IFDEF VER130} {$DEFINE D5} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D5
+{$IFDEF VER140} {$DEFINE D6} {$DEFINE D5} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D6
+{$IFDEF VER150} {$DEFINE D7} {$DEFINE D6} {$DEFINE D5} {$DEFINE D4} {$DEFINE D3} {$DEFINE D2} {$ENDIF} // D7
+
+// ======== Define base compiler options
+{$BOOLEVAL OFF}
+{$EXTENDEDSYNTAX ON}
+{$LONGSTRINGS ON}
+{$IFNDEF SYN_LAZARUS}
+{$OPTIMIZATION ON}
+{$ENDIF}
+{$IFDEF D6}
+ {$WARN SYMBOL_PLATFORM OFF} // Suppress .Net warnings
+{$ENDIF}
+{$IFDEF D7}
+ {$WARN UNSAFE_CAST OFF} // Suppress .Net warnings
+ {$WARN UNSAFE_TYPE OFF} // Suppress .Net warnings
+ {$WARN UNSAFE_CODE OFF} // Suppress .Net warnings
+{$ENDIF}
+{$IFDEF FPC}
+ {$IFNDEF SYN_LAZARUS}
+ {$MODE DELPHI} // Delphi-compatible mode in FreePascal
+ {$ENDIF}
+{$ENDIF}
+
+// ======== Define options for TRegExpr engine
+{.$DEFINE UniCode} // Unicode support
+{$DEFINE RegExpPCodeDump} // p-code dumping (see Dump method)
+{$IFNDEF FPC} // the option is not supported in FreePascal
+ {$DEFINE reRealExceptionAddr} // exceptions will point to appropriate source line, not to Error procedure
+{$ENDIF}
+{$DEFINE ComplexBraces} // support braces in complex cases
+{$IFNDEF UniCode} // the option applicable only for non-UniCode mode
+ {$DEFINE UseSetOfChar} // Significant optimization by using set of char
+{$ENDIF}
+{$IFDEF UseSetOfChar}
+ {$DEFINE UseFirstCharSet} // Fast skip between matches for r.e. that starts with determined set of chars
+{$ENDIF}
+
+// ======== Define Pascal-language options
+// Define 'UseAsserts' option (do not edit this definitions).
+// Asserts used to catch 'strange bugs' in TRegExpr implementation (when something goes
+// completely wrong). You can swith asserts on/off with help of {$C+}/{$C-} compiler options.
+{$IFDEF D3} {$DEFINE UseAsserts} {$ENDIF}
+{$IFDEF FPC} {$DEFINE UseAsserts} {$ENDIF}
+
+// Define 'use subroutine parameters default values' option (do not edit this definition).
+{$IFDEF D4} {$DEFINE DefParam} {$ENDIF}
+
+// Define 'OverMeth' options, to use method overloading (do not edit this definitions).
+{$IFDEF D5} {$DEFINE OverMeth} {$ENDIF}
+{$IFDEF FPC} {$DEFINE OverMeth} {$ENDIF}
+
+uses
+ Classes, // TStrings in Split method
+ SysUtils; // Exception
+
+type
+ {$IFDEF UniCode}
+ PRegExprChar = PWideChar;
+ RegExprString = WideString;
+ REChar = WideChar;
+ {$ELSE}
+ PRegExprChar = PChar;
+ RegExprString = AnsiString; //###0.952 was string
+ REChar = Char;
+ {$ENDIF}
+ TREOp = REChar; // internal p-code type //###0.933
+ PREOp = ^TREOp;
+ TRENextOff = integer; // internal Next "pointer" (offset to current p-code) //###0.933
+ PRENextOff = ^TRENextOff; // used for extracting Next "pointers" from compiled r.e. //###0.933
+ TREBracesArg = integer; // type of {m,n} arguments
+ PREBracesArg = ^TREBracesArg;
+
+const
+ REOpSz = SizeOf (TREOp) div SizeOf (REChar); // size of p-code in RegExprString units
+ RENextOffSz = SizeOf (TRENextOff) div SizeOf (REChar); // size of Next 'pointer' -"-
+ REBracesArgSz = SizeOf (TREBracesArg) div SizeOf (REChar); // size of BRACES arguments -"-
+
+type
+ TRegExprInvertCaseFunction = function (const Ch : REChar) : REChar
+ of object;
+
+const
+ EscChar = '\'; // 'Escape'-char ('\' in common r.e.) used for escaping metachars (\w, \d etc).
+ RegExprModifierI : boolean = False; // default value for ModifierI
+ RegExprModifierR : boolean = True; // default value for ModifierR
+ RegExprModifierS : boolean = True; // default value for ModifierS
+ RegExprModifierG : boolean = True; // default value for ModifierG
+ RegExprModifierM : boolean = False; // default value for ModifierM
+ RegExprModifierX : boolean = False; // default value for ModifierX
+ RegExprSpaceChars : RegExprString = // default value for SpaceChars
+ ' '#$9#$A#$D#$C;
+ RegExprWordChars : RegExprString = // default value for WordChars
+ '0123456789' //###0.940
+ + 'abcdefghijklmnopqrstuvwxyz'
+ + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_';
+ RegExprLineSeparators : RegExprString =// default value for LineSeparators
+ #$d#$a{$IFDEF UniCode}+#$b#$c#$2028#$2029#$85{$ENDIF}; //###0.947
+ RegExprLinePairedSeparator : RegExprString =// default value for LinePairedSeparator
+ #$d#$a;
+ { if You need Unix-styled line separators (only \n), then use:
+ RegExprLineSeparators = #$a;
+ RegExprLinePairedSeparator = '';
+ }
+
+
+const
+ NSUBEXP = 15; // max number of subexpression //###0.929
+ // Cannot be more than NSUBEXPMAX
+ // Be carefull - don't use values which overflow CLOSE opcode
+ // (in this case you'll get compiler erorr).
+ // Big NSUBEXP will cause more slow work and more stack required
+ NSUBEXPMAX = 255; // Max possible value for NSUBEXP. //###0.945
+ // Don't change it! It's defined by internal TRegExpr design.
+
+ MaxBracesArg = $7FFFFFFF - 1; // max value for {n,m} arguments //###0.933
+
+ {$IFDEF ComplexBraces}
+ LoopStackMax = 10; // max depth of loops stack //###0.925
+ {$ENDIF}
+
+ TinySetLen = 3;
+ // if range includes more then TinySetLen chars, //###0.934
+ // then use full (32 bytes) ANYOFFULL instead of ANYOF[BUT]TINYSET
+ // !!! Attension ! If you change TinySetLen, you must
+ // change code marked as "//!!!TinySet"
+
+
+type
+
+{$IFDEF UseSetOfChar}
+ PSetOfREChar = ^TSetOfREChar;
+ TSetOfREChar = set of REChar;
+{$ENDIF}
+
+ TRegExpr = class;
+
+ TRegExprReplaceFunction = function (ARegExpr : TRegExpr): string
+ of object;
+
+ TRegExpr = class
+ private
+ startp : array [0 .. NSUBEXP - 1] of PRegExprChar; // founded expr starting points
+ endp : array [0 .. NSUBEXP - 1] of PRegExprChar; // founded expr end points
+
+ {$IFDEF ComplexBraces}
+ LoopStack : array [1 .. LoopStackMax] of integer; // state before entering loop
+ LoopStackIdx : integer; // 0 - out of all loops
+ {$ENDIF}
+
+ // The "internal use only" fields to pass info from compile
+ // to execute that permits the execute phase to run lots faster on
+ // simple cases.
+ regstart : REChar; // char that must begin a match; '\0' if none obvious
+ reganch : REChar; // is the match anchored (at beginning-of-line only)?
+ regmust : PRegExprChar; // string (pointer into program) that match must include, or nil
+ regmlen : integer; // length of regmust string
+ // Regstart and reganch permit very fast decisions on suitable starting points
+ // for a match, cutting down the work a lot. Regmust permits fast rejection
+ // of lines that cannot possibly match. The regmust tests are costly enough
+ // that regcomp() supplies a regmust only if the r.e. contains something
+ // potentially expensive (at present, the only such thing detected is * or +
+ // at the start of the r.e., which can involve a lot of backup). Regmlen is
+ // supplied because the test in regexec() needs it and regcomp() is computing
+ // it anyway.
+ {$IFDEF UseFirstCharSet} //###0.929
+ FirstCharSet : TSetOfREChar;
+ {$ENDIF}
+
+ // work variables for Exec's routins - save stack in recursion}
+ reginput : PRegExprChar; // String-input pointer.
+ fInputStart : PRegExprChar; // Pointer to first char of input string.
+ fInputEnd : PRegExprChar; // Pointer to char AFTER last char of input string
+
+ // work variables for compiler's routines
+ regparse : PRegExprChar; // Input-scan pointer.
+ regnpar : integer; // count.
+ regdummy : char;
+ regcode : PRegExprChar; // Code-emit pointer; @regdummy = don't.
+ regsize : integer; // Code size.
+
+ regexpbeg : PRegExprChar; // only for error handling. Contains
+ // pointer to beginning of r.e. while compiling
+ fExprIsCompiled : boolean; // true if r.e. successfully compiled
+
+ // programm is essentially a linear encoding
+ // of a nondeterministic finite-state machine (aka syntax charts or
+ // "railroad normal form" in parsing technology). Each node is an opcode
+ // plus a "next" pointer, possibly plus an operand. "Next" pointers of
+ // all nodes except BRANCH implement concatenation; a "next" pointer with
+ // a BRANCH on both ends of it is connecting two alternatives. (Here we
+ // have one of the subtle syntax dependencies: an individual BRANCH (as
+ // opposed to a collection of them) is never concatenated with anything
+ // because of operator precedence.) The operand of some types of node is
+ // a literal string; for others, it is a node leading into a sub-FSM. In
+ // particular, the operand of a BRANCH node is the first node of the branch.
+ // (NB this is *not* a tree structure: the tail of the branch connects
+ // to the thing following the set of BRANCHes.) The opcodes are:
+ programm : PRegExprChar; // Unwarranted chumminess with compiler.
+
+ fExpression : PRegExprChar; // source of compiled r.e.
+ fInputString : PRegExprChar; // input string
+
+ fLastError : integer; // see Error, LastError
+
+ fModifiers : integer; // modifiers
+ fCompModifiers : integer; // compiler's copy of modifiers
+ fProgModifiers : integer; // modifiers values from last programm compilation
+
+ fSpaceChars : RegExprString; //###0.927
+ fWordChars : RegExprString; //###0.929
+ fInvertCase : TRegExprInvertCaseFunction; //###0.927
+
+ fLineSeparators : RegExprString; //###0.941
+ fLinePairedSeparatorAssigned : boolean;
+ fLinePairedSeparatorHead,
+ fLinePairedSeparatorTail : REChar;
+ {$IFNDEF UniCode}
+ fLineSeparatorsSet : set of REChar;
+ {$ENDIF}
+
+ procedure InvalidateProgramm;
+ // Mark programm as have to be [re]compiled
+
+ function IsProgrammOk : boolean; //###0.941
+ // Check if we can use precompiled r.e. or
+ // [re]compile it if something changed
+
+ function GetExpression : RegExprString;
+ procedure SetExpression (const s : RegExprString);
+
+ function GetModifierStr : RegExprString;
+ class function ParseModifiersStr (const AModifiers : RegExprString;
+ var AModifiersInt : integer) : boolean; //###0.941 class function now
+ // Parse AModifiers string and return true and set AModifiersInt
+ // if it's in format 'ismxrg-ismxrg'.
+ procedure SetModifierStr (const AModifiers : RegExprString);
+
+ function GetModifier (AIndex : integer) : boolean;
+ procedure SetModifier (AIndex : integer; ASet : boolean);
+
+ procedure Error (AErrorID : integer); virtual; // error handler.
+ // Default handler raise exception ERegExpr with
+ // Message = ErrorMsg (AErrorID), ErrorCode = AErrorID
+ // and CompilerErrorPos = value of property CompilerErrorPos.
+
+
+ {==================== Compiler section ===================}
+ function CompileRegExpr (exp : PRegExprChar) : boolean;
+ // compile a regular expression into internal code
+
+ procedure Tail (p : PRegExprChar; val : PRegExprChar);
+ // set the next-pointer at the end of a node chain
+
+ procedure OpTail (p : PRegExprChar; val : PRegExprChar);
+ // regoptail - regtail on operand of first argument; nop if operandless
+
+ function EmitNode (op : TREOp) : PRegExprChar;
+ // regnode - emit a node, return location
+
+ procedure EmitC (b : REChar);
+ // emit (if appropriate) a byte of code
+
+ procedure InsertOperator (op : TREOp; opnd : PRegExprChar; sz : integer); //###0.90
+ // insert an operator in front of already-emitted operand
+ // Means relocating the operand.
+
+ function ParseReg (paren : integer; var flagp : integer) : PRegExprChar;
+ // regular expression, i.e. main body or parenthesized thing
+
+ function ParseBranch (var flagp : integer) : PRegExprChar;
+ // one alternative of an | operator
+
+ function ParsePiece (var flagp : integer) : PRegExprChar;
+ // something followed by possible [*+?]
+
+ function ParseAtom (var flagp : integer) : PRegExprChar;
+ // the lowest level
+
+ function GetCompilerErrorPos : integer;
+ // current pos in r.e. - for error hanling
+
+ {$IFDEF UseFirstCharSet} //###0.929
+ procedure FillFirstCharSet (prog : PRegExprChar);
+ {$ENDIF}
+
+ {===================== Mathing section ===================}
+ function regrepeat (p : PRegExprChar; AMax : integer) : integer;
+ // repeatedly match something simple, report how many
+
+ function regnext (p : PRegExprChar) : PRegExprChar;
+ // dig the "next" pointer out of a node
+
+ function MatchPrim (prog : PRegExprChar) : boolean;
+ // recursively matching routine
+
+ function ExecPrim (AOffset: integer) : boolean;
+ // Exec for stored InputString
+
+ {$IFDEF RegExpPCodeDump}
+ function DumpOp (op : REChar) : RegExprString;
+ {$ENDIF}
+
+ function GetSubExprMatchCount : integer;
+ function GetMatchPos (Idx : integer) : integer;
+ function GetMatchLen (Idx : integer) : integer;
+ function GetMatch (Idx : integer) : RegExprString;
+
+ function GetInputString : RegExprString;
+ procedure SetInputString (const AInputString : RegExprString);
+
+ {$IFNDEF UseSetOfChar}
+ function StrScanCI (s : PRegExprChar; ch : REChar) : PRegExprChar; //###0.928
+ {$ENDIF}
+
+ procedure SetLineSeparators (const AStr : RegExprString);
+ procedure SetLinePairedSeparator (const AStr : RegExprString);
+ function GetLinePairedSeparator : RegExprString;
+
+ public
+ constructor Create;
+ destructor Destroy; override;
+
+ class function VersionMajor : integer; //###0.944
+ class function VersionMinor : integer; //###0.944
+
+ property Expression : RegExprString read GetExpression write SetExpression;
+ // Regular expression.
+ // For optimization, TRegExpr will automatically compiles it into 'P-code'
+ // (You can see it with help of Dump method) and stores in internal
+ // structures. Real [re]compilation occures only when it really needed -
+ // while calling Exec[Next], Substitute, Dump, etc
+ // and only if Expression or other P-code affected properties was changed
+ // after last [re]compilation.
+ // If any errors while [re]compilation occures, Error method is called
+ // (by default Error raises exception - see below)
+
+ property ModifierStr : RegExprString read GetModifierStr write SetModifierStr;
+ // Set/get default values of r.e.syntax modifiers. Modifiers in
+ // r.e. (?ismx-ismx) will replace this default values.
+ // If you try to set unsupported modifier, Error will be called
+ // (by defaul Error raises exception ERegExpr).
+
+ property ModifierI : boolean index 1 read GetModifier write SetModifier;
+ // Modifier /i - caseinsensitive, initialized from RegExprModifierI
+
+ property ModifierR : boolean index 2 read GetModifier write SetModifier;
+ // Modifier /r - use r.e.syntax extended for russian,
+ // (was property ExtSyntaxEnabled in previous versions)
+ // If true, then а-я additional include russian letter 'ё',
+ // А-Я additional include 'Ё', and а-Я include all russian symbols.
+ // You have to turn it off if it may interfere with you national alphabet.
+ // , initialized from RegExprModifierR
+
+ property ModifierS : boolean index 3 read GetModifier write SetModifier;
+ // Modifier /s - '.' works as any char (else as [^\n]),
+ // , initialized from RegExprModifierS
+
+ property ModifierG : boolean index 4 read GetModifier write SetModifier;
+ // Switching off modifier /g switchs all operators in
+ // non-greedy style, so if ModifierG = False, then
+ // all '*' works as '*?', all '+' as '+?' and so on.
+ // , initialized from RegExprModifierG
+
+ property ModifierM : boolean index 5 read GetModifier write SetModifier;
+ // Treat string as multiple lines. That is, change `^' and `$' from
+ // matching at only the very start or end of the string to the start
+ // or end of any line anywhere within the string.
+ // , initialized from RegExprModifierM
+
+ property ModifierX : boolean index 6 read GetModifier write SetModifier;
+ // Modifier /x - eXtended syntax, allow r.e. text formatting,
+ // see description in the help. Initialized from RegExprModifierX
+
+ function Exec (const AInputString : RegExprString) : boolean; {$IFDEF OverMeth} overload;
+ {$IFNDEF FPC} // I do not know why FreePascal cannot overload methods with empty param list
+ function Exec : boolean; overload; //###0.949
+ {$ENDIF}
+ function Exec (AOffset: integer) : boolean; overload; //###0.949
+ {$ENDIF}
+ // match a programm against a string AInputString
+ // !!! Exec store AInputString into InputString property
+ // For Delphi 5 and higher available overloaded versions - first without
+ // parameter (uses already assigned to InputString property value)
+ // and second that has integer parameter and is same as ExecPos
+
+ function ExecNext : boolean;
+ // find next match:
+ // ExecNext;
+ // works same as
+ // if MatchLen [0] = 0 then ExecPos (MatchPos [0] + 1)
+ // else ExecPos (MatchPos [0] + MatchLen [0]);
+ // but it's more simpler !
+ // Raises exception if used without preceeding SUCCESSFUL call to
+ // Exec* (Exec, ExecPos, ExecNext). So You always must use something like
+ // if Exec (InputString) then repeat { proceed results} until not ExecNext;
+
+ function ExecPos (AOffset: integer {$IFDEF DefParam}= 1{$ENDIF}) : boolean;
+ // find match for InputString starting from AOffset position
+ // (AOffset=1 - first char of InputString)
+
+ property InputString : RegExprString read GetInputString write SetInputString;
+ // returns current input string (from last Exec call or last assign
+ // to this property).
+ // Any assignment to this property clear Match* properties !
+
+ function Substitute (const ATemplate : RegExprString) : RegExprString;
+ // Returns ATemplate with '$&' or '$0' replaced by whole r.e.
+ // occurence and '$n' replaced by occurence of subexpression #n.
+ // Since v.0.929 '$' used instead of '\' (for future extensions
+ // and for more Perl-compatibility) and accept more then one digit.
+ // If you want place into template raw '$' or '\', use prefix '\'
+ // Example: '1\$ is $2\\rub\\' -> '1$ is <Match[2]>\rub\'
+ // If you want to place raw digit after '$n' you must delimit
+ // n with curly braces '{}'.
+ // Example: 'a$12bc' -> 'a<Match[12]>bc'
+ // 'a${1}2bc' -> 'a<Match[1]>2bc'.
+
+ procedure Split (AInputStr : RegExprString; APieces : TStrings);
+ // Split AInputStr into APieces by r.e. occurencies
+ // Internally calls Exec[Next]
+
+ function Replace (AInputStr : RegExprString;
+ const AReplaceStr : RegExprString;
+ AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) //###0.946
+ : RegExprString; {$IFDEF OverMeth} overload;
+ function Replace (AInputStr : RegExprString;
+ AReplaceFunc : TRegExprReplaceFunction)
+ : RegExprString; overload;
+ {$ENDIF}
+ function ReplaceEx (AInputStr : RegExprString;
+ AReplaceFunc : TRegExprReplaceFunction)
+ : RegExprString;
+ // Returns AInputStr with r.e. occurencies replaced by AReplaceStr
+ // If AUseSubstitution is true, then AReplaceStr will be used
+ // as template for Substitution methods.
+ // For example:
+ // Expression := '({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*';
+ // Replace ('BLOCK( test1)', 'def "$1" value "$2"', True);
+ // will return: def 'BLOCK' value 'test1'
+ // Replace ('BLOCK( test1)', 'def "$1" value "$2"')
+ // will return: def "$1" value "$2"
+ // Internally calls Exec[Next]
+ // Overloaded version and ReplaceEx operate with call-back function,
+ // so You can implement really complex functionality.
+
+ property SubExprMatchCount : integer read GetSubExprMatchCount;
+ // Number of subexpressions has been found in last Exec* call.
+ // If there are no subexpr. but whole expr was found (Exec* returned True),
+ // then SubExprMatchCount=0, if no subexpressions nor whole
+ // r.e. found (Exec* returned false) then SubExprMatchCount=-1.
+ // Note, that some subexpr. may be not found and for such
+ // subexpr. MathPos=MatchLen=-1 and Match=''.
+ // For example: Expression := '(1)?2(3)?';
+ // Exec ('123'): SubExprMatchCount=2, Match[0]='123', [1]='1', [2]='3'
+ // Exec ('12'): SubExprMatchCount=1, Match[0]='12', [1]='1'
+ // Exec ('23'): SubExprMatchCount=2, Match[0]='23', [1]='', [2]='3'
+ // Exec ('2'): SubExprMatchCount=0, Match[0]='2'
+ // Exec ('7') - return False: SubExprMatchCount=-1
+
+ property MatchPos [Idx : integer] : integer read GetMatchPos;
+ // pos of entrance subexpr. #Idx into tested in last Exec*
+ // string. First subexpr. have Idx=1, last - MatchCount,
+ // whole r.e. have Idx=0.
+ // Returns -1 if in r.e. no such subexpr. or this subexpr.
+ // not found in input string.
+
+ property MatchLen [Idx : integer] : integer read GetMatchLen;
+ // len of entrance subexpr. #Idx r.e. into tested in last Exec*
+ // string. First subexpr. have Idx=1, last - MatchCount,
+ // whole r.e. have Idx=0.
+ // Returns -1 if in r.e. no such subexpr. or this subexpr.
+ // not found in input string.
+ // Remember - MatchLen may be 0 (if r.e. match empty string) !
+
+ property Match [Idx : integer] : RegExprString read GetMatch;
+ // == copy (InputString, MatchPos [Idx], MatchLen [Idx])
+ // Returns '' if in r.e. no such subexpr. or this subexpr.
+ // not found in input string.
+
+ function LastError : integer;
+ // Returns ID of last error, 0 if no errors (unusable if
+ // Error method raises exception) and clear internal status
+ // into 0 (no errors).
+
+ function ErrorMsg (AErrorID : integer) : RegExprString; virtual;
+ // Returns Error message for error with ID = AErrorID.
+
+ property CompilerErrorPos : integer read GetCompilerErrorPos;
+ // Returns pos in r.e. there compiler stopped.
+ // Useful for error diagnostics
+
+ property SpaceChars : RegExprString read fSpaceChars write fSpaceChars; //###0.927
+ // Contains chars, treated as /s (initially filled with RegExprSpaceChars
+ // global constant)
+
+ property WordChars : RegExprString read fWordChars write fWordChars; //###0.929
+ // Contains chars, treated as /w (initially filled with RegExprWordChars
+ // global constant)
+
+ property LineSeparators : RegExprString read fLineSeparators write SetLineSeparators; //###0.941
+ // line separators (like \n in Unix)
+
+ property LinePairedSeparator : RegExprString read GetLinePairedSeparator write SetLinePairedSeparator; //###0.941
+ // paired line separator (like \r\n in DOS and Windows).
+ // must contain exactly two chars or no chars at all
+
+ class function InvertCaseFunction (const Ch : REChar) : REChar;
+ // Converts Ch into upper case if it in lower case or in lower
+ // if it in upper (uses current system local setings)
+
+ property InvertCase : TRegExprInvertCaseFunction read fInvertCase write fInvertCase; //##0.935
+ // Set this property if you want to override case-insensitive functionality.
+ // Create set it to RegExprInvertCaseFunction (InvertCaseFunction by default)
+
+ procedure Compile; //###0.941
+ // [Re]compile r.e. Useful for example for GUI r.e. editors (to check
+ // all properties validity).
+
+ {$IFDEF RegExpPCodeDump}
+ function Dump : RegExprString;
+ // dump a compiled regexp in vaguely comprehensible form
+ {$ENDIF}
+ end;
+
+ ERegExpr = class (Exception)
+ public
+ ErrorCode : integer;
+ CompilerErrorPos : integer;
+ end;
+
+const
+ RegExprInvertCaseFunction : TRegExprInvertCaseFunction = {$IFDEF FPC} nil {$ELSE} TRegExpr.InvertCaseFunction{$ENDIF};
+ // defaul for InvertCase property
+
+function ExecRegExpr (const ARegExpr, AInputStr : RegExprString) : boolean;
+// true if string AInputString match regular expression ARegExpr
+// ! will raise exeption if syntax errors in ARegExpr
+
+procedure SplitRegExpr (const ARegExpr, AInputStr : RegExprString; APieces : TStrings);
+// Split AInputStr into APieces by r.e. ARegExpr occurencies
+
+function ReplaceRegExpr (const ARegExpr, AInputStr, AReplaceStr : RegExprString;
+ AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) : RegExprString; //###0.947
+// Returns AInputStr with r.e. occurencies replaced by AReplaceStr
+// If AUseSubstitution is true, then AReplaceStr will be used
+// as template for Substitution methods.
+// For example:
+// ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
+// 'BLOCK( test1)', 'def "$1" value "$2"', True)
+// will return: def 'BLOCK' value 'test1'
+// ReplaceRegExpr ('({-i}block|var)\s*\(\s*([^ ]*)\s*\)\s*',
+// 'BLOCK( test1)', 'def "$1" value "$2"')
+// will return: def "$1" value "$2"
+
+function QuoteRegExprMetaChars (const AStr : RegExprString) : RegExprString;
+// Replace all metachars with its safe representation,
+// for example 'abc$cd.(' converts into 'abc\$cd\.\('
+// This function useful for r.e. autogeneration from
+// user input
+
+function RegExprSubExpressions (const ARegExpr : string;
+ ASubExprs : TStrings; AExtendedSyntax : boolean{$IFDEF DefParam}= False{$ENDIF}) : integer;
+// Makes list of subexpressions found in ARegExpr r.e.
+// In ASubExps every item represent subexpression,
+// from first to last, in format:
+// String - subexpression text (without '()')
+// low word of Object - starting position in ARegExpr, including '('
+// if exists! (first position is 1)
+// high word of Object - length, including starting '(' and ending ')'
+// if exist!
+// AExtendedSyntax - must be True if modifier /m will be On while
+// using the r.e.
+// Useful for GUI editors of r.e. etc (You can find example of using
+// in TestRExp.dpr project)
+// Returns
+// 0 Success. No unbalanced brackets was found;
+// -1 There are not enough closing brackets ')';
+// -(n+1) At position n was found opening '[' without //###0.942
+// corresponding closing ']';
+// n At position n was found closing bracket ')' without
+// corresponding opening '('.
+// If Result <> 0, then ASubExpr can contain empty items or illegal ones
+
+
+implementation
+
+{$IFDEF SYN_LAZARUS}
+{$ELSE}
+uses
+{$IFDEF SYN_WIN32}
+ Windows; // CharUpper/Lower
+{$ELSE}
+ Libc; //Qt.pas from Borland does not expose char handling functions
+{$ENDIF}
+{$ENDIF}
+
+const
+ TRegExprVersionMajor : integer = 0;
+ TRegExprVersionMinor : integer = 952;
+ // TRegExpr.VersionMajor/Minor return values of this constants
+
+ MaskModI = 1; // modifier /i bit in fModifiers
+ MaskModR = 2; // -"- /r
+ MaskModS = 4; // -"- /s
+ MaskModG = 8; // -"- /g
+ MaskModM = 16; // -"- /m
+ MaskModX = 32; // -"- /x
+
+ {$IFDEF UniCode}
+ XIgnoredChars = ' '#9#$d#$a;
+ {$ELSE}
+ XIgnoredChars = [' ', #9, #$d, #$a];
+ {$ENDIF}
+
+{=============================================================}
+{=================== WideString functions ====================}
+{=============================================================}
+
+{$IFDEF UniCode}
+
+function StrPCopy (Dest: PRegExprChar; const Source: RegExprString): PRegExprChar;
+ var
+ i, Len : Integer;
+ begin
+ Len := length (Source); //###0.932
+ for i := 1 to Len do
+ Dest [i - 1] := Source [i];
+ Dest [Len] := #0;
+ Result := Dest;
+ end; { of function StrPCopy
+--------------------------------------------------------------}
+
+function StrLCopy (Dest, Source: PRegExprChar; MaxLen: Cardinal): PRegExprChar;
+ var i: Integer;
+ begin
+ for i := 0 to MaxLen - 1 do
+ Dest [i] := Source [i];
+ Result := Dest;
+ end; { of function StrLCopy
+--------------------------------------------------------------}
+
+function StrLen (Str: PRegExprChar): Cardinal;
+ begin
+ Result:=0;
+ while Str [result] <> #0
+ do Inc (Result);
+ end; { of function StrLen
+--------------------------------------------------------------}
+
+function StrPos (Str1, Str2: PRegExprChar): PRegExprChar;
+ var n: Integer;
+ begin
+ Result := nil;
+ n := Pos (RegExprString (Str2), RegExprString (Str1));
+ if n = 0
+ then EXIT;
+ Result := Str1 + n - 1;
+ end; { of function StrPos
+--------------------------------------------------------------}
+
+function StrLComp (Str1, Str2: PRegExprChar; MaxLen: Cardinal): Integer;
+ var S1, S2: RegExprString;
+ begin
+ S1 := Str1;
+ S2 := Str2;
+ if Copy (S1, 1, MaxLen) > Copy (S2, 1, MaxLen)
+ then Result := 1
+ else
+ if Copy (S1, 1, MaxLen) < Copy (S2, 1, MaxLen)
+ then Result := -1
+ else Result := 0;
+ end; { function StrLComp
+--------------------------------------------------------------}
+
+function StrScan (Str: PRegExprChar; Chr: WideChar): PRegExprChar;
+ begin
+ Result := nil;
+ while (Str^ <> #0) and (Str^ <> Chr)
+ do Inc (Str);
+ if (Str^ <> #0)
+ then Result := Str;
+ end; { of function StrScan
+--------------------------------------------------------------}
+
+{$ENDIF}
+
+
+{=============================================================}
+{===================== Global functions ======================}
+{=============================================================}
+
+function ExecRegExpr (const ARegExpr, AInputStr : RegExprString) : boolean;
+ var r : TRegExpr;
+ begin
+ r := TRegExpr.Create;
+ try
+ r.Expression := ARegExpr;
+ Result := r.Exec (AInputStr);
+ finally r.Free;
+ end;
+ end; { of function ExecRegExpr
+--------------------------------------------------------------}
+
+procedure SplitRegExpr (const ARegExpr, AInputStr : RegExprString; APieces : TStrings);
+ var r : TRegExpr;
+ begin
+ APieces.Clear;
+ r := TRegExpr.Create;
+ try
+ r.Expression := ARegExpr;
+ r.Split (AInputStr, APieces);
+ finally r.Free;
+ end;
+ end; { of procedure SplitRegExpr
+--------------------------------------------------------------}
+
+function ReplaceRegExpr (const ARegExpr, AInputStr, AReplaceStr : RegExprString;
+ AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) : RegExprString;
+ begin
+ with TRegExpr.Create do try
+ Expression := ARegExpr;
+ Result := Replace (AInputStr, AReplaceStr, AUseSubstitution);
+ finally Free;
+ end;
+ end; { of function ReplaceRegExpr
+--------------------------------------------------------------}
+
+function QuoteRegExprMetaChars (const AStr : RegExprString) : RegExprString;
+ const
+ RegExprMetaSet : RegExprString = '^$.[()|?+*'+EscChar+'{'
+ + ']}'; // - this last are additional to META.
+ // Very similar to META array, but slighly changed.
+ // !Any changes in META array must be synchronized with this set.
+ var
+ i, i0, Len : integer;
+ begin
+ Result := '';
+ Len := length (AStr);
+ i := 1;
+ i0 := i;
+ while i <= Len do begin
+ if Pos (AStr [i], RegExprMetaSet) > 0 then begin
+ Result := Result + System.Copy (AStr, i0, i - i0)
+ + EscChar + AStr [i];
+ i0 := i + 1;
+ end;
+ inc (i);
+ end;
+ Result := Result + System.Copy (AStr, i0, MaxInt); // Tail
+ end; { of function QuoteRegExprMetaChars
+--------------------------------------------------------------}
+
+function RegExprSubExpressions (const ARegExpr : string;
+ ASubExprs : TStrings; AExtendedSyntax : boolean{$IFDEF DefParam}= False{$ENDIF}) : integer;
+ type
+ TStackItemRec = record //###0.945
+ SubExprIdx : integer;
+ StartPos : integer;
+ end;
+ TStackArray = packed array [0 .. NSUBEXPMAX - 1] of TStackItemRec;
+ var
+ Len, SubExprLen : integer;
+ i, i0 : integer;
+ Modif : integer;
+ Stack : ^TStackArray; //###0.945
+ StackIdx, StackSz : integer;
+ begin
+ Result := 0; // no unbalanced brackets found at this very moment
+
+ ASubExprs.Clear; // I don't think that adding to non empty list
+ // can be useful, so I simplified algorithm to work only with empty list
+
+ Len := length (ARegExpr); // some optimization tricks
+
+ // first we have to calculate number of subexpression to reserve
+ // space in Stack array (may be we'll reserve more then need, but
+ // it's faster then memory reallocation during parsing)
+ StackSz := 1; // add 1 for entire r.e.
+ for i := 1 to Len do
+ if ARegExpr [i] = '('
+ then inc (StackSz);
+// SetLength (Stack, StackSz); //###0.945
+ GetMem (Stack, SizeOf (TStackItemRec) * StackSz);
+ try
+
+ StackIdx := 0;
+ i := 1;
+ while (i <= Len) do begin
+ case ARegExpr [i] of
+ '(': begin
+ if (i < Len) and (ARegExpr [i + 1] = '?') then begin
+ // this is not subexpression, but comment or other
+ // Perl extension. We must check is it (?ismxrg-ismxrg)
+ // and change AExtendedSyntax if /x is changed.
+ inc (i, 2); // skip '(?'
+ i0 := i;
+ while (i <= Len) and (ARegExpr [i] <> ')')
+ do inc (i);
+ if i > Len
+ then Result := -1 // unbalansed '('
+ else
+ if TRegExpr.ParseModifiersStr (System.Copy (ARegExpr, i, i - i0), Modif)
+ then AExtendedSyntax := (Modif and MaskModX) <> 0;
+ end
+ else begin // subexpression starts
+ ASubExprs.Add (''); // just reserve space
+ with Stack [StackIdx] do begin
+ SubExprIdx := ASubExprs.Count - 1;
+ StartPos := i;
+ end;
+ inc (StackIdx);
+ end;
+ end;
+ ')': begin
+ if StackIdx = 0
+ then Result := i // unbalanced ')'
+ else begin
+ dec (StackIdx);
+ with Stack [StackIdx] do begin
+ SubExprLen := i - StartPos + 1;
+ ASubExprs.Objects [SubExprIdx] :=
+ TObject (StartPos or (SubExprLen ShL 16));
+ ASubExprs [SubExprIdx] := System.Copy (
+ ARegExpr, StartPos + 1, SubExprLen - 2); // add without brackets
+ end;
+ end;
+ end;
+ EscChar: inc (i); // skip quoted symbol
+ '[': begin
+ // we have to skip character ranges at once, because they can
+ // contain '#', and '#' in it must NOT be recognized as eXtended
+ // comment beginning!
+ i0 := i;
+ inc (i);
+ if ARegExpr [i] = ']' // cannot be 'emty' ranges - this interpretes
+ then inc (i); // as ']' by itself
+ while (i <= Len) and (ARegExpr [i] <> ']') do
+ if ARegExpr [i] = EscChar //###0.942
+ then inc (i, 2) // skip 'escaped' char to prevent stopping at '\]'
+ else inc (i);
+ if (i > Len) or (ARegExpr [i] <> ']') //###0.942
+ then Result := - (i0 + 1); // unbalansed '[' //###0.942
+ end;
+ '#': if AExtendedSyntax then begin
+ // skip eXtended comments
+ while (i <= Len) and (ARegExpr [i] <> #$d) and (ARegExpr [i] <> #$a)
+ // do not use [#$d, #$a] due to UniCode compatibility
+ do inc (i);
+ while (i + 1 <= Len) and ((ARegExpr [i + 1] = #$d) or (ARegExpr [i + 1] = #$a))
+ do inc (i); // attempt to work with different kinds of line separators
+ // now we are at the line separator that must be skipped.
+ end;
+ // here is no 'else' clause - we simply skip ordinary chars
+ end; // of case
+ inc (i); // skip scanned char
+ // ! can move after Len due to skipping quoted symbol
+ end;
+
+ // check brackets balance
+ if StackIdx <> 0
+ then Result := -1; // unbalansed '('
+
+ // check if entire r.e. added
+ if (ASubExprs.Count = 0)
+ or ((PtrInt (ASubExprs.Objects [0]) and $FFFF) <> 1)
+ or (((PtrInt (ASubExprs.Objects [0]) ShR 16) and $FFFF) <> Len)
+ // whole r.e. wasn't added because it isn't bracketed
+ // well, we add it now:
+ then ASubExprs.InsertObject (0, ARegExpr, TObject ((Len ShL 16) or 1));
+
+ finally FreeMem (Stack);
+ end;
+ end; { of function RegExprSubExpressions
+--------------------------------------------------------------}
+
+
+
+const
+ MAGIC = TREOp (216);// programm signature
+
+// name opcode opnd? meaning
+ EEND = TREOp (0); // - End of program
+ BOL = TREOp (1); // - Match "" at beginning of line
+ EOL = TREOp (2); // - Match "" at end of line
+ ANY = TREOp (3); // - Match any one character
+ ANYOF = TREOp (4); // Str Match any character in string Str
+ ANYBUT = TREOp (5); // Str Match any char. not in string Str
+ BRANCH = TREOp (6); // Node Match this alternative, or the next
+ BACK = TREOp (7); // - Jump backward (Next < 0)
+ EXACTLY = TREOp (8); // Str Match string Str
+ NOTHING = TREOp (9); // - Match empty string
+ STAR = TREOp (10); // Node Match this (simple) thing 0 or more times
+ PLUS = TREOp (11); // Node Match this (simple) thing 1 or more times
+ ANYDIGIT = TREOp (12); // - Match any digit (equiv [0-9])
+ NOTDIGIT = TREOp (13); // - Match not digit (equiv [0-9])
+ ANYLETTER = TREOp (14); // - Match any letter from property WordChars
+ NOTLETTER = TREOp (15); // - Match not letter from property WordChars
+ ANYSPACE = TREOp (16); // - Match any space char (see property SpaceChars)
+ NOTSPACE = TREOp (17); // - Match not space char (see property SpaceChars)
+ BRACES = TREOp (18); // Node,Min,Max Match this (simple) thing from Min to Max times.
+ // Min and Max are TREBracesArg
+ COMMENT = TREOp (19); // - Comment ;)
+ EXACTLYCI = TREOp (20); // Str Match string Str case insensitive
+ ANYOFCI = TREOp (21); // Str Match any character in string Str, case insensitive
+ ANYBUTCI = TREOp (22); // Str Match any char. not in string Str, case insensitive
+ LOOPENTRY = TREOp (23); // Node Start of loop (Node - LOOP for this loop)
+ LOOP = TREOp (24); // Node,Min,Max,LoopEntryJmp - back jump for LOOPENTRY.
+ // Min and Max are TREBracesArg
+ // Node - next node in sequence,
+ // LoopEntryJmp - associated LOOPENTRY node addr
+ ANYOFTINYSET= TREOp (25); // Chrs Match any one char from Chrs (exactly TinySetLen chars)
+ ANYBUTTINYSET=TREOp (26); // Chrs Match any one char not in Chrs (exactly TinySetLen chars)
+ ANYOFFULLSET= TREOp (27); // Set Match any one char from set of char
+ // - very fast (one CPU instruction !) but takes 32 bytes of p-code
+ BSUBEXP = TREOp (28); // Idx Match previously matched subexpression #Idx (stored as REChar) //###0.936
+ BSUBEXPCI = TREOp (29); // Idx -"- in case-insensitive mode
+
+ // Non-Greedy Style Ops //###0.940
+ STARNG = TREOp (30); // Same as START but in non-greedy mode
+ PLUSNG = TREOp (31); // Same as PLUS but in non-greedy mode
+ BRACESNG = TREOp (32); // Same as BRACES but in non-greedy mode
+ LOOPNG = TREOp (33); // Same as LOOP but in non-greedy mode
+
+ // Multiline mode \m
+ BOLML = TREOp (34); // - Match "" at beginning of line
+ EOLML = TREOp (35); // - Match "" at end of line
+ ANYML = TREOp (36); // - Match any one character
+
+ // Word boundary
+ BOUND = TREOp (37); // Match "" between words //###0.943
+ NOTBOUND = TREOp (38); // Match "" not between words //###0.943
+
+ // !!! Change OPEN value if you add new opcodes !!!
+
+ OPEN = TREOp (39); // - Mark this point in input as start of \n
+ // OPEN + 1 is \1, etc.
+ CLOSE = TREOp (ord (OPEN) + NSUBEXP);
+ // - Analogous to OPEN.
+
+ // !!! Don't add new OpCodes after CLOSE !!!
+
+// We work with p-code thru pointers, compatible with PRegExprChar.
+// Note: all code components (TRENextOff, TREOp, TREBracesArg, etc)
+// must have lengths that can be divided by SizeOf (REChar) !
+// A node is TREOp of opcode followed Next "pointer" of TRENextOff type.
+// The Next is a offset from the opcode of the node containing it.
+// An operand, if any, simply follows the node. (Note that much of
+// the code generation knows about this implicit relationship!)
+// Using TRENextOff=integer speed up p-code processing.
+
+// Opcodes description:
+//
+// BRANCH The set of branches constituting a single choice are hooked
+// together with their "next" pointers, since precedence prevents
+// anything being concatenated to any individual branch. The
+// "next" pointer of the last BRANCH in a choice points to the
+// thing following the whole choice. This is also where the
+// final "next" pointer of each individual branch points; each
+// branch starts with the operand node of a BRANCH node.
+// BACK Normal "next" pointers all implicitly point forward; BACK
+// exists to make loop structures possible.
+// STAR,PLUS,BRACES '?', and complex '*' and '+', are implemented as
+// circular BRANCH structures using BACK. Complex '{min,max}'
+// - as pair LOOPENTRY-LOOP (see below). Simple cases (one
+// character per match) are implemented with STAR, PLUS and
+// BRACES for speed and to minimize recursive plunges.
+// LOOPENTRY,LOOP {min,max} are implemented as special pair
+// LOOPENTRY-LOOP. Each LOOPENTRY initialize loopstack for
+// current level.
+// OPEN,CLOSE are numbered at compile time.
+
+
+{=============================================================}
+{================== Error handling section ===================}
+{=============================================================}
+
+const
+ reeOk = 0;
+ reeCompNullArgument = 100;
+ reeCompRegexpTooBig = 101;
+ reeCompParseRegTooManyBrackets = 102;
+ reeCompParseRegUnmatchedBrackets = 103;
+ reeCompParseRegUnmatchedBrackets2 = 104;
+ reeCompParseRegJunkOnEnd = 105;
+ reePlusStarOperandCouldBeEmpty = 106;
+ reeNestedSQP = 107;
+ reeBadHexDigit = 108;
+ reeInvalidRange = 109;
+ reeParseAtomTrailingBackSlash = 110;
+ reeNoHexCodeAfterBSlashX = 111;
+ reeHexCodeAfterBSlashXTooBig = 112;
+ reeUnmatchedSqBrackets = 113;
+ reeInternalUrp = 114;
+ reeQPSBFollowsNothing = 115;
+ reeTrailingBackSlash = 116;
+ reeRarseAtomInternalDisaster = 119;
+ reeBRACESArgTooBig = 122;
+ reeBracesMinParamGreaterMax = 124;
+ reeUnclosedComment = 125;
+ reeComplexBracesNotImplemented = 126;
+ reeUrecognizedModifier = 127;
+ reeBadLinePairedSeparator = 128;
+ reeRegRepeatCalledInappropriately = 1000;
+ reeMatchPrimMemoryCorruption = 1001;
+ reeMatchPrimCorruptedPointers = 1002;
+ reeNoExpression = 1003;
+ reeCorruptedProgram = 1004;
+ reeNoInpitStringSpecified = 1005;
+ reeOffsetMustBeGreaterThen0 = 1006;
+ reeExecNextWithoutExec = 1007;
+ reeGetInputStringWithoutInputString = 1008;
+ reeDumpCorruptedOpcode = 1011;
+ reeModifierUnsupported = 1013;
+ reeLoopStackExceeded = 1014;
+ reeLoopWithoutEntry = 1015;
+ reeBadPCodeImported = 2000;
+
+function TRegExpr.ErrorMsg (AErrorID : integer) : RegExprString;
+ begin
+ case AErrorID of
+ reeOk: Result := 'No errors';
+ reeCompNullArgument: Result := 'TRegExpr(comp): Null Argument';
+ reeCompRegexpTooBig: Result := 'TRegExpr(comp): Regexp Too Big';
+ reeCompParseRegTooManyBrackets: Result := 'TRegExpr(comp): ParseReg Too Many ()';
+ reeCompParseRegUnmatchedBrackets: Result := 'TRegExpr(comp): ParseReg Unmatched ()';
+ reeCompParseRegUnmatchedBrackets2: Result := 'TRegExpr(comp): ParseReg Unmatched ()';
+ reeCompParseRegJunkOnEnd: Result := 'TRegExpr(comp): ParseReg Junk On End';
+ reePlusStarOperandCouldBeEmpty: Result := 'TRegExpr(comp): *+ Operand Could Be Empty';
+ reeNestedSQP: Result := 'TRegExpr(comp): Nested *?+';
+ reeBadHexDigit: Result := 'TRegExpr(comp): Bad Hex Digit';
+ reeInvalidRange: Result := 'TRegExpr(comp): Invalid [] Range';
+ reeParseAtomTrailingBackSlash: Result := 'TRegExpr(comp): Parse Atom Trailing \';
+ reeNoHexCodeAfterBSlashX: Result := 'TRegExpr(comp): No Hex Code After \x';
+ reeHexCodeAfterBSlashXTooBig: Result := 'TRegExpr(comp): Hex Code After \x Is Too Big';
+ reeUnmatchedSqBrackets: Result := 'TRegExpr(comp): Unmatched []';
+ reeInternalUrp: Result := 'TRegExpr(comp): Internal Urp';
+ reeQPSBFollowsNothing: Result := 'TRegExpr(comp): ?+*{ Follows Nothing';
+ reeTrailingBackSlash: Result := 'TRegExpr(comp): Trailing \';
+ reeRarseAtomInternalDisaster: Result := 'TRegExpr(comp): RarseAtom Internal Disaster';
+ reeBRACESArgTooBig: Result := 'TRegExpr(comp): BRACES Argument Too Big';
+ reeBracesMinParamGreaterMax: Result := 'TRegExpr(comp): BRACE Min Param Greater then Max';
+ reeUnclosedComment: Result := 'TRegExpr(comp): Unclosed (?#Comment)';
+ reeComplexBracesNotImplemented: Result := 'TRegExpr(comp): If you want take part in beta-testing BRACES ''{min,max}'' and non-greedy ops ''*?'', ''+?'', ''??'' for complex cases - remove ''.'' from {.$DEFINE ComplexBraces}';
+ reeUrecognizedModifier: Result := 'TRegExpr(comp): Urecognized Modifier';
+ reeBadLinePairedSeparator: Result := 'TRegExpr(comp): LinePairedSeparator must countain two different chars or no chars at all';
+
+ reeRegRepeatCalledInappropriately: Result := 'TRegExpr(exec): RegRepeat Called Inappropriately';
+ reeMatchPrimMemoryCorruption: Result := 'TRegExpr(exec): MatchPrim Memory Corruption';
+ reeMatchPrimCorruptedPointers: Result := 'TRegExpr(exec): MatchPrim Corrupted Pointers';
+ reeNoExpression: Result := 'TRegExpr(exec): Not Assigned Expression Property';
+ reeCorruptedProgram: Result := 'TRegExpr(exec): Corrupted Program';
+ reeNoInpitStringSpecified: Result := 'TRegExpr(exec): No Input String Specified';
+ reeOffsetMustBeGreaterThen0: Result := 'TRegExpr(exec): Offset Must Be Greater Then 0';
+ reeExecNextWithoutExec: Result := 'TRegExpr(exec): ExecNext Without Exec[Pos]';
+ reeGetInputStringWithoutInputString: Result := 'TRegExpr(exec): GetInputString Without InputString';
+ reeDumpCorruptedOpcode: Result := 'TRegExpr(dump): Corrupted Opcode';
+ reeLoopStackExceeded: Result := 'TRegExpr(exec): Loop Stack Exceeded';
+ reeLoopWithoutEntry: Result := 'TRegExpr(exec): Loop Without LoopEntry !';
+
+ reeBadPCodeImported: Result := 'TRegExpr(misc): Bad p-code imported';
+ else Result := 'Unknown error';
+ end;
+ end; { of procedure TRegExpr.Error
+--------------------------------------------------------------}
+
+function TRegExpr.LastError : integer;
+ begin
+ Result := fLastError;
+ fLastError := reeOk;
+ end; { of function TRegExpr.LastError
+--------------------------------------------------------------}
+
+
+{=============================================================}
+{===================== Common section ========================}
+{=============================================================}
+
+class function TRegExpr.VersionMajor : integer; //###0.944
+ begin
+ Result := TRegExprVersionMajor;
+ end; { of class function TRegExpr.VersionMajor
+--------------------------------------------------------------}
+
+class function TRegExpr.VersionMinor : integer; //###0.944
+ begin
+ Result := TRegExprVersionMinor;
+ end; { of class function TRegExpr.VersionMinor
+--------------------------------------------------------------}
+
+constructor TRegExpr.Create;
+ begin
+ inherited;
+ programm := nil;
+ fExpression := nil;
+ fInputString := nil;
+
+ regexpbeg := nil;
+ fExprIsCompiled := false;
+
+ ModifierI := RegExprModifierI;
+ ModifierR := RegExprModifierR;
+ ModifierS := RegExprModifierS;
+ ModifierG := RegExprModifierG;
+ ModifierM := RegExprModifierM; //###0.940
+
+ SpaceChars := RegExprSpaceChars; //###0.927
+ WordChars := RegExprWordChars; //###0.929
+ fInvertCase := RegExprInvertCaseFunction; //###0.927
+
+ fLineSeparators := RegExprLineSeparators; //###0.941
+ LinePairedSeparator := RegExprLinePairedSeparator; //###0.941
+ end; { of constructor TRegExpr.Create
+--------------------------------------------------------------}
+
+destructor TRegExpr.Destroy;
+ begin
+ if programm <> nil
+ then FreeMem (programm);
+ if fExpression <> nil
+ then FreeMem (fExpression);
+ if fInputString <> nil
+ then FreeMem (fInputString);
+ end; { of destructor TRegExpr.Destroy
+--------------------------------------------------------------}
+
+class function TRegExpr.InvertCaseFunction (const Ch : REChar) : REChar;
+ begin
+ {$IFDEF UniCode}
+ if Ch >= #128
+ then Result := Ch
+ else
+ {$ENDIF}
+ begin
+ Result := {$IFDEF FPC}AnsiUpperCase (Ch) [1]{$ELSE} {$IFDEF SYN_WIN32}REChar (CharUpper (PChar (Ch))){$ELSE}REChar (toupper (integer (Ch))){$ENDIF} {$ENDIF};
+ if Result = Ch
+ then Result := {$IFDEF FPC}AnsiLowerCase (Ch) [1]{$ELSE} {$IFDEF SYN_WIN32}REChar (CharLower (PChar (Ch))){$ELSE}REChar(tolower (integer (Ch))){$ENDIF} {$ENDIF};
+ end;
+ end; { of function TRegExpr.InvertCaseFunction
+--------------------------------------------------------------}
+
+function TRegExpr.GetExpression : RegExprString;
+ begin
+ if fExpression <> nil
+ then Result := fExpression
+ else Result := '';
+ end; { of function TRegExpr.GetExpression
+--------------------------------------------------------------}
+
+procedure TRegExpr.SetExpression (const s : RegExprString);
+ var
+ Len : integer; //###0.950
+ begin
+ if (s <> fExpression) or not fExprIsCompiled then begin
+ fExprIsCompiled := false;
+ if fExpression <> nil then begin
+ FreeMem (fExpression);
+ fExpression := nil;
+ end;
+ if s <> '' then begin
+ Len := length (s); //###0.950
+ GetMem (fExpression, (Len + 1) * SizeOf (REChar));
+// StrPCopy (fExpression, s); //###0.950 replaced due to StrPCopy limitation of 255 chars
+ {$IFDEF UniCode}
+ StrPCopy (fExpression, Copy (s, 1, Len)); //###0.950
+ {$ELSE}
+ StrLCopy (fExpression, PRegExprChar (s), Len); //###0.950
+ {$ENDIF UniCode}
+
+ InvalidateProgramm; //###0.941
+ end;
+ end;
+ end; { of procedure TRegExpr.SetExpression
+--------------------------------------------------------------}
+
+function TRegExpr.GetSubExprMatchCount : integer;
+ begin
+ if Assigned (fInputString) then begin
+ Result := NSUBEXP - 1;
+ while (Result > 0) and ((startp [Result] = nil)
+ or (endp [Result] = nil))
+ do dec (Result);
+ end
+ else Result := -1;
+ end; { of function TRegExpr.GetSubExprMatchCount
+--------------------------------------------------------------}
+
+function TRegExpr.GetMatchPos (Idx : integer) : integer;
+ begin
+ if (Idx >= 0) and (Idx < NSUBEXP) and Assigned (fInputString)
+ and Assigned (startp [Idx]) and Assigned (endp [Idx]) then begin
+ Result := (startp [Idx] - fInputString) + 1;
+ end
+ else Result := -1;
+ end; { of function TRegExpr.GetMatchPos
+--------------------------------------------------------------}
+
+function TRegExpr.GetMatchLen (Idx : integer) : integer;
+ begin
+ if (Idx >= 0) and (Idx < NSUBEXP) and Assigned (fInputString)
+ and Assigned (startp [Idx]) and Assigned (endp [Idx]) then begin
+ Result := endp [Idx] - startp [Idx];
+ end
+ else Result := -1;
+ end; { of function TRegExpr.GetMatchLen
+--------------------------------------------------------------}
+
+function TRegExpr.GetMatch (Idx : integer) : RegExprString;
+ begin
+ if (Idx >= 0) and (Idx < NSUBEXP) and Assigned (fInputString)
+ and Assigned (startp [Idx]) and Assigned (endp [Idx])
+ //then Result := copy (fInputString, MatchPos [Idx], MatchLen [Idx]) //###0.929
+ then begin
+ {$IFDEF SYN_LAZARUS}
+ Result:='';
+ {$ENDIF}
+ SetString (Result, startp [idx], endp [idx] - startp [idx])
+ end
+ else Result := '';
+ end; { of function TRegExpr.GetMatch
+--------------------------------------------------------------}
+
+function TRegExpr.GetModifierStr : RegExprString;
+ begin
+ Result := '-';
+
+ if ModifierI
+ then Result := 'i' + Result
+ else Result := Result + 'i';
+ if ModifierR
+ then Result := 'r' + Result
+ else Result := Result + 'r';
+ if ModifierS
+ then Result := 's' + Result
+ else Result := Result + 's';
+ if ModifierG
+ then Result := 'g' + Result
+ else Result := Result + 'g';
+ if ModifierM
+ then Result := 'm' + Result
+ else Result := Result + 'm';
+ if ModifierX
+ then Result := 'x' + Result
+ else Result := Result + 'x';
+
+ if Result [length (Result)] = '-' // remove '-' if all modifiers are 'On'
+ then System.Delete (Result, length (Result), 1);
+ end; { of function TRegExpr.GetModifierStr
+--------------------------------------------------------------}
+
+class function TRegExpr.ParseModifiersStr (const AModifiers : RegExprString;
+var AModifiersInt : integer) : boolean;
+// !!! Be carefull - this is class function and must not use object instance fields
+ var
+ i : integer;
+ IsOn : boolean;
+ Mask : integer;
+ begin
+ Result := true;
+ IsOn := true;
+ Mask := 0; // prevent compiler warning
+ for i := 1 to length (AModifiers) do
+ if AModifiers [i] = '-'
+ then IsOn := false
+ else begin
+ if Pos (AModifiers [i], 'iI') > 0
+ then Mask := MaskModI
+ else if Pos (AModifiers [i], 'rR') > 0
+ then Mask := MaskModR
+ else if Pos (AModifiers [i], 'sS') > 0
+ then Mask := MaskModS
+ else if Pos (AModifiers [i], 'gG') > 0
+ then Mask := MaskModG
+ else if Pos (AModifiers [i], 'mM') > 0
+ then Mask := MaskModM
+ else if Pos (AModifiers [i], 'xX') > 0
+ then Mask := MaskModX
+ else begin
+ Result := false;
+ EXIT;
+ end;
+ if IsOn
+ then AModifiersInt := AModifiersInt or Mask
+ else AModifiersInt := AModifiersInt and not Mask;
+ end;
+ end; { of function TRegExpr.ParseModifiersStr
+--------------------------------------------------------------}
+
+procedure TRegExpr.SetModifierStr (const AModifiers : RegExprString);
+ begin
+ if not ParseModifiersStr (AModifiers, fModifiers)
+ then Error (reeModifierUnsupported);
+ end; { of procedure TRegExpr.SetModifierStr
+--------------------------------------------------------------}
+
+function TRegExpr.GetModifier (AIndex : integer) : boolean;
+ var
+ Mask : integer;
+ begin
+ Result := false;
+ case AIndex of
+ 1: Mask := MaskModI;
+ 2: Mask := MaskModR;
+ 3: Mask := MaskModS;
+ 4: Mask := MaskModG;
+ 5: Mask := MaskModM;
+ 6: Mask := MaskModX;
+ else begin
+ Error (reeModifierUnsupported);
+ EXIT;
+ end;
+ end;
+ Result := (fModifiers and Mask) <> 0;
+ end; { of function TRegExpr.GetModifier
+--------------------------------------------------------------}
+
+procedure TRegExpr.SetModifier (AIndex : integer; ASet : boolean);
+ var
+ Mask : integer;
+ begin
+ case AIndex of
+ 1: Mask := MaskModI;
+ 2: Mask := MaskModR;
+ 3: Mask := MaskModS;
+ 4: Mask := MaskModG;
+ 5: Mask := MaskModM;
+ 6: Mask := MaskModX;
+ else begin
+ Error (reeModifierUnsupported);
+ EXIT;
+ end;
+ end;
+ if ASet
+ then fModifiers := fModifiers or Mask
+ else fModifiers := fModifiers and not Mask;
+ end; { of procedure TRegExpr.SetModifier
+--------------------------------------------------------------}
+
+
+{=============================================================}
+{==================== Compiler section =======================}
+{=============================================================}
+
+procedure TRegExpr.InvalidateProgramm;
+ begin
+ if programm <> nil then begin
+ FreeMem (programm);
+ programm := nil;
+ end;
+ end; { of procedure TRegExpr.InvalidateProgramm
+--------------------------------------------------------------}
+
+procedure TRegExpr.Compile; //###0.941
+ begin
+ if fExpression = nil then begin // No Expression assigned
+ Error (reeNoExpression);
+ EXIT;
+ end;
+ CompileRegExpr (fExpression);
+ end; { of procedure TRegExpr.Compile
+--------------------------------------------------------------}
+
+function TRegExpr.IsProgrammOk : boolean;
+ {$IFNDEF UniCode}
+ var
+ i : integer;
+ {$ENDIF}
+ begin
+ Result := false;
+
+ // check modifiers
+ if fModifiers <> fProgModifiers //###0.941
+ then InvalidateProgramm;
+
+ // can we optimize line separators by using sets?
+ {$IFNDEF UniCode}
+ fLineSeparatorsSet := [];
+ for i := 1 to length (fLineSeparators)
+ do System.Include (fLineSeparatorsSet, fLineSeparators [i]);
+ {$ENDIF}
+
+ // [Re]compile if needed
+ if programm = nil
+ then Compile; //###0.941
+
+ // check [re]compiled programm
+ if programm = nil
+ then EXIT // error was set/raised by Compile (was reeExecAfterCompErr)
+ else if programm [0] <> MAGIC // Program corrupted.
+ then Error (reeCorruptedProgram)
+ else Result := true;
+ end; { of function TRegExpr.IsProgrammOk
+--------------------------------------------------------------}
+
+procedure TRegExpr.Tail (p : PRegExprChar; val : PRegExprChar);
+// set the next-pointer at the end of a node chain
+ var
+ scan : PRegExprChar;
+ temp : PRegExprChar;
+// i : int64;
+ begin
+ if p = @regdummy
+ then EXIT;
+ // Find last node.
+ scan := p;
+ REPEAT
+ temp := regnext (scan);
+ if temp = nil
+ then BREAK;
+ scan := temp;
+ UNTIL false;
+ // Set Next 'pointer'
+ if val < scan
+ then PRENextOff (scan + REOpSz)^ := - (scan - val) //###0.948
+ // work around PWideChar subtraction bug (Delphi uses
+ // shr after subtraction to calculate widechar distance %-( )
+ // so, if difference is negative we have .. the "feature" :(
+ // I could wrap it in $IFDEF UniCode, but I didn't because
+ // "P – Q computes the difference between the address given
+ // by P (the higher address) and the address given by Q (the
+ // lower address)" - Delphi help quotation.
+ else PRENextOff (scan + REOpSz)^ := val - scan; //###0.933
+ end; { of procedure TRegExpr.Tail
+--------------------------------------------------------------}
+
+procedure TRegExpr.OpTail (p : PRegExprChar; val : PRegExprChar);
+// regtail on operand of first argument; nop if operandless
+ begin
+ // "Operandless" and "op != BRANCH" are synonymous in practice.
+ if (p = nil) or (p = @regdummy) or (PREOp (p)^ <> BRANCH)
+ then EXIT;
+ Tail (p + REOpSz + RENextOffSz, val); //###0.933
+ end; { of procedure TRegExpr.OpTail
+--------------------------------------------------------------}
+
+function TRegExpr.EmitNode (op : TREOp) : PRegExprChar; //###0.933
+// emit a node, return location
+ begin
+ Result := regcode;
+ if Result <> @regdummy then begin
+ PREOp (regcode)^ := op;
+ inc (regcode, REOpSz);
+ PRENextOff (regcode)^ := 0; // Next "pointer" := nil
+ inc (regcode, RENextOffSz);
+ end
+ else inc (regsize, REOpSz + RENextOffSz); // compute code size without code generation
+ end; { of function TRegExpr.EmitNode
+--------------------------------------------------------------}
+
+procedure TRegExpr.EmitC (b : REChar);
+// emit a byte to code
+ begin
+ if regcode <> @regdummy then begin
+ regcode^ := b;
+ inc (regcode);
+ end
+ else inc (regsize); // Type of p-code pointer always is ^REChar
+ end; { of procedure TRegExpr.EmitC
+--------------------------------------------------------------}
+
+procedure TRegExpr.InsertOperator (op : TREOp; opnd : PRegExprChar; sz : integer);
+// insert an operator in front of already-emitted operand
+// Means relocating the operand.
+ var
+ src, dst, place : PRegExprChar;
+ i : integer;
+ begin
+ if regcode = @regdummy then begin
+ inc (regsize, sz);
+ EXIT;
+ end;
+ src := regcode;
+ inc (regcode, sz);
+ dst := regcode;
+ while src > opnd do begin
+ dec (dst);
+ dec (src);
+ dst^ := src^;
+ end;
+ place := opnd; // Op node, where operand used to be.
+ PREOp (place)^ := op;
+ inc (place, REOpSz);
+ for i := 1 + REOpSz to sz do begin
+ place^ := #0;
+ inc (place);
+ end;
+ end; { of procedure TRegExpr.InsertOperator
+--------------------------------------------------------------}
+
+function strcspn (s1 : PRegExprChar; s2 : PRegExprChar) : integer;
+// find length of initial segment of s1 consisting
+// entirely of characters not from s2
+ var scan1, scan2 : PRegExprChar;
+ begin
+ Result := 0;
+ scan1 := s1;
+ while scan1^ <> #0 do begin
+ scan2 := s2;
+ while scan2^ <> #0 do
+ if scan1^ = scan2^
+ then EXIT
+ else inc (scan2);
+ inc (Result);
+ inc (scan1)
+ end;
+ end; { of function strcspn
+--------------------------------------------------------------}
+
+const
+// Flags to be passed up and down.
+ HASWIDTH = 01; // Known never to match nil string.
+ SIMPLE = 02; // Simple enough to be STAR/PLUS/BRACES operand.
+ SPSTART = 04; // Starts with * or +.
+ WORST = 0; // Worst case.
+ META : array [0 .. 12] of REChar = (
+ '^', '$', '.', '[', '(', ')', '|', '?', '+', '*', EscChar, '{', #0);
+ // Any modification must be synchronized with QuoteRegExprMetaChars !!!
+
+{$IFDEF UniCode}
+ RusRangeLo : array [0 .. 33] of REChar =
+ (#$430,#$431,#$432,#$433,#$434,#$435,#$451,#$436,#$437,
+ #$438,#$439,#$43A,#$43B,#$43C,#$43D,#$43E,#$43F,
+ #$440,#$441,#$442,#$443,#$444,#$445,#$446,#$447,
+ #$448,#$449,#$44A,#$44B,#$44C,#$44D,#$44E,#$44F,#0);
+ RusRangeHi : array [0 .. 33] of REChar =
+ (#$410,#$411,#$412,#$413,#$414,#$415,#$401,#$416,#$417,
+ #$418,#$419,#$41A,#$41B,#$41C,#$41D,#$41E,#$41F,
+ #$420,#$421,#$422,#$423,#$424,#$425,#$426,#$427,
+ #$428,#$429,#$42A,#$42B,#$42C,#$42D,#$42E,#$42F,#0);
+ RusRangeLoLow = #$430{'а'};
+ RusRangeLoHigh = #$44F{'я'};
+ RusRangeHiLow = #$410{'А'};
+ RusRangeHiHigh = #$42F{'Я'};
+{$ELSE}
+ RusRangeLo = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя';
+ RusRangeHi = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ';
+ RusRangeLoLow = 'а';
+ RusRangeLoHigh = 'я';
+ RusRangeHiLow = 'А';
+ RusRangeHiHigh = 'Я';
+{$ENDIF}
+
+function TRegExpr.CompileRegExpr (exp : PRegExprChar) : boolean;
+// compile a regular expression into internal code
+// We can't allocate space until we know how big the compiled form will be,
+// but we can't compile it (and thus know how big it is) until we've got a
+// place to put the code. So we cheat: we compile it twice, once with code
+// generation turned off and size counting turned on, and once "for real".
+// This also means that we don't allocate space until we are sure that the
+// thing really will compile successfully, and we never have to move the
+// code and thus invalidate pointers into it. (Note that it has to be in
+// one piece because free() must be able to free it all.)
+// Beware that the optimization-preparation code in here knows about some
+// of the structure of the compiled regexp.
+ var
+ scan, longest : PRegExprChar;
+ len : cardinal;
+ flags : integer;
+ begin
+ Result := false; // life too dark
+
+ regparse := nil; // for correct error handling
+ regexpbeg := exp;
+ try
+
+ if programm <> nil then begin
+ FreeMem (programm);
+ programm := nil;
+ end;
+
+ if exp = nil then begin
+ Error (reeCompNullArgument);
+ EXIT;
+ end;
+
+ fProgModifiers := fModifiers;
+ // well, may it's paranoia. I'll check it later... !!!!!!!!
+
+ // First pass: determine size, legality.
+ fCompModifiers := fModifiers;
+ regparse := exp;
+ regnpar := 1;
+ regsize := 0;
+ regcode := @regdummy;
+ EmitC (MAGIC);
+ if ParseReg (0, flags) = nil
+ then EXIT;
+
+ // Small enough for 2-bytes programm pointers ?
+ // ###0.933 no real p-code length limits now :)))
+// if regsize >= 64 * 1024 then begin
+// Error (reeCompRegexpTooBig);
+// EXIT;
+// end;
+
+ // Allocate space.
+ GetMem (programm, regsize * SizeOf (REChar));
+
+ // Second pass: emit code.
+ fCompModifiers := fModifiers;
+ regparse := exp;
+ regnpar := 1;
+ regcode := programm;
+ EmitC (MAGIC);
+ if ParseReg (0, flags) = nil
+ then EXIT;
+
+ // Dig out information for optimizations.
+ {$IFDEF UseFirstCharSet} //###0.929
+ FirstCharSet := [];
+ FillFirstCharSet (programm + REOpSz);
+ {$ENDIF}
+ regstart := #0; // Worst-case defaults.
+ reganch := #0;
+ regmust := nil;
+ regmlen := 0;
+ scan := programm + REOpSz; // First BRANCH.
+ if PREOp (regnext (scan))^ = EEND then begin // Only one top-level choice.
+ scan := scan + REOpSz + RENextOffSz;
+
+ // Starting-point info.
+ if PREOp (scan)^ = EXACTLY
+ then regstart := (scan + REOpSz + RENextOffSz)^
+ else if PREOp (scan)^ = BOL
+ then inc (reganch);
+
+ // If there's something expensive in the r.e., find the longest
+ // literal string that must appear and make it the regmust. Resolve
+ // ties in favor of later strings, since the regstart check works
+ // with the beginning of the r.e. and avoiding duplication
+ // strengthens checking. Not a strong reason, but sufficient in the
+ // absence of others.
+ if (flags and SPSTART) <> 0 then begin
+ longest := nil;
+ len := 0;
+ while scan <> nil do begin
+ if (PREOp (scan)^ = EXACTLY)
+ and (strlen (scan + REOpSz + RENextOffSz) >= integer(len)) then begin
+ longest := scan + REOpSz + RENextOffSz;
+ len := strlen (longest);
+ end;
+ scan := regnext (scan);
+ end;
+ regmust := longest;
+ regmlen := len;
+ end;
+ end;
+
+ Result := true;
+
+ finally begin
+ if not Result
+ then InvalidateProgramm;
+ regexpbeg := nil;
+ fExprIsCompiled := Result; //###0.944
+ end;
+ end;
+
+ end; { of function TRegExpr.CompileRegExpr
+--------------------------------------------------------------}
+
+function TRegExpr.ParseReg (paren : integer; var flagp : integer) : PRegExprChar;
+// regular expression, i.e. main body or parenthesized thing
+// Caller must absorb opening parenthesis.
+// Combining parenthesis handling with the base level of regular expression
+// is a trifle forced, but the need to tie the tails of the branches to what
+// follows makes it hard to avoid.
+ var
+ ret, br, ender : PRegExprChar;
+ parno : integer;
+ flags : integer;
+ SavedModifiers : integer;
+ begin
+ Result := nil;
+ flagp := HASWIDTH; // Tentatively.
+ parno := 0; // eliminate compiler stupid warning
+ SavedModifiers := fCompModifiers;
+
+ // Make an OPEN node, if parenthesized.
+ if paren <> 0 then begin
+ if regnpar >= NSUBEXP then begin
+ Error (reeCompParseRegTooManyBrackets);
+ EXIT;
+ end;
+ parno := regnpar;
+ inc (regnpar);
+ ret := EmitNode (TREOp (ord (OPEN) + parno));
+ end
+ else ret := nil;
+
+ // Pick up the branches, linking them together.
+ br := ParseBranch (flags);
+ if br = nil then begin
+ Result := nil;
+ EXIT;
+ end;
+ if ret <> nil
+ then Tail (ret, br) // OPEN -> first.
+ else ret := br;
+ if (flags and HASWIDTH) = 0
+ then flagp := flagp and not HASWIDTH;
+ flagp := flagp or flags and SPSTART;
+ while (regparse^ = '|') do begin
+ inc (regparse);
+ br := ParseBranch (flags);
+ if br = nil then begin
+ Result := nil;
+ EXIT;
+ end;
+ Tail (ret, br); // BRANCH -> BRANCH.
+ if (flags and HASWIDTH) = 0
+ then flagp := flagp and not HASWIDTH;
+ flagp := flagp or flags and SPSTART;
+ end;
+
+ // Make a closing node, and hook it on the end.
+ if paren <> 0
+ then ender := EmitNode (TREOp (ord (CLOSE) + parno))
+ else ender := EmitNode (EEND);
+ Tail (ret, ender);
+
+ // Hook the tails of the branches to the closing node.
+ br := ret;
+ while br <> nil do begin
+ OpTail (br, ender);
+ br := regnext (br);
+ end;
+
+ // Check for proper termination.
+ if paren <> 0 then
+ if regparse^ <> ')' then begin
+ Error (reeCompParseRegUnmatchedBrackets);
+ EXIT;
+ end
+ else inc (regparse); // skip trailing ')'
+ if (paren = 0) and (regparse^ <> #0) then begin
+ if regparse^ = ')'
+ then Error (reeCompParseRegUnmatchedBrackets2)
+ else Error (reeCompParseRegJunkOnEnd);
+ EXIT;
+ end;
+ fCompModifiers := SavedModifiers; // restore modifiers of parent
+ Result := ret;
+ end; { of function TRegExpr.ParseReg
+--------------------------------------------------------------}
+
+function TRegExpr.ParseBranch (var flagp : integer) : PRegExprChar;
+// one alternative of an | operator
+// Implements the concatenation operator.
+ var
+ ret, chain, latest : PRegExprChar;
+ flags : integer;
+ begin
+ flagp := WORST; // Tentatively.
+
+ ret := EmitNode (BRANCH);
+ chain := nil;
+ while (regparse^ <> #0) and (regparse^ <> '|')
+ and (regparse^ <> ')') do begin
+ latest := ParsePiece (flags);
+ if latest = nil then begin
+ Result := nil;
+ EXIT;
+ end;
+ flagp := flagp or flags and HASWIDTH;
+ if chain = nil // First piece.
+ then flagp := flagp or flags and SPSTART
+ else Tail (chain, latest);
+ chain := latest;
+ end;
+ if chain = nil // Loop ran zero times.
+ then EmitNode (NOTHING);
+ Result := ret;
+ end; { of function TRegExpr.ParseBranch
+--------------------------------------------------------------}
+
+function TRegExpr.ParsePiece (var flagp : integer) : PRegExprChar;
+// something followed by possible [*+?{]
+// Note that the branching code sequences used for ? and the general cases
+// of * and + and { are somewhat optimized: they use the same NOTHING node as
+// both the endmarker for their branch list and the body of the last branch.
+// It might seem that this node could be dispensed with entirely, but the
+// endmarker role is not redundant.
+ function parsenum (AStart, AEnd : PRegExprChar) : TREBracesArg;
+ begin
+ Result := 0;
+ if AEnd - AStart + 1 > 8 then begin // prevent stupid scanning
+ Error (reeBRACESArgTooBig);
+ EXIT;
+ end;
+ while AStart <= AEnd do begin
+ Result := Result * 10 + (ord (AStart^) - ord ('0'));
+ inc (AStart);
+ end;
+ if (Result > MaxBracesArg) or (Result < 0) then begin
+ Error (reeBRACESArgTooBig);
+ EXIT;
+ end;
+ end;
+
+ var
+ op : REChar;
+ NonGreedyOp, NonGreedyCh : boolean; //###0.940
+ TheOp : TREOp; //###0.940
+ NextNode : PRegExprChar;
+ flags : integer;
+ BracesMin, Bracesmax : TREBracesArg;
+ p, savedparse : PRegExprChar;
+
+ procedure EmitComplexBraces (ABracesMin, ABracesMax : TREBracesArg;
+ ANonGreedyOp : boolean); //###0.940
+ {$IFDEF ComplexBraces}
+ var
+ off : integer;
+ {$ENDIF}
+ begin
+ {$IFNDEF ComplexBraces}
+ Error (reeComplexBracesNotImplemented);
+ {$ELSE}
+ if ANonGreedyOp
+ then TheOp := LOOPNG
+ else TheOp := LOOP;
+ InsertOperator (LOOPENTRY, Result, REOpSz + RENextOffSz);
+ NextNode := EmitNode (TheOp);
+ if regcode <> @regdummy then begin
+ off := (Result + REOpSz + RENextOffSz)
+ - (regcode - REOpSz - RENextOffSz); // back to Atom after LOOPENTRY
+ PREBracesArg (regcode)^ := ABracesMin;
+ inc (regcode, REBracesArgSz);
+ PREBracesArg (regcode)^ := ABracesMax;
+ inc (regcode, REBracesArgSz);
+ PRENextOff (regcode)^ := off;
+ inc (regcode, RENextOffSz);
+ end
+ else inc (regsize, REBracesArgSz * 2 + RENextOffSz);
+ Tail (Result, NextNode); // LOOPENTRY -> LOOP
+ if regcode <> @regdummy then
+ Tail (Result + REOpSz + RENextOffSz, NextNode); // Atom -> LOOP
+ {$ENDIF}
+ end;
+
+ procedure EmitSimpleBraces (ABracesMin, ABracesMax : TREBracesArg;
+ ANonGreedyOp : boolean); //###0.940
+ begin
+ if ANonGreedyOp //###0.940
+ then TheOp := BRACESNG
+ else TheOp := BRACES;
+ InsertOperator (TheOp, Result, REOpSz + RENextOffSz + REBracesArgSz * 2);
+ if regcode <> @regdummy then begin
+ PREBracesArg (Result + REOpSz + RENextOffSz)^ := ABracesMin;
+ PREBracesArg (Result + REOpSz + RENextOffSz + REBracesArgSz)^ := ABracesMax;
+ end;
+ end;
+
+ begin
+ Result := ParseAtom (flags);
+ if Result = nil
+ then EXIT;
+
+ op := regparse^;
+ if not ((op = '*') or (op = '+') or (op = '?') or (op = '{')) then begin
+ flagp := flags;
+ EXIT;
+ end;
+ if ((flags and HASWIDTH) = 0) and (op <> '?') then begin
+ Error (reePlusStarOperandCouldBeEmpty);
+ EXIT;
+ end;
+
+ case op of
+ '*': begin
+ flagp := WORST or SPSTART;
+ NonGreedyCh := (regparse + 1)^ = '?'; //###0.940
+ NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940
+ if (flags and SIMPLE) = 0 then begin
+ if NonGreedyOp //###0.940
+ then EmitComplexBraces (0, MaxBracesArg, NonGreedyOp)
+ else begin // Emit x* as (x&|), where & means "self".
+ InsertOperator (BRANCH, Result, REOpSz + RENextOffSz); // Either x
+ OpTail (Result, EmitNode (BACK)); // and loop
+ OpTail (Result, Result); // back
+ Tail (Result, EmitNode (BRANCH)); // or
+ Tail (Result, EmitNode (NOTHING)); // nil.
+ end
+ end
+ else begin // Simple
+ if NonGreedyOp //###0.940
+ then TheOp := STARNG
+ else TheOp := STAR;
+ InsertOperator (TheOp, Result, REOpSz + RENextOffSz);
+ end;
+ if NonGreedyCh //###0.940
+ then inc (regparse); // Skip extra char ('?')
+ end; { of case '*'}
+ '+': begin
+ flagp := WORST or SPSTART or HASWIDTH;
+ NonGreedyCh := (regparse + 1)^ = '?'; //###0.940
+ NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940
+ if (flags and SIMPLE) = 0 then begin
+ if NonGreedyOp //###0.940
+ then EmitComplexBraces (1, MaxBracesArg, NonGreedyOp)
+ else begin // Emit x+ as x(&|), where & means "self".
+ NextNode := EmitNode (BRANCH); // Either
+ Tail (Result, NextNode);
+ Tail (EmitNode (BACK), Result); // loop back
+ Tail (NextNode, EmitNode (BRANCH)); // or
+ Tail (Result, EmitNode (NOTHING)); // nil.
+ end
+ end
+ else begin // Simple
+ if NonGreedyOp //###0.940
+ then TheOp := PLUSNG
+ else TheOp := PLUS;
+ InsertOperator (TheOp, Result, REOpSz + RENextOffSz);
+ end;
+ if NonGreedyCh //###0.940
+ then inc (regparse); // Skip extra char ('?')
+ end; { of case '+'}
+ '?': begin
+ flagp := WORST;
+ NonGreedyCh := (regparse + 1)^ = '?'; //###0.940
+ NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940
+ if NonGreedyOp then begin //###0.940 // We emit x?? as x{0,1}?
+ if (flags and SIMPLE) = 0
+ then EmitComplexBraces (0, 1, NonGreedyOp)
+ else EmitSimpleBraces (0, 1, NonGreedyOp);
+ end
+ else begin // greedy '?'
+ InsertOperator (BRANCH, Result, REOpSz + RENextOffSz); // Either x
+ Tail (Result, EmitNode (BRANCH)); // or
+ NextNode := EmitNode (NOTHING); // nil.
+ Tail (Result, NextNode);
+ OpTail (Result, NextNode);
+ end;
+ if NonGreedyCh //###0.940
+ then inc (regparse); // Skip extra char ('?')
+ end; { of case '?'}
+ '{': begin
+ savedparse := regparse;
+ // !!!!!!!!!!!!
+ // Filip Jirsak's note - what will happen, when we are at the end of regparse?
+ inc (regparse);
+ p := regparse;
+ while Pos (regparse^, '0123456789') > 0 // <min> MUST appear
+ do inc (regparse);
+ if (regparse^ <> '}') and (regparse^ <> ',') or (p = regparse) then begin
+ regparse := savedparse;
+ flagp := flags;
+ EXIT;
+ end;
+ BracesMin := parsenum (p, regparse - 1);
+ if regparse^ = ',' then begin
+ inc (regparse);
+ p := regparse;
+ while Pos (regparse^, '0123456789') > 0
+ do inc (regparse);
+ if regparse^ <> '}' then begin
+ regparse := savedparse;
+ EXIT;
+ end;
+ if p = regparse
+ then BracesMax := MaxBracesArg
+ else BracesMax := parsenum (p, regparse - 1);
+ end
+ else BracesMax := BracesMin; // {n} == {n,n}
+ if BracesMin > BracesMax then begin
+ Error (reeBracesMinParamGreaterMax);
+ EXIT;
+ end;
+ if BracesMin > 0
+ then flagp := WORST;
+ if BracesMax > 0
+ then flagp := flagp or HASWIDTH or SPSTART;
+
+ NonGreedyCh := (regparse + 1)^ = '?'; //###0.940
+ NonGreedyOp := NonGreedyCh or ((fCompModifiers and MaskModG) = 0); //###0.940
+ if (flags and SIMPLE) <> 0
+ then EmitSimpleBraces (BracesMin, BracesMax, NonGreedyOp)
+ else EmitComplexBraces (BracesMin, BracesMax, NonGreedyOp);
+ if NonGreedyCh //###0.940
+ then inc (regparse); // Skip extra char '?'
+ end; // of case '{'
+// else // here we can't be
+ end; { of case op}
+
+ inc (regparse);
+ if (regparse^ = '*') or (regparse^ = '+') or (regparse^ = '?') or (regparse^ = '{') then begin
+ Error (reeNestedSQP);
+ EXIT;
+ end;
+ end; { of function TRegExpr.ParsePiece
+--------------------------------------------------------------}
+
+function TRegExpr.ParseAtom (var flagp : integer) : PRegExprChar;
+// the lowest level
+// Optimization: gobbles an entire sequence of ordinary characters so that
+// it can turn them into a single node, which is smaller to store and
+// faster to run. Backslashed characters are exceptions, each becoming a
+// separate node; the code is simpler that way and it's not worth fixing.
+ var
+ ret : PRegExprChar;
+ flags : integer;
+ RangeBeg, RangeEnd : REChar;
+ CanBeRange : boolean;
+ len : integer;
+ ender : REChar;
+ begmodfs : PRegExprChar;
+
+ {$IFDEF UseSetOfChar} //###0.930
+ RangePCodeBeg : PRegExprChar;
+ RangePCodeIdx : integer;
+ RangeIsCI : boolean;
+ RangeSet : TSetOfREChar;
+ RangeLen : integer;
+ RangeChMin, RangeChMax : REChar;
+ {$ENDIF}
+
+ procedure EmitExactly (ch : REChar);
+ begin
+ if (fCompModifiers and MaskModI) <> 0
+ then ret := EmitNode (EXACTLYCI)
+ else ret := EmitNode (EXACTLY);
+ EmitC (ch);
+ EmitC (#0);
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+
+ procedure EmitStr (const s : RegExprString);
+ var i : integer;
+ begin
+ for i := 1 to length (s)
+ do EmitC (s [i]);
+ end;
+
+ function HexDig (ch : REChar) : integer;
+ begin
+ Result := 0;
+ if (ch >= 'a') and (ch <= 'f')
+ then ch := REChar (ord (ch) - (ord ('a') - ord ('A')));
+ if (ch < '0') or (ch > 'F') or ((ch > '9') and (ch < 'A')) then begin
+ Error (reeBadHexDigit);
+ EXIT;
+ end;
+ Result := ord (ch) - ord ('0');
+ if ch >= 'A'
+ then Result := Result - (ord ('A') - ord ('9') - 1);
+ end;
+
+ function EmitRange (AOpCode : REChar) : PRegExprChar;
+ begin
+ {$IFDEF UseSetOfChar}
+ case AOpCode of
+ ANYBUTCI, ANYBUT:
+ Result := EmitNode (ANYBUTTINYSET);
+ else // ANYOFCI, ANYOF
+ Result := EmitNode (ANYOFTINYSET);
+ end;
+ case AOpCode of
+ ANYBUTCI, ANYOFCI:
+ RangeIsCI := True;
+ else // ANYBUT, ANYOF
+ RangeIsCI := False;
+ end;
+ RangePCodeBeg := regcode;
+ RangePCodeIdx := regsize;
+ RangeLen := 0;
+ RangeSet := [];
+ RangeChMin := #255;
+ RangeChMax := #0;
+ {$ELSE}
+ Result := EmitNode (AOpCode);
+ // ToDo:
+ // !!!!!!!!!!!!! Implement ANYOF[BUT]TINYSET generation for UniCode !!!!!!!!!!
+ {$ENDIF}
+ end;
+
+{$IFDEF UseSetOfChar}
+ procedure EmitRangeCPrim (b : REChar); //###0.930
+ begin
+ if b in RangeSet
+ then EXIT;
+ inc (RangeLen);
+ if b < RangeChMin
+ then RangeChMin := b;
+ if b > RangeChMax
+ then RangeChMax := b;
+ Include (RangeSet, b);
+ end;
+ {$ENDIF}
+
+ procedure EmitRangeC (b : REChar);
+ {$IFDEF UseSetOfChar}
+ var
+ Ch : REChar;
+ {$ENDIF}
+ begin
+ CanBeRange := false;
+ {$IFDEF UseSetOfChar}
+ if b <> #0 then begin
+ EmitRangeCPrim (b); //###0.930
+ if RangeIsCI
+ then EmitRangeCPrim (InvertCase (b)); //###0.930
+ end
+ else begin
+ {$IFDEF UseAsserts}
+ Assert (RangeLen > 0, 'TRegExpr.ParseAtom(subroutine EmitRangeC): empty range'); // impossible, but who knows..
+ Assert (RangeChMin <= RangeChMax, 'TRegExpr.ParseAtom(subroutine EmitRangeC): RangeChMin > RangeChMax'); // impossible, but who knows..
+ {$ENDIF}
+ if RangeLen <= TinySetLen then begin // emit "tiny set"
+ if regcode = @regdummy then begin
+ regsize := RangePCodeIdx + TinySetLen; // RangeChMin/Max !!!
+ EXIT;
+ end;
+ regcode := RangePCodeBeg;
+ for Ch := RangeChMin to RangeChMax do //###0.930
+ if Ch in RangeSet then begin
+ regcode^ := Ch;
+ inc (regcode);
+ end;
+ // fill rest:
+ while regcode < RangePCodeBeg + TinySetLen do begin
+ regcode^ := RangeChMax;
+ inc (regcode);
+ end;
+ end
+ else begin
+ if regcode = @regdummy then begin
+ regsize := RangePCodeIdx + SizeOf (TSetOfREChar);
+ EXIT;
+ end;
+ if (RangePCodeBeg - REOpSz - RENextOffSz)^ = ANYBUTTINYSET
+ then RangeSet := [#0 .. #255] - RangeSet;
+ PREOp (RangePCodeBeg - REOpSz - RENextOffSz)^ := ANYOFFULLSET;
+ regcode := RangePCodeBeg;
+ Move (RangeSet, regcode^, SizeOf (TSetOfREChar));
+ inc (regcode, SizeOf (TSetOfREChar));
+ end;
+ end;
+ {$ELSE}
+ EmitC (b);
+ {$ENDIF}
+ end;
+
+ procedure EmitSimpleRangeC (b : REChar);
+ begin
+ RangeBeg := b;
+ EmitRangeC (b);
+ CanBeRange := true;
+ end;
+
+ procedure EmitRangeStr (const s : RegExprString);
+ var i : integer;
+ begin
+ for i := 1 to length (s)
+ do EmitRangeC (s [i]);
+ end;
+
+ function UnQuoteChar (var APtr : PRegExprChar) : REChar; //###0.934
+ begin
+ case APtr^ of
+ 't': Result := #$9; // tab (HT/TAB)
+ 'n': Result := #$a; // newline (NL)
+ 'r': Result := #$d; // car.return (CR)
+ 'f': Result := #$c; // form feed (FF)
+ 'a': Result := #$7; // alarm (bell) (BEL)
+ 'e': Result := #$1b; // escape (ESC)
+ 'x': begin // hex char
+ Result := #0;
+ inc (APtr);
+ if APtr^ = #0 then begin
+ Error (reeNoHexCodeAfterBSlashX);
+ EXIT;
+ end;
+ if APtr^ = '{' then begin // \x{nnnn} //###0.936
+ REPEAT
+ inc (APtr);
+ if APtr^ = #0 then begin
+ Error (reeNoHexCodeAfterBSlashX);
+ EXIT;
+ end;
+ if APtr^ <> '}' then begin
+ if (Ord (Result)
+ ShR (SizeOf (REChar) * 8 - 4)) and $F <> 0 then begin
+ Error (reeHexCodeAfterBSlashXTooBig);
+ EXIT;
+ end;
+ Result := REChar ((Ord (Result) ShL 4) or HexDig (APtr^));
+ // HexDig will cause Error if bad hex digit found
+ end
+ else BREAK;
+ UNTIL False;
+ end
+ else begin
+ Result := REChar (HexDig (APtr^));
+ // HexDig will cause Error if bad hex digit found
+ inc (APtr);
+ if APtr^ = #0 then begin
+ Error (reeNoHexCodeAfterBSlashX);
+ EXIT;
+ end;
+ Result := REChar ((Ord (Result) ShL 4) or HexDig (APtr^));
+ // HexDig will cause Error if bad hex digit found
+ end;
+ end;
+ else Result := APtr^;
+ end;
+ end;
+
+ begin
+ Result := nil;
+ flagp := WORST; // Tentatively.
+
+ inc (regparse);
+ case (regparse - 1)^ of
+ '^': if ((fCompModifiers and MaskModM) = 0)
+ or ((fLineSeparators = '') and not fLinePairedSeparatorAssigned)
+ then ret := EmitNode (BOL)
+ else ret := EmitNode (BOLML);
+ '$': if ((fCompModifiers and MaskModM) = 0)
+ or ((fLineSeparators = '') and not fLinePairedSeparatorAssigned)
+ then ret := EmitNode (EOL)
+ else ret := EmitNode (EOLML);
+ '.':
+ if (fCompModifiers and MaskModS) <> 0 then begin
+ ret := EmitNode (ANY);
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end
+ else begin // not /s, so emit [^:LineSeparators:]
+ ret := EmitNode (ANYML);
+ flagp := flagp or HASWIDTH; // not so simple ;)
+// ret := EmitRange (ANYBUT);
+// EmitRangeStr (LineSeparators); //###0.941
+// EmitRangeStr (LinePairedSeparator); // !!! isn't correct if have to accept only paired
+// EmitRangeC (#0);
+// flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+ '[': begin
+ if regparse^ = '^' then begin // Complement of range.
+ if (fCompModifiers and MaskModI) <> 0
+ then ret := EmitRange (ANYBUTCI)
+ else ret := EmitRange (ANYBUT);
+ inc (regparse);
+ end
+ else
+ if (fCompModifiers and MaskModI) <> 0
+ then ret := EmitRange (ANYOFCI)
+ else ret := EmitRange (ANYOF);
+
+ CanBeRange := false;
+
+ if (regparse^ = ']') then begin
+ EmitSimpleRangeC (regparse^); // []-a] -> ']' .. 'a'
+ inc (regparse);
+ end;
+
+ while (regparse^ <> #0) and (regparse^ <> ']') do begin
+ if (regparse^ = '-')
+ and ((regparse + 1)^ <> #0) and ((regparse + 1)^ <> ']')
+ and CanBeRange then begin
+ inc (regparse);
+ RangeEnd := regparse^;
+ if RangeEnd = EscChar then begin
+ {$IFDEF UniCode} //###0.935
+ if (ord ((regparse + 1)^) < 256)
+ and (char ((regparse + 1)^)
+ in ['d', 'D', 's', 'S', 'w', 'W']) then begin
+ {$ELSE}
+ if (regparse + 1)^ in ['d', 'D', 's', 'S', 'w', 'W'] then begin
+ {$ENDIF}
+ EmitRangeC ('-'); // or treat as error ?!!
+ CONTINUE;
+ end;
+ inc (regparse);
+ RangeEnd := UnQuoteChar (regparse);
+ end;
+
+ // r.e.ranges extension for russian
+ if ((fCompModifiers and MaskModR) <> 0)
+ and (RangeBeg = RusRangeLoLow) and (RangeEnd = RusRangeLoHigh) then begin
+ EmitRangeStr (RusRangeLo);
+ end
+ else if ((fCompModifiers and MaskModR) <> 0)
+ and (RangeBeg = RusRangeHiLow) and (RangeEnd = RusRangeHiHigh) then begin
+ EmitRangeStr (RusRangeHi);
+ end
+ else if ((fCompModifiers and MaskModR) <> 0)
+ and (RangeBeg = RusRangeLoLow) and (RangeEnd = RusRangeHiHigh) then begin
+ EmitRangeStr (RusRangeLo);
+ EmitRangeStr (RusRangeHi);
+ end
+ else begin // standard r.e. handling
+ if RangeBeg > RangeEnd then begin
+ Error (reeInvalidRange);
+ EXIT;
+ end;
+ inc (RangeBeg);
+ EmitRangeC (RangeEnd); // prevent infinite loop if RangeEnd=$ff
+ while RangeBeg < RangeEnd do begin //###0.929
+ EmitRangeC (RangeBeg);
+ inc (RangeBeg);
+ end;
+ end;
+ inc (regparse);
+ end
+ else begin
+ if regparse^ = EscChar then begin
+ inc (regparse);
+ if regparse^ = #0 then begin
+ Error (reeParseAtomTrailingBackSlash);
+ EXIT;
+ end;
+ case regparse^ of // r.e.extensions
+ 'd': EmitRangeStr ('0123456789');
+ 'w': EmitRangeStr (WordChars);
+ 's': EmitRangeStr (SpaceChars);
+ else EmitSimpleRangeC (UnQuoteChar (regparse));
+ end; { of case}
+ end
+ else EmitSimpleRangeC (regparse^);
+ inc (regparse);
+ end;
+ end; { of while}
+ EmitRangeC (#0);
+ if regparse^ <> ']' then begin
+ Error (reeUnmatchedSqBrackets);
+ EXIT;
+ end;
+ inc (regparse);
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+ '(': begin
+ if regparse^ = '?' then begin
+ // check for extended Perl syntax : (?..)
+ if (regparse + 1)^ = '#' then begin // (?#comment)
+ inc (regparse, 2); // find closing ')'
+ while (regparse^ <> #0) and (regparse^ <> ')')
+ do inc (regparse);
+ if regparse^ <> ')' then begin
+ Error (reeUnclosedComment);
+ EXIT;
+ end;
+ inc (regparse); // skip ')'
+ ret := EmitNode (COMMENT); // comment
+ end
+ else begin // modifiers ?
+ inc (regparse); // skip '?'
+ begmodfs := regparse;
+ while (regparse^ <> #0) and (regparse^ <> ')')
+ do inc (regparse);
+ if (regparse^ <> ')')
+ or not ParseModifiersStr (copy (begmodfs, 1, (regparse - begmodfs)), fCompModifiers) then begin
+ Error (reeUrecognizedModifier);
+ EXIT;
+ end;
+ inc (regparse); // skip ')'
+ ret := EmitNode (COMMENT); // comment
+// Error (reeQPSBFollowsNothing);
+// EXIT;
+ end;
+ end
+ else begin
+ ret := ParseReg (1, flags);
+ if ret = nil then begin
+ Result := nil;
+ EXIT;
+ end;
+ flagp := flagp or flags and (HASWIDTH or SPSTART);
+ end;
+ end;
+ #0, '|', ')': begin // Supposed to be caught earlier.
+ Error (reeInternalUrp);
+ EXIT;
+ end;
+ '?', '+', '*': begin
+ Error (reeQPSBFollowsNothing);
+ EXIT;
+ end;
+ EscChar: begin
+ if regparse^ = #0 then begin
+ Error (reeTrailingBackSlash);
+ EXIT;
+ end;
+ case regparse^ of // r.e.extensions
+ 'b': ret := EmitNode (BOUND); //###0.943
+ 'B': ret := EmitNode (NOTBOUND); //###0.943
+ 'A': ret := EmitNode (BOL); //###0.941
+ 'Z': ret := EmitNode (EOL); //###0.941
+ 'd': begin // r.e.extension - any digit ('0' .. '9')
+ ret := EmitNode (ANYDIGIT);
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+ 'D': begin // r.e.extension - not digit ('0' .. '9')
+ ret := EmitNode (NOTDIGIT);
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+ 's': begin // r.e.extension - any space char
+ {$IFDEF UseSetOfChar}
+ ret := EmitRange (ANYOF);
+ EmitRangeStr (SpaceChars);
+ EmitRangeC (#0);
+ {$ELSE}
+ ret := EmitNode (ANYSPACE);
+ {$ENDIF}
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+ 'S': begin // r.e.extension - not space char
+ {$IFDEF UseSetOfChar}
+ ret := EmitRange (ANYBUT);
+ EmitRangeStr (SpaceChars);
+ EmitRangeC (#0);
+ {$ELSE}
+ ret := EmitNode (NOTSPACE);
+ {$ENDIF}
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+ 'w': begin // r.e.extension - any english char / digit / '_'
+ {$IFDEF UseSetOfChar}
+ ret := EmitRange (ANYOF);
+ EmitRangeStr (WordChars);
+ EmitRangeC (#0);
+ {$ELSE}
+ ret := EmitNode (ANYLETTER);
+ {$ENDIF}
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+ 'W': begin // r.e.extension - not english char / digit / '_'
+ {$IFDEF UseSetOfChar}
+ ret := EmitRange (ANYBUT);
+ EmitRangeStr (WordChars);
+ EmitRangeC (#0);
+ {$ELSE}
+ ret := EmitNode (NOTLETTER);
+ {$ENDIF}
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+ '1' .. '9': begin //###0.936
+ if (fCompModifiers and MaskModI) <> 0
+ then ret := EmitNode (BSUBEXPCI)
+ else ret := EmitNode (BSUBEXP);
+ EmitC (REChar (ord (regparse^) - ord ('0')));
+ flagp := flagp or HASWIDTH or SIMPLE;
+ end;
+ else EmitExactly (UnQuoteChar (regparse));
+ end; { of case}
+ inc (regparse);
+ end;
+ else begin
+ dec (regparse);
+ if ((fCompModifiers and MaskModX) <> 0) and // check for eXtended syntax
+ ((regparse^ = '#')
+ or ({$IFDEF UniCode}StrScan (XIgnoredChars, regparse^) <> nil //###0.947
+ {$ELSE}regparse^ in XIgnoredChars{$ENDIF})) then begin //###0.941 \x
+ if regparse^ = '#' then begin // Skip eXtended comment
+ // find comment terminator (group of \n and/or \r)
+ while (regparse^ <> #0) and (regparse^ <> #$d) and (regparse^ <> #$a)
+ do inc (regparse);
+ while (regparse^ = #$d) or (regparse^ = #$a) // skip comment terminator
+ do inc (regparse); // attempt to support different type of line separators
+ end
+ else begin // Skip the blanks!
+ while {$IFDEF UniCode}StrScan (XIgnoredChars, regparse^) <> nil //###0.947
+ {$ELSE}regparse^ in XIgnoredChars{$ENDIF}
+ do inc (regparse);
+ end;
+ ret := EmitNode (COMMENT); // comment
+ end
+ else begin
+ len := strcspn (regparse, META);
+ if len <= 0 then
+ if regparse^ <> '{' then begin
+ Error (reeRarseAtomInternalDisaster);
+ EXIT;
+ end
+ else len := strcspn (regparse + 1, META) + 1; // bad {n,m} - compile as EXATLY
+ ender := (regparse + len)^;
+ if (len > 1)
+ and ((ender = '*') or (ender = '+') or (ender = '?') or (ender = '{'))
+ then dec (len); // Back off clear of ?+*{ operand.
+ flagp := flagp or HASWIDTH;
+ if len = 1
+ then flagp := flagp or SIMPLE;
+ if (fCompModifiers and MaskModI) <> 0
+ then ret := EmitNode (EXACTLYCI)
+ else ret := EmitNode (EXACTLY);
+ while (len > 0)
+ and (((fCompModifiers and MaskModX) = 0) or (regparse^ <> '#')) do begin
+ if ((fCompModifiers and MaskModX) = 0) or not ( //###0.941
+ {$IFDEF UniCode}StrScan (XIgnoredChars, regparse^) <> nil //###0.947
+ {$ELSE}regparse^ in XIgnoredChars{$ENDIF} )
+ then EmitC (regparse^);
+ inc (regparse);
+ dec (len);
+ end;
+ EmitC (#0);
+ end; { of if not comment}
+ end; { of case else}
+ end; { of case}
+
+ Result := ret;
+ end; { of function TRegExpr.ParseAtom
+--------------------------------------------------------------}
+
+function TRegExpr.GetCompilerErrorPos : integer;
+ begin
+ Result := 0;
+ if (regexpbeg = nil) or (regparse = nil)
+ then EXIT; // not in compiling mode ?
+ Result := regparse - regexpbeg;
+ end; { of function TRegExpr.GetCompilerErrorPos
+--------------------------------------------------------------}
+
+
+{=============================================================}
+{===================== Matching section ======================}
+{=============================================================}
+
+{$IFNDEF UseSetOfChar}
+function TRegExpr.StrScanCI (s : PRegExprChar; ch : REChar) : PRegExprChar; //###0.928 - now method of TRegExpr
+ begin
+ while (s^ <> #0) and (s^ <> ch) and (s^ <> InvertCase (ch))
+ do inc (s);
+ if s^ <> #0
+ then Result := s
+ else Result := nil;
+ end; { of function TRegExpr.StrScanCI
+--------------------------------------------------------------}
+{$ENDIF}
+
+function TRegExpr.regrepeat (p : PRegExprChar; AMax : integer) : integer;
+// repeatedly match something simple, report how many
+ var
+ scan : PRegExprChar;
+ opnd : PRegExprChar;
+ TheMax : integer;
+ {Ch,} InvCh : REChar; //###0.931
+ sestart, seend : PRegExprChar; //###0.936
+ begin
+ Result := 0;
+ scan := reginput;
+ opnd := p + REOpSz + RENextOffSz; //OPERAND
+ TheMax := fInputEnd - scan;
+ if TheMax > AMax
+ then TheMax := AMax;
+ case PREOp (p)^ of
+ ANY: begin
+ // note - ANYML cannot be proceeded in regrepeat because can skip
+ // more than one char at once
+ Result := TheMax;
+ inc (scan, Result);
+ end;
+ EXACTLY: begin // in opnd can be only ONE char !!!
+// Ch := opnd^; // store in register //###0.931
+ while (Result < TheMax) and (opnd^ = scan^) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ end;
+ EXACTLYCI: begin // in opnd can be only ONE char !!!
+// Ch := opnd^; // store in register //###0.931
+ while (Result < TheMax) and (opnd^ = scan^) do begin // prevent unneeded InvertCase //###0.931
+ inc (Result);
+ inc (scan);
+ end;
+ if Result < TheMax then begin //###0.931
+ InvCh := InvertCase (opnd^); // store in register
+ while (Result < TheMax) and
+ ((opnd^ = scan^) or (InvCh = scan^)) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ end;
+ end;
+ BSUBEXP: begin //###0.936
+ sestart := startp [ord (opnd^)];
+ if sestart = nil
+ then EXIT;
+ seend := endp [ord (opnd^)];
+ if seend = nil
+ then EXIT;
+ REPEAT
+ opnd := sestart;
+ while opnd < seend do begin
+ if (scan >= fInputEnd) or (scan^ <> opnd^)
+ then EXIT;
+ inc (scan);
+ inc (opnd);
+ end;
+ inc (Result);
+ reginput := scan;
+ UNTIL Result >= AMax;
+ end;
+ BSUBEXPCI: begin //###0.936
+ sestart := startp [ord (opnd^)];
+ if sestart = nil
+ then EXIT;
+ seend := endp [ord (opnd^)];
+ if seend = nil
+ then EXIT;
+ REPEAT
+ opnd := sestart;
+ while opnd < seend do begin
+ if (scan >= fInputEnd) or
+ ((scan^ <> opnd^) and (scan^ <> InvertCase (opnd^)))
+ then EXIT;
+ inc (scan);
+ inc (opnd);
+ end;
+ inc (Result);
+ reginput := scan;
+ UNTIL Result >= AMax;
+ end;
+ ANYDIGIT:
+ while (Result < TheMax) and
+ (scan^ >= '0') and (scan^ <= '9') do begin
+ inc (Result);
+ inc (scan);
+ end;
+ NOTDIGIT:
+ while (Result < TheMax) and
+ ((scan^ < '0') or (scan^ > '9')) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ {$IFNDEF UseSetOfChar} //###0.929
+ ANYLETTER:
+ while (Result < TheMax) and
+ (Pos (scan^, fWordChars) > 0) //###0.940
+ { ((scan^ >= 'a') and (scan^ <= 'z') !! I've forgotten (>='0') and (<='9')
+ or (scan^ >= 'A') and (scan^ <= 'Z') or (scan^ = '_'))} do begin
+ inc (Result);
+ inc (scan);
+ end;
+ NOTLETTER:
+ while (Result < TheMax) and
+ (Pos (scan^, fWordChars) <= 0) //###0.940
+ { not ((scan^ >= 'a') and (scan^ <= 'z') !! I've forgotten (>='0') and (<='9')
+ or (scan^ >= 'A') and (scan^ <= 'Z')
+ or (scan^ = '_'))} do begin
+ inc (Result);
+ inc (scan);
+ end;
+ ANYSPACE:
+ while (Result < TheMax) and
+ (Pos (scan^, fSpaceChars) > 0) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ NOTSPACE:
+ while (Result < TheMax) and
+ (Pos (scan^, fSpaceChars) <= 0) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ {$ENDIF}
+ ANYOFTINYSET: begin
+ while (Result < TheMax) and //!!!TinySet
+ ((scan^ = opnd^) or (scan^ = (opnd + 1)^)
+ or (scan^ = (opnd + 2)^)) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ end;
+ ANYBUTTINYSET: begin
+ while (Result < TheMax) and //!!!TinySet
+ (scan^ <> opnd^) and (scan^ <> (opnd + 1)^)
+ and (scan^ <> (opnd + 2)^) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ end;
+ {$IFDEF UseSetOfChar} //###0.929
+ ANYOFFULLSET: begin
+ while (Result < TheMax) and
+ (scan^ in PSetOfREChar (opnd)^) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ end;
+ {$ELSE}
+ ANYOF:
+ while (Result < TheMax) and
+ (StrScan (opnd, scan^) <> nil) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ ANYBUT:
+ while (Result < TheMax) and
+ (StrScan (opnd, scan^) = nil) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ ANYOFCI:
+ while (Result < TheMax) and (StrScanCI (opnd, scan^) <> nil) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ ANYBUTCI:
+ while (Result < TheMax) and (StrScanCI (opnd, scan^) = nil) do begin
+ inc (Result);
+ inc (scan);
+ end;
+ {$ENDIF}
+ else begin // Oh dear. Called inappropriately.
+ Result := 0; // Best compromise.
+ Error (reeRegRepeatCalledInappropriately);
+ EXIT;
+ end;
+ end; { of case}
+ reginput := scan;
+ end; { of function TRegExpr.regrepeat
+--------------------------------------------------------------}
+
+function TRegExpr.regnext (p : PRegExprChar) : PRegExprChar;
+// dig the "next" pointer out of a node
+ var offset : TRENextOff;
+ begin
+ if p = @regdummy then begin
+ Result := nil;
+ EXIT;
+ end;
+ offset := PRENextOff (p + REOpSz)^; //###0.933 inlined NEXT
+ if offset = 0
+ then Result := nil
+ else Result := p + offset;
+ end; { of function TRegExpr.regnext
+--------------------------------------------------------------}
+
+function TRegExpr.MatchPrim (prog : PRegExprChar) : boolean;
+// recursively matching routine
+// Conceptually the strategy is simple: check to see whether the current
+// node matches, call self recursively to see whether the rest matches,
+// and then act accordingly. In practice we make some effort to avoid
+// recursion, in particular by going through "ordinary" nodes (that don't
+// need to know whether the rest of the match failed) by a loop instead of
+// by recursion.
+ var
+ scan : PRegExprChar; // Current node.
+ next : PRegExprChar; // Next node.
+ len : integer;
+ opnd : PRegExprChar;
+ no : integer;
+ save : PRegExprChar;
+ nextch : REChar;
+ BracesMin, BracesMax : integer; // we use integer instead of TREBracesArg for better support */+
+ {$IFDEF ComplexBraces}
+ SavedLoopStack : array [1 .. LoopStackMax] of integer; // :(( very bad for recursion
+ SavedLoopStackIdx : integer; //###0.925
+ {$ENDIF}
+ begin
+ Result := false;
+ scan := prog;
+
+ while scan <> nil do begin
+ len := PRENextOff (scan + 1)^; //###0.932 inlined regnext
+ if len = 0
+ then next := nil
+ else next := scan + len;
+
+ case scan^ of
+ NOTBOUND, //###0.943 //!!! think about UseSetOfChar !!!
+ BOUND:
+ if (scan^ = BOUND)
+ xor (
+ ((reginput = fInputStart) or (Pos ((reginput - 1)^, fWordChars) <= 0))
+ and (reginput^ <> #0) and (Pos (reginput^, fWordChars) > 0)
+ or
+ (reginput <> fInputStart) and (Pos ((reginput - 1)^, fWordChars) > 0)
+ and ((reginput^ = #0) or (Pos (reginput^, fWordChars) <= 0)))
+ then EXIT;
+
+ BOL: if reginput <> fInputStart
+ then EXIT;
+ EOL: if reginput^ <> #0
+ then EXIT;
+ BOLML: if reginput > fInputStart then begin
+ nextch := (reginput - 1)^;
+ if (nextch <> fLinePairedSeparatorTail)
+ or ((reginput - 1) <= fInputStart)
+ or ((reginput - 2)^ <> fLinePairedSeparatorHead)
+ then begin
+ if (nextch = fLinePairedSeparatorHead)
+ and (reginput^ = fLinePairedSeparatorTail)
+ then EXIT; // don't stop between paired separator
+ if
+ {$IFNDEF UniCode}
+ not (nextch in fLineSeparatorsSet)
+ {$ELSE}
+ (pos (nextch, fLineSeparators) <= 0)
+ {$ENDIF}
+ then EXIT;
+ end;
+ end;
+ EOLML: if reginput^ <> #0 then begin
+ nextch := reginput^;
+ if (nextch <> fLinePairedSeparatorHead)
+ or ((reginput + 1)^ <> fLinePairedSeparatorTail)
+ then begin
+ if (nextch = fLinePairedSeparatorTail)
+ and (reginput > fInputStart)
+ and ((reginput - 1)^ = fLinePairedSeparatorHead)
+ then EXIT; // don't stop between paired separator
+ if
+ {$IFNDEF UniCode}
+ not (nextch in fLineSeparatorsSet)
+ {$ELSE}
+ (pos (nextch, fLineSeparators) <= 0)
+ {$ENDIF}
+ then EXIT;
+ end;
+ end;
+ ANY: begin
+ if reginput^ = #0
+ then EXIT;
+ inc (reginput);
+ end;
+ ANYML: begin //###0.941
+ if (reginput^ = #0)
+ or ((reginput^ = fLinePairedSeparatorHead)
+ and ((reginput + 1)^ = fLinePairedSeparatorTail))
+ or {$IFNDEF UniCode} (reginput^ in fLineSeparatorsSet)
+ {$ELSE} (pos (reginput^, fLineSeparators) > 0) {$ENDIF}
+ then EXIT;
+ inc (reginput);
+ end;
+ ANYDIGIT: begin
+ if (reginput^ = #0) or (reginput^ < '0') or (reginput^ > '9')
+ then EXIT;
+ inc (reginput);
+ end;
+ NOTDIGIT: begin
+ if (reginput^ = #0) or ((reginput^ >= '0') and (reginput^ <= '9'))
+ then EXIT;
+ inc (reginput);
+ end;
+ {$IFNDEF UseSetOfChar} //###0.929
+ ANYLETTER: begin
+ if (reginput^ = #0) or (Pos (reginput^, fWordChars) <= 0) //###0.943
+ then EXIT;
+ inc (reginput);
+ end;
+ NOTLETTER: begin
+ if (reginput^ = #0) or (Pos (reginput^, fWordChars) > 0) //###0.943
+ then EXIT;
+ inc (reginput);
+ end;
+ ANYSPACE: begin
+ if (reginput^ = #0) or not (Pos (reginput^, fSpaceChars) > 0) //###0.943
+ then EXIT;
+ inc (reginput);
+ end;
+ NOTSPACE: begin
+ if (reginput^ = #0) or (Pos (reginput^, fSpaceChars) > 0) //###0.943
+ then EXIT;
+ inc (reginput);
+ end;
+ {$ENDIF}
+ EXACTLYCI: begin
+ opnd := scan + REOpSz + RENextOffSz; // OPERAND
+ // Inline the first character, for speed.
+ if (opnd^ <> reginput^)
+ and (InvertCase (opnd^) <> reginput^)
+ then EXIT;
+ len := strlen (opnd);
+ //###0.929 begin
+ no := len;
+ save := reginput;
+ while no > 1 do begin
+ inc (save);
+ inc (opnd);
+ if (opnd^ <> save^)
+ and (InvertCase (opnd^) <> save^)
+ then EXIT;
+ dec (no);
+ end;
+ //###0.929 end
+ inc (reginput, len);
+ end;
+ EXACTLY: begin
+ opnd := scan + REOpSz + RENextOffSz; // OPERAND
+ // Inline the first character, for speed.
+ if opnd^ <> reginput^
+ then EXIT;
+ len := strlen (opnd);
+ //###0.929 begin
+ no := len;
+ save := reginput;
+ while no > 1 do begin
+ inc (save);
+ inc (opnd);
+ if opnd^ <> save^
+ then EXIT;
+ dec (no);
+ end;
+ //###0.929 end
+ inc (reginput, len);
+ end;
+ BSUBEXP: begin //###0.936
+ no := ord ((scan + REOpSz + RENextOffSz)^);
+ if startp [no] = nil
+ then EXIT;
+ if endp [no] = nil
+ then EXIT;
+ save := reginput;
+ opnd := startp [no];
+ while opnd < endp [no] do begin
+ if (save >= fInputEnd) or (save^ <> opnd^)
+ then EXIT;
+ inc (save);
+ inc (opnd);
+ end;
+ reginput := save;
+ end;
+ BSUBEXPCI: begin //###0.936
+ no := ord ((scan + REOpSz + RENextOffSz)^);
+ if startp [no] = nil
+ then EXIT;
+ if endp [no] = nil
+ then EXIT;
+ save := reginput;
+ opnd := startp [no];
+ while opnd < endp [no] do begin
+ if (save >= fInputEnd) or
+ ((save^ <> opnd^) and (save^ <> InvertCase (opnd^)))
+ then EXIT;
+ inc (save);
+ inc (opnd);
+ end;
+ reginput := save;
+ end;
+ ANYOFTINYSET: begin
+ if (reginput^ = #0) or //!!!TinySet
+ ((reginput^ <> (scan + REOpSz + RENextOffSz)^)
+ and (reginput^ <> (scan + REOpSz + RENextOffSz + 1)^)
+ and (reginput^ <> (scan + REOpSz + RENextOffSz + 2)^))
+ then EXIT;
+ inc (reginput);
+ end;
+ ANYBUTTINYSET: begin
+ if (reginput^ = #0) or //!!!TinySet
+ (reginput^ = (scan + REOpSz + RENextOffSz)^)
+ or (reginput^ = (scan + REOpSz + RENextOffSz + 1)^)
+ or (reginput^ = (scan + REOpSz + RENextOffSz + 2)^)
+ then EXIT;
+ inc (reginput);
+ end;
+ {$IFDEF UseSetOfChar} //###0.929
+ ANYOFFULLSET: begin
+ if (reginput^ = #0)
+ or not (reginput^ in PSetOfREChar (scan + REOpSz + RENextOffSz)^)
+ then EXIT;
+ inc (reginput);
+ end;
+ {$ELSE}
+ ANYOF: begin
+ if (reginput^ = #0) or (StrScan (scan + REOpSz + RENextOffSz, reginput^) = nil)
+ then EXIT;
+ inc (reginput);
+ end;
+ ANYBUT: begin
+ if (reginput^ = #0) or (StrScan (scan + REOpSz + RENextOffSz, reginput^) <> nil)
+ then EXIT;
+ inc (reginput);
+ end;
+ ANYOFCI: begin
+ if (reginput^ = #0) or (StrScanCI (scan + REOpSz + RENextOffSz, reginput^) = nil)
+ then EXIT;
+ inc (reginput);
+ end;
+ ANYBUTCI: begin
+ if (reginput^ = #0) or (StrScanCI (scan + REOpSz + RENextOffSz, reginput^) <> nil)
+ then EXIT;
+ inc (reginput);
+ end;
+ {$ENDIF}
+ NOTHING: ;
+ COMMENT: ;
+ BACK: ;
+ Succ (OPEN) .. TREOp (Ord (OPEN) + NSUBEXP - 1) : begin //###0.929
+ no := ord (scan^) - ord (OPEN);
+// save := reginput;
+ save := startp [no]; //###0.936
+ startp [no] := reginput; //###0.936
+ Result := MatchPrim (next);
+ if not Result //###0.936
+ then startp [no] := save;
+// if Result and (startp [no] = nil)
+// then startp [no] := save;
+ // Don't set startp if some later invocation of the same
+ // parentheses already has.
+ EXIT;
+ end;
+ Succ (CLOSE) .. TREOp (Ord (CLOSE) + NSUBEXP - 1): begin //###0.929
+ no := ord (scan^) - ord (CLOSE);
+// save := reginput;
+ save := endp [no]; //###0.936
+ endp [no] := reginput; //###0.936
+ Result := MatchPrim (next);
+ if not Result //###0.936
+ then endp [no] := save;
+// if Result and (endp [no] = nil)
+// then endp [no] := save;
+ // Don't set endp if some later invocation of the same
+ // parentheses already has.
+ EXIT;
+ end;
+ BRANCH: begin
+ if (next^ <> BRANCH) // No choice.
+ then next := scan + REOpSz + RENextOffSz // Avoid recursion
+ else begin
+ REPEAT
+ save := reginput;
+ Result := MatchPrim (scan + REOpSz + RENextOffSz);
+ if Result
+ then EXIT;
+ reginput := save;
+ scan := regnext (scan);
+ UNTIL (scan = nil) or (scan^ <> BRANCH);
+ EXIT;
+ end;
+ end;
+ {$IFDEF ComplexBraces}
+ LOOPENTRY: begin //###0.925
+ no := LoopStackIdx;
+ inc (LoopStackIdx);
+ if LoopStackIdx > LoopStackMax then begin
+ Error (reeLoopStackExceeded);
+ EXIT;
+ end;
+ save := reginput;
+ LoopStack [LoopStackIdx] := 0; // init loop counter
+ Result := MatchPrim (next); // execute LOOP
+ LoopStackIdx := no; // cleanup
+ if Result
+ then EXIT;
+ reginput := save;
+ EXIT;
+ end;
+ LOOP, LOOPNG: begin //###0.940
+ if LoopStackIdx <= 0 then begin
+ Error (reeLoopWithoutEntry);
+ EXIT;
+ end;
+ opnd := scan + PRENextOff (scan + REOpSz + RENextOffSz + 2 * REBracesArgSz)^;
+ BracesMin := PREBracesArg (scan + REOpSz + RENextOffSz)^;
+ BracesMax := PREBracesArg (scan + REOpSz + RENextOffSz + REBracesArgSz)^;
+ save := reginput;
+ if LoopStack [LoopStackIdx] >= BracesMin then begin // Min alredy matched - we can work
+ if scan^ = LOOP then begin
+ // greedy way - first try to max deep of greed ;)
+ if LoopStack [LoopStackIdx] < BracesMax then begin
+ inc (LoopStack [LoopStackIdx]);
+ no := LoopStackIdx;
+ Result := MatchPrim (opnd);
+ LoopStackIdx := no;
+ if Result
+ then EXIT;
+ reginput := save;
+ end;
+ dec (LoopStackIdx); // Fail. May be we are too greedy? ;)
+ Result := MatchPrim (next);
+ if not Result
+ then reginput := save;
+ EXIT;
+ end
+ else begin
+ // non-greedy - try just now
+ Result := MatchPrim (next);
+ if Result
+ then EXIT
+ else reginput := save; // failed - move next and try again
+ if LoopStack [LoopStackIdx] < BracesMax then begin
+ inc (LoopStack [LoopStackIdx]);
+ no := LoopStackIdx;
+ Result := MatchPrim (opnd);
+ LoopStackIdx := no;
+ if Result
+ then EXIT;
+ reginput := save;
+ end;
+ dec (LoopStackIdx); // Failed - back up
+ EXIT;
+ end
+ end
+ else begin // first match a min_cnt times
+ inc (LoopStack [LoopStackIdx]);
+ no := LoopStackIdx;
+ Result := MatchPrim (opnd);
+ LoopStackIdx := no;
+ if Result
+ then EXIT;
+ dec (LoopStack [LoopStackIdx]);
+ reginput := save;
+ EXIT;
+ end;
+ end;
+ {$ENDIF}
+ STAR, PLUS, BRACES, STARNG, PLUSNG, BRACESNG: begin
+ // Lookahead to avoid useless match attempts when we know
+ // what character comes next.
+ nextch := #0;
+ if next^ = EXACTLY
+ then nextch := (next + REOpSz + RENextOffSz)^;
+ BracesMax := MaxInt; // infinite loop for * and + //###0.92
+ if (scan^ = STAR) or (scan^ = STARNG)
+ then BracesMin := 0 // STAR
+ else if (scan^ = PLUS) or (scan^ = PLUSNG)
+ then BracesMin := 1 // PLUS
+ else begin // BRACES
+ BracesMin := PREBracesArg (scan + REOpSz + RENextOffSz)^;
+ BracesMax := PREBracesArg (scan + REOpSz + RENextOffSz + REBracesArgSz)^;
+ end;
+ save := reginput;
+ opnd := scan + REOpSz + RENextOffSz;
+ if (scan^ = BRACES) or (scan^ = BRACESNG)
+ then inc (opnd, 2 * REBracesArgSz);
+
+ if (scan^ = PLUSNG) or (scan^ = STARNG) or (scan^ = BRACESNG) then begin
+ // non-greedy mode
+ BracesMax := regrepeat (opnd, BracesMax); // don't repeat more than BracesMax
+ // Now we know real Max limit to move forward (for recursion 'back up')
+ // In some cases it can be faster to check only Min positions first,
+ // but after that we have to check every position separtely instead
+ // of fast scannig in loop.
+ no := BracesMin;
+ while no <= BracesMax do begin
+ reginput := save + no;
+ // If it could work, try it.
+ if (nextch = #0) or (reginput^ = nextch) then begin
+ {$IFDEF ComplexBraces}
+ System.Move (LoopStack, SavedLoopStack, SizeOf (LoopStack)); //###0.925
+ SavedLoopStackIdx := LoopStackIdx;
+ {$ENDIF}
+ if MatchPrim (next) then begin
+ Result := true;
+ EXIT;
+ end;
+ {$IFDEF ComplexBraces}
+ System.Move (SavedLoopStack, LoopStack, SizeOf (LoopStack));
+ LoopStackIdx := SavedLoopStackIdx;
+ {$ENDIF}
+ end;
+ inc (no); // Couldn't or didn't - move forward.
+ end; { of while}
+ EXIT;
+ end
+ else begin // greedy mode
+ no := regrepeat (opnd, BracesMax); // don't repeat more than max_cnt
+ while no >= BracesMin do begin
+ // If it could work, try it.
+ if (nextch = #0) or (reginput^ = nextch) then begin
+ {$IFDEF ComplexBraces}
+ System.Move (LoopStack, SavedLoopStack, SizeOf (LoopStack)); //###0.925
+ SavedLoopStackIdx := LoopStackIdx;
+ {$ENDIF}
+ if MatchPrim (next) then begin
+ Result := true;
+ EXIT;
+ end;
+ {$IFDEF ComplexBraces}
+ System.Move (SavedLoopStack, LoopStack, SizeOf (LoopStack));
+ LoopStackIdx := SavedLoopStackIdx;
+ {$ENDIF}
+ end;
+ dec (no); // Couldn't or didn't - back up.
+ reginput := save + no;
+ end; { of while}
+ EXIT;
+ end;
+ end;
+ EEND: begin
+ Result := true; // Success!
+ EXIT;
+ end;
+ else begin
+ Error (reeMatchPrimMemoryCorruption);
+ EXIT;
+ end;
+ end; { of case scan^}
+ scan := next;
+ end; { of while scan <> nil}
+
+ // We get here only if there's trouble -- normally "case EEND" is the
+ // terminating point.
+ Error (reeMatchPrimCorruptedPointers);
+ end; { of function TRegExpr.MatchPrim
+--------------------------------------------------------------}
+
+{$IFDEF UseFirstCharSet} //###0.929
+procedure TRegExpr.FillFirstCharSet (prog : PRegExprChar);
+ var
+ scan : PRegExprChar; // Current node.
+ next : PRegExprChar; // Next node.
+ opnd : PRegExprChar;
+ min_cnt : integer;
+ begin
+ scan := prog;
+ while scan <> nil do begin
+ next := regnext (scan);
+ case PREOp (scan)^ of
+ BSUBEXP, BSUBEXPCI: begin //###0.938
+ FirstCharSet := [#0 .. #255]; // :((( we cannot
+ // optimize r.e. if it starts with back reference
+ EXIT;
+ end;
+ BOL, BOLML: ; // EXIT; //###0.937
+ EOL, EOLML: begin //###0.948 was empty in 0.947, was EXIT in 0.937
+ Include (FirstCharSet, #0);
+ if ModifierM
+ then begin
+ opnd := PRegExprChar (LineSeparators);
+ while opnd^ <> #0 do begin
+ Include (FirstCharSet, opnd^);
+ inc (opnd);
+ end;
+ end;
+ EXIT;
+ end;
+ BOUND, NOTBOUND: ; //###0.943 ?!!
+ ANY, ANYML: begin // we can better define ANYML !!!
+ FirstCharSet := [#0 .. #255]; //###0.930
+ EXIT;
+ end;
+ ANYDIGIT: begin
+ FirstCharSet := FirstCharSet + ['0' .. '9'];
+ EXIT;
+ end;
+ NOTDIGIT: begin
+ FirstCharSet := FirstCharSet + ([#0 .. #255] - ['0' .. '9']); //###0.948 FirstCharSet was forgotten
+ EXIT;
+ end;
+ EXACTLYCI: begin
+ Include (FirstCharSet, (scan + REOpSz + RENextOffSz)^);
+ Include (FirstCharSet, InvertCase ((scan + REOpSz + RENextOffSz)^));
+ EXIT;
+ end;
+ EXACTLY: begin
+ Include (FirstCharSet, (scan + REOpSz + RENextOffSz)^);
+ EXIT;
+ end;
+ ANYOFFULLSET: begin
+ FirstCharSet := FirstCharSet + PSetOfREChar (scan + REOpSz + RENextOffSz)^;
+ EXIT;
+ end;
+ ANYOFTINYSET: begin
+ //!!!TinySet
+ Include (FirstCharSet, (scan + REOpSz + RENextOffSz)^);
+ Include (FirstCharSet, (scan + REOpSz + RENextOffSz + 1)^);
+ Include (FirstCharSet, (scan + REOpSz + RENextOffSz + 2)^);
+ // ... // up to TinySetLen
+ EXIT;
+ end;
+ ANYBUTTINYSET: begin
+ //!!!TinySet
+ FirstCharSet := FirstCharSet + ([#0 .. #255] - [ //###0.948 FirstCharSet was forgotten
+ (scan + REOpSz + RENextOffSz)^,
+ (scan + REOpSz + RENextOffSz + 1)^,
+ (scan + REOpSz + RENextOffSz + 2)^]);
+ // ... // up to TinySetLen
+ EXIT;
+ end;
+ NOTHING: ;
+ COMMENT: ;
+ BACK: ;
+ Succ (OPEN) .. TREOp (Ord (OPEN) + NSUBEXP - 1) : begin //###0.929
+ FillFirstCharSet (next);
+ EXIT;
+ end;
+ Succ (CLOSE) .. TREOp (Ord (CLOSE) + NSUBEXP - 1): begin //###0.929
+ FillFirstCharSet (next);
+ EXIT;
+ end;
+ BRANCH: begin
+ if (PREOp (next)^ <> BRANCH) // No choice.
+ then next := scan + REOpSz + RENextOffSz // Avoid recursion.
+ else begin
+ REPEAT
+ FillFirstCharSet (scan + REOpSz + RENextOffSz);
+ scan := regnext (scan);
+ UNTIL (scan = nil) or (PREOp (scan)^ <> BRANCH);
+ EXIT;
+ end;
+ end;
+ {$IFDEF ComplexBraces}
+ LOOPENTRY: begin //###0.925
+// LoopStack [LoopStackIdx] := 0; //###0.940 line removed
+ FillFirstCharSet (next); // execute LOOP
+ EXIT;
+ end;
+ LOOP, LOOPNG: begin //###0.940
+ opnd := scan + PRENextOff (scan + REOpSz + RENextOffSz + REBracesArgSz * 2)^;
+ min_cnt := PREBracesArg (scan + REOpSz + RENextOffSz)^;
+ FillFirstCharSet (opnd);
+ if min_cnt = 0
+ then FillFirstCharSet (next);
+ EXIT;
+ end;
+ {$ENDIF}
+ STAR, STARNG: //###0.940
+ FillFirstCharSet (scan + REOpSz + RENextOffSz);
+ PLUS, PLUSNG: begin //###0.940
+ FillFirstCharSet (scan + REOpSz + RENextOffSz);
+ EXIT;
+ end;
+ BRACES, BRACESNG: begin //###0.940
+ opnd := scan + REOpSz + RENextOffSz + REBracesArgSz * 2;
+ min_cnt := PREBracesArg (scan + REOpSz + RENextOffSz)^; // BRACES
+ FillFirstCharSet (opnd);
+ if min_cnt > 0
+ then EXIT;
+ end;
+ EEND: begin
+ FirstCharSet := [#0 .. #255]; //###0.948
+ EXIT;
+ end;
+ else begin
+ Error (reeMatchPrimMemoryCorruption);
+ EXIT;
+ end;
+ end; { of case scan^}
+ scan := next;
+ end; { of while scan <> nil}
+ end; { of procedure FillFirstCharSet
+--------------------------------------------------------------}
+{$ENDIF}
+
+function TRegExpr.Exec (const AInputString : RegExprString) : boolean;
+ begin
+ InputString := AInputString;
+ Result := ExecPrim (1);
+ end; { of function TRegExpr.Exec
+--------------------------------------------------------------}
+
+{$IFDEF OverMeth}
+{$IFNDEF FPC}
+function TRegExpr.Exec : boolean;
+ begin
+ Result := ExecPrim (1);
+ end; { of function TRegExpr.Exec
+--------------------------------------------------------------}
+{$ENDIF}
+function TRegExpr.Exec (AOffset: integer) : boolean;
+ begin
+ Result := ExecPrim (AOffset);
+ end; { of function TRegExpr.Exec
+--------------------------------------------------------------}
+{$ENDIF}
+
+function TRegExpr.ExecPos (AOffset: integer {$IFDEF DefParam}= 1{$ENDIF}) : boolean;
+ begin
+ Result := ExecPrim (AOffset);
+ end; { of function TRegExpr.ExecPos
+--------------------------------------------------------------}
+
+function TRegExpr.ExecPrim (AOffset: integer) : boolean;
+ procedure ClearMatchs;
+ // Clears matchs array
+ var i : integer;
+ begin
+ for i := 0 to NSUBEXP - 1 do begin
+ startp [i] := nil;
+ endp [i] := nil;
+ end;
+ end; { of procedure ClearMatchs;
+..............................................................}
+ function RegMatch (str : PRegExprChar) : boolean;
+ // try match at specific point
+ begin
+ //###0.949 removed clearing of start\endp
+ reginput := str;
+ Result := MatchPrim (programm + REOpSz);
+ if Result then begin
+ startp [0] := str;
+ endp [0] := reginput;
+ end;
+ end; { of function RegMatch
+..............................................................}
+ var
+ s : PRegExprChar;
+ StartPtr: PRegExprChar;
+ InputLen : integer;
+ begin
+ Result := false; // Be paranoid...
+
+ ClearMatchs; //###0.949
+ // ensure that Match cleared either if optimization tricks or some error
+ // will lead to leaving ExecPrim without actual search. That is
+ // importent for ExecNext logic and so on.
+
+ if not IsProgrammOk //###0.929
+ then EXIT;
+
+ // Check InputString presence
+ if not Assigned (fInputString) then begin
+ Error (reeNoInpitStringSpecified);
+ EXIT;
+ end;
+
+ InputLen := length (fInputString);
+
+ //Check that the start position is not negative
+ if AOffset < 1 then begin
+ Error (reeOffsetMustBeGreaterThen0);
+ EXIT;
+ end;
+ // Check that the start position is not longer than the line
+ // If so then exit with nothing found
+ if AOffset > (InputLen + 1) // for matching empty string after last char.
+ then EXIT;
+
+ StartPtr := fInputString + AOffset - 1;
+
+ // If there is a "must appear" string, look for it.
+ if regmust <> nil then begin
+ s := StartPtr;
+ REPEAT
+ s := StrScan (s, regmust [0]);
+ if s <> nil then begin
+ if StrLComp (s, regmust, regmlen) = 0
+ then BREAK; // Found it.
+ inc (s);
+ end;
+ UNTIL s = nil;
+ if s = nil // Not present.
+ then EXIT;
+ end;
+
+ // Mark beginning of line for ^ .
+ fInputStart := fInputString;
+
+ // Pointer to end of input stream - for
+ // pascal-style string processing (may include #0)
+ fInputEnd := fInputString + InputLen;
+
+ {$IFDEF ComplexBraces}
+ // no loops started
+ LoopStackIdx := 0; //###0.925
+ {$ENDIF}
+
+ // Simplest case: anchored match need be tried only once.
+ if reganch <> #0 then begin
+ Result := RegMatch (StartPtr);
+ EXIT;
+ end;
+
+ // Messy cases: unanchored match.
+ s := StartPtr;
+ if regstart <> #0 then // We know what char it must start with.
+ REPEAT
+ s := StrScan (s, regstart);
+ if s <> nil then begin
+ Result := RegMatch (s);
+ if Result
+ then EXIT
+ else ClearMatchs; //###0.949
+ inc (s);
+ end;
+ UNTIL s = nil
+ else begin // We don't - general case.
+ repeat //###0.948
+ {$IFDEF UseFirstCharSet}
+ if s^ in FirstCharSet
+ then Result := RegMatch (s);
+ {$ELSE}
+ Result := RegMatch (s);
+ {$ENDIF}
+ if Result or (s^ = #0) // Exit on a match or after testing the end-of-string.
+ then EXIT
+ else ClearMatchs; //###0.949
+ inc (s);
+ until false;
+(* optimized and fixed by Martin Fuller - empty strings
+ were not allowed to pass thru in UseFirstCharSet mode
+ {$IFDEF UseFirstCharSet} //###0.929
+ while s^ <> #0 do begin
+ if s^ in FirstCharSet
+ then Result := RegMatch (s);
+ if Result
+ then EXIT;
+ inc (s);
+ end;
+ {$ELSE}
+ REPEAT
+ Result := RegMatch (s);
+ if Result
+ then EXIT;
+ inc (s);
+ UNTIL s^ = #0;
+ {$ENDIF}
+*)
+ end;
+ // Failure
+ end; { of function TRegExpr.ExecPrim
+--------------------------------------------------------------}
+
+function TRegExpr.ExecNext : boolean;
+ var offset : integer;
+ begin
+ Result := false;
+ if not Assigned (startp[0]) or not Assigned (endp[0]) then begin
+ Error (reeExecNextWithoutExec);
+ EXIT;
+ end;
+// Offset := MatchPos [0] + MatchLen [0];
+// if MatchLen [0] = 0
+ Offset := endp [0] - fInputString + 1; //###0.929
+ if endp [0] = startp [0] //###0.929
+ then inc (Offset); // prevent infinite looping if empty string match r.e.
+ Result := ExecPrim (Offset);
+ end; { of function TRegExpr.ExecNext
+--------------------------------------------------------------}
+
+function TRegExpr.GetInputString : RegExprString;
+ begin
+ if not Assigned (fInputString) then begin
+ Error (reeGetInputStringWithoutInputString);
+ EXIT;
+ end;
+ Result := fInputString;
+ end; { of function TRegExpr.GetInputString
+--------------------------------------------------------------}
+
+procedure TRegExpr.SetInputString (const AInputString : RegExprString);
+ var
+ Len : integer;
+ i : integer;
+ begin
+ // clear Match* - before next Exec* call it's undefined
+ for i := 0 to NSUBEXP - 1 do begin
+ startp [i] := nil;
+ endp [i] := nil;
+ end;
+
+ // need reallocation of input string buffer ?
+ Len := length (AInputString);
+ if Assigned (fInputString) and (Length (fInputString) <> Len) then begin
+ FreeMem (fInputString);
+ fInputString := nil;
+ end;
+ // buffer [re]allocation
+ if not Assigned (fInputString)
+ then GetMem (fInputString, (Len + 1) * SizeOf (REChar));
+
+ // copy input string into buffer
+ {$IFDEF UniCode}
+ StrPCopy (fInputString, Copy (AInputString, 1, Len)); //###0.927
+ {$ELSE}
+ StrLCopy (fInputString, PRegExprChar (AInputString), Len);
+ {$ENDIF}
+
+ {
+ fInputString : string;
+ fInputStart, fInputEnd : PRegExprChar;
+
+ SetInputString:
+ fInputString := AInputString;
+ UniqueString (fInputString);
+ fInputStart := PChar (fInputString);
+ Len := length (fInputString);
+ fInputEnd := PRegExprChar (integer (fInputStart) + Len); ??
+ !! startp/endp все равно будет опасно использовать ?
+ }
+ end; { of procedure TRegExpr.SetInputString
+--------------------------------------------------------------}
+
+procedure TRegExpr.SetLineSeparators (const AStr : RegExprString);
+ begin
+ if AStr <> fLineSeparators then begin
+ fLineSeparators := AStr;
+ InvalidateProgramm;
+ end;
+ end; { of procedure TRegExpr.SetLineSeparators
+--------------------------------------------------------------}
+
+procedure TRegExpr.SetLinePairedSeparator (const AStr : RegExprString);
+ begin
+ if length (AStr) = 2 then begin
+ if AStr [1] = AStr [2] then begin
+ // it's impossible for our 'one-point' checking to support
+ // two chars separator for identical chars
+ Error (reeBadLinePairedSeparator);
+ EXIT;
+ end;
+ if not fLinePairedSeparatorAssigned
+ or (AStr [1] <> fLinePairedSeparatorHead)
+ or (AStr [2] <> fLinePairedSeparatorTail) then begin
+ fLinePairedSeparatorAssigned := true;
+ fLinePairedSeparatorHead := AStr [1];
+ fLinePairedSeparatorTail := AStr [2];
+ InvalidateProgramm;
+ end;
+ end
+ else if length (AStr) = 0 then begin
+ if fLinePairedSeparatorAssigned then begin
+ fLinePairedSeparatorAssigned := false;
+ InvalidateProgramm;
+ end;
+ end
+ else Error (reeBadLinePairedSeparator);
+ end; { of procedure TRegExpr.SetLinePairedSeparator
+--------------------------------------------------------------}
+
+function TRegExpr.GetLinePairedSeparator : RegExprString;
+ begin
+ if fLinePairedSeparatorAssigned then begin
+ {$IFDEF UniCode}
+ // Here is some UniCode 'magic'
+ // If You do know better decision to concatenate
+ // two WideChars, please, let me know!
+ Result := fLinePairedSeparatorHead; //###0.947
+ Result := Result + fLinePairedSeparatorTail;
+ {$ELSE}
+ Result := fLinePairedSeparatorHead + fLinePairedSeparatorTail;
+ {$ENDIF}
+ end
+ else Result := '';
+ end; { of function TRegExpr.GetLinePairedSeparator
+--------------------------------------------------------------}
+
+function TRegExpr.Substitute (const ATemplate : RegExprString) : RegExprString;
+// perform substitutions after a regexp match
+// completely rewritten in 0.929
+type
+ TSubstMode = (smodeNormal, smodeOneUpper, smodeOneLower, smodeAllUpper,
+ smodeAllLower);
+var
+ TemplateLen : integer;
+ TemplateBeg, TemplateEnd : PRegExprChar;
+ p, p0, p1, ResultPtr : PRegExprChar;
+ ResultLen : integer;
+ n : integer;
+ Ch : REChar;
+ Mode: TSubstMode;
+ LineEnd: String = LineEnding;
+
+ function ParseVarName (var APtr : PRegExprChar) : integer;
+ // extract name of variable (digits, may be enclosed with
+ // curly braces) from APtr^, uses TemplateEnd !!!
+ const
+ Digits = ['0' .. '9'];
+ var
+ p : PRegExprChar;
+ Delimited : boolean;
+ begin
+ Result := 0;
+ p := APtr;
+ Delimited := (p < TemplateEnd) and (p^ = '{');
+ if Delimited
+ then inc (p); // skip left curly brace
+ if (p < TemplateEnd) and (p^ = '&')
+ then inc (p) // this is '$&' or '${&}'
+ else
+ while (p < TemplateEnd) and
+ {$IFDEF UniCode} //###0.935
+ (ord (p^) < 256) and (char (p^) in Digits)
+ {$ELSE}
+ (p^ in Digits)
+ {$ENDIF}
+ do begin
+ Result := Result * 10 + (ord (p^) - ord ('0')); //###0.939
+ inc (p);
+ end;
+ if Delimited then
+ if (p < TemplateEnd) and (p^ = '}')
+ then inc (p) // skip right curly brace
+ else p := APtr; // isn't properly terminated
+ if p = APtr
+ then Result := -1; // no valid digits found or no right curly brace
+ APtr := p;
+ end;
+
+begin
+ // Check programm and input string
+ if not IsProgrammOk
+ then EXIT;
+ if not Assigned (fInputString) then begin
+ Error (reeNoInpitStringSpecified);
+ EXIT;
+ end;
+ // Prepare for working
+ TemplateLen := length (ATemplate);
+ if TemplateLen = 0 then begin // prevent nil pointers
+ Result := '';
+ EXIT;
+ end;
+ TemplateBeg := pointer (ATemplate);
+ TemplateEnd := TemplateBeg + TemplateLen;
+ // Count result length for speed optimization.
+ ResultLen := 0;
+ p := TemplateBeg;
+ while p < TemplateEnd do begin
+ Ch := p^;
+ inc (p);
+ if Ch = '$'
+ then n := ParseVarName (p)
+ else n := -1;
+ if n >= 0 then begin
+ if (n < NSUBEXP) and Assigned (startp [n]) and Assigned (endp [n])
+ then inc (ResultLen, endp [n] - startp [n]);
+ end
+ else begin
+ if (Ch = EscChar) and (p < TemplateEnd) then begin // quoted or special char followed
+ Ch := p^;
+ inc (p);
+ case Ch of
+ 'n' : inc(ResultLen, Length(LineEnding));
+ 'u', 'l', 'U', 'L': {nothing};
+ else inc(ResultLen);
+ end;
+ end
+ else
+ inc(ResultLen);
+ end;
+ end;
+ // Get memory. We do it once and it significant speed up work !
+ if ResultLen = 0 then begin
+ Result := '';
+ EXIT;
+ end;
+ SetString (Result, nil, ResultLen);
+ // Fill Result
+ ResultPtr := pointer (Result);
+ p := TemplateBeg;
+ Mode := smodeNormal;
+ while p < TemplateEnd do begin
+ Ch := p^;
+ p0 := p;
+ inc (p);
+ p1 := p;
+ if Ch = '$'
+ then n := ParseVarName (p)
+ else n := -1;
+ if (n >= 0) then begin
+ p0 := startp[n];
+ p1 := endp[n];
+ if (n >= NSUBEXP) or not Assigned (p0) or not Assigned (endp [n]) then
+ p1 := p0; // empty
+ end
+ else begin
+ if (Ch = EscChar) and (p < TemplateEnd) then begin // quoted or special char followed
+ Ch := p^;
+ inc (p);
+ case Ch of
+ 'n' : begin
+ p0 := @LineEnd[1];
+ p1 := p0 + Length(LineEnding);
+ end;
+ 'l' : begin
+ Mode := smodeOneLower;
+ p1 := p0;
+ end;
+ 'L' : begin
+ Mode := smodeAllLower;
+ p1 := p0;
+ end;
+ 'u' : begin
+ Mode := smodeOneUpper;
+ p1 := p0;
+ end;
+ 'U' : begin
+ Mode := smodeAllUpper;
+ p1 := p0;
+ end;
+ else
+ begin
+ inc(p0);
+ inc(p1);
+ end;
+ end;
+ end
+ end;
+ if p0 < p1 then begin
+ while p0 < p1 do begin
+ case Mode of
+ smodeOneLower, smodeAllLower:
+ begin
+ Ch := p0^;
+ if Ch < #128 then
+ Ch := AnsiLowerCase(Ch)[1];
+ ResultPtr^ := Ch;
+ if Mode = smodeOneLower then
+ Mode := smodeNormal;
+ end;
+ smodeOneUpper, smodeAllUpper:
+ begin
+ Ch := p0^;
+ if Ch < #128 then
+ Ch := AnsiUpperCase(Ch)[1];
+ ResultPtr^ := Ch;
+ if Mode = smodeOneUpper then
+ Mode := smodeNormal;
+ end;
+ else
+ ResultPtr^ := p0^;
+ end;
+ inc (ResultPtr);
+ inc (p0);
+ end;
+ Mode := smodeNormal;
+ end;
+ end;
+end; { of function TRegExpr.Substitute
+--------------------------------------------------------------}
+
+procedure TRegExpr.Split (AInputStr : RegExprString; APieces : TStrings);
+ var PrevPos : integer;
+ begin
+ PrevPos := 1;
+ if Exec (AInputStr) then
+ REPEAT
+ APieces.Add (System.Copy (AInputStr, PrevPos, MatchPos [0] - PrevPos));
+ PrevPos := MatchPos [0] + MatchLen [0];
+ UNTIL not ExecNext;
+ APieces.Add (System.Copy (AInputStr, PrevPos, MaxInt)); // Tail
+ end; { of procedure TRegExpr.Split
+--------------------------------------------------------------}
+
+function TRegExpr.Replace (AInputStr : RegExprString; const AReplaceStr : RegExprString;
+ AUseSubstitution : boolean{$IFDEF DefParam}= False{$ENDIF}) : RegExprString;
+ var
+ PrevPos : integer;
+ begin
+ Result := '';
+ PrevPos := 1;
+ if Exec (AInputStr) then
+ REPEAT
+ Result := Result + System.Copy (AInputStr, PrevPos,
+ MatchPos [0] - PrevPos);
+ if AUseSubstitution //###0.946
+ then Result := Result + Substitute (AReplaceStr)
+ else Result := Result + AReplaceStr;
+ PrevPos := MatchPos [0] + MatchLen [0];
+ UNTIL not ExecNext;
+ Result := Result + System.Copy (AInputStr, PrevPos, MaxInt); // Tail
+ end; { of function TRegExpr.Replace
+--------------------------------------------------------------}
+
+function TRegExpr.ReplaceEx (AInputStr : RegExprString;
+ AReplaceFunc : TRegExprReplaceFunction)
+ : RegExprString;
+ var
+ PrevPos : integer;
+ begin
+ Result := '';
+ PrevPos := 1;
+ if Exec (AInputStr) then
+ REPEAT
+ Result := Result + System.Copy (AInputStr, PrevPos,
+ MatchPos [0] - PrevPos)
+ + AReplaceFunc (Self);
+ PrevPos := MatchPos [0] + MatchLen [0];
+ UNTIL not ExecNext;
+ Result := Result + System.Copy (AInputStr, PrevPos, MaxInt); // Tail
+ end; { of function TRegExpr.ReplaceEx
+--------------------------------------------------------------}
+
+
+{$IFDEF OverMeth}
+function TRegExpr.Replace (AInputStr : RegExprString;
+ AReplaceFunc : TRegExprReplaceFunction)
+ : RegExprString;
+ begin
+ {$IFDEF SYN_LAZARUS}Result:={$ENDIF}ReplaceEx (AInputStr, AReplaceFunc);
+ end; { of function TRegExpr.Replace
+--------------------------------------------------------------}
+{$ENDIF}
+
+{=============================================================}
+{====================== Debug section ========================}
+{=============================================================}
+
+{$IFDEF RegExpPCodeDump}
+function TRegExpr.DumpOp (op : TREOp) : RegExprString;
+// printable representation of opcode
+ begin
+ case op of
+ BOL: Result := 'BOL';
+ EOL: Result := 'EOL';
+ BOLML: Result := 'BOLML';
+ EOLML: Result := 'EOLML';
+ BOUND: Result := 'BOUND'; //###0.943
+ NOTBOUND: Result := 'NOTBOUND'; //###0.943
+ ANY: Result := 'ANY';
+ ANYML: Result := 'ANYML'; //###0.941
+ ANYLETTER: Result := 'ANYLETTER';
+ NOTLETTER: Result := 'NOTLETTER';
+ ANYDIGIT: Result := 'ANYDIGIT';
+ NOTDIGIT: Result := 'NOTDIGIT';
+ ANYSPACE: Result := 'ANYSPACE';
+ NOTSPACE: Result := 'NOTSPACE';
+ ANYOF: Result := 'ANYOF';
+ ANYBUT: Result := 'ANYBUT';
+ ANYOFCI: Result := 'ANYOF/CI';
+ ANYBUTCI: Result := 'ANYBUT/CI';
+ BRANCH: Result := 'BRANCH';
+ EXACTLY: Result := 'EXACTLY';
+ EXACTLYCI: Result := 'EXACTLY/CI';
+ NOTHING: Result := 'NOTHING';
+ COMMENT: Result := 'COMMENT';
+ BACK: Result := 'BACK';
+ EEND: Result := 'END';
+ BSUBEXP: Result := 'BSUBEXP';
+ BSUBEXPCI: Result := 'BSUBEXP/CI';
+ Succ (OPEN) .. TREOp (Ord (OPEN) + NSUBEXP - 1): //###0.929
+ Result := Format ('OPEN[%d]', [ord (op) - ord (OPEN)]);
+ Succ (CLOSE) .. TREOp (Ord (CLOSE) + NSUBEXP - 1): //###0.929
+ Result := Format ('CLOSE[%d]', [ord (op) - ord (CLOSE)]);
+ STAR: Result := 'STAR';
+ PLUS: Result := 'PLUS';
+ BRACES: Result := 'BRACES';
+ {$IFDEF ComplexBraces}
+ LOOPENTRY: Result := 'LOOPENTRY'; //###0.925
+ LOOP: Result := 'LOOP'; //###0.925
+ LOOPNG: Result := 'LOOPNG'; //###0.940
+ {$ENDIF}
+ ANYOFTINYSET: Result:= 'ANYOFTINYSET';
+ ANYBUTTINYSET:Result:= 'ANYBUTTINYSET';
+ {$IFDEF UseSetOfChar} //###0.929
+ ANYOFFULLSET: Result:= 'ANYOFFULLSET';
+ {$ENDIF}
+ STARNG: Result := 'STARNG'; //###0.940
+ PLUSNG: Result := 'PLUSNG'; //###0.940
+ BRACESNG: Result := 'BRACESNG'; //###0.940
+ else Error (reeDumpCorruptedOpcode);
+ end; {of case op}
+ Result := ':' + Result;
+ end; { of function TRegExpr.DumpOp
+--------------------------------------------------------------}
+
+function TRegExpr.Dump : RegExprString;
+// dump a regexp in vaguely comprehensible form
+ var
+ s : PRegExprChar;
+ op : TREOp; // Arbitrary non-END op.
+ next : PRegExprChar;
+ i : integer;
+ Diff : integer;
+{$IFDEF UseSetOfChar} //###0.929
+ Ch : REChar;
+{$ENDIF}
+ begin
+ if not IsProgrammOk //###0.929
+ then EXIT;
+
+ op := EXACTLY;
+ Result := '';
+ s := programm + REOpSz;
+ while op <> EEND do begin // While that wasn't END last time...
+ op := s^;
+ Result := Result + Format ('%2d%s', [s - programm, DumpOp (s^)]); // Where, what.
+ next := regnext (s);
+ if next = nil // Next ptr.
+ then Result := Result + ' (0)'
+ else begin
+ if next > s //###0.948 PWideChar subtraction workaround (see comments in Tail method for details)
+ then Diff := next - s
+ else Diff := - (s - next);
+ Result := Result + Format (' (%d) ', [(s - programm) + Diff]);
+ end;
+ inc (s, REOpSz + RENextOffSz);
+ if (op = ANYOF) or (op = ANYOFCI) or (op = ANYBUT) or (op = ANYBUTCI)
+ or (op = EXACTLY) or (op = EXACTLYCI) then begin
+ // Literal string, where present.
+ while s^ <> #0 do begin
+ Result := Result + s^;
+ inc (s);
+ end;
+ inc (s);
+ end;
+ if (op = ANYOFTINYSET) or (op = ANYBUTTINYSET) then begin
+ for i := 1 to TinySetLen do begin
+ Result := Result + s^;
+ inc (s);
+ end;
+ end;
+ if (op = BSUBEXP) or (op = BSUBEXPCI) then begin
+ Result := Result + ' \' + IntToStr (Ord (s^));
+ inc (s);
+ end;
+ {$IFDEF UseSetOfChar} //###0.929
+ if op = ANYOFFULLSET then begin
+ for Ch := #0 to #255 do
+ if Ch in PSetOfREChar (s)^ then
+ if Ch < ' '
+ then Result := Result + '#' + IntToStr (Ord (Ch)) //###0.936
+ else Result := Result + Ch;
+ inc (s, SizeOf (TSetOfREChar));
+ end;
+ {$ENDIF}
+ if (op = BRACES) or (op = BRACESNG) then begin //###0.941
+ // show min/max argument of BRACES operator
+ Result := Result + Format ('{%d,%d}', [PREBracesArg (s)^, PREBracesArg (s + REBracesArgSz)^]);
+ inc (s, REBracesArgSz * 2);
+ end;
+ {$IFDEF ComplexBraces}
+ if (op = LOOP) or (op = LOOPNG) then begin //###0.940
+ Result := Result + Format (' -> (%d) {%d,%d}', [
+ (s - programm - (REOpSz + RENextOffSz)) + PRENextOff (s + 2 * REBracesArgSz)^,
+ PREBracesArg (s)^, PREBracesArg (s + REBracesArgSz)^]);
+ inc (s, 2 * REBracesArgSz + RENextOffSz);
+ end;
+ {$ENDIF}
+ Result := Result + #$d#$a;
+ end; { of while}
+
+ // Header fields of interest.
+
+ if regstart <> #0
+ then Result := Result + 'start ' + regstart;
+ if reganch <> #0
+ then Result := Result + 'anchored ';
+ if regmust <> nil
+ then Result := Result + 'must have ' + regmust;
+ {$IFDEF UseFirstCharSet} //###0.929
+ Result := Result + #$d#$a'FirstCharSet:';
+ for Ch := #0 to #255 do
+ if Ch in FirstCharSet
+ then begin
+ if Ch < ' '
+ then Result := Result + '#' + IntToStr(Ord(Ch)) //###0.948
+ else Result := Result + Ch;
+ end;
+ {$ENDIF}
+ Result := Result + #$d#$a;
+ end; { of function TRegExpr.Dump
+--------------------------------------------------------------}
+{$ENDIF}
+
+{$IFDEF reRealExceptionAddr}
+{$OPTIMIZATION ON}
+// ReturnAddr works correctly only if compiler optimization is ON
+// I placed this method at very end of unit because there are no
+// way to restore compiler optimization flag ...
+{$ENDIF}
+procedure TRegExpr.Error (AErrorID : integer);
+{$IFDEF reRealExceptionAddr}
+ function ReturnAddr : pointer; //###0.938
+ asm
+ mov eax,[ebp+4]
+ end;
+{$ENDIF}
+ var
+ e : ERegExpr;
+ begin
+ fLastError := AErrorID; // dummy stub - useless because will raise exception
+ if AErrorID < 1000 // compilation error ?
+ then e := ERegExpr.Create (ErrorMsg (AErrorID) // yes - show error pos
+ + ' (pos ' + IntToStr (CompilerErrorPos) + ')')
+ else e := ERegExpr.Create (ErrorMsg (AErrorID));
+ e.ErrorCode := AErrorID;
+ e.CompilerErrorPos := CompilerErrorPos;
+ raise e
+ {$IFDEF reRealExceptionAddr}
+ At ReturnAddr; //###0.938
+ {$ENDIF}
+ end; { of procedure TRegExpr.Error
+--------------------------------------------------------------}
+
+(*
+ PCode persistence:
+ FirstCharSet
+ programm, regsize
+ regstart // -> programm
+ reganch // -> programm
+ regmust, regmlen // -> programm
+ fExprIsCompiled
+*)
+
+// be carefull - placed here code will be always compiled with
+// compiler optimization flag
+
+{$IFDEF FPC}
+initialization
+ RegExprInvertCaseFunction := TRegExpr.InvertCaseFunction;
+
+{$ENDIF}
+end.
+
diff --git a/examples/apps/ide/src/templates/default.project b/examples/apps/ide/src/templates/default.project
new file mode 100644
index 00000000..bbab8e40
--- /dev/null
+++ b/examples/apps/ide/src/templates/default.project
@@ -0,0 +1,39 @@
+[ProjectOptions]
+ProjectDir=/media/flash16gig/programming/fpgide/src/
+ProjectName=default.project
+MainUnit=default.pas
+TargetFile=default${EXEEXT}
+DefaultMake=0
+UnitOutputDir=units/${TARGET}/
+MakeOptionsCount=4
+MakeOptionEnabled1=1,1,1,1,1,1
+MakeOptionEnabled2=1,1,1,1,1,0
+MakeOptionEnabled3=0,1,0,0,0,1
+MakeOptionEnabled4=0,0,0,0,0,1
+MacroCount=6
+Macro1=TargetCPU=x86_64
+Macro2=TargetOS=linux
+Macro3=TargetCPU=i386
+Macro4=TargetOS=win32
+Macro5=FPGUI_DIR=/home/graemeg/programming/fpgui/
+Macro6=tiOPF_fpGUI_Dir=/home/graemeg/programming/3rdParty/tiOPF2/src/
+UnitDirsCount=6
+UnitDirEnabled1=1,1,1,1,0,0,0,1,0,0
+UnitDirEnabled2=1,1,1,1,0,0,1,1,0,0
+UnitDirEnabled3=1,1,1,0,0,0,1,1,0,0
+UnitDirEnabled4=0,0,0,1,0,0,1,1,0,0
+UnitDirEnabled5=1,1,1,1,0,0,1,0,0,0
+UnitDirEnabled6=1,1,1,1,0,0,1,0,0,0
+MakeOption1=-l -Mobjfpc -Sch
+MakeOption2=-gl -O-
+MakeOption3=-B
+MakeOption4=-O2 -XX -Xs -CX
+UnitDir1=${FPGUIDIR}src/
+UnitDir2=${FPGUIDIR}src/corelib/
+UnitDir3=${FPGUIDIR}src/corelib/x11/
+UnitDir4=${FPGUIDIR}src/corelib/gdi/
+UnitDir5=${FPGUIDIR}src/gui/
+UnitDir6=${FPGUIDIR}src/gui/db/
+
+[Units]
+UnitCount=0
diff --git a/examples/apps/ide/src/templates/empty.project b/examples/apps/ide/src/templates/empty.project
new file mode 100644
index 00000000..b775eb1b
--- /dev/null
+++ b/examples/apps/ide/src/templates/empty.project
@@ -0,0 +1,29 @@
+[ProjectOptions]
+ProjectDir=/media/flash16gig/programming/fpgide/src/
+ProjectName=fpgide
+MainUnit=empty.lpr
+TargetFile=empty
+DefaultMake=0
+UnitOutputDir=units/i386-linux/
+MakeOptionsCount=4
+MakeOption1=-l -Mobjfpc -Sch
+MakeOption2=-gl -O-
+MakeOption3=-B
+MakeOption4=-O2 -XX -Xs -CX
+MakeOptionEnabled1=1,1,1,1,1,1
+MakeOptionEnabled2=1,1,1,1,1,0
+MakeOptionEnabled3=0,1,0,0,0,1
+MakeOptionEnabled4=0,0,0,0,0,1
+MacroCount=6
+Macro1=TargetCPU=x86_64
+Macro2=TargetOS=linux
+Macro3=TargetCPU=i386
+Macro4=TargetOS=win32
+Macro5=FPGUI_DIR=/home/graemeg/programming/fpgui/
+Macro6=tiOPF_fpGUI_Dir=/home/graemeg/programming/3rdParty/tiOPF2/src/
+UnitDirsCount=1
+UnitDir1=./
+UnitDirEnabled1=1,1,1,1,1,1,1,0,0,0
+
+[Units]
+UnitCount=0
diff --git a/examples/apps/ide/src/unitlist.pas b/examples/apps/ide/src/unitlist.pas
new file mode 100644
index 00000000..b8ca59ad
--- /dev/null
+++ b/examples/apps/ide/src/unitlist.pas
@@ -0,0 +1,170 @@
+unit UnitList;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, fpg_base;
+
+type
+ TUnit = class(TObject)
+ private
+ FFilename: TfpgString;
+ FOpened: Boolean;
+ function GetUnitName: TfpgString;
+ public
+ constructor Create;
+ property FileName: TfpgString read FFilename write FFilename;
+ property UnitName: TfpgString read GetUnitName;
+ property Opened: Boolean read FOpened write FOpened;
+ end;
+
+
+ TUnitList = class(TObject)
+ private
+ FList: TList;
+ function GetItems(AIndex: integer): TUnit;
+ procedure SetItems(AIndex: integer; const AValue: TUnit);
+ public
+ constructor Create;
+ destructor Destroy; override;
+ function Count: integer;
+ function FindByName(const AUnitName: TfpgString): TUnit;
+ function FileExists(const AFilename: TfpgString): Boolean;
+ procedure Add(NewUnit: TUnit);
+ procedure Clear;
+ procedure Delete(AIndex: integer);
+ property Items[AIndex: integer]: TUnit read GetItems write SetItems; default;
+ end;
+
+
+implementation
+
+uses
+ fpg_utils;
+
+{ TUnitList }
+
+function TUnitList.GetItems(AIndex: integer): TUnit;
+begin
+ Result := TUnit(FList[AIndex]);
+end;
+
+procedure TUnitList.SetItems(AIndex: integer; const AValue: TUnit);
+begin
+ FList[AIndex] := AValue;
+end;
+
+constructor TUnitList.Create;
+begin
+ FList := TList.Create;
+ inherited Create;
+end;
+
+destructor TUnitList.Destroy;
+begin
+ Clear;
+ FList.Free;
+ inherited Destroy;
+end;
+
+function TUnitList.Count: integer;
+begin
+ Result := FList.Count;
+end;
+
+function TUnitList.FindByName(const AUnitName: TfpgString): TUnit;
+var
+ l: Integer;
+ r: Integer;
+ m: Integer;
+ cmp: Integer;
+begin
+ l := 0;
+ r := Count-1;
+ m := 0;
+ while l <= r do
+ begin
+ m := (l+r) shr 1;
+ Result := Items[m];
+ cmp := AnsiCompareText(AUnitName, Result.UnitName);
+ if cmp < 0 then
+ r := m-1
+ else if cmp > 0 then
+ l := m+1
+ else
+ exit;
+ end;
+ Result := nil;
+end;
+
+function TUnitList.FileExists(const AFilename: TfpgString): Boolean;
+var
+ i: integer;
+begin
+ Result := False;
+ for i := 0 to Count-1 do
+ begin
+ Result := Items[i].FileName = AFilename;
+ if Result then
+ Exit;
+ end;
+end;
+
+procedure TUnitList.Add(NewUnit: TUnit);
+var
+ l: Integer;
+ r: Integer;
+ m: Integer;
+ cmp: Integer;
+begin
+ l := 0;
+ r := Count-1;
+ m := 0;
+ while l <= r do
+ begin
+ m := (l+r) shr 1;
+ cmp := AnsiCompareText(NewUnit.UnitName, Items[m].UnitName);
+ if cmp < 0 then
+ r := m-1
+ else if cmp > 0 then
+ l := m + 1
+ else
+ break;
+ end;
+ if (m < Count) and (AnsiCompareText(NewUnit.UnitName, Items[m].UnitName) > 0) then
+ inc(m);
+ FList.Insert(m, NewUnit);
+end;
+
+procedure TUnitList.Clear;
+var
+ i: integer;
+begin
+ for i := Count-1 downto 0 do
+ TUnit(FList[i]).Free;
+ FList.Clear;
+end;
+
+procedure TUnitList.Delete(AIndex: integer);
+begin
+ TUnit(FList[AIndex]).Free;
+ FList.Delete(AIndex);
+end;
+
+{ TUnit }
+
+function TUnit.GetUnitName: TfpgString;
+begin
+ Result := fpgExtractFileName(Filename);
+end;
+
+constructor TUnit.Create;
+begin
+ inherited Create;
+ FOpened := False;
+end;
+
+end.
+