From 28822fe6e81a8be362bcc4e5b0b025f3f3454399 Mon Sep 17 00:00:00 2001 From: zeljko Date: Sat, 27 Nov 2010 15:27:40 +0000 Subject: [PATCH] Qt: rewritten most of TQtMemoStrings, simplified TQtWSCustomMemo.Fixes issues #17933,#18063,#18067 git-svn-id: trunk@28509 - --- lcl/interfaces/qt/qtprivate.pp | 238 ++++++++++++++++-------------- lcl/interfaces/qt/qtwidgets.pas | 38 ++++- lcl/interfaces/qt/qtwsstdctrls.pp | 18 +-- 3 files changed, 173 insertions(+), 121 deletions(-) diff --git a/lcl/interfaces/qt/qtprivate.pp b/lcl/interfaces/qt/qtprivate.pp index aa8630709b..7fed1fbc1c 100644 --- a/lcl/interfaces/qt/qtprivate.pp +++ b/lcl/interfaces/qt/qtprivate.pp @@ -87,22 +87,20 @@ type TQtMemoStrings = class(TStrings) private - FTextChangedHook : QTextEdit_hookH; - FTextChanged: Boolean; // StringList and QtTextEdit out of sync + FTextChanged: Boolean; // Inform TQtMemoStrings about change in TextChange event FStringList: TStringList; // Holds the lines to show FOwner: TWinControl; // Lazarus Control Owning MemoStrings - FUpdating: Boolean; // We're changing Qt Widget procedure InternalUpdate; - procedure ExternalUpdate(var Astr: WideString; AClear: Boolean = True); - procedure IsChanged; // OnChange triggered by program action + procedure ExternalUpdate(var AStr: WideString; + AClear, ABlockSignals: Boolean); protected - function getTextEdit: QTextEditH; // QtWidget handle function GetTextStr: string; override; function GetCount: integer; override; function Get(Index : Integer) : string; override; procedure Put(Index: Integer; const S: string); override; + procedure SetTextStr(const Value: string); override; public - constructor Create(TextEdit : QTextEditH; TheOwner: TWinControl); + constructor Create(TheOwner: TWinControl); destructor Destroy; override; procedure Assign(Source : TPersistent); override; procedure Clear; override; @@ -111,7 +109,7 @@ type procedure SetText(TheText: PChar); override; public property Owner: TWinControl read FOwner; - procedure TextChangedHandler; cdecl; + property TextChanged: Boolean read FTextChanged write FTextChanged; end; implementation @@ -128,69 +126,61 @@ implementation procedure TQtMemoStrings.InternalUpdate; var W: WideString; - TextEdit: QTextEditH; + TextEdit: TQtTextEdit; begin - TextEdit := getTextEdit; - if TextEdit <> nil then - QTextEdit_toPlainText(TextEdit, @W); // get the memo content - FStringList.Text := UTF16ToUTF8(W); + W := ''; + if FOwner.HandleAllocated then + begin + TextEdit := TQtTextEdit(FOwner.Handle); + W := TextEdit.getText; + end; + if W <> '' then + FStringList.Text := UTF16ToUTF8(W) + LineEnding + else + FStringList.Text := ''; FTextChanged := False; end; {------------------------------------------------------------------------------ Private Method: TQtMemoStrings.ExternalUpdate - Params: Astr: Text for Qt Widget; Clear: if we must clear first + Params: AStr: Text for Qt Widget; Clear: if we must clear first + ABlockSignals: block SignalTextChanged() so it does not send an + message to LCL. Returns: Nothing Updates Qt Widget from text - If DelphiOnChange, generates OnChange Event ------------------------------------------------------------------------------} -procedure TQtMemoStrings.ExternalUpdate(var Astr: WideString; AClear: Boolean = True); +procedure TQtMemoStrings.ExternalUpdate(var AStr: WideString; + AClear, ABlockSignals: Boolean); var W: WideString; - TextEdit: QTextEditH; + TextEdit: TQtTextEdit; + B: Boolean; begin - FUpdating := True; + if not FOwner.HandleAllocated then + exit; + {$ifdef VerboseQtMemoStrings} + writeln('TQtMemoStrings.ExternalUpdate'); + {$endif} + TextEdit := TQtTextEdit(FOwner.Handle); + if ABlockSignals then + TextEdit.BeginUpdate; W := GetUtf8String(AStr); - TextEdit := getTextEdit; - if TextEdit <> nil then + if AClear then begin - if AClear then - begin - QTextEdit_clear(TextEdit); - QTextEdit_setPlainText(TextEdit,@W); - end else - QTextEdit_append(TextEdit,@W); + // never trigger changed signal when clearing text here. + // we must clear text since QTextEdit can contain html text. + TextEdit.BeginUpdate; + TextEdit.ClearText; + TextEdit.EndUpdate; + TextEdit.setText(W); + end else + TextEdit.Append(W); - if QTextEdit_alignment(TextEdit) <> AlignmentMap[TCustomMemo(FOwner).Alignment] then - QTextEdit_setAlignment(TextEdit, AlignmentMap[TCustomMemo(FOwner).Alignment]); - end; - - FUpdating := False; - IsChanged; - FUpdating := False; -end; - -{------------------------------------------------------------------------------ - Private Method: TQtMemoStrings.IsChanged - Params: None - Returns: Nothing - - Triggers the OnChange Event, with modified set to false - ------------------------------------------------------------------------------} -procedure TQtMemoStrings.IsChanged; -begin - if Assigned(FOwner) and Assigned((FOwner as TCustomMemo).OnChange) then - begin - (FOwner as TCustomMemo).Modified := False; - (FOwner as TCustomMemo).OnChange(FOwner); - end; -end; - -function TQtMemoStrings.getTextEdit: QTextEditH; -begin - Result := nil; - if FOwner.HandleAllocated then - Result := QTextEditH(TQtTextEdit(FOwner.Handle).Widget); + if TextEdit.getAlignment <> AlignmentMap[TCustomMemo(FOwner).Alignment] then + TextEdit.setAlignment(AlignmentMap[TCustomMemo(FOwner).Alignment]); + if ABlockSignals then + TextEdit.EndUpdate; end; {------------------------------------------------------------------------------ @@ -201,9 +191,22 @@ end; Return the whole StringList content as a single string ------------------------------------------------------------------------------} function TQtMemoStrings.GetTextStr: string; +var + TextLen: Integer; begin + {$ifdef VerboseQtMemoStrings} + WriteLn('TQtMemoStrings.GetTextStr'); + {$endif} if FTextChanged then InternalUpdate; Result := FStringList.Text; + + // remove trailing line break + TextLen := Length(Result); + if (TextLen > 0) and (Result[TextLen] = #10) then + Dec(TextLen); + if (TextLen > 0) and (Result[TextLen] = #13) then + Dec(TextLen); + SetLength(Result, TextLen); end; {------------------------------------------------------------------------------ @@ -215,6 +218,9 @@ end; ------------------------------------------------------------------------------} function TQtMemoStrings.GetCount: integer; begin + {$ifdef VerboseQtMemoStrings} + WriteLn('TQtMemoStrings.GetCount'); + {$endif} if FTextChanged then InternalUpdate; Result := FStringList.Count; end; @@ -228,20 +234,42 @@ end; ------------------------------------------------------------------------------} function TQtMemoStrings.Get(Index: Integer): string; begin + {$ifdef VerboseQtMemoStrings} + WriteLn('TQtMemoStrings.Get Index=',Index); + {$endif} if FTextChanged then InternalUpdate; if Index < FStringList.Count then - Result := FStringList.Strings[Index] - else Result := ''; + Result := FStringList.Strings[Index] + else + Result := ''; end; procedure TQtMemoStrings.Put(Index: Integer; const S: string); var W: WideString; begin + {$ifdef VerboseQtMemoStrings} + WriteLn('TQtMemoStrings.Put Index=',Index,' S=',S); + {$endif} if FTextChanged then InternalUpdate; FStringList[Index] := S; W := GetUTF8String(S); + TQtTextEdit(FOwner.Handle).BeginUpdate; TQtTextEdit(FOwner.Handle).setLineText(Index, W); + TQtTextEdit(FOwner.Handle).EndUpdate; +end; + +procedure TQtMemoStrings.SetTextStr(const Value: string); +var + W: WideString; +begin + {$ifdef VerboseQtMemoStrings} + WriteLn('TQtMemoStrings.SetTextStr Value=',Value); + {$endif} + inherited SetTextStr(Value); + FStringList.Text := Value; + W := FStringList.Text; + ExternalUpdate(W, True, False); end; {------------------------------------------------------------------------------ @@ -251,22 +279,15 @@ end; Constructor for the class. ------------------------------------------------------------------------------} -constructor TQtMemoStrings.Create(TextEdit: QTextEditH; TheOwner: TWinControl); +constructor TQtMemoStrings.Create(TheOwner: TWinControl); begin inherited Create; - {$ifdef VerboseQt} - if (TextEdit = nil) then WriteLn('TQtMemoStrings.Create Unspecified TextEdit widget'); - if (TheOwner = nil) then WriteLn('TQtMemoStrings.Create Unspecified owner'); + if (TheOwner = nil) then + WriteLn('TQtMemoStrings.Create Unspecified owner'); {$endif} - FStringList := TStringList.Create; - QTextEdit_clear(TextEdit); - FOwner:=TheOwner; - // Callback Event - {Method := MemoChanged; } - FTextChangedHook := QTextEdit_hook_create(TextEdit); - QTextEdit_hook_hook_textChanged(FTextChangedHook, @TextChangedHandler); + FOwner := TheOwner; end; {------------------------------------------------------------------------------ @@ -280,32 +301,9 @@ destructor TQtMemoStrings.Destroy; begin Clear; FStringList.Free; - // don't destroy the widgets - if FTextChangedHook <> nil then - QTextEdit_hook_destroy(FTextChangedHook); inherited Destroy; end; -{------------------------------------------------------------------------------ - Method: TQtMemoStrings.TextChangedHandler - Params: None - Returns: Nothing - - Signal handler for the TextChanged Signal. - ------------------------------------------------------------------------------} -procedure TQtMemoStrings.TextChangedHandler; cdecl; -var - Mess: TLMessage; -begin - if not FUpdating then - begin - FTextChanged := True; - FillChar(Mess, SizeOf(Mess), #0); - Mess.Msg := CM_TEXTCHANGED; - FOwner.Dispatch(TLMessage(Mess)); - end; -end; - {------------------------------------------------------------------------------ Method: TQtMemoStrings.Assign Params: None @@ -317,15 +315,20 @@ procedure TQtMemoStrings.Assign(Source: TPersistent); var W: WideString; begin - if (Source=Self) or (Source=nil) - then + if (Source=Self) or (Source=nil) then exit; + if not FOwner.HandleAllocated then + exit; + if Source is TStrings then begin + {$ifdef VerboseQtMemoStrings} + writeln('TQtMemoStrings.Assign - handle ? ', FOwner.HandleAllocated); + {$endif} FStringList.Clear; FStringList.Text := TStrings(Source).Text; W := FStringList.Text; - ExternalUpdate(W,True); + ExternalUpdate(W, True, False); FTextChanged := False; exit; end; @@ -340,20 +343,20 @@ end; Clears all. ------------------------------------------------------------------------------} procedure TQtMemoStrings.Clear; -var - TextEdit: QTextEditH; begin - FUpdating := True; FStringList.Clear; - TextEdit := getTextEdit; - if not (csDestroying in FOwner.ComponentState) - and not (csFreeNotification in FOwner.ComponentState) - and (TextEdit <> nil) then - QTextEdit_clear(TextEdit); - - FTextChanged := False; - FUpdating := False; - IsChanged; + if not (csDestroying in FOwner.ComponentState) and + not (csFreeNotification in FOwner.ComponentState) and + FOwner.HandleAllocated then + begin + {$ifdef VerboseQtMemoStrings} + writeln('TQtMemoStrings.Clear'); + {$endif} + TQtTextEdit(FOwner.Handle).BeginUpdate; + TQtTextEdit(FOwner.Handle).ClearText; + TQtTextEdit(FOwner.Handle).EndUpdate; + FTextChanged := False; + end; end; {------------------------------------------------------------------------------ @@ -368,8 +371,13 @@ begin if FTextChanged then InternalUpdate; if (Index >= 0) and (Index < FStringList.Count) then begin + {$ifdef VerboseQtMemoStrings} + writeln('TQtMemoStrings.Delete'); + {$endif} FStringList.Delete(Index); - TQtTextEdit(FOwner.Handle).removeLine(Index); + TQtTextEdit(FOwner.Handle).BeginUpdate; + TQtTextEdit(FOwner.Handle).RemoveLine(Index); + TQtTextEdit(FOwner.Handle).EndUpdate; end; end; @@ -386,6 +394,11 @@ var begin if FTextChanged then InternalUpdate; if Index < 0 then Index := 0; + + {$ifdef VerboseQtMemoStrings} + writeln('TQtMemoStrings.Insert Index=',Index); + {$endif} + if Index <= FStringList.Count then begin FStringList.Insert(Index, S); @@ -395,18 +408,22 @@ begin begin // workaround for qt richtext parser bug W := GetUTF8String(S); + TQtTextEdit(FOwner.Handle).BeginUpdate; TQtTextEdit(FOwner.Handle).insertLine(Index, W); + TQtTextEdit(FOwner.Handle).EndUpdate; end else begin // append is much faster in case when we add strings W := S; - ExternalUpdate(W, False); + ExternalUpdate(W, False, True); FTextChanged := False; end; end else begin W := GetUTF8String(S); + TQtTextEdit(FOwner.Handle).BeginUpdate; TQtTextEdit(FOwner.Handle).insertLine(Index, W); + TQtTextEdit(FOwner.Handle).EndUpdate; end; end; end; @@ -423,10 +440,13 @@ Var S: String; W: WideString; begin + {$ifdef VerboseQtMemoStrings} + writeln('TQtMemoStrings.SetText'); + {$endif} S := StrPas(TheText); FStringList.Text := S; W := S; - ExternalUpdate(W,True); + ExternalUpdate(W, True, False); FTextChanged := False; end; diff --git a/lcl/interfaces/qt/qtwidgets.pas b/lcl/interfaces/qt/qtwidgets.pas index 809723df27..1182589ae1 100644 --- a/lcl/interfaces/qt/qtwidgets.pas +++ b/lcl/interfaces/qt/qtwidgets.pas @@ -700,12 +700,14 @@ type private FViewportEventHook: QObject_hookH; FUndoAvailableHook: QTextEdit_hookH; + FTextChangedHook: QTextEdit_hookH; FUndoAvailable: Boolean; protected function CreateWidget(const AParams: TCreateParams):QWidgetH; override; public FList: TStrings; - procedure append(AStr: WideString); + procedure Append(const AStr: WideString); + procedure ClearText; function getAlignment: QtAlignment; function getBlockCount: Integer; function getCursorPosition: Integer; @@ -741,6 +743,7 @@ type function getContextMenuPolicy: QtContextMenuPolicy; override; procedure setContextMenuPolicy(const AValue: QtContextMenuPolicy); override; procedure SignalUndoAvailable(b: Boolean); cdecl; + procedure SignalTextChanged(); cdecl; end; { TQtTabBar } @@ -1633,6 +1636,7 @@ uses LCLMessageGlue, qtCaret, qtproc, + qtprivate, WSControls; const @@ -6386,11 +6390,16 @@ begin QWidget_setAcceptDrops(Result, False); end; -procedure TQtTextEdit.append(AStr: WideString); +procedure TQtTextEdit.Append(const AStr: WideString); begin QTextEdit_append(QTextEditH(Widget), @AStr); end; +procedure TQtTextEdit.ClearText; +begin + QTextEdit_clear(QTextEditH(Widget)); +end; + function TQtTextEdit.getAlignment: QtAlignment; begin Result := QTextEdit_alignment(QTextEditH(Widget)); @@ -6700,6 +6709,9 @@ begin FUndoAvailableHook := QTextEdit_hook_create(Widget); QTextEdit_hook_hook_undoAvailable(FUndoAvailableHook, @SignalUndoAvailable); + FTextChangedHook := QTextEdit_hook_create(Widget); + QTextEdit_hook_hook_textChanged(FTextChangedHook, @SignalTextChanged); + FViewportEventHook := QObject_hook_create(viewportWidget); QObject_hook_hook_events(FViewportEventHook, @viewportEventFilter); @@ -6707,6 +6719,12 @@ end; procedure TQtTextEdit.DetachEvents; begin + if FUndoAvailableHook <> nil then + QTextEdit_hook_destroy(FUndoAvailableHook); + + if FTextChangedHook <> nil then + QTextEdit_hook_destroy(FTextChangedHook); + QObject_hook_destroy(FViewportEventHook); inherited DetachEvents; end; @@ -6739,6 +6757,22 @@ begin FUndoAvailable := b; end; +procedure TQtTextEdit.SignalTextChanged(); cdecl; +var + Mess: TLMessage; +begin + if (LCLObject = nil) or not GetVisible then + exit; + if Assigned(FList) then + TQtMemoStrings(FList).TextChanged := True; + if not InUpdate then + begin + FillChar(Mess, SizeOf(Mess), #0); + Mess.Msg := CM_TEXTCHANGED; + DeliverMessage(Mess); + end; +end; + { TQtTabBar } procedure TQtTabBar.AttachEvents; diff --git a/lcl/interfaces/qt/qtwsstdctrls.pp b/lcl/interfaces/qt/qtwsstdctrls.pp index 4da8eb3e8c..d18a449c00 100644 --- a/lcl/interfaces/qt/qtwsstdctrls.pp +++ b/lcl/interfaces/qt/qtwsstdctrls.pp @@ -635,15 +635,17 @@ var QtTextEdit: TQtTextEdit; begin QtTextEdit := TQtTextEdit.Create(AWinControl, AParams); - QtTextEdit.AttachEvents; + QtTextEdit.ClearText; QtTextEdit.setBorder(TCustomMemo(AWinControl).BorderStyle = bsSingle); QtTextEdit.setReadOnly(TCustomMemo(AWinControl).ReadOnly); QtTextEdit.setLineWrapMode(WordWrapMap[TCustomMemo(AWinControl).WordWrap]); // create our FList helper - QtTextEdit.FList := TQtMemoStrings.Create(QTextEditH(QtTextEdit.Widget), TCustomMemo(AWinControl)); + QtTextEdit.FList := TQtMemoStrings.Create(TCustomMemo(AWinControl)); QtTextEdit.setScrollStyle(TCustomMemo(AWinControl).ScrollBars); QtTextEdit.setTabChangesFocus(not TCustomMemo(AWinControl).WantTabs); + QtTextEdit.AttachEvents; + Result := TLCLIntfHandle(QtTextEdit); end; @@ -659,7 +661,9 @@ begin if not WSCheckHandleAllocated(ACustomMemo, 'AppendText') or (Length(AText) = 0) then Exit; AStr := GetUtf8String(AText); - TQtTextEdit(ACustomMemo.Handle).append(AStr); + TQtTextEdit(ACustomMemo.Handle).BeginUpdate; + TQtTextEdit(ACustomMemo.Handle).Append(AStr); + TQtTextEdit(ACustomMemo.Handle).EndUpdate; end; {------------------------------------------------------------------------------ @@ -668,16 +672,11 @@ end; Returns: Memo Contents as TStrings ------------------------------------------------------------------------------} class function TQtWSCustomMemo.GetStrings(const ACustomMemo: TCustomMemo): TStrings; -var - TextEditH: QTextEditH; begin if not WSCheckHandleAllocated(ACustomMemo, 'GetStrings') then Exit; if not Assigned(TQtTextEdit(ACustomMemo.Handle).FList) then - begin - TextEditH := QTextEditH((TQtTextEdit(ACustomMemo.Handle).Widget)); // set to proper type - TQtTextEdit(ACustomMemo.Handle).FList := TQtMemoStrings.Create(TextEditH, ACustomMemo); - end; + TQtTextEdit(ACustomMemo.Handle).FList := TQtMemoStrings.Create(ACustomMemo); Result := TQtTextEdit(ACustomMemo.Handle).FList; end; @@ -695,7 +694,6 @@ class procedure TQtWSCustomMemo.SetScrollbars(const ACustomMemo: TCustomMemo; begin if not WSCheckHandleAllocated(ACustomMemo, 'SetScrollBars') then Exit; - TQtTextEdit(ACustomMemo.Handle).setScrollStyle(NewScrollBars); end;