diff options
Diffstat (limited to 'src/gui/fpg_memo.pas')
-rw-r--r-- | src/gui/fpg_memo.pas | 1459 |
1 files changed, 1459 insertions, 0 deletions
diff --git a/src/gui/fpg_memo.pas b/src/gui/fpg_memo.pas new file mode 100644 index 00000000..a93b06d8 --- /dev/null +++ b/src/gui/fpg_memo.pas @@ -0,0 +1,1459 @@ +{ + fpGUI - Free Pascal GUI Toolkit + + 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: + Defines a Memo control. Also known as a multi-line text edit control. +} + +unit fpg_memo; + +{$mode objfpc}{$H+} + + { TODO : Started a implementation for Tab support. It is still very experimental and should not be used yet. } + +interface + +uses + Classes, + SysUtils, + fpg_base, + fpg_main, + fpg_widget, + fpg_scrollbar, + fpg_menu; + +type + + TfpgMemo = class(TfpgWidget) + private + FLines: TStringList; + FMaxLength: integer; + FCursorPos: integer; + FCursorLine: integer; + FOnChange: TNotifyEvent; + FSideMargin: integer; + FSelStartLine: integer; + FSelEndLine: integer; + FSelStartPos: integer; + FSelEndPos: integer; + FSelecting: boolean; + FMouseDragging: boolean; + FMouseDragPos: integer; + FFont: TfpgFont; + FDrawOffset: integer; + FLineHeight: integer; + FFirstLine: integer; + FTabWidth: integer; + FUseTabs: boolean; + FVScrollBar: TfpgScrollBar; + FHScrollBar: TfpgScrollBar; + FWrapping: boolean; + FLongestLineWidth: TfpgCoord; + FPopupMenu: TfpgPopupMenu; + function GetFontDesc: string; + procedure SetFontDesc(const AValue: string); + procedure RecalcLongestLine; + procedure DeleteSelection; + procedure DoCopy; + procedure DoPaste; + procedure AdjustCursor; + function LineCount: integer; + function GetLineText(linenum: integer): string; + procedure SetLineText(linenum: integer; Value: string); + function GetCursorX: integer; + procedure SetCPByX(x: integer); + function CurrentLine: string; + function VisibleLines: integer; + function VisibleWidth: integer; + procedure VScrollBarMove(Sender: TObject; position: integer); + procedure HScrollBarMove(Sender: TObject; position: integer); + procedure SetText(const AValue: TfpgString); + function GetText: TfpgString; + procedure SetCursorLine(aValue: integer); + procedure UpdateScrollBarCoords; + protected + procedure HandleKeyChar(var AText: TfpgChar; var shiftstate: TShiftState; var consumed: boolean); override; + procedure HandleKeyPress(var keycode: word; var shiftstate: TShiftState; var consumed: boolean); override; + procedure HandleLMouseDown(x, y: integer; shiftstate: TShiftState); override; + procedure HandleRMouseUp(x, y: integer; shiftstate: TShiftState); override; + procedure HandleMouseMove(x, y: integer; btnstate: word; shiftstate: TShiftState); override; + procedure HandleResize(dwidth, dheight: integer); override; + procedure HandleMouseScroll(x, y: integer; shiftstate: TShiftState; delta: smallint); override; + procedure HandlePaint; override; + procedure HandleShow; override; + procedure HandleMouseEnter; override; + procedure HandleMouseExit; override; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + procedure UpdateScrollBars; + function SelectionText: string; + property CursorLine: integer read FCursorLine write SetCursorLine; + property Font: TfpgFont read FFont; + property LineHeight: integer read FLineHeight; + property MaxLength: integer read FMaxLength write FMaxLength; + property TabWidth: integer read FTabWidth write FTabWidth; + property Text: TfpgString read GetText write SetText; + property UseTabs: boolean read FUseTabs write FUseTabs default False; + property PopupMenu: TfpgPopupMenu read FPopupMenu write FPopupMenu; + published + property BackgroundColor default clBoxColor; + property FontDesc: string read GetFontDesc write SetFontDesc; + property Lines: TStringList read FLines; + property ParentShowHint; + property ShowHint; + property TabOrder; + property TextColor; + property OnChange: TNotifyEvent read FOnChange write FOnChange; + property OnEnter; + property OnExit; + property OnKeyPress; + end; + + +function CreateMemo(AOwner: TComponent; x, y, w, h: TfpgCoord): TfpgMemo; + + +implementation + +uses + fpg_stringutils; + +type + // custom stringlist that will notify the memo of item changes + TfpgMemoStrings = class(TStringList) + protected + Memo: TfpgMemo; + procedure RefreshMemo; + public + constructor Create(AMemo: TfpgMemo); reintroduce; + destructor Destroy; override; + function Add(const s: String): Integer; override; + procedure Clear; override; + procedure Delete(Index: Integer); override; + procedure Insert(Index: Integer; const S: string); override; + end; + +{ TfpgMemoStrings } + +procedure TfpgMemoStrings.RefreshMemo; +begin + if Assigned(Memo) and (Memo.HasHandle) then + begin + Memo.Invalidate; + Memo.UpdateScrollBars; + end; +end; + +constructor TfpgMemoStrings.Create(AMemo: TfpgMemo); +begin + inherited Create; + Memo := AMemo; +end; + +destructor TfpgMemoStrings.Destroy; +begin + Memo := nil; + inherited Destroy; +end; + +function TfpgMemoStrings.Add(const s: String): Integer; +begin + Result := inherited Add(s); + RefreshMemo; +end; + +procedure TfpgMemoStrings.Delete(Index: Integer); +begin +// writeln('Delete''s Index = ', Index); + inherited Delete(Index); + RefreshMemo; +end; + +procedure TfpgMemoStrings.Insert(Index: Integer; const S: string); +begin +// writeln('Insert''s Index = ', Index); + inherited Insert(Index, S); + RefreshMemo; +end; + +procedure TfpgMemoStrings.Clear; +begin + inherited Clear; + RefreshMemo; +end; + + +{ TfpgMemo } + + +function CreateMemo(AOwner: TComponent; x, y, w, h: TfpgCoord): TfpgMemo; +begin + Result := TfpgMemo.Create(AOwner); + Result.Left := x; + Result.Top := y; + Result.Width := w; + if h > 0 then + Result.Height := h; +end; + + +procedure TfpgMemo.SetCursorLine(aValue: integer); +var + i: integer; + MaxLine: integer; + yp: integer; +begin + if (aValue < 0) or (aValue = FCursorLine) then + Exit; // wrong value + if aValue < FFirstLine then + begin + FFirstLine := aValue; // moves the selected line to the top of the displayed rectangle + FCursorLine := aValue; + FCursorPos := 0; + RePaint; + Exit; + end; + yp := 2; + MaxLine := 0; + for i := FFirstLine to LineCount-1 do + begin + yp := yp + LineHeight; + if yp > Height then + begin + MaxLine := i - 1; + break; + end; + end; + if MaxLine < aValue then + begin + FFirstLine := aValue; + FCursorLine := aValue; + FCursorPos := 0; + RePaint; + Exit; + end + else + begin + FCursorLine := aValue; + FCursorPos := 0; + RePaint; + Exit; + end; +end; + +procedure TfpgMemo.UpdateScrollBarCoords; +var + HWidth: integer; + VHeight: integer; +begin + VHeight := Height - 4; + HWidth := Width - 4; + + if FVScrollBar.Visible then + Dec(HWidth, FVScrollBar.Width); + if FHScrollBar.Visible then + Dec(VHeight, FHScrollBar.Height); + + FHScrollBar.Top := Height -FHScrollBar.Height - 2; + FHScrollBar.Left := 2; + FHScrollBar.Width := HWidth; + + FVScrollBar.Top := 2; + FVScrollBar.Left := Width - FVScrollBar.Width - 2; + FVScrollBar.Height := VHeight; + + FVScrollBar.UpdateWindowPosition; + FHScrollBar.UpdateWindowPosition; +end; + +constructor TfpgMemo.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + Focusable := True; + FFont := fpgGetFont('#Edit1'); + FHeight := FFont.Height * 3 + 4; + FWidth := 120; + FLineHeight := FFont.Height + 2; + FSelecting := False; + FSideMargin := 3; + FMaxLength := 0; + FWrapping := False; + FOnChange := nil; + FTextColor := Parent.TextColor; + FBackgroundColor := clBoxColor; + FUseTabs := False; + FTabWidth := 4; + FMinWidth := 20; + FMinHeight := 30; + + FLines := TfpgMemoStrings.Create(self); + FFirstLine := 0; + FCursorLine := 0; + + FCursorPos := 0; + FSelStartPos := FCursorPos; + FSelEndPos := 0; + FSelStartLine := -1; + FSelEndLine := -1; + + FDrawOffset := 0; + FMouseDragging := 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; +end; + +destructor TfpgMemo.Destroy; +begin + TfpgMemoStrings(FLines).Free; + FFont.Free; + inherited Destroy; +end; + +procedure TfpgMemo.RecalcLongestLine; +var + n: integer; + lw: TfpgCoord; +begin + FLongestLineWidth := 0; + for n := 0 to LineCount-1 do + begin + lw := FFont.TextWidth(getlinetext(n)); + if lw > FlongestLineWidth then + FlongestLineWidth := lw; + end; +end; + +function TfpgMemo.GetFontDesc: string; +begin + Result := FFont.FontDesc; +end; + +procedure TfpgMemo.DeleteSelection; +var + n: integer; + selsl: integer; + selsp: integer; + selel: integer; + selep: integer; + ls: string; + len: integer; + st: integer; +begin + if FSelEndLine < 0 then + Exit; + + if (FSelStartLine shl 16) + FSelStartPos <= (FSelEndLine shl 16) + FSelEndPos then + begin + selsl := FSelStartLine; + selsp := FSelStartPos; + selel := FSelEndLine; + selep := FSelEndPos; + end + else + begin + selel := FSelStartLine; + selep := FSelStartPos; + selsl := FSelEndLine; + selsp := FSelEndPos; + end; + + for n := selsl to selel do + begin + ls := GetLineText(n); + + if selsl < n then + st := 0 + else + st := selsp; + if selel > n then + len := UTF8Length(ls) + else + len := selep - st; + + UTF8Delete(ls, st + 1, len); + SetLineText(n, ls); + end; + + if selsl < selel then + begin + ls := GetlineText(selsl); + ls := ls + GetLineText(selel); + SetLineText(selsl, ls); + end; + + for n := selsl to selel do + FLines.Delete(n); + + FCursorPos := selsp; + FCursorLine := selsl; + FSelEndLine := -1; +end; + +procedure TfpgMemo.DoCopy; +var + n: integer; + selsl: integer; + selsp: integer; + selel: integer; + selep: integer; + ls: string; + len: integer; + st: integer; + s: string; +begin + if FSelEndLine < 0 then + Exit; + + if (FSelStartLine shl 16) + FSelStartPos <= (FSelEndLine shl 16) + FSelEndPos then + begin + selsl := FSelStartLine; + selsp := FSelStartPos; + selel := FSelEndLine; + selep := FSelEndPos; + end + else + begin + selel := FSelStartLine; + selep := FSelStartPos; + selsl := FSelEndLine; + selsp := FSelEndPos; + end; + + s := ''; + + for n := selsl to selel do + begin + if n > selsl then + s := s + #13#10; + + ls := GetLineText(n); + + if selsl < n then + st := 0 + else + st := selsp; + + if selel > n then + len := UTF8Length(ls) + else + len := selep - st; + + s := s + UTF8Copy(ls, st + 1, len); + end; + + //SetClipboardText(s); +end; + +procedure TfpgMemo.DoPaste; +{ +var + s: string; + si: string; + si8: string; + lineend: string; + n: integer; + l: integer; + lcnt: integer; +} +begin + Exit; + (* + DeleteSelection; + s := GetClipboardText; + + si := UTF8Copy(CurrentLine,1,FCursorPos); + lineend := UTF8Copy(CurrentLine,FCursorPos+1, UTF8Length(CurrentLine)); + l := FCursorLine; + n := 1; + lcnt := 0; + si8 := ''; + while n <= length(s) do + begin + if (s[n] = #13) or (s[n] = #10) then + begin + if lcnt = 0 then SetLineText(l, si + si8) + else FLines.Insert(l-1, si + si8); + + si := ''; + si8 := ''; + inc(lcnt); + inc(l); + + // skip multibyte line end: + if (s[n]=#13) and (n < length(s)) and (s[n+1]=#10) then inc(n); + end + else + begin + si8 := si8 + s[n]; + end; + inc(n); + end; + + si := si + si8; + + FCursorPos := UTF8Length(si); + si := si + lineend; + + if lcnt = 0 then + begin + SetLineText(l, si) + end + else + begin + FLines.Insert(l-1, si); + FCursorLine := l; + end; + + AdjustCursor; + Repaint; +*) +end; + +procedure TfpgMemo.AdjustCursor; +var + tw: integer; +begin + // horizontal adjust + RecalcLongestLine; + tw := FFont.TextWidth(UTF8Copy(CurrentLine, 1, FCursorPos)); + + if tw - FDrawOffset > VisibleWidth - 2 then + FDrawOffset := tw - VisibleWidth + 2 + else if tw - FDrawOffset < 0 then + begin + FDrawOffset := tw; + if tw <> 0 then + Dec(FDrawOffset, 2); + end; + + // vertical adjust + if FCursorLine < FFirstLine then + FFirstLine := FCursorLine; + if FCursorline - FFirstLine + 1 > VisibleLines then + FFirstLine := FCursorline - VisibleLines + 1; + + if (FFirstLine + VisibleLines) > LineCount then + begin + FFirstLine := LineCount - VisibleLines + 1; + if FFirstline < 0 then + FFirstLine := 0; + end; + + UpdateScrollbars; +end; + +procedure TfpgMemo.UpdateScrollBars; +var + vlines: integer; + vsbw: integer; + hsbwas: boolean; + vsbwas: boolean; + vsbvis: boolean; +begin + hsbwas := FHScrollBar.Visible; + vsbwas := FVScrollBar.Visible; + vlines := (Height - (FSideMargin shl 1)) div Lineheight; + vsbvis := (LineCount > vlines); + + if vsbvis then + vsbw := FVScrollBar.Width + else + vsbw := 0; + + FHScrollBar.Visible := FLongestLineWidth > (Width - vsbw - FSideMargin * 2) - 1; + + if FHScrollBar.Visible and not vsbvis then + begin + // recheck vertical scrollbar + vlines := (Height - (FSideMargin shl 1) - FHScrollBar.Height) div Lineheight; + vsbvis := (LineCount > vlines); + end; + + FVScrollBar.Visible := vsbvis; + + UpdateScrollBarCoords; + + if FHScrollBar.Visible then + begin + FHScrollBar.Min := 0; + FHScrollBar.Max := FLongestLineWidth - VisibleWidth - 1; + if (FLongestLineWidth <= 0) or (FLongestLineWidth <= VisibleWidth) then + FHScrollBar.SliderSize := 1 + else + FHScrollBar.SliderSize := VisibleWidth / FLongestLineWidth; + FHScrollBar.Position := FDrawOffset; + FHScrollBar.RepaintSlider; + end; + + if FVScrollBar.Visible then + begin + FVScrollBar.Min := 0; + // TODO: Look at calculation of vlines value to improve this! + if LineCount > 0 then + begin + FVScrollBar.SliderSize := VisibleLines / LineCount; + FVScrollBar.Max := LineCount - VisibleLines; + end + else + begin + FVScrollBar.SliderSize := 0.5; + FVScrollBar.Max := 10; + end; + FVScrollBar.Position := FFirstLine; + FVScrollBar.RepaintSlider; + end; + + if (hsbwas <> FHScrollBar.Visible) or (vsbwas <> FVScrollBar.Visible) then + AdjustCursor; +end; + +function TfpgMemo.LineCount: integer; +begin + Result := FLines.Count; +end; + +function TfpgMemo.GetLineText(linenum: integer): string; +begin + if LineCount = 0 then + FLines.Add(''); + if (linenum >= 0) and (linenum < LineCount) then + Result := FLines.Strings[linenum] + else + Result := ''; +end; + +procedure TfpgMemo.SetFontDesc(const AValue: string); +begin + FFont.Free; + FFont := fpgGetFont(AValue); + RePaint; +end; + +procedure TfpgMemo.SetLineText(linenum: integer; Value: string); +begin + FLines.Strings[linenum] := Value; +end; + +function TfpgMemo.GetCursorX: integer; +begin + Result := FFont.TextWidth(copy(CurrentLine, 1, FCursorPos)); +end; + +// Set cursor position by X +procedure TfpgMemo.SetCPByX(x: integer); +var + n: integer; + cpx: integer; + cp: integer; + cx: integer; + ls: string; +begin + // searching the appropriate character position + ls := CurrentLine; + cpx := FFont.TextWidth(UTF8Copy(ls, 1, FCursorPos)); // + FDrawOffset + FSideMargin; + cp := FCursorPos; + if cp > UTF8Length(ls) then + cp := UTF8Length(ls); + + for n := 0 to UTF8Length(ls) do + begin + cx := FFont.TextWidth(UTF8Copy(ls, 1, n)); // + FDrawOffset + FSideMargin; + if abs(cx - x) < abs(cpx - x) then + begin + cpx := cx; + cp := n; + end; + end; + + FCursorPos := cp; +end; + +function TfpgMemo.CurrentLine: string; +begin + Result := GetLineText(FCursorLine); +end; + +function TfpgMemo.VisibleLines: integer; +var + sh: integer; +begin + if FHScrollBar.Visible then + sh := 18 + else + sh := 0; + Result := (Height - (FSideMargin shl 1) - sh) div Lineheight; +end; + +function TfpgMemo.VisibleWidth: integer; +var + sw: integer; +begin + if FVScrollBar.Visible then + sw := FVScrollBar.Width + else + sw := 0; + Result := (Width - (FSideMargin shl 1) - sw); +end; + +procedure TfpgMemo.HandleShow; +begin + inherited HandleShow; + if (csLoading in ComponentState) then + Exit; + RecalcLongestLine; + UpdateScrollBars; + UpdateScrollBarCoords; +end; + +procedure TfpgMemo.HandleMouseEnter; +begin + inherited HandleMouseEnter; + MouseCursor := mcIBeam; +end; + +procedure TfpgMemo.HandleMouseExit; +begin + inherited HandleMouseExit; + MouseCursor := mcDefault; +end; + +procedure TfpgMemo.VScrollBarMove(Sender: TObject; position: integer); +begin + if FFirstLine <> position then + begin + FFirstLine := position; + repaint; + end; +end; + +procedure TfpgMemo.HScrollBarMove(Sender: TObject; position: integer); +begin + if position <> FDrawOffset then + begin + FDrawOffset := position; + Repaint; + end; +end; + +procedure TfpgMemo.HandlePaint; +var + n: integer; + tw, tw2, st, len: integer; + yp, xp: integer; + ls: string; + r: TfpgRect; + selsl, selsp, selel, selep: integer; + c: integer; + s: string; +begin + Canvas.ClearClipRect; + r.SetRect(0, 0, Width, Height); + Canvas.DrawControlFrame(r); + + InflateRect(r, -2, -2); + Canvas.SetClipRect(r); + + if Enabled then + Canvas.SetColor(FBackgroundColor) + else + Canvas.SetColor(clWindowBackground); + Canvas.FillRectAngle(r); + + Canvas.SetTextColor(FTextColor); + Canvas.SetFont(FFont); + + if (FSelStartLine shl 16) + FSelStartPos <= (FSelEndLine shl 16) + FSelEndPos then + begin + selsl := FSelStartLine; + selsp := FSelStartPos; + selel := FSelEndLine; + selep := FSelEndPos; + end + else + begin + selel := FSelStartLine; + selep := FSelStartPos; + selsl := FSelEndLine; + selsp := FSelEndPos; + end; + + yp := 3; + for n := FFirstline to LineCount-1 do + begin + ls := GetLineText(n); + if FUseTabs then + begin + xp := 0; + s := ''; + for c := 1 to Length(ls) do + begin + if ls[c] = #9 then + begin + if s <> '' then + Canvas.DrawString(-FDrawOffset + FSideMargin + xp, yp, s); + xp := xp + Canvas.Font.TextWidth(' ') * FTabWidth; + s := ''; + end + else + s := s + ls[c]; + end; + if s <> '' then + Canvas.DrawString(-FDrawOffset + FSideMargin + xp, yp, s); + end + else + Canvas.DrawString(-FDrawOffset + FSideMargin, yp, ls); + + if Focused then + begin + // drawing selection + if (FSelEndLine > -1) and (selsl <= n) and (selel >= n) then + begin + if selsl < n then + st := 0 + else + st := selsp; + if selel > n then + len := UTF8Length(ls) + else + len := selep - st; + + tw := FFont.TextWidth(UTF8Copy(ls, 1, st)); + tw2 := FFont.TextWidth(UTF8Copy(ls, 1, st + len)); + Canvas.XORFillRectangle(fpgColorToRGB(clSelection) xor $FFFFFF, -FDrawOffset + + FSideMargin + tw, yp, tw2 - tw, LineHeight); + end; + + //drawing cursor + if FCursorLine = n then + begin + // drawing cursor + tw := FFont.TextWidth(UTF8Copy(ls, 1, FCursorPos)); + fpgCaret.SetCaret(Canvas, -FDrawOffset + FSideMargin + tw, yp, fpgCaret.Width, FFont.Height); + end; + end; { if } + + yp := yp + LineHeight; + if yp > Height then + Break; + end; { for } + + if not Focused then + fpgCaret.UnSetCaret(Canvas); + + // 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 TfpgMemo.HandleKeyChar(var AText: TfpgChar; var shiftstate: TShiftState; var consumed: boolean); +var + prevval: string; + s: string; + ls: string; +begin + inherited; + prevval := Text; + s := AText; + + // Printable characters only + // Note: This is now UTF-8 compliant! + if (Ord(AText[1]) > 31) and (Ord(AText[1]) < 127) or (Length(AText) > 1) then + begin + if (FMaxLength <= 0) or (UTF8Length(FLines.Text) < FMaxLength) then + begin + if FCursorLine < 0 then + FCursorLine := 0; + DeleteSelection; + ls := GetLineText(FCursorLine); + UTF8Insert(s, ls, FCursorPos + 1); + SetLineText(FCursorLine, ls); + Inc(FCursorPos); + FSelStartPos := FCursorPos; + FSelStartLine := FCursorLine; + FSelEndLine := -1; + AdjustCursor; + end; + + consumed := True; + end; + + if prevval <> Text then + if Assigned(FOnChange) then + FOnChange(self); + + if consumed then + RePaint; +end; + +procedure TfpgMemo.HandleKeyPress(var keycode: word; + var shiftstate: TShiftState; var consumed: boolean); +var + cx: integer; + ls: string; + ls2: string; + hasChanged: boolean; + + procedure StopSelection; + begin + FSelStartLine := FCursorLine; + FSelStartPos := FCursorPos; + FSelEndLine := -1; + end; + +begin + Consumed := True; + hasChanged := False; + case CheckClipBoardKey(keycode, shiftstate) of + ckCopy: + begin + DoCopy; + end; + ckPaste: + begin + DoPaste; + hasChanged := True; + end; + ckCut: + begin + DoCopy; + DeleteSelection; + hasChanged := True; + end; + else + Consumed := False; + end; + + if not Consumed then + begin + // checking for movement keys: + consumed := True; + FSelecting := (ssShift in shiftstate); + + case keycode of + keyLeft: + if FCursorPos > 0 then + begin + Dec(FCursorPos); + + if (ssCtrl in shiftstate) then + // word search... + (* + while (FCursorPos > 0) and not pgfIsAlphaNum(copy(CurrentLine,FCursorPos,1)) + do Dec(FCursorPos); + + while (FCursorPos > 0) and pgfIsAlphaNum(copy(CurrentLine,FCursorPos,1)) + do Dec(FCursorPos); + *); + + end;// left + + keyRight: + if FCursorPos < UTF8Length(CurrentLine) then + begin + Inc(FCursorPos); + + if (ssCtrl in shiftstate) then + // word search... + (* + while (FCursorPos < length(CurrentLine)) and pgfIsAlphaNum(copy(CurrentLine,FCursorPos+1,1)) + do Inc(FCursorPos); + + while (FCursorPos < length(CurrentLine)) and not pgfIsAlphaNum(copy(CurrentLine,FCursorPos+1,1)) + do Inc(FCursorPos); + *); + + end;// right + + keyUp: + begin // up + cx := GetCursorX; + if FCursorLine > 0 then + begin + Dec(FCursorline); + SetCPByX(cx); + end; + end; + + keyDown: + begin + cx := GetCursorX; + if FCursorLine < (LineCount-1) then + begin + Inc(FCursorline); + SetCPByX(cx); + end; + end; + + keyHome: + begin + if (ssCtrl in shiftstate) then + FCursorLine := 0; + FCursorPos := 0; + end; + + keyEnd: + begin + if (ssCtrl in shiftstate) then + FCursorLine := LineCount-1; + FCursorPos := UTF8Length(CurrentLine); + end; + + keyPageUp: + if FCursorLine > 0 then + begin + cx := GetCursorX; + Dec(FCursorLine, VisibleLines); + if FCursorLine < 0 then + FCursorLine := 0; + SetCPByX(cx); + end; + + keyPageDown: + begin + cx := GetCursorX; + if FCursorLine < (LineCount-1) then + begin + Inc(FCursorline, VisibleLines); + if FCursorLine > (LineCount-1) then + FCursorLine := LineCount-1; + SetCPByX(cx); + end; + end; + + else + Consumed := False; + end; + + if Consumed then + begin + AdjustCursor; + + if FSelecting then + begin + FSelEndPos := FCursorPos; + FSelEndLine := FCursorLine; + end + else + StopSelection; + end; + end; + + if not Consumed then + begin + consumed := True; + + case keycode of + keyReturn, + keyPEnter: + begin + ls := UTF8Copy(FLines[FCursorline], 1, FCursorPos); + ls2 := UTF8Copy(FLines[FCursorline], FCursorPos + 1, UTF8Length(FLines[FCursorline])); + FLines.Insert(FCursorLine, ls); + Inc(FCursorLine); + SetLineText(FCursorLine, ls2); + FCursorPos := 0; + hasChanged := True; + end; + + keyBackSpace: + begin + if FCursorPos > 0 then + begin + ls := GetLineText(FCursorLine); + UTF8Delete(ls, FCursorPos, 1); + SetLineText(FCursorLine, ls); + Dec(FCursorPos); + end + else if FCursorLine > 0 then + begin + ls := CurrentLine; + FLines.Delete(FCursorLine); + Dec(FCursorLine); + FCursorPos := UTF8Length(FLines.Strings[FCursorLine]); + FLines.Strings[FCursorLine] := FLines.Strings[FCursorLine] + ls; + end; + hasChanged := True; + end; + + keyDelete: + begin + ls := GetLineText(FCursorLine); + if FSelEndLine > -1 then + DeleteSelection + else if FCursorPos < UTF8Length(ls) then + begin + UTF8Delete(ls, FCursorPos + 1, 1); + SetLineText(FCursorLine, ls); + end + else if FCursorLine < (LineCount-1) then + begin + ls2 := FLines.Strings[FCursorLine+1]; + FLines.Delete(FCursorLine); + FLines.Strings[FCursorLine] := ls + ls2; + end; + hasChanged := True; + end; + + keyTab: + begin + if FUseTabs then + begin + ls := GetLineText(FCursorLine); +{ if FSelEndLine > 0 then + DeleteSelection + else} if FCursorPos < UTF8Length(ls) then + begin + UTF8Insert(#9, ls, FCursorPos); + SetLineText(FCursorLine, ls); + end; +{ + else if FCursorLine < LineCount then + begin + ls2 := FLines.Strings[FCursorLine]; + FLines.Delete(FCursorLine); + FLines.Strings[FCursorLine - 1] := ls + ls2; + end; +} + hasChanged := True; + end + else + Consumed := False; + end; + else + Consumed := False; + end; + + if Consumed then + begin + StopSelection; + AdjustCursor; + end; + end; + + if Consumed then + RePaint + else + inherited; + + if hasChanged then + if Assigned(FOnChange) then + FOnChange(self); +end; + +procedure TfpgMemo.HandleLMouseDown(x, y: integer; shiftstate: TShiftState); +var + n: integer; + cpx: integer; + cp: integer; + cx: integer; + lnum: integer; + ls: string; +begin + inherited HandleLMouseDown(x, y, shiftstate); + + // searching the appropriate character position + lnum := FFirstLine + (y - FSideMargin) div LineHeight; + if lnum > (LineCount-1) then + lnum := LineCount-1; + + ls := GetLineText(lnum); + cpx := FFont.TextWidth(UTF8Copy(ls, 1, FCursorPos)) - FDrawOffset + FSideMargin; + cp := FCursorPos; + + for n := 0 to UTF8Length(ls) do + begin + cx := FFont.TextWidth(UTF8Copy(ls, 1, n)) - FDrawOffset + FSideMargin; + if abs(cx - x) < abs(cpx - x) then + begin + cpx := cx; + cp := n; + end; + end; + + FMouseDragging := True; + FMouseDragPos := cp; + FCursorPos := cp; + FCursorLine := lnum; + + if (ssShift in shiftstate) then + begin + FSelEndLine := lnum; + FSelEndpos := cp; + end + else + begin + FSelStartLine := lnum; + FSelStartPos := cp; + FSelEndLine := -1; + end; + Repaint; +end; + +procedure TfpgMemo.HandleRMouseUp(x, y: integer; shiftstate: TShiftState); +begin + inherited HandleRMouseUp(x, y, shiftstate); + if Assigned(PopupMenu) then + PopupMenu.ShowAt(self, x, y); +end; + +procedure TfpgMemo.HandleMouseMove(x, y: integer; btnstate: word; shiftstate: TShiftState); +var + n: integer; + cpx: integer; + cp: integer; + cx: integer; + lnum: integer; + ls: string; +begin + if not FMouseDragging or ((btnstate and 1) = 0) then + begin + FMouseDragging := False; + Exit; + end; + + // searching the appropriate character position + lnum := FFirstLine + (y - FSideMargin) div LineHeight; + if lnum > LineCount-1 then + lnum := LineCount-1; + + ls := GetLineText(lnum); + cpx := FFont.TextWidth(UTF8Copy(ls, 1, FCursorPos)) - FDrawOffset + FSideMargin; + cp := FCursorPos; + + for n := 0 to UTF8Length(ls) do + begin + cx := FFont.TextWidth(UTF8Copy(ls, 1, n)) - FDrawOffset + FSideMargin; + if abs(cx - x) < abs(cpx - x) then + begin + cpx := cx; + cp := n; + end; + end; + + if (cp <> FCursorPos) or (lnum <> FCursorLine) then + begin + FCursorLine := lnum; + FSelEndLine := lnum; + FSelEndPos := cp; + FCursorPos := cp; + Repaint; + end; + + + // searching the appropriate character position + { + cpx := FFont.TextWidth16(copy16(FText,1,FCursorPos)) + FDrawOffset + FSideMargin; + cp := FCursorPos; + + s := ''; + + for n := 0 to Length16(Text) do + begin + cx := FFont.TextWidth16(copy16(Text,1,n)) + FDrawOffset + FSideMargin; + if abs(cx - x) < abs(cpx - x) then + begin + cpx := cx; + cp := n; + end; + end; + + //FMouseDragPos := cp; + FSelOffset := cp-FSelStart; + if FCursorPos <> cp then + begin + FCursorPos := cp; + Repaint; + end; +} +end; + +(* +procedure TfpgMemo.HandleWindowScroll(direction, amount: integer); +var + pfl, pdo : integer; +begin + inherited HandleWindowScroll(direction, amount); + + pfl := FFirstLine; + pdo := FDrawOffset; + + if direction = 0 then + begin + dec(FFirstLine, amount); + end; + if direction = 1 then + begin + inc(FFirstLine, amount); + end; + if FFirstLine > LineCount - VisibleLines + 1 then FFirstLine := LineCount - VisibleLines + 1; + if FFirstLine < 1 then FFirstLine := 1; + + if FHScrollBar.Visible then + begin + if Direction = 2 then + begin + dec(FDrawOffset, amount*16); + end; + if Direction = 3 then + begin + inc(FDrawOffset, amount*16); + end; + + if FDrawOffset > FHScrollBar.Max then FDrawOffset := FHScrollBar.Max; + if FDrawOffset < 0 then FDrawOffset := 0; + end; + + if (pfl <> FFirstLine) or (pdo <> FDrawOffset) then + begin + UpdateScrollBars; + Repaint; + end; + +end; +*) + +procedure TfpgMemo.HandleResize(dwidth, dheight: integer); +begin + inherited HandleResize(dwidth, dheight); + if (csLoading in ComponentState) then + Exit; + UpdateScrollBarCoords; + UpdateScrollBars; +end; + +procedure TfpgMemo.HandleMouseScroll(x, y: integer; shiftstate: TShiftState; + delta: smallint); +var + pfl, pdo : integer; +begin + inherited HandleMouseScroll(x, y, shiftstate, delta); + + pfl := FFirstLine; + pdo := FDrawOffset; + + if delta < 0 then + dec(FFirstLine, abs(delta)) // scroll up + else + inc(FFirstLine, abs(delta)); // scroll down + + if FFirstLine > LineCount - VisibleLines{ + 1} then + FFirstLine := LineCount - VisibleLines {+ 1}; + if FFirstLine < 0 then + FFirstLine := 0; + + if FHScrollBar.Visible then + begin + if FDrawOffset > FHScrollBar.Max then + FDrawOffset := FHScrollBar.Max; + if FDrawOffset < 0 then + FDrawOffset := 0; + end; + + if (pfl <> FFirstLine) or (pdo <> FDrawOffset) then + begin + UpdateScrollBars; + Repaint; + end; +end; + +function TfpgMemo.SelectionText: string; +begin + { + if FSelOffset <> 0 then + begin + if FSelOffset < 0 then + begin + Result := Copy(FText,1+FSelStart + FSelOffset,-FSelOffset); + end + else + begin + result := Copy(FText,1+FSelStart,FSelOffset); + end; + end + else +} + Result := ''; +end; + +function TfpgMemo.GetText: TfpgString; +var + n: integer; + s: TfpgString; +begin + s := ''; + for n := 0 to LineCount-1 do + begin + if n > 0 then + s := s + #13#10; + s := s + GetLineText(n); + end; + Result := s; +end; + +procedure TfpgMemo.SetText(const AValue: TfpgString); +var + n: integer; + c: TfpgChar; + s: TfpgString; +begin + FLines.Clear; + s := ''; + n := 1; + while n <= UTF8Length(AValue) do + begin + c := UTF8Copy(AValue, n, 1); + if (c[1] = #13) or (c[1] = #10) then + begin + FLines.Add(s); + s := ''; + c := UTF8Copy(AValue, n + 1, 1); + if c[1] = #10 then + Inc(n); + end + else + s := s + c; + Inc(n); + end; + + if s <> '' then + FLines.Add(s); + + FDrawOffset := 0; + FCursorPos := 0; + FCursorLine := 0; + FSelStartLine := FCursorLine; + FSelStartPos := FCursorPos; + FSelEndLine := -1; + + AdjustCursor; + Repaint; +end; + +end. + |