~ubuntu-branches/debian/sid/xfce4-clipman-plugin/sid

« back to all changes in this revision

Viewing changes to panel-plugin/clipman.c

  • Committer: Bazaar Package Importer
  • Author(s): Yves-Alexis Perez
  • Date: 2009-04-21 07:48:11 UTC
  • mfrom: (1.2.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20090421074811-ro4ri3jy2lhbhcij
Tags: 2:1.0.0-1
* New upstream release.
  + ship a shortcutable binary to popup plugin.               closes: #405556
* debian/control:
  - move package to xfce section.
  - bump build-deps for Xfce 4.6.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  $Id: clipman.c 6467 2009-01-15 17:51:07Z mmassonnet $
2
 
 *
3
 
 *  Copyright (c) 2006-2007 Nick Schermer <nick@xfce.org>
4
 
 *                2008-2009 Mike Massonnet <mmassonnet@xfce.org>
5
 
 *                2008-2009 David Collins <david.8.collins@gmail.com>
6
 
 *
7
 
 *
8
 
 *  This program is free software; you can redistribute it and/or modify
9
 
 *  it under the terms of the GNU General Public License as published by
10
 
 *  the Free Software Foundation; either version 2 of the License, or
11
 
 *  (at your option) any later version.
12
 
 *
13
 
 *  This program is distributed in the hope that it will be useful,
14
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 
 *  GNU Library General Public License for more details.
17
 
 *
18
 
 *  You should have received a copy of the GNU General Public License
19
 
 *  along with this program; if not, write to the Free Software
20
 
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
 
 */
22
 
 
23
 
#ifdef HAVE_CONFIG_H
24
 
#include <config.h>
25
 
#endif
26
 
 
27
 
#include <string.h>
28
 
#include <gtk/gtk.h>
29
 
 
30
 
#include <exo/exo.h>
31
 
#include <libxfcegui4/libxfcegui4.h>
32
 
#include <libxfce4util/libxfce4util.h>
33
 
#include <libxfce4panel/xfce-panel-plugin.h>
34
 
#include <libxfce4panel/xfce-panel-convenience.h>
35
 
 
36
 
#include "clipman.h"
37
 
#include "clipman-dialogs.h"
38
 
 
39
 
/* The clipboards */
40
 
static GtkClipboard *primaryClip;
41
 
static GtkClipboard *defaultClip;
42
 
 
43
 
/* For event-driven clipboard_change() function */
44
 
gboolean MouseSelecting=FALSE;
45
 
gboolean ShiftSelecting=FALSE;
46
 
 
47
 
/* Register the plugin */
48
 
static void
49
 
clipman_construct (XfcePanelPlugin *plugin);
50
 
 
51
 
XFCE_PANEL_PLUGIN_REGISTER_EXTERNAL (clipman_construct);
52
 
 
53
 
static void
54
 
clipman_free_clip (ClipmanClip *clip)
55
 
{
56
 
    if (clip->datatype == RAWTEXT) {
57
 
      g_free (clip->data);
58
 
    } else if (clip->datatype == IMAGE) {
59
 
      g_object_unref(clip->data);
60
 
    }
61
 
    g_free (clip->title);
62
 
 
63
 
    panel_slice_free (ClipmanClip, clip);
64
 
 
65
 
    DBG ("Clip successfully freed");
66
 
}
67
 
 
68
 
static void
69
 
clipman_set_data (ClipmanClip *clip, GtkClipboard *clipboard)
70
 
{
71
 
    DBG ("Clipman_set_data ..");
72
 
    if (clip->datatype == RAWTEXT) {
73
 
      gtk_clipboard_set_text (clipboard, clip->data, -1);
74
 
    } else if (clip->datatype == IMAGE) {
75
 
      gtk_clipboard_set_image (clipboard, (GdkPixbuf *)clip->data);
76
 
    }
77
 
    DBG ("Clip data copied to clipboard");
78
 
}
79
 
 
80
 
static void
81
 
clipman_destroy_menu (GtkWidget     *menu,
82
 
                      ClipmanPlugin *clipman)
83
 
{
84
 
    GSList *l;
85
 
 
86
 
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (clipman->button), FALSE);
87
 
 
88
 
    gtk_widget_destroy (menu);
89
 
 
90
 
    /* Clear ClipmanAction structs */
91
 
    for (l = clipman->actions; l != NULL; l = l->next)
92
 
      panel_slice_free (ClipmanAction, l->data);
93
 
    g_slist_free (clipman->actions);
94
 
    clipman->actions = NULL;
95
 
 
96
 
    DBG ("Menu Destroyed");
97
 
}
98
 
 
99
 
/* Clear list of items AND both clipboards */
100
 
static gboolean
101
 
clipman_clear (GtkWidget *mi, GdkEventButton *ev, ClipmanPlugin *clipman) {
102
 
 
103
 
    ClipmanClip *clip;
104
 
 
105
 
    if (xfce_confirm (_("Are you sure you want to clear the history?"),
106
 
                                                              "gtk-yes", NULL)) {
107
 
                      
108
 
      gtk_clipboard_clear (primaryClip);
109
 
            gtk_clipboard_clear (defaultClip);
110
 
      clipman->DefaultIndex=-1;
111
 
      clipman->PrimaryIndex=-1;
112
 
 
113
 
      while (clipman->clips->len > 0)
114
 
      {
115
 
        clip = g_ptr_array_index (clipman->clips, 0);
116
 
        g_ptr_array_remove (clipman->clips, clip);
117
 
        clipman_free_clip (clip);
118
 
      }
119
 
    }
120
 
    return FALSE;
121
 
}
122
 
 
123
 
/* Remove the oldest items - these are at the start of the list */
124
 
void
125
 
clipman_array_remove_oldest (ClipmanPlugin *clipman)
126
 
