1/* Clipboard
2 *
3 * GdkClipboard is used for clipboard handling. This demo shows how to
4 * copy and paste text, images, colors or files to and from the clipboard.
5 *
6 * You can also use Drag-And-Drop to copy the data from the source to the
7 * target.
8 */
9
10#include <glib/gi18n.h>
11#include <gtk/gtk.h>
12#include <string.h>
13#include "demoimage.h"
14
15static void
16copy_button_clicked (GtkStack *source_stack,
17 gpointer user_data)
18{
19 GdkClipboard *clipboard;
20 const char *visible_child_name;
21 GtkWidget *visible_child;
22
23 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (source_stack));
24
25 visible_child = gtk_stack_get_visible_child (stack: source_stack);
26 visible_child_name = gtk_stack_get_visible_child_name (stack: source_stack);
27
28 if (strcmp (s1: visible_child_name, s2: "Text") == 0)
29 {
30 gdk_clipboard_set_text (clipboard, text: gtk_editable_get_text (GTK_EDITABLE (visible_child)));
31 }
32 else if (strcmp (s1: visible_child_name, s2: "Image") == 0)
33 {
34 GtkWidget *child;
35
36 for (child = gtk_widget_get_first_child (widget: visible_child); child; child = gtk_widget_get_next_sibling (widget: child))
37 {
38 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (child)))
39 {
40 GtkWidget *image = gtk_widget_get_first_child (widget: child);
41 GdkPaintable *paintable = gtk_image_get_paintable (GTK_IMAGE (image));
42
43 if (GDK_IS_TEXTURE (paintable))
44 gdk_clipboard_set (clipboard, GDK_TYPE_TEXTURE, paintable);
45 else
46 gdk_clipboard_set (clipboard, GDK_TYPE_PAINTABLE, paintable);
47 break;
48 }
49 }
50 }
51 else if (strcmp (s1: visible_child_name, s2: "Color") == 0)
52 {
53 GdkRGBA color;
54
55 gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (visible_child), color: &color);
56 gdk_clipboard_set (clipboard, GDK_TYPE_RGBA, &color);
57 }
58 else if (strcmp (s1: visible_child_name, s2: "File") == 0)
59 {
60 gdk_clipboard_set (clipboard, G_TYPE_FILE, g_object_get_data (G_OBJECT (visible_child), key: "file"), NULL);
61 }
62 else
63 {
64 g_print (format: "TODO");
65 }
66}
67
68static void
69present_value (GtkStack *dest_stack,
70 const GValue *value)
71{
72 GtkWidget *child;
73
74 if (G_VALUE_HOLDS (value, G_TYPE_FILE))
75 {
76 GFile *file;
77
78 gtk_stack_set_visible_child_name (stack: dest_stack, name: "File");
79 child = gtk_stack_get_visible_child (stack: dest_stack);
80
81 file = g_value_get_object (value);
82 g_object_set (object: child, first_property_name: "label", g_file_peek_path (file), NULL);
83 }
84 else if (G_VALUE_HOLDS (value, GDK_TYPE_RGBA))
85 {
86 GdkRGBA *color;
87
88 gtk_stack_set_visible_child_name (stack: dest_stack, name: "Color");
89 child = gtk_widget_get_first_child (widget: gtk_stack_get_visible_child (stack: dest_stack));
90
91 color = g_value_get_boxed (value);
92 g_object_set (object: child, first_property_name: "rgba", color, NULL);
93 }
94 else if (G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE) ||
95 G_VALUE_HOLDS (value, GDK_TYPE_PAINTABLE))
96 {
97 GdkPaintable *paintable;
98
99 gtk_stack_set_visible_child_name (stack: dest_stack, name: "Image");
100 child = gtk_stack_get_visible_child (stack: dest_stack);
101
102 paintable = g_value_get_object (value);
103 g_object_set (object: child, first_property_name: "paintable", paintable, NULL);
104 }
105 else if (G_VALUE_HOLDS (value, G_TYPE_STRING))
106 {
107 gtk_stack_set_visible_child_name (stack: dest_stack, name: "Text");
108 child = gtk_stack_get_visible_child (stack: dest_stack);
109
110 gtk_label_set_label (GTK_LABEL (child), str: g_value_get_string (value));
111 }
112}
113
114static void
115paste_received (GObject *source_object,
116 GAsyncResult *result,
117 gpointer user_data)
118{
119 GtkStack *dest_stack = user_data;
120 GdkClipboard *clipboard;
121 const GValue *value;
122 GError *error = NULL;
123
124 clipboard = GDK_CLIPBOARD (source_object);
125
126 value = gdk_clipboard_read_value_finish (clipboard, result, error: &error);
127 if (value)
128 {
129 present_value (dest_stack, value);
130 }
131 else
132 {
133 g_print (format: "%s\n", error->message);
134 g_error_free (error);
135 }
136}
137
138static void
139paste_button_clicked (GtkStack *dest_stack,
140 gpointer user_data)
141{
142 GdkClipboard *clipboard;
143 GdkContentFormats *formats;
144
145 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (dest_stack));
146 formats = gdk_clipboard_get_formats (clipboard);
147
148 if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_TEXTURE))
149 gdk_clipboard_read_value_async (clipboard, GDK_TYPE_TEXTURE, io_priority: 0, NULL, callback: paste_received, user_data: dest_stack);
150 else if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_PAINTABLE))
151 gdk_clipboard_read_value_async (clipboard, GDK_TYPE_PAINTABLE, io_priority: 0, NULL, callback: paste_received, user_data: dest_stack);
152 else if (gdk_content_formats_contain_gtype (formats, GDK_TYPE_RGBA))
153 gdk_clipboard_read_value_async (clipboard, GDK_TYPE_RGBA, io_priority: 0, NULL, callback: paste_received, user_data: dest_stack);
154 else if (gdk_content_formats_contain_gtype (formats, G_TYPE_FILE))
155 gdk_clipboard_read_value_async (clipboard, G_TYPE_FILE, io_priority: 0, NULL, callback: paste_received, user_data: dest_stack);
156 else if (gdk_content_formats_contain_gtype (formats, G_TYPE_STRING))
157 gdk_clipboard_read_value_async (clipboard, G_TYPE_STRING, io_priority: 0, NULL, callback: paste_received, user_data: dest_stack);
158}
159
160static void
161update_copy_button_sensitivity (GtkWidget *source_stack)
162{
163 GtkButton *copy_button;
164 const char *visible_child_name;
165 GtkWidget *visible_child;
166 gboolean sensitive;
167
168 copy_button = GTK_BUTTON (g_object_get_data (G_OBJECT (source_stack), "copy-button"));
169
170 visible_child = gtk_stack_get_visible_child (GTK_STACK (source_stack));
171 visible_child_name = gtk_stack_get_visible_child_name (GTK_STACK (source_stack));
172 if (strcmp (s1: visible_child_name, s2: "Text") == 0)
173 {
174 sensitive = strlen (s: gtk_editable_get_text (GTK_EDITABLE (visible_child))) > 0;
175 }
176 else if (strcmp (s1: visible_child_name, s2: "Color") == 0 ||
177 strcmp (s1: visible_child_name, s2: "Image") == 0)
178 {
179 sensitive = TRUE;
180 }
181 else if (strcmp (s1: visible_child_name, s2: "File") == 0)
182 {
183 sensitive = g_object_get_data (G_OBJECT (visible_child), key: "file") != NULL;
184 }
185 else
186 {
187 sensitive = FALSE;
188 }
189
190 gtk_widget_set_sensitive (GTK_WIDGET (copy_button), sensitive);
191}
192
193static void
194source_changed_cb (GtkButton *copy_button,
195 GParamSpec *pspec,
196 GtkWidget *source_stack)
197{
198 update_copy_button_sensitivity (source_stack);
199}
200
201static void
202text_changed_cb (GtkButton *copy_button,
203 GParamSpec *pspec,
204 GtkWidget *entry)
205{
206 update_copy_button_sensitivity (source_stack: gtk_widget_get_ancestor (widget: entry, GTK_TYPE_STACK));
207}
208
209static void
210file_button_set_file (GtkButton *button,
211 GFile *file)
212{
213 gtk_label_set_label (GTK_LABEL (gtk_button_get_child (button)), str: g_file_peek_path (file));
214 g_object_set_data_full (G_OBJECT (button), key: "file", g_object_ref (file), destroy: g_object_unref);
215}
216
217static void
218file_chooser_response (GtkNativeDialog *dialog,
219 int response,
220 GtkButton *button)
221{
222 gtk_native_dialog_hide (self: dialog);
223
224 if (response == GTK_RESPONSE_ACCEPT)
225 {
226 GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
227 file_button_set_file (button, file);
228 g_object_unref (object: file);
229
230 update_copy_button_sensitivity (source_stack: gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_STACK));
231 }
232
233 gtk_native_dialog_destroy (self: dialog);
234}
235
236static void
237open_file_cb (GtkWidget *button)
238{
239 GtkFileChooserNative *chooser;
240
241 chooser = gtk_file_chooser_native_new (title: "Choose a file",
242 GTK_WINDOW (gtk_widget_get_ancestor (button, GTK_TYPE_WINDOW)),
243 action: GTK_FILE_CHOOSER_ACTION_OPEN,
244 accept_label: "_Open",
245 cancel_label: "_Cancel");
246
247 g_signal_connect (chooser, "response", G_CALLBACK (file_chooser_response), button);
248 gtk_native_dialog_show (self: GTK_NATIVE_DIALOG (ptr: chooser));
249}
250
251static void
252update_paste_button_sensitivity (GdkClipboard *clipboard,
253 GtkWidget *paste_button)
254{
255 GdkContentFormats *formats;
256 gboolean sensitive = FALSE;
257
258 formats = gdk_clipboard_get_formats (clipboard);
259
260 if (gdk_content_formats_contain_gtype (formats, G_TYPE_FILE) ||
261 gdk_content_formats_contain_gtype (formats, GDK_TYPE_RGBA) ||
262 gdk_content_formats_contain_gtype (formats, GDK_TYPE_TEXTURE) ||
263 gdk_content_formats_contain_gtype (formats, GDK_TYPE_PAINTABLE) ||
264 gdk_content_formats_contain_gtype (formats, G_TYPE_STRING))
265 sensitive = TRUE;
266
267 gtk_widget_set_sensitive (widget: paste_button, sensitive);
268}
269
270static void
271unset_clipboard_handler (gpointer data)
272{
273 GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (data));
274
275 g_signal_handlers_disconnect_by_func (clipboard, update_paste_button_sensitivity, data);
276}
277
278static gboolean
279on_drop (GtkStack *dest_stack,
280 const GValue *value,
281 double x,
282 double y,
283 gpointer data)
284{
285 present_value (dest_stack, value);
286 return TRUE;
287}
288
289static GdkContentProvider *
290drag_prepare (GtkDragSource *drag_source,
291 double x,
292 double y,
293 gpointer data)
294{
295 GtkWidget *button;
296 GValue value = G_VALUE_INIT;
297
298 button = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag_source));
299
300 if (GTK_IS_TOGGLE_BUTTON (button))
301 {
302 GtkWidget *image = gtk_widget_get_first_child (widget: button);
303 GdkPaintable *paintable = gtk_image_get_paintable (GTK_IMAGE (image));
304
305 if (GDK_IS_TEXTURE (paintable))
306 {
307 g_value_init (value: &value, GDK_TYPE_TEXTURE);
308 g_value_set_object (value: &value, v_object: paintable);
309 }
310 else
311 {
312 g_value_init (value: &value, GDK_TYPE_PAINTABLE);
313 g_value_set_object (value: &value, v_object: paintable);
314 }
315 }
316 else
317 {
318 GFile *file = g_object_get_data (G_OBJECT (button), key: "file");
319
320 if (file)
321 {
322 g_value_init (value: &value, G_TYPE_FILE);
323 g_value_set_object (value: &value, v_object: file);
324 }
325 else
326 return NULL;
327 }
328
329 return gdk_content_provider_new_for_value (value: &value);
330}
331
332GtkWidget *
333do_clipboard (GtkWidget *do_widget)
334{
335 static GtkWidget *window = NULL;
336
337 if (!window)
338 {
339 GtkBuilderScope *scope;
340 GtkBuilder *builder;
341 GtkWidget *button;
342
343 scope = gtk_builder_cscope_new ();
344 gtk_builder_cscope_add_callback_symbols (self: GTK_BUILDER_CSCOPE (ptr: scope),
345 first_callback_name: "copy_button_clicked", G_CALLBACK (copy_button_clicked),
346 "paste_button_clicked", G_CALLBACK (paste_button_clicked),
347 "source_changed_cb", G_CALLBACK (source_changed_cb),
348 "text_changed_cb", G_CALLBACK (text_changed_cb),
349 "open_file_cb", G_CALLBACK (open_file_cb),
350 "on_drop", G_CALLBACK (on_drop),
351 "drag_prepare", G_CALLBACK (drag_prepare),
352 NULL);
353
354 builder = gtk_builder_new ();
355 gtk_builder_set_scope (builder, scope);
356 gtk_builder_add_from_resource (builder, resource_path: "/clipboard/clipboard.ui", NULL);
357
358 window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
359 g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window);
360 gtk_window_set_display (GTK_WINDOW (window), display: gtk_widget_get_display (widget: do_widget));
361
362 button = GTK_WIDGET (gtk_builder_get_object (builder, "copy_button"));
363 g_object_set_data (object: gtk_builder_get_object (builder, name: "source_stack"), key: "copy-button", data: button);
364
365 button = GTK_WIDGET (gtk_builder_get_object (builder, "paste_button"));
366 g_signal_connect (gtk_widget_get_clipboard (button), "changed",
367 G_CALLBACK (update_paste_button_sensitivity), button);
368 g_object_set_data_full (G_OBJECT (button), key: "clipboard-handler", data: button, destroy: unset_clipboard_handler);
369
370 g_object_unref (object: builder);
371 g_object_unref (object: scope);
372 }
373
374 if (!gtk_widget_get_visible (widget: window))
375 gtk_widget_show (widget: window);
376 else
377 gtk_window_destroy (GTK_WINDOW (window));
378
379 return window;
380}
381

source code of gtk/demos/gtk-demo/clipboard.c