unit CocoaUtils;

{$mode objfpc}{$H+}
{$modeswitch objectivec1}

interface

uses
  MacOSAll, CocoaAll,
  Types, LCLType;

type
  { NSObjectExtensionExternal }

  NSObjectExtensionExternal = objccategory(NSObject)
    //the method is available at NSObject, but not declared in NSObject,
    // for some reason
    function className: NSString; message 'className';
  end; external;

  { NSLCLDebugExtension }

  NSLCLDebugExtension = objccategory(NSObject)
    function lclClassName: shortstring; message 'lclClassName';
  end;

const
  NSNullRect : NSRect = (origin:(x:0; y:0); size:(width:0; height:0));

function GetNSPoint(x,y: single): NSPoint; inline;

function GetCGRect(x1, y1, x2, y2: Integer): CGRect;
function CGRectToRect(const c: CGRect): TRect;

function GetNSRect(x, y, width, height: Integer): NSRect; inline;
function RectToNSRect(const r: TRect): NSRect;

procedure NSToLCLRect(const ns: NSRect; var lcl: TRect); overload;
procedure NSToLCLRect(const ns: NSRect; ParentHeight: Single; var lcl: TRect); overload;

procedure LCLToNSRect(const lcl: TRect; var ns: NSRect); overload;
procedure LCLToNSRect(const lcl: TRect; ParentHeight: Single; var ns: NSRect); overload;

function CreateParamsToNSRect(const params: TCreateParams): NSRect;

function NSStringUtf8(s: PChar): NSString;
function NSStringUtf8(const s: String): NSString;
function NSStringToString(ns: NSString): String;

function GetNSObjectView(obj: NSObject): NSView;
procedure AddViewToNSObject(ctrl: NSView; obj: NSObject);
procedure AddViewToNSObject(ctrl: NSView; obj: NSObject; X,Y: integer);

procedure SetNSText(text: NSText; const s: String); inline;
function GetNSText(text: NSText): string; inline;

procedure SetNSControlValue(c: NSControl; const S: String); inline;
function GetNSControlValue(c: NSControl): String; inline;

implementation

const
  DEFAULT_CFSTRING_ENCODING = kCFStringEncodingUTF8;

function CFStringToStr(AString: CFStringRef; Encoding: CFStringEncoding = DEFAULT_CFSTRING_ENCODING): String;
var
  Str: Pointer;
  StrSize: CFIndex;
  StrRange: CFRange;
begin
  if AString = nil then
  begin
    Result := '';
    Exit;
  end;

  // Try the quick way first
  Str := CFStringGetCStringPtr(AString, Encoding);
  if Str <> nil then
    Result := PChar(Str)
  else
  begin
    // if that doesn't work this will
    StrRange.location := 0;
    StrRange.length := CFStringGetLength(AString);

    CFStringGetBytes(AString, StrRange, Encoding,
      Ord('?'), False, nil, 0, StrSize);
    SetLength(Result, StrSize);

    if StrSize > 0 then
      CFStringGetBytes(AString, StrRange, Encoding,
        Ord('?'), False, @Result[1], StrSize, StrSize);
  end;
end;

function GetNSObjectView(obj: NSObject): NSView;
begin
  Result:=nil;
  if not Assigned(obj) then Exit;
  if obj.isKindOfClass_(NSView) then Result:=NSView(obj)
  else if obj.isKindOfClass_(NSWindow) then Result:=NSWindow(obj).contentView;
end;

procedure AddViewToNSObject(ctrl: NSView; obj: NSObject);
var
  view : NSView;
begin
  view:=GetNSObjectView(obj);
  if not Assigned(view) then Exit;
  view.addSubView(ctrl);
end;

procedure AddViewToNSObject(ctrl: NSView; obj: NSObject; X,Y: integer);
begin
  AddViewToNSObject(ctrl, obj);
  //SetViewFramePos(ctrl, x,y);
end;

function GetNSPoint(x, y: single): NSPoint;
begin
  Result.x:=x;
  Result.y:=y;
end;