{
127
 
    ClipmanClip *clip;
128
 
    gint i;
129
 
    
130
 
    while (clipman->clips->len > clipman->HistoryItems) {
131
 
 
132
 
      // Leave items in list if they are active
133
 
      for (i=0; i<2; ++i) {
134
 
        if (clipman->DefaultIndex != i && clipman->PrimaryIndex != i) break;
135
 
      }
136
 
 
137
 
      clip = g_ptr_array_index (clipman->clips, i);
138
 
      g_ptr_array_remove (clipman->clips, clip);
139
 
      clipman_free_clip (clip);
140
 
      /* Adjust indexes to clipboard data */
141
 
      if (clipman->DefaultIndex > i) --clipman->DefaultIndex;
142
 
      if (clipman->PrimaryIndex > i) --clipman->PrimaryIndex;
143
 
 
144
 
      DBG("A clip have been removed");
145
 
    }
146
 
}
147
 
 
148
 
void
149
 
clipman_array_remove_image_data (ClipmanPlugin *clipman)
150
 
{
151
 
    ClipmanClip *clip;
152
 
    gint i;
153
 
    
154
 
    DBG ("Removing image data from array");
155
 
 
156
 
    for (i=0; i<clipman->clips->len; ++i) {
157
 
 
158
 
      clip = g_ptr_array_index (clipman->clips, i);
159
 
      if (clip->datatype == IMAGE) {
160
 
        g_ptr_array_remove (clipman->clips, clip);
161
 
        clipman_free_clip (clip);
162
 
        /* Adjust indexes to clipboard data */
163
 
        if (clipman->DefaultIndex == i) clipman->DefaultIndex=-1;
164
 
        if (clipman->DefaultIndex > i) --clipman->DefaultIndex;
165
 
      }
166
 
    }
167
 
}
168
 
 
169
 
static gchar *
170
 
clipman_create_title (gchar *text,
171
 
                      gint   length)
172
 
{
173
 
  gchar                *short_text, *tmp = NULL;
174
 
  const gchar          *offset;
175
 
 
176
 
  g_return_val_if_fail (G_LIKELY (NULL != text), NULL);
177
 
 
178
 
  if (G_UNLIKELY (!g_utf8_validate (text, -1, NULL)))
179
 
    return NULL;
180
 
 
181
 
  short_text = g_strstrip (g_strdup (text));
182
 
 
183
 
  /* Shorten */
184
 
  if (g_utf8_strlen (short_text, -1) > length)
185
 
    {
186
 
      offset = g_utf8_offset_to_pointer (short_text, length);
187
 
      tmp = g_strndup (short_text, offset - short_text);
188
 
      g_free (short_text);
189
 
 
190
 
      short_text = g_strconcat (tmp, "...", NULL); /* Ellipsis */
191
 
      g_free (tmp);
192
 
    }
193
 
 
194
 
  /* Cleanup */
195
 
  tmp = g_strdelimit (short_text, "\n\r\t", ' ');
196
 
  short_text = g_markup_escape_text (tmp, -1);
197
 
  g_free (tmp);
198
 
 
199
 
  return short_text;
200
 
}
201
 
 
202
 
/* Add new item to the end of the list */
203
 
static void
204
 
clipman_add_clip (ClipmanPlugin *clipman, void *data, ClipboardType cliptype, ClipDataType datatype) {
205
 
                  
206
 
  ClipmanClip *clip;
207
 
 
208
 
  if (G_LIKELY (data != NULL)) {
209
 
      // &&  G_LIKELY (strcmp (data, ""))) {
210
 
    
211
 
    clip = panel_slice_new0 (ClipmanClip);
212
 
    
213
 
    if (datatype == RAWTEXT) {
214
 
      clip->title = clipman_create_title (data, clipman->MenuCharacters);
215
 
    } else {
216
 
      /* Change this to store a pixbuf preview */
217
 
      clip->title = clipman_create_title (CLIPIMAGETITLE, clipman->MenuCharacters);
218
 
      clip->preview = exo_gdk_pixbuf_scale_ratio (GDK_PIXBUF (data), 128);
219
 
    }
220
 
 
221
 
        /* No valid title could be created, drop it */
222
 
          if (clip->title == NULL) {
223
 
            DBG("A title couldn't be created");
224
 
        panel_slice_free (ClipmanClip, clip);
225
 
            return;
226
 
          }
227
 
 
228
 
    /* Make a copy of the data and add to the pointer array */
229
 
    if (datatype == RAWTEXT) {
230
 
      clip->data = g_strdup(data);
231
 
      clip->datatype = RAWTEXT;
232
 
    } else if (datatype == IMAGE) {
233
 
      clip->data = gdk_pixbuf_copy((GdkPixbuf *)data);
234
 
      DBG("Made copy of image");
235
 
      clip->datatype = IMAGE;
236
 
      // Remove any other image data first ..
237
 
      clipman_array_remove_image_data(clipman);
238
 
    }
239
 
    g_ptr_array_add (clipman->clips, clip);
240
 
                                                         
241
 
    /* Indicate this item is in the clipboard */
242
 
    if (cliptype == DEFAULT) {
243
 
      clipman->DefaultIndex=clipman->clips->len-1;
244
 
    } else if (cliptype == PRIMARY) {
245
 
      clipman->PrimaryIndex=clipman->clips->len-1;
246
 
    }
247
 
    DBG("Added clip %d of %d", clipman->clips->len, clipman->HistoryItems);
248
 
  }
249
 
}
250
 
 
251
 
/* See if the text/image is already in the list.  If so, mark it as the current DEFAULT or PRIMARY clipboard.
252
 
   If not found then return FALSE.
253
 
   Currently, this will always return FALSE for IMAGE data. */
254
 
static gboolean
255
 
clipman_exists (ClipmanPlugin *clipman, void *data, ClipboardType cliptype, ClipDataType datatype) {
256
 
                
257
 
    gint        i;
258
 
    ClipmanClip *clip;
259
 
 
260
 
    /* Walk through the array backwards, because
261
 
     * if the text exists, this will probably be the newest */
262
 
    for (i=(gint)clipman->clips->len-1; i>=0; i--) {
263
 
    
264
 
      clip = g_ptr_array_index (clipman->clips, i);
265
 
 
266
 
      if (G_LIKELY(clip->data != NULL)) {
267
 
        if (datatype == RAWTEXT && strcmp(clip->data, data) == 0) {
268
 
          if (cliptype == PRIMARY) {
269
 
            clipman->PrimaryIndex=i;
270
 
            DBG("String re-selected");
271
 
          } else if (cliptype == DEFAULT) {
272
 
            clipman->DefaultIndex=i;
273
 
            DBG("Selection Copied");
274
 
          }
275
 
          return TRUE;
276
 
        }
277
 
      }
278
 
  }
279
 
  return FALSE;
280
 
}
281
 
 
282
 
