mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-07-24 18:16:19 +02:00

1. NSTableView.fittingSize returns the full height, not the visible height 2. the issue can be observed in IDE Options - Component Palette
1320 lines
42 KiB
ObjectPascal
1320 lines
42 KiB
ObjectPascal
{ $Id: $}
|
|
{ --------------------------------------------
|
|
cocoatables.pas - Cocoa internal classes
|
|
--------------------------------------------
|
|
|
|
This unit contains the private classhierarchy for the Cocoa implemetations
|
|
|
|
*****************************************************************************
|
|
This file is part of the Lazarus Component Library (LCL)
|
|
|
|
See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
for details about the license.
|
|
*****************************************************************************
|
|
}
|
|
unit CocoaTables;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$modeswitch objectivec1}
|
|
{$modeswitch objectivec2}
|
|
{$interfaces corba}
|
|
{$include cocoadefines.inc}
|
|
|
|
{.$DEFINE COCOA_DEBUG_LISTVIEW}
|
|
|
|
interface
|
|
|
|
uses
|
|
// rtl+ftl
|
|
Types, Classes, SysUtils,
|
|
// Libs
|
|
MacOSAll, CocoaAll, CocoaUtils, CocoaGDIObjects,
|
|
cocoa_extra, CocoaPrivate, CocoaConst, CocoaConfig,
|
|
// LCL
|
|
LCLType;
|
|
|
|
type
|
|
|
|
{ IListViewCallBack }
|
|
|
|
IListViewCallBack = interface(ICommonCallback)
|
|
function ItemsCount: Integer;
|
|
function GetItemTextAt(ARow, ACol: Integer; var Text: String): Boolean;
|
|
function GetItemCheckedAt(ARow, ACol: Integer; var CheckState: Integer): Boolean;
|
|
function GetItemImageAt(ARow, ACol: Integer; var imgIdx: Integer): Boolean;
|
|
function GetImageFromIndex(imgIdx: Integer): NSImage;
|
|
procedure SetItemTextAt(ARow, ACol: Integer; const Text: String);
|
|
procedure SetItemCheckedAt(ARow, ACol: Integer; CheckState: Integer);
|
|
procedure tableSelectionChange(ARow: Integer; Added, Removed: NSIndexSet);
|
|
function shouldTableSelectionChange(NewSel: Integer): Boolean;
|
|
procedure ColumnClicked(ACol: Integer);
|
|
procedure DrawRow(rowidx: Integer; ctx: TCocoaContext; const r: TRect; state: TOwnerDrawState);
|
|
procedure GetRowHeight(rowidx: Integer; var height: Integer);
|
|
end;
|
|
|
|
{ TCocoaStringList }
|
|
|
|
TCocoaStringList = class(TStringList)
|
|
protected
|
|
procedure Changed; override;
|
|
public
|
|
Owner: NSTableView;
|
|
// some notificaitons (i.e. selection change)
|
|
// should not be passed to LCL while clearing
|
|
isClearing: Boolean;
|
|
constructor Create(AOwner: NSTableView);
|
|
procedure Clear; override;
|
|
end;
|
|
|
|
{ TCocoaTableListView }
|
|
|
|
TCocoaTableListView = objcclass(NSTableView, NSTableViewDelegateProtocol, NSTableViewDataSourceProtocol)
|
|
public
|
|
callback: IListViewCallback;
|
|
|
|
readOnly: Boolean;
|
|
|
|
beforeSel : NSIndexSet;
|
|
|
|
isImagesInCell: Boolean;
|
|
isFirstColumnCheckboxes: Boolean;
|
|
isOwnerDraw : Boolean;
|
|
isDynamicRowHeight: Boolean;
|
|
CustomRowHeight: Integer;
|
|
ScrollWidth: Integer;
|
|
|
|
smallimages : NSMutableDictionary;
|
|
|
|
function acceptsFirstResponder: LCLObjCBoolean; override;
|
|
function lclGetCallback: ICommonCallback; override;
|
|
procedure lclClearCallback; override;
|
|
|
|
// Own methods, mostly convenience methods
|
|
function getIndexOfColumn(ACol: NSTableColumn): Integer; message 'getIndexOfColumn:';
|
|
procedure reloadDataForRow_column(ARow, ACol: NSInteger); message 'reloadDataForRow:column:';
|
|
|
|
function initWithFrame(frameRect: NSRect): id; override;
|
|
procedure dealloc; override;
|
|
function fittingSize: NSSize; override;
|
|
|
|
procedure drawRow_clipRect(row: NSInteger; clipRect: NSRect); override;
|
|
procedure drawRect(dirtyRect: NSRect); override;
|
|
|
|
// mouse
|
|
procedure mouseDown(event: NSEvent); override;
|
|
procedure mouseUp(event: NSEvent); override;
|
|
procedure rightMouseDown(event: NSEvent); override;
|
|
procedure rightMouseUp(event: NSEvent); override;
|
|
procedure otherMouseDown(event: NSEvent); override;
|
|
procedure otherMouseUp(event: NSEvent); override;
|
|
procedure mouseDragged(event: NSEvent); override;
|
|
procedure mouseEntered(event: NSEvent); override;
|
|
procedure mouseExited(event: NSEvent); override;
|
|
procedure mouseMoved(event: NSEvent); override;
|
|
// key
|
|
procedure lclExpectedKeys(var wantTabs, wantKeys, wantReturn, wantAllKeys: Boolean); override;
|
|
procedure lclSetFirstColumCheckboxes(acheckboxes: Boolean); message 'lclSetFirstColumCheckboxes:';
|
|
procedure lclSetImagesInCell(aimagesInCell: Boolean); message 'lclSetImagesInCell:';
|
|
|
|
procedure lclRegisterSmallImage(idx: Integer; img: NSImage); message 'lclRegisterSmallImage::';
|
|
function lclGetSmallImage(idx: INteger): NSImage; message 'lclGetSmallImage:';
|
|
function lclGetItemImageAt(ARow, ACol: Integer): NSImage; message 'lclGetItemImageAt::';
|
|
|
|
// BoundsRect - is the rectangle of the cell, speciifed of aRow, acol.
|
|
// so the function lclGetLabelRect, lclGetIconRect should only adjust "BoundsRect"
|
|
// and return the adjusted rectangle
|
|
function lclGetLabelRect(ARow, ACol: Integer; const BoundsRect: TRect): TRect; message 'lclGetLabelRect:::';
|
|
function lclGetIconRect(ARow, ACol: Integer; const BoundsRect: TRect): TRect; message 'lclGetIconRect:::';
|
|
|
|
procedure lclInsDelRow(Arow: Integer; inserted: Boolean); message 'lclInsDelRow::';
|
|
procedure lclSetColumnAlign(acolumn: NSTableColumn; aalignment: NSTextAlignment); message 'lclSetColumn:Align:';
|
|
|
|
// NSTableViewDataSourceProtocol
|
|
function numberOfRowsInTableView(tableView: NSTableView): NSInteger; message 'numberOfRowsInTableView:';
|
|
//procedure tableView_sortDescriptorsDidChange(tableView: NSTableView; oldDescriptors: NSArray); message 'tableView:sortDescriptorsDidChange:';
|
|
//function tableView_writeRowsWithIndexes_toPasteboard(tableView: NSTableView; rowIndexes: NSIndexSet; pboard: NSPasteboard): Boolean; message 'tableView:writeRowsWithIndexes:toPasteboard:';
|
|
//function tableView_validateDrop_proposedRow_proposedDropOperation(tableView: NSTableView; info: NSDraggingInfoProtocol; row: NSInteger; dropOperation: NSTableViewDropOperation): NSDragOperation; message 'tableView:validateDrop:proposedRow:proposedDropOperation:';
|
|
//function tableView_acceptDrop_row_dropOperation(tableView: NSTableView; info: NSDraggingInfoProtocol; row: NSInteger; dropOperation: NSTableViewDropOperation): Boolean; message 'tableView:acceptDrop:row:dropOperation:';
|
|
//function tableView_namesOfPromisedFilesDroppedAtDestination_forDraggedRowsWithIndexes(tableView: NSTableView; dropDestination: NSURL; indexSet: NSIndexSet): NSArray; message 'tableView:namesOfPromisedFilesDroppedAtDestination:forDraggedRowsWithIndexes:';
|
|
|
|
// NSTableViewDelegateProtocol
|
|
//procedure tableView_willDisplayCell_forTableColumn_row(tableView: NSTableView; cell: id; tableColumn: NSTableColumn; row: NSInteger); message 'tableView:willDisplayCell:forTableColumn:row:';
|
|
function tableView_shouldEditTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): Boolean; message 'tableView:shouldEditTableColumn:row:';
|
|
function selectionShouldChangeInTableView(tableView: NSTableView): Boolean; message 'selectionShouldChangeInTableView:';
|
|
function tableView_shouldSelectRow(tableView: NSTableView; row: NSInteger): Boolean; message 'tableView:shouldSelectRow:';
|
|
{function tableView_selectionIndexesForProposedSelection(tableView: NSTableView; proposedSelectionIndexes: NSIndexSet): NSIndexSet; message 'tableView:selectionIndexesForProposedSelection:';
|
|
function tableView_shouldSelectTableColumn(tableView: NSTableView; tableColumn: NSTableColumn): Boolean; message 'tableView:shouldSelectTableColumn:';
|
|
procedure tableView_mouseDownInHeaderOfTableColumn(tableView: NSTableView; tableColumn: NSTableColumn); message 'tableView:mouseDownInHeaderOfTableColumn:';}
|
|
procedure tableView_didClickTableColumn(tableView: NSTableView; tableColumn: NSTableColumn); message 'tableView:didClickTableColumn:';
|
|
{procedure tableView_didDragTableColumn(tableView: NSTableView; tableColumn: NSTableColumn); message 'tableView:didDragTableColumn:';
|
|
function tableView_toolTipForCell_rect_tableColumn_row_mouseLocation(tableView: NSTableView; cell: NSCell; rect: NSRectPointer; tableColumn: NSTableColumn; row: NSInteger; mouseLocation: NSPoint): NSString; message 'tableView:toolTipForCell:rect:tableColumn:row:mouseLocation:';}
|
|
function tableView_heightOfRow(tableView: NSTableView; row: NSInteger): CGFloat; message 'tableView:heightOfRow:';
|
|
{function tableView_typeSelectStringForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): NSString; message 'tableView:typeSelectStringForTableColumn:row:';
|
|
function tableView_nextTypeSelectMatchFromRow_toRow_forString(tableView: NSTableView; startRow: NSInteger; endRow: NSInteger; searchString: NSString): NSInteger; message 'tableView:nextTypeSelectMatchFromRow:toRow:forString:';
|
|
function tableView_shouldTypeSelectForEvent_withCurrentSearchString(tableView: NSTableView; event: NSEvent; searchString: NSString): Boolean; message 'tableView:shouldTypeSelectForEvent:withCurrentSearchString:';
|
|
function tableView_shouldShowCellExpansionForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): Boolean; message 'tableView:shouldShowCellExpansionForTableColumn:row:';
|
|
function tableView_shouldTrackCell_forTableColumn_row(tableView: NSTableView; cell: NSCell; tableColumn: NSTableColumn; row: NSInteger): Boolean; message 'tableView:shouldTrackCell:forTableColumn:row:';
|
|
}
|
|
{
|
|
function tableView_isGroupRow(tableView: NSTableView; row: NSInteger): Boolean; message 'tableView:isGroupRow:';
|
|
function tableView_sizeToFitWidthOfColumn(tableView: NSTableView; column: NSInteger): CGFloat; message 'tableView:sizeToFitWidthOfColumn:';
|
|
function tableView_shouldReorderColumn_toColumn(tableView: NSTableView; columnIndex: NSInteger; newColumnIndex: NSInteger): Boolean; message 'tableView:shouldReorderColumn:toColumn:';}
|
|
procedure tableViewSelectionDidChange(notification: NSNotification); message 'tableViewSelectionDidChange:';
|
|
{procedure tableViewColumnDidMove(notification: NSNotification); message 'tableViewColumnDidMove:';
|
|
procedure tableViewColumnDidResize(notification: NSNotification); message 'tableViewColumnDidResize:';
|
|
procedure tableViewSelectionIsChanging(notification: NSNotification); message 'tableViewSelectionIsChanging:';}
|
|
end;
|
|
|
|
|
|
{ NSImageAndTextCell }
|
|
|
|
NSImageAndTextCell = objcclass(NSTextFieldCell)
|
|
drawImage : NSImage;
|
|
procedure drawWithFrame_inView(cellFrame: NSRect; controlView_: NSView); override;
|
|
end;
|
|
|
|
{ NSTableButtonCell }
|
|
|
|
// NSButtonCell would handle a click on a checkbox caption as a click on
|
|
// the checkbox itself. (You can experience that by clicking on TCheckBox)
|
|
// This is the expected behavior for a standalone checkbox control.
|
|
// However, for a checkbox is in a table, it's not desired to have checkbox
|
|
// triggered, by clicking on its caption.
|
|
// (You can try it in macOS "System Preferences"->"Keyboard"->"Shortcuts")
|
|
// Since a checkbox and the text are put in the same NSTableView column
|
|
// using NSButtonCell, there's an override for hitTesting function.
|
|
// IF a user hits caption, then hitTest returns "none" suppressing the hit.
|
|
// Thus a checkbox in a table can only be checked if clicked directly into
|
|
// the checkbox.
|
|
//
|
|
// todo: add support for images. (For TListView with checkboxes and images)
|
|
// It's rarely used (if ever), yet it's possible
|
|
|
|
NSTableButtonCell = objcclass (NSButtonCell)
|
|
_type: NSButtonType;
|
|
function hitTestForEvent_inRect_ofView(event: NSEvent; cellFrame: NSRect; controlView_: NSView): NSUInteger; override;
|
|
procedure setButtonType(aType: NSButtonType); override;
|
|
end;
|
|
|
|
{ TCellCocoaTableListView }
|
|
|
|
TCellCocoaTableListView = objcclass(TCocoaTableListView, NSTableViewDelegateProtocol, NSTableViewDataSourceProtocol)
|
|
public
|
|
function tableView_objectValueForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): id; message 'tableView:objectValueForTableColumn:row:';
|
|
procedure tableView_setObjectValue_forTableColumn_row(tableView: NSTableView; object_: id; tableColumn: NSTableColumn; row: NSInteger); message 'tableView:setObjectValue:forTableColumn:row:';
|
|
function tableView_dataCellForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): NSCell; message 'tableView:dataCellForTableColumn:row:';
|
|
procedure lclInsDelRow(Arow: Integer; inserted: Boolean); override;
|
|
procedure lclSetColumnAlign(acolumn: NSTableColumn; aalignment: NSTextAlignment); override;
|
|
end;
|
|
|
|
TCellCocoaTableListView1013 = objcclass(TCellCocoaTableListView, NSTableViewDelegateProtocol, NSTableViewDataSourceProtocol)
|
|
// overriding the highlight color for dark theme
|
|
procedure highlightSelectionInClipRect(clipRect: NSRect); override;
|
|
end;
|
|
|
|
// View based NSTableView
|
|
|
|
TCocoaTableListItem = objcclass(NSView)
|
|
private
|
|
column: NSTableColumn;
|
|
checkedSubView: NSButton;
|
|
imageSubView: NSImageView;
|
|
textSubView: NSTextField;
|
|
idStr: NSString;
|
|
public
|
|
function initWithFrame(frameRect: NSRect): id; override;
|
|
procedure dealloc; override;
|
|
procedure setColumn(AColumn: NSTableColumn); message 'setColumn:';
|
|
procedure setImage(AImage: NSImage); message 'setImage:';
|
|
procedure setCheckState(AState: NSInteger); message 'setCheckState:';
|
|
procedure setStringValue(AString: NSString); message 'setStringValue:';
|
|
procedure setEditable(flag: Boolean); message 'setEditable:';
|
|
procedure setFont(AFont: NSFont); message 'setFont:';
|
|
procedure setTarget(ATarget: id); message 'setTarget:';
|
|
procedure setCheckAction(aSelector: SEL); message 'setCheckAction:';
|
|
procedure setTextAction(aSelector: SEL); message 'setTextAction:';
|
|
procedure resizeSubviewsWithOldSize(oldSize: NSSize); override;
|
|
procedure setIdentifier(identifier_: NSString); message 'setIdentifier:'; override;
|
|
function identifier: NSString; message 'identifier'; override;
|
|
function textFrame: NSRect; message 'textFrame';
|
|
procedure lclSetEnabled(AEnabled: Boolean); override;
|
|
end;
|
|
|
|
|
|
{ TViewCocoaTableListView }
|
|
|
|
TViewCocoaTableListView = objcclass(TCocoaTableListView, NSTableViewDelegateProtocol, NSTableViewDataSourceProtocol)
|
|
// todo: this should be "override" for 10.7 and later
|
|
// on the other hand, it doesn't call "inherited" so there's no need
|
|
// to do an actual override
|
|
function tableView_viewForTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): NSView; message 'tableView:viewForTableColumn:row:';
|
|
|
|
procedure textFieldAction(sender: NSTextField); message 'textFieldAction:';
|
|
procedure checkboxAction(sender: NSButton); message 'checkboxAction:';
|
|
|
|
function lclGetLabelRect(ARow, ACol: Integer; const BoundsRect: TRect): TRect; override;
|
|
procedure lclInsDelRow(Arow: Integer; inserted: Boolean); override;
|
|
end;
|
|
|
|
function AllocCocoaTableListView: TCocoaTableListView;
|
|
|
|
function LCLCoordToRow(tbl: NSTableView; X,Y: Integer): Integer;
|
|
function LCLGetItemRect(tbl: NSTableView; row, col: Integer; var r: TRect): Boolean;
|
|
function LCLGetTopRow(tbl: NSTableView): Integer;
|
|
|
|
const
|
|
DefaultRowHeight = 16; // per "rowHeight" property docs
|
|
|
|
implementation
|
|
|
|
function LCLCoordToRow(tbl: NSTableView; X,Y: Integer): Integer;
|
|
var
|
|
pt : NSPoint;
|
|
sc : NSScrollView;
|
|
vr : NSRect;
|
|
begin
|
|
if not Assigned(tbl) then
|
|
begin
|
|
Result := -1;
|
|
Exit;
|
|
end;
|
|
|
|
sc := tbl.enclosingScrollView;
|
|
if Assigned(sc) then
|
|
vr := sc.documentVisibleRect
|
|
else
|
|
vr := tbl.visibleRect;
|
|
pt.x := X;
|
|
if tbl.isFlipped
|
|
then pt.y := Y + vr.origin.y
|
|
else pt.y := tbl.frame.size.height - Y - vr.origin.y;
|
|
|
|
Result := tbl.rowAtPoint(pt);
|
|
end;
|
|
|
|
function LCLGetItemRect(tbl: NSTableView; row, col: Integer; var r: TRect): Boolean;
|
|
var
|
|
nsr : NSRect;
|
|
begin
|
|
if not Assigned(tbl) then begin
|
|
Result := false;
|
|
r := Bounds(0,0,0,0);
|
|
Exit;
|
|
end;
|
|
nsr:=tbl.frameOfCellAtColumn_row(col,row);
|
|
r:=NSRectToRect(nsr);
|
|
Result := True;
|
|
end;
|
|
|
|
function LCLGetTopRow(tbl: NSTableView): Integer;
|
|
var
|
|
visRange: NSRange;
|
|
begin
|
|
if not Assigned(tbl) then
|
|
Result := -1
|
|
else
|
|
begin
|
|
visRange := tbl.rowsInRect(tbl.visibleRect());
|
|
Result := visRange.location;
|
|
end;
|
|
end;
|
|
|
|
// not yet!
|
|
{.$DEFINE DYNAMIC_NSTABLEVIEW_BASE}
|
|
|
|
function AllocCocoaTableListView: TCocoaTableListView; // init will happen outside
|
|
begin
|
|
{$IFDEF DYNAMIC_NSTABLEVIEW_BASE}
|
|
if NSAppKitVersionNumber >= NSAppKitVersionNumber10_7 then
|
|
Result := TViewCocoaTableListView.alloc
|
|
else
|
|
Result := TCellCocoaTableListView.alloc;
|
|
{$ELSE}
|
|
|
|
// this is required for "dark theme" support on 10.13
|
|
if (NSAppkitVersionNumber >= NSAppKitVersionNumber10_13)
|
|
and (NSAppkitVersionNumber < NSAppKitVersionNumber10_14)
|
|
then
|
|
Result := TCellCocoaTableListView1013.alloc
|
|
else
|
|
Result := TCellCocoaTableListView.alloc;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{ NSTableButtonCell }
|
|
|
|
function NSTableButtonCell.hitTestForEvent_inRect_ofView(event: NSEvent;
|
|
cellFrame: NSRect; controlView_: NSView): NSUInteger;
|
|
var
|
|
r : NSRect;
|
|
pt : NSPoint;
|
|
begin
|
|
Result := inherited hitTestForEvent_inRect_ofView(event, cellFrame, controlView_);
|
|
if _type = NSSwitchButton then
|
|
begin
|
|
pt := event.locationInWindow;
|
|
if Assigned(controlView_) then
|
|
pt := controlView_.convertPoint_fromView(pt, nil);
|
|
r := titleRectForBounds(cellFrame);
|
|
|
|
// todo: pt.y seems to be off by some amount
|
|
if ((pt.x >= r.origin.x) and (pt.x<=r.origin.x + r.size.width)) then
|
|
Result := NSCellHitNone;
|
|
end;
|
|
end;
|
|
|
|
procedure NSTableButtonCell.setButtonType(aType: NSButtonType);
|
|
begin
|
|
_type := aType;
|
|
inherited setButtonType(aType);
|
|
end;
|
|
|
|
{ NSImageAndTextCell }
|
|
|
|
procedure NSImageAndTextCell.drawWithFrame_inView(cellFrame: NSRect;
|
|
controlView_: NSView);
|
|
var
|
|
r : NSRect;
|
|
srcsz : NSSize;
|
|
begin
|
|
r:=cellFrame;
|
|
cellFrame.origin.x:=cellFrame.origin.x+cellFrame.size.height;
|
|
cellFrame.size.width:=cellFrame.size.width-cellFrame.size.height;
|
|
inherited drawWithFrame_inView(cellFrame, controlView_);
|
|
if Assigned(drawImage) then
|
|
begin
|
|
r.size.width:=r.size.height;
|
|
srcsz := drawImage.size;
|
|
drawImage.drawInRect_fromRect_operation_fraction_respectFlipped_hints(
|
|
r, NSMakeRect(0,0, srcsz.width, srcsz.height), NSCompositeSourceOver, 1, true, nil
|
|
);
|
|
end;
|
|
end;
|
|
|
|
{ TCocoaTableListView }
|
|
|
|
procedure TCocoaTableListView.lclExpectedKeys(var wantTabs, wantKeys, wantReturn,
|
|
wantAllKeys: Boolean);
|
|
begin
|
|
wantTabs := false;
|
|
wantKeys := true;
|
|
wantReturn := false; // todo: this should be "true" for editting purposes.
|
|
// or false, to let LCL handle editting
|
|
wantAllKeys := false;
|
|
end;
|
|
|
|
procedure TCocoaTableListView.lclSetFirstColumCheckboxes(acheckboxes: Boolean);
|
|
begin
|
|
if isFirstColumnCheckboxes = acheckboxes then Exit;
|
|
isFirstColumnCheckboxes := acheckboxes;
|
|
reloadData();
|
|
end;
|
|
|
|
procedure TCocoaTableListView.lclSetImagesInCell(aimagesInCell: Boolean);
|
|
begin
|
|
if isImagesInCell = aimagesInCell then Exit;
|
|
isImagesInCell := aimagesInCell;
|
|
reloadData();
|
|
end;
|
|
|
|
procedure TCocoaTableListView.lclRegisterSmallImage(idx: Integer; img: NSImage);
|
|
begin
|
|
if not Assigned(smallimages) then
|
|
smallimages := (NSMutableDictionary.alloc).init;
|
|
|
|
if Assigned(img) then
|
|
smallimages.setObject_forKey(img, NSNumber.numberWithInt(idx) )
|
|
else
|
|
smallimages.removeObjectForKey( NSNumber.numberWithInt(idx) );
|
|
end;
|
|
|
|
function TCocoaTableListView.lclGetSmallImage(idx: INteger): NSImage;
|
|
begin
|
|
if not Assigned(smallimages) then Result := nil;
|
|
Result := NSImage(smallimages.objectForKey( NSNumber.numberWithInt(idx) ) );
|
|
end;
|
|
|
|
function TCocoaTableListView.lclGetItemImageAt(ARow, ACol: Integer): NSImage;
|
|
var
|
|
idx : Integer;
|
|
img : NSimage;
|
|
begin
|
|
if not Assigned(callback) then
|
|
begin
|
|
Result := nil;
|
|
Exit;
|
|
end;
|
|
|
|
idx := -1;
|
|
callback.GetItemImageAt(ARow, ACol, idx);
|
|
if idx>=0 then
|
|
begin
|
|
img := lclGetSmallImage(idx);
|
|
if not Assigned(img) then begin
|
|
img := callback.GetImageFromIndex(idx);
|
|
if Assigned(img) then lclRegisterSmallImage(idx, img);
|
|
end;
|
|
end else
|
|
img := nil;
|
|
Result := img;
|
|
end;
|
|
|
|
function TCocoaTableListView.lclGetLabelRect(ARow, ACol: Integer;
|
|
const BoundsRect: TRect): TRect;
|
|
begin
|
|
Result := BoundsRect;
|
|
end;
|
|
|
|
function TCocoaTableListView.lclGetIconRect(ARow, ACol: Integer;
|
|
const BoundsRect: TRect): TRect;
|
|
begin
|
|
Result := BoundsRect;
|
|
end;
|
|
|
|
procedure TCocoaTableListView.lclInsDelRow(Arow: Integer; inserted: Boolean);
|
|
begin
|
|
// a row has been inserted or removed
|
|
// the following rows needs to be invalidated
|
|
// as well as number of total items in the table should be marked as modified
|
|
end;
|
|
|
|
procedure TCocoaTableListView.lclSetColumnAlign(acolumn: NSTableColumn;
|
|
aalignment: NSTextAlignment);
|
|
begin
|
|
|
|
end;
|
|
|
|
function TCocoaTableListView.acceptsFirstResponder: LCLObjCBoolean;
|
|
begin
|
|
Result := NSViewCanFocus(Self);
|
|
end;
|
|
|
|
function TCocoaTableListView.lclGetCallback: ICommonCallback;
|
|
begin
|
|
Result := callback;
|
|
end;
|
|
|
|
procedure TCocoaTableListView.lclClearCallback;
|
|
begin
|
|
callback := nil;
|
|
end;
|
|
|
|
procedure TCocoaTableListView.dealloc;
|
|
begin
|
|
//if Assigned(Items) then FreeAndNil(Items);
|
|
if Assigned(beforeSel) then beforeSel.release;
|
|
if Assigned(smallimages) then smallimages.release; // all contents is released automatically
|
|
inherited dealloc;
|
|
end;
|
|
|
|
function TCocoaTableListView.fittingSize: NSSize;
|
|
begin
|
|
Result:= NSZeroSize;
|
|
end;
|
|
|
|
procedure TCocoaTableListView.drawRow_clipRect(row: NSInteger; clipRect: NSRect
|
|
);
|
|
var
|
|
ctx: TCocoaContext;
|
|
ItemState: TOwnerDrawState;
|
|
begin
|
|
inherited;
|
|
if not Assigned(callback) then Exit;
|
|
ctx := TCocoaContext.Create(NSGraphicsContext.currentContext);
|
|
try
|
|
ItemState := [];
|
|
if isRowSelected(row) then Include(ItemState, odSelected);
|
|
if lclIsEnabled then Include(ItemState, odDisabled);
|
|
if Assigned(window) and (window.firstResponder = self) and (odSelected in ItemState) then
|
|
Include(ItemState, odFocused);
|
|
|
|
callback.DrawRow(row, ctx, NSRectToRect(rectOfRow(row)), ItemState);
|
|
finally
|
|
ctx.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TCocoaTableListView.drawRect(dirtyRect: NSRect);
|
|
begin
|
|
inherited drawRect(dirtyRect);
|
|
if CheckMainThread and Assigned(callback) then
|
|
callback.Draw(NSGraphicsContext.currentContext, bounds, dirtyRect);
|
|
end;
|
|
|
|
function TCocoaTableListView.getIndexOfColumn(ACol: NSTableColumn): Integer;
|
|
var
|
|
idx : NSUInteger;
|
|
begin
|
|
idx := tableColumns.indexOfObject(ACol);
|
|
if idx = NSNotFound then
|
|
Result := -1
|
|
else
|
|
Result := Integer(idx);
|
|
end;
|
|
|
|
procedure TCocoaTableListView.reloadDataForRow_column(ARow, ACol: NSInteger);
|
|
var
|
|
lRowSet, lColSet: NSIndexSet;
|
|
begin
|
|
lRowSet := NSIndexSet.indexSetWithIndex(ARow);
|
|
lColSet := NSIndexSet.indexSetWithIndex(ACol);
|
|
reloadDataForRowIndexes_columnIndexes(lRowSet, lColSet);
|
|
end;
|
|
|
|
function TCocoaTableListView.initWithFrame(frameRect: NSRect): id;
|
|
begin
|
|
Result:=inherited initWithFrame(frameRect);
|
|
if NSAppkitVersionNumber >= NSAppKitVersionNumber11_0 then
|
|
setStyle( CocoaConfig.CocoaTableViewStyle );
|
|
end;
|
|
|
|
procedure TCocoaTableListView.mouseDown(event: NSEvent);
|
|
begin
|
|
if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
|
|
begin
|
|
inherited mouseDown(event);
|
|
if Assigned(callback) then
|
|
callback.MouseUpDownEvent(event, true);
|
|
end;
|
|
end;
|
|
|
|
procedure TCocoaTableListView.mouseUp(event: NSEvent);
|
|
begin
|
|
if Assigned(callback) and not callback.MouseUpDownEvent(event) then
|
|
inherited mouseUp(event);
|
|
end;
|
|
|
|
procedure TCocoaTableListView.rightMouseDown(event: NSEvent);
|
|
begin
|
|
if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
|
|
inherited rightMouseDown(event);
|
|
end;
|
|
|
|
procedure TCocoaTableListView.rightMouseUp(event: NSEvent);
|
|
begin
|
|
if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
|
|
inherited rightMouseUp(event);
|
|
end;
|
|
|
|
procedure TCocoaTableListView.otherMouseDown(event: NSEvent);
|
|
begin
|
|
if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
|
|
inherited otherMouseDown(event);
|
|
end;
|
|
|
|
procedure TCocoaTableListView.otherMouseUp(event: NSEvent);
|
|
begin
|
|
if not Assigned(callback) or not callback.MouseUpDownEvent(event) then
|
|
inherited otherMouseUp(event);
|
|
end;
|
|
|
|
procedure TCocoaTableListView.mouseDragged(event: NSEvent);
|
|
begin
|
|
if not Assigned(callback) or not callback.MouseMove(event) then
|
|
inherited mouseDragged(event);
|
|
end;
|
|
|
|
procedure TCocoaTableListView.mouseEntered(event: NSEvent);
|
|
begin
|
|
inherited mouseEntered(event);
|
|
end;
|
|
|
|
procedure TCocoaTableListView.mouseExited(event: NSEvent);
|
|
begin
|
|
inherited mouseExited(event);
|
|
end;
|
|
|
|
procedure TCocoaTableListView.mouseMoved(event: NSEvent);
|
|
begin
|
|
inherited mouseMoved(event);
|
|
end;
|
|
|
|
function TCocoaTableListView.numberOfRowsInTableView(tableView: NSTableView
|
|
): NSInteger;
|
|
begin
|
|
if Assigned(callback) then
|
|
Result := callback.ItemsCount
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TCocoaTableListView.tableView_shouldEditTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): Boolean;
|
|
begin
|
|
Result := not readOnly;
|
|
end;
|
|
|
|
function TCocoaTableListView.selectionShouldChangeInTableView(
|
|
tableView: NSTableView): Boolean;
|
|
begin
|
|
if Assigned(beforeSel) then beforeSel.release;
|
|
beforeSel := (NSIndexSet.alloc).initWithIndexSet(selectedRowIndexes);
|
|
Result := true;
|
|
end;
|
|
|
|
function TCocoaTableListView.tableView_shouldSelectRow(tableView: NSTableView;
|
|
row: NSInteger): Boolean;
|
|
begin
|
|
Result:= callback.shouldTableSelectionChange( row );
|
|
end;
|
|
|
|
procedure TCocoaTableListView.tableView_didClickTableColumn(
|
|
tableView: NSTableView; tableColumn: NSTableColumn);
|
|
begin
|
|
if Assigned(callback) then
|
|
callback.ColumnClicked(getIndexOfColumn(tableColumn));
|
|
end;
|
|
|
|
function TCocoaTableListView.tableView_heightOfRow(tableView: NSTableView;
|
|
row: NSInteger): CGFloat;
|
|
var
|
|
h : integer;
|
|
begin
|
|
h := CustomRowHeight;
|
|
if h = 0 then h := DefaultRowHeight;
|
|
|
|
if isDynamicRowHeight and Assigned(callback) then
|
|
begin
|
|
callback.GetRowHeight(Integer(row), h);
|
|
if h<=0 then h:=1; // must be positive (non-zero)
|
|
end;
|
|
Result := h;
|
|
end;
|
|
|
|
type
|
|
TCompareData = record
|
|
rmved : NSMutableIndexSet;
|
|
added : NSMutableIndexSet;
|
|
src : NSIndexSet;
|
|
dst : NSIndexSet;
|
|
end;
|
|
|
|
procedure DumpIndexes(src: NSIndexSet; dst: NSMutableIndexSet; rng: NSRange);
|
|
var
|
|
buf : array [0..512 * 4-1] of NSUInteger;
|
|
i : NSUInteger;
|
|
j : Integer;
|
|
trg : NSUInteger;
|
|
r : NSRange;
|
|
gth : NSRange;
|
|
k : Integer;
|
|
cmp : NSUInteger;
|
|
begin
|
|
if src.containsIndexesInRange(rng) then begin
|
|
// the range is packed
|
|
dst.addIndexesInRange(rng);
|
|
Exit;
|
|
end;
|
|
|
|
i := rng.location;
|
|
trg := rng.location+rng.length;
|
|
gth.location := NSNotFound;
|
|
gth.length := 0;
|
|
|
|
while i<=trg do begin
|
|
r.location := i;
|
|
r.length := trg - r.location;
|
|
j := Integer(src.getIndexes_maxCount_inIndexRange(@buf[0], length(buf), @r));
|
|
if j = 0 then i:=trg+1
|
|
else
|
|
begin
|
|
i := buf[j-1]+1;
|
|
if (gth.location = NSNotFound) then gth.location := buf[0];
|
|
|
|
cmp := gth.length + gth.location;
|
|
|
|
for k := 0 to j - 1 do begin
|
|
if cmp = buf[k] then
|
|
inc(cmp)
|
|
else begin
|
|
gth.length := cmp - gth.location + 1;
|
|
dst.addIndexesInRange(gth);
|
|
|
|
if k < j-1 then
|
|
gth.location := buf[k]
|
|
else
|
|
gth.location := NSNotFound;
|
|
gth.length := 0;
|
|
|
|
cmp := gth.length + gth.location;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if gth.location <> NSNotFound then begin
|
|
gth.length := gth.length + 1;
|
|
dst.addIndexesInRange(gth);
|
|
end;
|
|
end;
|
|
|
|
procedure CompareIdxRange(const data: TCompareData; startIdx, endIdx: NSUInteger);
|
|
var
|
|
isrc, idst: Boolean;
|
|
rng : NSRange;
|
|
m : Integer;
|
|
begin
|
|
rng.location := startIdx;
|
|
rng.length := endIdx - startIdx + 1;
|
|
isrc := data.src.intersectsIndexesInRange(rng);
|
|
idst := data.dst.intersectsIndexesInRange(rng);
|
|
if (not isrc) and (not idst) then
|
|
// there are no indexes in either selection
|
|
// no need to dig deeper
|
|
Exit;
|
|
|
|
|
|
if (not isrc) then begin
|
|
// there are only added indexes in this range
|
|
DumpIndexes(data.dst, data.added, rng);
|
|
Exit;
|
|
end else if not (idst) then begin
|
|
// there are only removed indexes in this range
|
|
DumpIndexes(data.src, data.rmved, rng);
|
|
Exit;
|
|
end;
|
|
|
|
isrc := data.src.containsIndexesInRange(rng);
|
|
idst := data.dst.containsIndexesInRange(rng);
|
|
if isrc and idst then begin
|
|
// indexes match in both sets. No need for further investigation
|
|
Exit;
|
|
end;
|
|
|
|
if startIdx < endIdx then
|
|
begin
|
|
m := (endidx - startidx) div 2 + startIdx;
|
|
CompareIdxRange(data, startIdx, m);
|
|
if m <= endIdx then
|
|
CompareIdxRange(data, m+1, endIdx);
|
|
end;
|
|
end;
|
|
|
|
procedure CompareIndexSets(src, dst: NSIndexSet; out removed, added: NSIndexSet);
|
|
var
|
|
rm, ad : NSMutableIndexSet;
|
|
srci : NSUInteger;
|
|
dsti : NSUinteger;
|
|
|
|
srcl : NSUInteger;
|
|
dstl : NSUInteger;
|
|
data : TCompareData;
|
|
begin
|
|
rm := NSMutableIndexSet.alloc.init.autorelease;
|
|
ad := NSMutableIndexSet.alloc.init.autorelease;
|
|
removed := rm;
|
|
added := ad;
|
|
|
|
srci := src.firstIndex;
|
|
dsti := dst.firstIndex;
|
|
if (srci = NSNotFound) and (dsti <> NSNotFound) then begin
|
|
// has not been previosly selected;
|
|
ad.addIndexes(dst);
|
|
Exit;
|
|
end else if (dsti = NSNotFound) and (srci <> NSNotFound) then begin
|
|
// cleared the selection
|
|
rm.addIndexes(src);
|
|
Exit;
|
|
end;
|
|
|
|
if srci < dsti then begin
|
|
DumpIndexes(src, rm, NSMakeRange(srci, dsti-srci));
|
|
srci := dsti;
|
|
end else if dsti < srci then begin
|
|
DumpIndexes(dst, ad, NSMakeRange(dsti, srci-dsti));
|
|
dsti := srci;
|
|
end;
|
|
|
|
srcl := src.lastIndex;
|
|
dstl := dst.lastIndex;
|
|
if srcl > dstl then begin
|
|
DumpIndexes(src, rm, NSMakeRange(dstl+1, srcl - dstl));
|
|
srcl := dstl;
|
|
end else if dstl > srcl then begin
|
|
DumpIndexes(dst, ad, NSMakeRange(srcl+1, dstl - srcl));
|
|
dstl := srcl;
|
|
end;
|
|
|
|
if srci <= srcl then begin
|
|
data.rmved := rm;
|
|
data.added := ad;
|
|
data.src := src;
|
|
data.dst := dst;
|
|
CompareIdxRange( data, srci, srcl);
|
|
end;
|
|
end;
|
|
|
|
procedure TCocoaTableListView.tableViewSelectionDidChange(notification: NSNotification);
|
|
var
|
|
NewSel: Integer;
|
|
Unsel : NSIndexSet;
|
|
rm : NSIndexSet;
|
|
ad : NSIndexSet;
|
|
begin
|
|
if Assigned(callback) then
|
|
begin
|
|
CompareIndexSets(beforeSel, selectedRowIndexes, rm, ad);
|
|
|
|
NewSel := Self.selectedRow();
|
|
callback.tableSelectionChange(NewSel, ad, rm);
|
|
|
|
beforeSel.release;
|
|
beforeSel := nil;
|
|
end;
|
|
end;
|
|
|
|
{ TCocoaStringList }
|
|
|
|
procedure TCocoaStringList.Changed;
|
|
begin
|
|
inherited Changed;
|
|
Owner.reloadData;
|
|
end;
|
|
|
|
constructor TCocoaStringList.Create(AOwner: NSTableView);
|
|
begin
|
|
Owner:=AOwner;
|
|
inherited Create;
|
|
end;
|
|
|
|
procedure TCocoaStringList.Clear;
|
|
begin
|
|
isClearing := true;
|
|
try
|
|
inherited Clear;
|
|
finally
|
|
isClearing := false;
|
|
end;
|
|
end;
|
|
|
|
{ TCellCocoaTableListView }
|
|
|
|
procedure TCellCocoaTableListView.tableView_setObjectValue_forTableColumn_row(
|
|
tableView: NSTableView; object_: id; tableColumn: NSTableColumn;
|
|
row: NSInteger);
|
|
var
|
|
lColumnIndex: NSInteger;
|
|
lNewValue: NSString;
|
|
isSel: Integer;
|
|
begin
|
|
if (NSObject(object_).isKindOfClass(NSNumber)) and isFirstColumnCheckboxes then begin
|
|
lColumnIndex := getIndexOfColumn(tableColumn);
|
|
if Assigned(callback) and (lColumnIndex = 0) then
|
|
callback.SetItemCheckedAt(row, lColumnIndex, NSNumber(object_).integerValue);
|
|
|
|
Exit;
|
|
end;
|
|
|
|
//WriteLn('[TCocoaTableListView.tableView_setObjectValue_forTableColumn_row]');
|
|
if not NSObject(object_).isKindOfClass(NSString) then Exit;
|
|
lNewValue := NSString(object_);
|
|
//WriteLn('[TCocoaTableListView.tableView_setObjectValue_forTableColumn_row] A');}
|
|
if ReadOnly then Exit;
|
|
|
|
lColumnIndex := getIndexOfColumn(tableColumn);
|
|
if Assigned(callback) then
|
|
begin
|
|
callback.SetItemTextAt(row, lColumnIndex, lNewValue.UTF8String);
|
|
reloadDataForRow_column(lColumnIndex, row);
|
|
end;
|
|
end;
|
|
|
|
function TCellCocoaTableListView.tableView_dataCellForTableColumn_row(
|
|
tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): NSCell;
|
|
var
|
|
chk : Boolean;
|
|
txt : string;
|
|
col : Integer;
|
|
nstxt : NSString;
|
|
idx : Integer;
|
|
img : NSImage;
|
|
btn : NSTableButtonCell;
|
|
colorTitle : NSMutableAttributedString;
|
|
begin
|
|
Result:=nil;
|
|
if not isFirstColumnCheckboxes and not isImagesInCell then Exit;
|
|
|
|
col := getIndexOfColumn(tableColumn);
|
|
if (col <> 0) then Exit;
|
|
|
|
if not isFirstColumnCheckboxes and isImagesInCell then begin
|
|
img := lclGetItemImageAt(row, col);
|
|
|
|
Result := NSImageAndTextCell(NSImageAndTextCell.alloc).initTextCell(NSSTR_EMPTY);
|
|
NSImageAndTextCell(Result).drawImage := img; // if "image" is assigned, text won't be drawn :(
|
|
Exit;
|
|
end;
|
|
txt := '';
|
|
chk := false;
|
|
|
|
callback.GetItemTextAt(row, col, txt);
|
|
|
|
if txt = '' then nstxt := NSString.string_
|
|
else nstxt := NSString.stringWithUTF8String(@txt[1]);
|
|
|
|
btn := NSTableButtonCell.alloc.init.autorelease;
|
|
//Result.setAllowsMixedState(True);
|
|
btn.setButtonType(NSSwitchButton);
|
|
btn.setTitle(nstxt);
|
|
|
|
// forced "controlTextColor" provides a better result on unfocused
|
|
// nstablelist view with checkboxes
|
|
colorTitle := NSMutableAttributedString.alloc.initWithAttributedString(btn.attributedTitle);
|
|
colorTitle.addAttribute_value_range(NSForegroundColorAttributeName
|
|
, NSColor.controlTextColor
|
|
, NSMakeRange(0, colorTitle.length));
|
|
btn.setAttributedTitle(colorTitle);
|
|
colorTitle.release;
|
|
|
|
if chk then begin
|
|
btn.setIntValue(1);
|
|
btn.setCellAttribute_to(NSCellState, NSOnState);
|
|
end;
|
|
Result := btn;
|
|
end;
|
|
|
|
procedure TCellCocoaTableListView.lclInsDelRow(Arow: Integer; inserted: Boolean);
|
|
begin
|
|
noteNumberOfRowsChanged;
|
|
end;
|
|
|
|
procedure TCellCocoaTableListView.lclSetColumnAlign(acolumn: NSTableColumn;
|
|
aalignment: NSTextAlignment);
|
|
begin
|
|
if not Assigned(acolumn) then Exit;
|
|
NSCell(acolumn.headerCell).setAlignment( aalignment );
|
|
NSCell(acolumn.dataCell).setAlignment( aalignment );
|
|
end;
|
|
|
|
function TCellCocoaTableListView.tableView_objectValueForTableColumn_row(
|
|
tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): id;
|
|
var
|
|
lStringList: TStringList;
|
|
col: NSInteger;
|
|
StrResult: NSString;
|
|
chk : Integer;
|
|
txt : string;
|
|
begin
|
|
{$IFDEF COCOA_DEBUG_TABCONTROL}
|
|
WriteLn(Format('[TCocoaTableListView.tableView_objectValueForTableColumn_row] col=%d row=%d Items.Count=%d',
|
|
[col, row, Items.Count]));
|
|
{$ENDIF}
|
|
|
|
Result := nil;
|
|
if not Assigned(callback) then Exit;
|
|
col := getIndexOfColumn(tableColumn);
|
|
if (col = 0) and isFirstColumnCheckboxes then begin
|
|
chk := 0;
|
|
callback.GetItemCheckedAt(row, col, chk);
|
|
Result := NSNumber.numberWithInt(chk);
|
|
Exit;
|
|
end;
|
|
|
|
txt := '';
|
|
if callback.GetItemTextAt(row, col, txt) then begin
|
|
if txt = '' then Result := NSString.string_
|
|
else Result := NSString.stringWithUTF8String(@txt[1])
|
|
end;
|
|
(*
|
|
if row > Items.Count-1 then begin
|
|
Result := nil;
|
|
Exit;
|
|
end;
|
|
if col = 0 then
|
|
StrResult := NSStringUTF8(Items.Strings[row])
|
|
else
|
|
begin
|
|
lStringList := TStringList(Items.Objects[row]);
|
|
StrResult := NSStringUTF8(lStringList.Strings[col-1]);
|
|
end;
|
|
Result := StrResult;
|
|
*)
|
|
end;
|
|
|
|
{ TCocoaTableListItem }
|
|
|
|
function TCocoaTableListItem.initWithFrame(frameRect: NSRect): id;
|
|
var
|
|
ImageIndex: NSInteger;
|
|
//bmp: TBitmap;
|
|
Img: NSImage;
|
|
begin
|
|
Result := inherited initWithFrame(frameRect);
|
|
{$ifdef BOOLFIX}
|
|
Result.setAutoresizesSubviews_(Ord(True));
|
|
{$else}
|
|
Result.setAutoresizesSubviews(True);
|
|
{$endif}
|
|
|
|
checkedSubview := NSButton.alloc.init;
|
|
checkedSubview.setButtonType(NSSwitchButton);
|
|
checkedSubview.setFrameOrigin(GetNSPoint(0,0));
|
|
checkedSubview.setFrameSize(GetNSSize(frameRect.size.height, frameRect.size.height));
|
|
Result.addSubview(checkedSubView);
|
|
|
|
imageSubView := NSImageView.alloc.initWithFrame(checkedSubView.frame);
|
|
Result.addSubview(imageSubView);
|
|
|
|
textSubView := NSTextField.alloc.initWithFrame(frameRect);
|
|
textSubView.setBordered(False);
|
|
textSubView.setDrawsBackground(False);
|
|
textSubView.cell.setSendsActionOnEndEditing(True);
|
|
textSubView.cell.setLineBreakMode(NSLineBreakByTruncatingTail);
|
|
textSubView.setEditable(False);
|
|
textSubView.setAllowsEditingTextAttributes(False);
|
|
Result.addSubview(textSubView);
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.dealloc;
|
|
begin
|
|
checkedSubView.release;
|
|
imageSubView.release;
|
|
textSubView.release;
|
|
inherited dealloc;
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setColumn(AColumn: NSTableColumn);
|
|
begin
|
|
column := AColumn;
|
|
resizeSubviewsWithOldSize(GetNSSize(column.width, column.tableView.rowHeight));
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setImage(AImage: NSImage);
|
|
begin
|
|
imageSubView.setImage(AImage);
|
|
{$ifdef BOOLFIX}
|
|
imageSubView.setHidden_(Ord(AImage = nil));
|
|
{$else}
|
|
imageSubView.setHidden(AImage = nil);
|
|
{$endif}
|
|
resizeSubviewsWithOldSize(GetNSSize(column.width, column.tableView.rowHeight));
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setCheckState(AState: NSInteger);
|
|
begin
|
|
checkedSubView.setState(AState);
|
|
{$ifdef BOOLFIX}
|
|
checkedSubView.setHidden_(Ord(AState = -1));
|
|
{$else}
|
|
checkedSubView.setHidden(AState = -1);
|
|
{$endif}
|
|
resizeSubviewsWithOldSize(GetNSSize(column.width, column.tableView.rowHeight));
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setStringValue(AString: NSString);
|
|
begin
|
|
if Assigned(textSubView) and Assigned(AString) then
|
|
textSubView.setStringValue(AString);
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setEditable(flag: Boolean);
|
|
begin
|
|
textSubView.setEditable(flag);
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setFont(AFont: NSFont);
|
|
begin
|
|
textSubView.setFont(AFont);
|
|
resizeSubviewsWithOldSize(GetNSSize(column.width, column.tableView.rowHeight));
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setTarget(ATarget: id);
|
|
begin
|
|
checkedSubView.setTarget(ATarget);
|
|
textSubView.setTarget(ATarget);
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setCheckAction(aSelector: SEL);
|
|
begin
|
|
checkedSubView.setAction(aSelector);
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setTextAction(aSelector: SEL);
|
|
begin
|
|
textSubView.setAction(aSelector);
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.resizeSubviewsWithOldSize(oldSize: NSSize);
|
|
var
|
|
origin: NSPoint;
|
|
size: NSSize;
|
|
height: CGFloat;
|
|
begin
|
|
origin := bounds.origin;
|
|
size := oldSize;
|
|
if not checkedSubView.isHidden then
|
|
begin
|
|
checkedSubView.setFrameOrigin(origin);
|
|
origin.x := origin.x + checkedSubView.frame.size.width;
|
|
size.width := size.width - checkedSubView.frame.size.width;
|
|
end;
|
|
|
|
if not imageSubView.isHidden then
|
|
begin
|
|
imageSubView.setFrameOrigin(origin);
|
|
origin.x := origin.x + imageSubView.frame.size.width;
|
|
size.width := size.width - imageSubView.frame.size.width;
|
|
end;
|
|
|
|
// Vertically center text
|
|
height := textSubView.font.boundingRectForFont.size.height;
|
|
origin.y := ((size.height - height + 0.5) / 2.0);
|
|
size.height := height;
|
|
textSubView.setFrameOrigin(origin);
|
|
textSubView.setFrameSize(size);
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.setIdentifier(identifier_: NSString);
|
|
begin
|
|
idStr := identifier_;
|
|
end;
|
|
|
|
function TCocoaTableListItem.identifier: NSString;
|
|
begin
|
|
Result := idStr;
|
|
end;
|
|
|
|
function TCocoaTableListItem.textFrame: NSRect;
|
|
begin
|
|
Result := textSubView.frame;
|
|
end;
|
|
|
|
procedure TCocoaTableListItem.lclSetEnabled(AEnabled: Boolean);
|
|
begin
|
|
// If NSTextField editable is set False, text color won't change when disabled
|
|
if AEnabled then
|
|
textSubView.setTextColor(NSColor.controlTextColor)
|
|
else
|
|
textSubView.setTextColor(NSColor.disabledControlTextColor);
|
|
inherited lclSetEnabled(AEnabled);
|
|
end;
|
|
|
|
{ TViewCocoaTableListView }
|
|
|
|
function TViewCocoaTableListView.tableView_viewForTableColumn_row(tableView: NSTableView;
|
|
tableColumn: NSTableColumn; row: NSInteger): NSView;
|
|
var
|
|
col: NSInteger;
|
|
frameRect: NSRect;
|
|
item: TCocoaTableListItem;
|
|
StrValue: NSString;
|
|
chkst: Integer;
|
|
txt: String;
|
|
begin
|
|
frameRect.origin := GetNSPoint(0,0);
|
|
frameRect.size := GetNSSize(tableColumn.width, rowHeight);
|
|
|
|
item := TCocoaTableListItem(makeViewWithIdentifier_owner(NSSTR('tblview'), self));
|
|
if item = nil then begin
|
|
item := TCocoaTableListItem.alloc.initWithFrame(frameRect);
|
|
//todo: should be system font :?
|
|
//item.setfont(TCocoaFont(ListView.Font.Handle).Font);
|
|
item.setTarget(self);
|
|
item.setCheckAction(ObjCSelector('checkboxAction:'));
|
|
item.setTextAction(ObjCSelector('textFieldAction:'));
|
|
item.setidentifier(NSSTR('tblview'));
|
|
end;
|
|
|
|
item.setFrame(frameRect);
|
|
item.setColumn(tableColumn);
|
|
col := tableColumns.indexOfObject(tableColumn);
|
|
if (col = 0) and isFirstColumnCheckboxes then begin
|
|
callback.GetItemCheckedAt(row, col, chkst);
|
|
item.setCheckState(chkst)
|
|
end
|
|
else
|
|
item.setCheckState(-1);
|
|
|
|
item.setImage(lclGetItemImageAt(row, col));
|
|
|
|
txt := '';
|
|
if not callback.GetItemTextAt(row, col, txt) then txt := '';
|
|
|
|
item.setStringValue(NSStringUtf8(txt));
|
|
item.lclSetEnabled(isEnabled);
|
|
Result := item
|
|
end;
|
|
|
|
procedure TViewCocoaTableListView.textFieldAction(sender: NSTextField);
|
|
var
|
|
column, row: NSInteger;
|
|
begin
|
|
if not Assigned(callback) then Exit;
|
|
|
|
column := columnForView(sender);
|
|
row := rowForView(sender);
|
|
callback.SetItemTextAt(row, column, NSStringToString(sender.stringValue));
|
|
reloadDataForRow_column(row, column);
|
|
end;
|
|
|
|
procedure TViewCocoaTableListView.checkboxAction(sender: NSButton);
|
|
var
|
|
row: NSInteger;
|
|
begin
|
|
if not Assigned(callback) then Exit;
|
|
|
|
row := rowForView(sender);
|
|
callback.SetItemCheckedAt(row, 0, sender.state);
|
|
reloadDataForRow_column(row, 0);
|
|
end;
|
|
|
|
function TViewCocoaTableListView.lclGetLabelRect(ARow, ACol: Integer;
|
|
const BoundsRect: TRect): TRect;
|
|
var
|
|
lTableItemLV: TCocoaTableListItem;
|
|
begin
|
|
Result := BoundsRect;
|
|
lTableItemLV := TCocoaTableListItem(viewAtColumn_row_makeIfNecessary(ACol, ARow, False));
|
|
Result.Left := Round(lTableItemLV.textFrame.origin.x - 1);
|
|
Result.Width := Round(lTableItemLV.textFrame.size.width);
|
|
end;
|
|
|
|
procedure TViewCocoaTableListView.lclInsDelRow(Arow: Integer; inserted: Boolean);
|
|
var
|
|
rows: NSIndexSet;
|
|
begin
|
|
rows := NSIndexSet.indexSetWithIndexesInRange(NSMakeRange(Arow,1));
|
|
if inserted then
|
|
insertRowsAtIndexes_withAnimation(rows, 0)
|
|
else
|
|
removeRowsAtIndexes_withAnimation(rows, 0);
|
|
end;
|
|
|
|
{ TCellCocoaTableListView1013 }
|
|
|
|
procedure TCellCocoaTableListView1013.highlightSelectionInClipRect(clipRect: NSRect
|
|
);
|
|
var
|
|
r : NSInteger;
|
|
rows : NSRange;
|
|
cnt : integer;
|
|
focused: Boolean;
|
|
hicolor: NSColor;
|
|
begin
|
|
if not IsPaintDark then begin
|
|
inherited highlightSelectionInClipRect(clipRect);
|
|
Exit;
|
|
end;
|
|
|
|
|
|
focused := Assigned(window) and (window.firstResponder = Self);
|
|
if focused then
|
|
hicolor := NSColor.alternateSelectedControlColor
|
|
else
|
|
// the default color is NSColor.secondarySelectedControlColor;
|
|
hicolor := NSColor.darkGrayColor;
|
|
|
|
rows := rowsInRect(clipRect);
|
|
cnt := rows.length;
|
|
r := rows.location;
|
|
hicolor.setFill;
|
|
while cnt>0 do
|
|
begin
|
|
if isRowSelected(r) then NSRectFill(rectOfRow(r));
|
|
inc(r);
|
|
dec(cnt);
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|