Browse Source

* Clipboard working on gtk 3

Michaël Van Canneyt 5 months ago
parent
commit
be49941b9f

+ 126 - 0
src/base/fresnel.clipboard.pp

@@ -0,0 +1,126 @@
+unit fresnel.clipboard;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, Types, fpImage;
+
+Type
+  EFresnelClipboard = Class(Exception);
+  TFresnelClipBoard = Class;
+  TFresnelClipBoardClass = Class of TFresnelClipBoard;
+
+  { TFresnelClipBoard }
+
+  TFresnelClipBoard = class abstract (TComponent)
+  private
+    class var _Instance : TFresnelClipBoard;
+    class function GetInstance: TFresnelClipBoard; static;
+  private
+    procedure SetAsText(const aValue: String);
+    function GetAsText: String;
+  Protected
+    Function DoHasClipboardType(const aMimeType : String) : boolean; virtual; abstract;
+    Function DoGetContentTypes : TStringDynArray; virtual; abstract;
+    // For text formats, TBytes content is UTF8.
+    Function DoGetClipboardAsType(const aMimeType : String; out aContents : TBytes) : string; virtual; abstract;
+    procedure DoSetClipboardAsType(const aMimeType : String; aContents : TBytes); virtual; abstract;
+  public
+    Class var _ClipboardClass : TFresnelClipBoardClass;
+    class destructor done;
+    Class Property Instance : TFresnelClipBoard Read GetInstance;
+  Public
+    Function GetContentTypes : TStringDynArray;
+    // aMimetype is "text/text" or "text", in which case it is assumed to be the first part.
+    Function HasType(const aMimeType : string) : boolean;
+    // Get clipboard data. aMimetype has the same form as HasType. Returns the exact type.
+    // For example Get
+    function GetAsType(const aMimeType : string; out aContents : TBytes) : string;
+    procedure SetAsType(const aMimeType : string; aContents : TBytes);
+    function HasText(strict : boolean) : Boolean;
+    Property AsText : String Read GetAsText Write SetAsText;
+  end;
+  TFLClipBoard = TFresnelClipBoard;
+
+Function Clipboard : TFresnelClipBoard;
+
+implementation
+
+uses fresnel.strconsts;
+
+Function Clipboard : TFresnelClipBoard;
+
+begin
+  Result:=TFresnelClipBoard.Instance;
+end;
+
+{ TFresnelClipBoard }
+
+function TFresnelClipBoard.GetAsText: String;
+
+var
+  lContent : TBytes;
+
+begin
+  if DoGetClipboardAsType('text',lContent)<>'' then
+    Result:=TEncoding.UTF8.GetAnsiString(lContent)
+  else
+    Result:='';
+end;
+
+class destructor TFresnelClipBoard.done;
+begin
+  _instance.Free;
+end;
+
+class function TFresnelClipBoard.GetInstance: TFresnelClipBoard;
+begin
+  if _Instance=Nil then
+    begin
+    if _ClipboardClass=nil then
+      Raise EFresnelClipboard.Create(rsErrNoClipboardSupport);
+    _instance:=_ClipboardClass.Create(Nil);
+    end;
+  Result:=_instance;
+end;
+
+procedure TFresnelClipBoard.SetAsText(const aValue: String);
+var
+  lContent : TBytes;
+begin
+  lContent:=TEncoding.UTF8.GetAnsiBytes(aValue);
+  DoSetClipboardAsType('text/text',lContent);
+end;
+
+function TFresnelClipBoard.GetContentTypes: TStringDynArray;
+begin
+  DoGetContentTypes;
+end;
+
+function TFresnelClipBoard.HasType(const aMimeType: string): boolean;
+begin
+  Result:=DoHasClipboardType(aMimeType);
+end;
+
+function TFresnelClipBoard.GetAsType(const aMimeType: string; out aContents: TBytes): string;
+begin
+  Result:=DoGetClipboardAsType(aMimeType,aContents);
+end;
+
+procedure TFresnelClipBoard.SetAsType(const aMimeType: string; aContents: TBytes);
+begin
+  DoSetClipboardAsType(aMimeType,aContents);
+end;
+
+function TFresnelClipBoard.HasText(strict: boolean): Boolean;
+begin
+  if Strict then
+    Result:=DoHasClipboardType('text/text')
+  else
+    Result:=DoHasClipboardType('text')
+end;
+
+end.
+