static gboolean
283
 
clipman_item_clicked (GtkWidget      *mi,
284
 
                      GdkEventButton *ev,
285
 
                      ClipmanAction  *action)
286
 
{
287
 
    gboolean defaultcleared, primarycleared; 
288
 
 
289
 
    /* Left mouse button - put item in BOTH clipboards */
290
 
    if (ev->button == 1) {
291
 
      gtk_clipboard_clear (defaultClip);
292
 
      clipman_set_data (action->clip, defaultClip);      
293
 
      //gtk_clipboard_set_text (defaultClip, action->clip->data, -1);
294
 
      action->clipman->DefaultIndex = action->index;
295
 
            DBG ("Clip copied to default clipboard");
296
 
 
297
 
            if (action->clipman->AddSelect)     {
298
 
              gtk_clipboard_clear (primaryClip);
299
 
        clipman_set_data (action->clip, primaryClip);      
300
 
        action->clipman->PrimaryIndex = action->index;
301
 
              DBG ("Clip copied to primary clipboard");
302
 
            }
303
 
            
304
 
          /* Right mouse button - remove item */  
305
 
    } else if (ev->button == 3) {
306
 
 
307
 
      defaultcleared=FALSE;
308
 
      primarycleared=FALSE; 
309
 
      
310
 
      DBG ("Removed the selected clip from the History");
311
 
            /* If this item is in clipboard, clear the clipbard */
312
 
      if (action->clipman->DefaultIndex == action->index) {
313
 
        gtk_clipboard_clear (defaultClip);
314
 
        defaultcleared=TRUE;
315
 
            } else if (action->clipman->DefaultIndex > action->index) {
316
 
              // index needs adjustment
317
 
        --action->clipman->DefaultIndex;
318
 
      }
319
 
      
320
 
            if (action->clipman->AddSelect)     {
321
 
            /* If this item is in clipboard, clear the clipbard */
322
 
        if (action->clipman->PrimaryIndex == action->index) {
323
 
          gtk_clipboard_clear (primaryClip);
324
 
          primarycleared=TRUE;
325
 
              } else if (action->clipman->PrimaryIndex > action->index) {
326
 
                // index needs adjustment
327
 
          --action->clipman->PrimaryIndex;
328
 
                }
329
 
            }
330
 
 
331
 
      /* Remove chosen item from the list */
332
 
      g_ptr_array_remove (action->clipman->clips, action->clip);
333
 
      clipman_free_clip (action->clip);
334
 
      guint len = action->clipman->clips->len;
335
 
 
336
 
      /* List is now empty */
337
 
      if (len == 0) {
338
 
                  if (defaultcleared) {
339
 
                    gtk_clipboard_set_text (defaultClip, "", -1); // this might not be needed?
340
 
                    action->clipman->DefaultIndex = -1;
341
 
                  }
342
 
                  if (primarycleared) {
343
 
                    gtk_clipboard_set_text (primaryClip, "", -1); // this might not be needed?
344
 
                  action->clipman->PrimaryIndex = -1;
345
 
                  }
346
 
 
347
 
      } else {
348
 
        /* Get the newest item left in the list */
349
 
        ClipmanClip *clip = g_ptr_array_index (action->clipman->clips, len-1);
350
 
 
351
 
        /* If Clipboard has been cleared, put in a replacement */
352
 
                if (defaultcleared) {
353
 
          clipman_set_data (clip, defaultClip);      
354
 
                  action->clipman->DefaultIndex = len-1;
355
 
                }
356
 
                if (primarycleared) {
357
 
          clipman_set_data (clip, primaryClip);      
358
 
                  action->clipman->PrimaryIndex = len-1;
359
 
                }
360
 
        } 
361
 
    }
362
 
 
363
 
    return FALSE;
364
 
}
365
 
 
366
 
static GtkWidget *
367
 
clipman_create_menuitem (ClipmanAction *action,
368
 
                         guint          width,
369
 
                         guint          number,
370
 
                         ClipMenuFormat format) {
371
 
 
372
 
    GtkWidget *mi;
373
 
    gchar     *title, *string_num;
374
 
 
375
 
    if (action->clipman->ItemNumbers) {
376
 
        if (number < 10)
377
 
            string_num = g_strdup_printf("<tt><span size=\"smaller\">%d. </span></tt> ", number);
378
 
        else
379
 
            string_num = g_strdup_printf("<tt><span size=\"smaller\">%d.</span></tt> ", number);
380
 
    } else {
381
 
        string_num = g_strdup ("");
382
 
    }
383
 
 
384
 
    if (format==BOLD)
385
 
        title = g_strdup_printf("%s<b>%s</b>", string_num, action->clip->title);
386
 
    else if (format==ITALICS)
387
 
        title = g_strdup_printf("%s<i>%s</i>", string_num, action->clip->title);
388
 
    else
389
 
        title = g_strdup_printf("%s%s", string_num, action->clip->title);
390
 
 
391
 
    g_free (string_num);
392
 
 
393
 
    mi = gtk_menu_item_new_with_label ("");
394
 
    gtk_label_set_markup (GTK_LABEL (GTK_BIN (mi)->child), title);
395
 
    g_free (title);
396
 
 
397
 
    return mi;
398
 
}
399
 
 
400
 
static GtkWidget *
401
 
clipman_create_imagemenuitem (ClipmanAction *action) {
402
 
 
403
 
    GtkWidget *mi, *image;
404
 
 
405
 
    mi = gtk_menu_item_new ();
406
 
    image = gtk_image_new_from_pixbuf (action->clip->preview);
407
 
    gtk_container_add (GTK_CONTAINER (mi), image);
408
 
 
409
 
    return mi;
410
 
}
411
 
 
412
 
