1414package  org .eclipse .swt .dnd ;
1515
1616
17+ import  java .time .*;
18+ import  java .util .*;
19+ import  java .util .List ;
1720import  java .util .concurrent .*;
1821
1922import  org .eclipse .swt .*;
20- import  org .eclipse .swt .graphics .*;
2123import  org .eclipse .swt .internal .*;
2224import  org .eclipse .swt .internal .gtk .*;
2325import  org .eclipse .swt .internal .gtk3 .*;
@@ -41,15 +43,24 @@ public class Clipboard {
4143
4244	static  long  GTKCLIPBOARD ;
4345	static  long  GTKPRIMARYCLIPBOARD ;
46+ 	/** 
47+ 	 * GTK3 only 
48+ 	 */ 
4449	private  static  long  TARGET ;
4550
4651	static  {
47- 		GTKCLIPBOARD  = GTK .GTK4  ? GDK .gdk_display_get_clipboard (GDK .gdk_display_get_default ()) : GTK3 .gtk_clipboard_get  (GDK .GDK_NONE );
48- 		byte [] buffer  = Converter .wcsToMbcs ("PRIMARY" , true );
49- 		long  primary  = GTK .GTK4  ? 0  : GDK .gdk_atom_intern (buffer , false );
50- 		GTKPRIMARYCLIPBOARD  = GTK .GTK4  ? GDK .gdk_display_get_primary_clipboard (GDK .gdk_display_get_default ()) : GTK3 .gtk_clipboard_get (primary );
51- 		buffer  = Converter .wcsToMbcs ("TARGETS" , true );
52- 		TARGET  = GTK .GTK4  ? 0  : GDK .gdk_atom_intern (buffer , false );
52+ 		if  (GTK .GTK4 ) {
53+ 			GTKCLIPBOARD  = GDK .gdk_display_get_clipboard (GDK .gdk_display_get_default ());
54+ 			GTKPRIMARYCLIPBOARD  = GDK .gdk_display_get_primary_clipboard (GDK .gdk_display_get_default ());
55+ 			TARGET  = 0 ;
56+ 		} else  {
57+ 			GTKCLIPBOARD  = GTK3 .gtk_clipboard_get (GDK .GDK_NONE );
58+ 			byte [] buffer  = Converter .wcsToMbcs ("PRIMARY" , true );
59+ 			long  primary  = GDK .gdk_atom_intern (buffer , false );
60+ 			GTKPRIMARYCLIPBOARD  = GTK3 .gtk_clipboard_get (primary );
61+ 			buffer  = Converter .wcsToMbcs ("TARGETS" , true );
62+ 			TARGET  = GDK .gdk_atom_intern (buffer , false );
63+ 		}
5364	}
5465
5566/** 
@@ -190,8 +201,13 @@ public void clearContents() {
190201 */ 
191202public  void  clearContents (int  clipboards ) {
192203	checkWidget ();
193- 	ClipboardProxy  proxy  = ClipboardProxy ._getInstance (display );
194- 	proxy .clear (this , clipboards );
204+ 	if  (GTK .GTK4 ) {
205+ 		ClipboardProxyGTK4  proxy  = ClipboardProxyGTK4 ._getInstance (display );
206+ 		proxy .clear (this , clipboards , false );
207+ 	} else  {
208+ 		ClipboardProxy  proxy  = ClipboardProxy ._getInstance (display );
209+ 		proxy .clear (this , clipboards );
210+ 	}
195211}
196212
197213/** 
@@ -302,24 +318,40 @@ public Object getContents(Transfer transfer) {
302318 * @since 3.1 
303319 */ 
304320public  Object  getContents (Transfer  transfer , int  clipboards ) {
305- 	checkWidget ();
306- 	if  (transfer  == null ) DND .error (SWT .ERROR_NULL_ARGUMENT );
307- 
308- 	if (GTK .GTK4 ) {
309- 		Object  result  = getContents_gtk4 (transfer , clipboards );
310- 		return  result ;
321+ 	if  (GTK .GTK4 ) {
322+ 		CompletableFuture <Object > contentsAsync  = gtk4_getContentsAsync (transfer , clipboards );
323+ 		try  {
324+ 			// We limit how long the clipboard has to respond to 5 seconds 
325+ 			// based on similar decisions in GTK3, see 
326+ 			// https://github.com/eclipse-platform/eclipse.platform.swt/blob/d2de8e31f846d655949e81fa006ad2ebcc542b2f/bundles/org.eclipse.swt/Eclipse%20SWT%20Drag%20and%20Drop/gtk/org/eclipse/swt/dnd/Clipboard.java#L719 
327+ 			return  GTK4GlibFuture .get (display , contentsAsync , Duration .ofSeconds (5 ));
328+ 		} catch  (InterruptedException  e ) {
329+ 			Thread .currentThread ().interrupt ();
330+ 			return  null ;
331+ 		} catch  (ExecutionException  | TimeoutException  e ) {
332+ 			// Bug 241957: In case of timeout take clipboard ownership to unblock future calls 
333+ 			ClipboardProxyGTK4  proxy  = ClipboardProxyGTK4 ._getInstance (display );
334+ 			proxy .clear (this , clipboards , true );
335+ 			return  null ;
336+ 		}
311337	}
312338
339+ 	return  gtk3_getContents (transfer , clipboards );
340+ }
341+ 
342+ private  Object  gtk3_getContents (Transfer  transfer , int  clipboards ) {
343+ 	checkWidget ();
344+ 	if  (transfer  == null ) DND .error (SWT .ERROR_NULL_ARGUMENT );
313345	long  selection_data  = 0 ;
314346	int [] typeIds  = transfer .getTypeIds ();
315347	boolean  textTransfer  = transfer .getTypeNames ()[0 ].equals ("UTF8_STRING" );
316348	Object  result  = null ;
317349	for  (int  i  = 0 ; i  < typeIds .length ; i ++) {
318350		if  ((clipboards  & DND .CLIPBOARD ) != 0 ) {
319- 			selection_data  = gtk_clipboard_wait_for_contents (GTKCLIPBOARD , typeIds [i ]);
351+ 			selection_data  = gtk3_clipboard_wait_for_contents (GTKCLIPBOARD , typeIds [i ]);
320352		}
321353		if  (selection_data  == 0  && (clipboards  & DND .SELECTION_CLIPBOARD ) != 0 ) {
322- 			selection_data  = gtk_clipboard_wait_for_contents (GTKPRIMARYCLIPBOARD , typeIds [i ]);
354+ 			selection_data  = gtk3_clipboard_wait_for_contents (GTKPRIMARYCLIPBOARD , typeIds [i ]);
323355		}
324356		if  (selection_data  != 0 ) {
325357			TransferData  tdata  = new  TransferData ();
@@ -419,69 +451,20 @@ public CompletableFuture<Object> getContentsAsync(Transfer transfer) {
419451 * @since 3.132 
420452 */ 
421453public  CompletableFuture <Object > getContentsAsync (Transfer  transfer , int  clipboards ) {
422- 	return  CompletableFuture .completedFuture (getContents (transfer , clipboards ));
454+ 	if  (GTK .GTK4 ) {
455+ 		return  gtk4_getContentsAsync (transfer , clipboards );
456+ 	}
457+ 
458+ 	return  CompletableFuture .completedFuture (gtk3_getContents (transfer , clipboards ));
423459}
424460
425- private  Object  getContents_gtk4 (Transfer  transfer , int  clipboards ) {
426- 
427- 	long  contents  = GTK4 .gdk_clipboard_get_content (Clipboard .GTKCLIPBOARD );
428- 	if (contents  == 0 ) return  null ;
429- 	long  value  = OS .g_malloc  (OS .GValue_sizeof  ());
430- 	C .memset  (value , 0 , OS .GValue_sizeof  ());
431- 
432- 	//Pasting of text (TextTransfer/RTFTransfer) 
433- 	if (transfer .getTypeNames ()[0 ].equals ("text/plain" ) || transfer .getTypeNames ()[0 ].equals ("text/rtf" )) {
434- 		OS .g_value_init (value , OS .G_TYPE_STRING ());
435- 		if  (!GTK4 .gdk_content_provider_get_value  (contents , value , null )) return  null ;
436- 		long  cStr  = OS .g_value_get_string (value );
437- 		long  [] items_written  = new  long  [1 ];
438- 		long  utf16Ptr  = OS .g_utf8_to_utf16 (cStr , -1 , null , items_written , null );
439- 		OS .g_free (cStr );
440- 		if  (utf16Ptr  == 0 ) return  null ;
441- 		int  length  = (int )items_written [0 ];
442- 		char [] buffer  = new  char [length ];
443- 		C .memmove (buffer , utf16Ptr , length  * 2 );
444- 		OS .g_free (utf16Ptr );
445- 		String  str  = new  String (buffer );
446- 		if (transfer .getTypeNames ()[0 ].equals ("text/rtf" ) && !str .contains ("{\\ rtf1" )) {
447- 			return  null ;
448- 		}
449- 		if (transfer .getTypeNames ()[0 ].equals ("text/plain" ) && str .contains ("{\\ rtf1" )){
450- 			return  null ;
451- 		}
452- 		return  str ;
453- 	}
454- 	//Pasting of Image 
455- 	if (transfer .getTypeIds ()[0 ] == (int )GDK .GDK_TYPE_PIXBUF ()) {
456- 		ImageData  imgData  = null ;
457- 		OS .g_value_init (value , GDK .GDK_TYPE_PIXBUF ());
458- 		if  (!GTK4 .gdk_content_provider_get_value  (contents , value , null )) return  null ;
459- 		long  pixbufObj  = OS .g_value_get_object (value );
460- 		if  (pixbufObj  != 0 ) {
461- 			Image  img  = Image .gtk_new_from_pixbuf (Display .getCurrent (), SWT .BITMAP , pixbufObj );
462- 			imgData  = img .getImageData ();
463- 			img .dispose ();
464- 		}
465- 		return  imgData ;
466- 	}
467- 	//Pasting of HTML 
468- 	if (transfer .getTypeNames ()[0 ].equals ("text/html" )) {
469- 		OS .g_value_init (value , OS .G_TYPE_STRING ());
470- 		if  (!GTK4 .gdk_content_provider_get_value  (contents , value , null )) return  null ;
471- 		long  cStr  = OS .g_value_get_string (value );
472- 		long  [] items_written  = new  long  [1 ];
473- 		long  utf16Ptr  = OS .g_utf8_to_utf16 (cStr , -1 , null , items_written , null );
474- 		OS .g_free (cStr );
475- 		if  (utf16Ptr  == 0 ) return  null ;
476- 		int  length  = (int )items_written [0 ];
477- 		char [] buffer  = new  char [length ];
478- 		C .memmove (buffer , utf16Ptr , length  * 2 );
479- 		OS .g_free (utf16Ptr );
480- 		String  str  = new  String (buffer );
481- 		return  str ;
482- 	}
483- 	//TODO: [GTK4] Other cases 
484- 	return  null ;
461+ private  CompletableFuture <Object > gtk4_getContentsAsync (Transfer  transfer , int  clipboards ) {
462+ 	checkWidget ();
463+ 	if  (transfer  == null )
464+ 		DND .error (SWT .ERROR_NULL_ARGUMENT );
465+ 
466+ 	ClipboardProxyGTK4  proxy  = ClipboardProxyGTK4 ._getInstance (display );
467+ 	return  proxy .getData (this , transfer , clipboards );
485468}
486469
487470/** 
@@ -621,9 +604,16 @@ public void setContents(Object[] data, Transfer[] dataTypes, int clipboards) {
621604			DND .error (SWT .ERROR_INVALID_ARGUMENT );
622605		}
623606	}
624- 	ClipboardProxy  proxy  = ClipboardProxy ._getInstance (display );
625- 	if  (!proxy .setData (this , data , dataTypes , clipboards )) {
626- 		DND .error (DND .ERROR_CANNOT_SET_CLIPBOARD );
607+ 	if  (GTK .GTK4 ) {
608+ 		ClipboardProxyGTK4  proxy  = ClipboardProxyGTK4 ._getInstance (display );
609+ 		if  (!proxy .setData (this , data , dataTypes , clipboards )) {
610+ 			DND .error (DND .ERROR_CANNOT_SET_CLIPBOARD );
611+ 		}
612+ 	} else  {
613+ 		ClipboardProxy  proxy  = ClipboardProxy ._getInstance (display );
614+ 		if  (!proxy .setData (this , data , dataTypes , clipboards )) {
615+ 			DND .error (DND .ERROR_CANNOT_SET_CLIPBOARD );
616+ 		}
627617	}
628618}
629619
@@ -671,18 +661,21 @@ public TransferData[] getAvailableTypes() {
671661 */ 
672662public  TransferData [] getAvailableTypes (int  clipboards ) {
673663	checkWidget ();
664+ 	if (GTK .GTK4 ) {
665+ 		return  gtk4_getAvailableTypes (clipboards );
666+ 	}
674667
675668	TransferData [] result  = null ;
676669	if  ((clipboards  & DND .CLIPBOARD ) != 0 ) {
677- 		int [] types  = getAvailableClipboardTypes ( );
670+ 		int [] types  = gtk3_getAvailableTypes ( GTKCLIPBOARD );
678671		result  = new  TransferData [types .length ];
679672		for  (int  i  = 0 ; i  < types .length ; i ++) {
680673			result [i ] = new  TransferData ();
681674			result [i ].type  = types [i ];
682675		}
683676	}
684677	if  ((clipboards  & DND .SELECTION_CLIPBOARD ) != 0 ) {
685- 		int [] types  = getAvailablePrimaryTypes ( );
678+ 		int [] types  = gtk3_getAvailableTypes ( GTKPRIMARYCLIPBOARD );
686679		int  offset  = 0 ;
687680		if  (result  != null ) {
688681			TransferData [] newResult  = new  TransferData [result .length  + types .length ];
@@ -719,13 +712,10 @@ public TransferData[] getAvailableTypes(int clipboards) {
719712public  String [] getAvailableTypeNames () {
720713	checkWidget ();
721714	if (GTK .GTK4 ) {
722- 		long  formatsCStr  = GTK4 .gdk_content_formats_to_string (GTK4 .gdk_clipboard_get_formats (Clipboard .GTKCLIPBOARD ));
723- 		String  formatsStr  = Converter .cCharPtrToJavaString (formatsCStr , true );
724- 		String [] types  = formatsStr .split (" " );
725- 		return  types ;
715+ 		return  gtk4_getAvailableTypeNames ();
726716	}
727- 	int [] types1  = getAvailableClipboardTypes ( );
728- 	int [] types2  = getAvailablePrimaryTypes ( );
717+ 	int [] types1  = gtk3_getAvailableTypes ( GTKCLIPBOARD );
718+ 	int [] types2  = gtk3_getAvailableTypes ( GTKPRIMARYCLIPBOARD );
729719	String [] result  = new  String [types1 .length  + types2 .length ];
730720	int  count  = 0 ;
731721	for  (int  i  = 0 ; i  < types1 .length ; i ++) {
@@ -756,37 +746,90 @@ public String[] getAvailableTypeNames() {
756746	return  result ;
757747}
758748
759- private   int [] getAvailablePrimaryTypes () {
760- 	if  (GTK .GTK4 ) {
761- 		return  gtk4_getAvailableTypes (GTKPRIMARYCLIPBOARD );
762- 	}
763- 	return  gtk3_getAvailableTypes (GTKPRIMARYCLIPBOARD );
749+ private  TransferData [] gtk4_getAvailableTypes (int  clipboards ) {
750+ 	/* 
751+ 	 * The first time we see a type name (mime type) may be when asking the 
752+ 	 * clipboard. The user may not load the specific Transfer class until after 
753+ 	 * getting the available types. 
754+ 	 * 
755+ 	 * Therefore we need to register any type names we see with the transfer system. 
756+ 	 */ 
757+ 	return  Arrays .stream (gtk4_getAvailableTypeNames (clipboards )).map (Transfer ::registerType ).map ((type ) -> {
758+ 		TransferData  transferData  = new  TransferData ();
759+ 		transferData .type  = type ;
760+ 		return  transferData ;
761+ 	}).toArray (TransferData []::new );
764762}
765- private  int [] getAvailableClipboardTypes  () {
766- 	if  (GTK .GTK4 ) {
767- 		return  gtk4_getAvailableTypes (GTKCLIPBOARD );
768- 	}
769- 	return  gtk3_getAvailableTypes (GTKCLIPBOARD );
763+ 
764+ private  String [] gtk4_getAvailableTypeNames () {
765+ 	Set <String > all  = new  LinkedHashSet <>();
766+ 	Arrays .stream (gtk4_getAvailableTypeNames (DND .CLIPBOARD )).map (n  -> "GTKCLIPBOARD " + n ).forEachOrdered (all ::add );
767+ 	Arrays .stream (gtk4_getAvailableTypeNames (DND .SELECTION_CLIPBOARD )).map (n  -> "GTKPRIMARYCLIPBOARD " + n ).forEachOrdered (all ::add );
768+ 	return  all .toArray (String []::new );
770769}
771770
772- private  int [] gtk4_getAvailableTypes (long  clipboard ) {
773- 	long  formats  = GTK4 .gdk_clipboard_get_formats (clipboard );
774- 	long [] n_gtypes  = new  long [1 ];
775- 	long  gtypes  = GTK4 .gdk_content_formats_get_gtypes (formats , n_gtypes );
776- 
777- 	int  gtypes_length  = (int ) n_gtypes [0 ];
778- 	int [] types  = new  int [gtypes_length ];
779- 	for  (int  i  = 0  ; i  < gtypes_length  ; ++i ) {
780- 		long [] ptr  = new  long [1 ];
781- 		C .memmove (ptr , gtypes  + i  * C .PTR_SIZEOF , C .PTR_SIZEOF );
782- 		types [i ] = (int ) ptr [0 ];
771+ private  String [] gtk4_getAvailableTypeNames (int  clipboards ) {
772+ 	long  gtkClipboard  = ((clipboards  & DND .CLIPBOARD ) != 0 ) ? Clipboard .GTKCLIPBOARD  : Clipboard .GTKPRIMARYCLIPBOARD ;
773+ 
774+ 	List <String > names  = new  ArrayList <>();
775+ 
776+ 	long  formats  = GTK4 .gdk_clipboard_get_formats (gtkClipboard );
777+ 
778+ 	{
779+ 		long [] n_gtypes  = new  long [1 ];
780+ 		long  gtypes  = GTK4 .gdk_content_formats_get_gtypes (formats , n_gtypes );
781+ 		if  (gtypes  != 0 ) {
782+ 			int  gtypes_length  = (int ) n_gtypes [0 ];
783+ 
784+ 			for  (int  i  = 0 ; i  < gtypes_length ; i ++) {
785+ 				long [] ptr  = new  long [1 ];
786+ 				C .memmove (ptr , gtypes  + i  * C .PTR_SIZEOF , C .PTR_SIZEOF );
787+ 				long  gtype  = ptr [0 ];
788+ 				if  (gtype  == 0 ) {
789+ 					// End reached of null terminated array 
790+ 					break ;
791+ 				}
792+ 				if  (gtype  != OS .G_TYPE_INVALID ()) {
793+ 					long  g_type_name  = OS .g_type_name (gtype );
794+ 					if  (g_type_name  == 0 ) {
795+ 						continue ;
796+ 					}
797+ 					String  gtypeName  = Converter .cCharPtrToJavaString (g_type_name , false );
798+ 					names .add (gtypeName );
799+ 				}
800+ 			}
801+ 		}
802+ 	}
803+ 
804+ 	{
805+ 		long [] n_mime_types  = new  long [1 ];
806+ 		long  mime_types  = GTK4 .gdk_content_formats_get_mime_types (formats , n_mime_types );
807+ 		if  (mime_types  != 0 ) {
808+ 			int  mime_types_length  = (int ) n_mime_types [0 ];
809+ 
810+ 			for  (int  i  = 0 ; i  < mime_types_length ; i ++) {
811+ 				long [] ptr  = new  long [1 ];
812+ 				C .memmove (ptr , mime_types  + i  * C .PTR_SIZEOF , C .PTR_SIZEOF );
813+ 				long  mime_type  = ptr [0 ];
814+ 				if  (mime_type  == 0 ) {
815+ 					// End reached of null terminated array 
816+ 					break ;
817+ 				}
818+ 
819+ 				String  typeName  = Converter .cCharPtrToJavaString (mime_type , false );
820+ 				if  (!typeName .isBlank ()) {
821+ 					names .add (typeName );
822+ 				}
823+ 			}
824+ 		}
783825	}
784- 	return  types ;
826+ 
827+ 	return  names .toArray (String []::new );
785828}
786829
787830private  int [] gtk3_getAvailableTypes (long  clipboard ) {
788831	int [] types  = new  int [0 ];
789- 	long  selection_data  = gtk_clipboard_wait_for_contents (clipboard , TARGET );
832+ 	long  selection_data  = gtk3_clipboard_wait_for_contents (clipboard , TARGET );
790833	if  (selection_data  != 0 ) {
791834		try  {
792835			int  length  = GTK3 .gtk_selection_data_get_length (selection_data );
@@ -803,7 +846,7 @@ private int[] gtk3_getAvailableTypes(long clipboard) {
803846	return  types ;
804847}
805848
806- long  gtk_clipboard_wait_for_contents (long  clipboard , long  target ) {
849+ private   long  gtk3_clipboard_wait_for_contents (long  clipboard , long  target ) {
807850	long  startTime  = System .currentTimeMillis ();
808851	String  key  = "org.eclipse.swt.internal.gtk.dispatchEvent" ;
809852	Display  display  = this .display ;
0 commit comments