+ 54 - 5
src/base/fresnel.edit.pp

@@ -7,7 +7,7 @@ interface
 uses
   Classes, fpImage, fpCSSResParser, fpCSSTree, fresnel.CursorTimer,
   Fresnel.Classes, Fresnel.Dom, Fresnel.Keys, Fresnel.Controls,
-  FCL.events, Fresnel.Events, Utf8Utils;
+  FCL.events, Fresnel.ClipBoard, Fresnel.Events, Utf8Utils;
 
 
 Type
@@ -62,6 +62,7 @@ Type
     procedure SetSelectionEnd(const aValue: Integer);
     procedure SetSelectionStart(const aValue: Integer);
     Procedure NormalizeSelection;
+    procedure SetSelectionText(const aValue: String);
   protected
     // char index -> X pos
     function CalcXOffset(aCharPos: Integer; aUseOffset: Boolean=true; aUseDrawText : Boolean = False): TFresnelLength;
@@ -117,7 +118,7 @@ Type
     // Max length
     Property MaxLength : Integer Read FMaxLength Write SetMaxLength;
     // Get selection
-    Property SelectionText : String Read GetSelectionText;
+    Property SelectionText : String Read GetSelectionText write SetSelectionText;
   end;
 
 
@@ -183,7 +184,9 @@ begin
   lStart:=PAnsiChar(FValue);
   lDeletePos:=UTF8CodepointStart(lStart,Length(FValue),FSelectionStart);
   lDeleteEndPos:=UTF8CodepointStart(lStart,Length(FValue),FSelectionEnd);
-  Delete(FValue,1 + (lDeletePos-lStart),lDeleteEndPos-lDeletePos);
+  UTF8Delete(FValue,1 + (lDeletePos-lStart),lDeleteEndPos-lDeletePos);
+  FSelectionEnd:=FSelectionStart;
+  FCursorPos:=FSelectionStart;
   EditParamsChanged;
 end;
 
@@ -231,10 +234,11 @@ procedure TEdit.HandleKeyDown(aEvent: TFresnelKeyEvent);
 var
   lChanged : Boolean;
   lOffset : Integer;
+  cb : TShortCutCombo;
 
 begin
   // Printable character has numkey >0
-  if (aEvent.NumKey>0) and not (aEvent.CtrlKey) then
+  if (aEvent.NumKey>0) and not (aEvent.CtrlKey or aEvent.AltKey) then
     exit;
   lChanged:=True;
   Case aEvent.NumKey of
@@ -281,7 +285,39 @@ begin
         FSelectionStart:=FCursorPos;
       end;
   else
-    lChanged:=False;
+    if (aEvent.Key='c') and (aEvent.ShiftState=[ssCtrl]) then
+      begin
+      Writeln('OK');
+      end;
+
+    case ShortCutRegistry.CheckShortCut(TShortCutCombo.Create(aEvent.NumKey,aEvent.ShiftState)) of
+      scaCopy :
+        Clipboard.AsText:=SelectionText;
+      scaCut :
+        begin
+        Clipboard.AsText:=SelectionText;
+        DoDeleteSelection;
+        end;
+      scaPaste :
+        begin
+        DoDeleteSelection;
+        SelectionText:=Clipboard.AsText;
+        end;
+      scaSelectAll :
+        begin
+        FSelectionStart:=0;
+        FSelectionEnd:=UTF8Length(FValue);
+        lChanged:=true;
+        end;
+      scaUndo :
+        // todo
+        ;
+      scaRedo :
+        // todo
+        ;
+    else
+      lChanged:=False;
+    end;
   end;
   if lChanged then
     EditParamsChanged;
@@ -750,6 +786,19 @@ begin
     end;
 end;
 