static void
413
 
clipman_build_menu_body (GtkWidget *menu, ClipmanPlugin *clipman) {
414
 
                               
415
 
    guint          i;
416
 
    ClipmanAction *action = NULL;
417
 
    ClipmanClip   *clip;
418
 
    GtkWidget     *mi;
419
 
 
420
 
    for (i=clipman->clips->len; i--;) {
421
 
        clip = g_ptr_array_index (clipman->clips, i);
422
 
 
423
 
        action = panel_slice_new0 (ClipmanAction);
424
 
        action->clipman = clipman;
425
 
        action->clip = clip;
426
 
        action->index = i;
427
 
 
428
 
        if (clip->datatype == RAWTEXT) {
429
 
          if (clipman->DefaultIndex == i) {
430
 
            mi = clipman_create_menuitem (action, clipman->MenuCharacters,
431
 
                                          clipman->clips->len-i, BOLD);
432
 
          }
433
 
          else if (clipman->PrimaryIndex == i) {
434
 
            mi = clipman_create_menuitem (action, clipman->MenuCharacters,
435
 
                                          clipman->clips->len-i, ITALICS);
436
 
          } else {
437
 
            mi = clipman_create_menuitem (action, clipman->MenuCharacters,
438
 
                                          clipman->clips->len-i, PLAIN);
439
 
          }
440
 
        } else if (clip->datatype == IMAGE) {
441
 
          mi = clipman_create_imagemenuitem (action);
442
 
        }
443
 
 
444
 
        clipman->actions = g_slist_prepend (clipman->actions, action);
445
 
        g_signal_connect (G_OBJECT(mi), "button_release_event",
446
 
                G_CALLBACK(clipman_item_clicked), action);
447
 
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
448
 
    }
449
 
 
450
 
    mi = gtk_separator_menu_item_new ();
451
 
    gtk_widget_set_sensitive (mi, FALSE);
452
 
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
453
 
 
454
 
    mi = gtk_menu_item_new_with_label (_("Clear History"));
455
 
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
456
 
 
457
 
    g_signal_connect (G_OBJECT (mi), "button_release_event",
458
 
        G_CALLBACK (clipman_clear), clipman);
459
 
}
460
 
 
461
 
static void
462
 
clipman_position_menu (GtkMenu       *menu,
463
 
                       int           *x,
464
 
                       int           *y,
465
 
                       gboolean      *push_in,
466
 
                       ClipmanPlugin *clipman)
467
 
{
468
 
    GtkRequisition  req;
469
 
    GdkScreen      *screen;
470
 
    GdkRectangle    geom;
471
 
    gint            num;
472
 
 
473
 
    gtk_widget_size_request (GTK_WIDGET (menu), &req);
474
 
 
475
 
    gdk_window_get_origin (GTK_WIDGET (clipman->plugin)->window, x, y);
476
 
 
477
 
    switch (xfce_panel_plugin_get_screen_position(clipman->plugin))
478
 
    {
479
 
        case XFCE_SCREEN_POSITION_SW_H:
480
 
        case XFCE_SCREEN_POSITION_S:
481
 
        case XFCE_SCREEN_POSITION_SE_H:
482
 
            DBG("Bottom");
483
 
            *y -= req.height;
484
 
            break;
485
 
 
486
 
        case XFCE_SCREEN_POSITION_NW_H:
487
 
        case XFCE_SCREEN_POSITION_N:
488
 
        case XFCE_SCREEN_POSITION_NE_H:
489
 
            DBG("Top");
490
 
            *y += clipman->button->allocation.height;
491
 
            break;
492
 
 
493
 
        case XFCE_SCREEN_POSITION_NW_V:
494
 
        case XFCE_SCREEN_POSITION_W:
495
 
        case XFCE_SCREEN_POSITION_SW_V:
496
 
            DBG("Left");
497
 
            *x += clipman->button->allocation.width;
498
 
            *y += clipman->button->allocation.height - req.height;
499
 
            break;
500
 
 
501
 
        case XFCE_SCREEN_POSITION_NE_V:
502
 
        case XFCE_SCREEN_POSITION_E:
503
 
        case XFCE_SCREEN_POSITION_SE_V:
504
 
            DBG("Right");
505
 
            *x -= req.width;
506
 
            *y += clipman->button->allocation.height - req.height;
507
 
            break;
508
 
 
509
 
        case XFCE_SCREEN_POSITION_FLOATING_H:
510
 
        case XFCE_SCREEN_POSITION_FLOATING_V:
511
 
        case XFCE_SCREEN_POSITION_NONE:
512
 
            DBG("Floating");
513
 
            gdk_display_get_pointer(gtk_widget_get_display(GTK_WIDGET(clipman->plugin)),
514
 
                NULL, x, y, NULL);
515
 
    }
516
 
 
517
 
    screen = gtk_widget_get_screen (clipman->button);
518
 
    num = gdk_screen_get_monitor_at_window (screen, clipman->button->window);
519
 
    gdk_screen_get_monitor_geometry (screen, num, &geom);
520
 
 
521
 
    if (*x > geom.x + geom.width - req.width)
522
 
        *x = geom.x + geom.width - req.width;
523
 
    if (*x < geom.x)
524
 
        *x = geom.x;
525
 
 
526
 
    if (*y > geom.y + geom.height - req.height)
527
 
        *y = geom.y + geom.height - req.height;
528
 
    if (*y < geom.y)
529
 
        *y = geom.y;
530
 
}
531
 
 
532
 
/* Show list when the clipman icon is clicked with the left mouse button */
533
 
static gboolean
534
 
clipman_icon_clicked (GtkWidget      *button,
535
 
                 GdkEventButton *ev,
536
 
                 ClipmanPlugin  *clipman)
537
 
{
538
 
    GtkWidget *mi;
539
 
    GtkWidget *menu;
540
 
    gchar     *title;
541
 
 
542
 
    if (ev->button != 1) return FALSE;
543
 
 
544
 
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (clipman->button), TRUE);
545
 
 
546
 
    menu = gtk_menu_new ();
