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