+procedure TEdit.SetSelectionText(const aValue: String);
+begin
+  DoDeleteSelection;
+  if aValue='' then
+    exit;
+  If FCursorPos<UTF8Length(FValue) then
+    UTF8Insert(aValue,FValue,FCursorPos+1)
+  else
+    FValue:=FValue+aValue;
+  FCursorPos:=FCursorPos+UTF8Length(aValue);
+  EditParamsChanged;
+end;
+
 procedure TEdit.HandleFocusChange(GotFocus: Boolean);
 
 begin

+ 180 - 2
src/base/fresnel.keys.pas

@@ -5,6 +5,8 @@ unit fresnel.keys;
 
 interface
 
+uses Classes;
+
 Type
   TKeyNames = Record
   Const
@@ -756,11 +758,58 @@ Type
     class function GetArray : TKeyCodeNameArray;
   end;
 
+  { TShortCutCombo }
+
+  TShortCutCombo = record
+    KeyCode : Integer;
+    Shift : TShiftState;
+    class operator = (a,b :TShortCutCombo) : Boolean;
+    constructor create(aKeyCode : Integer; aShift : TShiftState);
+  end;
+
+  TShortCutAction = (scaNone,scaCopy,scaCut,scaPaste,scaSelectAll,scaUndo,scaRedo);
+  TShortCutActions = set of TShortCutAction;
+
+  { TShortcutRegistry }
+
+  TShortcutRegistry = class(TObject)
+  private
+    class var _Instance: TShortcutRegistry;
+    class function GetInstance: TShortcutRegistry; static;
+  protected
+    const
+      DefaultCapacity = ord(high(TShortCutAction))+1;
+    type
+      TShortCutActionReg = record
+        Combo: TShortCutCombo;
+        Action : TShortCutAction;
+      end;
+      TShortCutActionRegArray = Array of TShortCutActionReg;
+  private
+    FList : TShortCutActionRegArray;
+    FCount : Integer;
+  protected
+    function Add(aCombo: TShortCutCombo; aAction: TShortcutAction): Integer;
+    function Delete(aCombo: TShortCutCombo): Boolean;
+    function IndexOf(aCombo: TShortCutCombo): Integer;
+    function Find(aCombo: TShortCutCombo): TShortCutAction;
+    procedure DoRegisterDefaultShortCuts; virtual;
+    procedure DoUnRegisterDefaultShortCuts; virtual;
+  Public
+    constructor Create(aCapacity: integer = DefaultCapacity);
+    procedure RegisterDefaultShortCuts;
+    procedure UnRegisterDefaultShortCuts; virtual;
+    procedure RegisterShortCut(aCombo : TShortCutCombo; aAction : TShortCutAction);
+    procedure UnRegisterShortCut(aCombo : TShortCutCombo);
+    Function CheckShortCut(aCombo : TShortCutCombo) : TShortCutAction;
+    class property Instance : TShortcutRegistry read GetInstance;
+  end;
 
 procedure EnumSpecialKeys(aCallback : TEnumKeysCallback);
 function GetSpecialKeyCount : Integer;
 function GetSpecialKeyArray : TKeyCodeNameArray;
 function GetSpecialKeyName(aCode : Integer) : String;
+function ShortCutRegistry : TShortcutRegistry;
 
 implementation
 
@@ -784,6 +833,11 @@ begin
   Result:=TKeyCodes.Names[aCode];
 end;
 
+function ShortCutRegistry: TShortcutRegistry;
+begin
+  Result:=TShortcutRegistry.Instance;
+end;
+
 procedure EnumSpecialKeys(aCallback : TEnumKeysCallback);
 
 begin
@@ -1108,8 +1162,6 @@ end;
 { TKeyCodes }
 
 // Once we have sortbase, this can disappear.
-type
-  TIntegerArray = array of Integer;
 
 procedure Swap(var a, b: TKeyCodeName); inline;
 var
@@ -1153,6 +1205,7 @@ begin
   end;
 end;
 
+
 class function TKeyCodes.indexOf(aKeyCode: Integer): Integer;
 
 var
@@ -1236,6 +1289,131 @@ begin
   Result:=_keyArray;
 end;
 