547
 
 
548
 
    title = g_strdup_printf("<span weight=\"bold\">%s</span>", _("Clipman History"));
549
 
    mi = gtk_menu_item_new_with_label  ("");
550
 
    gtk_label_set_markup(GTK_LABEL(GTK_BIN(mi)->child), title);
551
 
    gtk_widget_set_sensitive (mi, FALSE);
552
 
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
553
 
 
554
 
    mi = gtk_separator_menu_item_new ();
555
 
    gtk_widget_set_sensitive (mi, FALSE);
556
 
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
557
 
 
558
 
    if (G_LIKELY (clipman->clips->len > 0)) {
559
 
        clipman_build_menu_body (menu, clipman);
560
 
    } else {
561
 
        mi = gtk_menu_item_new_with_label (_("< Clipboard Empty >"));
562
 
        gtk_widget_set_sensitive (mi, FALSE);
563
 
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
564
 
    }
565
 
 
566
 
    gtk_widget_show_all (menu);
567
 
 
568
 
    /* Also destroy the menu items when nothing is clicked */
569
 
    g_signal_connect (G_OBJECT(menu), "deactivate",
570
 
        G_CALLBACK(clipman_destroy_menu), clipman);
571
 
 
572
 
    gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
573
 
                        (GtkMenuPositionFunc) clipman_position_menu,
574
 
                        clipman, 0,
575
 
                        gtk_get_current_event_time ());
576
 
 
577
 
    return TRUE;
578
 
}
579
 
 
580
 
/* Called when a program closes. */
581
 
static void
582
 
clipman_refill_clipboard (ClipmanPlugin *clipman,
583
 
                       ClipboardType  type)
584
 
{
585
 
    ClipmanClip *clip;
586
 
 
587
 
    if (clipman->clips->len > 0) {
588
 
    
589
 
        if (type == DEFAULT) {
590
 
          clip = g_ptr_array_index(clipman->clips, clipman->DefaultIndex);
591
 
          clipman_set_data (clip, defaultClip);      
592
 
        } else if (type == PRIMARY) {
593
 
          clip = g_ptr_array_index(clipman->clips, clipman->PrimaryIndex);
594
 
          clipman_set_data (clip, primaryClip);      
595
 
        }
596
 
        DBG("Active clip restored");
597
 
    }
598
 
}
599
 
 
600
 
/* This function is called when populating the clipboard in these events -
601
 
  - user selects data             - add to list
602
 
  - user copies data to clipboard - add to list
603
 
  - program holding the DEFAULT clipboard closes down - populate DEFAULT clipboard with active list item
604
 
  - program holding the PRIMARY clipboard closes down - populate PRIMARY clipboard with active list item
605
 
*/
606
 
static void clipman_clipboard_changed(GtkClipboard *clipboard, ClipmanPlugin *clipman, GdkOwnerChange reason) {
607
 
 
608
 
  gchar *ptext = NULL, *dtext;
609
 
 
610
 
  // Information for the programmer
611
 
  if (reason == GDK_OWNER_CHANGE_DESTROY) {
612
 
    DBG("GDK_OWNER_CHANGE_DESTROY");
613
 
  }
614
 
 
615
 
  /* DEFAULT clipboard - 'Copied' data */
616
 
  if (clipboard == defaultClip) {
617
 
       
618
 
    // Program holding selection has closed
619
 
    // (Mousepad gives GDK_OWNER_CHANGE_CLOSE, Kolorpaint gives GDK_OWNER_CHANGE_DESTROY)
620
 
    if (reason == GDK_OWNER_CHANGE_CLOSE || reason == GDK_OWNER_CHANGE_DESTROY) {
621
 
      clipman_refill_clipboard (clipman, DEFAULT);
622
 
      return;
623
 
    }
624
 
 
625
 
    // User copied data to clipboard
626
 
    if (reason == GDK_OWNER_CHANGE_NEW_OWNER) {
627
 
 
628
 
      dtext = gtk_clipboard_wait_for_text (defaultClip);
629
 
      if (dtext != NULL) {
630
 
        if (!clipman_exists (clipman, dtext, DEFAULT, RAWTEXT)) {
631
 
          DBG("Text copy ..");
632
 
          clipman_add_clip (clipman, dtext, DEFAULT, RAWTEXT);
633
 
          clipman_array_remove_oldest (clipman);
634
 
        }
635
 
        g_free (dtext);
636
 
      } else {
637
 
        GdkPixbuf *image = gtk_clipboard_wait_for_image(defaultClip);
638
 
        if (image != NULL) {
639
 
          if (!clipman_exists (clipman, image, DEFAULT, IMAGE)) {
640
 
            DBG("Image copy ..");
641
 
            clipman_add_clip (clipman, image, DEFAULT, IMAGE);
642
 
            clipman_array_remove_oldest (clipman);
643
 
          }
644
 
          g_object_unref(image);
645
 
        }
646
 
      }
647
 
    }
648
 
  }
649
 
 
650
 
  /* PRIMARY - only if 'Add Selections' is ticked */
651
 
  if (clipboard == primaryClip && clipman->AddSelect) {
652
 
    // Program holding selection has closed - restore Primary clipboard
653
 
    if (reason == GDK_OWNER_CHANGE_CLOSE || reason == GDK_OWNER_CHANGE_DESTROY) {
654
 
      clipman_refill_clipboard (clipman, PRIMARY);
655
 
      return;
656
 
    }
657
 
     
658
 
    if (reason == GDK_OWNER_CHANGE_NEW_OWNER) {
659
 
      // Get selected - TEXT only
660
 
      ptext = gtk_clipboard_wait_for_text (primaryClip);
661
 
      // User has selected text.
662
 
      if (ptext != NULL) {
663
 
        if (!clipman_exists (clipman, ptext, PRIMARY, RAWTEXT)) {
664
 
          DBG("Text select done");
665
 
          clipman_add_clip (clipman, ptext, PRIMARY, RAWTEXT);
666
 
          clipman_array_remove_oldest (clipman);
667
 
        }
668
 
        //gtk_clipboard_set_text (defaultClip, ptext, -1);
669
 
        g_free (ptext);
670
 
        
671
 
      // If an image has been selected, don't process it - but reset PrimaryIndex
672
 
      } else if (gtk_clipboard_wait_is_image_available(primaryClip)) {
673
 
        clipman->PrimaryIndex=-1;
674
 
      }
675
 
    }
676
 
  }
677
 
 
678
 
}
679
 
 
680
 
