Simplify lxpanel_button_{set,update}_icon() usage.
[lxde/lxpanel.git] / src / misc.c
1 /**
2 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <X11/Xatom.h>
20 #include <X11/cursorfont.h>
21
22 #include <gtk/gtk.h>
23 #include <gdk/gdk.h>
24 #include <gdk/gdkx.h>
25 #include <ctype.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <stdarg.h>
30 #include <libfm/fm-gtk.h>
31
32 #include "misc.h"
33 #include "private.h"
34
35 #include "dbg.h"
36
37 /* data used by themed images buttons */
38 typedef struct {
39 FmIcon *icon;
40 guint theme_changed_handler;
41 guint icon_changed_handler;
42 GdkPixbuf* pixbuf;
43 GdkPixbuf* hilight;
44 gulong hicolor;
45 gint size; /* desired size */
46 LXPanel *panel;
47 char *fallback;
48 } ImgData;
49
50 static GQuark img_data_id = 0;
51
52 static void _gtk_image_set_from_file_scaled(GtkWidget *img, ImgData *data);
53
54 /* X11 data types */
55 Atom a_UTF8_STRING;
56 Atom a_XROOTPMAP_ID;
57
58 /* old WM spec */
59 Atom a_WM_STATE;
60 Atom a_WM_CLASS;
61 Atom a_WM_DELETE_WINDOW;
62 Atom a_WM_PROTOCOLS;
63
64 /* new NET spec */
65 Atom a_NET_WORKAREA;
66 Atom a_NET_CLIENT_LIST;
67 Atom a_NET_CLIENT_LIST_STACKING;
68 Atom a_NET_NUMBER_OF_DESKTOPS;
69 Atom a_NET_CURRENT_DESKTOP;
70 Atom a_NET_DESKTOP_VIEWPORT;
71 Atom a_NET_DESKTOP_NAMES;
72 Atom a_NET_ACTIVE_WINDOW;
73 Atom a_NET_CLOSE_WINDOW;
74 Atom a_NET_SHOWING_DESKTOP;
75 Atom a_NET_SUPPORTED;
76 Atom a_NET_WM_STATE;
77 Atom a_NET_WM_STATE_SKIP_TASKBAR;
78 Atom a_NET_WM_STATE_SKIP_PAGER;
79 Atom a_NET_WM_STATE_STICKY;
80 Atom a_NET_WM_STATE_HIDDEN;
81 Atom a_NET_WM_STATE_SHADED;
82 Atom a_NET_WM_WINDOW_TYPE;
83 Atom a_NET_WM_WINDOW_TYPE_DESKTOP;
84 Atom a_NET_WM_WINDOW_TYPE_DOCK;
85 Atom a_NET_WM_WINDOW_TYPE_TOOLBAR;
86 Atom a_NET_WM_WINDOW_TYPE_MENU;
87 Atom a_NET_WM_WINDOW_TYPE_UTILITY;
88 Atom a_NET_WM_WINDOW_TYPE_SPLASH;
89 Atom a_NET_WM_WINDOW_TYPE_DIALOG;
90 Atom a_NET_WM_WINDOW_TYPE_NORMAL;
91 Atom a_NET_WM_DESKTOP;
92 Atom a_NET_WM_PID;
93 Atom a_NET_WM_NAME;
94 Atom a_NET_WM_VISIBLE_NAME;
95 Atom a_NET_WM_STRUT;
96 Atom a_NET_WM_STRUT_PARTIAL;
97 Atom a_NET_WM_ICON;
98 Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
99
100 /* SYSTEM TRAY spec */
101 Atom a_NET_SYSTEM_TRAY_OPCODE;
102 Atom a_NET_SYSTEM_TRAY_MESSAGE_DATA;
103 Atom a_NET_SYSTEM_TRAY_ORIENTATION;
104 Atom a_MANAGER;
105
106 Atom a_LXPANEL_CMD; /* for private client message */
107
108 /* if current window manager is EWMH conforming. */
109 gboolean is_ewmh_supported;
110
111 enum{
112 I_UTF8_STRING,
113 I_XROOTPMAP_ID,
114 I_WM_STATE,
115 I_WM_CLASS,
116 I_WM_DELETE_WINDOW,
117 I_WM_PROTOCOLS,
118
119 I_NET_WORKAREA,
120 I_NET_CLIENT_LIST,
121 I_NET_CLIENT_LIST_STACKING,
122 I_NET_NUMBER_OF_DESKTOPS,
123 I_NET_CURRENT_DESKTOP,
124 I_NET_DESKTOP_VIEWPORT,
125 I_NET_DESKTOP_NAMES,
126 I_NET_ACTIVE_WINDOW,
127 I_NET_SHOWING_DESKTOP,
128 I_NET_SUPPORTED,
129
130 I_NET_WM_STATE,
131 I_NET_WM_STATE_SKIP_TASKBAR,
132 I_NET_WM_STATE_SKIP_PAGER,
133 I_NET_WM_STATE_STICKY,
134 I_NET_WM_STATE_HIDDEN,
135 I_NET_WM_STATE_SHADED,
136 I_NET_WM_WINDOW_TYPE,
137
138 I_NET_WM_WINDOW_TYPE_DESKTOP,
139 I_NET_WM_WINDOW_TYPE_DOCK,
140 I_NET_WM_WINDOW_TYPE_TOOLBAR,
141 I_NET_WM_WINDOW_TYPE_MENU,
142 I_NET_WM_WINDOW_TYPE_UTILITY,
143 I_NET_WM_WINDOW_TYPE_SPLASH,
144 I_NET_WM_WINDOW_TYPE_DIALOG,
145 I_NET_WM_WINDOW_TYPE_NORMAL,
146 I_NET_WM_DESKTOP,
147 I_NET_WM_PID,
148 I_NET_WM_NAME,
149 I_NET_WM_VISIBLE_NAME,
150 I_NET_WM_STRUT,
151 I_NET_WM_STRUT_PARTIAL,
152 I_NET_WM_ICON,
153 I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR,
154
155 I_NET_SYSTEM_TRAY_OPCODE,
156 I_NET_SYSTEM_TRAY_MESSAGE_DATA,
157 I_NET_SYSTEM_TRAY_ORIENTATION,
158 I_MANAGER,
159
160 I_LXPANEL_CMD,
161 N_ATOMS
162 };
163
164 pair allign_pair[] = {
165 { ALLIGN_NONE, "none" },
166 { ALLIGN_LEFT, "left" },
167 { ALLIGN_RIGHT, "right" },
168 { ALLIGN_CENTER, "center"},
169 { 0, NULL },
170 };
171
172 pair edge_pair[] = {
173 { EDGE_NONE, "none" },
174 { EDGE_LEFT, "left" },
175 { EDGE_RIGHT, "right" },
176 { EDGE_TOP, "top" },
177 { EDGE_BOTTOM, "bottom" },
178 { 0, NULL },
179 };
180
181 pair width_pair[] = {
182 { WIDTH_NONE, "none" },
183 { WIDTH_REQUEST, "request" },
184 { WIDTH_PIXEL, "pixel" },
185 { WIDTH_PERCENT, "percent" },
186 { 0, NULL },
187 };
188
189 pair height_pair[] = {
190 { HEIGHT_NONE, "none" },
191 { HEIGHT_PIXEL, "pixel" },
192 { 0, NULL },
193 };
194
195 pair bool_pair[] = {
196 { 0, "0" },
197 { 1, "1" },
198 { 0, NULL },
199 };
200
201 int
202 str2num(pair *p, const gchar *str, int defval)
203 {
204 ENTER;
205 for (;p && p->str; p++) {
206 if (!g_ascii_strcasecmp(str, p->str))
207 RET(p->num);
208 }
209 RET(defval);
210 }
211
212 const gchar *
213 num2str(pair *p, int num, const gchar *defval)
214 {
215 ENTER;
216 for (;p && p->str; p++) {
217 if (num == p->num)
218 RET(p->str);
219 }
220 RET(defval);
221 }
222
223 int buf_gets( char* buf, int len, char **fp )
224 {
225 char* p;
226 int i = 0;
227 if( !fp || !(p = *fp) || !**fp )
228 {
229 buf[0] = '\0';
230 return 0;
231 }
232
233 do
234 {
235 if( G_LIKELY( i < len ) )
236 {
237 buf[i] = *p;
238 ++i;
239 }
240 if( G_UNLIKELY(*p == '\n') )
241 {
242 ++p;
243 break;
244 }
245 }while( *(++p) );
246 buf[i] = '\0';
247 *fp = p;
248 return i;
249 }
250
251 extern int
252 lxpanel_put_line(FILE* fp, const char* format, ...)
253 {
254 static int indent = 0;
255 int i, ret;
256 va_list args;
257
258 if( strchr(format, '}') )
259 --indent;
260
261 for( i = 0; i < indent; ++i )
262 fputs( " ", fp );
263
264 va_start (args, format);
265 ret = vfprintf (fp, format, args);
266 va_end (args);
267
268 if( strchr(format, '{') )
269 ++indent;
270 fputc( '\n', fp ); /* add line break */
271 return (ret + 1);
272 }
273
274 extern int
275 lxpanel_get_line(char**fp, line *s)
276 {
277 gchar *tmp, *tmp2;
278
279 s->type = LINE_NONE;
280 if (!fp)
281 RET(s->type);
282 while (buf_gets(s->str, s->len, fp)) {
283
284 g_strstrip(s->str);
285
286 if (s->str[0] == '#' || s->str[0] == 0) {
287 continue;
288 }
289 if (!g_ascii_strcasecmp(s->str, "}")) {
290 s->type = LINE_BLOCK_END;
291 break;
292 }
293
294 s->t[0] = s->str;
295 for (tmp = s->str; isalnum(*tmp); tmp++);
296 for (tmp2 = tmp; isspace(*tmp2); tmp2++);
297 if (*tmp2 == '=') {
298 for (++tmp2; isspace(*tmp2); tmp2++);
299 s->t[1] = tmp2;
300 *tmp = 0;
301 s->type = LINE_VAR;
302 } else if (*tmp2 == '{') {
303 *tmp = 0;
304 s->type = LINE_BLOCK_START;
305 } else {
306 g_warning( "parser: unknown token: '%c'", *tmp2);
307 }
308 break;
309 }
310 return s->type;
311 }
312
313 void resolve_atoms()
314 {
315 static const char* atom_names[ N_ATOMS ];
316
317 atom_names[ I_UTF8_STRING ] = "UTF8_STRING";
318 atom_names[ I_XROOTPMAP_ID ] = "_XROOTPMAP_ID";
319 atom_names[ I_WM_STATE ] = "WM_STATE";
320 atom_names[ I_WM_CLASS ] = "WM_CLASS";
321 atom_names[ I_WM_DELETE_WINDOW ] = "WM_DELETE_WINDOW";
322 atom_names[ I_WM_PROTOCOLS ] = "WM_PROTOCOLS";
323 atom_names[ I_NET_WORKAREA ] = "_NET_WORKAREA";
324 atom_names[ I_NET_CLIENT_LIST ] = "_NET_CLIENT_LIST";
325 atom_names[ I_NET_CLIENT_LIST_STACKING ] = "_NET_CLIENT_LIST_STACKING";
326 atom_names[ I_NET_NUMBER_OF_DESKTOPS ] = "_NET_NUMBER_OF_DESKTOPS";
327 atom_names[ I_NET_CURRENT_DESKTOP ] = "_NET_CURRENT_DESKTOP";
328 atom_names[ I_NET_DESKTOP_VIEWPORT ] = "_NET_DESKTOP_VIEWPORT";
329 atom_names[ I_NET_DESKTOP_NAMES ] = "_NET_DESKTOP_NAMES";
330 atom_names[ I_NET_ACTIVE_WINDOW ] = "_NET_ACTIVE_WINDOW";
331 atom_names[ I_NET_SHOWING_DESKTOP ] = "_NET_SHOWING_DESKTOP";
332 atom_names[ I_NET_SUPPORTED ] = "_NET_SUPPORTED";
333 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
334 atom_names[ I_NET_WM_STATE ] = "_NET_WM_STATE";
335 atom_names[ I_NET_WM_STATE_SKIP_TASKBAR ] = "_NET_WM_STATE_SKIP_TASKBAR";
336 atom_names[ I_NET_WM_STATE_SKIP_PAGER ] = "_NET_WM_STATE_SKIP_PAGER";
337 atom_names[ I_NET_WM_STATE_STICKY ] = "_NET_WM_STATE_STICKY";
338 atom_names[ I_NET_WM_STATE_HIDDEN ] = "_NET_WM_STATE_HIDDEN";
339 atom_names[ I_NET_WM_STATE_SHADED ] = "_NET_WM_STATE_SHADED";
340 atom_names[ I_NET_WM_WINDOW_TYPE ] = "_NET_WM_WINDOW_TYPE";
341
342 atom_names[ I_NET_WM_WINDOW_TYPE_DESKTOP ] = "_NET_WM_WINDOW_TYPE_DESKTOP";
343 atom_names[ I_NET_WM_WINDOW_TYPE_DOCK ] = "_NET_WM_WINDOW_TYPE_DOCK";
344 atom_names[ I_NET_WM_WINDOW_TYPE_TOOLBAR ] = "_NET_WM_WINDOW_TYPE_TOOLBAR";
345 atom_names[ I_NET_WM_WINDOW_TYPE_MENU ] = "_NET_WM_WINDOW_TYPE_MENU";
346 atom_names[ I_NET_WM_WINDOW_TYPE_UTILITY ] = "_NET_WM_WINDOW_TYPE_UTILITY";
347 atom_names[ I_NET_WM_WINDOW_TYPE_SPLASH ] = "_NET_WM_WINDOW_TYPE_SPLASH";
348 atom_names[ I_NET_WM_WINDOW_TYPE_DIALOG ] = "_NET_WM_WINDOW_TYPE_DIALOG";
349 atom_names[ I_NET_WM_WINDOW_TYPE_NORMAL ] = "_NET_WM_WINDOW_TYPE_NORMAL";
350 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
351 atom_names[ I_NET_WM_PID ] = "_NET_WM_PID";
352 atom_names[ I_NET_WM_NAME ] = "_NET_WM_NAME";
353 atom_names[ I_NET_WM_VISIBLE_NAME ] = "_NET_WM_VISIBLE_NAME";
354 atom_names[ I_NET_WM_STRUT ] = "_NET_WM_STRUT";
355 atom_names[ I_NET_WM_STRUT_PARTIAL ] = "_NET_WM_STRUT_PARTIAL";
356 atom_names[ I_NET_WM_ICON ] = "_NET_WM_ICON";
357 atom_names[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ] = "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR";
358
359 atom_names[ I_NET_SYSTEM_TRAY_OPCODE ] = "_NET_SYSTEM_TRAY_OPCODE";
360 atom_names[ I_NET_SYSTEM_TRAY_MESSAGE_DATA ] = "_NET_SYSTEM_TRAY_MESSAGE_DATA";
361 atom_names[ I_NET_SYSTEM_TRAY_ORIENTATION ] = "_NET_SYSTEM_TRAY_ORIENTATION";
362 atom_names[ I_MANAGER ] = "MANAGER";
363
364 atom_names[ I_LXPANEL_CMD ] = "_LXPANEL_CMD";
365
366 Atom atoms[ N_ATOMS ];
367
368 ENTER;
369
370 if( ! XInternAtoms( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), (char**)atom_names,
371 N_ATOMS, False, atoms ) )
372 {
373 g_warning( "Error: unable to return Atoms" );
374 return;
375 }
376
377 a_UTF8_STRING = atoms[ I_UTF8_STRING ];
378 a_XROOTPMAP_ID = atoms[ I_XROOTPMAP_ID ];
379 a_WM_STATE = atoms[ I_WM_STATE ];
380 a_WM_CLASS = atoms[ I_WM_CLASS ];
381 a_WM_DELETE_WINDOW = atoms[ I_WM_DELETE_WINDOW ];
382 a_WM_PROTOCOLS = atoms[ I_WM_PROTOCOLS ];
383
384 a_NET_WORKAREA = atoms[ I_NET_WORKAREA ];
385 a_NET_CLIENT_LIST = atoms[ I_NET_CLIENT_LIST ];
386 a_NET_CLIENT_LIST_STACKING = atoms[ I_NET_CLIENT_LIST_STACKING ];
387 a_NET_NUMBER_OF_DESKTOPS = atoms[ I_NET_NUMBER_OF_DESKTOPS ];
388 a_NET_CURRENT_DESKTOP = atoms[ I_NET_CURRENT_DESKTOP ];
389 a_NET_DESKTOP_VIEWPORT = atoms[ I_NET_DESKTOP_VIEWPORT ];
390 a_NET_DESKTOP_NAMES = atoms[ I_NET_DESKTOP_NAMES ];
391 a_NET_ACTIVE_WINDOW = atoms[ I_NET_ACTIVE_WINDOW ];
392 a_NET_SHOWING_DESKTOP = atoms[ I_NET_SHOWING_DESKTOP ];
393 a_NET_SUPPORTED = atoms[ I_NET_SUPPORTED ];
394 a_NET_WM_STATE = atoms[ I_NET_WM_STATE ];
395 a_NET_WM_STATE_SKIP_TASKBAR = atoms[ I_NET_WM_STATE_SKIP_TASKBAR ];
396 a_NET_WM_STATE_SKIP_PAGER = atoms[ I_NET_WM_STATE_SKIP_PAGER ];
397 a_NET_WM_STATE_STICKY = atoms[ I_NET_WM_STATE_STICKY ];
398 a_NET_WM_STATE_HIDDEN = atoms[ I_NET_WM_STATE_HIDDEN ];
399 a_NET_WM_STATE_SHADED = atoms[ I_NET_WM_STATE_SHADED ];
400 a_NET_WM_WINDOW_TYPE = atoms[ I_NET_WM_WINDOW_TYPE ];
401
402 a_NET_WM_WINDOW_TYPE_DESKTOP = atoms[ I_NET_WM_WINDOW_TYPE_DESKTOP ];
403 a_NET_WM_WINDOW_TYPE_DOCK = atoms[ I_NET_WM_WINDOW_TYPE_DOCK ];
404 a_NET_WM_WINDOW_TYPE_TOOLBAR = atoms[ I_NET_WM_WINDOW_TYPE_TOOLBAR ];
405 a_NET_WM_WINDOW_TYPE_MENU = atoms[ I_NET_WM_WINDOW_TYPE_MENU ];
406 a_NET_WM_WINDOW_TYPE_UTILITY = atoms[ I_NET_WM_WINDOW_TYPE_UTILITY ];
407 a_NET_WM_WINDOW_TYPE_SPLASH = atoms[ I_NET_WM_WINDOW_TYPE_SPLASH ];
408 a_NET_WM_WINDOW_TYPE_DIALOG = atoms[ I_NET_WM_WINDOW_TYPE_DIALOG ];
409 a_NET_WM_WINDOW_TYPE_NORMAL = atoms[ I_NET_WM_WINDOW_TYPE_NORMAL ];
410 a_NET_WM_DESKTOP = atoms[ I_NET_WM_DESKTOP ];
411 a_NET_WM_PID = atoms[ I_NET_WM_PID ];
412 a_NET_WM_NAME = atoms[ I_NET_WM_NAME ];
413 a_NET_WM_VISIBLE_NAME = atoms[ I_NET_WM_VISIBLE_NAME ];
414 a_NET_WM_STRUT = atoms[ I_NET_WM_STRUT ];
415 a_NET_WM_STRUT_PARTIAL = atoms[ I_NET_WM_STRUT_PARTIAL ];
416 a_NET_WM_ICON = atoms[ I_NET_WM_ICON ];
417 a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR = atoms[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ];
418
419 a_NET_SYSTEM_TRAY_OPCODE = atoms[ I_NET_SYSTEM_TRAY_OPCODE ];
420 a_NET_SYSTEM_TRAY_MESSAGE_DATA = atoms [ I_NET_SYSTEM_TRAY_MESSAGE_DATA ];
421 a_NET_SYSTEM_TRAY_ORIENTATION = atoms[ I_NET_SYSTEM_TRAY_ORIENTATION ];
422 a_MANAGER = atoms[ I_MANAGER ];
423
424 a_LXPANEL_CMD = atoms[ I_LXPANEL_CMD ];
425
426 RET();
427 }
428
429
430 void
431 Xclimsg(Window win, Atom type, long l0, long l1, long l2, long l3, long l4)
432 {
433 XClientMessageEvent xev;
434 xev.type = ClientMessage;
435 xev.window = win;
436 xev.message_type = type;
437 xev.format = 32;
438 xev.data.l[0] = l0;
439 xev.data.l[1] = l1;
440 xev.data.l[2] = l2;
441 xev.data.l[3] = l3;
442 xev.data.l[4] = l4;
443 XSendEvent(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), GDK_ROOT_WINDOW(), False,
444 (SubstructureNotifyMask | SubstructureRedirectMask),
445 (XEvent *) &xev);
446 }
447
448 void
449 Xclimsgwm(Window win, Atom type, Atom arg)
450 {
451 XClientMessageEvent xev;
452
453 xev.type = ClientMessage;
454 xev.window = win;
455 xev.message_type = type;
456 xev.format = 32;
457 xev.data.l[0] = arg;
458 xev.data.l[1] = GDK_CURRENT_TIME;
459 XSendEvent(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, False, 0L, (XEvent *) &xev);
460 }
461
462
463 void *
464 get_utf8_property(Window win, Atom atom)
465 {
466 Atom type;
467 int format;
468 gulong nitems;
469 gulong bytes_after;
470 gchar *val, *retval;
471 int result;
472 guchar *tmp = NULL;
473
474 type = None;
475 retval = NULL;
476 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, atom, 0, G_MAXLONG, False,
477 a_UTF8_STRING, &type, &format, &nitems,
478 &bytes_after, &tmp);
479 if (result != Success || type == None)
480 return NULL;
481 val = (gchar *) tmp;
482 if (val) {
483 if (type == a_UTF8_STRING && format == 8 && nitems != 0)
484 retval = g_strndup (val, nitems);
485 XFree (val);
486 }
487 return retval;
488
489 }
490
491 char **
492 get_utf8_property_list(Window win, Atom atom, int *count)
493 {
494 Atom type;
495 int format;
496 gulong nitems, i;
497 gulong bytes_after;
498 gchar *s, **retval = NULL;
499 int result;
500 guchar *tmp = NULL;
501
502 *count = 0;
503 result = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, atom, 0, G_MAXLONG, False,
504 a_UTF8_STRING, &type, &format, &nitems,
505 &bytes_after, &tmp);
506 if (result != Success || type != a_UTF8_STRING || tmp == NULL)
507 return NULL;
508
509 if (nitems) {
510 gchar *val = (gchar *) tmp;
511 DBG("res=%d(%d) nitems=%d val=%s\n", result, Success, nitems, val);
512 for (i = 0; i < nitems; i++) {
513 if (!val[i])
514 (*count)++;
515 }
516 retval = g_new0 (char*, *count + 2);
517 for (i = 0, s = val; (int)i < *count; i++, s = s + strlen (s) + 1) {
518 retval[i] = g_strdup(s);
519 }
520 if (val[nitems-1]) {
521 result = nitems - (s - val);
522 DBG("val does not ends by 0, moving last %d bytes\n", result);
523 g_memmove(s - 1, s, result);
524 val[nitems-1] = 0;
525 DBG("s=%s\n", s -1);
526 retval[i] = g_strdup(s - 1);
527 (*count)++;
528 }
529 }
530 XFree (tmp);
531
532 return retval;
533
534 }
535
536 void *
537 get_xaproperty (Window win, Atom prop, Atom type, int *nitems)
538 {
539 Atom type_ret;
540 int format_ret;
541 unsigned long items_ret;
542 unsigned long after_ret;
543 unsigned char *prop_data;
544
545 ENTER;
546 prop_data = NULL;
547 if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, prop, 0, G_MAXLONG, False,
548 type, &type_ret, &format_ret, &items_ret,
549 &after_ret, &prop_data) != Success)
550 {
551 if( G_UNLIKELY(prop_data) )
552 XFree( prop_data );
553 if( nitems )
554 *nitems = 0;
555 RET(NULL);
556 }
557 if (nitems)
558 *nitems = items_ret;
559 RET(prop_data);
560 }
561
562 static char*
563 text_property_to_utf8 (const XTextProperty *prop)
564 {
565 char **list;
566 int count;
567 char *retval;
568
569 ENTER;
570 list = NULL;
571 count = gdk_text_property_to_utf8_list_for_display (gdk_display_get_default(),
572 gdk_x11_xatom_to_atom (prop->encoding),
573 prop->format,
574 prop->value,
575 prop->nitems,
576 &list);
577
578 DBG("count=%d\n", count);
579 if (count == 0)
580 return NULL;
581
582 retval = list[0];
583 list[0] = g_strdup (""); /* something to free */
584
585 g_strfreev (list);
586
587 RET(retval);
588 }
589
590 char *
591 get_textproperty(Window win, Atom atom)
592 {
593 XTextProperty text_prop;
594 char *retval;
595
596 ENTER;
597 if (XGetTextProperty(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, &text_prop, atom)) {
598 DBG("format=%d enc=%d nitems=%d value=%s \n",
599 text_prop.format,
600 text_prop.encoding,
601 text_prop.nitems,
602 text_prop.value);
603 retval = text_property_to_utf8 (&text_prop);
604 if (text_prop.nitems > 0)
605 XFree (text_prop.value);
606 RET(retval);
607
608 }
609 RET(NULL);
610 }
611
612
613 int
614 get_net_number_of_desktops()
615 {
616 int desknum;
617 gulong *data;
618
619 ENTER;
620 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_NUMBER_OF_DESKTOPS,
621 XA_CARDINAL, 0);
622 if (!data)
623 RET(0);
624
625 desknum = *data;
626 XFree (data);
627 RET(desknum);
628 }
629
630
631 int
632 get_net_current_desktop ()
633 {
634 int desk;
635 gulong *data;
636
637 ENTER;
638 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, XA_CARDINAL, 0);
639 if (!data)
640 RET(0);
641
642 desk = *data;
643 XFree (data);
644 RET(desk);
645 }
646
647 int
648 get_net_wm_desktop(Window win)
649 {
650 int desk = 0;
651 gulong *data;
652
653 ENTER;
654 data = get_xaproperty (win, a_NET_WM_DESKTOP, XA_CARDINAL, 0);
655 if (data) {
656 desk = *data;
657 XFree (data);
658 }
659 RET(desk);
660 }
661
662 GPid
663 get_net_wm_pid(Window win)
664 {
665 GPid pid = 0;
666 gulong *data;
667
668 ENTER;
669 data = get_xaproperty (win, a_NET_WM_PID, XA_CARDINAL, 0);
670 if (data) {
671 pid = *data;
672 XFree (data);
673 }
674 RET(pid);
675 }
676
677 void
678 get_net_wm_state(Window win, NetWMState *nws)
679 {
680 Atom *state;
681 int num3;
682
683
684 ENTER;
685 memset(nws, 0, sizeof(*nws));
686 if (!(state = get_xaproperty(win, a_NET_WM_STATE, XA_ATOM, &num3)))
687 RET();
688
689 DBG( "%x: netwm state = { ", (unsigned int)win);
690 while (--num3 >= 0) {
691 if (state[num3] == a_NET_WM_STATE_SKIP_PAGER) {
692 DBG("NET_WM_STATE_SKIP_PAGER ");
693 nws->skip_pager = 1;
694 } else if (state[num3] == a_NET_WM_STATE_SKIP_TASKBAR) {
695 DBG( "NET_WM_STATE_SKIP_TASKBAR ");
696 nws->skip_taskbar = 1;
697 } else if (state[num3] == a_NET_WM_STATE_STICKY) {
698 DBG( "NET_WM_STATE_STICKY ");
699 nws->sticky = 1;
700 } else if (state[num3] == a_NET_WM_STATE_HIDDEN) {
701 DBG( "NET_WM_STATE_HIDDEN ");
702 nws->hidden = 1;
703 } else if (state[num3] == a_NET_WM_STATE_SHADED) {
704 DBG( "NET_WM_STATE_SHADED ");
705 nws->shaded = 1;
706 } else {
707 DBG( "... ");
708 }
709 }
710 XFree(state);
711 DBG( "}\n");
712 RET();
713 }
714
715 void
716 get_net_wm_window_type(Window win, NetWMWindowType *nwwt)
717 {
718 Atom *state;
719 int num3;
720
721
722 ENTER;
723 memset(nwwt, 0, sizeof(*nwwt));
724 if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3)))
725 RET();
726
727 DBG( "%x: netwm state = { ", (unsigned int)win);
728 while (--num3 >= 0) {
729 if (state[num3] == a_NET_WM_WINDOW_TYPE_DESKTOP) {
730 DBG("NET_WM_WINDOW_TYPE_DESKTOP ");
731 nwwt->desktop = 1;
732 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DOCK) {
733 DBG( "NET_WM_WINDOW_TYPE_DOCK ");
734 nwwt->dock = 1;
735 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_TOOLBAR) {
736 DBG( "NET_WM_WINDOW_TYPE_TOOLBAR ");
737 nwwt->toolbar = 1;
738 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_MENU) {
739 DBG( "NET_WM_WINDOW_TYPE_MENU ");
740 nwwt->menu = 1;
741 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_UTILITY) {
742 DBG( "NET_WM_WINDOW_TYPE_UTILITY ");
743 nwwt->utility = 1;
744 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_SPLASH) {
745 DBG( "NET_WM_WINDOW_TYPE_SPLASH ");
746 nwwt->splash = 1;
747 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DIALOG) {
748 DBG( "NET_WM_WINDOW_TYPE_DIALOG ");
749 nwwt->dialog = 1;
750 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_NORMAL) {
751 DBG( "NET_WM_WINDOW_TYPE_NORMAL ");
752 nwwt->normal = 1;
753 } else {
754 DBG( "... ");
755 }
756 }
757 XFree(state);
758 DBG( "}\n");
759 RET();
760 }
761
762 int
763 get_wm_state (Window win)
764 {
765 unsigned long *data;
766 int ret = 0;
767
768 ENTER;
769 data = get_xaproperty (win, a_WM_STATE, a_WM_STATE, 0);
770 if (data) {
771 ret = data[0];
772 XFree (data);
773 }
774 RET(ret);
775 }
776
777 int panel_handle_x_error(Display * d, XErrorEvent * ev)
778 {
779 char buf[256];
780
781 XGetErrorText(d, ev->error_code, buf, 256);
782 g_warning("lxpanel : X error: %s", buf);
783 return 0; /* Ignored */
784 }
785
786 int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
787 {
788 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
789 panel_handle_x_error(d, ev);
790 return 0; /* Ignored */
791 }
792
793 static void
794 calculate_width(int scrw, int wtype, int allign, int margin,
795 int *panw, int *x)
796 {
797 ENTER;
798 DBG("scrw=%d\n", scrw);
799 DBG("IN panw=%d, margin=%d\n", *panw, margin);
800 //scrw -= 2;
801 if (wtype == WIDTH_PERCENT) {
802 /* sanity check */
803 if (*panw > 100)
804 *panw = 100;
805 else if (*panw < 0)
806 *panw = 1;
807 *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0;
808 }
809 if (allign != ALLIGN_CENTER) {
810 if (margin > scrw) {
811 g_warning( "margin is bigger then edge size %d > %d. Ignoring margin",
812 margin, scrw);
813 margin = 0;
814 }
815 *panw = MIN(scrw - margin, *panw);
816 }
817 DBG("OUT panw=%d\n", *panw);
818 if (allign == ALLIGN_LEFT)
819 *x += margin;
820 else if (allign == ALLIGN_RIGHT) {
821 *x += scrw - *panw - margin;
822 if (*x < 0)
823 *x = 0;
824 } else if (allign == ALLIGN_CENTER)
825 *x += (scrw - *panw) / 2;
826 RET();
827 }
828
829
830 void _calculate_position(LXPanel *panel, GdkRectangle *rect)
831 {
832 Panel *np = panel->priv;
833 GdkScreen *screen;
834 GdkRectangle marea;
835
836 ENTER;
837 /* FIXME: Why this doesn't work? */
838 /* if you are still going to use this, be sure to update it to take into
839 account multiple monitors */
840 if (0) {
841 // if (np->curdesk < np->wa_len/4) {
842 marea.x = np->workarea[np->curdesk*4 + 0];
843 marea.y = np->workarea[np->curdesk*4 + 1];
844 marea.width = np->workarea[np->curdesk*4 + 2];
845 marea.height = np->workarea[np->curdesk*4 + 3];
846 } else {
847 screen = gdk_screen_get_default();
848 if (np->monitor < 0) /* all monitors */
849 {
850 marea.x = 0;
851 marea.y = 0;
852 marea.width = gdk_screen_get_width(screen);
853 marea.height = gdk_screen_get_height(screen);
854 }
855 else if (np->monitor < gdk_screen_get_n_monitors(screen))
856 gdk_screen_get_monitor_geometry(screen,np->monitor,&marea);
857 else
858 {
859 marea.x = 0;
860 marea.y = 0;
861 marea.width = 0;
862 marea.height = 0;
863 }
864 }
865
866 if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
867 rect->width = np->width;
868 rect->x = marea.x;
869 calculate_width(marea.width, np->widthtype, np->allign, np->margin,
870 &rect->width, &rect->x);
871 rect->height = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
872 rect->y = marea.y + ((np->edge == EDGE_TOP) ? 0 : (marea.height - rect->height));
873
874 } else {
875 rect->height = np->width;
876 rect->y = marea.y;
877 calculate_width(marea.height, np->widthtype, np->allign, np->margin,
878 &rect->height, &rect->y);
879 rect->width = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
880 rect->x = marea.x + ((np->edge == EDGE_LEFT) ? 0 : (marea.width - rect->width));
881 }
882 //g_debug("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah);
883 RET();
884 }
885
886 void calculate_position(Panel *np)
887 {
888 GdkRectangle rect;
889
890 rect.width = np->aw;
891 rect.height = np->ah;
892 _calculate_position(np->topgwin, &rect);
893 np->aw = rect.width;
894 np->ah = rect.height;
895 np->ax = rect.x;
896 np->ay = rect.y;
897 }
898
899 gchar *
900 expand_tilda(const gchar *file)
901 {
902 ENTER;
903 RET((file[0] == '~') ?
904 g_strdup_printf("%s%s", getenv("HOME"), file+1)
905 : g_strdup(file));
906
907 }
908
909 /*
910 * SuxPanel version 0.1
911 * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
912 *
913 * This program may be distributed under the terms of GNU General
914 * Public License version 2. You should have received a copy of the
915 * license with this program; if not, please consult http://www.fsf.org/.
916 *
917 * This program comes with no warranty. Use at your own risk.
918 *
919 */
920
921 /* DestroyNotify handler for image data in _gtk_image_new_from_file_scaled. */
922 static void img_data_free(ImgData * data)
923 {
924 g_object_unref(data->icon);
925 if (data->theme_changed_handler != 0)
926 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
927 if (data->panel != NULL)
928 {
929 g_object_remove_weak_pointer(G_OBJECT(data->panel), (gpointer *)&data->panel);
930 g_signal_handler_disconnect(data->panel, data->icon_changed_handler);
931 }
932 if (data->pixbuf != NULL)
933 g_object_unref(data->pixbuf);
934 if (data->hilight != NULL)
935 g_object_unref(data->hilight);
936 if (data->fallback != NULL)
937 g_free(data->fallback);
938 g_free(data);
939 }
940
941 /* Handler for "changed" signal in _gtk_image_new_from_file_scaled. */
942 static void on_theme_changed(GtkWidget * img, GObject * object)
943 {
944 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
945 _gtk_image_set_from_file_scaled(img, data);
946 }
947
948 /* consumes reference on icon */
949 static void _lxpanel_button_set_icon(GtkWidget* btn, FmIcon* icon, gint size)
950 {
951 /* Locate the image within the button. */
952 GtkWidget * child = gtk_bin_get_child(GTK_BIN(btn));
953 GtkWidget * img = NULL;
954 if (GTK_IS_IMAGE(child))
955 img = child;
956 else if (GTK_IS_BOX(child))
957 {
958 GList * children = gtk_container_get_children(GTK_CONTAINER(child));
959 img = GTK_WIDGET(GTK_IMAGE(children->data));
960 g_list_free(children);
961 }
962
963 if (img != NULL)
964 {
965 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
966
967 if (size <= 0)
968 size = data->size;
969 if (icon != data->icon || size != data->size) /* something was changed */
970 {
971 g_object_unref(data->icon);
972 data->icon = icon;
973 data->size = size;
974 _gtk_image_set_from_file_scaled(img, data);
975 }
976 else
977 g_object_unref(icon);
978 }
979 else
980 g_object_unref(icon);
981 }
982
983 void lxpanel_button_set_icon(GtkWidget* btn, const gchar *name, gint size)
984 {
985 _lxpanel_button_set_icon(btn, fm_icon_from_name(name), size);
986 }
987
988 void lxpanel_button_update_icon(GtkWidget* btn, FmIcon *icon, gint size)
989 {
990 _lxpanel_button_set_icon(btn, g_object_ref(icon), size);
991 }
992
993 /* parameters width and keep_ratio are unused, kept for backward compatibility */
994 void fb_button_set_from_file(GtkWidget * btn, const char * img_file, gint width, gint height, gboolean keep_ratio)
995 {
996 lxpanel_button_set_icon(btn, img_file, height);
997 }
998
999 static void _gtk_image_set_from_file_scaled(GtkWidget * img, ImgData * data)
1000 {
1001 gint size = data->size;
1002
1003 if (size < 0 && data->panel)
1004 size = data->panel->priv->icon_size;
1005
1006 if (data->pixbuf != NULL)
1007 {
1008 g_object_unref(data->pixbuf);
1009 data->pixbuf = NULL;
1010 }
1011
1012 /* if there is a cached hilighted version of this pixbuf, free it */
1013 if (data->hilight != NULL)
1014 {
1015 g_object_unref(data->hilight);
1016 data->hilight = NULL;
1017 }
1018
1019 if (G_LIKELY(G_IS_THEMED_ICON(data->icon)))
1020 {
1021 const char *fallback = data->fallback;
1022
1023 if (fallback == NULL)
1024 fallback = "application-x-executable";
1025 data->pixbuf = fm_pixbuf_from_icon_with_fallback(data->icon, size, fallback);
1026 }
1027 else
1028 {
1029 char *file = g_icon_to_string(fm_icon_get_gicon(data->icon));
1030 data->pixbuf = gdk_pixbuf_new_from_file_at_scale(file, -1, size, TRUE, NULL);
1031 g_free(file);
1032 }
1033
1034 if (data->pixbuf == NULL && data->fallback != NULL && data->fallback[0] == '/')
1035 {
1036 /* if fallback was provided as a file path */
1037 data->pixbuf = gdk_pixbuf_new_from_file_at_scale(data->fallback, -1, size, TRUE, NULL);
1038 }
1039
1040 if (data->pixbuf != NULL)
1041 {
1042 /* Set the pixbuf into the image widget. */
1043 gtk_image_set_from_pixbuf((GtkImage *)img, data->pixbuf);
1044 }
1045 else
1046 {
1047 /* No pixbuf available. Set the "missing image" icon. */
1048 gtk_image_set_from_stock(GTK_IMAGE(img), GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON);
1049 }
1050 }
1051
1052 /* consumes reference on icon */
1053 static GtkWidget *_gtk_image_new_for_icon(LXPanel *p, FmIcon *icon, gint size,
1054 const char *fallback)
1055 {
1056 GtkWidget * img = gtk_image_new();
1057 ImgData * data = g_new0(ImgData, 1);
1058
1059 data->icon = icon;
1060 data->size = size;
1061 data->fallback = g_strdup(fallback);
1062 if (img_data_id == 0)
1063 img_data_id = g_quark_from_static_string("ImgData");
1064 g_object_set_qdata_full(G_OBJECT(img), img_data_id, data, (GDestroyNotify) img_data_free);
1065 if (p && size < 0)
1066 {
1067 data->panel = p;
1068 data->icon_changed_handler = g_signal_connect_swapped(p, "icon-size-changed",
1069 G_CALLBACK(on_theme_changed), img);
1070 /* it is in fact not required if image is panel child but let be safe */
1071 g_object_add_weak_pointer(G_OBJECT(p), (gpointer *)&data->panel);
1072 }
1073 _gtk_image_set_from_file_scaled(img, data);
1074 if (G_IS_THEMED_ICON(data->icon))
1075 {
1076 /* This image is loaded from icon theme. Update the image if the icon theme is changed. */
1077 data->theme_changed_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(),
1078 "changed", G_CALLBACK(on_theme_changed), img);
1079 }
1080 return img;
1081 }
1082
1083 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1084 GtkWidget * _gtk_image_new_from_file_scaled(const gchar * file, gint width, gint height, gboolean keep_ratio)
1085 {
1086 return _gtk_image_new_for_icon(NULL, fm_icon_from_name(file), height, NULL);
1087 }
1088
1089 GtkWidget *lxpanel_image_new_for_icon(LXPanel *panel, const gchar *name,
1090 gint height, const char *fallback)
1091 {
1092 return _gtk_image_new_for_icon(panel, fm_icon_from_name(name), height, fallback);
1093 }
1094
1095 gboolean lxpanel_image_change_icon(GtkWidget *img, const gchar *name, const char *fallback)
1096 {
1097 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
1098
1099 g_return_val_if_fail(data != NULL && name != NULL, FALSE);
1100 g_object_unref(data->icon);
1101 g_free(data->fallback);
1102 data->icon = fm_icon_from_name(name);
1103 data->fallback = g_strdup(fallback);
1104 if (!G_IS_THEMED_ICON(data->icon))
1105 {
1106 if (data->theme_changed_handler != 0)
1107 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
1108 data->theme_changed_handler = 0;
1109 }
1110 else if (data->theme_changed_handler == 0)
1111 {
1112 /* This image is loaded from icon theme. Update the image if the icon theme is changed. */
1113 data->theme_changed_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(),
1114 "changed", G_CALLBACK(on_theme_changed), img);
1115 }
1116 _gtk_image_set_from_file_scaled(img, data);
1117 return TRUE;
1118 }
1119
1120 void
1121 get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
1122 {
1123 GtkWidget *b;
1124
1125 ENTER;
1126 b = gtk_button_new();
1127 gtk_widget_set_name(GTK_WIDGET(b), name);
1128 gtk_widget_set_can_focus(b, FALSE);
1129 gtk_widget_set_can_default(b, FALSE);
1130 gtk_container_set_border_width (GTK_CONTAINER (b), 0);
1131
1132 if (parent)
1133 gtk_container_add(parent, b);
1134
1135 gtk_widget_show(b);
1136 gtk_widget_size_request(b, req);
1137
1138 gtk_widget_destroy(b);
1139 RET();
1140 }
1141
1142
1143 guint32 gcolor2rgb24(GdkColor *color)
1144 {
1145 guint32 i;
1146
1147 ENTER;
1148
1149 i = (color->red * 0xFF / 0xFFFF) & 0xFF;
1150 i <<= 8;
1151 i |= (color->green * 0xFF / 0xFFFF) & 0xFF;
1152 i <<= 8;
1153 i |= (color->blue * 0xFF / 0xFFFF) & 0xFF;
1154 DBG("i=%x\n", i);
1155 RET(i);
1156 }
1157
1158 /* Handler for "enter-notify-event" signal on image that has highlighting requested. */
1159 static gboolean fb_button_enter(GtkImage * widget, GdkEventCrossing * event)
1160 {
1161 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1162 {
1163 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1164 if (data != NULL)
1165 {
1166 if (data->hilight == NULL)
1167 {
1168 GdkPixbuf * dark = data->pixbuf;
1169 int height = gdk_pixbuf_get_height(dark);
1170 int rowstride = gdk_pixbuf_get_rowstride(dark);
1171 gulong hicolor = data->hicolor;
1172
1173 GdkPixbuf * light = gdk_pixbuf_add_alpha(dark, FALSE, 0, 0, 0);
1174 if (light != NULL)
1175 {
1176 guchar extra[3];
1177 int i;
1178 for (i = 2; i >= 0; i--, hicolor >>= 8)
1179 extra[i] = hicolor & 0xFF;
1180
1181 guchar * src = gdk_pixbuf_get_pixels(light);
1182 guchar * up;
1183 for (up = src + height * rowstride; src < up; src += 4)
1184 {
1185 if (src[3] != 0)
1186 {
1187 for (i = 0; i < 3; i++)
1188 {
1189 int value = src[i] + extra[i];
1190 if (value > 255) value = 255;
1191 src[i] = value;
1192 }
1193 }
1194 }
1195 data->hilight = light;
1196 }
1197 }
1198
1199 if (data->hilight != NULL)
1200 gtk_image_set_from_pixbuf(widget, data->hilight);
1201 }
1202 }
1203 return TRUE;
1204 }
1205
1206 /* Handler for "leave-notify-event" signal on image that has highlighting requested. */
1207 static gboolean fb_button_leave(GtkImage * widget, GdkEventCrossing * event, gpointer user_data)
1208 {
1209 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1210 {
1211 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1212 if ((data != NULL) && (data->pixbuf != NULL))
1213 gtk_image_set_from_pixbuf(widget, data->pixbuf);
1214 }
1215 return TRUE;
1216 }
1217
1218
1219 /* consumes reference on icon */
1220 static GtkWidget *_lxpanel_button_new_for_icon(LXPanel *panel, FmIcon *icon,
1221 gint size, gulong highlight_color,
1222 const gchar *label)
1223 {
1224 GtkWidget * event_box = gtk_event_box_new();
1225 gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
1226 gtk_widget_set_can_focus(event_box, FALSE);
1227
1228 GtkWidget * image = _gtk_image_new_for_icon(panel, icon, size, NULL);
1229 gtk_misc_set_padding(GTK_MISC(image), 0, 0);
1230 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
1231 if (highlight_color != 0)
1232 {
1233 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(image), img_data_id);
1234 data->hicolor = highlight_color;
1235
1236 gtk_widget_add_events(event_box, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1237 g_signal_connect_swapped(G_OBJECT(event_box), "enter-notify-event", G_CALLBACK(fb_button_enter), image);
1238 g_signal_connect_swapped(G_OBJECT(event_box), "leave-notify-event", G_CALLBACK(fb_button_leave), image);
1239 }
1240
1241 if (label == NULL)
1242 gtk_container_add(GTK_CONTAINER(event_box), image);
1243 else
1244 {
1245 GtkWidget * inner = gtk_hbox_new(FALSE, 0);
1246 gtk_container_set_border_width(GTK_CONTAINER(inner), 0);
1247 gtk_widget_set_can_focus(inner, FALSE);
1248 gtk_container_add(GTK_CONTAINER(event_box), inner);
1249
1250 gtk_box_pack_start(GTK_BOX(inner), image, FALSE, FALSE, 0);
1251
1252 GtkWidget * lbl = gtk_label_new("");
1253 lxpanel_draw_label_text(panel, lbl, label, FALSE, 1, TRUE);
1254 gtk_misc_set_padding(GTK_MISC(lbl), 2, 0);
1255 gtk_box_pack_end(GTK_BOX(inner), lbl, FALSE, FALSE, 0);
1256 }
1257
1258 gtk_widget_show_all(event_box);
1259 return event_box;
1260 }
1261
1262 GtkWidget *lxpanel_button_new_for_icon(LXPanel *panel, const gchar *name, GdkColor *color, const gchar *label)
1263 {
1264 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1265 return _lxpanel_button_new_for_icon(panel, fm_icon_from_name(name), -1,
1266 highlight_color, label);
1267 }
1268
1269 GtkWidget *lxpanel_button_new_for_fm_icon(LXPanel *panel, FmIcon *icon, GdkColor *color, const gchar *label)
1270 {
1271 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1272 return _lxpanel_button_new_for_icon(panel, g_object_ref(icon), -1,
1273 highlight_color, label);
1274 }
1275
1276 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1277 GtkWidget * fb_button_new_from_file(
1278 const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio)
1279 {
1280 return fb_button_new_from_file_with_label(image_file, width, height, highlight_color, keep_ratio, NULL, NULL);
1281 }
1282
1283 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1284 GtkWidget * fb_button_new_from_file_with_label(
1285 const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio, Panel * panel, const gchar * label)
1286 {
1287 return _lxpanel_button_new_for_icon(panel->topgwin, fm_icon_from_name(image_file), height, highlight_color, label);
1288 }
1289
1290 char* translate_exec_to_cmd( const char* exec, const char* icon,
1291 const char* title, const char* fpath )
1292 {
1293 GString* cmd = g_string_sized_new( 256 );
1294 if (!exec)
1295 return NULL;
1296 for( ; *exec; ++exec )
1297 {
1298 if( G_UNLIKELY(*exec == '%') )
1299 {
1300 ++exec;
1301 if( !*exec )
1302 break;
1303 switch( *exec )
1304 {
1305 case 'c':
1306 if( title )
1307 {
1308 g_string_append( cmd, title );
1309 }
1310 break;
1311 case 'i':
1312 if( icon )
1313 {
1314 g_string_append( cmd, "--icon " );
1315 g_string_append( cmd, icon );
1316 }
1317 break;
1318 case 'k':
1319 if( fpath )
1320 {
1321 char* uri = g_filename_to_uri( fpath, NULL, NULL );
1322 g_string_append( cmd, uri );
1323 g_free( uri );
1324 }
1325 break;
1326 case '%':
1327 g_string_append_c( cmd, '%' );
1328 break;
1329 }
1330 }
1331 else
1332 g_string_append_c( cmd, *exec );
1333 }
1334 return g_string_free( cmd, FALSE );
1335 }
1336
1337 /*
1338 This function is used to re-create a new box with different
1339 orientation from the old one, add all children of the old one to
1340 the new one, and then destroy the old box.
1341 It's mainly used when we need to change the orientation of the panel or
1342 any plugin with a layout box. Since GtkHBox cannot be changed to GtkVBox,
1343 recreating a new box to replace the old one is required.
1344 */
1345 /* for compatibility with old plugins */
1346 GtkWidget* recreate_box( GtkBox* oldbox, GtkOrientation orientation )
1347 {
1348 gtk_orientable_set_orientation(GTK_ORIENTABLE(oldbox), orientation);
1349 return GTK_WIDGET(oldbox);
1350 }
1351
1352 /* for compatibility with old plugins */
1353 void show_error( GtkWindow* parent_win, const char* msg )
1354 {
1355 fm_show_error(parent_win, NULL, msg);
1356 }
1357
1358 /* old plugins compatibility mode, use fm_pixbuf_from_icon_with_fallback() instead */
1359 GdkPixbuf * lxpanel_load_icon(const char * name, int width, int height, gboolean use_fallback)
1360 {
1361 FmIcon * fm_icon;
1362 GdkPixbuf * icon = NULL;
1363
1364 fm_icon = fm_icon_from_name(name ? name : "application-x-executable");
1365 /* well, we don't use parameter width and not really use cache here */
1366 icon = fm_pixbuf_from_icon_with_fallback(fm_icon, height,
1367 use_fallback ? "application-x-executable" : NULL);
1368 g_object_unref(fm_icon);
1369 return icon;
1370 }
1371
1372 /*
1373 * Taken from pcmanfm:
1374 * Parse Exec command line of app desktop file, and translate
1375 * it into a real command which can be passed to g_spawn_command_line_async().
1376 * file_list is a null-terminated file list containing full
1377 * paths of the files passed to app.
1378 * returned char* should be freed when no longer needed.
1379 */
1380 static char* translate_app_exec_to_command_line( const char* pexec,
1381 GList* file_list )
1382 {
1383 char* file;
1384 GList* l;
1385 gchar *tmp;
1386 GString* cmd = g_string_new("");
1387 gboolean add_files = FALSE;
1388
1389 for( ; *pexec; ++pexec )
1390 {
1391 if( *pexec == '%' )
1392 {
1393 ++pexec;
1394 switch( *pexec )
1395 {
1396 case 'U':
1397 for( l = file_list; l; l = l->next )
1398 {
1399 tmp = g_filename_to_uri( (char*)l->data, NULL, NULL );
1400 file = g_shell_quote( tmp );
1401 g_free( tmp );
1402 g_string_append( cmd, file );
1403 if (l->next)
1404 g_string_append_c( cmd, ' ' );
1405 g_free( file );
1406 }
1407 add_files = TRUE;
1408 break;
1409 case 'u':
1410 if( file_list && file_list->data )
1411 {
1412 file = (char*)file_list->data;
1413 tmp = g_filename_to_uri( file, NULL, NULL );
1414 file = g_shell_quote( tmp );
1415 g_free( tmp );
1416 g_string_append( cmd, file );
1417 g_free( file );
1418 add_files = TRUE;
1419 }
1420 break;
1421 case 'F':
1422 case 'N':
1423 for( l = file_list; l; l = l->next )
1424 {
1425 file = (char*)l->data;
1426 tmp = g_shell_quote( file );
1427 g_string_append( cmd, tmp );
1428 if (l->next)
1429 g_string_append_c( cmd, ' ' );
1430 g_free( tmp );
1431 }
1432 add_files = TRUE;
1433 break;
1434 case 'f':
1435 case 'n':
1436 if( file_list && file_list->data )
1437 {
1438 file = (char*)file_list->data;
1439 tmp = g_shell_quote( file );
1440 g_string_append( cmd, tmp );
1441 g_free( tmp );
1442 add_files = TRUE;
1443 }
1444 break;
1445 case 'D':
1446 for( l = file_list; l; l = l->next )
1447 {
1448 tmp = g_path_get_dirname( (char*)l->data );
1449 file = g_shell_quote( tmp );
1450 g_free( tmp );
1451 g_string_append( cmd, file );
1452 if (l->next)
1453 g_string_append_c( cmd, ' ' );
1454 g_free( file );
1455 }
1456 add_files = TRUE;
1457 break;
1458 case 'd':
1459 if( file_list && file_list->data )
1460 {
1461 tmp = g_path_get_dirname( (char*)file_list->data );
1462 file = g_shell_quote( tmp );
1463 g_free( tmp );
1464 g_string_append( cmd, file );
1465 g_free( tmp );
1466 add_files = TRUE;
1467 }
1468 break;
1469 case 'c':
1470 #if 0
1471 g_string_append( cmd, vfs_app_desktop_get_disp_name( app ) );
1472 #endif
1473 break;
1474 case 'i':
1475 /* Add icon name */
1476 #if 0
1477 if( vfs_app_desktop_get_icon_name( app ) )
1478 {
1479 g_string_append( cmd, "--icon " );
1480 g_string_append( cmd, vfs_app_desktop_get_icon_name( app ) );
1481 }
1482 #endif
1483 break;
1484 case 'k':
1485 /* Location of the desktop file */
1486 break;
1487 case 'v':
1488 /* Device name */
1489 break;
1490 case '%':
1491 g_string_append_c ( cmd, '%' );
1492 break;
1493 case '\0':
1494 goto _finish;
1495 break;
1496 }
1497 }
1498 else /* not % escaped part */
1499 {
1500 g_string_append_c ( cmd, *pexec );
1501 }
1502 }
1503 _finish:
1504 if( ! add_files )
1505 {
1506 for( l = file_list; l; l = l->next )
1507 {
1508 g_string_append_c( cmd, ' ' );
1509 file = (char*)l->data;
1510 tmp = g_shell_quote( file );
1511 g_string_append( cmd, tmp );
1512 g_free( tmp );
1513 }
1514 }
1515
1516 return g_string_free( cmd, FALSE );
1517 }
1518
1519 gboolean spawn_command_async(GtkWindow *parent_window, gchar const* workdir,
1520 gchar const* cmd)
1521 {
1522 GError* err = NULL;
1523 gchar** argv = NULL;
1524
1525 g_info("lxpanel: spawning \"%s\"...", cmd);
1526
1527 g_shell_parse_argv(cmd, NULL, &argv, &err);
1528 if (!err)
1529 g_spawn_async(workdir, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
1530
1531 if (err)
1532 {
1533 g_warning("%s\n", err->message);
1534 fm_show_error(parent_window, NULL, err->message);
1535 g_error_free(err);
1536 }
1537
1538 g_strfreev(argv);
1539
1540 return !err;
1541 }
1542
1543 /* FIXME: this should be replaced with fm_launch_file_simple() */
1544 gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal, char const* in_workdir)
1545 {
1546 GError *error = NULL;
1547 char* cmd;
1548 if( ! exec )
1549 return FALSE;
1550 cmd = translate_app_exec_to_command_line(exec, files);
1551 if( in_terminal )
1552 {
1553 char * escaped_cmd = g_shell_quote(cmd);
1554 char* term_cmd;
1555 const char* term = fm_config->terminal ? fm_config->terminal : "lxterminal";
1556 if( strstr(term, "%s") )
1557 term_cmd = g_strdup_printf(term, escaped_cmd);
1558 else
1559 term_cmd = g_strconcat( term, " -e ", escaped_cmd, NULL );
1560 g_free(escaped_cmd);
1561 if( cmd != exec )
1562 g_free(cmd);
1563 cmd = term_cmd;
1564 }
1565
1566 spawn_command_async(NULL, in_workdir, cmd);
1567
1568 g_free(cmd);
1569
1570 return (error == NULL);
1571 }
1572
1573 /* vim: set sw=4 et sts=4 : */