diff --git a/.gitattributes b/.gitattributes
index 33873cc80b..0f53819290 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -8884,6 +8884,7 @@ lcl/interfaces/cocoa/cocoawinapih.inc svneol=native#text/pascal
lcl/interfaces/cocoa/cocoawindows.pas svneol=native#text/plain
lcl/interfaces/cocoa/cocoawsbuttons.pp svneol=native#text/pascal
lcl/interfaces/cocoa/cocoawschecklst.pas svneol=native#text/plain
+lcl/interfaces/cocoa/cocoawsclipboard.pas svneol=native#text/plain
lcl/interfaces/cocoa/cocoawscomctrls.pas svneol=native#text/plain
lcl/interfaces/cocoa/cocoawscommon.pas svneol=native#text/plain
lcl/interfaces/cocoa/cocoawsdatepicker.pas svneol=native#text/plain
diff --git a/lcl/interfaces/cocoa/cocoaint.pas b/lcl/interfaces/cocoa/cocoaint.pas
index 624c032996..7bcca56911 100644
--- a/lcl/interfaces/cocoa/cocoaint.pas
+++ b/lcl/interfaces/cocoa/cocoaint.pas
@@ -28,8 +28,6 @@ interface
uses
// rtl+ftl
Types, Classes, SysUtils, Math, contnrs,
- // fcl-image
- fpreadpng, fpwritepng, fpimage, fpreadbmp, fpwritebmp,
// carbon bindings
MacOSAll,
// interfacebase
@@ -37,6 +35,7 @@ uses
// private
CocoaAll, CocoaPrivate, CocoaUtils, CocoaGDIObjects,
cocoa_extra, CocoaWSMenus, CocoaWSForms, CocoaWindows, CocoaScrollers,
+ CocoaWSClipboard,
// LCL
LCLStrConsts, LMessages, LCLMessageGlue, LCLProc, LCLIntf, LCLType,
Controls, Forms, Themes, Menus,
@@ -52,20 +51,6 @@ type
class function newWithFunc(afunc: TWSTimerProc): TCocoaTimerObject; message 'newWithFunc:';
end;
- TCocoaClipboardDataType = (ccdtText,
- ccdtCocoaStandard, // Formats supported natively by Mac OS X
- ccdtBitmap, // BMPs need conversion to PNG to work with other Mac OS X apps
- ccdtNonStandard { Formats that will only work in LCL apps } );
-
- TCocoaClipboardData = class(TObject) // TClipboardFormat is a reference to a TClipboardData
- public
- MimeType: string;
- CocoaFormat: NSString; // utilized for ccdtCocoaStandard and ccdtNonStandard
- DataType: TCocoaClipboardDataType;
- constructor Create(AMimeType: string; ACocoaFormat: NSString; ADataType: TCocoaClipboardDataType);
- destructor Destroy; override;
- end;
-
TAppDelegate = objcclass(NSObject, NSApplicationDelegateProtocol)
procedure application_openFiles(sender: NSApplication; filenames: NSArray);
procedure applicationDidHide(notification: NSNotification);
@@ -86,13 +71,6 @@ type
function nextEventMatchingMask_untilDate_inMode_dequeue(mask: NSUInteger; expiration: NSDate; mode: NSString; deqFlag: Boolean): NSEvent; override;
end;
- TCocoaPasteboardsRef = record
- pasteboard : NSPasteboard;
- changeCount: NSInteger;
- dataProc : TClipboardRequestEvent;
- isOwned : Boolean;
- end;
-
{ TCocoaWidgetSet }
TCocoaWidgetSet = class(TWidgetSet)
@@ -121,18 +99,11 @@ type
// Sandboxing
SandboxingOn: Boolean;
+ fClipboard: TCocoaWSClipboard;
// Clipboard
- PrimarySelection: NSPasteboard;
- SecondarySelection: NSPasteboard;
- ClipboardFormats: TFPObjectList; // of TCocoaClipboardData
- ClipboardChangeCount: NSInteger;
- Pasteboards: array [TClipboardType] of TCocoaPasteboardsRef;
- procedure InitClipboard();
- procedure FreeClipboard();
procedure SyncClipboard();
- function GetClipboardDataForFormat(AFormat: TClipboardFormat): TCocoaClipboardData;
function PromptUser(const DialogCaption, DialogMessage: String;
DialogType: longint; Buttons: PLongint; ButtonCount, DefaultIndex,
diff --git a/lcl/interfaces/cocoa/cocoalclintf.inc b/lcl/interfaces/cocoa/cocoalclintf.inc
index c32ef772d4..d3be384f9c 100644
--- a/lcl/interfaces/cocoa/cocoalclintf.inc
+++ b/lcl/interfaces/cocoa/cocoalclintf.inc
@@ -149,54 +149,9 @@ begin
Result := (WindowHandle <> 0) and (DC <> 0) and TCocoaContext(DC).isDesignDC;
end;
-procedure TCocoaWidgetSet.InitClipboard;
-var
- t : TClipboardType;
-begin
- PrimarySelection := NSPasteboard.pasteboardWithUniqueName();
- SecondarySelection := NSPasteboard.pasteboardWithUniqueName();
- ClipboardFormats := TFPObjectList.Create(True);
-
- Pasteboards[ctPrimarySelection].pasteboard := PrimarySelection;
- Pasteboards[ctSecondarySelection].pasteboard := SecondarySelection;
- Pasteboards[ctClipboard].pasteboard := NSPasteboard.generalPasteboard;
-
- for t := Low(TClipboardType) to High(TClipboardType) do
- if Assigned(Pasteboards[t].pasteboard) then
- Pasteboards[t].changeCount := Pasteboards[t].pasteboard.changeCount;
-end;
-
-procedure TCocoaWidgetSet.FreeClipboard;
-begin
- PrimarySelection.releaseGlobally();
- SecondarySelection.releaseGlobally();
- ClipboardFormats.Free;
-end;
-
procedure TCocoaWidgetSet.SyncClipboard();
-var
- ct : TClipboardType;
- pb : NSPasteboard;
begin
- for ct := low(TClipboardType) to high(TClipboardType) do begin
- if not Pasteboards[ct].isOwned then Continue;
-
- pb := Pasteboards[ct].pasteboard;
- if not Assigned(pb) then Continue;
-
- if (pb.changeCount <> Pasteboards[ct].changeCount) then
- begin
- Pasteboards[ct].isOwned:=false;
- if Assigned(Pasteboards[ct].dataProc) then
- // notifying about the loss of ownership
- Pasteboards[ct].dataProc(0, nil);
- end;
- end;
-end;
-
-function TCocoaWidgetSet.GetClipboardDataForFormat(AFormat: TClipboardFormat): TCocoaClipboardData;
-begin
- Result := TCocoaClipboardData(AFormat);
+ fClipboard.Sync;
end;
{------------------------------------------------------------------------------
diff --git a/lcl/interfaces/cocoa/cocoaobject.inc b/lcl/interfaces/cocoa/cocoaobject.inc
index 2e2b9cd3d4..3424213344 100644
--- a/lcl/interfaces/cocoa/cocoaobject.inc
+++ b/lcl/interfaces/cocoa/cocoaobject.inc
@@ -170,7 +170,7 @@ begin
InitStockItems;
- InitClipboard(); // must be here otherwise clipboard calls before Application.Initialize crash
+ fClipboard := TCocoaWSClipboard.Create; // must be here otherwise clipboard calls before Application.Initialize crash
end;
{------------------------------------------------------------------------------
@@ -193,7 +193,7 @@ begin
FreeSysColorBrushes;
- FreeClipboard();
+ fClipboard.Free;
// The CocoaCaret is based WidgetSet timer.
// The GlobalCaret is freed in finalization section, which is called
@@ -612,20 +612,6 @@ begin
Result.func:=afunc;
end;
-{ TCocoaClipboardData }
-
-constructor TCocoaClipboardData.Create(AMimeType: string; ACocoaFormat: NSString; ADataType: TCocoaClipboardDataType);
-begin
- MimeType := AMimeType;
- CocoaFormat := ACocoaFormat;
- DataType := ADataType;
-end;
-
-destructor TCocoaClipboardData.Destroy;
-begin
- CocoaFormat.release;
-end;
-
procedure TAppDelegate.application_openFiles(sender: NSApplication; filenames: NSArray);
var
lFiles: array of string;
diff --git a/lcl/interfaces/cocoa/cocoawinapi.inc b/lcl/interfaces/cocoa/cocoawinapi.inc
index 33da6991d3..24a501ab02 100644
--- a/lcl/interfaces/cocoa/cocoawinapi.inc
+++ b/lcl/interfaces/cocoa/cocoawinapi.inc
@@ -141,16 +141,11 @@ end;
Returns: The corresponding mime type as string
------------------------------------------------------------------------------}
function TCocoaWidgetSet.ClipboardFormatToMimeType(FormatID: TClipboardFormat): string;
-var
- lFormat: TCocoaClipboardData;
begin
{$IFDEF VerboseClipboard}
DebugLn('TCocoaWidgetSet.ClipboardFormatToMimeType FormatID: ' + DbgS(FormatID));
{$ENDIF}
-
- lFormat := GetClipboardDataForFormat(FormatID);
- if lFormat = nil then Exit;
- Result := lFormat.MimeType;
+ Result := fClipboard.FormatToMimeType(FormatID);
end;
{------------------------------------------------------------------------------
@@ -163,84 +158,12 @@ end;
------------------------------------------------------------------------------}
function TCocoaWidgetSet.ClipboardGetData(ClipboardType: TClipboardType;
FormatID: TClipboardFormat; Stream: TStream): boolean;
-var
- pasteboard: NSPasteboard;
- lFormat: TCocoaClipboardData;
- lNSStr: NSString;
- // for text
- lStr: String;
- // for standard
- lNSData: NSData;
- lNSbytes: PByte;
- i: Integer;
- // for bitmap
- image: TFPCustomImage;
- lTmpStream: TMemoryStream;
- reader: TFPCustomImageReader;
- writer: TFPCustomImageWriter;
begin
- Result := False;
{$IFDEF VerboseClipboard}
DebugLn('TCocoaWidgetSet.ClipboardGetData ClipboardType=' +
ClipboardTypeName[ClipboardType] + ' FormatID: ' + DbgS(FormatID));
{$ENDIF}
-
- case ClipboardType of
- ctPrimarySelection: pasteboard := PrimarySelection;
- ctSecondarySelection: pasteboard := SecondarySelection;
- ctClipboard: pasteboard := NSPasteboard.generalPasteboard;
- end;
-
- lFormat := GetClipboardDataForFormat(FormatID);
- if lFormat = nil then Exit;
-
- case lFormat.DataType of
- ccdtText:
- begin
- lNSStr := pasteboard.stringForType(lFormat.CocoaFormat);
- if lNSStr = nil then Exit;
- lStr := NSStringToString(lNSStr);
- Stream.Write(lStr[1], Length(lStr));
- {$IFDEF VerboseClipboard}
- DebugLn('TCocoaWidgetSet.ClipboardGetData IsText Result=' + lStr);
- {$ENDIF}
- end;
- ccdtCocoaStandard, ccdtNonStandard:
- begin
- lNSData := pasteboard.dataForType(lFormat.CocoaFormat);
- if lNSData = nil then Exit;
- lNSbytes := lNSData.bytes;
- for i := 0 to lNSData.length-1 do
- Stream.WriteByte(lNSbytes[i]);
- end;
- // In Cocoa images are stored as PNG, convert to BMP for LCL app usage
- ccdtBitmap:
- begin
- lNSData := pasteboard.dataForType(lFormat.CocoaFormat);
- if lNSData = nil then Exit;
- lNSbytes := lNSData.bytes;
-
- Image := TFPMemoryImage.Create(10, 10);
- Reader := TFPReaderPNG.Create;
- Writer := TFPWriterBMP.Create;
- lTmpStream := TMemoryStream.Create;
- try
- for i := 0 to lNSData.length-1 do
- lTmpStream.WriteByte(lNSbytes[i]);
- lTmpStream.Position := 0;
-
- Image.LoadFromStream(lTmpStream, Reader);
- Image.SaveToStream(Stream, Writer);
- finally
- Image.Free;
- Reader.Free;
- Writer.Free;
- lTmpStream.Free;
- end;
- end;
- end;
-
- Result := True;
+ Result := fClipboard.GetData(ClipboardType, FormatID, Stream);
end;
{------------------------------------------------------------------------------
@@ -251,28 +174,23 @@ end;
(you must free it yourself)
Returns: If the function succeeds
------------------------------------------------------------------------------}
+
function TCocoaWidgetSet.ClipboardGetFormats(ClipboardType: TClipboardType;
var Count: integer; var List: PClipboardFormat): boolean;
var
- ListDataSize, ListCount: Integer;
- i: Integer;
+ fmt: TDynClipboardFormatArray;
begin
- Result := False;
{$IFDEF VerboseClipboard}
DebugLn('TCocoaWidgetSet.ClipboardGetFormats ClipboardType' +
ClipboardTypeName[ClipboardType]);
{$ENDIF}
-
- ListCount := Min(ClipboardFormats.Count, Count);
- ListDataSize := SizeOf(Pointer) * ListCount;
- List := GetMem(ListDataSize);
-
- for i := 0 to ListCount - 1 do
- begin
- List[i] := PtrUInt(ClipboardFormats.Items[i]);
- end;
-
- Result := True;
+ fmt := nil;
+ Result := fClipboard.GetFormats(ClipboardType, Count, fmt);
+ if Count > 0 then begin
+ GetMem(List, Count * sizeof(TClipboardFormat));
+ Move(fmt[0], List^, Count * sizeof(TClipboardFormat));
+ end else
+ List := nil;
end;
{------------------------------------------------------------------------------
@@ -295,116 +213,12 @@ end;
function TCocoaWidgetSet.ClipboardGetOwnerShip(ClipboardType: TClipboardType;
OnRequestProc: TClipboardRequestEvent; FormatCount: integer;
Formats: PClipboardFormat): boolean;
-var
- pasteboard: NSPasteboard;
- i: Integer;
- lCurFormat: TCocoaClipboardData;
- DataStream: TMemoryStream;
- FormatToOwn: NSString;
- FormatToOwnArray: NSArray;
- // text format
- lText: string;
- lNSText: NSString;
- // non-text
- lNSData: NSData;
- // for bitmap
- image: TFPCustomImage;
- lTmpStream: TMemoryStream;
- reader: TFPCustomImageReader;
- writer: TFPCustomImageWriter;
begin
- Result := False;
{$IFDEF VerboseClipboard}
DebugLn('TCocoaWidgetSet.ClipboardGetOwnerShip ClipboardType=' +
ClipboardTypeName[ClipboardType] + ' FormatCount: ' + DbgS(FormatCount));
{$ENDIF}
-
- case ClipboardType of
- ctPrimarySelection: pasteboard := PrimarySelection;
- ctSecondarySelection: pasteboard := SecondarySelection;
- ctClipboard: pasteboard := NSPasteboard.generalPasteboard;
- end;
-
- DataStream := TMemoryStream.Create;
- try
- FormatToOwnArray := nil;
- if FormatCount>0 then
- begin
- FormatToOwnArray := NSArray(NSMutableArray.array_);
- for i := 0 to FormatCount-1 do
- begin
- lCurFormat := TCocoaClipboardData(Formats[i]);
- if lCurFormat = nil then Continue;
- FormatToOwn := lCurFormat.CocoaFormat;
- NSMutableArray(FormatToOwnArray).addObject(FormatToOwn);
- end;
- end;
-
- if Assigned(FormatToOwnArray) and (FormatToOwnArray.count>0) then
- pasteboard.declareTypes_owner(FormatToOwnArray, nil);
-
- for i := 0 to FormatCount-1 do
- begin
- lCurFormat := TCocoaClipboardData(Formats[i]);
- if lCurFormat = nil then Continue;
- DataStream.Position := 0;
- DataStream.Size := 0;
- OnRequestProc(Formats[i], DataStream);
-
- case lCurFormat.DataType of
- ccdtText:
- begin
- DataStream.Position := 0;
- SetLength(lText, DataStream.Size);
- DataStream.Read(lText[1], DataStream.Size);
- lNSText := NSStringUtf8(lText);
-
- pasteboard.setString_forType(lNSText, lCurFormat.CocoaFormat);
- end;
- ccdtCocoaStandard, ccdtNonStandard:
- begin
- DataStream.Position := 0;
- lNSData := NSData.dataWithBytes_length(DataStream.Memory, DataStream.Size);
-
- pasteboard.setData_forType(lNSData, lCurFormat.CocoaFormat);
- end;
- ccdtBitmap:
- begin
- Image := TFPMemoryImage.Create(10, 10);
- Reader := TFPReaderBMP.Create;
- Writer := TFPWriterPNG.Create;
- lTmpStream := TMemoryStream.Create;
- try
- DataStream.Position := 0;
- Image.LoadFromStream(DataStream, Reader);
- Image.SaveToStream(lTmpStream, Writer);
- lTmpStream.Position := 0;
- lNSData := NSData.dataWithBytes_length(lTmpStream.Memory, lTmpStream.Size);
- pasteboard.setData_forType(lNSData, lCurFormat.CocoaFormat);
- finally
- Image.Free;
- Reader.Free;
- Writer.Free;
- lTmpStream.Free;
- end;
- end;
- end;
- end;
- finally
- DataStream.Free;
- end;
-
- Pasteboards[ClipboardType].pasteboard:=pasteboard;
- Pasteboards[ClipboardType].dataProc:=OnRequestProc;
- if Assigned(pasteboard) then
- begin
- Pasteboards[ClipboardType].changeCount:=pasteboard.changeCount;
- Pasteboards[ClipboardType].isOwned:=true;
- end else
- Pasteboards[ClipboardType].isOwned:=false;
-
-
- Result := True;
+ Result := fClipboard.GetOwnership(ClipboardType, OnRequestProc, FormatCount, Formats);
end;
{------------------------------------------------------------------------------
@@ -414,64 +228,8 @@ end;
Returns: The registered Format identifier (TClipboardFormat)
------------------------------------------------------------------------------}
function TCocoaWidgetSet.ClipboardRegisterFormat(const AMimeType: string): TClipboardFormat;
-var
- i: Integer;
- lCurData: TCocoaClipboardData;
- lNSStr: NSString = nil;
- lDataType: TCocoaClipboardDataType;
begin
- Result := 0;
-
- // Check first if it was already registered
- for i := 0 to ClipboardFormats.Count-1 do
- begin
- lCurData := TCocoaClipboardData(ClipboardFormats.Items[i]);
- if lCurData.MimeType = AMimeType then
- begin
- Result := TClipboardFormat(lCurData);
- {$IFDEF VerboseClipboard}
- DebugLn('TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=' + AMimeType
- + ' Result='+DbgS(Result));
- {$ENDIF}
- Exit;
- end;
- end;
-
- // if none was found, we need to register it
-
- lDataType := ccdtNonStandard;
- // See PredefinedClipboardMimeTypes for the most common mime-types
- case AMimeType of
- 'text/plain':
- begin
- //hack: the name of constants is a hack
- // should be replaced with either weaklinking
- // or dynamic loading (dlsym)
- lNSStr := NSSTR('public.utf8-plain-text'); // NSPasteboardTypeString; // commented out for OSX < 10.6 see #33672
- lDataType := ccdtText;
- end;
- 'image/png':
- begin
- lNSStr := NSSTR('public.png'); // NSPasteboardTypePNG
- lDataType := ccdtCocoaStandard;
- end;
- 'image/bmp':
- begin
- lNSStr := NSSTR('public.png'); // NSPasteboardTypePNG
- lDataType := ccdtBitmap;
- end;
- else
- lNSStr := NSStringUtf8(AMimeType);
- lDataType := ccdtNonStandard;
- end;
-
- if lNSStr <> nil then
- begin
- lCurData := TCocoaClipboardData.Create(AMimeType, lNSStr, lDataType);
- ClipboardFormats.Add(lCurData);
- Result := TClipboardFormat(lCurData);
- end;
-
+ Result := fClipboard.RegisterFormat(AMimeType);
{$IFDEF VerboseClipboard}
DebugLn('TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=' + AMimeType
+ ' Result='+DbgS(Result));
diff --git a/lcl/interfaces/cocoa/cocoawsclipboard.pas b/lcl/interfaces/cocoa/cocoawsclipboard.pas
new file mode 100644
index 0000000000..e565cc4b40
--- /dev/null
+++ b/lcl/interfaces/cocoa/cocoawsclipboard.pas
@@ -0,0 +1,444 @@
+unit CocoaWSClipboard;
+
+interface
+
+{$modeswitch objectivec2}
+
+uses
+ CocoaAll, Classes, SysUtils, contnrs
+ // fcl-image
+ ,fpreadpng, fpwritepng, fpimage, fpreadbmp, fpwritebmp
+ ,LCLType
+ ,CocoaUtils;
+
+type
+ TCocoaClipboardDataType = (ccdtText,
+ ccdtCocoaStandard, // Formats supported natively by Mac OS X
+ ccdtBitmap, // BMPs need conversion to PNG to work with other Mac OS X apps
+ ccdtNonStandard { Formats that will only work in LCL apps } );
+
+ TCocoaClipboardData = class(TObject) // TClipboardFormat is a reference to a TClipboardData
+ public
+ MimeType: string;
+ CocoaFormat: NSString; // utilized for ccdtCocoaStandard and ccdtNonStandard
+ DataType: TCocoaClipboardDataType;
+ constructor Create(AMimeType: string; ACocoaFormat: NSString; ADataType: TCocoaClipboardDataType);
+ destructor Destroy; override;
+ end;
+
+ TCocoaPasteboardsRef = record
+ pasteboard : NSPasteboard;
+ changeCount: NSInteger;
+ dataProc : TClipboardRequestEvent;
+ isOwned : Boolean;
+ end;
+
+
+ TDynClipboardFormatArray = array of TClipboardFormat;
+
+ { TCocoaWSClipboard }
+
+ TCocoaWSClipboard = class(TObject)
+ public
+ PrimarySelection: NSPasteboard;
+ SecondarySelection: NSPasteboard;
+ ClipboardFormats: TFPObjectList; // of TCocoaClipboardData
+ Pasteboards: array [TClipboardType] of TCocoaPasteboardsRef;
+
+ constructor Create;
+ destructor Destroy; override;
+
+ procedure Sync;
+
+ function FormatToMimeType(FormatID: TClipboardFormat): string;
+ function GetData(ClipboardType: TClipboardType;
+ FormatID: TClipboardFormat; Stream: TStream): boolean;
+ function GetFormats(ClipboardType: TClipboardType;
+ var Count: integer; var List: TDynClipboardFormatArray): boolean;
+ function GetOwnership(ClipboardType: TClipboardType;
+ OnRequestProc: TClipboardRequestEvent; FormatCount: integer;
+ Formats: PClipboardFormat): boolean;
+ function RegisterFormat(const AMimeType: string): TClipboardFormat;
+
+ function GetClipboardDataForFormat(AFormat: TClipboardFormat): TCocoaClipboardData;
+ function RegisterCocoaType(AType: NSString): TClipboardFormat;
+ function CocoaTypeToMimeType(AType: NSString): string;
+ end;
+
+
+const
+ // these constants are available starting MacOSX 10.6
+ // thus for earlier systems must be redeclared
+ _NSPasteboardTypeString : NSString = nil;
+ _NSPasteboardTypePNG : NSString = nil;
+
+implementation
+
+procedure InitConst;
+begin
+ _NSPasteboardTypeString := NSSTR('public.utf8-plain-text');
+ _NSPasteboardTypePNG := NSSTR('public.png');
+end;
+
+{ TCocoaWSClipboard }
+
+constructor TCocoaWSClipboard.Create;
+var
+ t : TClipboardType;
+begin
+ inherited Create;
+ PrimarySelection := NSPasteboard.pasteboardWithUniqueName();
+ SecondarySelection := NSPasteboard.pasteboardWithUniqueName();
+ ClipboardFormats := TFPObjectList.Create(True);
+
+ Pasteboards[ctPrimarySelection].pasteboard := PrimarySelection;
+ Pasteboards[ctSecondarySelection].pasteboard := SecondarySelection;
+ Pasteboards[ctClipboard].pasteboard := NSPasteboard.generalPasteboard;
+
+ for t := Low(TClipboardType) to High(TClipboardType) do
+ if Assigned(Pasteboards[t].pasteboard) then
+ Pasteboards[t].changeCount := Pasteboards[t].pasteboard.changeCount;
+end;
+
+destructor TCocoaWSClipboard.Destroy;
+begin
+ PrimarySelection.releaseGlobally();
+ SecondarySelection.releaseGlobally();
+ ClipboardFormats.Free;
+ inherited;
+end;
+
+procedure TCocoaWSClipboard.Sync;
+var
+ ct : TClipboardType;
+ pb : NSPasteboard;
+begin
+ for ct := low(TClipboardType) to high(TClipboardType) do begin
+ if not Pasteboards[ct].isOwned then Continue;
+
+ pb := Pasteboards[ct].pasteboard;
+ if not Assigned(pb) then Continue;
+
+ if (pb.changeCount <> Pasteboards[ct].changeCount) then
+ begin
+ Pasteboards[ct].isOwned:=false;
+ if Assigned(Pasteboards[ct].dataProc) then
+ // notifying about the loss of ownership
+ Pasteboards[ct].dataProc(0, nil);
+ end;
+ end;
+end;
+
+function TCocoaWSClipboard.FormatToMimeType(FormatID: TClipboardFormat
+ ): string;
+var
+ lFormat: TCocoaClipboardData;
+begin
+ lFormat := GetClipboardDataForFormat(FormatID);
+ if lFormat = nil then Exit;
+ Result := lFormat.MimeType;
+end;
+
+function TCocoaWSClipboard.GetClipboardDataForFormat(AFormat: TClipboardFormat
+ ): TCocoaClipboardData;
+begin
+ Result := TCocoaClipboardData(AFormat);
+end;
+
+function TCocoaWSClipboard.GetData(ClipboardType: TClipboardType;
+ FormatID: TClipboardFormat; Stream: TStream): boolean;
+var
+ pasteboard: NSPasteboard;
+ lFormat: TCocoaClipboardData;
+ lNSStr: NSString;
+ // for text
+ lStr: String;
+ // for standard
+ lNSData: NSData;
+ lNSbytes: PByte;
+ i: Integer;
+ // for bitmap
+ image: TFPCustomImage;
+ lTmpStream: TMemoryStream;
+ reader: TFPCustomImageReader;
+ writer: TFPCustomImageWriter;
+begin
+ Result := False;
+ pasteboard := Pasteboards[ClipboardType].pasteboard;
+
+ lFormat := GetClipboardDataForFormat(FormatID);
+ if lFormat = nil then Exit;
+
+ case lFormat.DataType of
+ ccdtText:
+ begin
+ lNSStr := pasteboard.stringForType(lFormat.CocoaFormat);
+ if lNSStr = nil then Exit;
+ lStr := NSStringToString(lNSStr);
+ Stream.Write(lStr[1], Length(lStr));
+ {$IFDEF VerboseClipboard}
+ DebugLn('TCocoaWidgetSet.ClipboardGetData IsText Result=' + lStr);
+ {$ENDIF}
+ end;
+ ccdtCocoaStandard, ccdtNonStandard:
+ begin
+ lNSData := pasteboard.dataForType(lFormat.CocoaFormat);
+ if lNSData = nil then Exit;
+ lNSbytes := lNSData.bytes;
+ for i := 0 to lNSData.length-1 do
+ Stream.WriteByte(lNSbytes[i]);
+ end;
+ // In Cocoa images are stored as PNG, convert to BMP for LCL app usage
+ ccdtBitmap:
+ begin
+ lNSData := pasteboard.dataForType(lFormat.CocoaFormat);
+ if lNSData = nil then Exit;
+ lNSbytes := lNSData.bytes;
+
+ Image := TFPMemoryImage.Create(10, 10);
+ Reader := TFPReaderPNG.Create;
+ Writer := TFPWriterBMP.Create;
+ lTmpStream := TMemoryStream.Create;
+ try
+ for i := 0 to lNSData.length-1 do
+ lTmpStream.WriteByte(lNSbytes[i]);
+ lTmpStream.Position := 0;
+
+ Image.LoadFromStream(lTmpStream, Reader);
+ Image.SaveToStream(Stream, Writer);
+ finally
+ Image.Free;
+ Reader.Free;
+ Writer.Free;
+ lTmpStream.Free;
+ end;
+ end;
+ end;
+
+ Result := True;
+end;
+
+function TCocoaWSClipboard.GetFormats(ClipboardType: TClipboardType;
+ var Count: integer; var List: TDynClipboardFormatArray): boolean;
+var
+ i: Integer;
+ pb : NSPasteboard;
+ tp : NSString;
+begin
+ pb := Pasteboards[ClipboardType].pasteboard;
+ if not Assigned(pb) then begin
+ Result := false;
+ Exit;
+ end;
+
+ i := 0;
+ SetLength(List, pb.types.count);
+ for tp in pb.types do
+ begin
+ List[i]:=RegisterCocoaType(tp);
+ inc(i);
+ end;
+
+ Count := i;
+end;
+
+function TCocoaWSClipboard.GetOwnership(ClipboardType: TClipboardType;
+ OnRequestProc: TClipboardRequestEvent; FormatCount: integer;
+ Formats: PClipboardFormat): boolean;
+var
+ pasteboard: NSPasteboard;
+ i: Integer;
+ lCurFormat: TCocoaClipboardData;
+ DataStream: TMemoryStream;
+ FormatToOwn: NSString;
+ FormatToOwnArray: NSArray;
+ // text format
+ lText: string;
+ lNSText: NSString;
+ // non-text
+ lNSData: NSData;
+ // for bitmap
+ image: TFPCustomImage;
+ lTmpStream: TMemoryStream;
+ reader: TFPCustomImageReader;
+ writer: TFPCustomImageWriter;
+begin
+ pasteboard := Pasteboards[ClipboardType].pasteboard;
+
+ DataStream := TMemoryStream.Create;
+ try
+ FormatToOwnArray := nil;
+ if FormatCount>0 then
+ begin
+ FormatToOwnArray := NSArray(NSMutableArray.array_);
+ for i := 0 to FormatCount-1 do
+ begin
+ lCurFormat := TCocoaClipboardData(Formats[i]);
+ if lCurFormat = nil then Continue;
+ FormatToOwn := lCurFormat.CocoaFormat;
+ NSMutableArray(FormatToOwnArray).addObject(FormatToOwn);
+ end;
+ end;
+
+ if Assigned(FormatToOwnArray) and (FormatToOwnArray.count>0) then
+ pasteboard.declareTypes_owner(FormatToOwnArray, nil);
+
+ for i := 0 to FormatCount-1 do
+ begin
+ lCurFormat := TCocoaClipboardData(Formats[i]);
+ if lCurFormat = nil then Continue;
+ DataStream.Position := 0;
+ DataStream.Size := 0;
+ OnRequestProc(Formats[i], DataStream);
+
+ case lCurFormat.DataType of
+ ccdtText:
+ begin
+ DataStream.Position := 0;
+ SetLength(lText, DataStream.Size);
+ DataStream.Read(lText[1], DataStream.Size);
+ lNSText := NSStringUtf8(lText);
+
+ pasteboard.setString_forType(lNSText, lCurFormat.CocoaFormat);
+ end;
+ ccdtCocoaStandard, ccdtNonStandard:
+ begin
+ DataStream.Position := 0;
+ lNSData := NSData.dataWithBytes_length(DataStream.Memory, DataStream.Size);
+
+ pasteboard.setData_forType(lNSData, lCurFormat.CocoaFormat);
+ end;
+ ccdtBitmap:
+ begin
+ Image := TFPMemoryImage.Create(10, 10);
+ Reader := TFPReaderBMP.Create;
+ Writer := TFPWriterPNG.Create;
+ lTmpStream := TMemoryStream.Create;
+ try
+ DataStream.Position := 0;
+ Image.LoadFromStream(DataStream, Reader);
+ Image.SaveToStream(lTmpStream, Writer);
+ lTmpStream.Position := 0;
+ lNSData := NSData.dataWithBytes_length(lTmpStream.Memory, lTmpStream.Size);
+ pasteboard.setData_forType(lNSData, lCurFormat.CocoaFormat);
+ finally
+ Image.Free;
+ Reader.Free;
+ Writer.Free;
+ lTmpStream.Free;
+ end;
+ end;
+ end;
+ end;
+ finally
+ DataStream.Free;
+ end;
+
+ Pasteboards[ClipboardType].pasteboard:=pasteboard;
+ Pasteboards[ClipboardType].dataProc:=OnRequestProc;
+ if Assigned(pasteboard) then
+ begin
+ Pasteboards[ClipboardType].changeCount:=pasteboard.changeCount;
+ Pasteboards[ClipboardType].isOwned:=true;
+ end else
+ Pasteboards[ClipboardType].isOwned:=false;
+
+
+ Result := True;
+
+end;
+
+function TCocoaWSClipboard.RegisterFormat(const AMimeType: string
+ ): TClipboardFormat;
+var
+ i: Integer;
+ lCurData: TCocoaClipboardData;
+ lNSStr: NSString = nil;
+ lDataType: TCocoaClipboardDataType;
+begin
+ Result := 0;
+
+ // Check first if it was already registered
+ for i := 0 to ClipboardFormats.Count-1 do
+ begin
+ lCurData := TCocoaClipboardData(ClipboardFormats.Items[i]);
+ if lCurData.MimeType = AMimeType then
+ begin
+ Result := TClipboardFormat(lCurData);
+ {$IFDEF VerboseClipboard}
+ DebugLn('TCocoaWidgetSet.ClipboardRegisterFormat AMimeType=' + AMimeType
+ + ' Result='+DbgS(Result));
+ {$ENDIF}
+ Exit;
+ end;
+ end;
+
+ // if none was found, we need to register it
+
+ lDataType := ccdtNonStandard;
+ // See PredefinedClipboardMimeTypes for the most common mime-types
+ case AMimeType of
+ 'text/plain':
+ begin
+ //hack: the name of constants is a hack
+ // should be replaced with either weaklinking
+ // or dynamic loading (dlsym)
+ lNSStr := NSSTR('public.utf8-plain-text'); // NSPasteboardTypeString; // commented out for OSX < 10.6 see #33672
+ lDataType := ccdtText;
+ end;
+ 'image/png':
+ begin
+ lNSStr := NSSTR('public.png'); // NSPasteboardTypePNG
+ lDataType := ccdtCocoaStandard;
+ end;
+ 'image/bmp':
+ begin
+ lNSStr := NSSTR('public.png'); // NSPasteboardTypePNG
+ lDataType := ccdtBitmap;
+ end;
+ else
+ lNSStr := NSStringUtf8(AMimeType);
+ lDataType := ccdtNonStandard;
+ end;
+
+ if lNSStr <> nil then
+ begin
+ lCurData := TCocoaClipboardData.Create(AMimeType, lNSStr, lDataType);
+ ClipboardFormats.Add(lCurData);
+ Result := TClipboardFormat(lCurData);
+ end;
+end;
+
+function TCocoaWSClipboard.RegisterCocoaType(AType: NSString): TClipboardFormat;
+begin
+ Result := RegisterFormat( CocoaTypeToMimeType(AType) );
+end;
+
+function TCocoaWSClipboard.CocoaTypeToMimeType(AType: NSString): string;
+begin
+ // "default" types must be mapped to a default LCL mime-type
+ if AType.isEqualToString(_NSPasteboardTypeString) then
+ Result := 'text/plan'
+ else
+ Result := NSStringToString(AType);
+end;
+
+{ TCocoaClipboardData }
+
+constructor TCocoaClipboardData.Create(AMimeType: string; ACocoaFormat: NSString; ADataType: TCocoaClipboardDataType);
+begin
+ MimeType := AMimeType;
+ CocoaFormat := ACocoaFormat;
+ DataType := ADataType;
+end;
+
+destructor TCocoaClipboardData.Destroy;
+begin
+ CocoaFormat.release;
+end;
+
+
+initialization
+ InitConst;
+
+end.
diff --git a/lcl/interfaces/lcl.lpk b/lcl/interfaces/lcl.lpk
index 45a405c586..a7fc27424b 100644
--- a/lcl/interfaces/lcl.lpk
+++ b/lcl/interfaces/lcl.lpk
@@ -129,7 +129,7 @@ end;"/>
-
+
@@ -2391,6 +2391,11 @@ end;"/>
+
+
+
+
+