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 | |
15 | static void |
16 | copy_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 | |
68 | static void |
69 | present_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 | |
114 | static void |
115 | paste_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 | |
138 | static void |
139 | paste_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 | |
160 | static void |
161 | update_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 | |
193 | static void |
194 | source_changed_cb (GtkButton *copy_button, |
195 | GParamSpec *pspec, |
196 | GtkWidget *source_stack) |
197 | { |
198 | update_copy_button_sensitivity (source_stack); |
199 | } |
200 | |
201 | static void |
202 | text_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 | |
209 | static void |
210 | file_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 | |
217 | static void |
218 | file_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 | |
236 | static void |
237 | open_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 | |
251 | static void |
252 | update_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 | |
270 | static void |
271 | unset_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 | |
278 | static gboolean |
279 | on_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 | |
289 | static GdkContentProvider * |
290 | drag_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 | |
332 | GtkWidget * |
333 | do_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 | |