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;"/> + + + + +