Merge branch 'master' of git.lxde.org:/lxde/lxpanel
[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 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 { ALLIGN_NONE, "none" },
167 { ALLIGN_LEFT, "left" },
168 { ALLIGN_RIGHT, "right" },
169 { ALLIGN_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)
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 allign, 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 (allign != ALLIGN_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 (allign == ALLIGN_LEFT)
820 *x += margin;
821 else if (allign == ALLIGN_RIGHT) {
822 *x += scrw - *panw - margin;
823 if (*x < 0)
824 *x = 0;
825 } else if (allign == ALLIGN_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 /* FIXME: Why this doesn't work? */
839 /* if you are still going to use this, be sure to update it to take into
840 account multiple monitors */
841 if (0) {
842 // if (np->curdesk < np->wa_len/4) {
843 marea.x = np->workarea[np->curdesk*4 + 0];
844 marea.y = np->workarea[np->curdesk*4 + 1];
845 marea.width = np->workarea[np->curdesk*4 + 2];
846 marea.height = np->workarea[np->curdesk*4 + 3];
847 } else {
848 screen = gdk_screen_get_default();
849 if (np->monitor < 0) /* all monitors */
850 {
851 marea.x = 0;
852 marea.y = 0;
853 marea.width = gdk_screen_get_width(screen);
854 marea.height = gdk_screen_get_height(screen);
855 }
856 else if (np->monitor < gdk_screen_get_n_monitors(screen))
857 gdk_screen_get_monitor_geometry(screen,np->monitor,&marea);
858 else
859 {
860 marea.x = 0;
861 marea.y = 0;
862 marea.width = 0;
863 marea.height = 0;
864 }
865 }
866
867 if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
868 rect->width = np->width;
869 rect->x = marea.x;
870 calculate_width(marea.width, np->widthtype, np->allign, np->margin,
871 &rect->width, &rect->x);
872 rect->height = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
873 rect->y = marea.y + ((np->edge == EDGE_TOP) ? 0 : (marea.height - rect->height));
874
875 } else {
876 rect->height = np->width;
877 rect->y = marea.y;
878 calculate_width(marea.height, np->widthtype, np->allign, np->margin,
879 &rect->height, &rect->y);
880 rect->width = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
881 rect->x = marea.x + ((np->edge == EDGE_LEFT) ? 0 : (marea.width - rect->width));
882 }
883 //g_debug("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah);
884 RET();
885 }
886
887 void calculate_position(Panel *np)
888 {
889 GdkRectangle rect;
890
891 rect.width = np->aw;
892 rect.height = np->ah;
893 _calculate_position(np->topgwin, &rect);
894 np->aw = rect.width;
895 np->ah = rect.height;
896 np->ax = rect.x;
897 np->ay = rect.y;
898 }
899
900 gchar *
901 expand_tilda(const gchar *file)
902 {
903 ENTER;
904 RET((file[0] == '~') ?
905 g_strdup_printf("%s%s", getenv("HOME"), file+1)
906 : g_strdup(file));
907
908 }
909
910 /*
911 * SuxPanel version 0.1
912 * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
913 *
914 * This program may be distributed under the terms of GNU General
915 * Public License version 2. You should have received a copy of the
916 * license with this program; if not, please consult http://www.fsf.org/.
917 *
918 * This program comes with no warranty. Use at your own risk.
919 *
920 */
921
922 /* DestroyNotify handler for image data in _gtk_image_new_from_file_scaled. */
923 static void img_data_free(ImgData * data)
924 {
925 g_object_unref(data->icon);
926 if (data->theme_changed_handler != 0)
927 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
928 if (data->panel != NULL)
929 {
930 g_object_remove_weak_pointer(G_OBJECT(data->panel), (gpointer *)&data->panel);
931 g_signal_handler_disconnect(data->panel, data->icon_changed_handler);
932 if (data->font_changed_handler != 0)
933 g_signal_handler_disconnect(data->panel, data->font_changed_handler);
934 }
935 if (data->pixbuf != NULL)
936 g_object_unref(data->pixbuf);
937 if (data->hilight != NULL)
938 g_object_unref(data->hilight);
939 if (data->fallback != NULL)
940 g_free(data->fallback);
941 g_free(data);
942 }
943
944 /* Handler for "changed" signal in _gtk_image_new_from_file_scaled. */
945 static void on_theme_changed(GtkWidget * img, GObject * object)
946 {
947 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
948 _gtk_image_set_from_file_scaled(img, data);
949 }
950
951 /* consumes reference on icon */
952 static void _lxpanel_button_set_icon(GtkWidget* btn, FmIcon* icon, gint size)
953 {
954 /* Locate the image within the button. */
955 GtkWidget * child = gtk_bin_get_child(GTK_BIN(btn));
956 GtkWidget * img = NULL;
957 if (GTK_IS_IMAGE(child))
958 img = child;
959 else if (GTK_IS_BOX(child))
960 {
961 GList * children = gtk_container_get_children(GTK_CONTAINER(child));
962 img = GTK_WIDGET(GTK_IMAGE(children->data));
963 g_list_free(children);
964 }
965
966 if (img != NULL)
967 {
968 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
969
970 if (size == 0) /* 0: set as before; -1: reset to panel icon size */
971 size = data->size;
972 if (icon != data->icon || size != data->size) /* something was changed */
973 {
974 g_object_unref(data->icon);
975 data->icon = icon;
976 data->size = size;
977 _gtk_image_set_from_file_scaled(img, data);
978 }
979 else
980 g_object_unref(icon);
981 }
982 else
983 g_object_unref(icon);
984 }
985
986 void lxpanel_button_set_icon(GtkWidget* btn, const gchar *name, gint size)
987 {
988 _lxpanel_button_set_icon(btn, fm_icon_from_name(name), size);
989 }
990
991 void lxpanel_button_update_icon(GtkWidget* btn, FmIcon *icon, gint size)
992 {
993 _lxpanel_button_set_icon(btn, g_object_ref(icon), size);
994 }
995
996 gboolean lxpanel_button_set_label(GtkWidget *btn, const char *label)
997 {
998 /* Locate the image within the button. */
999 GtkWidget * child = gtk_bin_get_child(GTK_BIN(btn));
1000 GtkWidget * lbl = NULL;
1001 GtkWidget * img = NULL;
1002 ImgData * data = NULL;
1003
1004 if (GTK_IS_BOX(child))
1005 {
1006 GList * children = gtk_container_get_children(GTK_CONTAINER(child)), *l;
1007 for (l = children; l; l = l->next)
1008 if (GTK_IS_LABEL(l->data))
1009 lbl = l->data;
1010 else if (GTK_IS_IMAGE(l->data))
1011 img = l->data;
1012 g_list_free(children);
1013 }
1014
1015 if (G_UNLIKELY(lbl == NULL))
1016 return FALSE;
1017
1018 if (G_LIKELY(img != NULL))
1019 data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
1020
1021 if (G_LIKELY(data != NULL && data->panel != NULL))
1022 lxpanel_draw_label_text(data->panel, lbl, label, FALSE, 1, TRUE);
1023 else
1024 gtk_label_set_text(GTK_LABEL(lbl), label);
1025 return TRUE;
1026 }
1027
1028 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1029 void fb_button_set_from_file(GtkWidget * btn, const char * img_file, gint width, gint height, gboolean keep_ratio)
1030 {
1031 lxpanel_button_set_icon(btn, img_file, height);
1032 }
1033
1034 static void _gtk_image_set_from_file_scaled(GtkWidget * img, ImgData * data)
1035 {
1036 gint size = data->size;
1037
1038 if (size < 0 && data->panel)
1039 size = data->panel->priv->icon_size;
1040
1041 if (data->pixbuf != NULL)
1042 {
1043 g_object_unref(data->pixbuf);
1044 data->pixbuf = NULL;
1045 }
1046
1047 /* if there is a cached hilighted version of this pixbuf, free it */
1048 if (data->hilight != NULL)
1049 {
1050 g_object_unref(data->hilight);
1051 data->hilight = NULL;
1052 }
1053
1054 if (G_LIKELY(G_IS_THEMED_ICON(data->icon)))
1055 {
1056 const char *fallback = data->fallback;
1057
1058 if (fallback == NULL)
1059 fallback = "application-x-executable";
1060 data->pixbuf = fm_pixbuf_from_icon_with_fallback(data->icon, size, fallback);
1061 }
1062 else
1063 {
1064 char *file = g_icon_to_string(fm_icon_get_gicon(data->icon));
1065 data->pixbuf = gdk_pixbuf_new_from_file_at_scale(file, -1, size, TRUE, NULL);
1066 g_free(file);
1067 }
1068
1069 if (data->pixbuf == NULL && data->fallback != NULL && data->fallback[0] == '/')
1070 {
1071 /* if fallback was provided as a file path */
1072 data->pixbuf = gdk_pixbuf_new_from_file_at_scale(data->fallback, -1, size, TRUE, NULL);
1073 }
1074
1075 if (data->pixbuf != NULL)
1076 {
1077 /* Set the pixbuf into the image widget. */
1078 gtk_image_set_from_pixbuf((GtkImage *)img, data->pixbuf);
1079 }
1080 else
1081 {
1082 /* No pixbuf available. Set the "missing image" icon. */
1083 gtk_image_set_from_stock(GTK_IMAGE(img), GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON);
1084 }
1085 }
1086
1087 /* consumes reference on icon */
1088 static GtkWidget *_gtk_image_new_for_icon(LXPanel *p, FmIcon *icon, gint size,
1089 const char *fallback)
1090 {
1091 GtkWidget * img = gtk_image_new();
1092 ImgData * data = g_new0(ImgData, 1);
1093
1094 data->icon = icon;
1095 data->size = size;
1096 data->fallback = g_strdup(fallback);
1097 if (img_data_id == 0)
1098 img_data_id = g_quark_from_static_string("ImgData");
1099 g_object_set_qdata_full(G_OBJECT(img), img_data_id, data, (GDestroyNotify) img_data_free);
1100 if (p && size < 0)
1101 {
1102 data->panel = p;
1103 data->icon_changed_handler = g_signal_connect_swapped(p, "icon-size-changed",
1104 G_CALLBACK(on_theme_changed), img);
1105 /* it is in fact not required if image is panel child but let be safe */
1106 g_object_add_weak_pointer(G_OBJECT(p), (gpointer *)&data->panel);
1107 }
1108 _gtk_image_set_from_file_scaled(img, data);
1109 if (G_IS_THEMED_ICON(data->icon))
1110 {
1111 /* This image is loaded from icon theme. Update the image if the icon theme is changed. */
1112 data->theme_changed_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(),
1113 "changed", G_CALLBACK(on_theme_changed), img);
1114 }
1115 return img;
1116 }
1117
1118 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1119 GtkWidget * _gtk_image_new_from_file_scaled(const gchar * file, gint width, gint height, gboolean keep_ratio)
1120 {
1121 return _gtk_image_new_for_icon(NULL, fm_icon_from_name(file), height, NULL);
1122 }
1123
1124 GtkWidget *lxpanel_image_new_for_icon(LXPanel *panel, const gchar *name,
1125 gint height, const char *fallback)
1126 {
1127 return _gtk_image_new_for_icon(panel, fm_icon_from_name(name), height, fallback);
1128 }
1129
1130 GtkWidget *lxpanel_image_new_for_fm_icon(LXPanel *panel, FmIcon *icon,
1131 gint height, const char *fallback)
1132 {
1133 return _gtk_image_new_for_icon(panel, g_object_ref(icon), height, fallback);
1134 }
1135
1136 gboolean lxpanel_image_change_icon(GtkWidget *img, const gchar *name, const char *fallback)
1137 {
1138 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
1139
1140 g_return_val_if_fail(data != NULL && name != NULL, FALSE);
1141 g_object_unref(data->icon);
1142 g_free(data->fallback);
1143 data->icon = fm_icon_from_name(name);
1144 data->fallback = g_strdup(fallback);
1145 if (!G_IS_THEMED_ICON(data->icon))
1146 {
1147 if (data->theme_changed_handler != 0)
1148 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
1149 data->theme_changed_handler = 0;
1150 }
1151 else if (data->theme_changed_handler == 0)
1152 {
1153 /* This image is loaded from icon theme. Update the image if the icon theme is changed. */
1154 data->theme_changed_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(),
1155 "changed", G_CALLBACK(on_theme_changed), img);
1156 }
1157 _gtk_image_set_from_file_scaled(img, data);
1158 return TRUE;
1159 }
1160
1161 void
1162 get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
1163 {
1164 GtkWidget *b;
1165
1166 ENTER;
1167 b = gtk_button_new();
1168 gtk_widget_set_name(GTK_WIDGET(b), name);
1169 gtk_widget_set_can_focus(b, FALSE);
1170 gtk_widget_set_can_default(b, FALSE);
1171 gtk_container_set_border_width (GTK_CONTAINER (b), 0);
1172
1173 if (parent)
1174 gtk_container_add(parent, b);
1175
1176 gtk_widget_show(b);
1177 gtk_widget_size_request(b, req);
1178
1179 gtk_widget_destroy(b);
1180 RET();
1181 }
1182
1183
1184 guint32 gcolor2rgb24(GdkColor *color)
1185 {
1186 guint32 i;
1187
1188 ENTER;
1189
1190 i = (color->red * 0xFF / 0xFFFF) & 0xFF;
1191 i <<= 8;
1192 i |= (color->green * 0xFF / 0xFFFF) & 0xFF;
1193 i <<= 8;
1194 i |= (color->blue * 0xFF / 0xFFFF) & 0xFF;
1195 DBG("i=%x\n", i);
1196 RET(i);
1197 }
1198
1199 /* Handler for "enter-notify-event" signal on image that has highlighting requested. */
1200 static gboolean fb_button_enter(GtkImage * widget, GdkEventCrossing * event)
1201 {
1202 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1203 {
1204 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1205 if (data != NULL)
1206 {
1207 if (data->hilight == NULL)
1208 {
1209 GdkPixbuf * dark = data->pixbuf;
1210 int height = gdk_pixbuf_get_height(dark);
1211 int rowstride = gdk_pixbuf_get_rowstride(dark);
1212 gulong hicolor = data->hicolor;
1213
1214 GdkPixbuf * light = gdk_pixbuf_add_alpha(dark, FALSE, 0, 0, 0);
1215 if (light != NULL)
1216 {
1217 guchar extra[3];
1218 int i;
1219 for (i = 2; i >= 0; i--, hicolor >>= 8)
1220 extra[i] = hicolor & 0xFF;
1221
1222 guchar * src = gdk_pixbuf_get_pixels(light);
1223 guchar * up;
1224 for (up = src + height * rowstride; src < up; src += 4)
1225 {
1226 if (src[3] != 0)
1227 {
1228 for (i = 0; i < 3; i++)
1229 {
1230 int value = src[i] + extra[i];
1231 if (value > 255) value = 255;
1232 src[i] = value;
1233 }
1234 }
1235 }
1236 data->hilight = light;
1237 }
1238 }
1239
1240 if (data->hilight != NULL)
1241 gtk_image_set_from_pixbuf(widget, data->hilight);
1242 }
1243 }
1244 return TRUE;
1245 }
1246
1247 /* Handler for "leave-notify-event" signal on image that has highlighting requested. */
1248 static gboolean fb_button_leave(GtkImage * widget, GdkEventCrossing * event, gpointer user_data)
1249 {
1250 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1251 {
1252 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1253 if ((data != NULL) && (data->pixbuf != NULL))
1254 gtk_image_set_from_pixbuf(widget, data->pixbuf);
1255 }
1256 return TRUE;
1257 }
1258
1259 static void on_font_changed(LXPanel * panel, GtkLabel * lbl)
1260 {
1261 const char *label = gtk_label_get_text(lbl);
1262 lxpanel_draw_label_text(panel, GTK_WIDGET(lbl), label, FALSE, 1, TRUE);
1263 }
1264
1265 static GtkWidget *_lxpanel_button_compose(GtkWidget *event_box, GtkWidget *image,
1266 gulong highlight_color, const gchar *label)
1267 {
1268 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(image), img_data_id);
1269
1270 gtk_misc_set_padding(GTK_MISC(image), 0, 0);
1271 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
1272 if (highlight_color != 0 && data != NULL)
1273 {
1274 data->hicolor = highlight_color;
1275 gtk_widget_add_events(event_box, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1276 g_signal_connect_swapped(G_OBJECT(event_box), "enter-notify-event", G_CALLBACK(fb_button_enter), image);
1277 g_signal_connect_swapped(G_OBJECT(event_box), "leave-notify-event", G_CALLBACK(fb_button_leave), image);
1278 }
1279
1280 if (label == NULL)
1281 gtk_container_add(GTK_CONTAINER(event_box), image);
1282 else
1283 {
1284 GtkWidget *inner, *lbl;
1285
1286 inner = gtk_hbox_new(FALSE, 0);
1287 gtk_container_set_border_width(GTK_CONTAINER(inner), 0);
1288 gtk_widget_set_can_focus(inner, FALSE);
1289 gtk_container_add(GTK_CONTAINER(event_box), inner);
1290
1291 gtk_box_pack_start(GTK_BOX(inner), image, FALSE, FALSE, 0);
1292
1293 lbl = gtk_label_new("");
1294 if (G_LIKELY(data != NULL && data->panel != NULL))
1295 {
1296 lxpanel_draw_label_text(data->panel, lbl, label, FALSE, 1, TRUE);
1297 data->font_changed_handler = g_signal_connect(data->panel,
1298 "panel-font-changed",
1299 G_CALLBACK(on_font_changed),
1300 lbl);
1301 }
1302 else
1303 gtk_label_set_text(GTK_LABEL(lbl), label);
1304 gtk_misc_set_padding(GTK_MISC(lbl), 2, 0);
1305 gtk_box_pack_end(GTK_BOX(inner), lbl, FALSE, FALSE, 0);
1306 }
1307
1308 gtk_widget_show_all(event_box);
1309 return event_box;
1310 }
1311
1312 GtkWidget *lxpanel_button_compose(GtkWidget *event_box, GtkWidget *image,
1313 GdkColor *color, const gchar *label)
1314 {
1315 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1316 return _lxpanel_button_compose(event_box, image, highlight_color, label);
1317 }
1318
1319 /* consumes reference on icon */
1320 static GtkWidget *_lxpanel_button_new_for_icon(LXPanel *panel, FmIcon *icon,
1321 gint size, gulong highlight_color,
1322 const gchar *label)
1323 {
1324 GtkWidget * event_box = gtk_event_box_new();
1325 gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
1326 gtk_widget_set_can_focus(event_box, FALSE);
1327
1328 GtkWidget * image = _gtk_image_new_for_icon(panel, icon, size, NULL);
1329 return _lxpanel_button_compose(event_box, image, highlight_color, label);
1330 }
1331
1332 GtkWidget *lxpanel_button_new_for_icon(LXPanel *panel, const gchar *name, GdkColor *color, const gchar *label)
1333 {
1334 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1335 return _lxpanel_button_new_for_icon(panel, fm_icon_from_name(name), -1,
1336 highlight_color, label);
1337 }
1338
1339 GtkWidget *lxpanel_button_new_for_fm_icon(LXPanel *panel, FmIcon *icon, GdkColor *color, const gchar *label)
1340 {
1341 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1342 return _lxpanel_button_new_for_icon(panel, g_object_ref(icon), -1,
1343 highlight_color, label);
1344 }
1345
1346 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1347 GtkWidget * fb_button_new_from_file(
1348 const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio)
1349 {
1350 return fb_button_new_from_file_with_label(image_file, width, height, highlight_color, keep_ratio, NULL, NULL);
1351 }
1352
1353 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1354 GtkWidget * fb_button_new_from_file_with_label(
1355 const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio, Panel * panel, const gchar * label)
1356 {
1357 return _lxpanel_button_new_for_icon(panel->topgwin, fm_icon_from_name(image_file), height, highlight_color, label);
1358 }
1359
1360 char* translate_exec_to_cmd( const char* exec, const char* icon,
1361 const char* title, const char* fpath )
1362 {
1363 GString* cmd = g_string_sized_new( 256 );
1364 if (!exec)
1365 return NULL;
1366 for( ; *exec; ++exec )
1367 {
1368 if( G_UNLIKELY(*exec == '%') )
1369 {
1370 ++exec;
1371 if( !*exec )
1372 break;
1373 switch( *exec )
1374 {
1375 case 'c':
1376 if( title )
1377 {
1378 g_string_append( cmd, title );
1379 }
1380 break;
1381 case 'i':
1382 if( icon )
1383 {
1384 g_string_append( cmd, "--icon " );
1385 g_string_append( cmd, icon );
1386 }
1387 break;
1388 case 'k':
1389 if( fpath )
1390 {
1391 char* uri = g_filename_to_uri( fpath, NULL, NULL );
1392 g_string_append( cmd, uri );
1393 g_free( uri );
1394 }
1395 break;
1396 case '%':
1397 g_string_append_c( cmd, '%' );
1398 break;
1399 }
1400 }
1401 else
1402 g_string_append_c( cmd, *exec );
1403 }
1404 return g_string_free( cmd, FALSE );
1405 }
1406
1407 /*
1408 This function is used to re-create a new box with different
1409 orientation from the old one, add all children of the old one to
1410 the new one, and then destroy the old box.
1411 It's mainly used when we need to change the orientation of the panel or
1412 any plugin with a layout box. Since GtkHBox cannot be changed to GtkVBox,
1413 recreating a new box to replace the old one is required.
1414 */
1415 /* for compatibility with old plugins */
1416 GtkWidget* recreate_box( GtkBox* oldbox, GtkOrientation orientation )
1417 {
1418 gtk_orientable_set_orientation(GTK_ORIENTABLE(oldbox), orientation);
1419 return GTK_WIDGET(oldbox);
1420 }
1421
1422 /* for compatibility with old plugins */
1423 void show_error( GtkWindow* parent_win, const char* msg )
1424 {
1425 fm_show_error(parent_win, NULL, msg);
1426 }
1427
1428 /* old plugins compatibility mode, use fm_pixbuf_from_icon_with_fallback() instead */
1429 GdkPixbuf * lxpanel_load_icon(const char * name, int width, int height, gboolean use_fallback)
1430 {
1431 FmIcon * fm_icon;
1432 GdkPixbuf * icon = NULL;
1433
1434 fm_icon = fm_icon_from_name(name ? name : "application-x-executable");
1435 /* well, we don't use parameter width and not really use cache here */
1436 icon = fm_pixbuf_from_icon_with_fallback(fm_icon, height,
1437 use_fallback ? "application-x-executable" : NULL);
1438 g_object_unref(fm_icon);
1439 return icon;
1440 }
1441
1442 /*
1443 * Taken from pcmanfm:
1444 * Parse Exec command line of app desktop file, and translate
1445 * it into a real command which can be passed to g_spawn_command_line_async().
1446 * file_list is a null-terminated file list containing full
1447 * paths of the files passed to app.
1448 * returned char* should be freed when no longer needed.
1449 */
1450 static char* translate_app_exec_to_command_line( const char* pexec,
1451 GList* file_list )
1452 {
1453 char* file;
1454 GList* l;
1455 gchar *tmp;
1456 GString* cmd = g_string_new("");
1457 gboolean add_files = FALSE;
1458
1459 for( ; *pexec; ++pexec )
1460 {
1461 if( *pexec == '%' )
1462 {
1463 ++pexec;
1464 switch( *pexec )
1465 {
1466 case 'U':
1467 for( l = file_list; l; l = l->next )
1468 {
1469 tmp = g_filename_to_uri( (char*)l->data, NULL, NULL );
1470 file = g_shell_quote( tmp );
1471 g_free( tmp );
1472 g_string_append( cmd, file );
1473 if (l->next)
1474 g_string_append_c( cmd, ' ' );
1475 g_free( file );
1476 }
1477 add_files = TRUE;
1478 break;
1479 case 'u':
1480 if( file_list && file_list->data )
1481 {
1482 file = (char*)file_list->data;
1483 tmp = g_filename_to_uri( file, NULL, NULL );
1484 file = g_shell_quote( tmp );
1485 g_free( tmp );
1486 g_string_append( cmd, file );
1487 g_free( file );
1488 add_files = TRUE;
1489 }
1490 break;
1491 case 'F':
1492 case 'N':
1493 for( l = file_list; l; l = l->next )
1494 {
1495 file = (char*)l->data;
1496 tmp = g_shell_quote( file );
1497 g_string_append( cmd, tmp );
1498 if (l->next)
1499 g_string_append_c( cmd, ' ' );
1500 g_free( tmp );
1501 }
1502 add_files = TRUE;
1503 break;
1504 case 'f':
1505 case 'n':
1506 if( file_list && file_list->data )
1507 {
1508 file = (char*)file_list->data;
1509 tmp = g_shell_quote( file );
1510 g_string_append( cmd, tmp );
1511 g_free( tmp );
1512 add_files = TRUE;
1513 }
1514 break;
1515 case 'D':
1516 for( l = file_list; l; l = l->next )
1517 {
1518 tmp = g_path_get_dirname( (char*)l->data );
1519 file = g_shell_quote( tmp );
1520 g_free( tmp );
1521 g_string_append( cmd, file );
1522 if (l->next)
1523 g_string_append_c( cmd, ' ' );
1524 g_free( file );
1525 }
1526 add_files = TRUE;
1527 break;
1528 case 'd':
1529 if( file_list && file_list->data )
1530 {
1531 tmp = g_path_get_dirname( (char*)file_list->data );
1532 file = g_shell_quote( tmp );
1533 g_free( tmp );
1534 g_string_append( cmd, file );
1535 g_free( tmp );
1536 add_files = TRUE;
1537 }
1538 break;
1539 case 'c':
1540 #if 0
1541 g_string_append( cmd, vfs_app_desktop_get_disp_name( app ) );
1542 #endif
1543 break;
1544 case 'i':
1545 /* Add icon name */
1546 #if 0
1547 if( vfs_app_desktop_get_icon_name( app ) )
1548 {
1549 g_string_append( cmd, "--icon " );
1550 g_string_append( cmd, vfs_app_desktop_get_icon_name( app ) );
1551 }
1552 #endif
1553 break;
1554 case 'k':
1555 /* Location of the desktop file */
1556 break;
1557 case 'v':
1558 /* Device name */
1559 break;
1560 case '%':
1561 g_string_append_c ( cmd, '%' );
1562 break;
1563 case '\0':
1564 goto _finish;
1565 break;
1566 }
1567 }
1568 else /* not % escaped part */
1569 {
1570 g_string_append_c ( cmd, *pexec );
1571 }
1572 }
1573 _finish:
1574 if( ! add_files )
1575 {
1576 for( l = file_list; l; l = l->next )
1577 {
1578 g_string_append_c( cmd, ' ' );
1579 file = (char*)l->data;
1580 tmp = g_shell_quote( file );
1581 g_string_append( cmd, tmp );
1582 g_free( tmp );
1583 }
1584 }
1585
1586 return g_string_free( cmd, FALSE );
1587 }
1588
1589 gboolean spawn_command_async(GtkWindow *parent_window, gchar const* workdir,
1590 gchar const* cmd)
1591 {
1592 GError* err = NULL;
1593 gchar** argv = NULL;
1594
1595 g_info("lxpanel: spawning \"%s\"...", cmd);
1596
1597 g_shell_parse_argv(cmd, NULL, &argv, &err);
1598 if (!err)
1599 g_spawn_async(workdir, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
1600
1601 if (err)
1602 {
1603 g_warning("%s\n", err->message);
1604 fm_show_error(parent_window, NULL, err->message);
1605 g_error_free(err);
1606 }
1607
1608 g_strfreev(argv);
1609
1610 return !err;
1611 }
1612
1613 /* FIXME: this should be replaced with fm_launch_file_simple() */
1614 gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal, char const* in_workdir)
1615 {
1616 GError *error = NULL;
1617 char* cmd;
1618 if( ! exec )
1619 return FALSE;
1620 cmd = translate_app_exec_to_command_line(exec, files);
1621 if( in_terminal )
1622 {
1623 char * escaped_cmd = g_shell_quote(cmd);
1624 char* term_cmd;
1625 const char* term = fm_config->terminal ? fm_config->terminal : "lxterminal";
1626 if( strstr(term, "%s") )
1627 term_cmd = g_strdup_printf(term, escaped_cmd);
1628 else
1629 term_cmd = g_strconcat( term, " -e ", escaped_cmd, NULL );
1630 g_free(escaped_cmd);
1631 if( cmd != exec )
1632 g_free(cmd);
1633 cmd = term_cmd;
1634 }
1635
1636 spawn_command_async(NULL, in_workdir, cmd);
1637
1638 g_free(cmd);
1639
1640 return (error == NULL);
1641 }
1642
1643 /* vim: set sw=4 et sts=4 : */