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