+{ TShortCutCombo }
+
+class operator TShortCutCombo.=(a, b: TShortCutCombo): Boolean;
+begin
+  Result:=(a.KeyCode=b.KeyCode) and (a.Shift=b.Shift);
+end;
+
+constructor TShortCutCombo.create(aKeyCode: Integer; aShift: TShiftState);
+begin
+  KeyCode:=aKeyCode;
+  Shift:=aShift;
+end;
+
+{ TShortcutRegistry }
+
+class function TShortcutRegistry.GetInstance: TShortcutRegistry;
+begin
+  if _Instance=nil then
+    _Instance:=TShortcutRegistry.Create;
+  Result:=_Instance;
+end;
+
+procedure TShortcutRegistry.RegisterShortCut(aCombo: TShortCutCombo; aAction: TShortCutAction);
+
+begin
+  Add(aCombo,aAction);
+end;
+
+procedure TShortcutRegistry.UnRegisterShortCut(aCombo: TShortCutCombo);
+begin
+  Delete(aCombo);
+end;
+
+function TShortcutRegistry.CheckShortCut(aCombo: TShortCutCombo): TShortCutAction;
+begin
+  Result:=Find(aCombo);
+end;
+
+function TShortCutRegistry.IndexOf(aCombo: TShortCutCombo): Integer;
+
+begin
+  Result:=FCount-1;
+  While (Result>=0) and (FList[Result].Combo<>aCombo) do
+    Dec(Result);
+end;
+
+function TShortcutRegistry.Find(aCombo: TShortCutCombo): TShortCutAction;
+var
+  Idx : Integer;
+begin
+  Result:=TShortCutAction.scaNone;
+  Idx:=IndexOf(aCombo);
+  if Idx<>-1 then
+    Result:=FList[Idx].Action;
+end;
+
+function TShortCutRegistry.Add(aCombo: TShortCutCombo; aAction: TShortcutAction): Integer;
+begin
+  Result:=IndexOf(aCombo);
+  if Result=-1 then
+    begin
+    if FCount=Length(FList) then
+      SetLength(FList,FCount+10);
+    FList[FCount].Combo:=aCombo;
+    Result:=FCount;
+    Inc(FCount);
+    end;
+  FList[Result].Action:=aAction;
+end;
+
+function TShortCutRegistry.Delete(aCombo: TShortCutCombo): Boolean;
+var
+  Idx: Integer;
+begin
+  Result:=False;
+  Idx:=IndexOf(aCombo);
+  if Idx=-1 then
+    exit;
+  While (Idx<FCount-1) do
+    FList[Idx]:=FList[FCount-1];
+  Dec(FCount);
+  Result:=True;
+end;
+
+constructor TShortCutRegistry.Create(aCapacity: integer);
+begin
+  SetLength(FList,aCapacity);
+  FCount:=0;
+  RegisterDefaultShortCuts;
+end;
+
+procedure TShortcutRegistry.RegisterDefaultShortCuts;
+begin
+  DoRegisterDefaultShortCuts;
+end;
+
+procedure TShortcutRegistry.UnRegisterDefaultShortCuts;
+
+begin
+  DoUnRegisterDefaultShortCuts;
+end;
+
+procedure TShortcutRegistry.DoRegisterDefaultShortCuts;
+
+begin
+  RegisterShortCut(TShortCutCombo.create(Ord('c'),[ssCtrl]),scaCopy);
+  RegisterShortCut(TShortCutCombo.create(Ord('x'),[ssCtrl]),scaCut);
+  RegisterShortCut(TShortCutCombo.create(Ord('v'),[ssCtrl]),scaPaste);
+  RegisterShortCut(TShortCutCombo.create(Ord('z'),[ssCtrl]),scaUndo);
+  RegisterShortCut(TShortCutCombo.create(Ord('z'),[ssCtrl,ssShift]),scaRedo);
+  RegisterShortCut(TShortCutCombo.create(TKeyCodes.Insert,[ssCtrl]),scaCopy);
+  RegisterShortCut(TShortCutCombo.create(TKeyCodes.Insert,[ssShift]),scaPaste);
+end;
+
+procedure TShortcutRegistry.DoUnRegisterDefaultShortCuts;
+
+begin
+  UnRegisterShortCut(TShortCutCombo.create(Ord('c'),[ssCtrl]));
+  UnRegisterShortCut(TShortCutCombo.create(Ord('x'),[ssCtrl]));
+  UnRegisterShortCut(TShortCutCombo.create(Ord('v'),[ssCtrl]));
+  UnRegisterShortCut(TShortCutCombo.create(Ord('z'),[ssCtrl]));
+  UnRegisterShortCut(TShortCutCombo.create(Ord('z'),[ssCtrl,ssShift]));
+  UnRegisterShortCut(TShortCutCombo.create(TKeyCodes.Insert,[ssCtrl]));
+  UnRegisterShortCut(TShortCutCombo.create(TKeyCodes.Insert,[ssShift]));
+end;
 
 end.
 

