1 /* Copyright (C) 2007-2008 by Xyhthyx <xyhthyx@gmail.com>
2 *
3 * This file is part of Parcellite.
4 *
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.
9 *
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.
14 *
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/>.
17 */
18
19 #include "parcellite.h"
20 #include <sys/wait.h>
21
22 void setup_icon( void ); /**in main.c */
23
24 #define MAX_HISTORY 1000
25
26 #define INIT_HISTORY_KEY NULL
27 #define INIT_ACTIONS_KEY NULL
28 #define INIT_MENU_KEY NULL
29
30 #define DEF_USE_COPY TRUE
31 #define DEF_USE_PRIMARY FALSE
32 #define DEF_SYNCHRONIZE FALSE
33 #define DEF_SAVE_HISTORY TRUE
34 #define DEF_HISTORY_LIMIT 25
35 #define DEF_HYPERLINKS_ONLY FALSE
36 #define DEF_CONFIRM_CLEAR TRUE
37 #define DEF_SINGLE_LINE TRUE
38 #define DEF_REVERSE_HISTORY FALSE
39 #define DEF_ITEM_LENGTH 50
40 #define DEF_ITEM_LENGTH_MAX 200
41 #define DEF_ELLIPSIZE 2
42 #define DEF_PHISTORY_KEY "<Ctrl><Alt>X"
43 #define DEF_HISTORY_KEY "<Ctrl><Alt>H"
44 #define DEF_ACTIONS_KEY "<Ctrl><Alt>A"
45 #define DEF_MENU_KEY "<Ctrl><Alt>P"
46 #define DEF_NO_ICON FALSE
47
48 /**allow lower nibble to become the number of items of this type */
49 #define PREF_TYPE_TOGGLE 0x10
50 #define PREF_TYPE_SPIN 0x20
51 #define PREF_TYPE_COMBO 0x30
52 #define PREF_TYPE_ENTRY 0x40 /**gchar * */
53 #define PREF_TYPE_ALIGN 0x50 /**label, then align box */
54 #define PREF_TYPE_SPACER 0x60
55 #define PREF_TYPE_FRAME 0x70 /**frame for each section */
56 #define PREF_TYPE_MASK 0xF0
57 #define PREF_TYPE_NMASK 0xF
58 #define PREF_TYPE_SINGLE_LINE 1
59
60 #define PREF_SEC_NONE 0
61 #define PREF_SEC_CLIP 1
62 #define PREF_SEC_HIST 2
63 #define PREF_SEC_MISC 3
64 #define PREF_SEC_DISP 4
65
66 #define PREF_SEC_ACT 5
67 #define PREF_SEC_XMISC 6
68
69 #define RC_VERSION_NAME "RCVersion"
70 #define RC_VERSION 1
71
72 struct myadj {
73 gdouble lower;
74 gdouble upper;
75 gdouble step;
76 gdouble page;
77 };
78 static char *icon_name="parcellite";
79 struct myadj align_hist_x={1,100,1,10};
80 struct myadj align_hist_y={1,100,1,10};
81 struct myadj align_data_lim={0,1000000,1,10};
82 struct myadj align_hist_lim={5, MAX_HISTORY, 1, 10};
83 struct myadj align_line_lim={5, DEF_ITEM_LENGTH_MAX, 1, 5};
84 struct pref_item {
85 gchar *name; /**name/id to find pref */
86 gint32 val; /**int val */
87 float fval;
88 gchar *cval; /**char val */
89 GtkWidget *w; /**widget in menu */
90 gint type; /**PREF_TYPE_ */
91 gchar *desc; /**shows up in menu */
92 gchar *tip; /**tooltip */
93 gint sec; /**clipboard,history, misc,display,hotkeys */
94 gchar *sig; /**signal, if any */
95 GCallback sfunc; /**function to call */
96 struct myadj *adj;
97 };
98 static struct pref_item dummy[2];
99 static void check_toggled(GtkToggleButton *togglebutton, gpointer user_data);
100 static void search_toggled(GtkToggleButton *b, gpointer user);
101 static gint dbg=0;
102 static int tool_bitfield=0;
103 static int tool_bitfield_check=0;
104 struct pref2int *pref2int_mapper=NULL;
105 struct tool_flag tool_flags[]={
106 {.flag=TOOL_XDOTOOL,.name="xdotool"},
107 {.flag=0,.name=NULL },
108 /*{.flag=,.name= }, */
109 };
110 /**hot key list, mainly for easy sanity checks. */
111 struct keys keylist[]={
112 {.name="menu_key",.keyval=DEF_MENU_KEY,.keyfunc=(void *)menu_hotkey},
113 {.name="history_key",.keyval=DEF_HISTORY_KEY,.keyfunc=(void *)history_hotkey},
114 {.name="phistory_key",.keyval=DEF_PHISTORY_KEY,.keyfunc=(void *)phistory_hotkey},
115 {.name="actions_key",.keyval=DEF_ACTIONS_KEY,.keyfunc=(void *)actions_hotkey},
116 {.name=NULL,.keyval=NULL,.keyfunc=(void *)0},
117 };
118 /**must be in same order as above struct array */
119 gchar *def_keyvals[]={ DEF_MENU_KEY,DEF_HISTORY_KEY,DEF_PHISTORY_KEY,DEF_ACTIONS_KEY};
120 struct pref_item myprefs[]={
121 /**Behaviour */
122 /**Clipboards */
123 {.adj=NULL,.cval=NULL,.sig=NULL,.sfunc=NULL,.sec=PREF_SEC_CLIP,.name=NULL,.type=PREF_TYPE_FRAME,.desc="<b>Clipboards</b>",.tip=NULL,.val=0},
124 {.adj=NULL,.cval=NULL,.sig="toggled",.sfunc=(GCallback)check_toggled,.sec=PREF_SEC_CLIP,.name="use_copy",.type=PREF_TYPE_TOGGLE,.desc="Use _Copy (Ctrl-C)",.tip="If checked, Use the clipboard, which is Ctrl-C, Ctrl-V",.val=DEF_USE_COPY},
125 {.adj=NULL,.cval=NULL,.sig="toggled",.sfunc=(GCallback)check_toggled,.sec=PREF_SEC_CLIP,.name="use_primary",.type=PREF_TYPE_TOGGLE,.desc="Use _Primary (Selection)",.tip="If checked, Use the primary clipboard (mouse highlight-copy, middle mouse button paste)",.val=DEF_USE_PRIMARY},
126 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_CLIP,.name="synchronize",.type=PREF_TYPE_TOGGLE,.desc="S_ynchronize clipboards",.tip="If checked, will keep both clipboards with the same content. If primary is pasted, then copy will have the same data.",.val=DEF_SYNCHRONIZE},
127 /**History */
128 {.adj=NULL,.cval=NULL,.sig=NULL,.sfunc=NULL,.sec=PREF_SEC_HIST,.name=NULL,.type=PREF_TYPE_FRAME,.desc="<b>History</b>",.tip=NULL,.val=0},
129 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="save_history",.type=PREF_TYPE_TOGGLE,.desc="Save history",.tip="Save history to a file.",.val=DEF_SAVE_HISTORY},
130 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="history_pos",.type=PREF_TYPE_TOGGLE|PREF_TYPE_SINGLE_LINE,.desc="Position history",.tip="If checked, use X, Y to position the history list",.val=0},
131 {.adj=&align_hist_x,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="history_x",.type=PREF_TYPE_SPIN|PREF_TYPE_SINGLE_LINE,.desc="<b>X</b>",.tip=NULL,.val=1},
132 {.adj=&align_hist_y,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="history_y",.type=PREF_TYPE_SPIN|PREF_TYPE_SINGLE_LINE,.desc="<b>Y</b>",.tip="Position in pixels from the top of the screen",.val=1},
133 {.adj=&align_hist_lim,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="history_limit",.type=PREF_TYPE_SPIN,.desc="Items in history",.tip="Maximum number of clipboard entries to keep",.val=DEF_HISTORY_LIMIT},
134 {.adj=&align_data_lim,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="data_size",.type=PREF_TYPE_SPIN,.desc="Max Data Size(KB)",.tip="Maximum data size of entire history list",.val=0},
135 {.adj=&align_hist_lim,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="item_size",.type=PREF_TYPE_SPIN,.desc="Max Item Size(KB)",.tip="Maximum data size of one item",.val=0},
136 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="nop",.type=PREF_TYPE_SPACER,.desc=" ",.tip=NULL},
137 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="automatic_paste",.type=PREF_TYPE_TOGGLE|PREF_TYPE_SINGLE_LINE,.desc="Auto Paste",.tip="If checked, will use xdotool to paste wherever the mouse is.\nNOTE! Package xdotool MUST BE INSTALLED for this to work.",.val=0},
138 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="auto_key",.type=PREF_TYPE_TOGGLE|PREF_TYPE_SINGLE_LINE,.desc="Key",.tip="If checked, will use Ctrl-V paste.",.val=0},
139 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="auto_mouse",.type=PREF_TYPE_TOGGLE|PREF_TYPE_SINGLE_LINE,.desc="Mouse",.tip="If checked, will use middle mouse to paste.",.val=1},
140 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="key_input",.type=PREF_TYPE_TOGGLE,.desc="Keyboard Input",.tip="If checked, will emit the history entry via the keyboard entry.",.val=0},
141 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="restore_empty",.type=PREF_TYPE_TOGGLE,.desc="Restore Empty",.tip="If checked, will restore clipboard entry on application exit.",.val=1},
142 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_HIST,.name="rc_edit",.type=PREF_TYPE_TOGGLE,.desc="Right Click Edit",.tip="If checked, edit the history item clicked on.",.val=0},
143
144 /**Miscellaneous */
145 {.adj=NULL,.cval=NULL,.sig=NULL,.sfunc=NULL,.sec=PREF_SEC_MISC,.name=NULL,.type=PREF_TYPE_FRAME,.desc="<b>Miscellaneous</b>",.tip=NULL,.val=0},
146 {.adj=NULL,.cval=NULL,.sig="toggled",.sfunc=(GCallback)search_toggled,.sec=PREF_SEC_MISC,.name="type_search",.type=PREF_TYPE_TOGGLE,.desc="Search As You Type",.tip="If checked, does a search-as-you-type. Turns red if not found. Goes to top (Alt-E) line when no chars are entered for search"},
147 {.adj=NULL,.cval=NULL,.sig="toggled",.sfunc=(GCallback)search_toggled,.sec=PREF_SEC_MISC,.name="case_search",.type=PREF_TYPE_TOGGLE,.desc="Case Sensitive Search",.tip="If checked, does case sensitive search"},
148 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_MISC,.name="ignore_whiteonly",.type=PREF_TYPE_TOGGLE,.desc="Ignore Whitespace Only",.tip="If checked, will ignore any clipboard additions that contain only whitespace."},
149 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_MISC,.name="trim_wspace_begend",.type=PREF_TYPE_TOGGLE,.desc="Trim Whitespace",.tip="If checked, will trim whitespace from beginning and end of entry."},
150 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_MISC,.name="trim_newline",.type=PREF_TYPE_TOGGLE,.desc="Trim Newlines",.tip="If checked, will replace newlines with spaces."},
151 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_MISC,.name="hyperlinks_only",.type=PREF_TYPE_TOGGLE,.desc="Capture hyperlinks only",.tip=NULL,.val=DEF_HYPERLINKS_ONLY},
152 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_MISC,.name="confirm_clear",.type=PREF_TYPE_TOGGLE,.desc="Confirm before clearing history",.tip=NULL,.val=DEF_CONFIRM_CLEAR},
153
154 /**Display add icon here...*/
155 {.adj=NULL,.cval=NULL,.sig=NULL,.sfunc=NULL,.sec=PREF_SEC_DISP,.name=NULL,.type=PREF_TYPE_FRAME,.desc="<b>Items</b>",.tip=NULL,.val=0},
156 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="current_on_top",.type=PREF_TYPE_TOGGLE,.desc="Current entry on top",.tip="If checked, places current clipboard entry at top of list. If not checked, history does not get sorted.",.val=TRUE},
157 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="single_line",.type=PREF_TYPE_TOGGLE,.desc="Show in a single line",.tip=NULL,.val=DEF_SINGLE_LINE},
158 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="reverse_history",.type=PREF_TYPE_TOGGLE,.desc="Show in reverse order",.tip="If checked, the current entry will be on the bottom instead of the top.",.val=DEF_REVERSE_HISTORY},
159 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="nop",.type=PREF_TYPE_SPACER,.desc=" ",.tip=NULL},
160 {.adj=&align_line_lim,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="item_length",.type=PREF_TYPE_SPIN,.desc="Character length of items",.tip=NULL,.val=DEF_ITEM_LENGTH},
161 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="nop",.type=PREF_TYPE_SPACER,.desc=" ",.tip=NULL},
162 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="persistent_history",.type=PREF_TYPE_TOGGLE,.desc="Persistent History",.tip="If checked, enables the persistent history.",.val=FALSE},
163 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="persistent_separate",.type=PREF_TYPE_TOGGLE,.desc="Persistent As Separate List",.tip="If checked, puts the persistent history in a new list and enables the Persistent History HotKey. You will need to use the persistent history hotkey to see the history.",.val=FALSE},
164 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="persistent_on_top",.type=PREF_TYPE_TOGGLE,.desc="Persistent On Top",.tip="If checked, puts the persistent history at the top of the history list. If the option above is checked, this option has no effect.",.val=FALSE},
165 {.adj=NULL,.cval="\\n",.sig=NULL,.sec=PREF_SEC_DISP,.name="persistent_delim",.type=PREF_TYPE_ENTRY,.desc="Paste All Entry Delimiter",.tip="This string will be inserted between each line of history for paste all."},
166 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="nonprint_disp",.type=PREF_TYPE_TOGGLE,.desc="Alternate Non-printing Display",.tip="If checked, will display tabs with Right arrow (utf8,\\2192), newlines with pharagrph(\\204b), and spaces with square-u(\\2423).", .val=FALSE},
167 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_DISP,.name="nop",.type=PREF_TYPE_SPACER,.desc=" ",.tip=NULL},
168 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_NONE,.name="ellipsize",.type=PREF_TYPE_COMBO,.desc="Omit items in the:",.tip=NULL,.val=DEF_ELLIPSIZE},
169
170 /**miscellaneous that doesn't fit elswhew */
171 {.adj=NULL,.cval=NULL,.sig=NULL,.sfunc=NULL,.sec=PREF_SEC_XMISC,.name=NULL,.type=PREF_TYPE_FRAME,.desc="<b>Miscellaneous</b>",.tip=NULL,.val=0},
172 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_XMISC,.name="multi_user",.type=PREF_TYPE_TOGGLE,.desc="Multiuser",.tip="If checked, enables checking multiple concurrent user logic. Use if several different users are logged in at the same time.",.val=TRUE},
173 {.adj=NULL,.cval=PARCELLITE_ICON,.sig=NULL,.sec=PREF_SEC_XMISC,.name="icon_name",.type=PREF_TYPE_ENTRY,.desc="Parcellite Icon Name",.tip="Name of Parcellite icon. If this is mis-typed, icon will not appear.",.val=TRUE},
174 #ifdef DEBUG_UPDATE
175 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_XMISC,.name="debug_update",.type=PREF_TYPE_TOGGLE,.desc="DebugUpdate",.tip="If checked, enables debug prints on clipboard update logic. This only takes effect when enabled at start up, and may be disabled at compile time.",.val=FALSE},
176 #endif
177
178 /**Action Keys */
179 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_ACT,.name="menu_key",.type=PREF_TYPE_ENTRY,.desc="Menu key combination",.tip=NULL},
180 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_ACT,.name="history_key",.type=PREF_TYPE_ENTRY,.desc="History key combination:",.tip=NULL},
181 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_ACT,.name="phistory_key",.type=PREF_TYPE_ENTRY,.desc="Persistent history key:",.tip=NULL},
182 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_ACT,.name="actions_key",.type=PREF_TYPE_ENTRY,.desc="Actions key combination:",.tip=NULL},
183 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_NONE,.name="no_icon",.val=FALSE},
184 {.adj=NULL,.cval=NULL,.sig=NULL,.sec=PREF_SEC_NONE,.name=NULL,.desc=NULL},
185 };
186
187 GtkListStore* actions_list;
188 GtkTreeSelection* actions_selection;
189
190 /***************************************************************************/
191 /** Set up which tools are available on the system.
192 \n\b Arguments:
193 \n\b Returns:
194 ****************************************************************************/
check_for_tools_exit(GPid pid,gint status,gpointer data)195 static void check_for_tools_exit(GPid pid, gint status, gpointer data)
196 {
197 int flag= GPOINTER_TO_INT(data);
198 g_spawn_close_pid(pid);
199 if( WIFEXITED(status) ){
200 if(0 ==WEXITSTATUS(status) )
201 tool_bitfield|=flag;
202
203 }
204 tool_bitfield_check &= ~(flag);
205 g_fprintf(stderr,"Flag 0x%04x, status %d, EXIT %d STAT %d\n",flag,status,WIFEXITED(status),WEXITSTATUS(status) );
206
207 }
208 /***************************************************************************/
209 /** Check for installed tools and set flags accordingly.
210 \n\b Arguments:
211 \n\b Returns:
212 ****************************************************************************/
check_for_tools(void)213 void check_for_tools(void )
214 {
215 GPid pid;
216 gchar **argv;
217 int i;
218 for (i=0; NULL != tool_flags[i].name; ++i){
219 gchar cmd[100];
220 tool_bitfield_check|=tool_flags[i].flag;
221 sprintf(cmd,"/bin/sh -c 'which %s'>/dev/null\n",tool_flags[i].name);
222 g_shell_parse_argv(cmd, NULL, &argv, NULL);
223 g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL);
224 g_child_watch_add(pid, (GChildWatchFunc)check_for_tools_exit, GINT_TO_POINTER(tool_flags[i].flag));
225 g_strfreev(argv);
226 }
227 }
228
229 /***************************************************************************/
230 /** .
231 \n\b Arguments:
232 \n\b Returns: Structure of item
233 ****************************************************************************/
get_pref(char * name)234 struct pref_item* get_pref(char *name)
235 {
236 int i;
237 for (i=0;NULL != myprefs[i].desc; ++i){
238 if(NULL == myprefs[i].name)
239 continue;
240 if(!g_strcmp0(myprefs[i].name,name))
241 return &myprefs[i];
242 }
243 return &dummy[0];
244 }
245 /***************************************************************************/
246 /** Set an array that is updated every time the preferences are changed.
247 \n\b Arguments:
248 \n\b Returns:
249 ****************************************************************************/
pref_mapper(struct pref2int * m,int mode)250 void pref_mapper (struct pref2int *m, int mode)
251 {
252 int i;
253 if(PM_INIT == mode){
254 pref2int_mapper=m;
255 return;
256 }
257 for (i=0; pref2int_mapper[i].name != NULL && pref2int_mapper[i].val != NULL; ++i){
258 struct pref_item *p=get_pref(pref2int_mapper[i].name);
259 *pref2int_mapper[i].val=p->val;
260 }
261
262 }
263 /***************************************************************************/
264 /** .
265 \n\b Arguments: pointer of widget
266 \n\b Returns: Structure of item
267 ****************************************************************************/
get_pref_by_widget(GtkWidget * w)268 struct pref_item* get_pref_by_widget(GtkWidget *w)
269 {
270 int i;
271 for (i=0;NULL != myprefs[i].desc; ++i){
272 if(NULL == myprefs[i].name)
273 continue;
274 if(w == myprefs[i].w)
275 return &myprefs[i];
276 }
277 return &dummy[0];
278 }
279 /***************************************************************************/
280 /** Find first item in section.
281 \n\b Arguments:
282 \n\b Returns: index into struct where found, or end of list
283 ****************************************************************************/
get_first_pref(int section)284 int get_first_pref(int section)
285 {
286 int i;
287 for (i=0;NULL != myprefs[i].desc; ++i){
288 if(section == myprefs[i].sec){
289 if(dbg)g_printf("gfp:returning sec %d, '%s\n",section, myprefs[i].desc);
290 return i;
291 }
292
293 }
294 if(dbg)g_printf("Didn't find section %d\n",i);
295 return i;
296 }
297 /***************************************************************************/
298 /** .
299 \n\b Arguments:
300 \n\b Returns:
301 ****************************************************************************/
init_pref(void)302 int init_pref( void )
303 {
304 GdkScreen *s;
305 gint sx,sy;
306 s=gdk_screen_get_default();
307 sx= gdk_screen_get_width(s);
308 sy= gdk_screen_get_height(s);
309 dummy[0].cval="dummy String";
310 dummy[0].w=NULL;
311 dummy[0].desc="Dummy desc";
312 dummy[0].tip="Dummy tip";
313 dummy[1].name=NULL;
314 dummy[1].sec=PREF_SEC_NONE;
315 align_hist_y.upper=sy-100;
316 align_hist_x.upper=sx-100;
317 return 0;
318 }
319 /***************************************************************************/
320 /** set the wideget of item.
321 \n\b Arguments:
322 \n\b Returns:
323 ****************************************************************************/
set_pref_widget(char * name,GtkWidget * w)324 int set_pref_widget (char *name, GtkWidget *w)
325 {
326 struct pref_item *p=get_pref(name);
327 if(NULL == p)
328 return -1;
329 p->w=w;
330 return 0;
331 }
332 /***************************************************************************/
333 /** get the char * value of string.
334 \n\b Arguments:
335 \n\b Returns:
336 ****************************************************************************/
get_pref_widget(char * name)337 GtkWidget *get_pref_widget (char *name)
338 {
339 struct pref_item *p=get_pref(name);
340 if(NULL == p)
341 return dummy[0].w;
342 return p->w;
343 }
344 /***************************************************************************/
345 /** Set the int value of name.
346 \n\b Arguments:
347 \n\b Returns:
348 ****************************************************************************/
set_pref_int32(char * name,gint32 val)349 int set_pref_int32(char *name, gint32 val)
350 {
351 struct pref_item *p=get_pref(name);
352 if(NULL == p)
353 return -1;
354 p->val=val;
355 return 0;
356 }
357
358 /***************************************************************************/
359 /** get the int value of name.
360 \n\b Arguments:
361 \n\b Returns:
362 ****************************************************************************/
get_pref_int32(char * name)363 gint32 get_pref_int32 (char *name)
364 {
365 struct pref_item *p=get_pref(name);
366 if(NULL == p)
367 return -1;
368 return p->val;
369 }
370 /***************************************************************************/
371 /** Set the float value of name.
372 \n\b Arguments:
373 \n\b Returns:
374 ****************************************************************************/
set_pref_float(char * name,float fval)375 int set_pref_float(char *name, float fval)
376 {
377 struct pref_item *p=get_pref(name);
378 if(NULL == p)
379 return -1;
380 p->fval=fval;
381 return 0;
382 }
383
384 /***************************************************************************/
385 /** get the float value of name.
386 \n\b Arguments:
387 \n\b Returns:
388 ****************************************************************************/
get_pref_float(char * name)389 float get_pref_float (char *name)
390 {
391 struct pref_item *p=get_pref(name);
392 if(NULL == p)
393 return -1;
394 return p->fval;
395 }
396
397 /***************************************************************************/
398 /** set the char *value of string.
399 \n\b Arguments:
400 \n\b Returns:
401 ****************************************************************************/
set_pref_string(char * name,char * string)402 int set_pref_string (char *name, char *string)
403 {
404 struct pref_item *p=get_pref(name);
405 if(NULL == p)
406 return -1;
407 if(p->cval != NULL)
408 g_free(p->cval);
409 p->cval=g_strdup(string);
410 return 0;
411 }
412
413 /***************************************************************************/
414 /** get the char * value of string.
415 \n\b Arguments:
416 \n\b Returns:
417 ****************************************************************************/
get_pref_string(char * name)418 gchar *get_pref_string (char *name)
419 {
420 struct pref_item *p=get_pref(name);
421 if(NULL == p)
422 return dummy[0].cval;
423 return p->cval;
424 }
425
426 /***************************************************************************/
427 /** .
428 \n\b Arguments:
429 \n\b Returns:
430 ****************************************************************************/
unbind_itemkey(char * name,void * fhk)431 void unbind_itemkey(char *name, void *fhk )
432 {
433
434 struct pref_item *p=get_pref(name);
435 if(NULL == p){
436 if(dbg)g_printf("pref:null found for %s\n",name);
437 return;
438 }
439 keybinder_unbind(p->cval, fhk);
440 g_free(p->cval);
441 p->cval=NULL;
442
443 }
444
445 /***************************************************************************/
446 /** .
447 \n\b Arguments:
448 \n\b Returns:
449 ****************************************************************************/
bind_itemkey(char * name,void (fhk)(char *,gpointer))450 void bind_itemkey(char *name, void (fhk)(char *, gpointer) )
451 {
452 struct pref_item *p=get_pref(name);
453 if(NULL ==p){
454 if(dbg)g_printf("pref2:null found for %s\n",name);
455 return;
456 }
457 if(NULL != p->cval && 0 != p->cval)
458 keybinder_bind(p->cval, fhk, NULL);
459 }
460
461
462 /***************************************************************************/
463 /** .
464 \n\b Arguments:
465 \n\b Returns:
466 ****************************************************************************/
set_key_entry(gchar * name,gchar * val)467 void set_key_entry(gchar *name, gchar *val)
468 {
469 int i;
470 for (i=0;NULL != keylist[i].name; ++i){
471 if(!g_strcmp0(keylist[i].name,name)){
472 if(NULL == val)
473 keylist[i].keyval="";
474 else
475 keylist[i].keyval=val;
476 return;
477 }
478 }
479 }
480
481 /***************************************************************************/
482 /** .
483 \n\b Arguments:
484 \n\b Returns:
485 ****************************************************************************/
set_keys_from_prefs(void)486 void set_keys_from_prefs( void )
487 {
488 int i,l;
489 for (i=0;NULL != keylist[i].name; ++i){
490 /**NOTE: do not set up default keys here! User may WANT them null */
491 /** call egg_accelerator_parse_virtual to validate? */
492 set_key_entry(keylist[i].name,get_pref_string(keylist[i].name));
493 /*g_fprintf(stderr,"key '%s' val '%s'\n",keylist[i].name, keylist[i].keyval); */
494 }
495 /**now go through and make sure we have no duplicates */
496 for (i=0;NULL != keylist[i].name; ++i){
497 if(0 != keylist[i].keyval[0]){
498 /**see if it exists elsewhere */
499 for (l=0;NULL != keylist[l].name; ++l){
500 if(l!=i && 0 != keylist[l].keyval[0]){
501 if(!g_strcmp0(keylist[i].keyval, keylist[l].keyval)) { /**conflict!, delete second */
502 g_fprintf(stderr,"Error! Hot keys have same key '%s': '%s' and '%s'. Ignoring second entry\n",keylist[i].keyval,keylist[i].name,keylist[l].name);
503 set_key_entry(keylist[l].name,"");
504 set_pref_string(keylist[l].name,"");
505 }
506 }
507 }
508 }
509 }
510
511 }
512 /***************************************************************************/
513 /** .
514 \n\b Arguments:
515 mode - if 0, do not display helper missing dialog.
516 \n\b Returns:
517 ****************************************************************************/
check_sanity(int mode)518 void check_sanity(int mode)
519 {
520 gint32 x,y;
521 gchar *val;
522 check_for_tools(); /**update the list of tools parcellite needs. */
523 while(tool_bitfield_check){
524 g_main_context_iteration(NULL, TRUE);
525 usleep(100000);
526 }
527 val=get_pref_string("icon_name");
528 if(NULL != val && strcmp(icon_name, val)){
529 setup_icon( );
530 icon_name=strdup(val);
531 }
532 x=get_pref_int32("history_x");
533 y=get_pref_int32("history_y");
534 postition_history(NULL,&x,&y,NULL, 0); /**have function limit x,y according to screen limits */
535 set_pref_int32("history_x",x);
536 set_pref_int32("history_y",y);
537 x=get_pref_int32("history_limit");
538 if ((!x) || (x > MAX_HISTORY) || (x < 0))
539 set_pref_int32("history_limit",DEF_HISTORY_LIMIT);
540 x=get_pref_int32("item_length");
541 if ((!x) || (x > DEF_ITEM_LENGTH_MAX) || (x < 0))
542 set_pref_int32("item_length",DEF_ITEM_LENGTH);
543 x=get_pref_int32("ellipsize");
544 if ((!x) || (x > 3) || (x < 0))
545 set_pref_int32("ellipsize",DEF_ELLIPSIZE);
546 set_keys_from_prefs();
547
548
549 if(get_pref_int32("persistent_history")){
550 if(get_pref_int32("persistent_separate"))
551 set_pref_int32("persistent_on_top",0);
552 }else{
553 set_pref_int32("persistent_separate",0);
554 set_pref_int32("persistent_on_top",0);
555 }
556 if(get_pref_int32("automatic_paste")){
557 if(!(tool_bitfield&TOOL_XDOTOOL)){
558 g_fprintf(stderr,"tool_bitfield=0x%x\n",tool_bitfield);
559 set_pref_int32("automatic_paste",0);
560 if( mode )
561 show_gtk_dialog("xdotool is not installed\nParcellite's auto-paste will not function without xdotool.","xdotool Not Installed" );
562 } else{
563 if(get_pref_int32("auto_key") && get_pref_int32("auto_mouse"))
564 set_pref_int32("auto_key",0);
565 if(!get_pref_int32("auto_key") && !get_pref_int32("auto_mouse"))
566 set_pref_int32("auto_mouse",1);
567 }
568 }
569 }
570 /* Apply the new preferences */
apply_preferences()571 static void apply_preferences()
572 {
573 int i;
574 /* Unbind the keys before binding new ones */
575 for (i=0;NULL != keylist[i].name; ++i)
576 unbind_itemkey(keylist[i].name,keylist[i].keyfunc);
577
578
579 for (i=0;NULL != myprefs[i].desc; ++i){
580 if(NULL == myprefs[i].name)
581 continue;
582 switch(myprefs[i].type&PREF_TYPE_MASK){
583 case PREF_TYPE_TOGGLE:
584 myprefs[i].val=gtk_toggle_button_get_active((GtkToggleButton*)myprefs[i].w);
585 break;
586 case PREF_TYPE_SPIN:
587 myprefs[i].val=gtk_spin_button_get_value_as_int((GtkSpinButton*)myprefs[i].w);
588 myprefs[i].fval=(float)gtk_spin_button_get_value((GtkSpinButton*)myprefs[i].w);
589 break;
590 case PREF_TYPE_COMBO:
591 myprefs[i].val=gtk_combo_box_get_active((GtkComboBox*)myprefs[i].w) + 1;
592 break;
593 case PREF_TYPE_ENTRY:
594 myprefs[i].cval=g_strdup(gtk_entry_get_text((GtkEntry*)myprefs[i].w ));
595 break;
596 case PREF_TYPE_SPACER:
597 break;
598 default: if(dbg)g_printf("apply_pref:don't know how to handle type %d\n",myprefs[i].type);
599 break;
600 }
601 }
602 check_sanity(1);
603 /* Bind keys and apply the new history limit */
604 for (i=0;NULL != keylist[i].name; ++i)
605 bind_itemkey(keylist[i].name,keylist[i].keyfunc);
606 truncate_history();
607 pref_mapper(NULL, PM_UPDATE);
608 }
609
610
611 /***************************************************************************/
612 /** .
613 \n\b Arguments:
614 \n\b Returns:
615 ****************************************************************************/
save_preferences()616 static void save_preferences()
617 {
618 int i;
619 /* Create key */
620 GKeyFile* rc_key = g_key_file_new();
621 g_key_file_set_integer(rc_key, "rc", RC_VERSION_NAME, RC_VERSION);
622 if(0 == get_pref_int32("type_search"))
623 set_pref_int32("case_search",0);
624 /* Add values */
625 for (i=0;NULL != myprefs[i].desc; ++i){
626 if(NULL == myprefs[i].name)
627 continue;
628 switch(myprefs[i].type&PREF_TYPE_MASK){
629 case PREF_TYPE_TOGGLE:
630 g_key_file_set_boolean(rc_key, "rc", myprefs[i].name, myprefs[i].val);
631 break;
632 case PREF_TYPE_COMBO:
633 case PREF_TYPE_SPIN:
634 g_key_file_set_integer(rc_key, "rc", myprefs[i].name, myprefs[i].val);
635 break;
636 case PREF_TYPE_ENTRY:
637 g_key_file_set_string(rc_key, "rc", myprefs[i].name, myprefs[i].cval);
638 break;
639 case PREF_TYPE_SPACER:
640 break;
641 default: if(dbg)g_printf("save_pref:don't know how to handle type %d\n",myprefs[i].type);
642 break;
643 }
644 }
645 /* Check config and data directories */
646 check_dirs();
647 /* Save key to file */
648 gchar* rc_file = g_build_filename(g_get_user_config_dir(), PREFERENCES_FILE, NULL);
649 g_file_set_contents(rc_file, g_key_file_to_data(rc_key, NULL, NULL), -1, NULL);
650 g_key_file_free(rc_key);
651 g_free(rc_file);
652 }
653
654
655 /***************************************************************************/
656 /** Read the parcelliterc file.
657 \n\b Arguments:
658 mode - 0 to not display missing helper warnings
659 \n\b Returns:
660 ****************************************************************************/
read_preferences(int mode)661 void read_preferences(int mode)
662 {
663 gchar *c,*rc_file = g_build_filename(g_get_user_config_dir(), PREFERENCES_FILE, NULL);
664 gint32 z;
665 GError *err=NULL;
666 struct pref_item *p;
667 init_pref();
668 /* Create key */
669 GKeyFile* rc_key = g_key_file_new();
670 if (g_key_file_load_from_file(rc_key, rc_file, G_KEY_FILE_NONE, NULL)) {
671 int i;
672 i=g_key_file_get_integer(rc_key, "rc", RC_VERSION_NAME,&err); /**this begins in 1.1.8 */
673 /* Load values */
674 for (i=0;NULL != myprefs[i].desc; ++i){
675 if(NULL == myprefs[i].name)
676 continue;
677 err=NULL;
678 switch(myprefs[i].type&PREF_TYPE_MASK){
679 case PREF_TYPE_TOGGLE:
680 z=g_key_file_get_boolean(rc_key, "rc", myprefs[i].name,&err);
681 if( NULL ==err)
682 myprefs[i].val=z;
683 break;
684 case PREF_TYPE_COMBO:
685 case PREF_TYPE_SPIN:
686 z=g_key_file_get_integer(rc_key, "rc", myprefs[i].name,&err);
687 if( NULL ==err)
688 myprefs[i].val=z;
689 break;
690 case PREF_TYPE_ENTRY:
691 c=g_key_file_get_string(rc_key, "rc", myprefs[i].name, &err);
692 if( NULL ==err)
693 myprefs[i].cval=c;
694 break;
695 case PREF_TYPE_SPACER:
696 break;
697 default:
698 if(dbg) g_printf("read_pref:don't know how to handle type %d for '%s'\n",myprefs[i].type,myprefs[i].name);
699 continue;
700 break;
701 }
702 if(NULL != err)
703 g_printf("Unable to load pref '%s'\n",myprefs[i].name);
704 if(dbg)g_printf("rp:Set '%s' to %d (%s)\n",myprefs[i].name, myprefs[i].val, myprefs[i].cval);
705 }
706 p=get_pref("type_search");
707 if(0 == p->val){
708 p=get_pref("case_search");
709 p->val=0;
710 }
711
712 /* Check for errors and set default values if any */
713 check_sanity(mode);
714 }
715 else { /* Init default keys on error */
716 int i;
717
718 for (i=0;NULL != keylist[i].name; ++i)
719 set_pref_string(keylist[i].name,def_keyvals[i]);
720 }
721 pref_mapper(NULL, PM_UPDATE);
722 g_key_file_free(rc_key);
723 g_free(rc_file);
724 }
725
726 /* Read ~/.parcellite/actions into the treeview */
read_actions()727 static void read_actions()
728 {
729 /* Open the file for reading */
730 gchar* path = g_build_filename(g_get_user_data_dir(), ACTIONS_FILE, NULL);
731 FILE* actions_file = fopen(path, "rb");
732 g_free(path);
733 /* Check that it opened and begin read */
734 if (actions_file)
735 {
736 /* Keep a row reference */
737 GtkTreeIter row_iter;
738 /* Read the size of the first item */
739 gint size=0;
740 if(0 ==fread(&size, 4, 1, actions_file)) g_print("P1:0 Items read\n");
741 /* Continue reading until size is 0 */
742 while (size != 0)
743 {
744 /* Read name */
745 gchar* name = (gchar*)g_malloc(size + 1);
746 if(0 ==fread(name, size, 1, actions_file)) g_print("P1:0 Items read\n");
747 name[size] = '\0';
748 if(0 ==fread(&size, 4, 1, actions_file)) g_print("P1:0 Items read\n");
749 /* Read command */
750 gchar* command = (gchar*)g_malloc(size + 1);
751 if(0 ==fread(command, size, 1, actions_file)) g_print("P1:0 Items read\n");
752 command[size] = '\0';
753 if(0 ==fread(&size, 4, 1, actions_file)) g_print("P1:0 Items read\n");
754 /* Append the read action */
755 gtk_list_store_append(actions_list, &row_iter);
756 gtk_list_store_set(actions_list, &row_iter, 0, name, 1, command, -1);
757 g_free(name);
758 g_free(command);
759 }
760 fclose(actions_file);
761 }
762 }
763
764 /* Save the actions treeview to ~/.local/share/parcellite/actions */
save_actions()765 static void save_actions()
766 {
767 /* Check config and data directories */
768 check_dirs();
769 /* Open the file for writing */
770 gchar* path = g_build_filename(g_get_user_data_dir(), ACTIONS_FILE, NULL);
771 FILE* actions_file = fopen(path, "wb");
772 g_free(path);
773 /* Check that it opened and begin write */
774 if (actions_file)
775 {
776 GtkTreeIter action_iter;
777 /* Get and check if there's a first iter */
778 if (gtk_tree_model_get_iter_first((GtkTreeModel*)actions_list, &action_iter))
779 {
780 do
781 {
782 /* Get name and command */
783 gchar *name, *command;
784 gtk_tree_model_get((GtkTreeModel*)actions_list, &action_iter, 0, &name, 1, &command, -1);
785 GString* s_name = g_string_new(name);
786 GString* s_command = g_string_new(command);
787 g_free(name);
788 g_free(command);
789 /* Check that there's text to save */
790 if ((s_name->len == 0) || (s_command->len == 0))
791 {
792 /* Free strings and skip iteration */
793 g_string_free(s_name, TRUE);
794 g_string_free(s_command, TRUE);
795 continue;
796 }
797 else
798 {
799 /* Save action */
800 fwrite(&(s_name->len), 4, 1, actions_file);
801 fputs(s_name->str, actions_file);
802 fwrite(&(s_command->len), 4, 1, actions_file);
803 fputs(s_command->str, actions_file);
804 /* Free strings */
805 g_string_free(s_name, TRUE);
806 g_string_free(s_command, TRUE);
807 }
808 }
809 while(gtk_tree_model_iter_next((GtkTreeModel*)actions_list, &action_iter));
810 }
811 /* End of file write */
812 gint end = 0;
813 fwrite(&end, 4, 1, actions_file);
814 fclose(actions_file);
815 }
816 }
817
818 /* Called when clipboard checks are pressed */
check_toggled(GtkToggleButton * togglebutton,gpointer user_data)819 static void check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
820 {
821 if (gtk_toggle_button_get_active((GtkToggleButton*)get_pref_widget("use_copy")) &&
822 gtk_toggle_button_get_active((GtkToggleButton*)get_pref_widget("use_primary")))
823 {
824 /* Only allow synchronize option if both primary and clipboard are enabled */
825 gtk_widget_set_sensitive((GtkWidget*)get_pref_widget("synchronize"), TRUE);
826 }
827 else
828 {
829 /* Disable synchronize option */
830 gtk_toggle_button_set_active((GtkToggleButton*)get_pref_widget("synchronize"), FALSE);
831 gtk_widget_set_sensitive((GtkWidget*)get_pref_widget("synchronize"), FALSE);
832
833 }
834 }
835
search_toggled(GtkToggleButton * b,gpointer user)836 static void search_toggled(GtkToggleButton *b, gpointer user)
837 {
838 struct pref_item *u,*p;
839 u=get_pref_by_widget((GtkWidget *)user);
840 p=get_pref("case_search");
841
842 if(u == p){
843 if(TRUE == gtk_toggle_button_get_active((GtkToggleButton*)p->w) &&
844 FALSE == gtk_toggle_button_get_active((GtkToggleButton*)get_pref_widget("type_search")) )
845 gtk_toggle_button_set_active((GtkToggleButton*)get_pref_widget("type_search"), TRUE);
846 }else if( u == get_pref("type_search")){
847 if(FALSE == gtk_toggle_button_get_active((GtkToggleButton*)get_pref_widget("type_search")) &&
848 TRUE == gtk_toggle_button_get_active((GtkToggleButton*)p->w) )
849 gtk_toggle_button_set_active((GtkToggleButton*)p->w, FALSE);
850 }
851
852 }
853
854 /* Called when Add... button is clicked */
add_action(GtkButton * button,gpointer user_data)855 static void add_action(GtkButton *button, gpointer user_data)
856 {
857 /* Append new item */
858 GtkTreeIter row_iter;
859 gtk_list_store_append(actions_list, &row_iter);
860 /* Add a %s to the command */
861 gtk_list_store_set(actions_list, &row_iter, 1, "%s", -1);
862 /* Set the first column to editing */
863 GtkTreePath* row_path = gtk_tree_model_get_path((GtkTreeModel*)actions_list, &row_iter);
864 GtkTreeView* treeview = gtk_tree_selection_get_tree_view(actions_selection);
865 GtkTreeViewColumn* column = gtk_tree_view_get_column(treeview, 0);
866 gtk_tree_view_set_cursor(treeview, row_path, column, TRUE);
867 gtk_tree_path_free(row_path);
868 }
869
870 /* Called when Remove button is clicked */
remove_action(GtkButton * button,gpointer user_data)871 static void remove_action(GtkButton *button, gpointer user_data)
872 {
873 GtkTreeIter sel_iter;
874 /* Check if selected */
875 if (gtk_tree_selection_get_selected(actions_selection, NULL, &sel_iter))
876 {
877 /* Delete selected and select next */
878 GtkTreePath* tree_path = gtk_tree_model_get_path((GtkTreeModel*)actions_list, &sel_iter);
879 gtk_list_store_remove(actions_list, &sel_iter);
880 gtk_tree_selection_select_path(actions_selection, tree_path);
881 /* Select previous if the last row was deleted */
882 if (!gtk_tree_selection_path_is_selected(actions_selection, tree_path))
883 {
884 if (gtk_tree_path_prev(tree_path))
885 gtk_tree_selection_select_path(actions_selection, tree_path);
886 }
887 gtk_tree_path_free(tree_path);
888 }
889 }
890
891 /* Called when Up button is clicked */
move_action_up(GtkButton * button,gpointer user_data)892 static void move_action_up(GtkButton *button, gpointer user_data)
893 {
894 GtkTreeIter sel_iter;
895 /* Check if selected */
896 if (gtk_tree_selection_get_selected(actions_selection, NULL, &sel_iter))
897 {
898 /* Create path to previous row */
899 GtkTreePath* tree_path = gtk_tree_model_get_path((GtkTreeModel*)actions_list, &sel_iter);
900 /* Check if previous row exists */
901 if (gtk_tree_path_prev(tree_path))
902 {
903 /* Swap rows */
904 GtkTreeIter prev_iter;
905 gtk_tree_model_get_iter((GtkTreeModel*)actions_list, &prev_iter, tree_path);
906 gtk_list_store_swap(actions_list, &sel_iter, &prev_iter);
907 }
908 gtk_tree_path_free(tree_path);
909 }
910 }
911
912 /* Called when Down button is clicked */
move_action_down(GtkButton * button,gpointer user_data)913 static void move_action_down(GtkButton *button, gpointer user_data)
914 {
915 GtkTreeIter sel_iter;
916 /* Check if selected */
917 if (gtk_tree_selection_get_selected(actions_selection, NULL, &sel_iter))
918 {
919 /* Create iter to next row */
920 GtkTreeIter next_iter = sel_iter;
921 /* Check if next row exists */
922 if (gtk_tree_model_iter_next((GtkTreeModel*)actions_list, &next_iter))
923 /* Swap rows */
924 gtk_list_store_swap(actions_list, &sel_iter, &next_iter);
925 }
926 }
927
928 /* Called when delete key is pressed */
delete_key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer user_data)929 static void delete_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
930 {
931 /* Check if DEL key was pressed (keyval: 65535) */
932 if (event->keyval == 65535)
933 remove_action(NULL, NULL);
934 }
935
936 /* Called when a cell is edited */
edit_action(GtkCellRendererText * renderer,gchar * path,gchar * new_text,gpointer cell)937 static void edit_action(GtkCellRendererText *renderer, gchar *path, gchar *new_text, gpointer cell)
938 {
939 GtkTreeIter sel_iter;
940 /* Check if selected */
941 if (gtk_tree_selection_get_selected(actions_selection, NULL, &sel_iter))
942 {
943 /* Apply changes */
944 gtk_list_store_set(actions_list, &sel_iter, GPOINTER_TO_INT(cell), new_text, -1);
945 }
946 }
947
948 /***************************************************************************/
949 /** .
950 \n\b Arguments:
951 \n\b Returns:
952 ****************************************************************************/
update_pref_widgets(void)953 int update_pref_widgets( void)
954 {
955 int i,rtn=0;
956 for (i=0;NULL !=myprefs[i].desc; ++i){
957 if(NULL != myprefs[i].name){
958 switch (myprefs[i].type&PREF_TYPE_MASK){
959 case PREF_TYPE_TOGGLE:
960 gtk_toggle_button_set_active((GtkToggleButton*)myprefs[i].w, myprefs[i].val);
961 break;
962 case PREF_TYPE_SPIN:
963 gtk_spin_button_set_value((GtkSpinButton*)myprefs[i].w, (gdouble)myprefs[i].val);
964 break;
965 case PREF_TYPE_COMBO:
966 gtk_combo_box_set_active((GtkComboBox*)myprefs[i].w, myprefs[i].val - 1);
967 break;
968 case PREF_TYPE_ENTRY:
969 gtk_entry_set_text((GtkEntry*)myprefs[i].w, myprefs[i].cval);
970 break;
971 case PREF_TYPE_SPACER:
972 break;
973 default:
974 if(dbg)g_printf("apply_pref:don't know how to handle type %d\n",myprefs[i].type);
975 rtn=-1;
976 continue;
977 break;
978 }
979 if(dbg)g_printf("up:Set '%s' to %d (%s)\n",myprefs[i].name, myprefs[i].val, myprefs[i].cval);
980 }
981
982 }
983
984 return rtn;
985 }
986 /***************************************************************************/
987 /** .
988 \n\b Arguments:
989 section is the section to add, parent is the box to put it in.
990 \n\b Returns: -1 on error;
991 ****************************************************************************/
add_section(int sec,GtkWidget * parent)992 int add_section(int sec, GtkWidget *parent)
993 {
994 int i,rtn=0;
995 int single_st, single_is;
996 gint connect;
997 GtkWidget *hbox, *label, *child, *vbox, *alignment;
998 GtkWidget* packit;
999 vbox=parent;
1000 single_st=single_is=0;
1001 for (i=get_first_pref(sec);sec==myprefs[i].sec; ++i){
1002 connect=1;
1003 single_st=(myprefs[i].type&(PREF_TYPE_NMASK|PREF_TYPE_SINGLE_LINE)); /**deterimine if we are in single line */
1004
1005 if(single_st && !single_is){ /**start of single line */
1006 hbox = gtk_hbox_new(FALSE, 2); /**create hbox */
1007 /*g_printf("alloc %p hbox\n",hbox); */
1008 single_is=1;
1009 }
1010
1011 switch (myprefs[i].type&PREF_TYPE_MASK){
1012 case PREF_TYPE_FRAME:/**must be first in section, since it sets vbox. */
1013 myprefs[i].w= gtk_frame_new(NULL);
1014 gtk_frame_set_shadow_type((GtkFrame*) myprefs[i].w, GTK_SHADOW_NONE);
1015 label = gtk_label_new(NULL); /**<b>myprefs[i].desc */
1016 gtk_label_set_markup((GtkLabel*)label, _(myprefs[i].desc));
1017 gtk_frame_set_label_widget((GtkFrame*) myprefs[i].w, label);
1018 alignment = gtk_alignment_new(0.50, 0.50, 1.0, 1.0);
1019 gtk_alignment_set_padding((GtkAlignment*)alignment, 12, 0, 12, 0);
1020 gtk_container_add((GtkContainer*) myprefs[i].w, alignment);
1021 vbox = gtk_vbox_new(FALSE, 2);
1022 gtk_container_add((GtkContainer*)alignment, vbox);
1023 gtk_box_pack_start((GtkBox*)parent, myprefs[i].w,FALSE,FALSE,0);
1024 continue;
1025 break;
1026 case PREF_TYPE_TOGGLE:
1027 myprefs[i].w=gtk_check_button_new_with_mnemonic(_(myprefs[i].desc));
1028 packit=myprefs[i].w;
1029 break;
1030
1031 case PREF_TYPE_SPIN:
1032 if(!single_is)
1033 hbox = gtk_hbox_new(FALSE, 4);
1034 label = gtk_label_new(NULL);
1035 gtk_label_set_markup((GtkLabel*)label, _(myprefs[i].desc));
1036 gtk_box_pack_start((GtkBox*)hbox, label, FALSE, FALSE, 0);
1037 myprefs[i].w=gtk_spin_button_new((GtkAdjustment*)gtk_adjustment_new ( \
1038 myprefs[i].val,myprefs[i].adj->lower,myprefs[i].adj->upper,myprefs[i].adj->step,myprefs[i].adj->page,0 ),10,0);
1039 gtk_box_pack_start((GtkBox*)hbox, myprefs[i].w, FALSE, FALSE, 0);
1040 gtk_spin_button_set_update_policy((GtkSpinButton*)myprefs[i].w, GTK_UPDATE_IF_VALID);
1041 if(NULL != myprefs[i].tip) gtk_widget_set_tooltip_text(label, _(myprefs[i].tip));
1042 packit=hbox;
1043 break;
1044
1045 case PREF_TYPE_ENTRY:
1046 if(!single_is)
1047 hbox = gtk_hbox_new(TRUE, 4);
1048 label = gtk_label_new(NULL);
1049 gtk_label_set_markup((GtkLabel*)label, _(myprefs[i].desc));
1050 gtk_misc_set_alignment((GtkMisc*)label, 0.0, 0.50);
1051 gtk_box_pack_start((GtkBox*)hbox, label, TRUE, TRUE, 0);
1052 myprefs[i].w = gtk_entry_new();
1053 gtk_entry_set_width_chars((GtkEntry*)myprefs[i].w, 10);
1054 gtk_box_pack_end((GtkBox*)hbox,myprefs[i].w, TRUE, TRUE, 0);
1055 packit=hbox;
1056 break;
1057 case PREF_TYPE_COMBO: /**handled in show_preferences, only one so */
1058 continue;
1059 break;
1060 case PREF_TYPE_SPACER:
1061 packit=myprefs[i].w=gtk_menu_item_new_with_label("");
1062 child=gtk_bin_get_child((GtkBin *)packit);
1063 gtk_misc_set_padding((GtkMisc *)child,0,0);
1064 gtk_label_set_markup ((GtkLabel *)child, "<span size=\"0\"> </span>");
1065 break;
1066
1067 default:
1068 if(dbg)g_printf("add_sec:don't know how to handle type %d\n",myprefs[i].type);
1069 rtn=-1;
1070 continue;
1071 break;
1072 }
1073
1074 /**tooltips are set on the label of the spin box, not the widget and are handled above */
1075 if(PREF_TYPE_SPIN != myprefs[i].type && NULL != myprefs[i].tip)
1076 gtk_widget_set_tooltip_text(myprefs[i].w, _(myprefs[i].tip));
1077
1078 if(NULL != myprefs[i].sig && connect)
1079 g_signal_connect((GObject*)myprefs[i].w, myprefs[i].sig, (GCallback)myprefs[i].sfunc, myprefs[i].w);
1080
1081 if(dbg)g_printf("Packing %s\n",myprefs[i].name);
1082 if(single_is){
1083 if(packit != hbox){
1084 /*g_printf("Packed a slwidget %p<-%p\n",hbox, myprefs[i].w); */
1085 gtk_box_pack_start((GtkBox*)hbox,myprefs[i].w , FALSE, FALSE, 0);
1086 }
1087 /**else already packed above. */
1088 } else
1089 /**espand fill padding */
1090 gtk_box_pack_start((GtkBox*)vbox, packit, TRUE, TRUE, 0);
1091 /**check for end of single line. */
1092 single_st=(myprefs[i+1].type&(PREF_TYPE_NMASK|PREF_TYPE_SINGLE_LINE)); /**deterimine if we are in single line */
1093 if(single_is && !single_st){/**end of single line */
1094 /**exp fill padding */
1095 gtk_box_pack_start((GtkBox*)vbox, hbox, TRUE, TRUE, 0); /**pack the hbox into parent */
1096 /*g_printf("pack %p<-%p hbox\n",vbox,hbox); */
1097 single_is=0;
1098 }
1099 }
1100 if(dbg)g_printf("Ending on %d '%s'\n",i,myprefs[i].name);
1101 return rtn;
1102
1103 }
1104
1105 /* Shows the preferences dialog on the given tab */
show_preferences(gint tab)1106 void show_preferences(gint tab)
1107 {
1108 /* Declare some variables */
1109 GtkWidget *frame,*label,*alignment,*hbox, *vbox;
1110 struct pref_item *p;
1111 GtkTreeViewColumn *tree_column;
1112 init_pref();
1113
1114 /* Create the dialog */
1115 GtkWidget* dialog = gtk_dialog_new_with_buttons(_("Preferences"), NULL,
1116 (GTK_DIALOG_MODAL + GTK_DIALOG_NO_SEPARATOR),
1117 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1118 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
1119
1120 gtk_window_set_icon((GtkWindow*)dialog, gtk_widget_render_icon(dialog, GTK_STOCK_PREFERENCES, -1, NULL));
1121 gtk_window_set_resizable((GtkWindow*)dialog, FALSE);
1122
1123 /* Create notebook */
1124 GtkWidget* notebook = gtk_notebook_new();
1125 #if GTK_CHECK_VERSION (2,14,0)
1126 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area (GTK_DIALOG(dialog))), notebook, TRUE, TRUE, 2);
1127 #else
1128 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, TRUE, TRUE, 2);
1129 #endif
1130
1131 /* Build the behavior page */
1132 GtkWidget* page_behavior = gtk_alignment_new(0.50, 0.50, 1.0, 1.0);
1133 gtk_alignment_set_padding((GtkAlignment*)page_behavior, 12, 6, 12, 6);
1134 gtk_notebook_append_page((GtkNotebook*)notebook, page_behavior, gtk_label_new(_("Behavior")));
1135 GtkWidget* vbox_behavior = gtk_vbox_new(FALSE, 12);
1136 gtk_container_add((GtkContainer*)page_behavior, vbox_behavior);
1137
1138 /* Build the clipboards frame & copy section */
1139 add_section(PREF_SEC_CLIP,vbox_behavior);
1140 /* Build the history frame */
1141 add_section(PREF_SEC_HIST,vbox_behavior);
1142 /* Build the miscellaneous frame */
1143 add_section(PREF_SEC_MISC,vbox_behavior);
1144
1145 /* Build the display page */
1146 GtkWidget* page_display = gtk_alignment_new(0.50, 0.50, 1.0, 1.0);
1147 gtk_alignment_set_padding((GtkAlignment*)page_display, 12, 6, 12, 6);
1148 gtk_notebook_append_page((GtkNotebook*)notebook, page_display, gtk_label_new(_("Display")));
1149 GtkWidget* vbox_display = gtk_vbox_new(FALSE, 12);
1150 gtk_container_add((GtkContainer*)page_display, vbox_display);
1151
1152 /* Build the items frame */
1153 add_section(PREF_SEC_DISP,vbox_display);
1154
1155 /* Build the omitting frame */
1156 frame = gtk_frame_new(NULL);
1157 gtk_frame_set_shadow_type((GtkFrame*)frame, GTK_SHADOW_NONE);
1158 label = gtk_label_new(NULL);
1159 gtk_label_set_markup((GtkLabel*)label, _("<b>Omitting</b>"));
1160 gtk_frame_set_label_widget((GtkFrame*)frame, label);
1161 alignment = gtk_alignment_new(0.50, 0.50, 1.0, 1.0);
1162 gtk_alignment_set_padding((GtkAlignment*)alignment, 12, 0, 12, 0);
1163 gtk_container_add((GtkContainer*)frame, alignment);
1164 vbox = gtk_vbox_new(FALSE, 2);
1165 gtk_container_add((GtkContainer*)alignment, vbox);
1166
1167
1168 hbox = gtk_hbox_new(FALSE, 4);
1169 gtk_box_pack_start((GtkBox*)vbox, hbox, FALSE, FALSE, 0);
1170 p=get_pref("ellipsize");
1171 label = gtk_label_new(_(p->desc));
1172 gtk_misc_set_alignment((GtkMisc*)label, 0.0, 0.50);
1173 gtk_box_pack_start((GtkBox*)hbox, label, FALSE, FALSE, 0);
1174 p->w = gtk_combo_box_new_text();
1175 gtk_combo_box_append_text((GtkComboBox*)p->w, _("Beginning"));
1176 gtk_combo_box_append_text((GtkComboBox*)p->w, _("Middle"));
1177 gtk_combo_box_append_text((GtkComboBox*)p->w, _("End"));
1178 gtk_box_pack_start((GtkBox*)hbox, p->w, FALSE, FALSE, 0);
1179 gtk_box_pack_start((GtkBox*)vbox_display, frame, FALSE, FALSE, 0);
1180
1181 /* Build the misc Display frame */
1182 add_section(PREF_SEC_XMISC,vbox_display);
1183
1184 /* Build the actions page */
1185 GtkWidget* page_actions = gtk_alignment_new(0.50, 0.50, 1.0, 1.0);
1186 gtk_alignment_set_padding((GtkAlignment*)page_actions, 6, 6, 6, 6);
1187 gtk_notebook_append_page((GtkNotebook*)notebook, page_actions, gtk_label_new(_("Actions")));
1188 GtkWidget* vbox_actions = gtk_vbox_new(FALSE, 6);
1189 gtk_container_add((GtkContainer*)page_actions, vbox_actions);
1190
1191 /* Build the actions label */
1192 label = gtk_label_new(_("Control-click Parcellite's tray icon to use actions,"));
1193 gtk_widget_set_tooltip_text(label, _("Use %% if you want a literal %. See the man page for gnuC printf (man 2 printf) for more information."));
1194 gtk_label_set_line_wrap((GtkLabel*)label, TRUE);
1195 gtk_misc_set_alignment((GtkMisc*)label, 0.0, 0.50);
1196 gtk_box_pack_start((GtkBox*)vbox_actions, label, FALSE, FALSE, 0);
1197
1198 /* Build the actions treeview */
1199 GtkWidget* scrolled_window = gtk_scrolled_window_new(
1200 (GtkAdjustment*)gtk_adjustment_new(0, 0, 0, 0, 0, 0),
1201 (GtkAdjustment*)gtk_adjustment_new(0, 0, 0, 0, 0, 0));
1202
1203 gtk_scrolled_window_set_policy((GtkScrolledWindow*)scrolled_window, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1204 gtk_scrolled_window_set_shadow_type((GtkScrolledWindow*)scrolled_window, GTK_SHADOW_ETCHED_OUT);
1205 GtkWidget* treeview = gtk_tree_view_new();
1206 gtk_tree_view_set_reorderable((GtkTreeView*)treeview, TRUE);
1207 gtk_tree_view_set_rules_hint((GtkTreeView*)treeview, TRUE);
1208 actions_list = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
1209 gtk_tree_view_set_model((GtkTreeView*)treeview, (GtkTreeModel*)actions_list);
1210 GtkCellRenderer* name_renderer = gtk_cell_renderer_text_new();
1211 g_object_set(name_renderer, "editable", TRUE, NULL);
1212 g_signal_connect((GObject*)name_renderer, "edited", (GCallback)edit_action, (gpointer)0);
1213
1214 label=gtk_label_new(_("Action"));
1215 gtk_widget_set_tooltip_text(label,"This is the Action Name. \nDO NOT put commands here.");
1216 gtk_widget_show (label);
1217 tree_column = gtk_tree_view_column_new_with_attributes(_("Action"), name_renderer, "text", 0, NULL);
1218 gtk_tree_view_column_set_widget(tree_column,label);
1219
1220 gtk_tree_view_column_set_resizable(tree_column, TRUE);
1221 gtk_tree_view_append_column((GtkTreeView*)treeview, tree_column);
1222 GtkCellRenderer* command_renderer = gtk_cell_renderer_text_new();
1223 g_object_set(command_renderer, "editable", TRUE, NULL);
1224 g_object_set(command_renderer, "ellipsize-set", TRUE, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1225 g_signal_connect((GObject*)command_renderer, "edited", (GCallback)edit_action, (gpointer)1);
1226
1227 label=gtk_label_new(_("Command"));
1228 gtk_widget_set_tooltip_text(label,"Put the full commands here. ex echo \"parcellite gave me %s\">>$HOME/ptest");
1229 gtk_widget_show (label);
1230 tree_column = gtk_tree_view_column_new_with_attributes(_("Command"), command_renderer, "text", 1, NULL);
1231 gtk_tree_view_column_set_widget(tree_column,label);
1232 gtk_tree_view_column_set_expand(tree_column, TRUE);
1233 gtk_tree_view_append_column((GtkTreeView*)treeview, tree_column);
1234 gtk_container_add((GtkContainer*)scrolled_window, treeview);
1235 gtk_box_pack_start((GtkBox*)vbox_actions, scrolled_window, TRUE, TRUE, 0);
1236
1237 /* Edit selection and connect treeview related signals */
1238 actions_selection = gtk_tree_view_get_selection((GtkTreeView*)treeview);
1239 gtk_tree_selection_set_mode(actions_selection, GTK_SELECTION_BROWSE);
1240 g_signal_connect((GObject*)treeview, "key-press-event", (GCallback)delete_key_pressed, NULL);
1241
1242 /* Build the buttons */
1243 GtkWidget* hbbox = gtk_hbutton_box_new();
1244 gtk_box_set_spacing((GtkBox*)hbbox, 6);
1245 gtk_button_box_set_layout((GtkButtonBox*)hbbox, GTK_BUTTONBOX_START);
1246 GtkWidget* add_button = gtk_button_new_with_label(_("Add..."));
1247 gtk_button_set_image((GtkButton*)add_button, gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU));
1248 g_signal_connect((GObject*)add_button, "clicked", (GCallback)add_action, NULL);
1249 gtk_box_pack_start((GtkBox*)hbbox, add_button, FALSE, TRUE, 0);
1250 GtkWidget* remove_button = gtk_button_new_with_label(_("Remove"));
1251 gtk_button_set_image((GtkButton*)remove_button, gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
1252 g_signal_connect((GObject*)remove_button, "clicked", (GCallback)remove_action, NULL);
1253 gtk_box_pack_start((GtkBox*)hbbox, remove_button, FALSE, TRUE, 0);
1254 GtkWidget* up_button = gtk_button_new();
1255 gtk_button_set_image((GtkButton*)up_button, gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU));
1256 g_signal_connect((GObject*)up_button, "clicked", (GCallback)move_action_up, NULL);
1257 gtk_box_pack_start((GtkBox*)hbbox, up_button, FALSE, TRUE, 0);
1258 GtkWidget* down_button = gtk_button_new();
1259 gtk_button_set_image((GtkButton*)down_button, gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU));
1260 g_signal_connect((GObject*)down_button, "clicked", (GCallback)move_action_down, NULL);
1261 gtk_box_pack_start((GtkBox*)hbbox, down_button, FALSE, TRUE, 0);
1262 gtk_box_pack_start((GtkBox*)vbox_actions, hbbox, FALSE, FALSE, 0);
1263
1264 /* Build the hotkeys page */
1265 GtkWidget* page_extras = gtk_alignment_new(0.50, 0.50, 1.0, 1.0);
1266 gtk_alignment_set_padding((GtkAlignment*)page_extras, 12, 6, 12, 6);
1267 gtk_notebook_append_page((GtkNotebook*)notebook, page_extras, gtk_label_new(_("Hotkeys")));
1268 GtkWidget* vbox_extras = gtk_vbox_new(FALSE, 12);
1269 gtk_container_add((GtkContainer*)page_extras, vbox_extras);
1270
1271 /* Build the hotkeys frame */
1272 frame = gtk_frame_new(NULL);
1273 gtk_frame_set_shadow_type((GtkFrame*)frame, GTK_SHADOW_NONE);
1274 label = gtk_label_new(NULL);
1275 gtk_label_set_markup((GtkLabel*)label, _("<b>Hotkeys</b>"));
1276 gtk_frame_set_label_widget((GtkFrame*)frame, label);
1277 alignment = gtk_alignment_new(0.50, 0.50, 1.0, 1.0);
1278 gtk_alignment_set_padding((GtkAlignment*)alignment, 12, 0, 12, 0);
1279 gtk_container_add((GtkContainer*)frame, alignment);
1280 vbox = gtk_vbox_new(FALSE, 2);
1281 gtk_container_add((GtkContainer*)alignment, vbox);
1282 add_section(PREF_SEC_ACT,vbox);
1283 gtk_box_pack_start((GtkBox*)vbox_extras,frame,FALSE,FALSE,0);
1284
1285 /* Make widgets reflect current preferences */
1286 update_pref_widgets();
1287
1288 /* Read actions */
1289 read_actions();
1290
1291 /* Run the dialog */
1292 gtk_widget_show_all(dialog);
1293 gtk_notebook_set_current_page((GtkNotebook*)notebook, tab);
1294 if (gtk_dialog_run((GtkDialog*)dialog) == GTK_RESPONSE_ACCEPT)
1295 {
1296 /* Apply and save preferences */
1297 apply_preferences();
1298 save_preferences();
1299 save_actions();
1300 }
1301 gtk_widget_destroy(dialog);
1302 }
1303
1304