1{ $Id$} 2{ 3 ***************************************************************************** 4 * Gtk2WSSpin.pp * 5 * ------------- * 6 * * 7 * * 8 ***************************************************************************** 9 10 ***************************************************************************** 11 This file is part of the Lazarus Component Library (LCL) 12 13 See the file COPYING.modifiedLGPL.txt, included in this distribution, 14 for details about the license. 15 ***************************************************************************** 16} 17unit Gtk2WSSpin; 18 19{$mode objfpc}{$H+} 20 21interface 22 23uses 24 // RTL 25 glib2, gtk2, SysUtils, Classes, Math, 26 // LCL 27 Controls, LCLType, LCLProc, LMessages, LazUTF8, Spin, StdCtrls, 28 // Widgetset 29 Gtk2Extra, Gtk2Def, Gtk2WSStdCtrls, 30 Gtk2Proc, WSLCLClasses, WSProc, WSSpin; 31 32type 33 34 { TGtk2WSCustomFloatSpinEdit } 35 36 TGtk2WSCustomFloatSpinEdit = class(TWSCustomFloatSpinEdit) 37 protected 38 class procedure SetCallbacks(const AWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); virtual; 39 published 40 class function GetSelStart(const ACustomEdit: TCustomEdit): integer; override; 41 class function GetSelLength(const ACustomEdit: TCustomEdit): integer; override; 42 class function GetValue(const ACustomFloatSpinEdit: TCustomFloatSpinEdit): Double; override; 43 44 class procedure SetSelStart(const ACustomEdit: TCustomEdit; NewStart: integer); override; 45 class procedure SetSelLength(const ACustomEdit: TCustomEdit; NewLength: integer); override; 46 class procedure SetReadOnly(const ACustomEdit: TCustomEdit; ReadOnly: boolean); override; 47 48 class procedure SetEditorEnabled(const ACustomFloatSpinEdit: TCustomFloatSpinEdit; AValue: Boolean); override; 49 50 class procedure UpdateControl(const ACustomFloatSpinEdit: TCustomFloatSpinEdit); override; 51 class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override; 52 end; 53 54implementation 55 56function GetGtkFloatSpinEditable(Spin: PGtkSpinButton): PGtkEntry; 57begin 58 Result:=PGtkEntry(@(Spin^.entry)); 59end; 60 61function GetSpinGtkEditable(const Spin: TWinControl): PGtkEntry; 62begin 63 Result:=GetGtkFloatSpinEditable({%H-}PGtkSpinButton(Spin.Handle)); 64end; 65 66{ TGtk2WSCustomFloatSpinEdit } 67 68class procedure TGtk2WSCustomFloatSpinEdit.SetCallbacks( 69 const AWidget: PGtkWidget; const AWidgetInfo: PWidgetInfo); 70begin 71 TGtk2WSCustomEdit.SetCallbacks(AWidget, AWidgetInfo); 72end; 73 74class function TGtk2WSCustomFloatSpinEdit.GetSelStart(const ACustomEdit: TCustomEdit): integer; 75var 76 Entry: PGtkEntry; 77begin 78 if not WSCheckHandleAllocated(ACustomEdit, 'GetSelStart') then 79 Exit(0); 80 Entry := @{%H-}PGtkSpinButton(ACustomEdit.Handle)^.entry; 81 Result := Min(Entry^.current_pos, Entry^.selection_bound) 82end; 83 84class function TGtk2WSCustomFloatSpinEdit.GetSelLength(const ACustomEdit: TCustomEdit): integer; 85var 86 AStart, AEnd: gint; 87begin 88 Result := 0; 89 if not WSCheckHandleAllocated(ACustomEdit, 'GetSelLength') then 90 Exit; 91 if gtk_editable_get_selection_bounds(PGtkEditable(GetSpinGtkEditable(ACustomEdit)), @AStart, @AEnd) then 92 Result := Abs(AEnd-AStart); 93end; 94 95class function TGtk2WSCustomFloatSpinEdit.GetValue( 96 const ACustomFloatSpinEdit: TCustomFloatSpinEdit): Double; 97var 98 StrValue: String; 99 DecSeparator: Char; 100begin 101 if not WSCheckHandleAllocated(ACustomFloatSpinEdit, 'GetValue') then 102 Exit(0); 103 104 // gtk2 have different meaning of value vs text in GtkSpinBox when 105 // we are dealing with real FloatSpinEdit. #18679. 106 StrValue := StrPas(gtk_entry_get_text({%H-}PGtkEntry(ACustomFloatSpinEdit.Handle))); 107 DecSeparator := DefaultFormatSettings.DecimalSeparator; 108 if DecSeparator <> '.' then 109 StrValue := UTF8StringReplace(StrValue, '.', DecSeparator, [rfReplaceAll]); 110 if DecSeparator <> ',' then 111 StrValue := UTF8StringReplace(StrValue, ',', DecSeparator, [rfReplaceAll]); 112 Result := ACustomFloatSpinEdit.StrToValue(StrValue); 113end; 114 115class procedure TGtk2WSCustomFloatSpinEdit.SetSelStart(const ACustomEdit: TCustomEdit; 116 NewStart: integer); 117begin 118 if not WSCheckHandleAllocated(ACustomEdit, 'SetSelStart') then 119 Exit; 120 gtk_editable_set_position(GetSpinGtkEditable(ACustomEdit), NewStart); 121end; 122 123class procedure TGtk2WSCustomFloatSpinEdit.SetSelLength(const ACustomEdit: TCustomEdit; 124 NewLength: integer); 125var 126 Entry: PGtkEntry; 127 SelStart: Integer; 128begin 129 if not WSCheckHandleAllocated(ACustomEdit, 'SetSelLength') then 130 Exit; 131 Entry := @{%H-}PGtkSpinButton(ACustomEdit.Handle)^.entry; 132 SelStart := GetSelStart(ACustomEdit); 133 gtk_entry_select_region(Entry, 134 SelStart, 135 SelStart + NewLength); 136end; 137 138class procedure TGtk2WSCustomFloatSpinEdit.SetReadOnly(const ACustomEdit: TCustomEdit; ReadOnly: boolean); 139var 140 Widget: PGtkWidget; 141 AnAdjustment: PGtkAdjustment; 142 NewReadOnly: Boolean; 143begin 144 if not WSCheckHandleAllocated(ACustomEdit, 'SetReadOnly') then 145 Exit; 146 //Dont unset the edit's ReadOnly if EditorEnabled = False 147 NewReadOnly := ReadOnly or ((ACustomEdit is TCustomFloatSpinEdit) and (not TCustomFloatSpinEdit(ACustomEdit).EditorEnabled)); 148 Widget := {%H-}PGtkWidget(ACustomEdit.Handle); 149 if GTK_IS_EDITABLE(Widget) then 150 gtk_editable_set_editable(PGtkEditable(Widget), not NewReadOnly); 151 152 AnAdjustment:=gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(Widget)); 153 if ReadOnly then 154 begin 155 AnAdjustment^.lower := TCustomFloatSpinEdit(ACustomEdit).Value; 156 AnAdjustment^.upper := TCustomFloatSpinEdit(ACustomEdit).Value; 157 end 158 else 159 begin 160 if (TCustomFloatSpinEdit(ACustomEdit).MaxValue > TCustomFloatSpinEdit(ACustomEdit).MinValue) then 161 begin 162 AnAdjustment^.lower := TCustomFloatSpinEdit(ACustomEdit).MinValue; 163 AnAdjustment^.upper := TCustomFloatSpinEdit(ACustomEdit).MaxValue; 164 end 165 else 166 begin 167 AnAdjustment^.lower := -MaxDouble; 168 AnAdjustment^.upper := MaxDouble; 169 end; 170 end; 171 172 LockOnChange(PgtkObject(Widget), +1); 173 try 174 gtk_spin_button_update(GTK_SPIN_BUTTON(Widget)); 175 finally 176 LockOnChange(PgtkObject(Widget), -1); 177 end; 178end; 179 180class procedure TGtk2WSCustomFloatSpinEdit.SetEditorEnabled( 181 const ACustomFloatSpinEdit: TCustomFloatSpinEdit; AValue: Boolean); 182var 183 Widget: PGtkWidget; 184begin 185 if not WSCheckHandleAllocated(ACustomFloatSpinEdit, 'SetEditorEnabled') then 186 Exit; 187 Widget := {%H-}PGtkWidget(ACustomFloatSpinEdit.Handle); 188 if GTK_IS_EDITABLE(Widget) then 189 gtk_editable_set_editable(PGtkEditable(Widget), AValue); 190end; 191 192class procedure TGtk2WSCustomFloatSpinEdit.UpdateControl( 193 const ACustomFloatSpinEdit: TCustomFloatSpinEdit); 194var 195 AnAdjustment: PGtkAdjustment; 196 wHandle: HWND; 197 SpinWidget: PGtkSpinButton; 198 AMin, AMax: Double; 199 Mess: TLMessage; 200begin 201 //DebugLn(['TGtkWSCustomFloatSpinEdit.UpdateControl ',dbgsName(ACustomFloatSpinEdit)]); 202 if not WSCheckHandleAllocated(ACustomFloatSpinEdit, 'UpdateControl') then 203 Exit; 204 wHandle := ACustomFloatSpinEdit.Handle; 205 SpinWidget:=GTK_SPIN_BUTTON({%H-}Pointer(wHandle)); 206 207 if ACustomFloatSpinEdit.MaxValue > ACustomFloatSpinEdit.MinValue then 208 begin 209 AMin := ACustomFloatSpinEdit.MinValue; 210 AMax := ACustomFloatSpinEdit.MaxValue; 211 end else 212 begin 213 AMin := -MaxDouble; 214 AMax := MaxDouble; 215 end; 216 217 AnAdjustment:=gtk_spin_button_get_adjustment(SpinWidget); 218 if (AnAdjustment^.lower <> AMin) 219 or (AnAdjustment^.upper <> AMax) then 220 begin 221 AnAdjustment^.lower := AMin; 222 AnAdjustment^.upper := AMax; 223 gtk_adjustment_changed(AnAdjustment); 224 end; 225 226 LockOnChange(PgtkObject(SpinWidget), +1); 227 try 228 gtk_spin_button_set_digits(SpinWidget, ACustomFloatSpinEdit.DecimalPlaces); 229 gtk_spin_button_set_value(SpinWidget,ACustomFloatSpinEdit.Value); 230 AnAdjustment^.step_increment := ACustomFloatSpinEdit.Increment; 231 finally 232 LockOnChange(PgtkObject(SpinWidget), -1); 233 end; 234 235 SetReadOnly(TCustomEdit(ACustomFloatSpinEdit), ACustomFloatSpinEdit.ReadOnly); 236 237 FillByte(Mess{%H-},SizeOf(Mess),0); 238 Mess.Msg := CM_TEXTCHANGED; 239 DeliverMessage(ACustomFloatSpinEdit, Mess); 240end; 241 242class function TGtk2WSCustomFloatSpinEdit.CreateHandle( 243 const AWinControl: TWinControl; const AParams: TCreateParams 244 ): TLCLIntfHandle; 245var 246 Adjustment: PGtkAdjustment; 247 Widget: PGtkWidget; 248 WidgetInfo: PWidgetInfo; 249 Entry: PGtkEntry; 250begin 251 Adjustment := PGtkAdjustment(gtk_adjustment_new(1, 1, 100, 1, 0,0)); 252 Widget := gtk_spin_button_new(Adjustment, 1, 0); 253 gtk_widget_show_all(Widget); 254 255 {$IFDEF DebugLCLComponents} 256 DebugGtkWidgets.MarkCreated(Widget, dbgsName(AWinControl)); 257 {$ENDIF} 258 Result := TLCLIntfHandle({%H-}PtrUInt(Widget)); 259 260 WidgetInfo := CreateWidgetInfo(Widget, AWinControl, AParams); 261 Set_RC_Name(AWinControl, Widget); 262 if not AWinControl.HandleObjectShouldBeVisible and not (csDesigning in AWinControl.ComponentState) then 263 gtk_widget_hide(Widget); 264 SetCallbacks(Widget, WidgetInfo); 265 if Result <> 0 then 266 begin 267 Entry := GTK_ENTRY(Widget); 268 // PGtkEntry(@PGtkSpinButton(Result)^.entry); 269 g_object_set(gtk_widget_get_settings(PGtkWidget(Entry)), 270 'gtk-entry-select-on-focus', [0, nil]); 271 end; 272end; 273 274end. 275