+ 1 - 0
src/base/fresnel.strconsts.pas

@@ -23,6 +23,7 @@ resourcestring
   rsFormResourceSNotFoundForResourcelessFormsCreateNew = 'Form resource %s '
     +'not found. For resourceless forms CreateNew constructor must be used.';
   rsInvalidPropertyValue = 'Invalid property value';
+  rsErrNoClipboardSupport = 'No clipboard support was registered';
 
 implementation
 

+ 5 - 0
src/base/fresnel.widgetset.pas

@@ -134,6 +134,11 @@ begin
   case aKey of
     TKeyCodes.BackSpace : aType:=fitDeleteContentBackward;
     TKeyCodes.Delete : aType:=fitDeleteContentForward;
+  else
+    if (aKey<0) or ((aShift+[ssShift])<>[ssShift]) then
+      Result:=False
+    else
+      aType:=fitInsertText
   end;
 end;
 

+ 4 - 0
src/base/fresnelbase.lpk

@@ -123,6 +123,10 @@
         <Filename Value="fresnel.cursortimer.pp"/>
         <UnitName Value="fresnel.cursortimer"/>
       </Item>
+      <Item>
+        <Filename Value="fresnel.clipboard.pp"/>
+        <UnitName Value="fresnel.clipboard"/>
+      </Item>
     </Files>
     <UsageOptions>
       <UnitPath Value="$(PkgOutDir)"/>

+ 1 - 1
src/base/fresnelbase.pas

@@ -10,7 +10,7 @@ interface
 uses
   Fresnel.Controls, Fresnel.DOM, Fresnel.Layouter, Fresnel.Renderer, FCL.Events, Fresnel.Events, Fresnel.Forms, Fresnel.WidgetSet, 
   Fresnel.Resources, Fresnel.StrConsts, Fresnel.Classes, Fresnel.Images, UTF8Utils, Fresnel.AsyncCalls, Fresnel.TextLayouter, 
-  fresnel.keys, fresnel.edit, fresnel.cursortimer;
+  fresnel.keys, Fresnel.Edit, Fresnel.CursorTimer, fresnel.clipboard;
 
 implementation
 

+ 228 - 2
src/gtk3/fresnel.gtk3.pas

@@ -21,7 +21,7 @@ uses
   {$ENDIF}
   // fresnel
   Fresnel.Classes, Fresnel.Forms, Fresnel.WidgetSet, Fresnel.DOM,
-  Fresnel.Events, FCL.Events, Fresnel.Keys;
+  Fresnel.Events, FCL.Events, Fresnel.Keys, Fresnel.ClipBoard;
 
 const
   GTK3_LEFT_BUTTON = 1;
@@ -105,6 +105,43 @@ type
     property FontEngineGtk3: TGtk3FontEngine read FFontEngine;
   end;
 