/* Called when -
681
 
  - user selects data
682
 
  - user copies data to clipboard
683
 
  - program holding a clipboard closes down
684
 
*/
685
 
static void clipboard_changed(GtkClipboard *clipboard, GdkEvent *event, ClipmanPlugin *clipman) {
686
 
 
687
 
  // Signal has been sent by this plugin
688
 
  // (Found a GdkWindow that wraps the native X-window)
689
 
  if (gdk_window_lookup(((GdkEventOwnerChange*)event)->owner)!=NULL) {
690
 
    DBG("Signal Ignored");
691
 
    return;
692
 
  }
693
 
  
694
 
  /* Note this extra effort is only required for the PRIMARY selects
695
 
     in some applications */
696
 
  if (clipboard == primaryClip && clipman->AddSelect) {
697
 
    GdkModifierType  state;
698
 
    gdk_window_get_pointer(NULL, NULL, NULL, &state);
699
 
    if (state & GDK_BUTTON1_MASK) {
700
 
      DBG("Left btn pressed");
701
 
      MouseSelecting=TRUE;
702
 
      return;  // not done yet
703
 
    } else if (state & GDK_SHIFT_MASK) {
704
 
      DBG("Shift key pressed");
705
 
      ShiftSelecting=TRUE;
706
 
      return;  // not done yet
707
 
    }
708
 
  }
709
 
       
710
 
  /* Reason the signal was sent */
711
 
  GdkOwnerChange reason = ((GdkEventOwnerChange*)event)->reason;
712
 
  clipman_clipboard_changed(clipboard, clipman, reason);
713
 
 
714
 
}
715
 
 
716
 
/* This runs every 0.5 seconds - minimize what it does. */
717
 
static gboolean clipman_timed_poll (ClipmanPlugin *clipman)
718
 
{
719
 
    // Nearly always, this is all this function will do ..
720
 
    if (!MouseSelecting && !ShiftSelecting) return TRUE;
721
 
 
722
 
    GdkModifierType  state;
723
 
    gdk_window_get_pointer(NULL, NULL, NULL, &state);
724
 
    if (MouseSelecting==TRUE && !(state & GDK_BUTTON1_MASK)) MouseSelecting=FALSE;
725
 
    if (ShiftSelecting==TRUE && !(state & GDK_SHIFT_MASK))   ShiftSelecting=FALSE;
726
 
 
727
 
    // Now that the selection is finished, run the necessary code ..
728
 
    if (!MouseSelecting && !ShiftSelecting) {
729
 
      DBG("Finished selecting");    
730
 
      clipman_clipboard_changed(primaryClip, clipman, GDK_OWNER_CHANGE_NEW_OWNER);
731
 
    }
732
 
    
733
 
    return TRUE;
734
 
}
735
 
 
736
 
static void
737
 
clipman_reset_timeout (ClipmanPlugin *clipman)
738
 
{
739
 
    if (!(clipman->killTimeout))
740
 
    {
741
 
        if (clipman->TimeoutId != 0)
742
 
            g_source_remove (clipman->TimeoutId);
743
 
 
744
 
        clipman->TimeoutId = g_timeout_add_full (G_PRIORITY_LOW,
745
 
                                                 TIMER_INTERVAL,
746
 
                                                 (GSourceFunc) clipman_timed_poll,
747
 
                                                 clipman,
748
 
                                                 (GDestroyNotify) clipman_reset_timeout);
749
 
    }
750
 
}
751
 
 
752
 
void
753
 
clipman_save (XfcePanelPlugin *plugin,
754
 
              ClipmanPlugin   *clipman)
755
 
{
756
 
    gchar       *file, *image_path;
757
 
    XfceRc      *rc;
758
 
    GKeyFile    *keyfile;
759
 
    gchar       *data;
760
 
    const gchar **texts;
761
 
    gint        i, n_texts;
762
 
    ClipmanClip *clip;
763
 
 
764
 
    /* Save the preferences */
765
 
    DBG("Saving clipman preferences");
766
 
 
767
 
    file = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, "xfce4/panel/clipman.rc", TRUE);
768
 
 
769
 
    if (G_UNLIKELY (!file))
770
 
        return;
771
 
 
772
 
    rc = xfce_rc_simple_open (file, FALSE);
773
 
    g_free (file);
774
 
 
775
 
    xfce_rc_set_group (rc, "Properties");
776
 
 
777
 
    xfce_rc_write_bool_entry (rc, "ExitSave",     clipman->ExitSave);
778
 
    xfce_rc_write_bool_entry (rc, "AddSelect", clipman->AddSelect);
779
 
    
780
 
    xfce_rc_write_bool_entry (rc, "ItemNumbers",  clipman->ItemNumbers);
781
 
 
782
 
    xfce_rc_write_int_entry (rc, "HistoryItems",   clipman->HistoryItems);
783
 
    xfce_rc_write_int_entry (rc, "MenuCharacters", clipman->MenuCharacters);
784
 
 
785
 
    xfce_rc_close (rc);
786
 
 
787
 
    /* Save history */
788
 
    if (clipman->ExitSave &&
789
 
        clipman->clips->len > 0
790
 
       )
