1 { $Id: $}
2 {                  --------------------------------------------
3                   cocoatextedits.pas  -  Cocoa internal classes
4                   --------------------------------------------
5 
6  This unit contains the private classhierarchy for the Cocoa implemetations
7 
8  *****************************************************************************
9   This file is part of the Lazarus Component Library (LCL)
10 
11   See the file COPYING.modifiedLGPL.txt, included in this distribution,
12   for details about the license.
13  *****************************************************************************
14 }
15 unit CocoaTextEdits;
16 
17 {$mode objfpc}{$H+}
18 {$modeswitch objectivec1}
19 {$modeswitch objectivec2}
20 {$interfaces corba}
21 {$include cocoadefines.inc}
22 
23 {.$DEFINE COCOA_DEBUG_SETBOUNDS}
24 {.$DEFINE COCOA_SPIN_DEBUG}
25 {.$DEFINE COCOA_SPINEDIT_INSIDE_CONTAINER}
26 
27 interface
28 
29 uses
30   // rtl+ftl
31   Types, Classes, SysUtils,
32   CGGeometry,
33   // Libs
34   MacOSAll, CocoaAll, CocoaUtils, CocoaGDIObjects,
35   cocoa_extra, CocoaPrivate,
36   // LCL
37   LCLType;
38 
39 const
40   SPINEDIT_DEFAULT_STEPPER_WIDTH = 15;
41   SPINEDIT_EDIT_SPACING_FOR_SELECTION = 4;
42 
43   // From Interface Builder MacOSX 10.6
44   // The heights are from layout rectangle.
45   COMBOBOX_REG_HEIGHT   = 20;
46   COMBOBOX_SMALL_HEIGHT = 17;
47   COMBOBOX_MINI_HEIGHT  = 14;
48 
49   COMBOBOX_RO_REG_HEIGHT   = 20;
50   COMBOBOX_RO_SMALL_HEIGHT = 17;
51   COMBOBOX_RO_MINI_HEIGHT  = 15;
52 
53 
54 type
55 
56   TCocoaFieldEditor = objcclass;
57 
58   NSTextField_LCLExt = objcprotocol
59     procedure lclSetMaxLength(amax: integer); message 'lclSetMaxLength:';
60   end;
61 
62   { TCocoaTextField }
63 
64   TCocoaTextField = objcclass(NSTextField, NSTextField_LCLExt)
65     callback: ICommonCallback;
66     maxLength: Integer;
acceptsFirstRespondernull67     function acceptsFirstResponder: LCLObjCBoolean; override;
lclGetCallbacknull68     function lclGetCallback: ICommonCallback; override;
69     procedure lclClearCallback; override;
70     procedure resetCursorRects; override;
71     // key
72     procedure textDidChange(notification: NSNotification); override;
73     // mouse
74     procedure mouseDown(event: NSEvent); override;
75     procedure mouseUp(event: NSEvent); override;
76     procedure rightMouseDown(event: NSEvent); override;
77     procedure rightMouseUp(event: NSEvent); override;
78     procedure otherMouseDown(event: NSEvent); override;
79     procedure otherMouseUp(event: NSEvent); override;
80     procedure mouseDragged(event: NSEvent); override;
81     procedure mouseMoved(event: NSEvent); override;
82     procedure scrollWheel(event: NSEvent); override;
83 
84     procedure lclSetMaxLength(amax: integer);
85   end;
86 
87   { TCocoaSecureTextField }
88 
89   TCocoaSecureTextField = objcclass(NSSecureTextField, NSTextField_LCLExt)
90   public
91     maxLength: Integer;
92     callback: ICommonCallback;
acceptsFirstRespondernull93     function acceptsFirstResponder: LCLObjCBoolean; override;
94     procedure resetCursorRects; override;
lclGetCallbacknull95     function lclGetCallback: ICommonCallback; override;
96     procedure lclClearCallback; override;
97     // key
98     procedure textDidChange(notification: NSNotification); override;
99     // mouse
100     procedure mouseDown(event: NSEvent); override;
101     procedure mouseUp(event: NSEvent); override;
102     procedure rightMouseDown(event: NSEvent); override;
103     procedure rightMouseUp(event: NSEvent); override;
104     procedure otherMouseDown(event: NSEvent); override;
105     procedure otherMouseUp(event: NSEvent); override;
106     procedure mouseDragged(event: NSEvent); override;
107     procedure mouseMoved(event: NSEvent); override;
108     procedure scrollWheel(event: NSEvent); override;
109 
110     procedure lclSetMaxLength(amax: integer);
111   end;
112 
113   { TCocoaTextView }
114 
115   TCocoaTextView = objcclass(NSTextView, NSTextDelegateProtocol, NSTextViewDelegateProtocol)
116   public
117     callback: ICommonCallback;
118     FEnabled: Boolean;
119     FUndoManager: NSUndoManager;
120 
121     supressTextChangeEvent: Integer; // if above zero, then don't send text change event
122     keyCaptured: Boolean;
123     wantReturns: Boolean;
124 
125     procedure dealloc; override;
126     function acceptsFirstResponder: LCLObjCBoolean; override;
127     function lclGetCallback: ICommonCallback; override;
128     procedure lclClearCallback; override;
129     procedure resetCursorRects; override;
130 
131     procedure changeColor(sender: id); override;
132     // keyboard
133     procedure doCommandBySelector(aSelector: SEL); override;
134     procedure insertNewline(sender: id); override;
135     procedure keyDown(event: NSEvent); override;
136     // mouse
137     procedure mouseDown(event: NSEvent); override;
138     procedure mouseUp(event: NSEvent); override;
139     procedure rightMouseDown(event: NSEvent); override;
140     procedure rightMouseUp(event: NSEvent); override;
141     procedure otherMouseDown(event: NSEvent); override;
142     procedure otherMouseUp(event: NSEvent); override;
143 
144     procedure mouseDragged(event: NSEvent); override;
145     procedure mouseEntered(event: NSEvent); override;
146     procedure mouseExited(event: NSEvent); override;
147     procedure mouseMoved(event: NSEvent); override;
148 
149     function lclIsEnabled: Boolean; override;
150     procedure lclSetEnabled(AEnabled: Boolean); override;
151 
152     // delegate methods
153     procedure textDidChange(notification: NSNotification); message 'textDidChange:';
154     procedure lclExpectedKeys(var wantTabs, wantArrows, wantReturn, wantAll: Boolean); override;
155     function undoManagerForTextView(view: NSTextView): NSUndoManager; message 'undoManagerForTextView:';
156   end;
157 
158   { TCococaFieldEditorExt }
159 
160   TCococaFieldEditorExt = objccategory(NSTextView)
161     // this override should take care of any Cocoa editors possible
162     // for example NSSecureTextView used with TCocoaSecureField aka NSSecureTextField
163     function lclGetCallback: ICommonCallback; reintroduce;
164   end;
165 
166   { TCocoaFieldEditor }
167 
168   TCocoaFieldEditor = objcclass(NSTextView)
169   public
170     // This flag is used to hack an infinite loop
171     // when switching "editable" (readonly) mode of NSTextField
172     // see TCocoaWSCustomEdit.SetReadOnly
173     goingReadOnly: Boolean;
174     keyCaptured: Boolean;
175     function lclGetCallback: ICommonCallback; override;
176     function becomeFirstResponder: LCLObjCBoolean; override;
177     // keyboard
178     procedure doCommandBySelector(aSelector: SEL); override;
179     procedure insertNewline(sender: id); override;
180     procedure keyDown(event: NSEvent); override;
181     // mouse
182     procedure mouseDown(event: NSEvent); override;
183     procedure mouseUp(event: NSEvent); override;
184     procedure rightMouseDown(event: NSEvent); override;
185     procedure rightMouseUp(event: NSEvent); override;
186     procedure otherMouseDown(event: NSEvent); override;
187     procedure otherMouseUp(event: NSEvent); override;
188     procedure mouseDragged(event: NSEvent); override;
189     procedure mouseMoved(event: NSEvent); override;
190     procedure scrollWheel(event: NSEvent); override;
191   end;
192 
193 const
194   COMBOBOX_RO_MENUITEM_HEIGHT = 18;
195 
196 type
197   TCocoaComboBox = objcclass;
198   TCocoaReadOnlyComboBox = objcclass;
199 
200   { TCocoaComboBoxList }
201 
202   TCocoaComboBoxList = class(TStringList);
203 
204   TCocoaEditComboBoxList = class(TCocoaComboBoxList)
205   protected
206     FOwner: TCocoaComboBox;
207     procedure InsertItem(Index: Integer; const S: string; O: TObject); override;
208     procedure Changed; override;
209   public
210     // Pass only 1 owner and nil for the other ones
211     constructor Create(AOwner: TCocoaComboBox);
212   end;
213 
214   IComboboxCallBack = interface(ICommonCallBack)
215     procedure ComboBoxWillPopUp;
216     procedure ComboBoxWillDismiss;
217     procedure ComboBoxSelectionDidChange;
218     procedure ComboBoxSelectionIsChanging;
219 
220     procedure ComboBoxDrawItem(itemIndex: Integer; ctx: TCocoaContext;
221       const r: TRect; isSelected: Boolean);
222   end;
223 
224   { TCocoaComboBox }
225 
226   { TCocoaComboBoxItemCell }
227 
228   // represents an item in the combobox dropdown
229   // it should be able to call "draw" callback
230 
231   TCocoaComboBoxItemCell = objcclass(NSTextFieldCell)
232     procedure drawWithFrame_inView(cellFrame: NSRect; controlView_: NSView); override;
233   end;
234 
235   { TCocoaComboBoxCell }
236 
237   // represents combobox itself. All functionality is implemented
238   // in NSComboBoxCell. The cell is also acting as a delegate
239   // for NSTextView, that's used in popup drop-down window.
240   // Apple is deprecating "cells" so NSComboBox implementation
241   // will change in future and it must be expected that NSComboBoxCell
242   // would not be used in future.
243 
244   TCocoaComboBoxCell = objcclass(NSComboBoxCell)
tableView_objectValueForTableColumn_rownull245     //function tableView_objectValueForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): id; message 'tableView:objectValueForTableColumn:row:';
246     function tableView_dataCellForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): NSCell; message 'tableView:dataCellForTableColumn:row:';
tableView_sizeToFitWidthOfColumnnull247     //function tableView_sizeToFitWidthOfColumn(tableView: NSTableView; column: NSInteger): CGFloat; message 'tableView:sizeToFitWidthOfColumn:';
248     //procedure tableView_willDisplayCell_forTableColumn_row(tableView: NSTableView; cell: id; tableColumn: NSTableColumn; row: NSInteger); message 'tableView:willDisplayCell:forTableColumn:row:';
249     //function tableView_heightOfRow(tableView: NSTableView; row: NSInteger): CGFloat; message 'tableView:heightOfRow:';
250   end;
251 
252   TCocoaComboBox = objcclass(NSComboBox, NSComboBoxDataSourceProtocol, NSComboBoxDelegateProtocol)
253   private
254     userSel: boolean;
255   public
256     callback: IComboboxCallBack;
257     list: TCocoaComboBoxList;
258     resultNS: NSString;  //use to return values to combo
259     isDown: Boolean;
acceptsFirstRespondernull260     function acceptsFirstResponder: LCLObjCBoolean; override;
261     procedure textDidChange(notification: NSNotification); override;
262     // NSComboBoxDataSourceProtocol
comboBox_objectValueForItemAtIndex_null263     function comboBox_objectValueForItemAtIndex_(combo: TCocoaComboBox; row: NSInteger): id; message 'comboBox:objectValueForItemAtIndex:';
comboBox_indexOfItemWithStringValuenull264     function comboBox_indexOfItemWithStringValue(aComboBox: NSComboBox; string_: NSString): NSUInteger; message 'comboBox:indexOfItemWithStringValue:';
numberOfItemsInComboBoxnull265     function numberOfItemsInComboBox(combo: TCocoaComboBox): NSInteger; message 'numberOfItemsInComboBox:';
266     //
267     procedure dealloc; override;
lclGetCallbacknull268     function lclGetCallback: ICommonCallback; override;
269     procedure lclClearCallback; override;
270     procedure resetCursorRects; override;
271     // NSComboBoxDelegateProtocol
272     procedure comboBoxWillPopUp(notification: NSNotification); message 'comboBoxWillPopUp:';
273     procedure comboBoxWillDismiss(notification: NSNotification); message 'comboBoxWillDismiss:';
274     procedure comboBoxSelectionDidChange(notification: NSNotification); message 'comboBoxSelectionDidChange:';
275     procedure comboBoxSelectionIsChanging(notification: NSNotification); message 'comboBoxSelectionIsChanging:';
276     //
277     procedure setStringValue(avalue: NSString); override;
lclGetFrameToLayoutDeltanull278     function lclGetFrameToLayoutDelta: TRect; override;
279     // mouse
acceptsFirstMousenull280     function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
281     procedure mouseDown(event: NSEvent); override;
282     procedure mouseUp(event: NSEvent); override;
283     procedure rightMouseDown(event: NSEvent); override;
284     procedure rightMouseUp(event: NSEvent); override;
285     procedure rightMouseDragged(event: NSEvent); override;
286     procedure otherMouseDown(event: NSEvent); override;
287     procedure otherMouseUp(event: NSEvent); override;
288     procedure otherMouseDragged(event: NSEvent); override;
289     procedure mouseDragged(event: NSEvent); override;
290     procedure mouseMoved(event: NSEvent); override;
291     procedure scrollWheel(event: NSEvent); override;
292   end;
293 
294   { TCocoaReadOnlyView }
295 
296   TCocoaReadOnlyView = objcclass (NSView)
297     itemIndex: Integer;
298     combobox: TCocoaReadOnlyComboBox;
299     isMouseOver: Boolean;
300     procedure drawRect(dirtyRect: NSRect); override;
301     procedure mouseUp(event: NSEvent); override;
302     procedure mouseEntered(theEvent: NSEvent); override;
303     procedure mouseExited(theEvent: NSEvent); override;
304   end;
305 
306   { TCocoaReadOnlyComboBoxList }
307 
308   TCocoaReadOnlyComboBoxList = class(TCocoaComboBoxList)
309   protected
310     FOwner: TCocoaReadOnlyComboBox;
311     procedure InsertItem(Index: Integer; const S: string; O: TObject); override;
312     procedure Put(Index: Integer; const S: string); override;
313   public
314     // Pass only 1 owner and nil for the other ones
315     constructor Create(AOwner: TCocoaReadOnlyComboBox);
316     procedure Delete(Index: Integer); override;
317     procedure Clear; override;
318   end;
319 
320   { TCocoaReadOnlyComboBox }
321 
322   TCocoaReadOnlyComboBox = objcclass(NSPopUpButton)
323   public
324     //Owner: TCustomComboBox;
325     callback: IComboboxCallBack;
326     list: TCocoaComboBoxList;
327     resultNS: NSString;  //use to return values to combo
328     lastSelectedItemIndex: Integer; // -1 means invalid or none selected
329 
330     isOwnerDrawn: Boolean;
331     isOwnerMeasure: Boolean;
acceptsFirstRespondernull332     function acceptsFirstResponder: LCLObjCBoolean; override;
333     procedure dealloc; override;
lclGetCallbacknull334     function lclGetCallback: ICommonCallback; override;
335     procedure lclClearCallback; override;
lclGetFrameToLayoutDeltanull336     function lclGetFrameToLayoutDelta: TRect; override;
337     procedure resetCursorRects; override;
338     procedure comboboxAction(sender: id); message 'comboboxAction:';
stringValuenull339     function stringValue: NSString; override;
340     // drawing
341     procedure drawRect(dirtyRect: NSRect); override;
342     // mouse
acceptsFirstMousenull343     function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
344     procedure mouseDown(event: NSEvent); override;
345     procedure mouseUp(event: NSEvent); override;
346     procedure rightMouseDown(event: NSEvent); override;
347     procedure rightMouseUp(event: NSEvent); override;
348     procedure rightMouseDragged(event: NSEvent); override;
349     procedure otherMouseDown(event: NSEvent); override;
350     procedure otherMouseUp(event: NSEvent); override;
351     procedure otherMouseDragged(event: NSEvent); override;
352     procedure mouseDragged(event: NSEvent); override;
353     procedure mouseMoved(event: NSEvent); override;
354     procedure scrollWheel(event: NSEvent); override;
355   end;
356 
357   { TCocoaSpinEdit }
358 {$IFDEF COCOA_SPINEDIT_INSIDE_CONTAINER}
359   TCocoaSpinEdit = objcclass(NSControl)
360   public
361     callback: ICommonCallback;
362     Stepper: NSStepper;
363     Edit: NSTextField;
364     Spin: TCustomFloatSpinEdit;
365     procedure dealloc; override;
366     procedure UpdateControl(ASpinEdit: TCustomFloatSpinEdit); message 'UpdateControl:';
367     procedure CreateSubcontrols(ASpinEdit: TCustomFloatSpinEdit; const AParams: TCreateParams); message 'CreateSubControls:AParams:';
368     procedure PositionSubcontrols(const ALeft, ATop, AWidth, AHeight: Integer); message 'PositionSubcontrols:ATop:AWidth:AHeight:';
369     procedure CalculateSubcontrolPos(const ASpinLCLBounds: TRect; out AEditBounds, AStepperBounds: TRect); message 'CalculateSubcontrolPos:AEditBounds:AStepperBounds:';
370     procedure StepperChanged(sender: NSObject); message 'StepperChanged:';
371     // lcl
acceptsFirstRespondernull372     function acceptsFirstResponder: Boolean; override;
lclGetCallbacknull373     function lclGetCallback: ICommonCallback; override;
374     procedure lclClearCallback; override;
375     // NSViewFix
fittingSizenull376     function fittingSize: NSSize; override;
377   end;
378 {$ELSE}
379 
380   { TCocoaSpinEditStepper }
381 
382   TCocoaSpinEditStepper = objcclass(NSStepper)
383     callback: ICommonCallback;
acceptsFirstMousenull384     function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
385     procedure mouseDown(event: NSEvent); override;
386     procedure mouseUp(event: NSEvent); override;
387     procedure rightMouseDown(event: NSEvent); override;
388     procedure rightMouseUp(event: NSEvent); override;
389     procedure rightMouseDragged(event: NSEvent); override;
390     procedure otherMouseDown(event: NSEvent); override;
391     procedure otherMouseUp(event: NSEvent); override;
392     procedure otherMouseDragged(event: NSEvent); override;
393     procedure mouseDragged(event: NSEvent); override;
394     procedure mouseMoved(event: NSEvent); override;
395     procedure scrollWheel(event: NSEvent); override;
396   end;
397 
398   TCocoaSpinEdit = objcclass(TCocoaTextField)
399     Stepper: NSStepper;
400     NumberFormatter: NSNumberFormatter;
401     decimalPlaces: Integer;
402 
403     avoidChangeEvent: Integer;
404 
405     //Spin: TCustomFloatSpinEdit;
406     procedure dealloc; override;
updateSteppernull407     function updateStepper: boolean; message 'updateStepper';
408     procedure UpdateControl(min, max, inc, avalue: double; ADecimalPlaces: Integer); message 'UpdateControl:::::';
409     procedure lclCreateSubcontrols(const AParams: TCreateParams); message 'lclCreateSubControls:';
410     procedure lclReleaseSubcontrols; message 'lclReleaseSubcontrols';
411     procedure PositionSubcontrols(const ALeft, ATop, AWidth, AHeight: Integer); message 'PositionSubcontrols:ATop:AWidth:AHeight:';
412     procedure StepperChanged(sender: NSObject); message 'StepperChanged:';
413     procedure textDidChange(notification: NSNotification); override;
414     // lcl
acceptsFirstRespondernull415     function acceptsFirstResponder: LCLObjCBoolean; override;
lclGetCallbacknull416     function lclGetCallback: ICommonCallback; override;
417     procedure lclClearCallback; override;
418     procedure resetCursorRects; override;
419     procedure lclSetVisible(AVisible: Boolean); override;
420     procedure lclSetFrame(const r: TRect); override;
421     // NSViewFix
fittingSizenull422     function fittingSize: NSSize; override;
423     // mouse
acceptsFirstMousenull424     function acceptsFirstMouse(event: NSEvent): LCLObjCBoolean; override;
425     procedure mouseDown(event: NSEvent); override;
426     procedure mouseUp(event: NSEvent); override;
427     procedure rightMouseDown(event: NSEvent); override;
428     procedure rightMouseUp(event: NSEvent); override;
429     procedure rightMouseDragged(event: NSEvent); override;
430     procedure otherMouseDown(event: NSEvent); override;
431     procedure otherMouseUp(event: NSEvent); override;
432     procedure otherMouseDragged(event: NSEvent); override;
433     procedure mouseDragged(event: NSEvent); override;
434     procedure mouseMoved(event: NSEvent); override;
435   end;
436 {$ENDIF}
437 
438 // these constants are missing from CocoaAll for some reason
439 const
440   NSTextAlignmentLeft      = 0;
441   NSTextAlignmentRight     = 1; // it's 2 for iOS and family
442   NSTextAlignmentCenter    = 2; // it's 1 for iOS and family
443   NSTextAlignmentJustified = 3;
444   NSTextAlignmentNatural   = 4;
445 
GetFieldEditornull446 function GetFieldEditor(afield: NSTextField): TCocoaFieldEditor;
447 
448 implementation
449 
GetFieldEditornull450 function GetFieldEditor(afield: NSTextField): TCocoaFieldEditor;
451 var
452   lFieldEditor: TCocoaFieldEditor;
453   lText: NSText;
454   window: NSWindow;
455 begin
456   Result := nil;
457   if not Assigned(afield) then Exit;
458   window := afield.window;
459   if window = nil then Exit;
460 
461   {$ifdef BOOLFIX}
462   lText := window.fieldEditor_forObject_(Ord(True), afield);
463   {$else}
464   lText := window.fieldEditor_forObject(True, afield);
465   {$endif}
466   if (lText <> nil) and lText.isKindOfClass_(TCocoaFieldEditor) then
467   begin
468     Result := TCocoaFieldEditor(lText);
469   end;
470 end;
471 
472 { TCocoaReadOnlyComboBoxList }
473 
474 procedure TCocoaReadOnlyComboBoxList.InsertItem(Index: Integer;
475   const S: string; O: TObject);
476 var
477   astr     : NSString;
478   mn       : NSMenuItem;
479   menuItem : TCocoaReadOnlyView;
480   track: NSTrackingArea;
481 begin
482   inherited InsertItem(Index, S, O);
483 
484   // Adding an item with its final name will cause it to be deleted,
485   // so we need to first add all items with unique names, and then
486   // rename all of them, see bug 30847
487   astr := nsstr('@@@add');
488   if Index >= FOwner.numberOfItems then
489   begin
490     FOwner.addItemWithTitle(astr);
491     Index := FOwner.numberOfItems-1;
492   end else
493     FOwner.insertItemWithTitle_atIndex(astr, Index);
494 
495   mn := FOwner.itemAtIndex(Index);
496   if not Assigned(mn) then Exit;
497 
498   astr := NSStringUtf8(S);
499   mn.setTitle(astr);
500   astr.release;
501 
502   if FOwner.isOwnerDrawn then
503   begin
504     menuItem := TCocoaReadOnlyView.alloc.initWithFrame( NSMakeRect(0,0, FOwner.frame.size.width, COMBOBOX_RO_MENUITEM_HEIGHT) );
505     menuItem.itemIndex := Index;
506     menuItem.combobox := FOwner;
507 
508     track:=NSTrackingArea(NSTrackingArea.alloc).initWithRect_options_owner_userInfo(
509       menuItem.bounds
510       , NSTrackingMouseEnteredAndExited or NSTrackingActiveAlways
511       , menuItem, nil);
512     menuItem.addTrackingArea(track);
513     track.release;
514     mn.setView(menuItem);
515   end;
516 end;
517 
518 procedure TCocoaReadOnlyComboBoxList.Put(Index: Integer; const S: string);
519 var
520   astr: NSString;
521   mn  : NSMenuItem;
522 begin
523   inherited Put(Index, S);
524   if ((index >= 0) and (Index <= FOwner.numberOfItems)) then
525   begin
526     mn := FOwner.itemAtIndex(Index);
527     astr := NSStringUtf8(S);
528     mn.setTitle(astr);
529     astr.release;
530   end;
531 end;
532 
533 constructor TCocoaReadOnlyComboBoxList.Create(AOwner: TCocoaReadOnlyComboBox);
534 begin
535   inherited Create;
536   FOwner := AOwner;
537 end;
538 
539 procedure TCocoaReadOnlyComboBoxList.Delete(Index: Integer);
540 begin
541   inherited Delete(Index);
542   if (Index>=0) and (Index < FOwner.numberOfItems) then
543     FOwner.removeItemAtIndex(Index);
544 end;
545 
546 procedure TCocoaReadOnlyComboBoxList.Clear;
547 begin
548   inherited Clear;
549   FOwner.removeAllItems;
550 end;
551 
552 { TCococaFieldEditorExt }
553 
lclGetCallbacknull554 function TCococaFieldEditorExt.lclGetCallback: ICommonCallback;
555 begin
556   if isFieldEditor and Assigned(delegate) then
557   begin
558     Result := NSObject(delegate).lclGetCallback;
559   end
560   else
561     Result := inherited lclGetCallback;
562 end;
563 
564 { TCocoaSpinEditStepper }
565 
TCocoaSpinEditStepper.acceptsFirstMousenull566 function TCocoaSpinEditStepper.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
567 begin
568   Result := NSViewCanFocus(Self);
569 end;
570 
571 procedure TCocoaSpinEditStepper.mouseDown(event: NSEvent);
572 begin
573   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
574   begin
575     inherited mouseDown(event);
576     if Assigned(Callback) then
577       callback.MouseUpDownEvent(event, true);
578   end;
579 end;
580 
581 procedure TCocoaSpinEditStepper.mouseUp(event: NSEvent);
582 begin
583   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
584     inherited mouseUp(event);
585 end;
586 
587 procedure TCocoaSpinEditStepper.rightMouseDown(event: NSEvent);
588 begin
589   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
590     inherited rightMouseDown(event);
591 end;
592 
593 procedure TCocoaSpinEditStepper.rightMouseUp(event: NSEvent);
594 begin
595   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
596     inherited rightMouseUp(event);
597 end;
598 
599 procedure TCocoaSpinEditStepper.rightMouseDragged(event: NSEvent);
600 begin
601   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
602     inherited rightMouseDragged(event);
603 end;
604 
605 procedure TCocoaSpinEditStepper.otherMouseDown(event: NSEvent);
606 begin
607   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
608     inherited otherMouseDown(event);
609 end;
610 
611 procedure TCocoaSpinEditStepper.otherMouseUp(event: NSEvent);
612 begin
613   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
614     inherited otherMouseUp(event);
615 end;
616 
617 procedure TCocoaSpinEditStepper.otherMouseDragged(event: NSEvent);
618 begin
619   if not Assigned(callback) or not callback.MouseMove(event) then
620     inherited otherMouseDragged(event);
621 end;
622 
623 procedure TCocoaSpinEditStepper.mouseDragged(event: NSEvent);
624 begin
625   if not Assigned(callback) or not callback.MouseMove(event) then
626     inherited mouseDragged(event);
627 end;
628 
629 procedure TCocoaSpinEditStepper.mouseMoved(event: NSEvent);
630 begin
631   if not Assigned(callback) or not callback.MouseMove(event) then
632     inherited mouseMoved(event);
633 end;
634 
635 procedure TCocoaSpinEditStepper.scrollWheel(event: NSEvent);
636 begin
637   if not Assigned(callback) or not callback.scrollWheel(event) then
638     inherited scrollWheel(event);
639 end;
640 
641 { TCocoaReadOnlyView }
642 
643 procedure TCocoaReadOnlyView.drawRect(dirtyRect: NSRect);
644 var
645   ctx : TCocoaContext;
646 begin
647   inherited drawRect(dirtyRect);
648 
649   if not Assigned(combobox) then Exit;
650 
651   ctx := TCocoaContext.Create(NSGraphicsContext.currentContext);
652   try
653     ctx.InitDraw(Round(dirtyRect.size.width), Round(dirtyRect.size.height));
654     combobox.callback.ComboBoxDrawItem(itemIndex, ctx, NSRectToRect(frame), isMouseOver);
655   finally
656     ctx.Free;
657   end;
658 end;
659 
660 procedure TCocoaReadOnlyView.mouseUp(event: NSEvent);
661 begin
662   inherited mouseUp(event);
663   if Assigned(combobox) then
664   begin
665     combobox.selectItemAtIndex(itemIndex);
666     combobox.callback.ComboBoxSelectionDidChange;
667     combobox.menu.performActionForItemAtIndex(itemIndex);
668     combobox.menu.cancelTracking;
669   end;
670 end;
671 
672 procedure TCocoaReadOnlyView.mouseEntered(theEvent: NSEvent);
673 begin
674   isMouseOver := true;
675   inherited mouseEntered(theEvent);
676   lclInvalidate;
677 end;
678 
679 procedure TCocoaReadOnlyView.mouseExited(theEvent: NSEvent);
680 begin
681   isMouseOver := false;
682   inherited mouseExited(theEvent);
683   lclInvalidate;
684 end;
685 
686 { TCocoaComboBoxItemCell }
687 
688 procedure TCocoaComboBoxItemCell.drawWithFrame_inView(cellFrame: NSRect; controlView_: NSView);
689 begin
690   inherited drawWithFrame_inView(cellFrame, controlView_);
691 end;
692 
tableView_dataCellForTableColumn_rownull693 function TCocoaComboBoxCell.tableView_dataCellForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): NSCell;
694 begin
695   Result := TCocoaComboBoxItemCell.alloc.initTextCell(NSString.string_);
696 end;
697 
698 {
699 procedure TCocoaComboBoxCell.tableView_willDisplayCell_forTableColumn_row(
700   tableView: NSTableView; cell: id; tableColumn: NSTableColumn; row: NSInteger);
701 var
702   sz : NSSize;
703   pr : NSView;
704   frm : NSRect;
705 begin
706   writeln('will display ', row);
707   if row = 0 then
708   begin
709     sz := tableView.frame.size;
710     sz.width := 300;
711     tableView.setFrameSize(sz);
712     pr := tableView;
713     while Assigned(pr) do begin
714       writeln(pr.lclClassname);
715       pr := pr.superview;
716     end;
717     writeln('at 10: ', tableView.window.lclClassName);
718     writeln('max size = ', tableView.window.maxSize.width:0:0);
719     writeln('min size = ', tableView.window.minSize.width:0:0);
720     frm := tableView.window.frame;
721     writeln('    size = ', frm.size.width:0:0);
722     frm := NSView(tableView.window.contentView).frame;
723     writeln('clt size = ', frm.size.width:0:0);
724     frm.size.width := 96 * 2; //frm.size.width * 2;
725     tableView.window.setContentSize(frm.size);
726     writeln('clt size = ', frm.size.width:0:0);
727   end;
728 end;
729 }
730 
731 {function TCocoaComboBoxCell.tableView_heightOfRow(tableView: NSTableView;
732   row: NSInteger): CGFloat;
733 begin
734   writeln('height of row ', row);
735   Result := 32;
736 end;}
737 
738 { TCocoaFieldEditor }
739 
GetEditBoxnull740 function GetEditBox(src: TCocoaFieldEditor): NSView;
741 var
742   v : NSObject;
743 begin
744   Result := nil;
745   if not Assigned(src) then Exit;
746   v := NSObject(src.delegate);
747   if Assigned(v) and (v.isKindOfClass(NSView)) then
748     Result := NSView(v);
749 end;
750 
TCocoaFieldEditor.lclGetCallbacknull751 function TCocoaFieldEditor.lclGetCallback: ICommonCallback;
752 begin
753   if Assigned(delegate) then Result := NSObject(delegate).lclGetCallback
754   else Result := nil;
755 end;
756 
TCocoaFieldEditor.becomeFirstRespondernull757 function TCocoaFieldEditor.becomeFirstResponder: LCLObjCBoolean;
758 begin
759   if goingReadOnly then Result := false
760   else Result:=inherited becomeFirstResponder;
761 end;
762 
763 procedure TCocoaFieldEditor.doCommandBySelector(aSelector: SEL);
764 begin
765   inherited doCommandBySelector(aSelector);
766   keyCaptured := False;
767 end;
768 
769 procedure TCocoaFieldEditor.insertNewline(sender: id);
770 begin
771   // 10.6 cocoa handles the editors Return key as "insertNewLine" command (that makes sense)
772   // which turns into textDidEndEditing done command (that also makes sense)
773   // however, it ends up in an endless loop of "end-editing" calls.
774   //
775   // todo: find the reason for the endless loop and resolve it properly
776 end;
777 
778 procedure TCocoaFieldEditor.keyDown(event: NSEvent);
779 begin
780   // Input methods may capture Enter and Escape to accept/dismiss their popup
781   // windows.  There isn't a way to detect the popup is open, so allow the
782   // keys through.  If they make it to the default handlers let the LCL process
783   // them further.  If they got swallowed prevent further processing.
784   if Assigned(lclGetCallback) and (event.modifierFlags_ = 0) and
785     ((NSEventRawKeyChar(event) = #13) or (NSEventRawKeyChar(event) = #27)) then
786   begin
787     keyCaptured := True;
788     inherited keyDown(event);
789     if keyCaptured then
790       lclGetCallback.KeyEvHandled;
791   end
792   else
793     inherited keyDown(event);
794 end;
795 
796 procedure TCocoaFieldEditor.mouseDown(event: NSEvent);
797 var
798   v : NSView;
799 begin
800   v := GetEditBox(Self);
801   if Assigned(v) then
802   begin
803     if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
804     begin
805       inherited mouseDown(event);
806       // NSTextView runs internal mouse-tracking loop in it's mouseDown implemenation.
807       // Thus "inherited mouseDown" only returns after the mouse has been released.
808       // why is TCocoaTextView not affected?
809       if Assigned(v) and Assigned(v.lclGetCallback) then
810         v.lclGetCallback.MouseUpDownEvent(event, true);
811     end;
812   end else
813     inherited mouseDown(event);
814 end;
815 
816 procedure TCocoaFieldEditor.mouseUp(event: NSEvent);
817 var
818   v : NSView;
819 begin
820   v := GetEditBox(Self);
821   if Assigned(v) then
822   begin
823     if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
824       inherited mouseUp(event);
825   end else
826     inherited mouseUp(event);
827 end;
828 
829 procedure TCocoaFieldEditor.rightMouseDown(event: NSEvent);
830 var
831   v : NSView;
832 begin
833   v := GetEditBox(Self);
834   if Assigned(v) then
835   begin
836     if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
837       inherited rightMouseDown(event);
838   end else
839     inherited rightMouseDown(event);
840 end;
841 
842 procedure TCocoaFieldEditor.rightMouseUp(event: NSEvent);
843 var
844   v : NSView;
845 begin
846   v := GetEditBox(Self);
847   if Assigned(v) then
848   begin
849     if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
850       inherited rightMouseUp(event);
851   end else
852     inherited rightMouseUp(event);
853 end;
854 
855 procedure TCocoaFieldEditor.otherMouseDown(event: NSEvent);
856 var
857   v : NSView;
858 begin
859   v := GetEditBox(Self);
860   if Assigned(v) then
861   begin
862     if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
863       inherited otherMouseDown(event);
864   end else
865     inherited otherMouseDown(event);
866 end;
867 
868 procedure TCocoaFieldEditor.otherMouseUp(event: NSEvent);
869 var
870   v : NSView;
871 begin
872   v := GetEditBox(Self);
873   if Assigned(v) then
874   begin
875     if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseUpDownEvent(event) then
876       inherited otherMouseUp(event);
877   end else
878     inherited otherMouseUp(event);
879 end;
880 
881 procedure TCocoaFieldEditor.mouseDragged(event: NSEvent);
882 var
883   v : NSView;
884 begin
885   v := GetEditBox(Self);
886   if Assigned(v) then
887   begin
888     if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseMove(event) then
889       inherited mouseDragged(event);
890   end else
891     inherited mouseDragged(event);
892 end;
893 
894 procedure TCocoaFieldEditor.mouseMoved(event: NSEvent);
895 var
896   v : NSView;
897 begin
898   v := GetEditBox(Self);
899   if Assigned(v) then
900   begin
901     if Assigned(v.lclGetCallback) and not v.lclGetCallback.MouseMove(event) then
902       inherited mouseMoved(event);
903   end else
904     inherited mouseMoved(event);
905 end;
906 
907 procedure TCocoaFieldEditor.scrollWheel(event: NSEvent);
908 var
909   v : NSView;
910 begin
911   v := GetEditBox(Self);
912   if Assigned(v) then
913   begin
914     if Assigned(v.lclGetCallback) and not v.lclGetCallback.scrollWheel(event) then
915       inherited mouseMoved(event);
916   end else
917     inherited scrollWheel(event);
918 end;
919 
920 { TCocoaTextField }
921 
TCocoaTextField.acceptsFirstRespondernull922 function TCocoaTextField.acceptsFirstResponder: LCLObjCBoolean;
923 begin
924   Result := NSViewCanFocus(Self);
925 end;
926 
TCocoaTextField.lclGetCallbacknull927 function TCocoaTextField.lclGetCallback: ICommonCallback;
928 begin
929   Result := callback;
930 end;
931 
932 procedure TCocoaTextField.lclClearCallback;
933 begin
934   callback := nil;
935 end;
936 
937 procedure TCocoaTextField.resetCursorRects;
938 begin
939   // this will not work well because
940   // cocoa replaced TextField and TextView cursors in
941   // mouseEntered, mouseMoved and CursorUpdate
942   if not callback.resetCursorRects then
943     inherited resetCursorRects;
944 end;
945 
946 procedure TCocoaTextField.textDidChange(notification: NSNotification);
947 begin
948   if (maxLength>0) and Assigned(stringValue) and (stringValue.length > maxLength) then
949     setStringValue(stringValue.substringWithRange(NSMakeRange(0,maxLength)));
950   if callback <> nil then
951     callback.SendOnTextChanged;
952 end;
953 
954 procedure TCocoaTextField.mouseDown(event: NSEvent);
955 begin
956   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
957   begin
958     inherited mouseDown(event);
959     // the text selection is handled withing mouseDown
960     if Assigned(callback) then
961       callback.MouseUpDownEvent(event, True);
962   end;
963 end;
964 
965 procedure TCocoaTextField.mouseUp(event: NSEvent);
966 begin
967   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
968   begin
969     inherited mouseUp(event);
970   end;
971 end;
972 
973 procedure TCocoaTextField.rightMouseDown(event: NSEvent);
974 begin
975   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
976     inherited rightMouseDown(event);
977 end;
978 
979 procedure TCocoaTextField.rightMouseUp(event: NSEvent);
980 begin
981   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
982     inherited rightMouseUp(event);
983 end;
984 
985 procedure TCocoaTextField.otherMouseDown(event: NSEvent);
986 begin
987   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
988     inherited otherMouseDown(event);
989 end;
990 
991 procedure TCocoaTextField.otherMouseUp(event: NSEvent);
992 begin
993   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
994     inherited otherMouseUp(event);
995 end;
996 
997 procedure TCocoaTextField.mouseDragged(event: NSEvent);
998 begin
999   if Assigned(callback) and not callback.MouseMove(event) then
1000     inherited mouseDragged(event);
1001 end;
1002 
1003 procedure TCocoaTextField.mouseMoved(event: NSEvent);
1004 begin
1005   if Assigned(callback) and not callback.MouseMove(event) then
1006     inherited mouseMoved(event);
1007 end;
1008 
1009 procedure TCocoaTextField.scrollWheel(event: NSEvent);
1010 begin
1011   if Assigned(callback) and not callback.scrollWheel(event) then
1012     inherited scrollWheel(event);
1013 end;
1014 
1015 procedure TCocoaTextField.lclSetMaxLength(amax: integer);
1016 begin
1017   maxLength := amax;
1018 end;
1019 
1020 { TCocoaTextView }
1021 
1022 procedure TCocoaTextView.changeColor(sender: id);
1023 begin
1024   //preventing text color from being changed
1025   //inherited changeColor(sender);
1026 end;
1027 
1028 procedure TCocoaTextView.dealloc;
1029 begin
1030   if Assigned(FUndoManager) then
1031     FUndoManager.release;
1032   inherited dealloc;
1033 end;
1034 
acceptsFirstRespondernull1035 function TCocoaTextView.acceptsFirstResponder: LCLObjCBoolean;
1036 begin
1037   Result := NSViewCanFocus(Self);
1038 end;
1039 
lclGetCallbacknull1040 function TCocoaTextView.lclGetCallback: ICommonCallback;
1041 begin
1042   Result := callback;
1043 end;
1044 
1045 procedure TCocoaTextView.lclClearCallback;
1046 begin
1047   callback := nil;
1048 end;
1049 
1050 procedure TCocoaTextView.resetCursorRects;
1051 begin
1052   if not callback.resetCursorRects then
1053     inherited resetCursorRects;
1054 end;
1055 
1056 procedure TCocoaTextView.doCommandBySelector(aSelector: SEL);
1057 begin
1058   inherited doCommandBySelector(aSelector);
1059   keyCaptured := False;
1060 end;
1061 
1062 procedure TCocoaTextView.insertNewline(sender: id);
1063 begin
1064   if wantReturns then
1065     inherited insertNewline(sender);
1066 end;
1067 
1068 procedure TCocoaTextView.keyDown(event: NSEvent);
1069 begin
1070   // See TCocoaFieldEditor.keyDown
1071   if Assigned(lclGetCallback) and (event.modifierFlags_ = 0) and
1072     ((NSEventRawKeyChar(event) = #13) or (NSEventRawKeyChar(event) = #27)) then
1073   begin
1074     keyCaptured := True;
1075     inherited keyDown(event);
1076     if keyCaptured then
1077       lclGetCallback.KeyEvHandled;
1078   end
1079   else
1080     inherited keyDown(event);
1081 end;
1082 
1083 procedure TCocoaTextView.mouseDown(event: NSEvent);
1084 begin
1085   if Assigned(callback) then
1086   begin
1087     if not callback.MouseUpDownEvent(event) then
1088     begin
1089       inherited mouseDown(event);
1090 
1091       // Cocoa doesn't call mouseUp for NSTextView, so we have to emulate it here :(
1092       // See bug 29000
1093       if Assigned(callback) then
1094         callback.MouseUpDownEvent(event, True);
1095     end;
1096   end else
1097     inherited mouseDown(event);
1098 end;
1099 
1100 procedure TCocoaTextView.mouseUp(event: NSEvent);
1101 begin
1102   if callback <> nil then
1103     callback.MouseUpDownEvent(event);
1104   inherited mouseUp(event);
1105 end;
1106 
1107 procedure TCocoaTextView.rightMouseDown(event: NSEvent);
1108 begin
1109   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1110     inherited rightMouseDown(event);
1111 end;
1112 
1113 procedure TCocoaTextView.rightMouseUp(event: NSEvent);
1114 begin
1115   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1116     inherited rightMouseUp(event);
1117 end;
1118 
1119 procedure TCocoaTextView.otherMouseDown(event: NSEvent);
1120 begin
1121   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1122     inherited otherMouseDown(event);
1123 end;
1124 
1125 procedure TCocoaTextView.otherMouseUp(event: NSEvent);
1126 begin
1127   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1128     inherited otherMouseUp(event);
1129 end;
1130 
1131 procedure TCocoaTextView.mouseDragged(event: NSEvent);
1132 begin
1133   if Assigned(callback) and not callback.MouseMove(event) then
1134     inherited mouseDragged(event);
1135 end;
1136 
1137 procedure TCocoaTextView.mouseEntered(event: NSEvent);
1138 begin
1139   inherited mouseEntered(event);
1140 end;
1141 
1142 procedure TCocoaTextView.mouseExited(event: NSEvent);
1143 begin
1144   inherited mouseExited(event);
1145 end;
1146 
1147 procedure TCocoaTextView.mouseMoved(event: NSEvent);
1148 begin
1149   if Assigned(callback) and not callback.MouseMove(event) then
1150     inherited mouseMoved(event);
1151 end;
1152 
lclIsEnablednull1153 function TCocoaTextView.lclIsEnabled: Boolean;
1154 begin
1155   Result := FEnabled;
1156 end;
1157 
1158 procedure TCocoaTextView.lclSetEnabled(AEnabled: Boolean);
1159 begin
1160   FEnabled := AEnabled;
1161 end;
1162 
1163 procedure TCocoaTextView.textDidChange(notification: NSNotification);
1164 begin
1165   if (callback <> nil) and (supressTextChangeEvent = 0) then
1166     callback.SendOnTextChanged;
1167 end;
1168 
1169 procedure TCocoaTextView.lclExpectedKeys(var wantTabs, wantArrows, wantReturn,
1170   wantAll: Boolean);
1171 begin
1172   wantTabs := true;
1173   wantArrows := true;
1174   wantReturn := true;
1175   wantAll := true;
1176 end;
1177 
undoManagerForTextViewnull1178 function TCocoaTextView.undoManagerForTextView(view: NSTextView): NSUndoManager;
1179 begin
1180   if not Assigned(FUndoManager) then
1181     FUndoManager := NSUndoManager.alloc.init;
1182   Result := FUndoManager;
1183 end;
1184 
1185 { TCocoaSecureTextField }
1186 
acceptsFirstRespondernull1187 function TCocoaSecureTextField.acceptsFirstResponder: LCLObjCBoolean;
1188 begin
1189   Result := NSViewCanFocus(Self);
1190 end;
1191 
1192 procedure TCocoaSecureTextField.resetCursorRects;
1193 begin
1194   if not callback.resetCursorRects then
1195     inherited resetCursorRects;
1196 end;
1197 
lclGetCallbacknull1198 function TCocoaSecureTextField.lclGetCallback: ICommonCallback;
1199 begin
1200   Result := callback;
1201 end;
1202 
1203 procedure TCocoaSecureTextField.lclClearCallback;
1204 begin
1205   callback := nil;
1206 end;
1207 
1208 procedure TCocoaSecureTextField.textDidChange(notification: NSNotification);
1209 begin
1210   inherited;
1211   if (maxLength>0) and Assigned(stringValue) and (stringValue.length > maxLength) then
1212     setStringValue(stringValue.substringWithRange(NSMakeRange(0,maxLength)));
1213   if callback <> nil then
1214     callback.SendOnTextChanged;
1215 end;
1216 
1217 procedure TCocoaSecureTextField.mouseDown(event: NSEvent);
1218 begin
1219   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1220   begin
1221     inherited mouseDown(event);
1222 
1223     if Assigned(callback) then
1224       callback.MouseUpDownEvent(event, True);
1225   end;
1226 end;
1227 
1228 procedure TCocoaSecureTextField.mouseUp(event: NSEvent);
1229 begin
1230   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1231     inherited mouseUp(event);
1232 end;
1233 
1234 procedure TCocoaSecureTextField.rightMouseDown(event: NSEvent);
1235 begin
1236   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1237     inherited rightMouseDown(event);
1238 end;
1239 
1240 procedure TCocoaSecureTextField.rightMouseUp(event: NSEvent);
1241 begin
1242   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1243     inherited rightMouseUp(event);
1244 end;
1245 
1246 procedure TCocoaSecureTextField.otherMouseDown(event: NSEvent);
1247 begin
1248   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1249     inherited otherMouseDown(event);
1250 end;
1251 
1252 procedure TCocoaSecureTextField.otherMouseUp(event: NSEvent);
1253 begin
1254   if Assigned(callback) and not callback.MouseUpDownEvent(event) then
1255     inherited otherMouseUp(event);
1256 end;
1257 
1258 procedure TCocoaSecureTextField.mouseDragged(event: NSEvent);
1259 begin
1260   if Assigned(callback) and not callback.MouseMove(event) then
1261     inherited mouseDragged(event);
1262 end;
1263 
1264 procedure TCocoaSecureTextField.mouseMoved(event: NSEvent);
1265 begin
1266   if Assigned(callback) and not callback.MouseMove(event) then
1267     inherited mouseMoved(event);
1268 end;
1269 
1270 procedure TCocoaSecureTextField.scrollWheel(event: NSEvent);
1271 begin
1272   if Assigned(callback) and not callback.scrollWheel(event) then
1273     inherited scrollWheel(event);
1274 end;
1275 
1276 procedure TCocoaSecureTextField.lclSetMaxLength(amax: integer);
1277 begin
1278   MaxLength := amax;
1279 end;
1280 
1281 { TCocoaEditComboBoxList }
1282 
1283 procedure TCocoaEditComboBoxList.InsertItem(Index: Integer; const S: string;
1284   O: TObject);
1285 begin
1286   inherited InsertItem(Index, S, O);
1287   FOwner.noteNumberOfItemsChanged;
1288 end;
1289 
1290 procedure TCocoaEditComboBoxList.Changed;
1291 begin
1292   inherited Changed;
1293   FOwner.reloadData;
1294 end;
1295 
1296 constructor TCocoaEditComboBoxList.Create(AOwner: TCocoaComboBox);
1297 begin
1298   inherited Create;
1299   FOwner := AOwner;
1300 end;
1301 
1302 { TCocoaComboBox }
1303 
1304 procedure TCocoaComboBox.setStringValue(avalue: NSString);
1305 var
1306   ch : Boolean;
1307   s  : NSString;
1308 begin
1309   s := stringValue;
1310   ch := (Assigned(s)
1311         and Assigned(avalue)
1312         and (s.compare(avalue) <> NSOrderedSame));
1313 
1314   inherited setStringValue(avalue);
1315 
1316   if ch and userSel and Assigned(callback) then
1317     callback.SendOnChange;
1318 end;
1319 
lclGetFrameToLayoutDeltanull1320 function TCocoaComboBox.lclGetFrameToLayoutDelta: TRect;
1321 begin
1322   // todo: on 10.7 or later there's a special API for that!
1323     // The data is received from 10.6 Interface Builder
1324   case NSCell(Self.Cell).controlSize of
1325     NSSmallControlSize: begin
1326       Result.Left := 0;
1327       Result.Top := 1;
1328       Result.Right := -3;
1329       Result.Bottom := -4;
1330     end;
1331     NSMiniControlSize: begin
1332       Result.Left := 0;
1333       Result.Top := 1;
1334       Result.Right := -2;
1335       Result.Bottom := -4;
1336     end;
1337   else
1338     // NSRegularControlSize
1339     Result.Left := 0;
1340     Result.Top := 2;
1341     Result.Right := -3;
1342     Result.Bottom := -4;
1343   end;
1344 end;
1345 
TCocoaComboBox.acceptsFirstRespondernull1346 function TCocoaComboBox.acceptsFirstResponder: LCLObjCBoolean;
1347 begin
1348   Result := NSViewCanFocus(Self);
1349 end;
1350 
1351 procedure TCocoaComboBox.textDidChange(notification: NSNotification);
1352 begin
1353   inherited textDidChange(notification);
1354   if Assigned(callback) then
1355     callback.SendOnChange;
1356 end;
1357 
TCocoaComboBox.comboBox_objectValueForItemAtIndex_null1358 function TCocoaComboBox.comboBox_objectValueForItemAtIndex_(combo:TCocoaComboBox;
1359   row: NSInteger):id;
1360 var
1361   ns : NSString;
1362 begin
1363   if not Assigned(list) or (row<0) or (row>=list.Count) then
1364     Result:=nil
1365   else
1366   begin
1367     ns := NSStringUtf8(list[row]);
1368     Result := ns;
1369     ns.autorelease;
1370   end;
1371 end;
1372 
TCocoaComboBox.comboBox_indexOfItemWithStringValuenull1373 function TCocoaComboBox.comboBox_indexOfItemWithStringValue(
1374   aComboBox: NSComboBox; string_: NSString): NSUInteger;
1375 var
1376   idx : integer;
1377 begin
1378   idx := indexOfSelectedItem;
1379   if (idx>=0) and (idx<list.Count) and (list[idx]=string_.UTF8String) then
1380     // this is used for the case of the same items in the combobox
1381     Result:=idx
1382   else
1383   begin
1384     // todo: consider a faster search?
1385     idx := list.IndexOf(string_.UTF8String);
1386     if idx<0 then
1387       Result := NSNotFound
1388     else
1389       Result := idx;
1390   end;
1391 end;
1392 
numberOfItemsInComboBoxnull1393 function TCocoaComboBox.numberOfItemsInComboBox(combo:TCocoaComboBox):NSInteger;
1394 begin
1395   if not Assigned(list) then Result:=0
1396   else Result:=list.Count;
1397 end;
1398 
1399 procedure TCocoaComboBox.dealloc;
1400 begin
1401   if Assigned(resultNS) then resultNS.release;
1402   inherited dealloc;
1403 end;
1404 
TCocoaComboBox.lclGetCallbacknull1405 function TCocoaComboBox.lclGetCallback: ICommonCallback;
1406 begin
1407   Result := callback;
1408 end;
1409 
1410 procedure TCocoaComboBox.lclClearCallback;
1411 begin
1412   callback := nil;
1413 end;
1414 
1415 procedure TCocoaComboBox.resetCursorRects;
1416 begin
1417   if not callback.resetCursorRects then
1418     inherited resetCursorRects;
1419 end;
1420 
1421 procedure TCocoaComboBox.comboBoxWillPopUp(notification: NSNotification);
1422 begin
1423   callback.ComboBoxWillPopUp;
1424   isDown:=true;
1425 end;
1426 
1427 procedure TCocoaComboBox.comboBoxWillDismiss(notification: NSNotification);
1428 begin
1429   callback.ComboBoxWillDismiss;
1430   isDown:=false;
1431 end;
1432 
1433 procedure TCocoaComboBox.comboBoxSelectionDidChange(notification: NSNotification);
1434 var
1435   txt : NSString;
1436 begin
1437   txt := comboBox_objectValueForItemAtIndex_(self, indexOfSelectedItem);
1438   if Assigned(txt) then setStringValue( txt );
1439   if userSel then
1440     callback.ComboBoxSelectionDidChange;
1441   userSel := false;
1442 end;
1443 
1444 procedure TCocoaComboBox.comboBoxSelectionIsChanging(notification: NSNotification);
1445 begin
1446   userSel := true;
1447   callback.ComboBoxSelectionIsChanging;
1448 end;
1449 
TCocoaComboBox.acceptsFirstMousenull1450 function TCocoaComboBox.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
1451 begin
1452   Result:=true;
1453 end;
1454 
1455 procedure TCocoaComboBox.mouseDown(event: NSEvent);
1456 begin
1457   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1458     inherited mouseDown(event);
1459 end;
1460 
1461 procedure TCocoaComboBox.mouseUp(event: NSEvent);
1462 begin
1463   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1464     inherited mouseUp(event);
1465 end;
1466 
1467 procedure TCocoaComboBox.rightMouseDown(event: NSEvent);
1468 begin
1469   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1470     inherited rightMouseDown(event);
1471 end;
1472 
1473 procedure TCocoaComboBox.rightMouseUp(event: NSEvent);
1474 begin
1475   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1476     inherited rightMouseUp(event);
1477 end;
1478 
1479 procedure TCocoaComboBox.rightMouseDragged(event: NSEvent);
1480 begin
1481   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1482     inherited rightMouseDragged(event);
1483 end;
1484 
1485 procedure TCocoaComboBox.otherMouseDown(event: NSEvent);
1486 begin
1487   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1488     inherited otherMouseDown(event);
1489 end;
1490 
1491 procedure TCocoaComboBox.otherMouseUp(event: NSEvent);
1492 begin
1493   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1494     inherited otherMouseUp(event);
1495 end;
1496 
1497 procedure TCocoaComboBox.otherMouseDragged(event: NSEvent);
1498 begin
1499   if not Assigned(callback) or not callback.MouseMove(event) then
1500     inherited otherMouseDragged(event);
1501 end;
1502 
1503 procedure TCocoaComboBox.mouseDragged(event: NSEvent);
1504 begin
1505   if not Assigned(callback) or not callback.MouseMove(event) then
1506     inherited mouseDragged(event);
1507 end;
1508 
1509 procedure TCocoaComboBox.mouseMoved(event: NSEvent);
1510 begin
1511   // NSMouseMove event is being sent even after the selection is made.
1512   // In Cocoa world, the event is sent to the comboBox NSTextView edit section.
1513   // (even is the cursor is NOT over the NSTextView itself, but rather the popup window)
1514   //
1515   // The CocoaWS forwards the event to LCL. And LCL recognizes the event as a mouse action
1516   // beyond combobox boundries (it's NSMouseMove and not NSMouseDrag).
1517   // causing the issues with Object Inspector (where the mouse move with a left button down)
1518   // is recognized as a switch to a different row.
1519   //
1520   // WinAPI doesn't send MouseMove events when popup is dropped.
1521   // Enforcing the same approach for Cocoa. If combobox is showing popup
1522   // all mousemoves are suppressed
1523   if isDown then Exit;
1524 
1525   if not Assigned(callback) or not callback.MouseMove(event) then
1526     inherited mouseMoved(event);
1527 end;
1528 
1529 procedure TCocoaComboBox.scrollWheel(event: NSEvent);
1530 begin
1531   if not Assigned(callback) or not callback.scrollWheel(event) then
1532     inherited scrollWheel(event);
1533 end;
1534 
1535 { TCocoaReadOnlyComboBox }
1536 
TCocoaReadOnlyComboBox.acceptsFirstRespondernull1537 function TCocoaReadOnlyComboBox.acceptsFirstResponder: LCLObjCBoolean;
1538 begin
1539   Result := NSViewCanFocus(Self);
1540 end;
1541 
1542 procedure TCocoaReadOnlyComboBox.dealloc;
1543 begin
1544   if resultNS <> nil then resultNS.release;
1545   inherited dealloc;
1546 end;
1547 
lclGetCallbacknull1548 function TCocoaReadOnlyComboBox.lclGetCallback: ICommonCallback;
1549 begin
1550   Result := callback;
1551 end;
1552 
1553 procedure TCocoaReadOnlyComboBox.lclClearCallback;
1554 begin
1555   callback := nil;
1556 end;
1557 
lclGetFrameToLayoutDeltanull1558 function TCocoaReadOnlyComboBox.lclGetFrameToLayoutDelta: TRect;
1559 begin
1560   // todo: on 10.7 or later there's a special API for that!
1561     // The data is received from 10.6 Interface Builder
1562   case NSCell(Self.Cell).controlSize of
1563     NSSmallControlSize: begin
1564       Result.Left := 3;
1565       Result.Top := 1;
1566       Result.Right := -3;
1567       Result.Bottom := -4;
1568     end;
1569     NSMiniControlSize: begin
1570       Result.Left := 1;
1571       Result.Top := 0;
1572       Result.Right := -2;
1573       Result.Bottom := 0;
1574     end;
1575   else
1576     // NSRegularControlSize
1577     Result.Left := 3;
1578     Result.Top := 2;
1579     Result.Right := -3;
1580     Result.Bottom := -4;
1581   end;
1582 end;
1583 
1584 procedure TCocoaReadOnlyComboBox.resetCursorRects;
1585 begin
1586   if not callback.resetCursorRects then
1587     inherited resetCursorRects;
1588 end;
1589 
1590 procedure TCocoaReadOnlyComboBox.comboboxAction(sender: id);
1591 begin
1592   //setTitle(NSSTR(PChar(Format('%d=%d', [indexOfSelectedItem, lastSelectedItemIndex])))); // <= for debugging
1593   if Assigned(callback) then
1594     callback.SendOnChange;
1595   if (indexOfSelectedItem <> lastSelectedItemIndex) and (callback <> nil) then
1596     callback.ComboBoxSelectionDidChange;
1597   lastSelectedItemIndex := indexOfSelectedItem;
1598 end;
1599 
stringValuenull1600 function TCocoaReadOnlyComboBox.stringValue: NSString;
1601 begin
1602   if Assigned(selectedItem) then
1603     Result:=selectedItem.title
1604   else
1605     Result:=inherited stringValue;
1606 end;
1607 
1608 procedure TCocoaReadOnlyComboBox.drawRect(dirtyRect: NSRect);
1609 var
1610   ctx : TCocoaContext;
1611   r   : NSRect;
1612   rr  : NSRect;
1613   dr  : TRect;
1614 begin
1615   inherited drawRect(dirtyRect);
1616 
1617   // if ownerDrawn style, then need to call "DrawItem" event
1618   if isOwnerDrawn and Assigned(callback)
1619     and (lastSelectedItemIndex>=0) and (lastSelectedItemIndex<list.Count)
1620   then
1621   begin
1622     ctx := TCocoaContext.Create(NSGraphicsContext.currentContext);
1623     try
1624       // todo: it's possible to query "cell" using titleRectForBounds method
1625       //       it actually returns somewhat desired offsets.
1626       //       (however, one should be careful and take layout offsets into account!)
1627       //       on the other hand, "cells" themselves are being deprecated...
1628       dr := lclFrame;
1629       Types.OffsetRect(dr, -dr.Left, -dr.Top);
1630       SubLayoutFromFrame( lclGetFrameToLayoutDelta, dr);
1631 
1632       // magic offsets are based on the macOS 10.13.6 visual style
1633       // but hard-coding is never reliable
1634       inc(dr.Left, 11);
1635       inc(dr.Top, 5);
1636       dec(dr.Right,18);
1637       dec(dr.Bottom, 2);
1638 
1639       callback.ComboBoxDrawItem(lastSelectedItemIndex, ctx, dr, false);
1640     finally
1641       ctx.Free;
1642     end;
1643   end;
1644 end;
1645 
TCocoaReadOnlyComboBox.acceptsFirstMousenull1646 function TCocoaReadOnlyComboBox.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
1647 begin
1648   Result:=true;
1649 end;
1650 
1651 procedure TCocoaReadOnlyComboBox.mouseDown(event: NSEvent);
1652 begin
1653   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1654   begin
1655     // a typical Apple "mouseDown" loop. The popup is shown on mouseDown event
1656     // The event only exists, whenever the popup is closed (for whatever reason)
1657     if Assigned(callback) then callback.ComboBoxWillPopUp;
1658     inherited mouseDown(event);
1659     if Assigned(callback) then callback.ComboBoxWillDismiss;
1660     callback.MouseUpDownEvent(event, true);
1661   end;
1662 end;
1663 
1664 procedure TCocoaReadOnlyComboBox.mouseUp(event: NSEvent);
1665 begin
1666   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1667     inherited mouseUp(event);
1668 end;
1669 
1670 procedure TCocoaReadOnlyComboBox.rightMouseDown(event: NSEvent);
1671 begin
1672   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1673     inherited rightMouseDown(event);
1674 end;
1675 
1676 procedure TCocoaReadOnlyComboBox.rightMouseUp(event: NSEvent);
1677 begin
1678   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1679     inherited rightMouseUp(event);
1680 end;
1681 
1682 procedure TCocoaReadOnlyComboBox.rightMouseDragged(event: NSEvent);
1683 begin
1684   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1685     inherited rightMouseDragged(event);
1686 end;
1687 
1688 procedure TCocoaReadOnlyComboBox.otherMouseDown(event: NSEvent);
1689 begin
1690   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1691     inherited otherMouseDown(event);
1692 end;
1693 
1694 procedure TCocoaReadOnlyComboBox.otherMouseUp(event: NSEvent);
1695 begin
1696   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
1697     inherited otherMouseUp(event);
1698 end;
1699 
1700 procedure TCocoaReadOnlyComboBox.otherMouseDragged(event: NSEvent);
1701 begin
1702   if not Assigned(callback) or not callback.MouseMove(event) then
1703     inherited otherMouseDragged(event);
1704 end;
1705 
1706 procedure TCocoaReadOnlyComboBox.mouseDragged(event: NSEvent);
1707 begin
1708   if not Assigned(callback) or not callback.MouseMove(event) then
1709     inherited mouseDragged(event);
1710 end;
1711 
1712 procedure TCocoaReadOnlyComboBox.mouseMoved(event: NSEvent);
1713 begin
1714   if not Assigned(callback) or not callback.MouseMove(event) then
1715     inherited mouseMoved(event);
1716 end;
1717 
1718 procedure TCocoaReadOnlyComboBox.scrollWheel(event: NSEvent);
1719 begin
1720   if not Assigned(callback) or not callback.scrollWheel(event) then
1721     inherited scrollWheel(event);
1722 end;
1723 
1724 { TCocoaSpinEdit }
1725 
1726 {$IFDEF COCOA_SPINEDIT_INSIDE_CONTAINER}
1727 
1728 procedure TCocoaSpinEdit.dealloc;
1729 begin
1730   if Stepper <> nil then
1731     Stepper.release;
1732   if Edit <> nil then
1733     Edit.release;
1734   inherited dealloc;
1735 end;
1736 
1737 procedure TCocoaSpinEdit.UpdateControl(ASpinEdit: TCustomFloatSpinEdit);
1738 begin
1739   Stepper.setMaxValue(ASpinEdit.MaxValue);
1740   Stepper.setMinValue(ASpinEdit.MinValue);
1741   Stepper.setIncrement(ASpinEdit.Increment);
1742   Stepper.setDoubleValue(ASpinEdit.Value);
1743 
1744   // update the UI too
1745   StepperChanged(Self);
1746 end;
1747 
1748 procedure TCocoaSpinEdit.CreateSubcontrols(ASpinEdit: TCustomFloatSpinEdit; const AParams: TCreateParams);
1749 var
1750   lParams: TCreateParams;
1751   lEditRect, lStepperRect: TRect;
1752 begin
1753   {$IFDEF COCOA_SPIN_DEBUG}
1754   WriteLn('[TCocoaSpinEdit.CreateSubcontrols]');
1755   {$ENDIF}
1756 
1757   Spin := ASpinEdit;
1758   CalculateSubcontrolPos(Types.Bounds(AParams.X, AParams.Y, AParams.Width,
1759     AParams.Height), lEditRect, lStepperRect);
1760 
1761   // Now creates the subcontrols
1762   lParams := AParams;
1763   lParams.WndParent := HWND(Self);
1764   lParams.Style := AParams.Style or WS_VISIBLE;
1765 
1766   // Stepper
1767   lParams.X := lStepperRect.Left;
1768   lParams.Y := lStepperRect.Top;
1769   lParams.Width := lStepperRect.Right - lStepperRect.Left;
1770   lParams.Height := lStepperRect.Bottom - lStepperRect.Top;
1771   Stepper := NSStepper.alloc.lclInitWithCreateParams(lParams);
1772   Stepper.setValueWraps(False);
1773 
1774   // Edit
1775   lParams.X := lEditRect.Left;
1776   lParams.Y := lEditRect.Top;
1777   lParams.Width := lEditRect.Right - lEditRect.Left;
1778   lParams.Height := lEditRect.Bottom - lEditRect.Top;
1779   Edit := NSTextField.alloc.lclInitWithCreateParams(lParams);
1780 
1781   // Change event for the stepper
1782   Stepper.setTarget(Self);
1783   Stepper.setAction(objcselector('StepperChanged:'));
1784 end;
1785 
1786 procedure TCocoaSpinEdit.PositionSubcontrols(const ALeft, ATop, AWidth, AHeight: Integer);
1787 var
1788   lNSStepperRect, lRect: NSRect;
1789   lStepperRect, lEditRect: TRect;
1790 begin
1791   {$IFDEF COCOA_SPIN_DEBUG}
1792   WriteLn('[TCocoaSpinEdit.PositionSubcontrols] AHeight=', AHeight);
1793   {$ENDIF}
1794 
1795   CalculateSubcontrolPos(Types.Bounds(ALeft, ATop, AWidth, AHeight), lEditRect, lStepperRect);
1796 
1797   // Stepper
1798   LCLToNSRect(lStepperRect, AHeight, lNSStepperRect);
1799   Stepper.setBounds(lNSStepperRect);
1800 
1801   // Edit
1802   LCLToNSRect(lEditRect, AHeight, lRect);
1803   Edit.setBounds(lRect);
1804 
1805   {$IFDEF COCOA_SPIN_DEBUG}
1806   WriteLn(':<[TCocoaSpinEdit.PositionSubcontrols] Edit=> X=', lRect.origin.x,
1807     ' Y=', lRect.origin.y, ' W=', lRect.size.width, ' H=', lRect.size.height,
1808     ' Stepper X=', lNSStepperRect.origin.x, ' Y=', lNSStepperRect.origin.y,
1809     ' W=', lNSStepperRect.size.width, ' H=', lNSStepperRect.size.height,
1810     ' frame.size.height=', frame.size.height);
1811   {$ENDIF}
1812 end;
1813 
1814 procedure TCocoaSpinEdit.CalculateSubcontrolPos(
1815   const ASpinLCLBounds: TRect; out AEditBounds, AStepperBounds: TRect);
1816 var
1817   lWidth, lHeight: Integer;
1818 begin
1819   lWidth := ASpinLCLBounds.Right - ASpinLCLBounds.Left;
1820   lHeight := ASpinLCLBounds.Bottom - ASpinLCLBounds.Top;
1821 
1822   // Stepper
1823   AStepperBounds.Left := lWidth - SPINEDIT_DEFAULT_STEPPER_WIDTH;
1824   AStepperBounds.Top := SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1825   AStepperBounds.Right := lWidth;
1826   AStepperBounds.Bottom := lHeight - SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1827 
1828   // Edit
1829   AEditBounds.Left := SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1830   AEditBounds.Top := SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1831   AEditBounds.Right := lWidth - SPINEDIT_DEFAULT_STEPPER_WIDTH;
1832   AEditBounds.Bottom := lHeight - SPINEDIT_EDIT_SPACING_FOR_SELECTION;
1833 
1834   {$IFDEF COCOA_SPIN_DEBUG}
1835   WriteLn('[TCocoaSpinEdit.CalculateSubcontrolPos] lWidth=', lWidth, ' lHeight=', lHeight,
1836     ' Stepper.Left=', AStepperBounds.Left, ' Stepper.Top=', AStepperBounds.Top,
1837     ' Stepper.Right=', AStepperBounds.Right, ' Stepper.Bottom=', AStepperBounds.Bottom,
1838     ' Edit.Left=', AEditBounds.Left, ' Edit.Top=', AEditBounds.Top,
1839     ' Edit.Right=', AEditBounds.Right, ' Edit.Bottom=', AEditBounds.Bottom
1840     );
1841   {$ENDIF}
1842 end;
1843 
1844 procedure TCocoaSpinEdit.StepperChanged(sender: NSObject);
1845 var
1846   lNSStr: NSString;
1847   lStr: string;
1848 begin
1849   lStr := Format('%.*f', [Spin.DecimalPlaces, Stepper.doubleValue()]);
1850   lNSStr := CocoaUtils.NSStringUtf8(lStr);
1851   Edit.setStringValue(lNSStr);
1852   lNSStr.release;
1853   // This implements OnChange for both user and code changes
1854   if callback <> nil then callback.SendOnTextChanged();
1855 end;
1856 
TCocoaSpinEdit.acceptsFirstRespondernull1857 function TCocoaSpinEdit.acceptsFirstResponder: Boolean;
1858 begin
1859   Result := True;
1860 end;
1861 
lclGetCallbacknull1862 function TCocoaSpinEdit.lclGetCallback: ICommonCallback;
1863 begin
1864   Result := callback;
1865 end;
1866 
1867 procedure TCocoaSpinEdit.lclClearCallback;
1868 begin
1869   callback := nil;
1870 end;
1871 
TCocoaSpinEdit.fittingSizenull1872 function TCocoaSpinEdit.fittingSize: NSSize;
1873 begin
1874   Result.width := -1;
1875   Edit.sizeToFit();
1876   Result.height := Edit.bounds.size.height + SPINEDIT_EDIT_SPACING_FOR_SELECTION * 2;
1877   {$IFDEF COCOA_SPIN_DEBUG}
1878   WriteLn('[TCocoaSpinEdit.fittingSize] width=', Result.width,
1879     ' height=', Result.height);
1880   {$ENDIF}
1881 end;
1882 
1883 {$ELSE}
1884 
1885 procedure TCocoaSpinEdit.dealloc;
1886 begin
1887   lclReleaseSubControls;
1888   inherited dealloc;
1889 end;
1890 
updateSteppernull1891 function TCocoaSpinEdit.updateStepper: boolean;
1892 var
1893   lValid: Boolean = False;
1894   lValue: String;
1895   lFloat: Double;
1896 begin
1897   lValue := CocoaUtils.NSStringToString(stringValue());
1898   lValid := SysUtils.TryStrToFloat(lValue, lFloat);
1899   if lValid then
1900   begin
1901     Stepper.setDoubleValue(lFloat);
1902     Result := true;
1903   end else
1904     Result := false;
1905 end;
1906 
1907 procedure TCocoaSpinEdit.UpdateControl(min, max, inc, avalue: double; ADecimalPlaces: Integer);
1908 var
1909   notifyChange : Boolean;
1910   v : double;
1911 begin
1912   Stepper.setIncrement(inc);
1913 
1914   v := avalue;
1915   if v < min then v := min
1916   else if v > max then v := max;
1917 
1918   notifyChange := (v <> Stepper.doubleValue) or (decimalPlaces <> ADecimalPlaces);
1919 
1920   // set min/max after checking for notify change
1921   // .doubleValue would be adjusted by setting min/max
1922   decimalPlaces := ADecimalPlaces;
1923   Stepper.setMinValue(min);
1924   Stepper.setMaxValue(max);
1925 
1926   if notifychange then
1927   begin
1928     Stepper.setDoubleValue(v);
1929     StepperChanged(Self);
1930   end;
1931 end;
1932 
1933 procedure TCocoaSpinEdit.lclCreateSubcontrols(const AParams: TCreateParams);
1934 var
1935   lParams: TCreateParams;
1936 begin
1937   {$IFDEF COCOA_SPIN_DEBUG}
1938   WriteLn('[TCocoaSpinEdit.CreateSubcontrols]');
1939   {$ENDIF}
1940 
1941   // Now creates the subcontrols
1942   lParams := AParams;
1943   //lParams.Style := AParams.Style or WS_VISIBLE;
1944 
1945   // Stepper
1946   lParams.X := AParams.X + AParams.Width - SPINEDIT_DEFAULT_STEPPER_WIDTH;
1947   lParams.Width := SPINEDIT_DEFAULT_STEPPER_WIDTH;
1948   Stepper := TCocoaSpinEditStepper.alloc.lclInitWithCreateParams(lParams);
1949   TCocoaSpinEditStepper(Stepper).callback := callback;
1950   Stepper.setValueWraps(False);
1951 
1952   // Change event for the stepper
1953   Stepper.setTarget(Self);
1954   Stepper.setAction(objcselector('StepperChanged:'));
1955 
1956   { The default way to do this in Cocoa is with NSNumberFormatter
1957     But it is a bit annoying, it just disallows losing focus from the control
1958     instead of the Windows like solution to just override with the last value
1959     If we ever want the Cocoa behavior, instead of implementing controlTextDidChange
1960     do this:
1961   var
1962   lNSStr: NSString;
1963   lStr: string;
1964   i: Integer;
1965 
1966   NumberFormatter := NSNumberFormatter.alloc.init;
1967   lStr := '##0';
1968   if ASpinEdit.DecimalPlaces > 0 then lStr := lStr + '.';
1969   for i := 0 to ASpinEdit.DecimalPlaces-1 do
1970     lStr := lStr + '0';
1971   lNSStr := CocoaUtils.NSStringUtf8(lStr);
1972   NumberFormatter.setFormat(lNSStr);
1973   lNSStr.release;
1974   NumberFormatter.setNumberStyle(NSNumberFormatterDecimalStyle);
1975   setFormatter(NumberFormatter);}
1976 end;
1977 
1978 procedure TCocoaSpinEdit.lclReleaseSubcontrols;
1979 begin
1980   if Assigned(Stepper) then
1981   begin
1982     Stepper.removeFromSuperview;
1983     Stepper.release;
1984     Stepper := nil;
1985   end;
1986   if Assigned(NumberFormatter) then
1987   begin
1988     NumberFormatter.release;
1989     NumberFormatter := nil;
1990   end;
1991 end;
1992 
1993 procedure TCocoaSpinEdit.PositionSubcontrols(const ALeft, ATop, AWidth, AHeight: Integer);
1994 begin
1995   lclSetFrame(Types.Bounds(ALeft, ATop, AWidth, AHeight));
1996 end;
1997 
1998 procedure TCocoaSpinEdit.StepperChanged(sender: NSObject);
1999 var
2000   lNSStr: NSString;
2001   lStr: string;
2002 begin
2003   // Stepper not might be assigend while creating or destroying handle
2004   if not Assigned(Stepper) then Exit;
2005 
2006   lStr := Format('%.*f', [DecimalPlaces, Stepper.doubleValue()]);
2007   lNSStr := CocoaUtils.NSStringUtf8(lStr);
2008   setStringValue(lNSStr);
2009   lNSStr.release;
2010   // This implements OnChange for both user and code changes
2011   if (callback <> nil) and (avoidChangeEvent=0) then
2012     callback.SendOnTextChanged();
2013 end;
2014 
2015 procedure TCocoaSpinEdit.textDidChange(notification: NSNotification);
2016 begin
2017   inc(avoidChangeEvent);
2018   try
2019     updateStepper;
2020     StepperChanged(nil); // and refresh self
2021     inherited textDidChange(notification);
2022   finally
2023     dec(avoidChangeEvent);
2024   end;
2025 end;
2026 
TCocoaSpinEdit.acceptsFirstRespondernull2027 function TCocoaSpinEdit.acceptsFirstResponder: LCLObjCBoolean;
2028 begin
2029   Result := NSViewCanFocus(Self);
2030 end;
2031 
lclGetCallbacknull2032 function TCocoaSpinEdit.lclGetCallback: ICommonCallback;
2033 begin
2034   Result := callback;
2035 end;
2036 
2037 procedure TCocoaSpinEdit.lclClearCallback;
2038 begin
2039   callback := nil;
2040 end;
2041 
2042 procedure TCocoaSpinEdit.resetCursorRects;
2043 begin
2044   // this will not work well because
2045   // cocoa replaced TextField and TextView cursors in
2046   // mouseEntered, mouseMoved and CursorUpdate
2047   if not callback.resetCursorRects then
2048     inherited resetCursorRects;
2049 end;
2050 
2051 procedure TCocoaSpinEdit.lclSetVisible(AVisible: Boolean);
2052 begin
2053   inherited lclSetVisible(AVisible);
2054   {$ifdef BOOLFIX}
2055   Stepper.setHidden_(Ord(not AVisible));
2056   {$else}
2057   Stepper.setHidden(not AVisible);
2058   {$endif}
2059 end;
2060 
2061 procedure TCocoaSpinEdit.lclSetFrame(const r: TRect);
2062 var
2063   ns, lStepperNS: NSRect;
2064   svHeight: CGFloat;
2065   lRect, lStepperRect: TRect;
2066 begin
2067   lRect := r;
2068   lStepperRect := r;
2069   lRect.Right := lRect.Right - SPINEDIT_DEFAULT_STEPPER_WIDTH;
2070   lStepperRect.Left := lRect.Right;
2071   svHeight := GetNSViewSuperViewHeight(Self);
2072   if Assigned(superview)  then
2073   begin
2074     LCLToNSRect(lRect, svHeight, ns);
2075     LCLToNSRect(lStepperRect, svHeight, lStepperNS);
2076   end
2077   else
2078   begin
2079     ns := RectToNSRect(lRect);
2080     lStepperNS := RectToNSRect(lStepperRect);
2081   end;
2082   {$IFDEF COCOA_DEBUG_SETBOUNDS}
2083   WriteLn(Format('LCLViewExtension.lclSetFrame: %s Bounds=%s height=%d ns_pos=%d %d ns_size=%d %d',
2084     [NSStringToString(Self.ClassName), dbgs(r), Round(svHeight),
2085      Round(ns.origin.x), Round(ns.origin.y), Round(ns.size.width), Round(ns.size.height)]));
2086   {$ENDIF}
2087   setFrame(ns);
2088   Stepper.setFrame(lStepperNS);
2089 end;
2090 
TCocoaSpinEdit.fittingSizenull2091 function TCocoaSpinEdit.fittingSize: NSSize;
2092 var
2093   fr : NSRect;
2094 begin
2095   Result.width := -1;
2096   fr:=frame;
2097   sizeToFit();
2098   Result.height := bounds.size.height;
2099   if not NSEqualRects(frame, fr) then setFrame(fr); // prevent changes of frame after sizeToFit();
2100   {$IFDEF COCOA_SPIN_DEBUG}
2101   WriteLn('[TCocoaSpinEdit.fittingSize] width=', Result.width:0:0, ' height=', Result.height:0:0);
2102   {$ENDIF}
2103 end;
2104 
TCocoaSpinEdit.acceptsFirstMousenull2105 function TCocoaSpinEdit.acceptsFirstMouse(event: NSEvent): LCLObjCBoolean;
2106 begin
2107   Result:=true;
2108 end;
2109 
2110 procedure TCocoaSpinEdit.mouseDown(event: NSEvent);
2111 begin
2112   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2113   begin
2114     inherited mouseDown(event);
2115     if Assigned(callback) then
2116       callback.MouseUpDownEvent(event, true);
2117   end;
2118 end;
2119 
2120 procedure TCocoaSpinEdit.mouseUp(event: NSEvent);
2121 begin
2122   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2123     inherited mouseUp(event);
2124 end;
2125 
2126 procedure TCocoaSpinEdit.rightMouseDown(event: NSEvent);
2127 begin
2128   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2129     inherited rightMouseDown(event);
2130 end;
2131 
2132 procedure TCocoaSpinEdit.rightMouseUp(event: NSEvent);
2133 begin
2134   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2135     inherited rightMouseUp(event);
2136 end;
2137 
2138 procedure TCocoaSpinEdit.rightMouseDragged(event: NSEvent);
2139 begin
2140   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2141     inherited rightMouseDragged(event);
2142 end;
2143 
2144 procedure TCocoaSpinEdit.otherMouseDown(event: NSEvent);
2145 begin
2146   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2147     inherited otherMouseDown(event);
2148 end;
2149 
2150 procedure TCocoaSpinEdit.otherMouseUp(event: NSEvent);
2151 begin
2152   if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
2153     inherited otherMouseUp(event);
2154 end;
2155 
2156 procedure TCocoaSpinEdit.otherMouseDragged(event: NSEvent);
2157 begin
2158   if not Assigned(callback) or not callback.MouseMove(event) then
2159     inherited otherMouseDragged(event);
2160 end;
2161 
2162 procedure TCocoaSpinEdit.mouseDragged(event: NSEvent);
2163 begin
2164   if not Assigned(callback) or not callback.MouseMove(event) then
2165     inherited mouseDragged(event);
2166 end;
2167 
2168 procedure TCocoaSpinEdit.mouseMoved(event: NSEvent);
2169 begin
2170   if not Assigned(callback) or not callback.MouseMove(event) then
2171     inherited mouseMoved(event);
2172 end;
2173 
2174 {$ENDIF}
2175 
2176 end.
2177 
2178