{%MainUnit gtk2wsstdctrls.pp} { Callbacks } procedure Gtk2WS_MemoChanged({%H-}AGtkTextBuffer: PGtkTextBuffer; WidgetInfo: PWidgetInfo); cdecl; var Mess: TLMessage; begin EventTrace('Gtk2WS_MemoChanged', WidgetInfo^.LCLObject); if WidgetInfo^.ChangeLock > 0 then begin Dec(WidgetInfo^.ChangeLock); Exit; end; Mess.Msg := CM_TEXTCHANGED; DeliverMessage(WidgetInfo^.LCLObject, Mess); end; procedure Gtk2WS_MemoCutToClip(widget: PGtkWidget; WidgetInfo: PWidgetInfo); cdecl; var Mess: TLMessage; begin EventTrace('Gtk2WS_MemoCutToClip', WidgetInfo^.LCLObject); if (Widget=nil) then ; Mess.msg := LM_CUT; DeliverMessage(WidgetInfo^.LCLObject, Mess); end; procedure Gtk2WS_MemoCopyToClip(widget: PGtkWidget; WidgetInfo: PWidgetInfo); cdecl; var Mess: TLMessage; begin EventTrace('Gtk2WS_MemoCopyToClip', WidgetInfo^.LCLObject); if (Widget=nil) then ; Mess.msg := LM_COPY; DeliverMessage(WidgetInfo^.LCLObject, Mess); end; procedure Gtk2WS_MemoPasteFromClip(widget: PGtkWidget; WidgetInfo: PWidgetInfo); cdecl; var Mess: TLMessage; begin EventTrace('Gtk2WS_MemoPasteFromClip', WidgetInfo^.LCLObject); if (Widget=nil) then ; Mess.msg := LM_PASTE; DeliverMessage(WidgetInfo^.LCLObject, Mess); g_object_set_data(PGObject(widget), 'lcl-memo-paste-from-clip', gPointer(-1)); end; procedure Gtk2WS_MemoTextInserting (Textbuffer: PGtkTextBuffer; StartIter: PGtkTextIter; thetext: pgchar; NewTextLength: gint; WidgetInfo: PWidgetInfo); cdecl; var BeginIter, EndIter: TGtkTextIter; Memo: TCustomMemo; CurrLength, CutLength: integer; begin if g_object_get_data(PGObject(WidgetInfo^.CoreWidget), 'lcl-memo-paste-from-clip') <> nil then begin g_object_set_data(PGObject(WidgetInfo^.CoreWidget), 'lcl-memo-paste-from-clip', nil); gtk_text_buffer_get_selection_bounds(TextBuffer, @BeginIter, @EndIter); gtk_text_buffer_delete(TextBuffer, @BeginIter, @EndIter); gtk_text_buffer_insert(TextBuffer, @BeginIter, theText, NewTextLength); g_signal_stop_emission_by_name(PGtkObject(Textbuffer), 'insert-text'); Exit; end; { GTK2 does not provide its own max. length for memos so we have to do our own. } if TControl(WidgetInfo^.LCLObject) is TCustomMemo then begin Memo := TCustomMemo(WidgetInfo^.LCLObject); if Memo.MaxLength <= 0 then Exit; CurrLength := gtk_text_buffer_get_char_count(TextBuffer); if CurrLength + NewTextLength <= Memo.MaxLength then Exit; CutLength := CurrLength + NewTextLength - Memo.MaxLength; if NewTextLength - CutLength > 0 then gtk_text_buffer_insert(TextBuffer, StartIter, TheText, NewTextLength - CutLength); g_signal_stop_emission_by_name(PGtkObject(Textbuffer), 'insert-text'); end; end; { TGtk2WSCustomMemo } class procedure TGtk2WSCustomMemo.SetCallbacks(const AGtkWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); var TextBuf: PGtkTextBuffer; begin TextBuf := gtk_text_view_get_buffer(PGtkTextView(AWidgetInfo^.CoreWidget)); //TGtkWSBaseScrollingWinControl.SetCallbacks(AGtkWidget, AWidgetInfo); TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject)); SignalConnect(PGtkWidget(TextBuf), 'changed', @Gtk2WS_MemoChanged, AWidgetInfo); SignalConnect(PGtkWidget(TextBuf), 'insert-text', @Gtk2WS_MemoTextInserting, AWidgetInfo); //SetCallback(LM_ACTIVATE, AGTKObject,ALCLObject); SignalConnect(AWidgetInfo^.CoreWidget, 'cut-clipboard', @Gtk2WS_MemoCutToClip, AWidgetInfo); SignalConnect(AWidgetInfo^.CoreWidget, 'copy-clipboard', @Gtk2WS_MemoCopyToClip, AWidgetInfo); SignalConnect(AWidgetInfo^.CoreWidget, 'paste-clipboard', @Gtk2WS_MemoPasteFromClip, AWidgetInfo); end; class function TGtk2WSCustomMemo.CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; var Widget, TempWidget: PGtkWidget; WidgetInfo: PWidgetInfo; SS:TPoint; begin Widget := gtk_scrolled_window_new(nil, nil); Result := TLCLIntfHandle({%H-}PtrUInt(Widget)); if Result = 0 then Exit; {$IFDEF DebugLCLComponents} DebugGtkWidgets.MarkCreated(Widget,dbgsName(AWinControl)); {$ENDIF} WidgetInfo := CreateWidgetInfo({%H-}Pointer(Result), AWinControl, AParams); TempWidget := gtk_text_view_new(); gtk_container_add(PGtkContainer(Widget), TempWidget); GTK_WIDGET_UNSET_FLAGS(PGtkScrolledWindow(Widget)^.hscrollbar, GTK_CAN_FOCUS); GTK_WIDGET_UNSET_FLAGS(PGtkScrolledWindow(Widget)^.vscrollbar, GTK_CAN_FOCUS); SS:=Gtk2TranslateScrollStyle(TCustomMemo(AWinControl).ScrollBars); gtk_scrolled_window_set_policy(PGtkScrolledWindow(Widget),SS.X, SS.Y); // add border for memo gtk_scrolled_window_set_shadow_type(PGtkScrolledWindow(Widget), BorderStyleShadowMap[TCustomEdit(AWinControl).BorderStyle]); SetMainWidget(Widget, TempWidget); GetWidgetInfo(Widget, True)^.CoreWidget := TempWidget; gtk_text_buffer_set_text(gtk_text_view_get_buffer(PGtkTextView(TempWidget)), PChar(TCustomMemo(AWinControl).Text), -1); gtk_text_view_set_editable(PGtkTextView(TempWidget), not TCustomMemo(AWinControl).ReadOnly); gtk_text_view_set_justification(PGtkTextView(TempWidget), aGtkJustification[TCustomMemo(AWinControl).Alignment]); if TCustomMemo(AWinControl).WordWrap then gtk_text_view_set_wrap_mode(PGtkTextView(TempWidget), GTK_WRAP_WORD) else gtk_text_view_set_wrap_mode(PGtkTextView(TempWidget), GTK_WRAP_NONE); gtk_text_view_set_accepts_tab(PGtkTextView(TempWidget), TCustomMemo(AWinControl).WantTabs); gtk_widget_show_all(Widget); Set_RC_Name(AWinControl, Widget); if not AWinControl.HandleObjectShouldBeVisible and not (csDesigning in AWinControl.ComponentState) then gtk_widget_hide(Widget); SetCallbacks(Widget, WidgetInfo); end; class function TGtk2WSCustomMemo.GetStrings(const ACustomMemo: TCustomMemo ): TStrings; var TextView: PGtkTextView; begin TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomMemo.Handle), False)^.CoreWidget); Result := TGtk2MemoStrings.Create(TextView, ACustomMemo); end; class procedure TGtk2WSCustomMemo.SetAlignment(const ACustomEdit: TCustomEdit; const AAlignment: TAlignment); var TextView: PGtkTextView; begin TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle), False)^.CoreWidget); gtk_text_view_set_justification(TextView, aGtkJustification[AAlignment]); end; class procedure TGtk2WSCustomMemo.SetColor(const AWinControl: TWinControl); var AWidget: PGTKWidget; begin if not WSCheckHandleAllocated(AWinControl, 'SetColor') then Exit; AWidget := {%H-}PGtkWidget(AWinControl.Handle); AWidget := GetWidgetInfo(AWidget, True)^.CoreWidget; Gtk2WidgetSet.SetWidgetColor(AWidget, AWinControl.Font.Color, AWinControl.Color, [GTK_STATE_NORMAL, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STYLE_BASE]); end; class procedure TGtk2WSCustomMemo.SetFont(const AWinControl: TWinControl; const AFont: TFont); var AWidget: PGTKWidget; begin if not WSCheckHandleAllocated(AWinControl, 'SetFont') then Exit; AWidget:= {%H-}PGtkWidget(AWinControl.Handle); AWidget:= GetWidgetInfo(AWidget)^.CoreWidget; if AWidget <> nil then begin Gtk2WidgetSet.SetWidgetColor(AWidget, AFont.color, clNone, [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED, GTK_STYLE_TEXT]); Gtk2WidgetSet.SetWidgetFont(AWidget, AFont); end; end; class procedure TGtk2WSCustomMemo.SetSelStart(const ACustomEdit: TCustomEdit; NewStart: integer); var MemoStrings: TGtk2MemoStrings; begin if not WSCheckHandleAllocated(ACustomEdit, 'SetSelStart') then Exit; MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; MemoStrings.QueueCursorMove(NewStart); MemoStrings.QueueSelectLength(0); end; class procedure TGtk2WSCustomMemo.SetSelLength(const ACustomEdit: TCustomEdit; NewLength: integer); var MemoStrings: TGtk2MemoStrings; begin if not WSCheckHandleAllocated(ACustomEdit, 'SetSelLength') then Exit; MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; MemoStrings.QueueSelectLength(NewLength); end; class procedure TGtk2WSCustomMemo.SetWantTabs(const ACustomMemo: TCustomMemo; const NewWantTabs: boolean); var TextView: PGtkTextView; begin if not WSCheckHandleAllocated(ACustomMemo, 'SetWantTabs') then Exit; TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomMemo.Handle), False)^.CoreWidget); gtk_text_view_set_accepts_tab(TextView, NewWantTabs); end; class procedure TGtk2WSCustomMemo.SetEchoMode(const ACustomEdit: TCustomEdit; NewMode: TEchoMode); begin // not supported end; class procedure TGtk2WSCustomMemo.SetPasswordChar( const ACustomEdit: TCustomEdit; NewChar: char); begin // not supported end; class procedure TGtk2WSCustomMemo.SetWordWrap(const ACustomMemo: TCustomMemo; const NewWordWrap: boolean); var TextView: PGtkTextView; begin if not WSCheckHandleAllocated(ACustomMemo, 'SetWordWrap') then Exit; TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomMemo.Handle), False)^.CoreWidget); if NewWordWrap then gtk_text_view_set_wrap_mode(PGtkTextView(TextView), GTK_WRAP_WORD) else gtk_text_view_set_wrap_mode(PGtkTextView(TextView), GTK_WRAP_NONE); end; class procedure TGtk2WSCustomMemo.SetCharCase(const ACustomEdit: TCustomEdit; NewCase: TEditCharCase); begin // TODO: TGtk2WSCustomMemo.SetCharCase: implement me! end; class procedure TGtk2WSCustomMemo.SetScrollbars(const ACustomMemo: TCustomMemo; const NewScrollbars: TScrollStyle); var SS:TPoint; ScrollWidget: PGtkScrolledWindow; begin if not WSCheckHandleAllocated(ACustomMemo, 'SetScrollBars') then Exit; SS:=Gtk2TranslateScrollStyle(NewScrollBars); ScrollWidget:={%H-}PGtkScrolledWindow(ACustomMemo.Handle); gtk_scrolled_window_set_policy(ScrollWidget ,SS.X, SS.Y); end; class procedure TGtk2WSCustomMemo.SetMaxLength(const ACustomEdit: TCustomEdit; NewLength: integer); var Widget: PGtkWidget; begin Widget:={%H-}PGtkWidget(ACustomEdit.Handle); if GtkWidgetIsA(Widget, GTK_TYPE_ENTRY) then gtk_entry_set_max_length(GTK_ENTRY(Widget), guint16(NewLength)); end; class procedure TGtk2WSCustomMemo.SetReadOnly(const ACustomEdit: TCustomEdit; NewReadOnly: boolean); var TextView: PGtkTextView; begin if not WSCheckHandleAllocated(ACustomEdit, 'SetReadOnly') then Exit; TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle), False)^.CoreWidget); if TextView <> nil then gtk_text_view_set_editable(TextView, not NewReadOnly); end; class procedure TGtk2WSCustomMemo.SetSelText(const ACustomEdit: TCustomEdit; const NewSelText: string); var MemoStrings: TGtk2MemoStrings; TextBuf: PGtkTextBuffer; SelStart, SelLength, Utf8Len, CutLen: Integer; StartIter, EndIter: TGtkTextIter; begin if not WSCheckHandleAllocated(ACustomEdit, 'SetSelText') then Exit; MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; with GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle), True)^ do TextBuf := gtk_text_view_get_buffer(PGtkTextView(CoreWidget)); SelStart := GetSelStart(ACustomEdit); SelLength := GetSelLength(ACustomEdit); gtk_text_buffer_get_iter_at_offset(TextBuf, @StartIter, SelStart); if SelLength > 0 then begin gtk_text_buffer_get_iter_at_offset(TextBuf, @EndIter, SelStart + SelLength); MemoStrings.QueueSelectLength(0); gtk_text_buffer_delete(TextBuf, @StartIter, @EndIter); end; Utf8Len := UTF8Length(NewSelText); SelStart := SelStart + Utf8Len; if ACustomEdit.MaxLength > 0 then begin CutLen := gtk_text_buffer_get_char_count(TextBuf) + Utf8Len - ACustomEdit.MaxLength; if CutLen > 0 then Dec(SelStart, CutLen) end; MemoStrings.QueueCursorMove(SelStart); gtk_text_buffer_insert(TextBuf, @StartIter, PChar(NewSelText), -1); end; class procedure TGtk2WSCustomMemo.SetText(const AWinControl: TWinControl; const AText: string); var TextBuf: PGtkTextBuffer; StartIter: TGtkTextIter; begin if not WSCheckHandleAllocated(AWinControl, 'SetText') then Exit; TextBuf := gtk_text_view_get_buffer(PGtkTextView(GetWidgetInfo({%H-}Pointer(AWinControl.Handle), True)^.CoreWidget)); gtk_text_buffer_set_text(TextBuf, PChar(AText), -1); gtk_text_buffer_get_start_iter(TextBuf, @StartIter); gtk_text_buffer_place_cursor(TextBuf, @StartIter); end; class procedure TGtk2WSCustomMemo.GetPreferredSize( const AWinControl: TWinControl; var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean); begin GetGTKDefaultWidgetSize(AWinControl,PreferredWidth,PreferredHeight, WithThemeSpace); end; class function TGtk2WSCustomMemo.GetSelStart(const ACustomEdit: TCustomEdit ): integer; var MemoStrings: TGtk2MemoStrings; TextView: PGtkTextView; TextBuffer: PGtkTextBuffer; TextMark: PGtkTextMark; TextIter: TGtkTextIter; StartIter, EndIter: TGtkTextIter; StartPos, EndPos: Integer; begin Result := 0; if not WSCheckHandleAllocated(ACustomEdit, 'GetSelStart') then Exit; MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; Result := MemoStrings.QueueCursorMovePos; if Result > -1 then Exit; TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle), False)^.CoreWidget); TextBuffer := gtk_text_view_get_buffer(TextView); TextMark := gtk_text_buffer_get_insert(TextBuffer); gtk_text_buffer_get_iter_at_mark(TextBuffer, @TextIter, TextMark); Result := gtk_text_iter_get_offset(@TextIter); if GetSelLength(ACustomEdit) = 0 then Exit; if not gtk_text_buffer_get_selection_bounds(TextBuffer, @StartIter, @EndIter) then Exit; StartPos := gtk_text_iter_get_offset(@StartIter); EndPos := gtk_text_iter_get_offset(@EndIter); Result := Min(StartPos, EndPos); end; class function TGtk2WSCustomMemo.GetSelLength(const ACustomEdit: TCustomEdit): integer; var MemoStrings: TGtk2MemoStrings; TextView: PGtkTextView; TextBuffer: PGtkTextBuffer; StartIter, EndIter: TGtkTextIter; begin Result := 0; if not WSCheckHandleAllocated(ACustomEdit, 'GetSelLength') then Exit; MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; Result := MemoStrings.QueueSelLength; if Result = -1 then begin Result := 0; TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle), False)^.CoreWidget); TextBuffer := gtk_text_view_get_buffer(TextView); if not gtk_text_buffer_get_selection_bounds(TextBuffer, @StartIter, @EndIter) then Exit; Result := Abs(gtk_text_iter_get_offset(@EndIter) - gtk_text_iter_get_offset(@StartIter)); end; end; class function TGtk2WSCustomMemo.GetCaretPos(const ACustomEdit: TCustomEdit): TPoint; var TextView: PGtkTextView; TextBuffer: PGtkTextBuffer; Offset: Integer; TextIter: TGtkTextIter; begin Result := Point(0, 0); if not WSCheckHandleAllocated(ACustomEdit, 'GetCaretPos') then Exit; TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle), False)^.CoreWidget); TextBuffer := gtk_text_view_get_buffer(TextView); Offset := GetSelStart(ACustomEdit) + GetSelLength(ACustomEdit); gtk_text_buffer_get_iter_at_offset(TextBuffer, @TextIter, Offset); Result.X := gtk_text_iter_get_line_offset(@TextIter); Result.Y := gtk_text_iter_get_line(@TextIter); end; class procedure TGtk2WSCustomMemo.SetCaretPos(const ACustomEdit: TCustomEdit; const NewPos: TPoint); var TextView: PGtkTextView; TextBuffer: PGtkTextBuffer; TextIter: TGtkTextIter; begin if not WSCheckHandleAllocated(ACustomEdit, 'SetCaretPos') then Exit; TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle), False)^.CoreWidget); TextBuffer := gtk_text_view_get_buffer(TextView); if (NewPos.X < 0) or (NewPos.Y < 0) then Exit; { this is quicker, but crashes if given invalid coords: gtk_text_buffer_get_iter_at_line_offset(TextBuffer, @TextIter, NewPos.Y, NewPos.X); } if (NewPos.Y >= gtk_text_buffer_get_line_count(TextBuffer)) then Exit; gtk_text_buffer_get_iter_at_line(TextBuffer, @TextIter, NewPos.Y); if (NewPos.X >= gtk_text_iter_get_chars_in_line(@TextIter)) then Exit; gtk_text_iter_set_line_offset(@TextIter, NewPos.X); SetSelStart(ACustomEdit, gtk_text_iter_get_offset(@TextIter)); end;