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