1
/* Copyright (C) 2007-2008 by Xyhthyx <xyhthyx@gmail.com>
3
* This file is part of Parcellite.
5
* Parcellite is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 3 of the License, or
8
* (at your option) any later version.
10
* Parcellite is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
We keep track of a delete list while the history menu is up. We add/remove items from that
20
list until we get a selection done event, then we delete those items from the real history
22
From http://standards.freedesktop.org/clipboards-spec/clipboards-latest.txt
24
Application authors should follow the following guidelines to get
27
- selecting but with no explicit copy should only set PRIMARY,
30
- middle mouse button should paste PRIMARY, never CLIPBOARD
32
- explicit cut/copy commands (i.e. menu items, toolbar buttons)
33
should always set CLIPBOARD to the currently-selected data (i.e.
34
conceptually copy PRIMARY to CLIPBOARD)
36
- explicit cut/copy commands should always set both CLIPBOARD and
37
PRIMARY, even when copying doesn't involve a selection (e.g. a
38
"copy url" -option which explicitly copies an url without the
39
url being selected first)
41
- explicit paste commands should paste CLIPBOARD, not PRIMARY
43
- a selection becoming unselected should never unset PRIMARY
45
- possibly contradicting the ICCCM, clients don't need to support
46
SECONDARY, though if anyone can figure out what it's good
47
for they should feel free to use it for that
49
- cut buffers are evil; they only support ASCII, they don't
50
work with many clients, and they require data to be
51
copied to the X server. Therefore clients should avoid
52
using cut buffers and use only selections.
54
We can monitor ownership change with:
55
// This callback is invoked when the clipboard owner changes.
56
void handle_owner_change(GtkClipboard *clipboard, GdkEvent *event, gpointer data){ }
58
g_signal_connect(clipboard, "owner-change", G_CALLBACK(handle_owner_change), NULL);
65
#include "parcellite.h"
67
/**ACT are actions, and MODE is the mode of the action */
68
/** #define ACT_STOP 0
71
/** defines the mode of the above actions. These are bit-wise */
74
#define CMODE_ALL 3 /**needs to be or of the above */
76
#define FIFCMD_STOP_PRI "stop_pri"
77
#define FIFCMD_STOP_CLI "stop_cli"
78
#define FIFCMD_STOP_ALL "stop_all"
79
#define FIFCMD_RUN_PRI "run_pri"
80
#define FIFCMD_RUN_CLI "run_cli"
81
#define FIFCMD_RUN_ALL "run_all"
84
/* Uncomment the next line to print a debug trace. */
90
# define TRACE(x) do {} while (FALSE);
92
/**see parcellite.h for define */
94
static int debug_update=0; /**disable/enable DTRACE */
95
# define DTRACE(x) if (debug_update) x
97
# define DTRACE(x) do {} while (FALSE);
100
static GtkClipboard* primary;
101
static GtkClipboard* clipboard;
103
#ifdef HAVE_APPINDICATOR
104
static AppIndicator *indicator=NULL;
105
static GtkWidget *indicator_menu = NULL;
107
static GtkStatusIcon *status_icon;
108
static GMutex *clip_lock=NULL;
109
GMutex *hist_lock=NULL;
110
static gboolean actions_lock = FALSE;
111
static int show_icon=0;
112
static int have_appindicator=0; /**if set, we have a running indicator-appmenu */
113
static gchar *appindicator_process="indicator-messages-service"; /**process name */
115
static int cmd_mode=CMODE_ALL; /**both clipboards */
116
/** static int cmd_state=ACT_RUN; running */
117
/**defines for moving between clipboard histories */
118
#define HIST_MOVE_TO_CANCEL 0
119
#define HIST_MOVE_TO_OK 1
120
/**clipboard handling modes */
121
#define H_MODE_INIT 0 /**clear out clipboards */
122
#define H_MODE_NEW 1 /**new text, process it */
123
#define H_MODE_LIST 2 /**from list, just put it on the clip */
124
#define H_MODE_CHECK 3 /**see if there is new/lost contents. */
125
#define H_MODE_LAST 4 /**just return the last updated value. */
126
#define H_MODE_IGNORE 5 /**just put it on the clipboard, do not process
127
and do not add to hist list */
129
#define EDIT_MODE_USE_RIGHT_CLICK 1 /**used in edit dialog creation to determine behaviour.
130
If this is set, it will edit the entry, and replace it in the history. */
131
#define EDIT_MODE_RC_EDIT_SET 2 /**used to */
133
/**protos in this file */
134
void create_app_indicator(void);
136
/**Turns up in 2.16 */
137
int p_strcmp (const char *str1, const char *str2)
139
#if (GTK_MAJOR_VERSION > 2 || ( GTK_MAJOR_VERSION == 2 && GTK_MAJOR_VERSION >= 16))
140
return g_strcmp0(str1,str2);
142
if(NULL ==str1 && NULL == str2)
144
if(NULL ==str1 && str2)
146
if(NULL ==str2 && str1)
148
return strcmp(str1,str2);
152
/***************************************************************************/
153
/** Process the text based on our preferences.
155
\n\b Returns: processed text, or NULL if it is invalid.
156
****************************************************************************/
157
gchar *process_new_item(GtkClipboard *clip,gchar *ntext)
165
/**we now check our options... */
166
/*printf("opt\n"); fflush(NULL); */
167
if (get_pref_int32("hyperlinks_only")){
168
if(is_hyperlink(ntext))
171
/*printf("wo\n"); fflush(NULL); */
172
if(get_pref_int32("ignore_whiteonly")){
174
for (s=ntext; NULL !=s && *s; ++s){
176
/* printf("Saw 0x%x\n",*s); */
184
/**set the clipboard to the last entry - effectively deleting this entry */
187
process: /**now process the text. */
188
/*printf("proc\n"); fflush(NULL); */
189
len=strlen(ntext);/*g_utf8_strlen(ntext,-1); */
190
len= validate_utf8_text(ntext, len);
194
if(get_pref_int32("trim_newline")){
199
c=g_utf8_next_char(c);
203
if( get_pref_int32("trim_wspace_begend") )
204
ntext=g_strstrip(ntext);
211
/***************************************************************************/
214
\n\b Returns: text that was updated or NULL if not.
215
****************************************************************************/
216
gchar *_update_clipboard (GtkClipboard *clip, gchar *n, gchar **old, int set)
223
g_printf("set PRI to %s\n",n);
225
g_printf("set CLI to %s\n",n);
228
gtk_clipboard_set_text(clip, n, -1);
235
}else if( NULL != old){
244
/***************************************************************************/
245
/** This checks to see if ANY content exists (i.e. like images).
248
****************************************************************************/
249
gboolean content_exists(GtkClipboard *clip)
253
gboolean contents = gtk_clipboard_wait_for_targets(clip, &targets, &count);
258
/***************************************************************************/
261
\n\b Returns: clipboard contents, to be freed with g_free, or NULL if empty.
262
****************************************************************************/
263
gchar *is_clipboard_empty(GtkClipboard *clip)
267
if(clipboard == clip) g_printf("-%s-",gtk_clipboard_wait_for_text(clip));
268
gboolean contents = gtk_clipboard_wait_for_targets(clip, &targets, &count);
269
if(clipboard == clip) g_printf("-%s-2nd-",gtk_clipboard_wait_for_text(clip));
271
if(TRUE == contents || count >0)
274
if(TRUE == gtk_clipboard_wait_is_text_available(clip))
275
return(gtk_clipboard_wait_for_text(clip));
277
/**attempt to fix bug 87, Error converting selection from UTF8_STRING
278
Go http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
279
line 2.3.4 will reproduce this using FireFox. */
280
if(TRUE == gtk_clipboard_wait_is_text_available(clip)){
281
gchar *x=gtk_clipboard_wait_for_text(clip);
282
if(FALSE == g_utf8_validate(x,-1,NULL))
283
gtk_clipboard_set_text(clip,"_BAD_UTF8_",-1);
286
/**if NULL == x, we have a problem with whatever is in the clipboard. */
295
/***************************************************************************/
296
/** Update one clipboard at a time.
299
****************************************************************************/
300
gchar *update_clipboard(GtkClipboard *clip,gchar *intext, gint mode)
302
/**current/last item in clipboard */
303
static gchar *ptext=NULL;
304
static gchar *ctext=NULL;
305
static gchar *last=NULL; /**last text change, for either clipboard */
306
gchar **existing, *changed=NULL;
308
GdkModifierType button_state;
309
/* gchar *clipname; */
311
if( H_MODE_LAST == mode)
314
/* clipname="PRI "; */
317
/* clipname="CLI "; */
321
/** if(H_MODE_CHECK!=mode ) */
322
/*g_printf("HC%d-%c: in %s,ex %s\n",mode,clip==primary?'p':'c',intext,*existing); */
323
if( H_MODE_INIT == mode){
324
if(NULL != *existing)
328
_update_clipboard(clip,intext,NULL,1);
329
/* gtk_clipboard_set_text(clip, intext, -1); */
332
/**check that our clipboards are valid and user wants to use them */
333
if((clip != primary && clip != clipboard) ||
334
(clip == primary && !get_pref_int32("use_primary")) ||
335
(clip == clipboard && !get_pref_int32("use_copy")))
338
if( H_MODE_CHECK==mode &&clip == primary){/*fix auto-deselect of text in applications like DevHelp and LyX*/
339
gdk_window_get_pointer(NULL, NULL, NULL, &button_state);
340
if ( button_state & (GDK_BUTTON1_MASK|GDK_SHIFT_MASK) ) /**button down, done. */
343
if(0 && NULL != intext){ /**we run this in process_new_item */
344
validate_utf8_text(intext,strlen(intext));
346
/*g_printf("BS=0x%02X ",button_state); */
347
if( H_MODE_IGNORE == mode){ /**ignore processing and just put it on the clip. */
348
DTRACE(g_fprintf(stderr,"%sJustSet '%s'\n",clip==clipboard?"CLI":"PRI",intext));
349
_update_clipboard(clip,intext,NULL,1);
350
/*gtk_clipboard_set_text(clip, intext, -1); */
353
if(H_MODE_LIST == mode && 0 != p_strcmp(intext,*existing)){ /**just set clipboard contents. Already in list */
354
DTRACE(g_fprintf(stderr,"%sInList '%s' ex '%s'\n",clip==clipboard?"CLI":"PRI",intext,*existing));
355
last=_update_clipboard(clip,intext,existing,1);
356
if( NULL != last){/**maintain persistence, if set */
357
append_item(last,get_pref_int32("current_on_top")?HIST_DEL|HIST_CHECKDUP|HIST_KEEP_FLAGS:0);
361
/**check for lost contents and restore if lost */
362
changed=is_clipboard_empty(clip);
364
if(0 == validate_utf8_text(changed,strlen(changed)))
368
if(NULL != *existing && NULL == changed && 1 == get_pref_int32("restore_empty")) {
369
DTRACE(g_fprintf(stderr,"%sclp empty, ",clip==clipboard?"CLI":"PRI"));
370
/* Only recover lost contents if there isn't any other type of content in the clipboard */
371
if (!content_exists(clip)) {
372
DTRACE(g_fprintf(stderr,"set to '%s'\n",*existing));
373
_update_clipboard(clip, *existing,NULL,1);
374
/*gtk_clipboard_set_text(clip, *existing, -1); */
377
DTRACE(g_fprintf(stderr,"Left Null\n"));
382
/**check for changed clipboard content - in all modes */
383
/*changed=gtk_clipboard_wait_for_text(clip); */
384
if(0 == p_strcmp(*existing, changed) ){
385
g_free(changed); /**no change, do nothing */
388
DTRACE(g_fprintf(stderr,"%sclp changed: ex '%s' is '%s' - ",clip==clipboard?"CLI":"PRI",*existing,changed));
389
if(NULL != (processed=process_new_item(clip,changed)) ){
390
/**only check processed/changed. No need to update this clip, since the text is already there. */
391
if(0 == p_strcmp(processed,changed)) set=0;
393
DTRACE(g_fprintf(stderr,"set=%d. p='%s'\n",set,processed));
394
/*set=1; */ /** Always set the text.*/
395
last=_update_clipboard(clip,processed,existing,set);
396
}else {/**restore clipboard - new item is binary/garbage/empty */
399
if(NULL ==*existing && NULL != history_list){
400
struct history_item *c;
401
c=(struct history_item *)(history_list->data);
406
DTRACE(g_fprintf(stderr,"\n%srestore clp '%s', ex='%s'\n",clip==clipboard?"CLI":"PRI",d,*existing));
407
last=_update_clipboard(clip,d,existing,1);
412
append_item(last,get_pref_int32("current_on_top")?HIST_DEL|HIST_CHECKDUP|HIST_KEEP_FLAGS:0);
416
if( H_MODE_CHECK==mode ){
419
/**FIXME: Do we use the changed clip item or the one passed to us?
420
hmmm Use the changed one.
424
if(H_MODE_NEW==mode){
425
if(NULL != (processed=process_new_item(clip,intext)) ){
426
/* g_printf("%sNEW '%s'\n",clipname,processed); */
427
if(0 == p_strcmp(processed,*existing))set=0;
429
last=_update_clipboard(clip,processed,existing,set);
431
append_item(last,get_pref_int32("current_on_top")?HIST_DEL|HIST_CHECKDUP|HIST_KEEP_FLAGS:0);
440
/***************************************************************************/
441
/** Convience function to update both clipboards at the same time
444
****************************************************************************/
445
void update_clipboards(gchar *intext, gint mode)
447
/*g_printf("upclips\n"); */
448
update_clipboard(primary, intext, mode);
449
update_clipboard(clipboard, intext, mode);
452
/***************************************************************************/
453
/** Run a command. For now, just start and stop
456
****************************************************************************/
457
void do_command(gchar *buf, gint len)
459
g_printf("Got '%s' cmd\n",buf);
460
if(!p_strcmp(buf,FIFCMD_RUN_ALL)) {
464
if(!p_strcmp(buf,FIFCMD_RUN_CLI)) {
468
if(!p_strcmp(buf,FIFCMD_RUN_PRI)) {
472
if(!p_strcmp(buf,FIFCMD_STOP_ALL)) {
473
cmd_mode&=~(CMODE_ALL);
476
if(!p_strcmp(buf,FIFCMD_STOP_CLI)) {
477
cmd_mode&=~(CMODE_CLI);
480
if(!p_strcmp(buf,FIFCMD_STOP_PRI)) {
481
cmd_mode&=~(CMODE_PRI);
487
/***************************************************************************/
488
/** Checks the clipboards and fifos for changes.
491
****************************************************************************/
492
void check_clipboards(gint mode)
494
gchar *ptext, *ctext, *last;
497
/*g_printf("check_clipboards\n"); */
498
/*g_mutex_lock(clip_lock); */
499
if(fifo->clen){/**we have a command to execute */
500
/*fifo->which should be ID_CMD: */
501
if(fifo->dbg) g_printf("Running CMD '%s'\n",fifo->cbuf);
502
do_command(fifo->cbuf, fifo->clen);
503
if(fifo->dbg) g_printf("mode is 0x%X\n",cmd_mode);
507
if(!(CMODE_ALL & cmd_mode))
512
fifo->rlen=validate_utf8_text(fifo->buf, fifo->rlen);
513
if(fifo->dbg) g_printf("Setting PRI '%s'\n",fifo->buf);
514
update_clipboard(primary, fifo->buf, H_MODE_NEW);
519
fifo->rlen=validate_utf8_text(fifo->buf, fifo->rlen);
520
if(fifo->dbg) g_printf("Setting CLI '%s'\n",fifo->buf);
521
update_clipboard(clipboard, fifo->buf, H_MODE_NEW);
527
fifo->rlen=validate_utf8_text(fifo->buf, fifo->rlen);
528
g_printf("CLIP not set, discarding '%s'\n",fifo->buf);
533
ptext=update_clipboard(primary, NULL, H_MODE_CHECK);
534
ctext=update_clipboard(clipboard, NULL, H_MODE_CHECK);
536
/*g_printf("pt=%s,ct=%s\n",ptext,ctext); */
537
/* Synchronization */
538
if (get_pref_int32("synchronize")) {
540
if(NULL==ptext && NULL ==ctext)
542
last=update_clipboard(NULL, NULL, H_MODE_LAST);
543
if( NULL != last && 0 != p_strcmp(ptext,ctext)){
544
/**last is a copy, of things that may be deallocated */
546
/*g_printf("Update clipb '%s' '%s' to '%s'\n",ptext,ctext,last); */
547
update_clipboards(last, H_MODE_LIST);
554
/*g_mutex_unlock(clip_lock); */
556
#ifdef HAVE_APPINDICATOR
557
/***************************************************************************/
558
/** Check for appindicator.
561
****************************************************************************/
562
gboolean check_for_appindictor( gpointer data)
564
int mode=PROC_MODE_STRSTR;
565
if(NULL != appindicator_process && !have_appindicator ){
566
/*g_printf("Looking for '%s'\n",appindicator_process); */
567
if(get_pref_int32("multi_user"))
568
mode|=PROC_MODE_USER_QUALIFY;
569
if(proc_find(appindicator_process,mode,NULL) >0){
571
if(NULL == indicator && show_icon)
572
create_app_indicator();
579
/***************************************************************************/
580
/** Called every CHECK_INTERVAL seconds to check for new items
583
****************************************************************************/
584
gboolean check_clipboards_tic(gpointer data)
586
/** gchar *txt=gtk_clipboard_wait_for_text(clipboard);
587
g_printf("%s\n",txt);*/
588
check_clipboards(H_MODE_CHECK);
589
#ifdef HAVE_APPINDICATOR
590
if(have_appindicator){
591
if(NULL == indicator && show_icon)
592
create_app_indicator();
599
/* Thread function called for each action performed */
600
static void *execute_action(void *command)
604
if (!have_appindicator && show_icon) {
605
gtk_status_icon_set_from_stock((GtkStatusIcon*)status_icon, GTK_STOCK_EXECUTE);
606
gtk_status_icon_set_tooltip((GtkStatusIcon*)status_icon, _("Executing action..."));
608
if(system((gchar*)command))
609
g_fprintf(stderr,"sytem command '%s' failed\n",(gchar *)command);
610
if (!have_appindicator &&show_icon) {
611
gtk_status_icon_set_from_icon_name((GtkStatusIcon*)status_icon, PARCELLITE_ICON);
612
gtk_status_icon_set_tooltip((GtkStatusIcon*)status_icon, _("Clipboard Manager"));
614
actions_lock = FALSE;
615
g_free((gchar*)command);
616
/* Exit this thread */
620
/* Called when execution action exits */
621
static void action_exit(GPid pid, gint status, gpointer data)
623
g_spawn_close_pid(pid);
624
if (!have_appindicator && show_icon) {
625
gtk_status_icon_set_from_icon_name((GtkStatusIcon*)status_icon, PARCELLITE_ICON);
626
gtk_status_icon_set_tooltip((GtkStatusIcon*)status_icon, _("Clipboard Manager"));
628
actions_lock = FALSE;
631
/* Called when an action is selected from actions menu */
632
static void action_selected(GtkButton *button, gpointer user_data)
634
/* Change icon and enable lock */
636
if (!have_appindicator && show_icon) {
637
gtk_status_icon_set_from_stock((GtkStatusIcon*)status_icon, GTK_STOCK_EXECUTE);
638
gtk_status_icon_set_tooltip((GtkStatusIcon*)status_icon, _("Executing action..."));
640
/* Insert clipboard into command (user_data), and prepare it for execution */
641
gchar* clipboard_text = gtk_clipboard_wait_for_text(clipboard);
642
g_fprintf(stderr,"Got cmd '%s', text '%s'->",(gchar *)user_data,clipboard_text);fflush(NULL);
643
gchar* command=g_strdup_printf((gchar *)user_data,clipboard_text);
644
g_fprintf(stderr," '%s'\n",command);fflush(NULL);
645
g_free(clipboard_text);
647
gchar* shell_command = g_shell_quote(command);
649
gchar* cmd = g_strconcat("/bin/sh -c ", shell_command, NULL);
650
g_free(shell_command);
655
g_shell_parse_argv(cmd, NULL, &argv, NULL);
656
g_fprintf(stderr,"cmd '%s' argv '%s' '%s' '%s'\n",cmd,argv[1],argv[2],argv[3]);
658
g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL);
659
g_child_watch_add(pid, (GChildWatchFunc)action_exit, NULL);
663
/* Called when Edit Actions is selected from actions menu */
664
static void edit_actions_selected(GtkButton *button, gpointer user_data)
666
/* This helps prevent multiple instances */
667
if (!gtk_grab_get_current())
668
/* Show the preferences dialog on the actions tab */
669
show_preferences(ACTIONS_TAB);
672
/***************************************************************************/
673
/** Called when Edit is selected from history menu
676
****************************************************************************/
677
static void edit_selected(GtkMenuItem *menu_item, gpointer user_data)
679
struct history_info *h=(struct history_info*)user_data;
681
struct history_item *c=NULL;
684
/*g_fprintf(stderr,"edit_selected call\n"); */
685
/* This helps prevent multiple instances */
686
if (!gtk_grab_get_current() ||h->wi.tmp1&EDIT_MODE_RC_EDIT_SET) {
687
gchar* current_clipboard_text=NULL;
688
/*g_fprintf(stderr,"current..."); */
689
/* Create clipboard buffer and set its text */
690
GtkTextBuffer* clipboard_buffer = gtk_text_buffer_new(NULL);
691
if(h->wi.index != -1){/**use index as pointer to text */
692
element = g_list_nth(history_list, h->wi.index);
694
g_fprintf(stderr,"edit_selected: element is NULL\n");
697
c=(struct history_item *)(element->data);
699
g_fprintf(stderr,"edit_selected: element->data is NULL\n");
702
current_clipboard_text=p_strdup(c->text);
703
/*g_fprintf(stderr,"%s ",c->text); */
707
if( NULL != h->element_text){/**this case should never happen */
708
g_fprintf(stderr,"Oops. shouldn't be here\n");
709
current_clipboard_text=p_strdup(h->element_text);
712
g_fprintf(stderr,"List Empty. Grab clipboard.\n");
713
current_clipboard_text = gtk_clipboard_wait_for_text(clipboard);
718
if (current_clipboard_text != NULL) {
719
/*g_fprintf(stderr,"Got '%s'\n",current_clipboard_text); */
720
TRACE(g_fprintf(stderr,"Got '%s'\n",current_clipboard_text));
721
gtk_text_buffer_set_text(clipboard_buffer, current_clipboard_text, -1);
723
g_fprintf(stderr,"NULL text to edit. Nothing to do.\n");
726
/*g_fprintf(stderr,"cr dialog\n"); */
728
/* Create the dialog */
729
GtkWidget* dialog = gtk_dialog_new_with_buttons(_("Editing Clipboard"), NULL,
730
(GTK_DIALOG_MODAL + GTK_DIALOG_NO_SEPARATOR),
731
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
732
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
734
gtk_window_set_default_size((GtkWindow*)dialog, 450, 300);
735
gtk_window_set_icon((GtkWindow*)dialog, gtk_widget_render_icon(dialog, GTK_STOCK_EDIT, -1, NULL));
737
/* Build the scrolled window with the text view */
738
GtkWidget* scrolled_window = gtk_scrolled_window_new((GtkAdjustment*) gtk_adjustment_new(0, 0, 0, 0, 0, 0),
739
(GtkAdjustment*) gtk_adjustment_new(0, 0, 0, 0, 0, 0));
741
gtk_scrolled_window_set_policy((GtkScrolledWindow*)scrolled_window,
742
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
744
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrolled_window, TRUE, TRUE, 2);
745
GtkWidget* text_view = gtk_text_view_new_with_buffer(clipboard_buffer);
746
gtk_text_view_set_left_margin((GtkTextView*)text_view, 2);
747
gtk_text_view_set_right_margin((GtkTextView*)text_view, 2);
748
gtk_container_add((GtkContainer*)scrolled_window, text_view);
751
gtk_widget_show_all(dialog);
753
if (gtk_dialog_run((GtkDialog*)dialog) == GTK_RESPONSE_ACCEPT) {
754
GtkTextIter start, end;
757
gtk_text_buffer_get_start_iter(clipboard_buffer, &start);
758
gtk_text_buffer_get_end_iter(clipboard_buffer, &end);
759
gchar* new_clipboard_text = gtk_text_buffer_get_text(clipboard_buffer, &start, &end, TRUE);
760
slen=strlen(new_clipboard_text);
761
if(0 == slen ){ /**just delete history entry, and set next in order to clipboard */
762
if(NULL == element){/**just clear clipboard? or FIXME: is there a way to determine the element? */
763
/*gtk_clipboard_set_text(clipboard,"",0); */
764
gtk_clipboard_clear(clipboard);
767
/*g_fprintf(stderr,"Freeing %p\n",element->data); */
768
g_free(element->data);
769
if(element == history_list && NULL != element->next ){ /**set clipboard(s) to next entry */
770
c=(struct history_item *)(element->next->data);
775
history_list = g_list_delete_link(history_list, element);
776
if(NULL != ntext)/**set clipboards to next entry FIXME: Need logic here as to which clip(s) to update. */
777
update_clipboards(ntext, H_MODE_LIST);
778
}else {/**Text is not blank */
779
/*g_fprintf(stderr,"Try to add '%s'\n",new_clipboard_text); */
785
/**save changes to the history - deallocate current entry, and add new entry \Uffffffff�*/
786
/**FIXME: Need to filter this through existing & valid text? */
787
struct history_item *n=new_clip_item(type,slen, new_clipboard_text);
791
if(NULL != element && NULL != element->data){
792
g_free(element->data);
793
history_list=g_list_delete_link(history_list, element);
794
history_list=g_list_prepend(history_list,c);
797
/**Does this cause crash since we are in the middle of a history window?
798
Need to kill history menu too?
799
What about setting up an indicator that on next tic, if history is not active, we update?
801
update_clipboards(n->text, H_MODE_NEW);
804
if(NULL != new_clipboard_text)
805
g_free(new_clipboard_text);
807
gtk_widget_destroy(dialog);
808
g_free(current_clipboard_text);
810
else TRACE(g_fprintf(stderr,"gtk_grab_get_current returned !0\n"));
814
/***************************************************************************/
815
/** Only enabled when persistent history is enabled.
818
****************************************************************************/
819
gboolean history_item_right_click_on_edit(GtkWidget *menuitem, gpointer data)
821
struct history_info *h=(struct history_info*)data;
822
h->wi.tmp1|=EDIT_MODE_USE_RIGHT_CLICK;
823
edit_selected((GtkMenuItem *)menuitem, data);
826
/***************************************************************************/
827
/** Paste all. Grab all of the history and paste it to the clipboard.
830
****************************************************************************/
831
gboolean history_item_right_click_on_copy_all(GtkWidget *menuitem, gpointer data)
836
gchar*delim= g_strcompress(get_pref_string("persistent_delim"));
838
struct history_info *h=(struct history_info*)data;
839
/**if persistent and this history is persistent, only get persistent.
840
FIXME: this will only work with separarate set.
842
which=(get_pref_int32("persistent_history") && h->histno == HIST_DISPLAY_PERSISTENT);
843
for (element = history_list; element != NULL; element = element->next) {
844
struct history_item *c=(struct history_item *)(element->data);
845
if(which && !(c->flags&CLIP_TYPE_PERSISTENT))
851
str=g_strconcat(str,c->text,NULL);
853
str=g_strconcat(str,delim,c->text,NULL);
860
update_clipboards(str,H_MODE_IGNORE);
866
/***************************************************************************/
870
****************************************************************************/
871
gboolean handle_history_item_right_click (int i, gpointer data)
873
/* we passed the view as userdata when we connected the signal */
874
struct history_info *h=(struct history_info*)data;
875
struct history_item *c=NULL;
877
GList* element = g_list_nth(history_list, h->wi.index);
879
c=(struct history_item *)(element->data);
880
/*g_printf("%s ",c->text); */
884
case HIST_MOVE_TO_CANCEL:
885
/* g_print("canceled\n"); */
887
case HIST_MOVE_TO_OK:
888
/* g_printf("Move to"); */
889
handle_marking(h,h->wi.item,h->wi.index,OPERATE_PERSIST);
892
/*gtk_widget_grab_focus(h->menu); */
895
/**callback wrappers for the above function */
896
gboolean history_item_right_click_on_move (GtkWidget *menuitem, gpointer data)
898
return handle_history_item_right_click(HIST_MOVE_TO_OK,data);
900
gboolean history_item_right_click_on_cancel (GtkWidget *menuitem, gpointer data)
902
return handle_history_item_right_click(HIST_MOVE_TO_CANCEL,data);
904
/***************************************************************************/
908
if (event->type == GDK_BUTTON_PRESS && event->button == 3)
909
view_popup_menu(treeview, event, userdata);
910
h->wi.index contains the element of the item clicked on.
911
****************************************************************************/
912
void history_item_right_click (struct history_info *h, GdkEventKey *e, gint index)
914
GtkWidget *menu, *menuitem;
916
struct history_item *c=NULL;
918
GList* element = g_list_nth(history_list, h->wi.index);
920
c=(struct history_item *)(element->data);
921
/*g_printf("%s ",c->text); */
924
g_fprintf(stderr,"h-i-r-c: h is NULL");
928
if(get_pref_int32("persistent_history")) {
929
menu = gtk_menu_new();
930
menuitem = gtk_menu_item_new_with_label("Copy All to Clip");
931
g_signal_connect(menuitem, "activate", (GCallback) history_item_right_click_on_copy_all, (gpointer)h);
932
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
933
/*g_printf("CreatehistR\n"); */
935
if(c->flags & CLIP_TYPE_PERSISTENT)
936
menuitem = gtk_menu_item_new_with_label("Move To Normal");
938
menuitem = gtk_menu_item_new_with_label("Move To Persistent");
940
menuitem = gtk_menu_item_new_with_label("Move To?");
942
g_signal_connect(menuitem, "activate",(GCallback) history_item_right_click_on_move, (gpointer)h);
943
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
945
menuitem = gtk_menu_item_new_with_label("Edit");
946
g_signal_connect(menuitem, "activate", (GCallback) history_item_right_click_on_edit, (gpointer)h);
947
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
949
menuitem = gtk_menu_item_new_with_label("Cancel");
950
g_signal_connect(menuitem, "activate", (GCallback) history_item_right_click_on_cancel, (gpointer)h);
951
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
953
gtk_widget_show_all(menu);
955
/* Note: event can be NULL here when called from view_onPopupMenu;
956
* gdk_event_get_time() accepts a NULL argument */
957
gtk_menu_popup(GTK_MENU(menu), h->menu, NULL, NULL, NULL,
959
/*(e != NULL) ? ((GdkEventButton *)e)->button : 0, */
960
gdk_event_get_time((GdkEvent*)e));
961
/*gtk_widget_grab_focus(menu); */
962
}else if(get_pref_int32("rc_edit") ){ /**just edit the selected text */
963
h->wi.tmp1|=EDIT_MODE_USE_RIGHT_CLICK|EDIT_MODE_RC_EDIT_SET;
964
edit_selected((GtkMenuItem *)NULL,(gpointer)h);
970
/* Called when Clear is selected from history menu */
971
static void clear_selected(GtkMenuItem *menu_item, gpointer user_data)
974
/* Check for confirm clear option */
975
if (get_pref_int32("confirm_clear")) {
976
GtkWidget* confirm_dialog = gtk_message_dialog_new(NULL,
979
GTK_BUTTONS_OK_CANCEL,
980
_("Clear the history?"));
982
if (gtk_dialog_run((GtkDialog*)confirm_dialog) != GTK_RESPONSE_OK) {
985
gtk_widget_destroy(confirm_dialog);
988
struct history_info *h=(struct history_info *)user_data;
989
/* Clear history and free history-related variables */
990
remove_deleted_items(h); /**fix bug 92, Shift/ctrl right-click followed by clear segfaults/double free. */
992
/*g_printf("Clear hist done, h=%p, h->delete_list=%p\n",h, h->delete_list); */
993
update_clipboard(primary, "", H_MODE_INIT);
994
update_clipboard(clipboard, "", H_MODE_INIT);
999
/* Called when About is selected from right-click menu */
1000
static void show_about_dialog(GtkMenuItem *menu_item, gpointer user_data)
1002
/* This helps prevent multiple instances */
1003
if (!gtk_grab_get_current())
1005
const gchar* authors[] = {_("Gilberto \"Xyhthyx\" Miralla <xyhthyx@gmail.com>\nDoug Springer <gpib@rickyrockrat.net>"), NULL};
1006
const gchar* license =
1007
"This program is free software; you can redistribute it and/or modify\n"
1008
"it under the terms of the GNU General Public License as published by\n"
1009
"the Free Software Foundation; either version 3 of the License, or\n"
1010
"(at your option) any later version.\n\n"
1011
"This program is distributed in the hope that it will be useful,\n"
1012
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1013
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
1014
"GNU General Public License for more details.\n\n"
1015
"You should have received a copy of the GNU General Public License\n"
1016
"along with this program. If not, see <http://www.gnu.org/licenses/>.";
1018
/* Create the about dialog */
1019
GtkWidget* about_dialog = gtk_about_dialog_new();
1020
gtk_window_set_icon((GtkWindow*)about_dialog,
1021
gtk_widget_render_icon(about_dialog, GTK_STOCK_ABOUT, -1, NULL));
1023
gtk_about_dialog_set_name((GtkAboutDialog*)about_dialog, "Parcellite");
1024
#ifdef HAVE_CONFIG_H /**VER=555; sed "s#\(.*\)svn.*\".*#\1svn$VER\"#" config.h */
1025
gtk_about_dialog_set_version((GtkAboutDialog*)about_dialog, VERSION);
1027
gtk_about_dialog_set_comments((GtkAboutDialog*)about_dialog,
1028
_("Lightweight GTK+ clipboard manager."));
1030
gtk_about_dialog_set_website((GtkAboutDialog*)about_dialog,
1031
"http://parcellite.sourceforge.net");
1033
gtk_about_dialog_set_copyright((GtkAboutDialog*)about_dialog, _("Copyright (C) 2007, 2008 Gilberto \"Xyhthyx\" Miralla\nCopyright (C) 2010-2013 Doug Springer"));
1034
gtk_about_dialog_set_authors((GtkAboutDialog*)about_dialog, authors);
1035
gtk_about_dialog_set_translator_credits ((GtkAboutDialog*)about_dialog,
1036
"Miloš Koutný <milos.koutny@gmail.com>\n"
1037
"Kim Jensen <reklamepost@energimail.dk>\n"
1038
"Eckhard M. Jäger <bart@neeneenee.de>\n"
1039
"Michael Stempin <mstempin@web.de>\n"
1040
"Benjamin Danon <benjamin@sphax3d.org>\n"
1041
"Németh Tamás <ntomasz@vipmail.hu>\n"
1042
"Davide Truffa <davide@catoblepa.org>\n"
1043
"Jiro Kawada <jiro.kawada@gmail.com>\n"
1044
"Øyvind Sæther <oyvinds@everdot.org>\n"
1045
"pankamyk <pankamyk@o2.pl>\n"
1046
"Tomasz Rusek <tomek.rusek@gmail.com>\n"
1047
"Phantom X <megaphantomx@bol.com.br>\n"
1048
"Ovidiu D. Niţan <ov1d1u@sblug.ro>\n"
1049
"Alexander Kazancev <kazancas@mandriva.ru>\n"
1050
"Daniel Nylander <po@danielnylander.se>\n"
1051
"Hedef Türkçe <iletisim@hedefturkce.com>\n"
1052
"Lyman Li <lymanrb@gmail.com>\n"
1053
"Gilberto \"Xyhthyx\" Miralla <xyhthyx@gmail.com>");
1055
gtk_about_dialog_set_license((GtkAboutDialog*)about_dialog, license);
1056
gtk_about_dialog_set_logo_icon_name((GtkAboutDialog*)about_dialog, PARCELLITE_ICON);
1057
/* Run the about dialog */
1058
gtk_dialog_run((GtkDialog*)about_dialog);
1059
gtk_widget_destroy(about_dialog);
1063
/* Called when Preferences is selected from right-click menu */
1064
static void preferences_selected(GtkMenuItem *menu_item, gpointer user_data)
1066
/* This helps prevent multiple instances */
1067
if (!gtk_grab_get_current()){
1068
/* Show the preferences dialog */
1069
show_preferences(0);
1074
/* Called when Quit is selected from right-click menu */
1075
static void quit_selected(GtkMenuItem *menu_item, gpointer user_data)
1077
/* Prevent quit with dialogs open */
1078
if (!gtk_grab_get_current())
1079
/* Quit the program */
1083
/* Called when status icon is control-clicked */
1084
static gboolean show_actions_menu(gpointer data)
1086
/* Declare some variables */
1087
GtkWidget *menu, *menu_item,
1088
*menu_image, *item_label;
1091
menu = gtk_menu_new();
1092
g_signal_connect((GObject*)menu,"selection-done", (GCallback)gtk_widget_destroy, NULL);
1093
/* Actions using: */
1094
menu_item = gtk_image_menu_item_new_with_label("Actions using:");
1095
menu_image = gtk_image_new_from_stock(GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU);
1096
gtk_image_menu_item_set_image((GtkImageMenuItem*)menu_item, menu_image);
1097
g_signal_connect((GObject*)menu_item, "select", (GCallback)gtk_menu_item_deselect, NULL);
1098
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1099
/* Clipboard contents */
1100
gchar* text = gtk_clipboard_wait_for_text(clipboard);
1103
menu_item = gtk_menu_item_new_with_label("None");
1104
/* Modify menu item label properties */
1105
item_label = gtk_bin_get_child((GtkBin*)menu_item);
1106
gtk_label_set_single_line_mode((GtkLabel*)item_label, TRUE);
1107
gtk_label_set_ellipsize((GtkLabel*)item_label, get_pref_int32("ellipsize"));
1108
gtk_label_set_width_chars((GtkLabel*)item_label, 30);
1109
/* Making bold... */
1110
gchar* bold_text = g_markup_printf_escaped("<b>%s</b>", text);
1111
gtk_label_set_markup((GtkLabel*)item_label, bold_text);
1113
/* Append menu item */
1114
g_signal_connect((GObject*)menu_item, "select", (GCallback)gtk_menu_item_deselect, NULL);
1115
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1120
/* Create menu item for empty clipboard contents */
1121
menu_item = gtk_menu_item_new_with_label("None");
1122
/* Modify menu item label properties */
1123
item_label = gtk_bin_get_child((GtkBin*)menu_item);
1124
gtk_label_set_markup((GtkLabel*)item_label, _("<b>None</b>"));
1125
/* Append menu item */
1126
g_signal_connect((GObject*)menu_item, "select", (GCallback)gtk_menu_item_deselect, NULL);
1128
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1130
/* -------------------- */
1131
gtk_menu_shell_append((GtkMenuShell*)menu, gtk_separator_menu_item_new());
1133
gchar* path = g_build_filename(g_get_user_data_dir(), ACTIONS_FILE, NULL);
1134
printf("got path '%s'\n",path); fflush(NULL);
1135
FILE* actions_file = fopen(path, "rb");
1137
/* Check that it opened and begin read */
1141
if(0==fread(&size, 4, 1, actions_file))
1142
g_print("1:got 0 items from fread\n");
1143
/* Check if actions file is empty */
1146
/* File contained no actions so adding empty */
1147
menu_item = gtk_menu_item_new_with_label(_("Empty"));
1148
gtk_widget_set_sensitive(menu_item, FALSE);
1149
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1151
/* Continue reading items until size is 0 */
1155
gchar* name = (gchar*)g_malloc(size + 1);
1156
if( 0 ==fread(name, size, 1, actions_file))
1157
g_print("2:got 0 items from fread\n");
1159
menu_item = gtk_menu_item_new_with_label(name);
1161
if(0 ==fread(&size, 4, 1, actions_file))
1162
g_print("3:got 0 items from fread\n");
1164
gchar* command = (gchar*)g_malloc(size + 1);
1165
if(0 ==fread(command, size, 1, actions_file))
1166
g_print("4:got 0 items from fread\n");
1167
command[size] = '\0';
1168
g_print("name='%s' cmd='%s'\n",name,command);
1169
if(0 ==fread(&size, 4, 1, actions_file))
1170
g_print("5:got 0 items from fread\n");
1171
/* Append the action */
1172
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1173
g_signal_connect((GObject*)menu_item, "activate",
1174
(GCallback)action_selected, (gpointer)command);
1177
fclose(actions_file);
1181
/* File did not open so adding empty */
1182
menu_item = gtk_menu_item_new_with_label(_("Empty"));
1183
gtk_widget_set_sensitive(menu_item, FALSE);
1184
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1186
/* -------------------- */
1187
gtk_menu_shell_append((GtkMenuShell*)menu, gtk_separator_menu_item_new());
1189
menu_item = gtk_image_menu_item_new_with_mnemonic(_("_Edit actions"));
1190
menu_image = gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
1191
gtk_image_menu_item_set_image((GtkImageMenuItem*)menu_item, menu_image);
1192
g_signal_connect((GObject*)menu_item, "activate", (GCallback)edit_actions_selected, NULL);
1193
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1194
/* Popup the menu... */
1195
gtk_widget_show_all(menu);
1196
gtk_menu_popup((GtkMenu*)menu, NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time());
1197
/* Return false so the g_timeout_add() function is called only once */
1201
/***************************************************************************/
1202
/** Called just before destroying history menu.
1205
****************************************************************************/
1206
static gboolean selection_done(GtkMenuShell *menushell, gpointer user_data)
1208
struct history_info *h=(struct history_info *)user_data;
1209
if(NULL != h && NULL != h->delete_list){/**have a list of items to delete. */
1210
remove_deleted_items(h);
1213
/*g_print("selection_active=%d\n",selection_active); */
1214
/*g_print("Got selection_done\n"); */
1215
if(h->change_flag && get_pref_int32("save_history")){
1221
/*gtk_widget_destroy((GtkWidget *)menushell); - fixes annoying GTK_IS_WIDGET/GTK_IS_WINDOW
1222
warnings from GTK when history dialog is destroyed. */
1226
/***************************************************************************/
1227
/** Set the background color of the widget.
1230
****************************************************************************/
1231
void set_widget_bg(gchar *color, GtkWidget *w)
1238
/*g_print("set_widget_bg\n"); */
1240
gdk_color_parse (color, &c);
1245
gtk_widget_modify_bg(w, GTK_STATE_NORMAL, cp);
1247
#if 0 /**none of this works */
1248
gtk_widget_modify_bg(w, GTK_STATE_ACTIVE, cp);
1250
/*gdk_color_parse (color, &c); */
1251
st=gtk_widget_get_modifier_style(w);
1252
/*st=gtk_rc_style_new (); */
1253
st->bg[GTK_STATE_NORMAL] = st->bg[GTK_STATE_ACTIVE] = c;
1254
gtk_widget_modify_style (w, st);
1256
/*gtk_widget_modify_bg (w, GTK_STATE_NORMAL, &c); */
1260
/**postition the history dialog - should only be called if get_pref_int32("history_pos") is set */
1261
void postition_history(GtkMenu *menu,gint *x,gint *y,gboolean *push_in, gpointer user_data)
1265
s=gdk_screen_get_default();
1266
sx= gdk_screen_get_width(s);
1267
sy= gdk_screen_get_height(s);
1270
if(1 == GPOINTER_TO_INT(user_data)){
1274
if(get_pref_int32("history_pos")){
1276
if(get_pref_int32("history_x") > get_pref_int32("item_length") )
1277
xx=get_pref_int32("history_x")-get_pref_int32("item_length");
1280
if(get_pref_int32("history_y") > get_pref_int32("history_limit") )
1281
yy=get_pref_int32("history_y")-get_pref_int32("history_limit");
1286
TRACE(g_fprintf(stderr,"x=%d, y=%d\n",xx,yy));
1293
/***************************************************************************/
1294
/** This handles events for the history menu, which is the parent of each
1296
\n\b Arguments: user is the struct history_info created in history menu.
1297
\n\b Returns: FALSE if key was not handled, TRUE if it was.
1298
You get two key presses if you return FALSE.
1299
****************************************************************************/
1300
static gboolean key_release_cb (GtkWidget *w,GdkEventKey *e, gpointer user)
1302
static gchar *kstr=NULL;
1304
/*static GdkEvent *last_event=NULL; */
1305
gint first, current,off;
1306
static GtkWidget *item=NULL;
1308
struct history_info *h;
1310
h=(struct history_info *)user;
1313
if(GDK_MOTION_NOTIFY==e->type)
1315
printf("krc (%x) S%x T%x C%x,SE%x, G%x, W%p, wdg%p",
1316
e->keyval,e->state,e->type,
1317
e->hardware_keycode,e->send_event,e->group,e->window,w);
1318
if(GDK_DRAG_ENTER == e->type || GDK_DRAG_LEAVE==e->type){
1319
printf(" Drag %s\n",GDK_DRAG_ENTER == e->type?"ENTER":"LEAVE");
1321
if(GDK_BUTTON_RELEASE==e->type || GDK_BUTTON_PRESS==e->type ){
1322
GdkEventButton *b=(GdkEventButton *)e;
1323
printf(" button %d State 0x%x\n",b->button, b->state);
1324
if(GDK_BUTTON_RELEASE==e->type && 3 == b->button){
1325
/*toggle-size-request", */
1326
/*allow item_selected to get called */
1335
/**serves as init for keysearch */
1336
if(NULL ==w && NULL==e && NULL == user){
1339
kstr=g_strnfill(KBUF_SIZE+8,0);
1342
}else if(NULL == kstr){
1343
g_print("kstr null. Not init\n");
1347
g_print("No Event!\n");
1351
if(0 == get_pref_int32("type_search"))/**searching is turned off */
1353
/**ignore left-clicks */
1354
if(GDK_BUTTON_RELEASE==e->type && 3 == ((GdkEventButton *)e)->button)
1357
if(GDK_KEY_PRESS == e->type && ' ' == e->keyval) /**ignore space presses */
1359
if(GDK_KEY_PRESS == e->type)
1361
/**pass all other non-release events on */
1362
if(GDK_KEY_RELEASE != e->type && GDK_BUTTON_RELEASE != e->type)
1364
/** if(GDK_SELECTION_NOTIFY == e->type){
1365
g_print("last %x\n",last_event->type);
1366
last_event=(GdkEvent *)e;
1369
last_event=(GdkEvent *)e;*/
1371
if(e->state & (GDK_SHIFT_MASK|GDK_LOCK_MASK) != e->state){
1372
g_print("rfs to use mods\n");
1373
TRACE(g_fprintf(stderr,"state is %X. Refusing to use mods\n",e->state));
1375
} have to use for _ and others*/
1376
/**ignore Ctrl-Alt */
1377
if((GDK_CONTROL_MASK|GDK_MOD1_MASK)==(e->state & (GDK_CONTROL_MASK|GDK_MOD1_MASK)))
1379
if( e->state & GDK_MOD1_MASK){/**alt key pressed */
1380
if(e->keyval == 'e'){
1381
TRACE(g_fprintf(stderr,"Alt-E\n"));
1383
edit_selected((GtkMenuItem *)h, (gpointer)h);
1387
else if(e->keyval == 'c'){
1388
TRACE(g_fprintf(stderr,"Alt-C\n"));
1389
clear_selected(NULL, (gpointer)h);
1392
TRACE(g_fprintf(stderr,"Ignoring Alt-%c (0x%02x) state 0x%x",e->keyval,e->keyval,e->state));
1395
} /**end alt key pressed */
1396
if(e->state & GDK_CONTROL_MASK) /**ignore control keys */
1398
if(e->state &GDK_SHIFT_MASK && get_pref_int32("case_search")) /**ignore shift */
1400
if( GDK_EXPOSE== e->type || GDK_BUTTON_RELEASE==e->type) /**fix bug 3560995, item 1/2, red clipboard. */
1402
if(e->keyval == 0xff08){/**backspace */
1403
// g_printf("0x%x bs %d ",e->type,idx);
1406
else if( NULL != h->clip_item){
1407
gtk_menu_shell_select_item((GtkMenuShell *)h->menu,(GtkWidget *)h->clip_item);
1409
set_widget_bg(NULL,h->menu);
1411
// g_printf(" %d\n",idx);
1413
} /**end backspace */
1414
if( e->keyval == 0xffe1 || e->keyval == 0xffe2){
1415
/*fprintf(stderr,"Ignoring key '%c' 0x%02x\n",e->keyval,e->keyval); */
1416
TRACE(g_fprintf(stderr,"Ignoring key '%c' 0x%02x\n",e->keyval,e->keyval));
1419
if(e->keyval >= 0xff50 && e->keyval <= 0xff57) /**arrow keys, home,end,pgup,pgdwn */
1423
TRACE(g_fprintf(stderr,"keys full\n"));
1426
kstr[idx++]=e->keyval;
1428
for ( off=0; off<50;++off){ /** this loop does a char search based on offset */
1429
children=gtk_container_get_children((GtkContainer *)h->menu);
1431
current=first=0; /**first is edit, */
1432
while(NULL != children->next){
1435
GtkWidget *child=gtk_bin_get_child((GtkBin*)children->data);
1436
if(GTK_IS_LABEL(child)){
1437
l=(gchar *)gtk_label_get_text((GtkLabel *)child);
1441
if(get_pref_int32("case_search"))
1442
c=strncmp(kstr,&l[off],idx);
1444
c=g_ascii_strncasecmp(kstr,&l[off],idx);
1453
TRACE(g_fprintf(stderr,"Got cmp'%s'='%s'\n",kstr,l));
1454
item=(GtkWidget *)children->data;
1461
children=children->next;
1465
/**didn't find it. Set our title and return */
1466
set_widget_bg("red",h->menu);
1469
set_widget_bg(NULL,h->menu);
1470
/**user->children...
1472
gpointer data,next,prev
1473
data should be a GtkMenuItem list, whose children are labels...
1475
/*str=(gchar *)gtk_label_get_text((GtkLabel *)gtk_bin_get_child((GtkBin*)children->data)); */
1476
TRACE(g_fprintf(stderr,"Got '%c' 0x%02x, state 0x%02X",e->keyval,e->keyval,e->state));
1478
if(first){TRACE(g_fprintf(stderr,"First:"));}
1479
/*if(last)TRACE(g_fprintf(stderr,"Last:")); */
1480
TRACE(g_fprintf(stderr,"At Item '%s'",gtk_label_get_text((GtkLabel *)gtk_bin_get_child((GtkBin*)item))));
1481
gtk_menu_shell_select_item((GtkMenuShell *)h->menu,(GtkWidget *)item);
1484
TRACE(g_fprintf(stderr,"\n"));
1488
/***************************************************************************/
1489
/** Set clipboard from history list.
1492
****************************************************************************/
1493
void set_clipboard_text(struct history_info *h, GList *element)
1495
int auto_whatever=0;
1499
/*g_mutex_lock(clip_lock); */
1500
if(NULL == find_h_item(h->delete_list,NULL,element)){ /**not in our delete list */
1501
/**make a copy of txt, because it gets freed and re-allocated. */
1502
txt=p_strdup(((struct history_item *)(element->data))->text);
1503
DTRACE(g_fprintf(stderr,"set_clip_text %s\n",txt));
1504
if(get_pref_int32("use_copy") )
1505
update_clipboard(clipboard, txt, H_MODE_LIST);
1506
if(get_pref_int32("use_primary"))
1507
update_clipboard(primary, txt, H_MODE_LIST);
1511
g_signal_emit_by_name ((gpointer)h->menu,"selection-done");
1512
if(0 == auto_whatever)
1514
/*g_printf("set_clip_text done\n"); */
1515
/*g_mutex_unlock(clip_lock); */
1517
if (get_pref_int32("automatic_paste")) { /** mousedown 2 */
1518
if(get_pref_int32("auto_mouse"))
1519
action=g_strdup("mousedown 2 && xdotool mouseup 2'");
1520
else if(get_pref_int32("auto_key"))
1521
action=g_strdup("key ctrl+v'");
1524
if( get_pref_int32("key_input"))
1525
action=g_strconcat("type \"",txt,"\"'",NULL);
1529
/**from clipit 1.4.1 */
1530
cmd = g_strconcat("/bin/sh -c 'xdotool ", action, NULL);
1531
g_fprintf(stderr,"xdotool:'%s'\ntext:'%s'\n",cmd,txt);
1534
g_shell_parse_argv(cmd, NULL, &argv, NULL);
1536
g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL);
1537
g_child_watch_add(pid, (GChildWatchFunc)action_exit, NULL);
1539
/**end from clipit 1.4.1 */
1548
/***************************************************************************/
1549
/** This handles the events for each item in the history menu.
1550
\n\b Arguments: user is element number
1552
****************************************************************************/
1553
static gboolean my_item_event (GtkWidget *w,GdkEventKey *e, gpointer user)
1555
static struct history_info *h=NULL;
1556
GtkWidget *menu=NULL;
1558
if(NULL==w && NULL==e){
1559
h=(struct history_info *)user;
1560
/*g_print("my_item_event: Set menu to %p\n",h); */
1566
/**check for enter */
1567
if(GDK_MOTION_NOTIFY == e->type)
1569
/*printf("my_item_event: T 0x%x S 0x%x ",e->type,e->state); */
1570
if(NULL !=h && GDK_ENTER_NOTIFY ==e->type ){/**add to delete */
1571
GdkEventCrossing *enter=(GdkEventCrossing *)e;
1572
/*printf("state 0x%x\n",enter->state); */
1573
/**use shift and right-click */
1574
if(GDK_SHIFT_MASK&enter->state && GDK_BUTTON3_MASK&enter->state)
1575
handle_marking(h,w,GPOINTER_TO_INT(user),OPERATE_DELETE);
1577
if(GDK_KEY_PRESS == e->type){
1578
GdkEventKey *k= (GdkEventKey *)e;
1579
printf("key press %d (0x%x)\n",e->keyval,e->keyval);
1582
if(GDK_BUTTON_RELEASE==e->type){
1583
GdkEventButton *b=(GdkEventButton *)e;
1584
GList* element = g_list_nth(history_list, GPOINTER_TO_INT(user));
1585
/*printf("type %x State 0x%x val %x %p '%s'\n",e->type, b->state,b->button,w,(gchar *)((struct history_item *(element->data))->text)); */
1586
if(3 == b->button){ /**right-click */
1588
if(GDK_CONTROL_MASK&b->state){
1589
handle_marking(h,w,GPOINTER_TO_INT(user),OPERATE_DELETE);
1591
if((GDK_CONTROL_MASK|GDK_SHIFT_MASK)&b->state)
1593
/*g_print("Calling popup\n"); */
1596
h->wi.index=GPOINTER_TO_INT(user);
1597
/*g_fprintf(stderr,"Calling hist_itemRclk\n"); */
1598
history_item_right_click(h,e,GPOINTER_TO_INT(user));
1602
}else if( 1 == b->button){
1603
/* Get the text from the right element and set as clipboard */
1604
set_clipboard_text(h,element);
1612
/***************************************************************************/
1613
/** Attempt to handle enter key behaviour.
1616
****************************************************************************/
1617
static void item_selected(GtkMenuItem *menu_item, gpointer user_data)
1619
static struct history_info *h=NULL;
1620
if(NULL ==menu_item){
1621
h=(struct history_info *)user_data;
1626
GdkEventKey *k=(GdkEventKey *)gtk_get_current_event();
1627
GList* element = g_list_nth(history_list, GPOINTER_TO_INT(user_data));
1628
/*g_print ("item_selected '%s' type %x val %x\n",(gchar *)((struct history_item *(element->data))->text),k->type, k->keyval); */
1629
if(0xFF0d == k->keyval && GDK_KEY_PRESS == k->type){
1630
set_clipboard_text(h,element);
1634
/***************************************************************************/
1635
/** Write the elements to the menu.
1638
****************************************************************************/
1639
void write_history_menu_items(GList *list, GtkWidget *menu)
1644
for (element = list; element != NULL; element = element->next)
1645
gtk_menu_shell_append((GtkMenuShell*)menu,element->data);
1648
/***************************************************************************/
1649
/** Replace non-printing characters with ??.
1650
0x09->E28692 - \2192
1651
0x0a->E2818B - \204b
1652
space-E290A3 - \u2423
1655
****************************************************************************/
1656
GString* convert_string(GString* s)
1658
gchar arrow[4]={0xe2,0x86,0x92,0x00};/**0xe28692 (UTF-8 right-arrow) \\ */
1659
gchar pharagraph[4]={0xe2,0x81,0x8b,0x00}; /**utf-8 pharagraph symbol \2192 */
1660
gchar square_u[4]={0xe2,0x90,0xA3,0}; /**square-u \\2423 */
1664
for (p=s->str; p!= NULL; p=g_utf8_find_next_char(p,s->str+s->len)){
1666
case 0x09:r=arrow; break;
1667
case 0x0a:r=pharagraph; break;
1668
case 0x20:r=square_u; break;
1669
default:r=NULL; break;
1671
if(NULL !=r ) {/**replace. */
1672
gint32 pos=(p-s->str)+1;
1674
s=g_string_insert(s,pos,&r[1]);
1680
/***************************************************************************/
1681
/** Called when status icon is left-clicked or action key hit.
1684
****************************************************************************/
1686
static gboolean show_history_menu(gpointer data)
1688
/* Declare some variables */
1689
GtkWidget *menu, *menu_item,
1690
*menu_image, *item_label;
1691
static struct history_info h;
1693
h.histno=GPOINTER_TO_INT(data);/**persistent or normal history */
1695
h.element_text=NULL;
1697
/**init our keystroke function */
1698
key_release_cb(NULL,NULL,NULL);
1699
GList *element, *persistent=NULL;
1701
int single_line=get_pref_int32("single_line");
1703
/* Create the menu */
1704
menu = gtk_menu_new();
1709
h.persist_list=NULL;
1710
h.wi.tmp1=0; /** used to tell edit what we are to edit */
1711
/*g_print("histmen %p\n",menu); */
1712
my_item_event(NULL,NULL,(gpointer)&h); /**init our function */
1713
item_selected(NULL,(gpointer)&h); /**ditto */
1714
gtk_menu_shell_set_take_focus((GtkMenuShell *)menu,TRUE); /**grab keyboard focus */
1715
/*g_signal_connect((GObject*)menu, "selection-done", (GCallback)selection_done, gtk_menu_get_attach_widget (menu)); */
1716
g_signal_connect((GObject*)menu, "cancel", (GCallback)selection_done, &h);
1717
g_signal_connect((GObject*)menu, "selection-done", (GCallback)selection_done, &h);
1718
/*g_signal_connect((GObject*)menu, "selection-done", (GCallback)gtk_widget_destroy, NULL); */
1719
/**Trap key events */
1720
/* g_signal_connect((GObject*)menu, "key-release-event", (GCallback)key_release_cb, (gpointer)&h); */
1721
g_signal_connect((GObject*)menu, "event", (GCallback)key_release_cb, (gpointer)&h);
1722
/**trap mnemonic events */
1723
/*g_signal_connect((GObject*)menu, "mnemonic-activate", (GCallback)key_release_cb, (gpointer)menu); */
1725
/* -------------------- */
1726
/*gtk_menu_shell_append((GtkMenuShell*)menu, gtk_separator_menu_item_new()); */
1728
if ((history_list != NULL) && (history_list->data != NULL)) {
1729
/* Declare some variables */
1730
gint32 item_length= get_pref_int32("item_length");
1731
gint32 ellipsize = get_pref_int32("ellipsize");
1732
gint32 persistent_history=get_pref_int32("persistent_history");
1733
gint32 nonprint_disp=get_pref_int32("nonprint_disp");
1734
gint element_number = 0;
1735
gchar* primary_temp = gtk_clipboard_wait_for_text(primary);
1736
gchar* clipboard_temp = gtk_clipboard_wait_for_text(clipboard);
1737
/* Reverse history if enabled */
1738
if (0 && get_pref_int32("reverse_history")) {
1739
/*history_list = g_list_reverse(history_list); */
1740
element_number = g_list_length(history_list) - 1;
1742
/* Go through each element and adding each */
1743
for (element = history_list; element != NULL; element = element->next) {
1744
struct history_item *c=(struct history_item *)(element->data);
1745
gchar* hist_text=c->text;
1746
if(!(HIST_DISPLAY_PERSISTENT&h.histno) && (c->flags & CLIP_TYPE_PERSISTENT))
1748
else if( !(HIST_DISPLAY_NORMAL&h.histno) && !(c->flags & CLIP_TYPE_PERSISTENT))
1750
GString* string = g_string_new(hist_text);
1752
string=convert_string(string);
1753
glong len=g_utf8_strlen(string->str, string->len);
1754
/* Ellipsize text */
1755
if (len > item_length) {
1756
switch (ellipsize) {
1757
case PANGO_ELLIPSIZE_START:
1758
string = g_string_erase(string, 0, g_utf8_offset_to_pointer(string->str, len - item_length) - string->str);
1759
/*string = g_string_erase(string, 0, string->len-(get_pref_int32("item_length"))); */
1760
string = g_string_prepend(string, "...");
1762
case PANGO_ELLIPSIZE_MIDDLE:
1764
gchar* p1 = g_utf8_offset_to_pointer(string->str, item_length / 2);
1765
gchar* p2 = g_utf8_offset_to_pointer(string->str, len - item_length / 2);
1766
string = g_string_erase(string, p1 - string->str, p2 - p1);
1767
string = g_string_insert(string, p1 - string->str, "...");
1768
/** string = g_string_erase(string, (get_pref_int32("item_length")/2), string->len-(get_pref_int32("item_length")));
1769
string = g_string_insert(string, (string->len/2), "...");*/
1772
case PANGO_ELLIPSIZE_END:
1773
string = g_string_truncate(string, g_utf8_offset_to_pointer(string->str, item_length) - string->str);
1774
/*string = g_string_truncate(string, get_pref_int32("item_length")); */
1775
string = g_string_append(string, "...");
1779
/* Remove control characters */
1781
while (i < string->len)
1782
{ /**fix 100% CPU utilization for odd data. - bug 2976890 */
1784
while(string->str[i+nline] == '\n' && nline+i<string->len)
1787
g_string_erase(string, i, nline);
1788
/* RMME printf("e %ld",nline);fflush(NULL); */
1795
/* Make new item with ellipsized text */
1796
menu_item = gtk_menu_item_new_with_label(string->str);
1798
g_signal_connect((GObject*)menu_item, "event",
1799
(GCallback)my_item_event, GINT_TO_POINTER(element_number));
1800
g_signal_connect((GObject*)menu_item, "activate",
1801
(GCallback)item_selected, GINT_TO_POINTER(element_number));
1804
/* Modify menu item label properties */
1805
item_label = gtk_bin_get_child((GtkBin*)menu_item);
1807
/*gtk_label_set_line_wrap */
1808
gtk_label_set_single_line_mode((GtkLabel*)item_label, TRUE);
1810
gtk_label_set_single_line_mode((GtkLabel*)item_label, FALSE);
1813
/* Check if item is also clipboard text and make bold */
1814
if ((clipboard_temp) && (p_strcmp(hist_text, clipboard_temp) == 0))
1816
gchar* bold_text = g_markup_printf_escaped("<b>%s</b>", string->str);
1817
if( NULL == bold_text) g_fprintf(stderr,"NulBMKUp:'%s'\n",string->str);
1818
gtk_label_set_markup((GtkLabel*)item_label, bold_text);
1820
h.clip_item=menu_item;
1821
h.element_text=hist_text;
1822
h.wi.index=element_number;
1824
else if ((primary_temp) && (p_strcmp(hist_text, primary_temp) == 0))
1826
gchar* italic_text = g_markup_printf_escaped("<i>%s</i>", string->str);
1827
if( NULL == italic_text) g_fprintf(stderr,"NulIMKUp:'%s'\n",string->str);
1828
gtk_label_set_markup((GtkLabel*)item_label, italic_text);
1829
g_free(italic_text);
1830
h.clip_item=menu_item;
1831
h.element_text=hist_text;
1832
h.wi.index=element_number;
1834
if(persistent_history && c->flags &CLIP_TYPE_PERSISTENT){
1835
persistent = g_list_prepend(persistent, menu_item);
1836
/*g_printf("persistent %s\n",c->text); */
1839
lhist = g_list_prepend(lhist, menu_item);
1844
/* Prepare for next item */
1845
g_string_free(string, TRUE);
1847
/** if (get_pref_int32("reverse_history"))
1851
} /**end of for loop for each history item */
1853
g_free(primary_temp);
1854
g_free(clipboard_temp);
1855
/* Return history to normal if reversed */
1856
/** if (get_pref_int32("reverse_history"))
1857
history_list = g_list_reverse(history_list);*/
1861
/* Nothing in history so adding empty */
1862
menu_item = gtk_menu_item_new_with_label(_("Empty"));
1863
gtk_widget_set_sensitive(menu_item, FALSE);
1864
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1866
if (!get_pref_int32("reverse_history")) {
1867
lhist = g_list_reverse(lhist);
1868
persistent = g_list_reverse(persistent);
1870
/**now actually add them from the list */
1871
if(get_pref_int32("persistent_history")){
1872
if(get_pref_int32("persistent_on_top")){
1873
write_history_menu_items(persistent,menu);
1874
gtk_menu_shell_append((GtkMenuShell*)menu, gtk_separator_menu_item_new());
1875
write_history_menu_items(lhist,menu);
1877
write_history_menu_items(lhist,menu);
1878
gtk_menu_shell_append((GtkMenuShell*)menu, gtk_separator_menu_item_new());
1879
write_history_menu_items(persistent,menu);
1881
}else { /**normal old operation, forget about persistence. */
1882
write_history_menu_items(lhist,menu);
1885
/* -------------------- */
1886
gtk_menu_shell_append((GtkMenuShell*)menu, gtk_separator_menu_item_new());
1888
if(get_pref_int32("type_search")){
1889
/* Edit clipboard */
1890
h.title_item = gtk_image_menu_item_new_with_label( _("Use Alt-E to edit, Alt-C to clear") );
1891
menu_image = gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
1892
gtk_image_menu_item_set_image((GtkImageMenuItem*)h.title_item, menu_image);
1893
gtk_menu_shell_append((GtkMenuShell*)menu, h.title_item);
1895
menu_item = gtk_image_menu_item_new_with_mnemonic(_("_Edit Clipboard"));
1896
menu_image = gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU);
1897
gtk_image_menu_item_set_image((GtkImageMenuItem*)menu_item, menu_image);
1898
g_signal_connect((GObject*)menu_item, "activate", (GCallback)edit_selected, (gpointer)&h);
1899
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1900
menu_item = gtk_image_menu_item_new_with_mnemonic(_("_Clear"));
1902
menu_image = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
1903
gtk_image_menu_item_set_image((GtkImageMenuItem*)menu_item, menu_image);
1904
g_signal_connect((GObject*)menu_item, "activate", (GCallback)clear_selected, (gpointer)&h);
1905
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1908
g_list_free(persistent);
1909
/* Popup the menu... */
1910
gtk_widget_show_all(menu);
1911
gtk_menu_popup((GtkMenu*)menu, NULL, NULL, get_pref_int32("history_pos")?postition_history:NULL, NULL, 1, gtk_get_current_event_time());
1912
/**set last entry at first -fixes bug 2974614 */
1913
if(get_pref_int32("reverse_history") && NULL != h.clip_item)
1914
gtk_menu_shell_select_item((GtkMenuShell*)menu,h.clip_item);
1916
gtk_menu_shell_select_first((GtkMenuShell*)menu, TRUE);
1917
/* Return FALSE so the g_timeout_add() function is called only once */
1922
/***************************************************************************/
1926
****************************************************************************/
1927
void _show_history_menu (GtkMenuItem *m, gpointer data)
1929
g_timeout_add(POPUP_DELAY, show_history_menu, GINT_TO_POINTER(figure_histories()));
1931
/***************************************************************************/
1935
****************************************************************************/
1936
GtkWidget *create_parcellite_menu(guint button, guint activate_time)
1938
/* Declare some variables */
1939
GtkWidget *menu, *menu_item;
1941
/* Create the menu */
1942
menu = gtk_menu_new();
1943
/*g_signal_connect((GObject*)menu, "selection-done", (GCallback)gtk_widget_destroy, NULL); */
1945
menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
1946
g_signal_connect((GObject*)menu_item, "activate", (GCallback)show_about_dialog, NULL);
1947
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1950
menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE_AS, NULL);
1951
g_signal_connect((GObject*)menu_item, "activate", (GCallback)history_save_as, NULL);
1952
gtk_widget_set_tooltip_text(menu_item, _("Save History as a text file. Prepends xHIST_0000 to each entry. x is either P(persistent) or N (normal)"));
1953
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1955
menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
1956
g_signal_connect((GObject*)menu_item, "activate", (GCallback)preferences_selected, NULL);
1957
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1958
if(have_appindicator){
1960
GtkWidget *img=gtk_image_new_from_icon_name(PARCELLITE_ICON,GTK_ICON_SIZE_MENU);
1961
menu_item = gtk_image_menu_item_new_with_mnemonic(_("_History"));
1962
gtk_image_menu_item_set_image((GtkImageMenuItem *)menu_item,img);
1963
g_signal_connect((GObject*)menu_item, "activate", (GCallback)_show_history_menu, NULL);
1964
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1967
/* -------------------- */
1968
gtk_menu_shell_append((GtkMenuShell*)menu, gtk_separator_menu_item_new());
1970
menu_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
1971
g_signal_connect((GObject*)menu_item, "activate", (GCallback)quit_selected, NULL);
1972
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
1973
/* Popup the menu... */
1974
gtk_widget_show_all(menu);
1975
gtk_menu_popup((GtkMenu*)menu, NULL, NULL, NULL, NULL, button, activate_time);
1979
/* Called when status icon is right-clicked */
1980
static void show_parcellite_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer data)
1982
create_parcellite_menu(button, activate_time);
1985
/***************************************************************************/
1989
****************************************************************************/
1990
gboolean show_parcellite_menu_wrapper(gpointer data)
1992
create_parcellite_menu(0, gtk_get_current_event_time());
1995
/***************************************************************************/
1999
****************************************************************************/
2000
gint figure_histories(void)
2003
if(get_pref_int32("persistent_history")){
2004
if(get_pref_int32("persistent_separate"))
2005
i=HIST_DISPLAY_NORMAL;
2007
i=HIST_DISPLAY_PERSISTENT|HIST_DISPLAY_NORMAL;
2009
i=HIST_DISPLAY_NORMAL;
2010
/*g_printf("Using history 0x%X\n",i); */
2013
#ifdef HAVE_APPINDICATOR
2014
/***************************************************************************/
2018
****************************************************************************/
2019
void create_app_indicator(void)
2021
/* Create the menu */
2022
indicator_menu = create_parcellite_menu(0,gtk_get_current_event_time());
2023
/* check if we need to create the indicator or just refresh the menu */
2024
if(NULL == indicator) {
2025
indicator = app_indicator_new("parcellite", "parcellite", APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
2026
app_indicator_set_status (indicator, APP_INDICATOR_STATUS_ACTIVE);
2028
app_indicator_set_attention_icon (indicator,"parcellite");
2030
app_indicator_set_menu (indicator, GTK_MENU (indicator_menu));
2034
/* Called when status icon is left-clicked */
2035
static void status_icon_clicked(GtkStatusIcon *status_icon, gpointer user_data)
2037
/* Check what type of click was recieved */
2038
GdkModifierType state;
2039
gtk_get_current_event_state(&state);
2041
if (state == GDK_MOD2_MASK+GDK_CONTROL_MASK || state == GDK_CONTROL_MASK)
2043
g_fprintf(stderr,"Got Ctrl-click\n");
2044
if (actions_lock == FALSE)
2046
g_timeout_add(POPUP_DELAY, show_actions_menu, NULL);
2052
g_timeout_add(POPUP_DELAY, show_history_menu, GINT_TO_POINTER(figure_histories()));
2056
/* Called when history global hotkey is pressed */
2057
void history_hotkey(char *keystring, gpointer user_data)
2059
g_timeout_add(POPUP_DELAY, show_history_menu, GINT_TO_POINTER(figure_histories()));
2061
/* Called when persistent history global hotkey is pressed */
2062
void phistory_hotkey(char *keystring, gpointer user_data)
2064
if(get_pref_int32("persistent_history") && get_pref_int32("persistent_separate"))
2065
g_timeout_add(POPUP_DELAY, show_history_menu, GINT_TO_POINTER(HIST_DISPLAY_PERSISTENT));
2068
/* Called when actions global hotkey is pressed */
2069
void actions_hotkey(char *keystring, gpointer user_data)
2071
g_timeout_add(POPUP_DELAY, show_actions_menu, NULL);
2074
/* Called when actions global hotkey is pressed */
2075
void menu_hotkey(char *keystring, gpointer user_data)
2077
#ifdef HAVE_APPINDICATOR
2078
/*create_app_indicator(); */
2079
create_parcellite_menu(0, gtk_get_current_event_time());
2080
/** GtkWidget * w=create_parcellite_menu(0, gtk_get_current_event_time());
2081
app_indicator_set_menu (indicator, GTK_MENU (w));*/
2083
show_parcellite_menu(status_icon, 0, 0, NULL);
2087
/* Startup calls and initializations */
2088
static void parcellite_init()
2091
/* Create clipboard */
2092
primary = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
2093
clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
2095
if(FALSE ==g_thread_supported()){
2096
g_fprintf(stderr,"g_thread not init!\n");
2098
clip_lock= g_mutex_new();
2099
hist_lock= g_mutex_new();
2100
g_mutex_unlock(clip_lock);
2102
show_icon=!get_pref_int32("no_icon");
2104
if (get_pref_int32("save_history")){
2106
/*g_printf("Calling read_hist\n"); */
2108
if(NULL != history_list){
2109
struct history_item *c;
2110
c=(struct history_item *)(history_list->data);
2111
if(NULL == (x=is_clipboard_empty(primary)))
2112
update_clipboard(primary,c->text,H_MODE_LIST);
2114
if(NULL == (x=is_clipboard_empty(clipboard)))
2115
update_clipboard(clipboard,c->text,H_MODE_LIST);
2120
g_timeout_add(CHECK_INTERVAL, check_clipboards_tic, NULL);
2121
#ifdef HAVE_APPINDICATOR
2122
check_for_appindictor(NULL);
2123
if(!have_appindicator) /**maybe it slept in, check for it every 30 seconds. */
2124
g_timeout_add(CHECK_APPINDICATOR_INTERVAL, check_for_appindictor, NULL);
2127
/* Bind global keys */
2129
for (i=0;NULL != keylist[i].name; ++i)
2130
bind_itemkey(keylist[i].name,keylist[i].keyfunc);
2132
/* Create status icon */
2135
#ifdef HAVE_APPINDICATOR
2136
if(have_appindicator)/* Indicator */
2137
create_app_indicator();
2139
if(!have_appindicator){
2140
status_icon = gtk_status_icon_new_from_icon_name(PARCELLITE_ICON);
2141
gtk_status_icon_set_tooltip((GtkStatusIcon*)status_icon, _("Clipboard Manager"));
2142
g_signal_connect((GObject*)status_icon, "activate", (GCallback)status_icon_clicked, NULL);
2143
g_signal_connect((GObject*)status_icon, "popup-menu", (GCallback)show_parcellite_menu, NULL);
2150
/***************************************************************************/
2153
which - which fifo we write to.
2155
****************************************************************************/
2156
void write_stdin(struct p_fifo *fifo, int which)
2158
if (!isatty(fileno(stdin))) {
2159
GString* piped_string = g_string_new(NULL);
2160
/* Append stdin to string */
2162
gchar* buffer = (gchar*)g_malloc(256);
2163
if (fgets(buffer, 256, stdin) == NULL) {
2167
g_string_append(piped_string, buffer);
2170
/* Check if anything was piped in */
2171
if (piped_string->len > 0) {
2172
/* Truncate new line character */
2173
/* g_string_truncate(piped_string, (piped_string->len - 1)); */
2174
/* Copy to clipboard */
2175
write_fifo(fifo,which,piped_string->str,piped_string->len);
2179
g_string_free(piped_string, TRUE);
2184
/***************************************************************************/
2188
****************************************************************************/
2189
int main(int argc, char *argv[])
2191
struct cmdline_opts *opts;
2194
bindtextdomain(GETTEXT_PACKAGE, PARCELLITELOCALEDIR);
2195
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
2196
textdomain(GETTEXT_PACKAGE);
2199
gtk_init(&argc, &argv);
2200
/* Read preferences */
2203
if(get_pref_int32("debug_update")) debug_update=1;
2206
opts=parse_options(argc, argv);
2211
mode=PROC_MODE_EXACT;
2212
if(get_pref_int32("multi_user"))
2213
mode|=PROC_MODE_USER_QUALIFY;
2214
/*g_printf("mode=0x%X\n",mode); */
2215
if(proc_find(PARCELLITE_PROG_NAME,mode,NULL)<2) /**1 for me, and 1 for a running instance */
2216
mode=PROG_MODE_DAEMON; /**first instance */
2218
mode=PROG_MODE_CLIENT; /**already running, just access fifos & exit. */
2220
/**get options/cmd line not parsed. */
2221
if( NULL != opts->leftovers)g_print("%s\n",opts->leftovers);
2222
/**init fifo should set up the fifo and the callback (if we are daemon mode) */
2224
if(NULL == (fifo=init_fifo(FIFO_MODE_PRI|mode))) return 1;
2225
if(fifo->dbg) g_fprintf(stderr,"Hit PRI opt!\n");
2227
if(PROG_MODE_CLIENT & mode){
2228
if(NULL != opts->leftovers){
2229
write_fifo(fifo,FIFO_MODE_PRI,opts->leftovers,strlen(opts->leftovers));
2230
g_free(opts->leftovers);
2233
if(fifo->dbg) g_fprintf(stderr,"checking stdin\n");
2234
write_stdin(fifo,FIFO_MODE_PRI);
2238
GtkClipboard* prim = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
2239
/* Print primary text (if any) */
2240
gchar* prim_text = gtk_clipboard_wait_for_text(prim);
2242
g_print("%s", prim_text);
2245
} else if(opts->clipboard){
2246
if(NULL == (fifo=init_fifo(FIFO_MODE_CLI|mode))) return 1;
2248
if(PROG_MODE_CLIENT & mode){
2249
if(NULL != opts->leftovers){
2250
write_fifo(fifo,FIFO_MODE_CLI,opts->leftovers,strlen(opts->leftovers));
2251
g_free(opts->leftovers);
2253
write_stdin(fifo,FIFO_MODE_CLI);
2257
GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
2258
/* Print clipboard text (if any) */
2259
gchar* clip_text = gtk_clipboard_wait_for_text(clip);
2261
g_print("%s", clip_text);
2263
} else { /*use CLIPBOARD*/
2264
GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
2265
if(NULL == (fifo=init_fifo(FIFO_MODE_NONE|mode))) return 1;
2266
/* Copy from unrecognized options */
2267
if(PROG_MODE_CLIENT & mode){
2268
if(NULL != opts->leftovers){
2269
write_fifo(fifo,FIFO_MODE_CLI,opts->leftovers,strlen(opts->leftovers));
2270
g_free(opts->leftovers);
2272
/* Check if stdin is piped */
2273
write_stdin(fifo,FIFO_MODE_CLI);
2277
gchar* clip_text = gtk_clipboard_wait_for_text(clip);
2279
g_print("%s", clip_text);
2283
/* Run as daemon option */
2284
if (opts->daemon && (PROG_MODE_DAEMON & mode)) {
2287
if(PROG_MODE_CLIENT & mode){
2292
/* Init Parcellite */
2293
parcellite_init(mode);
2294
/*g_printf("Start main loop\n"); */
2298
#ifdef HAVE_APPINDICATOR
2299
if (have_appindicator & show_icon)
2300
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_PASSIVE);
2304
keybinder_unbind(get_pref_string("phistory_key"), phistory_hotkey);
2305
keybinder_unbind(get_pref_string("history_key"), history_hotkey);
2306
keybinder_unbind(get_pref_string("actions_key"), actions_hotkey);
2307
keybinder_unbind(get_pref_string("menu_key"), menu_hotkey);
2310
g_free(prefs.history_key);
2311
g_free(prefs.actions_key);
2312
g_free(prefs.menu_key);
2314
g_list_free(history_list);