1{%MainUnit gtk2wsstdctrls.pp} 2 3{ Callbacks } 4 5procedure Gtk2WS_MemoChanged({%H-}AGtkTextBuffer: PGtkTextBuffer; WidgetInfo: PWidgetInfo); cdecl; 6var 7 Mess: TLMessage; 8begin 9 EventTrace('Gtk2WS_MemoChanged', WidgetInfo^.LCLObject); 10 if WidgetInfo^.ChangeLock > 0 then 11 begin 12 Dec(WidgetInfo^.ChangeLock); 13 Exit; 14 end; 15 Mess.Msg := CM_TEXTCHANGED; 16 DeliverMessage(WidgetInfo^.LCLObject, Mess); 17end; 18 19procedure Gtk2WS_MemoCutToClip(widget: PGtkWidget; WidgetInfo: PWidgetInfo); cdecl; 20var 21 Mess: TLMessage; 22begin 23 EventTrace('Gtk2WS_MemoCutToClip', WidgetInfo^.LCLObject); 24 if (Widget=nil) then ; 25 Mess.msg := LM_CUT; 26 DeliverMessage(WidgetInfo^.LCLObject, Mess); 27end; 28 29procedure Gtk2WS_MemoCopyToClip(widget: PGtkWidget; WidgetInfo: PWidgetInfo); cdecl; 30var 31 Mess: TLMessage; 32begin 33 EventTrace('Gtk2WS_MemoCopyToClip', WidgetInfo^.LCLObject); 34 if (Widget=nil) then ; 35 Mess.msg := LM_COPY; 36 DeliverMessage(WidgetInfo^.LCLObject, Mess); 37end; 38 39procedure Gtk2WS_MemoPasteFromClip(widget: PGtkWidget; WidgetInfo: PWidgetInfo); cdecl; 40var 41 Mess: TLMessage; 42begin 43 EventTrace('Gtk2WS_MemoPasteFromClip', WidgetInfo^.LCLObject); 44 if (Widget=nil) then ; 45 Mess.msg := LM_PASTE; 46 DeliverMessage(WidgetInfo^.LCLObject, Mess); 47 g_object_set_data(PGObject(widget), 'lcl-memo-paste-from-clip', gPointer(-1)); 48end; 49 50procedure Gtk2WS_MemoTextInserting (Textbuffer: PGtkTextBuffer; StartIter: PGtkTextIter; 51 thetext: pgchar; NewTextLength: gint; WidgetInfo: PWidgetInfo); cdecl; 52var 53 BeginIter, EndIter: TGtkTextIter; 54 Memo: TCustomMemo; 55 CurrLength, CutLength: integer; 56begin 57 if g_object_get_data(PGObject(WidgetInfo^.CoreWidget), 'lcl-memo-paste-from-clip') <> nil then 58 begin 59 g_object_set_data(PGObject(WidgetInfo^.CoreWidget), 'lcl-memo-paste-from-clip', nil); 60 gtk_text_buffer_get_selection_bounds(TextBuffer, @BeginIter, @EndIter); 61 gtk_text_buffer_delete(TextBuffer, @BeginIter, @EndIter); 62 gtk_text_buffer_insert(TextBuffer, @BeginIter, theText, NewTextLength); 63 g_signal_stop_emission_by_name(PGtkObject(Textbuffer), 'insert-text'); 64 Exit; 65 end; 66 67 { GTK2 does not provide its own max. length for memos 68 so we have to do our own. } 69 70 if TControl(WidgetInfo^.LCLObject) is TCustomMemo then 71 begin 72 Memo := TCustomMemo(WidgetInfo^.LCLObject); 73 if Memo.MaxLength <= 0 then Exit; 74 75 CurrLength := gtk_text_buffer_get_char_count(TextBuffer); 76 if CurrLength + NewTextLength <= Memo.MaxLength then 77 Exit; 78 79 CutLength := CurrLength + NewTextLength - Memo.MaxLength; 80 81 if NewTextLength - CutLength > 0 then 82 gtk_text_buffer_insert(TextBuffer, StartIter, TheText, NewTextLength - CutLength); 83 84 g_signal_stop_emission_by_name(PGtkObject(Textbuffer), 'insert-text'); 85 end; 86end; 87 88 89 90{ TGtk2WSCustomMemo } 91 92class procedure TGtk2WSCustomMemo.SetCallbacks(const AGtkWidget: PGtkWidget; 93 const AWidgetInfo: PWidgetInfo); 94var 95 TextBuf: PGtkTextBuffer; 96begin 97 TextBuf := gtk_text_view_get_buffer(PGtkTextView(AWidgetInfo^.CoreWidget)); 98 99 //TGtkWSBaseScrollingWinControl.SetCallbacks(AGtkWidget, AWidgetInfo); 100 101 TGtk2WSWinControl.SetCallbacks(PGtkObject(AGtkWidget), TComponent(AWidgetInfo^.LCLObject)); 102 103 SignalConnect(PGtkWidget(TextBuf), 'changed', @Gtk2WS_MemoChanged, AWidgetInfo); 104 SignalConnect(PGtkWidget(TextBuf), 'insert-text', @Gtk2WS_MemoTextInserting, AWidgetInfo); 105 106 //SetCallback(LM_ACTIVATE, AGTKObject,ALCLObject); 107 108 SignalConnect(AWidgetInfo^.CoreWidget, 'cut-clipboard', @Gtk2WS_MemoCutToClip, AWidgetInfo); 109 SignalConnect(AWidgetInfo^.CoreWidget, 'copy-clipboard', @Gtk2WS_MemoCopyToClip, AWidgetInfo); 110 SignalConnect(AWidgetInfo^.CoreWidget, 'paste-clipboard', @Gtk2WS_MemoPasteFromClip, AWidgetInfo); 111 112 g_signal_connect_after(PGtkObject(AWidgetInfo^.CoreWidget), 'populate-popup', 113 gtk_signal_func(@gtkDefaultPopupMenuCloseFix), AWidgetInfo); 114end; 115 116class function TGtk2WSCustomMemo.CreateHandle(const AWinControl: TWinControl; 117 const AParams: TCreateParams): TLCLIntfHandle; 118var 119 Widget, 120 TempWidget: PGtkWidget; 121 WidgetInfo: PWidgetInfo; 122 SS:TPoint; 123begin 124 Widget := gtk_scrolled_window_new(nil, nil); 125 Result := TLCLIntfHandle({%H-}PtrUInt(Widget)); 126 if Result = 0 then Exit; 127 {$IFDEF DebugLCLComponents} 128 DebugGtkWidgets.MarkCreated(Widget,dbgsName(AWinControl)); 129 {$ENDIF} 130 131 WidgetInfo := CreateWidgetInfo({%H-}Pointer(Result), AWinControl, AParams); 132 133 TempWidget := gtk_text_view_new(); 134 gtk_container_add(PGtkContainer(Widget), TempWidget); 135 136 GTK_WIDGET_UNSET_FLAGS(PGtkScrolledWindow(Widget)^.hscrollbar, GTK_CAN_FOCUS); 137 GTK_WIDGET_UNSET_FLAGS(PGtkScrolledWindow(Widget)^.vscrollbar, GTK_CAN_FOCUS); 138 139 SS:=Gtk2TranslateScrollStyle(TCustomMemo(AWinControl).ScrollBars); 140 gtk_scrolled_window_set_policy(PGtkScrolledWindow(Widget),SS.X, SS.Y); 141 142 // add border for memo 143 gtk_scrolled_window_set_shadow_type(PGtkScrolledWindow(Widget), 144 BorderStyleShadowMap[TCustomEdit(AWinControl).BorderStyle]); 145 146 SetMainWidget(Widget, TempWidget); 147 GetOrCreateWidgetInfo(Widget)^.CoreWidget := TempWidget; 148 149 gtk_text_buffer_set_text(gtk_text_view_get_buffer(PGtkTextView(TempWidget)), PChar(TCustomMemo(AWinControl).Text), -1); 150 gtk_text_view_set_editable(PGtkTextView(TempWidget), not TCustomMemo(AWinControl).ReadOnly); 151 gtk_text_view_set_justification(PGtkTextView(TempWidget), aGtkJustification[TCustomMemo(AWinControl).Alignment]); 152 if TCustomMemo(AWinControl).WordWrap then 153 gtk_text_view_set_wrap_mode(PGtkTextView(TempWidget), GTK_WRAP_WORD) 154 else 155 gtk_text_view_set_wrap_mode(PGtkTextView(TempWidget), GTK_WRAP_NONE); 156 157 gtk_text_view_set_accepts_tab(PGtkTextView(TempWidget), TCustomMemo(AWinControl).WantTabs); 158 159 gtk_widget_show_all(Widget); 160 161 Set_RC_Name(AWinControl, Widget); 162 if not AWinControl.HandleObjectShouldBeVisible and not (csDesigning in AWinControl.ComponentState) then 163 gtk_widget_hide(Widget); 164 SetCallbacks(Widget, WidgetInfo); 165end; 166 167class function TGtk2WSCustomMemo.GetStrings(const ACustomMemo: TCustomMemo): TStrings; 168var 169 TextView: PGtkTextView; 170begin 171 TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomMemo.Handle))^.CoreWidget); 172 Result := TGtk2MemoStrings.Create(TextView, ACustomMemo); 173end; 174 175class procedure TGtk2WSCustomMemo.SetAlignment(const ACustomEdit: TCustomEdit; 176 const AAlignment: TAlignment); 177var 178 TextView: PGtkTextView; 179begin 180 TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle))^.CoreWidget); 181 gtk_text_view_set_justification(TextView, aGtkJustification[AAlignment]); 182end; 183 184 185class procedure TGtk2WSCustomMemo.SetColor(const AWinControl: TWinControl); 186var 187 AWidget: PGTKWidget; 188begin 189 if not WSCheckHandleAllocated(AWinControl, 'SetColor') then 190 Exit; 191 AWidget := {%H-}PGtkWidget(AWinControl.Handle); 192 AWidget := GetOrCreateWidgetInfo(AWidget)^.CoreWidget; 193 Gtk2WidgetSet.SetWidgetColor(AWidget, 194 AWinControl.Font.Color, 195 AWinControl.Color, 196 [GTK_STATE_NORMAL, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT, GTK_STYLE_BASE]); 197end; 198 199class procedure TGtk2WSCustomMemo.SetFont(const AWinControl: TWinControl; const AFont: TFont); 200var 201 AWidget: PGTKWidget; 202begin 203 if not WSCheckHandleAllocated(AWinControl, 'SetFont') then 204 Exit; 205 206 AWidget:= {%H-}PGtkWidget(AWinControl.Handle); 207 AWidget:= GetWidgetInfo(AWidget)^.CoreWidget; 208 209 if AWidget <> nil then 210 begin 211 Gtk2WidgetSet.SetWidgetColor(AWidget, AFont.color, clNone, 212 [GTK_STATE_NORMAL,GTK_STATE_ACTIVE,GTK_STATE_PRELIGHT,GTK_STATE_SELECTED, 213 GTK_STYLE_TEXT]); 214 Gtk2WidgetSet.SetWidgetFont(AWidget, AFont); 215 end; 216end; 217 218class procedure TGtk2WSCustomMemo.SetSelStart(const ACustomEdit: TCustomEdit; 219 NewStart: integer); 220var 221 MemoStrings: TGtk2MemoStrings; 222begin 223 if not WSCheckHandleAllocated(ACustomEdit, 'SetSelStart') then 224 Exit; 225 226 MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; 227 MemoStrings.QueueCursorMove(NewStart); 228 MemoStrings.QueueSelectLength(0); 229end; 230 231class procedure TGtk2WSCustomMemo.SetSelLength(const ACustomEdit: TCustomEdit; 232 NewLength: integer); 233var 234 MemoStrings: TGtk2MemoStrings; 235begin 236 if not WSCheckHandleAllocated(ACustomEdit, 'SetSelLength') then 237 Exit; 238 239 MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; 240 MemoStrings.QueueSelectLength(NewLength); 241end; 242 243class procedure TGtk2WSCustomMemo.SetWantTabs(const ACustomMemo: TCustomMemo; 244 const NewWantTabs: boolean); 245var 246 TextView: PGtkTextView; 247begin 248 if not WSCheckHandleAllocated(ACustomMemo, 'SetWantTabs') then 249 Exit; 250 251 TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomMemo.Handle))^.CoreWidget); 252 gtk_text_view_set_accepts_tab(TextView, NewWantTabs); 253end; 254 255class procedure TGtk2WSCustomMemo.SetEchoMode(const ACustomEdit: TCustomEdit; 256 NewMode: TEchoMode); 257begin 258 // not supported 259end; 260 261class procedure TGtk2WSCustomMemo.SetPasswordChar( 262 const ACustomEdit: TCustomEdit; NewChar: char); 263begin 264 // not supported 265end; 266 267class procedure TGtk2WSCustomMemo.SetWordWrap(const ACustomMemo: TCustomMemo; 268 const NewWordWrap: boolean); 269var 270 TextView: PGtkTextView; 271begin 272 if not WSCheckHandleAllocated(ACustomMemo, 'SetWordWrap') then 273 Exit; 274 TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomMemo.Handle))^.CoreWidget); 275 if NewWordWrap then 276 gtk_text_view_set_wrap_mode(PGtkTextView(TextView), GTK_WRAP_WORD) 277 else 278 gtk_text_view_set_wrap_mode(PGtkTextView(TextView), GTK_WRAP_NONE); 279end; 280 281class procedure TGtk2WSCustomMemo.SetCharCase(const ACustomEdit: TCustomEdit; 282 NewCase: TEditCharCase); 283begin 284 // TODO: TGtk2WSCustomMemo.SetCharCase: implement me! 285end; 286 287class procedure TGtk2WSCustomMemo.SetScrollbars(const ACustomMemo: TCustomMemo; 288 const NewScrollbars: TScrollStyle); 289var 290 SS:TPoint; 291 ScrollWidget: PGtkScrolledWindow; 292begin 293 if not WSCheckHandleAllocated(ACustomMemo, 'SetScrollBars') then Exit; 294 SS:=Gtk2TranslateScrollStyle(NewScrollBars); 295 ScrollWidget:={%H-}PGtkScrolledWindow(ACustomMemo.Handle); 296 gtk_scrolled_window_set_policy(ScrollWidget ,SS.X, SS.Y); 297end; 298 299class procedure TGtk2WSCustomMemo.SetMaxLength(const ACustomEdit: TCustomEdit; 300 NewLength: integer); 301var 302 Widget: PGtkWidget; 303begin 304 Widget:={%H-}PGtkWidget(ACustomEdit.Handle); 305 if GtkWidgetIsA(Widget, GTK_TYPE_ENTRY) then 306 gtk_entry_set_max_length(GTK_ENTRY(Widget), guint16(NewLength)); 307end; 308 309class procedure TGtk2WSCustomMemo.SetReadOnly(const ACustomEdit: TCustomEdit; 310 NewReadOnly: boolean); 311var 312 TextView: PGtkTextView; 313begin 314 if not WSCheckHandleAllocated(ACustomEdit, 'SetReadOnly') then 315 Exit; 316 317 TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle))^.CoreWidget); 318 if TextView <> nil then 319 gtk_text_view_set_editable(TextView, not NewReadOnly); 320end; 321 322class procedure TGtk2WSCustomMemo.SetSelText(const ACustomEdit: TCustomEdit; 323 const NewSelText: string); 324var 325 MemoStrings: TGtk2MemoStrings; 326 TextBuf: PGtkTextBuffer; 327 SelStart, SelLength, Utf8Len, CutLen: Integer; 328 StartIter, EndIter: TGtkTextIter; 329begin 330 if not WSCheckHandleAllocated(ACustomEdit, 'SetSelText') then 331 Exit; 332 MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; 333 with GetOrCreateWidgetInfo({%H-}Pointer(ACustomEdit.Handle))^ do 334 TextBuf := gtk_text_view_get_buffer(PGtkTextView(CoreWidget)); 335 SelStart := GetSelStart(ACustomEdit); 336 SelLength := GetSelLength(ACustomEdit); 337 gtk_text_buffer_get_iter_at_offset(TextBuf, @StartIter, SelStart); 338 if SelLength > 0 then 339 begin 340 gtk_text_buffer_get_iter_at_offset(TextBuf, @EndIter, SelStart + SelLength); 341 MemoStrings.QueueSelectLength(0); 342 gtk_text_buffer_delete(TextBuf, @StartIter, @EndIter); 343 end; 344 Utf8Len := UTF8Length(NewSelText); 345 SelStart := SelStart + Utf8Len; 346 if ACustomEdit.MaxLength > 0 then 347 begin 348 CutLen := gtk_text_buffer_get_char_count(TextBuf) + Utf8Len - ACustomEdit.MaxLength; 349 if CutLen > 0 then 350 Dec(SelStart, CutLen) 351 end; 352 MemoStrings.QueueCursorMove(SelStart); 353 gtk_text_buffer_insert(TextBuf, @StartIter, PChar(NewSelText), -1); 354end; 355 356class procedure TGtk2WSCustomMemo.SetText(const AWinControl: TWinControl; const AText: string); 357var 358 TextBuf: PGtkTextBuffer; 359 StartIter: TGtkTextIter; 360begin 361 if not WSCheckHandleAllocated(AWinControl, 'SetText') then 362 Exit; 363 TextBuf := gtk_text_view_get_buffer(PGtkTextView(GetOrCreateWidgetInfo({%H-}Pointer(AWinControl.Handle))^.CoreWidget)); 364 gtk_text_buffer_set_text(TextBuf, PChar(AText), -1); 365 gtk_text_buffer_get_start_iter(TextBuf, @StartIter); 366 gtk_text_buffer_place_cursor(TextBuf, @StartIter); 367end; 368 369class procedure TGtk2WSCustomMemo.GetPreferredSize( 370 const AWinControl: TWinControl; var PreferredWidth, PreferredHeight: integer; 371 WithThemeSpace: Boolean); 372begin 373 GetGTKDefaultWidgetSize(AWinControl,PreferredWidth,PreferredHeight, 374 WithThemeSpace); 375end; 376 377class function TGtk2WSCustomMemo.GetSelStart(const ACustomEdit: TCustomEdit): integer; 378var 379 MemoStrings: TGtk2MemoStrings; 380 TextView: PGtkTextView; 381 TextBuffer: PGtkTextBuffer; 382 TextMark: PGtkTextMark; 383 TextIter: TGtkTextIter; 384 StartIter, EndIter: TGtkTextIter; 385 StartPos, EndPos: Integer; 386begin 387 Result := 0; 388 if not WSCheckHandleAllocated(ACustomEdit, 'GetSelStart') then 389 Exit; 390 391 MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; 392 Result := MemoStrings.QueueCursorMovePos; 393 if Result > -1 then 394 Exit; 395 396 TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle))^.CoreWidget); 397 TextBuffer := gtk_text_view_get_buffer(TextView); 398 TextMark := gtk_text_buffer_get_insert(TextBuffer); 399 gtk_text_buffer_get_iter_at_mark(TextBuffer, @TextIter, TextMark); 400 401 Result := gtk_text_iter_get_offset(@TextIter); 402 if GetSelLength(ACustomEdit) = 0 then Exit; 403 404 if not gtk_text_buffer_get_selection_bounds(TextBuffer, @StartIter, @EndIter) then Exit; 405 406 StartPos := gtk_text_iter_get_offset(@StartIter); 407 EndPos := gtk_text_iter_get_offset(@EndIter); 408 409 Result := Min(StartPos, EndPos); 410end; 411 412class function TGtk2WSCustomMemo.GetSelLength(const ACustomEdit: TCustomEdit): integer; 413var 414 MemoStrings: TGtk2MemoStrings; 415 TextView: PGtkTextView; 416 TextBuffer: PGtkTextBuffer; 417 StartIter, EndIter: TGtkTextIter; 418begin 419 Result := 0; 420 if not WSCheckHandleAllocated(ACustomEdit, 'GetSelLength') then 421 Exit; 422 423 MemoStrings := TCustomMemo(ACustomEdit).Lines as TGtk2MemoStrings; 424 Result := MemoStrings.QueueSelLength; 425 426 if Result = -1 then 427 begin 428 Result := 0; 429 TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle))^.CoreWidget); 430 TextBuffer := gtk_text_view_get_buffer(TextView); 431 if not gtk_text_buffer_get_selection_bounds(TextBuffer, @StartIter, @EndIter) then Exit; 432 433 Result := Abs(gtk_text_iter_get_offset(@EndIter) - gtk_text_iter_get_offset(@StartIter)); 434 end; 435end; 436 437class function TGtk2WSCustomMemo.GetCaretPos(const ACustomEdit: TCustomEdit): TPoint; 438var 439 TextView: PGtkTextView; 440 TextBuffer: PGtkTextBuffer; 441 Offset: Integer; 442 TextIter: TGtkTextIter; 443begin 444 Result := Point(0, 0); 445 if not WSCheckHandleAllocated(ACustomEdit, 'GetCaretPos') then 446 Exit; 447 TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle))^.CoreWidget); 448 TextBuffer := gtk_text_view_get_buffer(TextView); 449 Offset := GetSelStart(ACustomEdit) + GetSelLength(ACustomEdit); 450 gtk_text_buffer_get_iter_at_offset(TextBuffer, @TextIter, Offset); 451 452 Result.X := gtk_text_iter_get_line_offset(@TextIter); 453 Result.Y := gtk_text_iter_get_line(@TextIter); 454end; 455 456class procedure TGtk2WSCustomMemo.SetCaretPos(const ACustomEdit: TCustomEdit; 457 const NewPos: TPoint); 458var 459 TextView: PGtkTextView; 460 TextBuffer: PGtkTextBuffer; 461 TextIter: TGtkTextIter; 462begin 463 if not WSCheckHandleAllocated(ACustomEdit, 'SetCaretPos') then 464 Exit; 465 TextView := PGtkTextView(GetWidgetInfo({%H-}Pointer(ACustomEdit.Handle))^.CoreWidget); 466 TextBuffer := gtk_text_view_get_buffer(TextView); 467 468 if (NewPos.X < 0) or (NewPos.Y < 0) 469 then Exit; 470 471{ this is quicker, but crashes if given invalid coords: 472 gtk_text_buffer_get_iter_at_line_offset(TextBuffer, @TextIter, NewPos.Y, NewPos.X); } 473 474 if (NewPos.Y >= gtk_text_buffer_get_line_count(TextBuffer)) 475 then Exit; 476 gtk_text_buffer_get_iter_at_line(TextBuffer, @TextIter, NewPos.Y); 477 if (NewPos.X >= gtk_text_iter_get_chars_in_line(@TextIter)) 478 then Exit; 479 gtk_text_iter_set_line_offset(@TextIter, NewPos.X); 480 481 SetSelStart(ACustomEdit, gtk_text_iter_get_offset(@TextIter)); 482end; 483