+  { TGtkClipboard }
+
+  TGtkClipboard = class(TFresnelClipBoard)
+  private
+    Type
+
+      { TFormatEnumerator }
+
+      TFormatEnumerator = class
+        Clipboard : PGtkClipboard;
+        Formats : Array of string; // atom names
+        constructor create (aClipboard : PGtkClipboard);
+      end;
+
+      { TClipboardData }
+
+      TClipboardData = class
+        Clipboard : PGtkClipboard;
+        Data : TBytes;
+        Target : String;
+        constructor create (aClipboard : PGtkClipboard;aData : TBytes; aTargetName : string);
+      end;
+  Private
+    FClipboard : PGtkClipboard;//
+
+  protected
+    function MimeTypeToAtomName(const aMimeType : string) : string;
+    function AtomNameToMimeType(const aAtom : string) : string;
+    function DoGetClipboardAsType(const aMimeType: String; out aContents: TBytes): string; override;
+    function DoGetContentTypes: TStringDynArray; override;
+    function DoHasClipboardType(const aMimeType: String): boolean; override;
+    procedure DoSetClipboardAsType(const aMimeType: String; aContents: TBytes); override;
+  public
+    constructor Create(AOwner: TComponent); override;
+  end;
+
+
 var
   Gtk3WidgetSet: TGtk3WidgetSet;
 
@@ -480,6 +517,7 @@ end;
 
 { TGtk3WSForm }
 
+
 procedure TGtk3WSForm.SetFormBounds(const AValue: TFresnelRect);
 var
   aRect: TGdkRectangle;
@@ -923,7 +961,6 @@ var
   AId: String;
 begin
   inherited Create(AOwner);
-
   Gtk3WidgetSet:=Self;
   SetExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);
   FWSFormClass:=TGtk3WSForm;
@@ -947,6 +984,7 @@ begin
 
   FFontEngine:=TGtk3FontEngine.Create(nil);
   TFresnelFontEngine.WSEngine:=FFontEngine;
+  TFresnelClipboard._ClipboardClass:=TGtkClipboard;
 end;
 
 destructor TGtk3WidgetSet.Destroy;
@@ -1017,8 +1055,196 @@ begin
   aWSForm.CreateGtkWindow;
 end;
 
