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