791
 
    {
792
 
        DBG("Saving the clipboard history");
793
 
 
794
 
        file = xfce_resource_save_location (XFCE_RESOURCE_CACHE, "xfce4/clipman/textsrc", TRUE);
795
 
        if (file == NULL)
796
 
        {
797
 
            g_warning ("Unable to save the clipman history");
798
 
            return;
799
 
        }
800
 
 
801
 
        texts = g_malloc0 (clipman->clips->len * sizeof (gchar*));
802
 
        for (n_texts = i = 0; i < clipman->clips->len; i++)
803
 
        {
804
 
            clip = g_ptr_array_index (clipman->clips, i);
805
 
 
806
 
            /* Save the image */
807
 
            if (clip->datatype == IMAGE)
808
 
            {
809
 
                image_path = xfce_resource_save_location (XFCE_RESOURCE_CACHE, "xfce4/clipman/image.png", FALSE);
810
 
                gdk_pixbuf_save (GDK_PIXBUF (clip->data), image_path, "png", NULL, NULL);
811
 
                g_free (image_path);
812
 
                continue;
813
 
            }
814
 
 
815
 
            texts[n_texts++] = clip->data;
816
 
        }
817
 
 
818
 
        if (n_texts > 0)
819
 
        {
820
 
            keyfile = g_key_file_new ();
821
 
            g_key_file_set_string_list (keyfile, "texts", "texts", texts, n_texts);
822
 
            data = g_key_file_to_data (keyfile, NULL, NULL);
823
 
            g_file_set_contents (file, data, -1, NULL);
824
 
            g_free (data);
825
 
            g_key_file_free (keyfile);
826
 
        }
827
 
        g_free (texts);
828
 
        g_free (file);
829
 
    }
830
 
}
831
 
 
832
 
static void
833
 
clipman_read (ClipmanPlugin *clipman)
834
 
{
835
 
    gchar       *file;
836
 
    XfceRc      *rc;
837
 
    GKeyFile    *keyfile;
838
 
    gchar       **texts = NULL;
839
 
    GdkPixbuf   *image;
840
 
    gint        i;
841
 
 
842
 
    /* Because Clipman is unique, we use 1 config file */
843
 
    /*
844
 
    file = xfce_panel_plugin_save_location (clipman->plugin, FALSE);
845
 
    DBG("Read from file: %s", file);
846
 
    */
847
 
 
848
 
    /* Restore the preferences */
849
 
    DBG ("Restoring preferences");
850
 
    file = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, "xfce4/panel/clipman.rc", TRUE);
851
 
 
852
 
    if (G_UNLIKELY (!file))
853
 
        return;
854
 
 
855
 
    rc = xfce_rc_simple_open (file, FALSE);
856
 
    g_free (file);
857
 
 
858
 
    xfce_rc_set_group (rc, "Properties");
859
 
 
860
 
    clipman->ExitSave         = xfce_rc_read_bool_entry (rc, "ExitSave",     DEFEXITSAVE);
861
 
    clipman->AddSelect     = xfce_rc_read_bool_entry (rc, "AddSelect", DEFADDSELECT);
862
 
    clipman->ItemNumbers      = xfce_rc_read_bool_entry (rc, "ItemNumbers",    DEFITEMNUMBERS);
863
 
 
864
 
    clipman->HistoryItems     = xfce_rc_read_int_entry  (rc, "HistoryItems",   DEFHISTORY);
865
 
    clipman->MenuCharacters   = xfce_rc_read_int_entry  (rc, "MenuCharacters", DEFCHARS);
866
 
 
867
 
    if (clipman->HistoryItems > MAXHISTORY)
868
 
        clipman->HistoryItems = MAXHISTORY;
869
 
    if (clipman->HistoryItems < MINHISTORY)
870
 
        clipman->HistoryItems = MINHISTORY;
871
 
 
872
 
    if (clipman->MenuCharacters > MAXCHARS)
873
 
        clipman->MenuCharacters = MAXCHARS;
874
 
    if (clipman->MenuCharacters < MINCHARS)
875
 
        clipman->MenuCharacters = MINCHARS;
876
 
 
877
 
    xfce_rc_close (rc);
878
 
 
879
 
    if (!clipman->ExitSave)
880
 
      return;
881
 
 
882
 
    /* Restore the history */
883
 
    DBG("Restoring the clipboard");
884
 
    file = xfce_resource_save_location (XFCE_RESOURCE_CACHE, "xfce4/clipman/textsrc", FALSE);
885
 
    keyfile = g_key_file_new ();
886
 
    if (g_key_file_load_from_file (keyfile, file, G_KEY_FILE_NONE, NULL))
887
 
    {
888
 
        texts = g_key_file_get_string_list (keyfile, "texts", "texts", NULL, NULL);
889
 
        for (i = 0; texts != NULL && texts[i] != NULL && i < clipman->HistoryItems; i++)
890
 
          clipman_add_clip (clipman, texts[i], DEFAULT, RAWTEXT);
891
 
        g_unlink (file);
892
 
    }
893
 
    g_key_file_free (keyfile);
894
 
    g_strfreev (texts);
895
 
    g_free (file);
896
 
 
897
 
    file = xfce_resource_save_location (XFCE_RESOURCE_CACHE, "xfce4/clipman/image.png", FALSE);
898
 
    image = gdk_pixbuf_new_from_file (file, NULL);
899
 
    g_unlink (file);
900
 
    g_free (file);
901
 
    if (image != NULL)
902
 
    {
903
 
        clipman_add_clip (clipman, image, DEFAULT, IMAGE);
904
 
        g_object_unref (image);
905
 
    }
906
 
 
907
 
    clipman->DefaultIndex = -1;
908
 
    clipman->PrimaryIndex = -1;
909
 
}
910
 
 
911
 
static ClipmanPlugin *
912
 
clipman_new (XfcePanelPlugin *plugin)
913
 