+{ TGtkClipboard }
+
+function TGtkClipboard.MimeTypeToAtomName(const aMimeType: string): string;
+var
+  lMime : String;
+begin
+  lMime:=lowercase(trim(aMimeType));
+  if pos('/',lMime)=0 then
+    lMime:=lMime+'/';
+  // todo: transform
+  if (pos('text/',lMime)=1) then
+    result:='STRING'
+  else
+    Result:=aMimeType;
+
+end;
+
+function TGtkClipboard.AtomNameToMimeType(const aAtom: string): string;
+begin
+  // todo: transform
+  if aAtom='STRING' then
+    result:='text/text';
+end;
+
+function TGtkClipboard.DoGetClipboardAsType(const aMimeType: String; out aContents: TBytes): string;
+
+var
+  lAtom:PGdkAtom;
+  lName : String;
+  lData : PGtkSelectionData;
+  lDataLen : Integer;
+  lDataName : PAnsiChar;
+
+begin
+  aContents:=[];
+  lName:=MimeTypeToAtomName(aMimeType);
+  lAtom:=TGdkAtom.intern(PAnsiChar(lName), False);
+  lData:=gtk_clipboard_wait_for_contents(fClipboard,lAtom);
+  if assigned(lData) then
+    begin
+    lDataLen:=gtk_selection_data_get_length(lData);
+    lAtom:=gtk_selection_data_get_data_type(lData);
+    lDataName:=gdk_atom_name(lAtom^);
+    if lDataName<>Nil then
+      begin
+      lName:=lDataName;
+      Result:=AtomNameToMimeType(lName);
+      g_free(lDataName);
+      end
+    else
+      Result:=aMimeType;
+    SetLength(aContents,lDataLen);
+    if lDataLen>0 then
+      move(gtk_selection_data_get_data(lData)^,aContents[0],lDataLen);
+    Result:=aMimeType;
+    end;
+end;
+
+procedure targets_received(clipboard : PGtkClipboard; atoms : PGdkAtom; n_atoms : cInt; user_data: gpointer ); cdecl;
+
+var
+  I,Count : integer;
+  lName : PAnsiChar;
+  lPasName : RawByteString;
+  lEnum : TGtkClipBoard.TFormatEnumerator absolute user_data;
+
+begin
+  if lEnum.Clipboard<>clipboard then // Safety
+    exit;
+  SetLength(lEnum.Formats,n_atoms);
+  Count:=0;
+  for i:=0 to n_atoms-1 do
+    begin
+    lName:=gdk_atom_name(atoms[i]);
+    if assigned(lName) then
+      begin
+      lPasName:=lName;
+      lEnum.Formats[Count]:=lPasName;
+      g_free(lName);
+      inc(Count);
+      end;
+    end;
+  SetLength(lEnum.Formats,Count);
+end;
+
+function TGtkClipboard.DoGetContentTypes: TStringDynArray;
+
+var
+  Enum : TFormatEnumerator;
+  i,len : Integer;
+
+begin
+  Result:=[];
+  Enum:=TFormatEnumerator.Create(FClipboard);
+  try
+    gtk_clipboard_request_targets(fclipboard, @targets_received, enum);
+    Len:=Length(enum.Formats);
+    SetLength(Result,Len);
+    For I:=0 to Len-1 do
+      Result[I]:=AtomNameToMimeType(enum.Formats[i]);
+  finally
+    enum.free;
+  end;
+end;
+
+function TGtkClipboard.DoHasClipboardType(const aMimeType: String): boolean;
+var
+  lTypes : TStringDynArray;
+  i : integer;
+begin
+  LTypes:=DoGetContentTypes;
+  Result:=False;
+  I:=Length(lTypes)-1;
+  While (I>=0) and Not Result do
+    begin
+    Result:=SameText(LTypes[i],aMimeType);
+    Dec(I);
+    end;
+end;
+
+procedure GetClipboardData(clipboard: PGtkClipboard; selection_data: PGtkSelectionData; info: guint; user_data_or_owner: gpointer); cdecl;
+var
+  ldata : TGtkClipboard.TClipboardData absolute user_data_or_owner;
+
+begin
+  if lData.Clipboard<>clipboard then
+    exit;
+  gtk_selection_data_set(selection_data,gdk_atom_intern(PAnsiChar(lData.Target),False),8,PByte(lData.Data),Length(lData.Data));
+end;
+
+procedure ClearClipboardData(clipboard: PGtkClipboard; user_data_or_owner: gpointer); cdecl;
+
+var
+  ldata : TGtkClipboard.TClipboardData absolute user_data_or_owner;
+
+begin
+  if lData.Clipboard<>clipboard then
+    exit;
+  lData.Free;
+end;
+
+
+procedure TGtkClipboard.DoSetClipboardAsType(const aMimeType: String; aContents: TBytes);
+
+var
+  lTargets : PGtkTargetEntry;
+  lData : TClipboardData;
+  lAtom : PGdkAtom;
+  lTargetName : string;
+
+begin
+  lTargetName:=MimeTypeToAtomName(aMimeType);
+  lAtom:=gdk_atom_intern(pansichar(lTargetName),false);
+  lTargets:=gtk_target_entry_new(Pansichar(lTargetName),0,0);
+  lData:=TClipboardData.Create(fClipboard,aContents,lTargetName);
+  gtk_clipboard_set_with_data(fclipboard,lTargets,1,@GetClipboardData,@ClearClipboardData,lData);
+end;
+
+constructor TGtkClipboard.Create(AOwner: TComponent);
+
+const
+  sClipboard = 'CLIPBOARD';
+
+var
+  lName:PgdkAtom;
+begin
+  inherited Create(AOwner);
+  lName:=TGdkAtom.intern(sClipboard,false);
+  FClipboard:=gtk_clipboard_get(lName);
+end;
+
+{ TGtkClipboard.TFormatEnumerator }
+
+constructor TGtkClipboard.TFormatEnumerator.create(aClipboard: PGtkClipboard);
+begin
+  Clipboard:=aClipboard;
+end;
+
+{ TGtkClipboard.TClipboardData }
+
+constructor TGtkClipboard.TClipboardData.create(aClipboard: PGtkClipboard; aData: TBytes; aTargetName: string);
+begin
+  Clipboard:=aClipBoard;
+  Data:=aData;
+  Target:=aTargetName;
+end;
+
 initialization
   TGtk3WidgetSet.Create(nil);
+
 finalization
   Gtk3WidgetSet.Free; // it will nil itself