function GetNSRect(x, y, width, height: Integer): NSRect;
begin
  Result.origin.x:=x;
  Result.origin.y:=y;
  Result.size.width:=width;
  Result.size.height:=height;
end;

function GetCGRect(x1, y1, x2, y2: Integer): CGRect;
begin
  Result.origin.x:=x1;
  Result.origin.y:=y1;
  Result.size.width:=x2-x1;
  Result.size.height:=y2-y1;
end;

function CGRectToRect(const c:CGRect):TRect;
begin
  with Result do begin
    Left:=round(c.origin.x);
    Top:=round(c.origin.y);
    Right:=round(c.origin.x+c.size.width);
    Bottom:=round(c.origin.y+c.size.height);
  end;
end;

function RectToNSRect(const r: TRect): NSRect;
begin
  Result:=GetNSRect(r.Left,r.Top,r.Right-r.Left,r.Bottom-r.Top);
end;

procedure NSToLCLRect(const ns: NSRect; var lcl: TRect);
begin
  lcl.Left:=round(ns.origin.x);
  lcl.Top:=round(ns.origin.y);
  lcl.Right:=round(ns.origin.x+ns.size.width);
  lcl.Bottom:=round(ns.origin.y+ns.size.height);
end;

procedure NSToLCLRect(const ns: NSRect; ParentHeight: Single; var lcl: TRect);
begin
  lcl.Left:=Round(ns.origin.x);
  lcl.Top:=Round(ParentHeight-ns.size.height-ns.origin.y);
  lcl.Right:=Round(ns.origin.x+ns.size.width);
  lcl.Bottom:=Round(lcl.Top+ns.size.height);
end;

procedure LCLToNSRect(const lcl: TRect; var ns: NSRect); overload;
begin
  ns.origin.x:=lcl.Left;
  ns.origin.y:=lcl.Top;
  ns.size.width:=lcl.Right-lcl.Left;
  ns.size.height:=lcl.Bottom-lcl.Top;
end;

procedure LCLToNSRect(const lcl: TRect; ParentHeight: Single; var ns: NSRect); overload;
begin
  ns.origin.x:=lcl.left;
  ns.origin.y:=ParentHeight-(lcl.bottom-lcl.Top)-lcl.Top;
  ns.size.width:=lcl.Right-lcl.Left;
  ns.size.height:=lcl.Bottom-lcl.Top;
end;


function CreateParamsToNSRect(const params: TCreateParams): NSRect;
begin
  with params do Result:=GetNSRect(X,Y,Width,Height);
end;

function NSStringUtf8(s: PChar): NSString;
var
  cf : CFStringRef;
begin
  {NSString and CFStringRef are interchangable}
  cf:=CFStringCreateWithCString(nil, S, kCFStringEncodingUTF8);
  Result:=NSString(cf);
end;

function NSStringUtf8(const s: String): NSString;
var
  cf : CFStringRef;
begin
  {NSString and CFStringRef are interchangable}
  cf:=CFStringCreateWithCString(nil, Pointer(PChar(S)), kCFStringEncodingUTF8);
  Result:=NSString(cf);
end;

function NSStringToString(ns: NSString): String;
begin
  Result:=CFStringToStr(CFStringRef(ns));
end;

procedure SetNSText(text: NSText; const s: String); inline;
begin
  if Assigned(text) then
    text.setString(NSStringUTF8(s));
end;

function GetNSText(text: NSText): string; inline;
begin
  if Assigned(text) then
    Result := NSStringToString(text.string_)
  else
    Result:='';
end;

procedure SetNSControlValue(c: NSControl; const S: String); inline;
begin
  if Assigned(c) then
    c.setStringValue(NSStringUtf8(S));
end;

function GetNSControlValue(c: NSControl): String; inline;
begin
  if Assigned(c) then
    Result:=NSStringToString(c.stringValue)
  else
    Result:='';
end;


{ NSLCLDebugExtension }

function NSLCLDebugExtension.lclClassName: shortstring;
begin
  Result:=NSStringToString(self.className);
end;

initialization

end.

