diff options
author | graemeg <graemeg@ae50a9b5-8222-0410-bf8d-8a13f76226bf> | 2008-10-30 21:12:08 +0000 |
---|---|---|
committer | graemeg <graemeg@ae50a9b5-8222-0410-bf8d-8a13f76226bf> | 2008-10-30 21:12:08 +0000 |
commit | 2dd4b960a67cbdc9e2064f6e84617ff0eaa08958 (patch) | |
tree | e7932bc8b2ccd49f6d2d7b054aa97db80ac301b0 /prototypes | |
parent | 2cf6c6ac7511d9bccbc500b15ec7f1df92ae4bf6 (diff) | |
download | fpGUI-2dd4b960a67cbdc9e2064f6e84617ff0eaa08958.tar.xz |
* Added my work-in-progress on TfpgTextEdit component.
Diffstat (limited to 'prototypes')
-rw-r--r-- | prototypes/textedit/demo_textedit.lpi | 68 | ||||
-rw-r--r-- | prototypes/textedit/demo_textedit.lpr | 491 | ||||
-rw-r--r-- | prototypes/textedit/extrafpc.cfg | 5 | ||||
-rw-r--r-- | prototypes/textedit/fpg_textedit.pas | 1265 |
4 files changed, 1829 insertions, 0 deletions
diff --git a/prototypes/textedit/demo_textedit.lpi b/prototypes/textedit/demo_textedit.lpi new file mode 100644 index 00000000..bde04ec6 --- /dev/null +++ b/prototypes/textedit/demo_textedit.lpi @@ -0,0 +1,68 @@ +<?xml version="1.0"?> +<CONFIG> + <ProjectOptions> + <PathDelim Value="/"/> + <Version Value="6"/> + <General> + <Flags> + <SaveOnlyProjectUnits Value="True"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <TargetFileExt Value=""/> + <Title Value="demo_textedit"/> + </General> + <VersionInfo> + <ProjectVersion Value=""/> + </VersionInfo> + <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 Use="True" 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="2"> + <Unit0> + <Filename Value="demo_textedit.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="demo_textedit"/> + </Unit0> + <Unit1> + <Filename Value="fpg_textedit.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="fpg_textedit"/> + </Unit1> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="8"/> + <Parsing> + <SyntaxOptions> + <CStyleOperator Value="False"/> + <AllowLabel Value="False"/> + <CPPInline Value="False"/> + </SyntaxOptions> + </Parsing> + <Linking> + <Debugging> + <UseHeaptrc Value="True"/> + </Debugging> + </Linking> + <Other> + <CustomOptions Value="-FUunits +"/> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> +</CONFIG> diff --git a/prototypes/textedit/demo_textedit.lpr b/prototypes/textedit/demo_textedit.lpr new file mode 100644 index 00000000..82f5d218 --- /dev/null +++ b/prototypes/textedit/demo_textedit.lpr @@ -0,0 +1,491 @@ +program demo_textedit; + +{$mode objfpc}{$H+} + +uses +// {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, +// {$ENDIF}{$ENDIF} + Classes, + typinfo, + Sysutils, + fpg_base, + fpg_main, + fpg_form, + fpg_button, + fpg_label, + fpg_memo, fpg_dialogs, fpg_utils, fpg_radiobutton, + fpg_progressbar, fpg_textedit, fpg_checkbox; + +type + + TMyThread = class(TThread) + private + bar: TfpgProgressBar; + pos: integer; + procedure UpdateGUI; + protected + procedure Execute; override; + public + constructor CreateCustom(pb: TfpgProgressBar); + end; + + + TMainForm = class(TfpgForm) + private + {@VFD_HEAD_BEGIN: MainForm} + memo: TfpgMemo; + btnQuit: TfpgButton; + TextEdit: TfpgTextEdit; + btnLoad: TfpgButton; + rbLeft: TfpgRadioButton; + rbRight: TfpgRadioButton; + rbBoth: TfpgRadioButton; + Label1: TfpgLabel; + ProgressBar2: TfpgProgressBar; + ProgressBar1: TfpgProgressBar; + ProgressBar3: TfpgProgressBar; + Button1: TfpgButton; + Button2: TfpgButton; + Button3: TfpgButton; + chkShowGutter: TfpgCheckBox; + chkLineNumbers: TfpgCheckBox; + {@VFD_HEAD_END: MainForm} + t1: TMyThread; + t2: TMyThread; + t3: TMyThread; + procedure ShowGutterChanged(Sender: TObject); + procedure ShowLineNumbers(Sender: TObject); + procedure AppExceptions(Sender: TObject; E: Exception); + procedure btnQuitClick(Sender: TObject); + procedure HandleResize(awidth, aheight: TfpgCoord); override; + procedure btnLoadClicked(Sender: TObject); + procedure btn1(Sender: TObject); + procedure btn2(Sender: TObject); + procedure btn3(Sender: TObject); + public + constructor Create(AOwner: TComponent); override; + procedure AfterCreate; override; + end; + +{ TMyThread } + +procedure TMyThread.UpdateGUI; +begin + bar.Position := pos; +end; + +procedure TMyThread.Execute; +begin + pos := -1; + while not Terminated do + begin + inc(pos); + Synchronize(@UpdateGUI); + sleep(200); + if pos >= 100 then + break; + end; +end; + +constructor TMyThread.CreateCustom(pb: TfpgProgressBar); +begin + Create(True); + bar := pb; + FreeOnTerminate := True; + Suspended := False; +end; + +{ TMainForm } + +procedure TMainForm.ShowGutterChanged(Sender: TObject); +begin + TextEdit.GutterVisible := chkShowGutter.Checked; +end; + +procedure TMainForm.ShowLineNumbers(Sender: TObject); +begin + TextEdit.GutterShowLineNumbers := chkLineNumbers.Checked; +end; + +procedure TMainForm.AppExceptions(Sender: TObject; E: Exception); +begin + DumpStack; +end; + +procedure TMainForm.btnQuitClick(Sender: TObject); +begin +{ + if Assigned(t1) then + begin + t1.Terminate; + writeln('t1 waiting...'); + t1.WaitFor; + end; + writeln('t1 done'); + if Assigned(t2) then + begin + t2.Terminate; + writeln('t2 waiting...'); + t2.WaitFor; + end; + writeln('t2 done'); + if Assigned(t3) then + begin + t3.Terminate; + writeln('t3 waiting...'); + t3.WaitFor; + end; + writeln('t3 done'); +} + Close; +end; + +procedure TMainForm.HandleResize(awidth, aheight: TfpgCoord); +begin + inherited HandleResize(awidth, aheight); + //if Assigned(Memo) then + //begin + //Memo.SetPosition(Memo.Left, Memo.Top, awidth-20, aheight- Memo.Top - 10); + //btnQuit.Left := awidth - btnQuit.Width - 10; + //btnQuit.UpdateWindowPosition; + //end; +end; + +procedure TMainForm.btnLoadClicked(Sender: TObject); +var + s: string; + t: Cardinal; +begin + s := SelectFileDialog; + if (s <> '') and fpgFileExists(s) then + begin + if rbLeft.Checked or rbBoth.Checked then + begin + t := fpgGetTickCount; + memo.Lines.LoadFromFile(s); + Label1.Text := Format('%d ticks', [fpgGetTickCount - t]); + end; + if rbRight.Checked or rbBoth.Checked then + begin + t := fpgGetTickCount; + TextEdit.Lines.LoadFromFile(s); + Label1.Text := Format('%d ticks', [fpgGetTickCount - t]); + TextEdit.Invalidate; + end; + end; + fpgApplication.ProcessMessages; +end; + +procedure TMainForm.btn1(Sender: TObject); +begin + t1 := TMyThread.CreateCustom(ProgressBar1); +end; + +procedure TMainForm.btn2(Sender: TObject); +begin + t2 := TMyThread.CreateCustom(ProgressBar2); +end; + +procedure TMainForm.btn3(Sender: TObject); +begin + t3 := TMyThread.CreateCustom(ProgressBar3); +end; + +constructor TMainForm.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + +// HandleResize(Width, Height); + + fpgApplication.OnException :=@AppExceptions; +end; + +procedure TMainForm.AfterCreate; +begin + {@VFD_BODY_BEGIN: MainForm} + Name := 'MainForm'; + SetPosition(319, 180, 594, 346); + WindowTitle := 'Memo test'; + WindowPosition := wpScreenCenter; + + memo := TfpgMemo.Create(self); + with memo do + begin + Name := 'memo'; + SetPosition(6, 116, 280, 170); + Anchors := [anLeft,anTop,anBottom]; + Lines.Add('Memo Test0'); + Lines.Add('Memo Test1'); + Lines.Add('Memo Test2'); + Lines.Add('Memo Test3'); + Lines.Add('Memo Test4'); + FontDesc := '#Edit1'; + ParentShowHint := True; + TabOrder := 0; + // FontDesc := 'Arial-15'; + // Lines.Insert(1,'0 Before 1 after'); + end; + + btnQuit := TfpgButton.Create(self); + with btnQuit do + begin + Name := 'btnQuit'; + SetPosition(310, 10, 80, 23); + Text := 'Button'; + AllowAllUp := False; + Embedded := False; + Flat := False; + FontDesc := '#Label1'; + GroupIndex := 0; + Hint := ''; + ImageLayout := ilImageLeft; + ImageMargin := 3; + ImageName := 'stdimg.quit'; + ImageSpacing := -1; + ModalResult := 0; + ParentShowHint := True; + ShowImage := True; + TabOrder := 1; + OnClick := @btnQuitClick; + end; + + TextEdit := TfpgTextEdit.Create(self); + with TextEdit do + begin + Name := 'TextEdit'; + SetPosition(300, 164, 280, 170); + Anchors := [anLeft,anRight,anTop,anBottom]; + Lines.Add('Memo Test0'); + Lines.Add('Memo Test1'); + Lines.Add('Memo Test2'); + Lines.Add('Memo Test3'); + Lines.Add('Memo Test4'); + //FontDesc := '#Edit1'; + FontDesc := 'Bitstream Vera Sans Mono-10'; + // Lines.Insert(1,'0 Beforje 1 after'); + ParentShowHint := True; + end; + + btnLoad := TfpgButton.Create(self); + with btnLoad do + begin + Name := 'btnLoad'; + SetPosition(312, 44, 80, 24); + Text := 'Load'; + AllowAllUp := False; + Embedded := False; + Flat := False; + FontDesc := '#Label1'; + GroupIndex := 0; + Hint := ''; + ImageLayout := ilImageLeft; + ImageMargin := 3; + ImageName := ''; + ImageSpacing := -1; + ModalResult := 0; + ParentShowHint := True; + ShowImage := True; + TabOrder := 3; + OnClick := @btnLoadClicked; + end; + + rbLeft := TfpgRadioButton.Create(self); + with rbLeft do + begin + Name := 'rbLeft'; + SetPosition(416, 8, 120, 20); + FontDesc := '#Label1'; + GroupIndex := 0; + ParentShowHint := True; + TabOrder := 4; + Text := 'Left'; + end; + + rbRight := TfpgRadioButton.Create(self); + with rbRight do + begin + Name := 'rbRight'; + SetPosition(416, 32, 120, 20); + FontDesc := '#Label1'; + GroupIndex := 0; + ParentShowHint := True; + TabOrder := 5; + Text := 'Right'; + end; + + rbBoth := TfpgRadioButton.Create(self); + with rbBoth do + begin + Name := 'rbBoth'; + SetPosition(416, 56, 120, 20); + Checked := True; + FontDesc := '#Label1'; + GroupIndex := 0; + ParentShowHint := True; + TabOrder := 6; + Text := 'Both'; + end; + + Label1 := TfpgLabel.Create(self); + with Label1 do + begin + Name := 'Label1'; + SetPosition(12, 304, 172, 16); + Anchors := [anLeft,anBottom]; + Alignment := taCenter; + FontDesc := '#Label2'; + Hint := ''; + Layout := tlTop; + ParentShowHint := True; + Text := 'Label'; + WrapText := False; + end; + + ProgressBar2 := TfpgProgressBar.Create(self); + with ProgressBar2 do + begin + Name := 'ProgressBar2'; + SetPosition(12, 48, 254, 22); + Min := 0; + Max := 100; + ParentShowHint := True; + Position := 0; + ShowCaption := False; + end; + + ProgressBar1 := TfpgProgressBar.Create(self); + with ProgressBar1 do + begin + Name := 'ProgressBar1'; + SetPosition(12, 20, 254, 22); + Min := 0; + Max := 100; + ParentShowHint := True; + Position := 0; + ShowCaption := False; + end; + + ProgressBar3 := TfpgProgressBar.Create(self); + with ProgressBar3 do + begin + Name := 'ProgressBar3'; + SetPosition(12, 76, 254, 22); + Min := 0; + Max := 100; + ParentShowHint := True; + Position := 0; + ShowCaption := False; + end; + + Button1 := TfpgButton.Create(self); + with Button1 do + begin + Name := 'Button1'; + SetPosition(276, 20, 20, 20); + Text := ''; + AllowAllUp := False; + Embedded := False; + Flat := False; + FontDesc := '#Label1'; + GroupIndex := 0; + Hint := ''; + ImageLayout := ilImageLeft; + ImageMargin := 3; + ImageName := ''; + ImageSpacing := -1; + ModalResult := 0; + ParentShowHint := True; + ShowImage := True; + TabOrder := 11; + OnClick := @btn1; + end; + + Button2 := TfpgButton.Create(self); + with Button2 do + begin + Name := 'Button2'; + SetPosition(276, 48, 20, 20); + Text := ''; + AllowAllUp := False; + Embedded := False; + Flat := False; + FontDesc := '#Label1'; + GroupIndex := 0; + Hint := ''; + ImageLayout := ilImageLeft; + ImageMargin := 3; + ImageName := ''; + ImageSpacing := -1; + ModalResult := 0; + ParentShowHint := True; + ShowImage := True; + TabOrder := 12; + OnClick := @btn2; + end; + + Button3 := TfpgButton.Create(self); + with Button3 do + begin + Name := 'Button3'; + SetPosition(276, 76, 20, 20); + Text := ''; + AllowAllUp := False; + Embedded := False; + Flat := False; + FontDesc := '#Label1'; + GroupIndex := 0; + Hint := ''; + ImageLayout := ilImageLeft; + ImageMargin := 3; + ImageName := ''; + ImageSpacing := -1; + ModalResult := 0; + ParentShowHint := True; + ShowImage := True; + TabOrder := 13; + OnClick := @btn3; + end; + + chkShowGutter := TfpgCheckBox.Create(self); + with chkShowGutter do + begin + Name := 'chkShowGutter'; + SetPosition(416, 84, 120, 20); + FontDesc := '#Label1'; + ParentShowHint := True; + TabOrder := 14; + Text := 'Show Gutter'; + OnChange :=@ShowGutterChanged; + end; + + chkLineNumbers := TfpgCheckBox.Create(self); + with chkLineNumbers do + begin + Name := 'chkLineNumbers'; + SetPosition(416, 108, 120, 20); + FontDesc := '#Label1'; + ParentShowHint := True; + TabOrder := 15; + Text := 'Show Line Numbers'; + Checked := True; + OnChange := @ShowLineNumbers; + end; + + {@VFD_BODY_END: MainForm} +end; + +procedure MainProc; +var + frm: TMainForm; +begin + fpgApplication.Initialize; + frm := TMainForm.Create(nil); + frm.Show; + fpgApplication.Run; + frm.Free; +end; + +begin + MainProc; +end. diff --git a/prototypes/textedit/extrafpc.cfg b/prototypes/textedit/extrafpc.cfg new file mode 100644 index 00000000..073dc4b6 --- /dev/null +++ b/prototypes/textedit/extrafpc.cfg @@ -0,0 +1,5 @@ +-FUunits +-Fu../../../lib +-Xs +-XX +-CX diff --git a/prototypes/textedit/fpg_textedit.pas b/prototypes/textedit/fpg_textedit.pas new file mode 100644 index 00000000..0b9d2ccd --- /dev/null +++ b/prototypes/textedit/fpg_textedit.pas @@ -0,0 +1,1265 @@ +{ + fpGUI - Free Pascal GUI Library + + Copyright (C) 2006 - 2008 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_panel, + fpg_scrollbar; + +type + // forward declaration + TfpgBaseTextEdit = class; + + TfpgGutter = class(TfpgBevel) + private + 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; + 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; + + + TfpgBaseTextEdit = class(TfpgWidget) + private + FFont: TfpgFont; + FFullRedraw: Boolean; + FLines: TStrings; + CaretPos: TPoint; + FScrollBarStyle: TfpgScrollStyle; + MousePos: TPoint; + FChrW: Integer; + FChrH: Integer; + FTopLine: Integer; + FVisLines: Integer; + FVisCols: Integer; + StartNo, EndNo, StartOffs, EndOffs: Integer; + FSelStartNo, FSelEndNo, 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; + 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; + protected + { -- internal events -- } + procedure HandlePaint; override; + procedure HandleMouseEnter; override; + procedure HandleMouseExit; override; + procedure HandleLMouseDown(x, y: integer; shiftstate: TShiftState); 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 I, Y: Integer); virtual; + procedure FormatLine(const I, 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; + 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 DeleteSelection; + 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; + end; + + + TfpgTextEdit = class(TfpgBaseTextEdit) + published + property FontDesc; + property FullRedraw; + property GutterVisible; + property GutterShowLineNumbers; + property Lines; + property ScrollBarStyle; + property TabWidth; + property Tracking; + end; + + +implementation + +uses + fpg_dialogs{, fpg_constants}, fpg_stringutils; + +{ 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; +begin + if not FShowNum then + Exit; //==> + w := GetClientRect.Width - FSpace - 1; + H := TfpgBaseTextEdit(Owner).FChrH; + MaxI := TfpgBaseTextEdit(Owner).FVisLines; + + 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.SetRect(2, I * H, W, (I * H) + H); +// Canvas.FillRectangle(r); + Canvas.DrawText(r, S); + end; +end; + +procedure TfpgGutter.SetZeroStart(const AValue: Boolean); +begin + if FZeroStart=AValue then exit; + FZeroStart:=AValue; +end; + +procedure TfpgGutter.HandlePaint; +begin + inherited HandlePaint; + DrawLineNums; +end; + +constructor TfpgGutter.CreateGutter(AOwner: TfpgBaseTextEdit); +begin + inherited Create(AOwner); + FDigits := 0; + FShowNum := True; + FSpace := 2; + FStartNum := 1; + FZeroStart := False; + Width := 35; + Shape := bsRightLine; +end; + +function TfpgGutter.GetClientRect: TfpgRect; +begin + Result := inherited GetClientRect; + Result.Width := Result.Width - 2; // bsRightLine 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; + 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; + +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 - 2; + FVScrollBar.Max := FLines.Count +1 - FVisLines; // +1 is so the last line is completely visible + FVScrollBar.Position := VPos; + FVScrollBar.Visible := FLines.Count > FVisLines; + + FHScrollBar.Min := 0; + FHScrollBar.PageSize := FVisCols div 4; //FMaxScrollH div 4; + FHScrollBar.Max := FMaxScrollH;// div 2; + FHScrollBar.Position := HPos; + FHScrollBar.Visible := FMaxScrollH > FVisCols; + + UpdateScrollBarCoords; +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; + UpdateScrollBars; + {$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; + UpdateScrollBars; + {$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; + + //if FVScrollBar.Visible then + //Dec(HWidth, FVScrollBar.Width); + //if FHScrollBar.Visible then + //Dec(VHeight, FHScrollBar.Height); + + 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); + + 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; + +begin + writeln('>> KeyboardCaretNav'); + 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 <= pred(FLines.Count) then + begin + writeln('********'); + //GliphY := (CaretPos.Y - FTopLine) * FChrH; + //DrawLine(Canvas, CaretPos.Y, GliphY); + //DrawCaret(CaretPos.X, CaretPos.Y + 1, False); + if (ssCtrl in ShiftState) and (CaretPos.Y > 0) then + begin + CaretPos.Y := CaretPos.Y - 1; + CaretPos.X := Length(FLines[CaretPos.Y]); + if FSelected then + begin + FSelEndNo := CaretPos.Y; + FSelEndOffs := CaretPos.X; +// DrawVisible; + end; + Exit; + end; + end; + CaretPos.Y := CaretPos.Y - 1; + CaretPos.X := Length(FLines[CaretPos.Y]); + //if not FSelected then + //begin + //GliphY := (CaretPos.Y - FTopLine) * FChrH; + //DrawLine(Canvas, CaretPos.X, GliphY); + //end else + //DrawVisible; + end + else + begin + CaretPos.X := 0; + //GliphY := (CaretPos.Y - FTopLine) * FChrH; + //if not FSelected then + //DrawLine(Canvas, CaretPos.Y, GliphY) + //else + //DrawVisible; + end; + end; + //else + //begin + //GliphY := (CaretPos.Y - FTopLine) * FChrH; + //DrawLine(Canvas, CaretPos.Y, GliphY); + //DrawCaret(CaretPos.X, CaretPos.Y, True); + //end; + if ssShift in ShiftState then + begin + if not FSelected then + begin + if CaretPos.Y <= pred(FLines.Count) then + if CaretPos.X > Length(FLines[CaretPos.Y]) then + CaretPos.Y := Length(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 <= pred(FLines.Count) then + begin + if FSelEndOffs > Length(FLines[FSelEndNo]) then + begin + FSelEndOffs := Length(FLines[FSelEndNo]) - 1; + CaretPos.X := FSelEndOffs; + end; + end else + begin + FSelEndOffs := 0; + CaretPos.X := 0; + end; + end; + FSelected := (FSelStartNo <> FSelEndNo) or (FSelStartOffs <> FSelEndOffs); + //DrawVisible; + Exit; + end; + if FSelected then + begin + FSelected := False; + //DrawVisible; + //DrawCaret(CaretPos.X, CaretPos.Y, True); + end; + if ssCtrl in ShiftState then + begin + CtrlKeyLeftKey; + //DrawVisible; + //DrawCaret(CaretPos.X, CaretPos.Y, True); + end; + FSelStartNo := CaretPos.Y; + FSelStartOffs := CaretPos.X; + end; + + keyRight: + begin + end; + + keyUp: + begin + end; + + keyDown: + begin + end; + + keyHome: + begin + end; + + keyEnd: + begin + end; + + keyPrior, keyNext: + begin + end; + end; + writeln('<< KeyboardCaretNav'); +end; + +procedure TfpgBaseTextEdit.InitMemoObjects; +begin + FGutterPan := TfpgGutter.CreateGutter(Self); + with FGutterPan do + begin + Left := -Width - 1; + Visible := False; + end; +end; + +procedure TfpgBaseTextEdit.HandlePaint; +begin +// inherited HandlePaint; + UpdateCharBounds; + // normal house keeping + Canvas.ClearClipRect; + Canvas.Clear(clBoxColor); + fpgStyle.DrawControlFrame(Canvas, 0, 0, Width, Height); + Canvas.Font := FFont; + Canvas.SetClipRect(GetClientRect); + + // do the actual drawing + UpdateScrollBarCoords; + UpdateGutterCoords; + 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 +writeln(' shiftstate not detected'); + 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; + +procedure TfpgBaseTextEdit.HandleKeyPress(var keycode: word; + var shiftstate: TShiftState; var consumed: boolean); +var + SLine: TfpgString; + AddS: TfpgString; + Y: Integer; + X: Integer; + CaretScroll: Boolean; +begin + writeln('>> TfpgBaseTextEdit.HandleKeyPress: keycode:', keycode); + writeln(' CaretPos X:', CaretPos.X, ' Y:', CaretPos.Y); +// 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(''); + 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 then + ScrollPos_V := CaretPos.Y + else if CaretPos.Y > FTopLine + FVisLines - 2 then + ScrollPos_V := CaretPos.Y - FVisLines + 2; + + Invalidate; + writeln('<< TfpgBaseTextEdit.HandleKeyPress'); +end; + +procedure TfpgBaseTextEdit.HandleKeyChar(var AText: TfpgChar; + var shiftstate: TShiftState; var consumed: boolean); +var + SLine: TfpgString; + Fill: Integer; +begin + writeln('>> TfpgBaseTextEdit.HandleKeyChar'); + 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.y + 1 then + for Fill := Length(SLine) to CaretPos.y + 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); + writeln('<< TfpgBaseTextEdit.HandleKeyChar'); +end; + +procedure TfpgBaseTextEdit.DrawVisible; +var + I, Y, cntVis: Integer; +begin + Y := 0; + cntVis := 1; + GetSelBounds(StartNo, EndNo, StartOffs, EndOffs); + for I := FTopLine to FTopLine + FVisLines do + begin + DrawLine(I, Y); + Y := Y + FChrH; + cntVis := cntVis + 1; + if cntVis > FVisLines then + Break; //==> + end; + { todo: implement this } +{ + if FRightEdge then + begin + if not FGutterOpt.Visible then + begin + with Canvas do + begin + Pen.Color := FEnvironment.RightEdgeColor; + MoveTo((FRightEdgeCol * FChrW) - (HPos * FChrW), 0); + LineTo((FRightEdgeCol * FChrW) - (HPos * FChrW), ClientHeight); + end; + end else + with Canvas do + begin + Pen.Color := FEnvironment.RightEdgeColor; + MoveTo((FRightEdgeCol * FChrW) - (HPos * FChrW) + FGutterPan.Width, 0); + LineTo((FRightEdgeCol * FChrW) - (HPos * FChrW) + FGutterPan.Width, ClientHeight); + end; + end; +} +end; + +procedure TfpgBaseTextEdit.DrawLine(const I, 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 I < FLines.Count then + begin + X := -(HPos * FChrW) + GSz; + FormatLine(I, X, Y); + end; +end; + +procedure TfpgBaseTextEdit.FormatLine(const I, 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 (I < 0) or (I > Pred(FLines.Count)) then + Exit; //==> + S := FLines[I]; + 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; +// if Assigned(FOnDrawLine) then FOnDrawLine(Self, S, I, LGliph, R, AllowDraw); + { Draw simple text line... } + if AllowDraw then + Canvas.DrawText(R, S); +// DrawText(LGliph.Canvas.Handle, PChar(S), Length(S), R, DT_DRAWLINE); + + { todo: Do other formatting here. } + { todo: Do selection painting here. } + 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.x; + FSelStartOffs := CaretPos.y; + 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 := 0; + VPos := 0; + HPos := 0; + FTracking := True; + FFullRedraw := False; + FSelected := False; + + 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.DeleteSelection; +begin + { TODO : Implement DeleteSelection } +end; + + +end. + |