{
914
 
    ClipmanPlugin *clipman;
915
 
    clipman = panel_slice_new0 (ClipmanPlugin);
916
 
 
917
 
    clipman->clips = g_ptr_array_new ();
918
 
    clipman->plugin = plugin;
919
 
 
920
 
    clipman->tooltip = gtk_tooltips_new ();
921
 
    g_object_ref (G_OBJECT (clipman->tooltip));
922
 
 
923
 
    clipman->DefaultIndex=-1;
924
 
    clipman->PrimaryIndex=-1;
925
 
 
926
 
    /* Load Settings, and possibly Data */
927
 
    clipman_read (clipman);
928
 
 
929
 
    /* Create panel widgets */
930
 
    clipman->button = xfce_create_panel_toggle_button ();
931
 
    gtk_widget_show (clipman->button);
932
 
 
933
 
    clipman->icon = gtk_image_new ();
934
 
    gtk_widget_show (clipman->icon);
935
 
    gtk_container_add (GTK_CONTAINER (clipman->button), clipman->icon);
936
 
 
937
 
    gtk_tooltips_set_tip (GTK_TOOLTIPS(clipman->tooltip),
938
 
                          clipman->button, _("Clipboard Manager"),
939
 
                          NULL);
940
 
 
941
 
    g_signal_connect(clipman->button, "button_press_event",
942
 
            G_CALLBACK(clipman_icon_clicked), clipman);
943
 
 
944
 
    /* Start the clipman_timed_poll function */
945
 
    /* TODO Run the timeout only if the plugin takes care of the selections */
946
 
    clipman->TimeoutId = g_timeout_add_full(G_PRIORITY_LOW,
947
 
                                            TIMER_INTERVAL,
948
 
                                            (GSourceFunc) clipman_timed_poll,
949
 
                                            clipman,
950
 
                                            (GDestroyNotify) clipman_reset_timeout);
951
 
 
952
 
    /* Connect to the clipboards */
953
 
    defaultClip = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
954
 
    primaryClip = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
955
 
 
956
 
    /* Respond to clipboard events - rather than polling twice a second */
957
 
        g_signal_connect(G_OBJECT(defaultClip), "owner-change", G_CALLBACK(clipboard_changed), clipman);
958
 
          g_signal_connect(G_OBJECT(primaryClip), "owner-change", G_CALLBACK(clipboard_changed), clipman);
959
 
 
960
 
    return clipman;
961
 
}
962
 
 
963
 
static void
964
 
clipman_free (XfcePanelPlugin *plugin,
965
 
              ClipmanPlugin   *clipman)
966
 
{
967
 
    gint        i;
968
 
    ClipmanClip *clip;
969
 
    GtkWidget   *dialog;
970
 
 
971
 
    /* Valgrind notes:
972
 
       - primaryClip and defaultClip should be cleared, but gtk
973
 
         takes care about this
974
 
    */
975
 
 
976
 
    /* Free the clipboards */
977
 
    gtk_clipboard_clear (primaryClip);
978
 
    gtk_clipboard_clear (defaultClip);
979
 
 
980
 
    /* Destroy the setting dialog, if this open */
981
 
    dialog = g_object_get_data (G_OBJECT (plugin), "dialog");
982
 
 
983
 
    if (dialog)
984
 
        gtk_widget_destroy (dialog);
985
 
 
986
 
    /* Stop the check loop */
987
 
    clipman->killTimeout = TRUE;
988
 
    if (clipman->TimeoutId != 0)
989
 
    {
990
 
        g_source_remove(clipman->TimeoutId);
991
 
        clipman->TimeoutId = 0;
992
 
    }
993
 
 
994
 
    /* Remove clipboard items */
995
 
    for (i=(gint)clipman->clips->len-1; i>=0; i--)
996
 
    {
997
 
        clip = g_ptr_array_index (clipman->clips, i);
998
 
        g_ptr_array_remove_fast (clipman->clips, clip);
999
 
        clipman_free_clip (clip);
1000
 
    }
1001
 
    g_ptr_array_free (clipman->clips, TRUE);
1002
 
 
1003
 
    gtk_tooltips_set_tip (clipman->tooltip, clipman->button, NULL, NULL);
1004
 
    g_object_unref (G_OBJECT (clipman->tooltip));
1005
 
 
1006
 
    gtk_widget_destroy (clipman->icon);
1007
 
    gtk_widget_destroy (clipman->button);
1008
 
 
1009
 
    clipman->plugin = NULL;
1010
 
 
1011
 
    DBG ("Plugin Freed");
1012
 
 
1013
 
    panel_slice_free (ClipmanPlugin, clipman);
1014
 
}
1015
 
 
1016
 
static gboolean
1017
 
clipman_set_size (XfcePanelPlugin *plugin,
1018
 
                  gint             wsize,
1019
 
                  ClipmanPlugin   *clipman)
1020
 
{
1021
 
    GdkPixbuf *pb;
1022
 
    gint       size;
1023
 
 
1024
 
    gtk_widget_set_size_request (clipman->button, wsize, wsize);
1025
 
 
1026
 
    size = wsize - 2 - (2 * MAX (clipman->button->style->xthickness,
1027
 
                                 clipman->button->style->ythickness));
1028
 
 
1029
 
    DBG("Set clipman button size to %dpx, load icon at %dpx", wsize, size);
1030
 
 
1031
 
    pb = xfce_themed_icon_load ("gtk-paste", size);
1032
 
    gtk_image_set_from_pixbuf (GTK_IMAGE (clipman->icon), pb);
1033
 
    g_object_unref (G_OBJECT (pb));
1034
 
 
1035
 
    return TRUE;
1036
 
}
1037
 
 
1038
 
static void
1039
 
clipman_construct (XfcePanelPlugin *plugin)
1040
 
{
1041
 
    ClipmanPlugin *clipman;
1042
 
 
1043
 
    xfce_textdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");
1044
 
 
1045
 
    clipman = clipman_new (plugin);
1046
 
 
1047
 
    gtk_container_add (GTK_CONTAINER (plugin), clipman->button);
1048
 
 
1049
 
    xfce_panel_plugin_add_action_widget (plugin, clipman->button);
1050
 
 
1051
 
    g_signal_connect (plugin, "free-data",
1052
 
        G_CALLBACK (clipman_free), clipman);
1053
 
 
1054
 
    g_signal_connect (plugin, "save",
1055
 
        G_CALLBACK (clipman_save), clipman);
1056
 
 
1057
 
    g_signal_connect (plugin, "size-changed",
1058
 
        G_CALLBACK (clipman_set_size), clipman);
1059
 
 
1060
 
    xfce_panel_plugin_menu_show_configure (plugin);
1061
 
    g_signal_connect (plugin, "configure-plugin",
1062
 
        G_CALLBACK (clipman_configure), clipman);
1063
 
}