1 /*
2 * Copyright (c) 2008-2012 Mike Massonnet <mmassonnet@xfce.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <gtk/gtk.h>
24 #include <libxfce4ui/libxfce4ui.h>
25 #include <X11/Xlib.h>
26 #include <X11/extensions/XTest.h>
27 #include <X11/keysym.h>
28
29 #ifdef HAVE_QRENCODE
30 #include <qrencode.h>
31 #endif
32
33 #include "common.h"
34 #include "collector.h"
35 #include "history.h"
36
37 #include "menu.h"
38
39 /*
40 * GObject declarations
41 */
42
43 struct _ClipmanMenuPrivate
44 {
45 GtkWidget *mi_clear_history;
46 ClipmanHistory *history;
47 GSList *list;
48 gboolean reverse_order;
49 #ifdef HAVE_QRENCODE
50 gboolean show_qr_code;
51 #endif
52 guint paste_on_activate;
53 guint max_menu_items;
54 gboolean never_confirm_history_clear;
55 };
56
57 G_DEFINE_TYPE_WITH_PRIVATE (ClipmanMenu, clipman_menu, GTK_TYPE_MENU)
58
59 enum
60 {
61 REVERSE_ORDER = 1,
62 #ifdef HAVE_QRENCODE
63 SHOW_QR_CODE,
64 #endif
65 PASTE_ON_ACTIVATE,
66 NEVER_CONFIRM_HISTORY_CLEAR,
67 MAX_MENU_ITEMS,
68 };
69
70 static void clipman_menu_finalize (GObject *object);
71 static void clipman_menu_set_property (GObject *object,
72 guint property_id,
73 const GValue *value,
74 GParamSpec *pspec);
75 static void clipman_menu_get_property (GObject *object,
76 guint property_id,
77 GValue *value,
78 GParamSpec *pspec);
79 #ifdef HAVE_QRENCODE
80 GdkPixbuf * clipman_menu_qrcode (char *text);
81 #endif
82
83
84 /*
85 * Private methods declarations
86 */
87
88 static void _clipman_menu_free_list (ClipmanMenu *menu);
89
90 /*
91 * Callbacks declarations
92 */
93
94 #ifdef HAVE_QRENCODE
95 static void cb_set_qrcode (GtkMenuItem *mi,
96 const GdkPixbuf *pixbuf);
97 #endif
98 static void cb_set_clipboard (GtkMenuItem *mi,
99 const ClipmanHistoryItem *item);
100 static void cb_clear_history (ClipmanMenu *menu);
101
102
103
104 /*
105 * Callbacks
106 */
107
108 #ifdef HAVE_QRENCODE
109 static void
cb_set_qrcode(GtkMenuItem * mi,const GdkPixbuf * pixbuf)110 cb_set_qrcode (GtkMenuItem *mi, const GdkPixbuf *pixbuf)
111 {
112 GtkClipboard *clipboard;
113 ClipmanCollector *collector;
114 ClipmanHistory *history;
115
116 collector = clipman_collector_get ();
117 clipman_collector_set_is_restoring (collector);
118 g_object_unref (collector);
119
120 history = clipman_history_get ();
121 clipman_history_add_image (history, pixbuf);
122
123 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
124 gtk_clipboard_set_image (clipboard, GDK_PIXBUF (pixbuf));
125
126 g_object_unref (history);
127 }
128 #endif
129
130 static void
cb_set_clipboard_from_primary(GtkMenuItem * mi)131 cb_set_clipboard_from_primary (GtkMenuItem *mi)
132 {
133 GtkClipboard *clipboard;
134
135 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
136 gtk_clipboard_set_text (clipboard, gtk_menu_item_get_label (mi), -1);
137 }
138
139 static void
cb_set_clipboard(GtkMenuItem * mi,const ClipmanHistoryItem * item)140 cb_set_clipboard (GtkMenuItem *mi, const ClipmanHistoryItem *item)
141 {
142 GtkClipboard *clipboard;
143 ClipmanCollector *collector;
144 ClipmanHistory *history;
145 gboolean add_primary_clipboard;
146
147 switch (item->type)
148 {
149 case CLIPMAN_HISTORY_TYPE_TEXT:
150 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
151 gtk_clipboard_set_text (clipboard, item->content.text, -1);
152
153 collector = clipman_collector_get ();
154 g_object_get (G_OBJECT (collector), "add-primary-clipboard", &add_primary_clipboard, NULL);
155 if (add_primary_clipboard)
156 {
157 g_warning ("sync primary clipboard");
158 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
159 gtk_clipboard_set_text (clipboard, item->content.text, -1);
160 }
161 g_object_unref (collector);
162 break;
163
164 case CLIPMAN_HISTORY_TYPE_IMAGE:
165 DBG ("Copy image (%p) to default clipboard", item->content.image);
166
167 collector = clipman_collector_get ();
168 clipman_collector_set_is_restoring (collector);
169 g_object_unref (collector);
170
171 history = clipman_history_get ();
172 clipman_history_set_item_to_restore (history, item);
173 g_object_unref (history);
174
175 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
176 gtk_clipboard_set_image (clipboard, GDK_PIXBUF (item->content.image));
177 break;
178
179 default:
180 DBG("Ignoring unknown history type %d", item->type);
181 return;
182 }
183
184 cb_paste_on_activate (GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (mi), "paste-on-activate")));
185 }
186
187 void
cb_paste_on_activate(guint paste_on_activate)188 cb_paste_on_activate (guint paste_on_activate)
189 {
190 int dummyi;
191 KeySym key_sym;
192 KeyCode key_code;
193
194 Display *display = XOpenDisplay (NULL);
195 if (display == NULL)
196 {
197 return;
198 }
199 else if (!XQueryExtension (display, "XTEST", &dummyi, &dummyi, &dummyi))
200 {
201 XCloseDisplay (display);
202 return;
203 }
204
205 switch (paste_on_activate)
206 {
207 case PASTE_INACTIVE:
208 break;
209
210 case PASTE_CTRL_V:
211 key_sym = XK_Control_L;
212 key_code = XKeysymToKeycode (display, key_sym);
213 XTestFakeKeyEvent (display, key_code, True, CurrentTime);
214 key_sym = XK_v;
215 key_code = XKeysymToKeycode (display, key_sym);
216 XTestFakeKeyEvent (display, key_code, True, CurrentTime);
217 key_sym = XK_v;
218 key_code = XKeysymToKeycode (display, key_sym);
219 XTestFakeKeyEvent (display, key_code, False, CurrentTime);
220 key_sym = XK_Control_L;
221 key_code = XKeysymToKeycode (display, key_sym);
222 XTestFakeKeyEvent (display, key_code, False, CurrentTime);
223 break;
224
225 case PASTE_SHIFT_INS:
226 key_sym = XK_Shift_L;
227 key_code = XKeysymToKeycode (display, key_sym);
228 XTestFakeKeyEvent (display, key_code, True, CurrentTime);
229 key_sym = XK_Insert;
230 key_code = XKeysymToKeycode (display, key_sym);
231 XTestFakeKeyEvent (display, key_code, True, CurrentTime);
232 key_sym = XK_Insert;
233 key_code = XKeysymToKeycode (display, key_sym);
234 XTestFakeKeyEvent (display, key_code, False, CurrentTime);
235 key_sym = XK_Shift_L;
236 key_code = XKeysymToKeycode (display, key_sym);
237 XTestFakeKeyEvent (display, key_code, False, CurrentTime);
238 break;
239 }
240
241 XCloseDisplay (display);
242 }
243
244 static void
cb_clear_history(ClipmanMenu * menu)245 cb_clear_history (ClipmanMenu *menu)
246 {
247 gint res;
248 GtkWidget *dialog;
249 GtkClipboard *clipboard;
250
251 if (!menu->priv->never_confirm_history_clear)
252 {
253 dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
254 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
255 _("Are you sure you want to clear the history?"));
256
257
258 {
259 GtkWidget *content_area = gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG (dialog));
260 GtkWidget *checkbox = gtk_check_button_new_with_label (_("Don't ask again"));
261 g_object_bind_property(G_OBJECT (checkbox), "active",
262 G_OBJECT (menu), "never-confirm-history-clear",
263 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
264 gtk_widget_show (checkbox);
265 gtk_container_add (GTK_CONTAINER (content_area), checkbox);
266
267 res = gtk_dialog_run (GTK_DIALOG (dialog));
268 gtk_widget_destroy (dialog);
269
270 if (res != GTK_RESPONSE_YES)
271 {
272 g_object_set (menu, "never-confirm-history-clear", FALSE, NULL);
273 return;
274 }
275 }
276
277 }
278
279 clipman_history_clear (menu->priv->history);
280
281 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
282 gtk_clipboard_set_text (clipboard, "", 1);
283 gtk_clipboard_clear (clipboard);
284
285 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
286 gtk_clipboard_set_text (clipboard, "", 1);
287 gtk_clipboard_clear (clipboard);
288 }
289
290 static void
cb_launch_clipman_bin(ClipmanMenu * menu,gpointer user_data)291 cb_launch_clipman_bin (ClipmanMenu *menu,
292 gpointer user_data)
293 {
294 GError *error = NULL;
295 GtkWidget *error_dialog;
296 GtkWidget *mi = GTK_WIDGET (user_data);
297
298 if (g_strcmp0 (gtk_menu_item_get_label (GTK_MENU_ITEM (mi)), "_Show full history...") == 0)
299 g_spawn_command_line_async ("xfce4-clipman-history", &error);
300 else
301 g_spawn_command_line_async ("xfce4-clipman-settings", &error);
302
303 if (error != NULL)
304 {
305 error_dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
306 _("Unable to open the clipman history dialog"));
307 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (error_dialog), "%s", error->message);
308 gtk_dialog_run (GTK_DIALOG (error_dialog));
309 gtk_widget_destroy (error_dialog);
310 g_error_free (error);
311 }
312 }
313
314 /*
315 * Private methods
316 */
317
318 static void
_clipman_menu_adjust_geometry(ClipmanMenu * menu)319 _clipman_menu_adjust_geometry (ClipmanMenu *menu)
320 {
321 GtkAllocation allocation = {};
322
323 gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &allocation.width);
324 gtk_widget_get_preferred_height (GTK_WIDGET (menu), NULL, &allocation.height);
325 gtk_widget_size_allocate (GTK_WIDGET (menu), &allocation);
326 }
327
328 static void
_clipman_menu_update_list(ClipmanMenu * menu)329 _clipman_menu_update_list (ClipmanMenu *menu)
330 {
331 GtkClipboard *clipboard;
332 GtkWidget *mi, *image;
333 #ifdef HAVE_QRENCODE
334 GdkPixbuf *pixbuf;
335 #endif
336 ClipmanHistoryItem *item;
337 const ClipmanHistoryItem *item_to_restore;
338 GSList *list, *l;
339 gint pos = 0;
340 guint i = 0;
341 gchar *selection_primary;
342 gchar *selection_clipboard;
343 gboolean skip_primary = FALSE;
344
345 /* Get the most recent item in the history */
346 item_to_restore = clipman_history_get_item_to_restore (menu->priv->history);
347
348 /* Clear the previous menu items */
349 _clipman_menu_free_list (menu);
350
351 /* Set the clear history item sensitive */
352 gtk_widget_set_sensitive (menu->priv->mi_clear_history, TRUE);
353
354 /* Insert an updated list of menu items */
355 list = clipman_history_get_list (menu->priv->history);
356 if (menu->priv->reverse_order)
357 list = g_slist_reverse (list);
358
359 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
360 selection_clipboard = gtk_clipboard_wait_for_text (clipboard);
361
362 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
363 selection_primary = gtk_clipboard_wait_for_text (clipboard);
364
365 for (i = 0, l = list; i < menu->priv->max_menu_items; i++, l = l->next)
366 {
367 if (l == NULL)
368 break;
369 item = l->data;
370
371 switch (item->type)
372 {
373 case CLIPMAN_HISTORY_TYPE_TEXT:
374 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
375 mi = gtk_image_menu_item_new_with_label (item->preview.text);
376 G_GNUC_END_IGNORE_DEPRECATIONS
377 break;
378
379 case CLIPMAN_HISTORY_TYPE_IMAGE:
380 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
381 mi = gtk_image_menu_item_new ();
382 image = gtk_image_new_from_pixbuf (item->preview.image);
383 gtk_container_add (GTK_CONTAINER (mi), image);
384 G_GNUC_END_IGNORE_DEPRECATIONS
385 break;
386
387 default:
388 DBG("Ignoring unknown history type %d", item->type);
389 continue;
390 }
391
392 g_signal_connect (mi, "activate", G_CALLBACK (cb_set_clipboard), item);
393 g_object_set_data (G_OBJECT (mi), "paste-on-activate", GUINT_TO_POINTER (menu->priv->paste_on_activate));
394
395 if (selection_clipboard && (item->type == CLIPMAN_HISTORY_TYPE_TEXT)
396 && (g_strcmp0 (selection_clipboard, item->content.text) == 0))
397 {
398 image = gtk_image_new_from_icon_name ("edit-paste-symbolic",
399 GTK_ICON_SIZE_MENU);
400 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
401 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM(mi), image);
402 G_GNUC_END_IGNORE_DEPRECATIONS
403 }
404 else
405 if (selection_primary && (item->type == CLIPMAN_HISTORY_TYPE_TEXT)
406 && (g_strcmp0 (selection_primary, item->content.text) == 0))
407 {
408 image = gtk_image_new_from_icon_name ("input-mouse-symbolic",
409 GTK_ICON_SIZE_MENU);
410 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
411 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM(mi), image);
412 G_GNUC_END_IGNORE_DEPRECATIONS
413 }
414 else
415 /* not sure if "item_to_restore" can be neither primary nor clipboard...
416 * but let's leave this as fallback
417 */
418 if (item == item_to_restore)
419 {
420 image = gtk_image_new_from_icon_name ("go-next-symbolic",
421 GTK_ICON_SIZE_MENU);
422 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
423 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM(mi), image);
424 G_GNUC_END_IGNORE_DEPRECATIONS
425 }
426
427 menu->priv->list = g_slist_prepend (menu->priv->list, mi);
428 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), mi, pos++);
429 gtk_widget_show_all (mi);
430 }
431
432 #ifdef HAVE_QRENCODE
433 /* Draw QR Code if clipboard content is text */
434 if (menu->priv->show_qr_code && item_to_restore && item_to_restore->type == CLIPMAN_HISTORY_TYPE_TEXT)
435 {
436 mi = gtk_separator_menu_item_new ();
437 menu->priv->list = g_slist_prepend (menu->priv->list, mi);
438 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), mi, pos++);
439 gtk_widget_show_all (mi);
440
441 if ((pixbuf = clipman_menu_qrcode (item_to_restore->content.text)) != NULL)
442 {
443 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
444 mi = gtk_image_menu_item_new ();
445 G_GNUC_END_IGNORE_DEPRECATIONS
446 gtk_container_add (GTK_CONTAINER (mi), gtk_image_new_from_pixbuf (pixbuf));
447 g_signal_connect (mi, "activate", G_CALLBACK (cb_set_qrcode), pixbuf);
448 menu->priv->list = g_slist_prepend (menu->priv->list, mi);
449 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), mi, pos++);
450 gtk_widget_show_all (mi);
451 g_object_unref(pixbuf);
452 }
453 else
454 {
455 mi = gtk_menu_item_new_with_label (_("Could not generate QR-Code."));
456 menu->priv->list = g_slist_prepend (menu->priv->list, mi);
457 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), mi, pos++);
458 gtk_widget_set_sensitive (mi, FALSE);
459 gtk_widget_show (mi);
460 }
461 }
462 #endif
463
464 g_slist_free (list);
465
466 if (pos == 0)
467 {
468 /* Insert empty menu item */
469 mi = gtk_menu_item_new_with_label (_("Clipboard is empty"));
470 menu->priv->list = g_slist_prepend (menu->priv->list, mi);
471 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), mi, 0);
472 gtk_widget_set_sensitive (mi, FALSE);
473 gtk_widget_show (mi);
474
475 /* Set the clear history item insensitive */
476 gtk_widget_set_sensitive (menu->priv->mi_clear_history, FALSE);
477 }
478
479 /* Show the primary clipboard item so it can be selected for keyboard pasting
480 * even if "ignore selection" is enabled!
481 */
482 if (selection_primary)
483 {
484 skip_primary = (g_strcmp0 (selection_primary, selection_clipboard) == 0) ||
485 (item_to_restore && (item_to_restore->type == CLIPMAN_HISTORY_TYPE_TEXT) && g_strcmp0 (selection_primary, item_to_restore->content.text) == 0);
486
487 if (skip_primary == FALSE)
488 {
489 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
490 mi = gtk_image_menu_item_new_with_label (selection_primary);
491 image = gtk_image_new_from_icon_name ("input-mouse-symbolic", GTK_ICON_SIZE_MENU);
492 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
493 G_GNUC_END_IGNORE_DEPRECATIONS
494 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), mi, 0);
495 gtk_widget_show_all (mi);
496 g_signal_connect (mi, "activate", G_CALLBACK (cb_set_clipboard_from_primary), NULL);
497 menu->priv->list = g_slist_prepend (menu->priv->list, mi);
498 }
499 g_free (selection_primary);
500 }
501 g_free (selection_clipboard);
502
503 _clipman_menu_adjust_geometry(menu);
504 }
505
506 static void
_clipman_menu_free_list(ClipmanMenu * menu)507 _clipman_menu_free_list (ClipmanMenu *menu)
508 {
509 GSList *list;
510 for (list = menu->priv->list; list != NULL; list = list->next)
511 gtk_widget_destroy (GTK_WIDGET (list->data));
512 g_slist_free (menu->priv->list);
513 menu->priv->list = NULL;
514 }
515
516 /*
517 * Public methods
518 */
519
520 GtkWidget *
clipman_menu_new(void)521 clipman_menu_new (void)
522 {
523 return g_object_new (CLIPMAN_TYPE_MENU, NULL);
524 }
525
526 /*
527 * GObject
528 */
529
530 static void
clipman_menu_class_init(ClipmanMenuClass * klass)531 clipman_menu_class_init (ClipmanMenuClass *klass)
532 {
533 GObjectClass *object_class;
534
535 clipman_menu_parent_class = g_type_class_peek_parent (klass);
536
537 object_class = G_OBJECT_CLASS (klass);
538 object_class->finalize = clipman_menu_finalize;
539 object_class->set_property = clipman_menu_set_property;
540 object_class->get_property = clipman_menu_get_property;
541
542 g_object_class_install_property (object_class, REVERSE_ORDER,
543 g_param_spec_boolean ("reverse-order",
544 "ReverseOrder",
545 "Set to TRUE to display the menu in the reverse order",
546 FALSE,
547 G_PARAM_CONSTRUCT|G_PARAM_READWRITE));
548
549 #ifdef HAVE_QRENCODE
550 g_object_class_install_property (object_class, SHOW_QR_CODE,
551 g_param_spec_boolean ("show-qr-code",
552 "ShowQrCode",
553 "Set to TRUE to display QR-Code in the menu",
554 FALSE,
555 G_PARAM_CONSTRUCT|G_PARAM_READWRITE));
556 #endif
557
558 g_object_class_install_property (object_class, PASTE_ON_ACTIVATE,
559 g_param_spec_uint ("paste-on-activate",
560 "PasteOnActivate",
561 "Paste the content of a menu item when it is activated",
562 0, 2, 0,
563 G_PARAM_CONSTRUCT|G_PARAM_READWRITE));
564
565 g_object_class_install_property (object_class, NEVER_CONFIRM_HISTORY_CLEAR,
566 g_param_spec_boolean ("never-confirm-history-clear",
567 "NeverConfirmHistoryClear",
568 "Set to FALSE to clear the history list with confirmation",
569 FALSE,
570 G_PARAM_CONSTRUCT|G_PARAM_READWRITE));
571
572 g_object_class_install_property (object_class, MAX_MENU_ITEMS,
573 g_param_spec_uint ("max-menu-items",
574 "MaxMenuItems",
575 "Maximum amount of items displayed in the plugin's menu",
576 1, 100, 15,
577 G_PARAM_CONSTRUCT|G_PARAM_READWRITE));
578 }
579
580 static void
clipman_menu_init(ClipmanMenu * menu)581 clipman_menu_init (ClipmanMenu *menu)
582 {
583 GtkWidget *mi;
584 GtkWidget *image;
585 guint max_texts_in_history;
586
587 menu->priv = clipman_menu_get_instance_private (menu);
588
589 /* ClipmanHistory */
590 menu->priv->history = clipman_history_get ();
591
592 /* Connect signal on show to update the items */
593 g_signal_connect_swapped (menu, "show", G_CALLBACK (_clipman_menu_update_list), menu);
594
595 /* Footer items */
596 mi = gtk_separator_menu_item_new ();
597 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
598
599 max_texts_in_history = clipman_history_get_max_texts_in_history (menu->priv->history);
600 if (max_texts_in_history > menu->priv->max_menu_items)
601 {
602 mi = gtk_menu_item_new_with_mnemonic (_("_Show full history..."));
603 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
604 g_signal_connect_swapped (mi, "activate", G_CALLBACK (cb_launch_clipman_bin), mi);
605 }
606
607 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
608 menu->priv->mi_clear_history = mi = gtk_image_menu_item_new_with_mnemonic (_("_Clear history"));
609 image = gtk_image_new_from_icon_name ("edit-clear-symbolic", GTK_ICON_SIZE_MENU);
610 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu->priv->mi_clear_history), image);
611 G_GNUC_END_IGNORE_DEPRECATIONS
612 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
613 g_signal_connect_swapped (mi, "activate", G_CALLBACK (cb_clear_history), menu);
614
615 mi = gtk_menu_item_new_with_mnemonic (_("_Clipman settings..."));
616 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
617 g_signal_connect_swapped (mi, "activate", G_CALLBACK (cb_launch_clipman_bin), mi);
618
619 /* Show all the items */
620 gtk_widget_show_all (GTK_WIDGET (menu));
621 }
622
623 static void
clipman_menu_finalize(GObject * object)624 clipman_menu_finalize (GObject *object)
625 {
626 _clipman_menu_free_list (CLIPMAN_MENU (object));
627 G_OBJECT_CLASS (clipman_menu_parent_class)->finalize (object);
628 }
629
630 static void
clipman_menu_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)631 clipman_menu_set_property (GObject *object,
632 guint property_id,
633 const GValue *value,
634 GParamSpec *pspec)
635 {
636 ClipmanMenuPrivate *priv = CLIPMAN_MENU (object)->priv;
637
638 switch (property_id)
639 {
640 case REVERSE_ORDER:
641 priv->reverse_order = g_value_get_boolean (value);
642 break;
643
644 #ifdef HAVE_QRENCODE
645 case SHOW_QR_CODE:
646 priv->show_qr_code = g_value_get_boolean (value);
647 break;
648 #endif
649
650 case PASTE_ON_ACTIVATE:
651 priv->paste_on_activate = g_value_get_uint (value);
652 break;
653
654 case NEVER_CONFIRM_HISTORY_CLEAR:
655 priv->never_confirm_history_clear = g_value_get_boolean (value);
656 break;
657
658 case MAX_MENU_ITEMS:
659 priv->max_menu_items = g_value_get_uint (value);
660 break;
661
662 default:
663 break;
664 }
665 }
666
667 static void
clipman_menu_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)668 clipman_menu_get_property (GObject *object,
669 guint property_id,
670 GValue *value,
671 GParamSpec *pspec)
672 {
673 ClipmanMenuPrivate *priv = CLIPMAN_MENU (object)->priv;
674
675 switch (property_id)
676 {
677 case REVERSE_ORDER:
678 g_value_set_boolean (value, priv->reverse_order);
679 break;
680
681 case PASTE_ON_ACTIVATE:
682 g_value_set_uint (value, priv->paste_on_activate);
683 break;
684
685 case NEVER_CONFIRM_HISTORY_CLEAR:
686 g_value_set_boolean (value, priv->never_confirm_history_clear);
687 break;
688
689 case MAX_MENU_ITEMS:
690 g_value_set_uint (value, priv->max_menu_items);
691 break;
692
693 default:
694 break;
695 }
696 }
697
698 #ifdef HAVE_QRENCODE
699 GdkPixbuf *
clipman_menu_qrcode(char * text)700 clipman_menu_qrcode (char *text)
701 {
702 QRcode *qrcode;
703 GdkPixbuf *pixbuf, *pixbuf_scaled;
704 int i, j, k, rowstride, channels;
705 guchar *pixel;
706 unsigned char *data;
707
708 qrcode = QRcode_encodeString8bit(text, 0, QR_ECLEVEL_L);
709
710 if (qrcode == NULL)
711 return NULL;
712
713 data = qrcode->data;
714
715 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, qrcode->width + 2, qrcode->width + 2);
716
717 pixel = gdk_pixbuf_get_pixels (pixbuf);
718 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
719 channels = gdk_pixbuf_get_n_channels (pixbuf);
720
721 gdk_pixbuf_fill(pixbuf, 0xffffffff);
722 for (i = 1; i <= qrcode->width; i++)
723 for (j = 1; j <= qrcode->width; j++) {
724 for (k = 0; k < channels; k++)
725 pixel[i * rowstride + j * channels + k] = !(*data & 0x1) * 0xff;
726 data++;
727 }
728
729 pixbuf_scaled = gdk_pixbuf_scale_simple (pixbuf, (qrcode->width + 2) * 3, (qrcode->width + 2) * 3, GDK_INTERP_NEAREST);
730
731 QRcode_free(qrcode);
732 g_object_unref(pixbuf);
733
734 return pixbuf_scaled;
735 }
736 #endif
737