Merge branch 'use-libfm'
[lxde/lxpanel.git] / src / misc.c
1 /**
2 * Copyright (c) 2006 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 ERR( "parser: unknown token: '%c'\n", *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(), (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(), 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(), 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(), 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(), 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(), win, prop, 0, 0x7fffffff, 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 (gdk_x11_xatom_to_atom (prop->encoding),
570 prop->format,
571 prop->value,
572 prop->nitems,
573 &list);
574
575 DBG("count=%d\n", count);
576 if (count == 0)
577 return NULL;
578
579 retval = list[0];
580 list[0] = g_strdup (""); /* something to free */
581
582 g_strfreev (list);
583
584 RET(retval);
585 }
586
587 char *
588 get_textproperty(Window win, Atom atom)
589 {
590 XTextProperty text_prop;
591 char *retval;
592
593 ENTER;
594 if (XGetTextProperty(GDK_DISPLAY(), win, &text_prop, atom)) {
595 DBG("format=%d enc=%d nitems=%d value=%s \n",
596 text_prop.format,
597 text_prop.encoding,
598 text_prop.nitems,
599 text_prop.value);
600 retval = text_property_to_utf8 (&text_prop);
601 if (text_prop.nitems > 0)
602 XFree (text_prop.value);
603 RET(retval);
604
605 }
606 RET(NULL);
607 }
608
609
610 int
611 get_net_number_of_desktops()
612 {
613 int desknum;
614 guint32 *data;
615
616 ENTER;
617 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_NUMBER_OF_DESKTOPS,
618 XA_CARDINAL, 0);
619 if (!data)
620 RET(0);
621
622 desknum = *data;
623 XFree (data);
624 RET(desknum);
625 }
626
627
628 int
629 get_net_current_desktop ()
630 {
631 int desk;
632 guint32 *data;
633
634 ENTER;
635 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, XA_CARDINAL, 0);
636 if (!data)
637 RET(0);
638
639 desk = *data;
640 XFree (data);
641 RET(desk);
642 }
643
644 int
645 get_net_wm_desktop(Window win)
646 {
647 int desk = 0;
648 guint32 *data;
649
650 ENTER;
651 data = get_xaproperty (win, a_NET_WM_DESKTOP, XA_CARDINAL, 0);
652 if (data) {
653 desk = *data;
654 XFree (data);
655 }
656 RET(desk);
657 }
658
659 GPid
660 get_net_wm_pid(Window win)
661 {
662 GPid pid = 0;
663 guint32 *data;
664
665 ENTER;
666 data = get_xaproperty (win, a_NET_WM_PID, XA_CARDINAL, 0);
667 if (data) {
668 pid = *data;
669 XFree (data);
670 }
671 RET(pid);
672 }
673
674 void
675 get_net_wm_state(Window win, NetWMState *nws)
676 {
677 Atom *state;
678 int num3;
679
680
681 ENTER;
682 memset(nws, 0, sizeof(*nws));
683 if (!(state = get_xaproperty(win, a_NET_WM_STATE, XA_ATOM, &num3)))
684 RET();
685
686 DBG( "%x: netwm state = { ", (unsigned int)win);
687 while (--num3 >= 0) {
688 if (state[num3] == a_NET_WM_STATE_SKIP_PAGER) {
689 DBG("NET_WM_STATE_SKIP_PAGER ");
690 nws->skip_pager = 1;
691 } else if (state[num3] == a_NET_WM_STATE_SKIP_TASKBAR) {
692 DBG( "NET_WM_STATE_SKIP_TASKBAR ");
693 nws->skip_taskbar = 1;
694 } else if (state[num3] == a_NET_WM_STATE_STICKY) {
695 DBG( "NET_WM_STATE_STICKY ");
696 nws->sticky = 1;
697 } else if (state[num3] == a_NET_WM_STATE_HIDDEN) {
698 DBG( "NET_WM_STATE_HIDDEN ");
699 nws->hidden = 1;
700 } else if (state[num3] == a_NET_WM_STATE_SHADED) {
701 DBG( "NET_WM_STATE_SHADED ");
702 nws->shaded = 1;
703 } else {
704 DBG( "... ");
705 }
706 }
707 XFree(state);
708 DBG( "}\n");
709 RET();
710 }
711
712 void
713 get_net_wm_window_type(Window win, NetWMWindowType *nwwt)
714 {
715 Atom *state;
716 int num3;
717
718
719 ENTER;
720 memset(nwwt, 0, sizeof(*nwwt));
721 if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3)))
722 RET();
723
724 DBG( "%x: netwm state = { ", (unsigned int)win);
725 while (--num3 >= 0) {
726 if (state[num3] == a_NET_WM_WINDOW_TYPE_DESKTOP) {
727 DBG("NET_WM_WINDOW_TYPE_DESKTOP ");
728 nwwt->desktop = 1;
729 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DOCK) {
730 DBG( "NET_WM_WINDOW_TYPE_DOCK ");
731 nwwt->dock = 1;
732 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_TOOLBAR) {
733 DBG( "NET_WM_WINDOW_TYPE_TOOLBAR ");
734 nwwt->toolbar = 1;
735 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_MENU) {
736 DBG( "NET_WM_WINDOW_TYPE_MENU ");
737 nwwt->menu = 1;
738 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_UTILITY) {
739 DBG( "NET_WM_WINDOW_TYPE_UTILITY ");
740 nwwt->utility = 1;
741 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_SPLASH) {
742 DBG( "NET_WM_WINDOW_TYPE_SPLASH ");
743 nwwt->splash = 1;
744 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DIALOG) {
745 DBG( "NET_WM_WINDOW_TYPE_DIALOG ");
746 nwwt->dialog = 1;
747 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_NORMAL) {
748 DBG( "NET_WM_WINDOW_TYPE_NORMAL ");
749 nwwt->normal = 1;
750 } else {
751 DBG( "... ");
752 }
753 }
754 XFree(state);
755 DBG( "}\n");
756 RET();
757 }
758
759 int
760 get_wm_state (Window win)
761 {
762 unsigned long *data;
763 int ret = 0;
764
765 ENTER;
766 data = get_xaproperty (win, a_WM_STATE, a_WM_STATE, 0);
767 if (data) {
768 ret = data[0];
769 XFree (data);
770 }
771 RET(ret);
772 }
773
774 static void
775 calculate_width(int scrw, int wtype, int allign, int margin,
776 int *panw, int *x)
777 {
778 ENTER;
779 DBG("scrw=%d\n", scrw);
780 DBG("IN panw=%d, margin=%d\n", *panw, margin);
781 //scrw -= 2;
782 if (wtype == WIDTH_PERCENT) {
783 /* sanity check */
784 if (*panw > 100)
785 *panw = 100;
786 else if (*panw < 0)
787 *panw = 1;
788 *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0;
789 }
790 if (allign != ALLIGN_CENTER) {
791 if (margin > scrw) {
792 ERR( "margin is bigger then edge size %d > %d. Ignoring margin\n",
793 margin, scrw);
794 margin = 0;
795 }
796 *panw = MIN(scrw - margin, *panw);
797 }
798 DBG("OUT panw=%d\n", *panw);
799 if (allign == ALLIGN_LEFT)
800 *x += margin;
801 else if (allign == ALLIGN_RIGHT) {
802 *x += scrw - *panw - margin;
803 if (*x < 0)
804 *x = 0;
805 } else if (allign == ALLIGN_CENTER)
806 *x += (scrw - *panw) / 2;
807 RET();
808 }
809
810
811 void
812 calculate_position(Panel *np)
813 {
814 GdkScreen *screen;
815 GdkRectangle marea;
816
817 ENTER;
818 /* FIXME: Why this doesn't work? */
819 /* if you are still going to use this, be sure to update it to take into
820 account multiple monitors */
821 if (0) {
822 // if (np->curdesk < np->wa_len/4) {
823 marea.x = np->workarea[np->curdesk*4 + 0];
824 marea.y = np->workarea[np->curdesk*4 + 1];
825 marea.width = np->workarea[np->curdesk*4 + 2];
826 marea.height = np->workarea[np->curdesk*4 + 3];
827 } else {
828 screen = gtk_widget_get_screen(np->topgwin);
829 g_assert(np->monitor >= 0 && np->monitor < gdk_screen_get_n_monitors(screen));
830 gdk_screen_get_monitor_geometry(screen,np->monitor,&marea);
831 }
832
833 if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
834 np->aw = np->width;
835 np->ax = marea.x;
836 calculate_width(marea.width, np->widthtype, np->allign, np->margin,
837 &np->aw, &np->ax);
838 np->ah = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
839 np->ay = marea.y + ((np->edge == EDGE_TOP) ? 0 : (marea.height - np->ah));
840
841 } else {
842 np->ah = np->width;
843 np->ay = marea.y;
844 calculate_width(marea.height, np->widthtype, np->allign, np->margin,
845 &np->ah, &np->ay);
846 np->aw = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
847 np->ax = marea.x + ((np->edge == EDGE_LEFT) ? 0 : (marea.width - np->aw));
848 }
849 //g_debug("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah);
850 RET();
851 }
852
853 gchar *
854 expand_tilda(const gchar *file)
855 {
856 ENTER;
857 RET((file[0] == '~') ?
858 g_strdup_printf("%s%s", getenv("HOME"), file+1)
859 : g_strdup(file));
860
861 }
862
863 /*
864 * SuxPanel version 0.1
865 * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
866 *
867 * This program may be distributed under the terms of GNU General
868 * Public License version 2. You should have received a copy of the
869 * license with this program; if not, please consult http://www.fsf.org/.
870 *
871 * This program comes with no warranty. Use at your own risk.
872 *
873 */
874
875 /* DestroyNotify handler for image data in _gtk_image_new_from_file_scaled. */
876 static void img_data_free(ImgData * data)
877 {
878 g_object_unref(data->icon);
879 if (data->theme_changed_handler != 0)
880 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
881 if (data->pixbuf != NULL)
882 g_object_unref(data->pixbuf);
883 if (data->hilight != NULL)
884 g_object_unref(data->hilight);
885 g_free(data);
886 }
887
888 /* Handler for "changed" signal in _gtk_image_new_from_file_scaled. */
889 static void on_theme_changed(GtkIconTheme * theme, GtkWidget * img)
890 {
891 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
892 _gtk_image_set_from_file_scaled(img, data);
893 }
894
895 /* consumes reference on icon */
896 static void _lxpanel_button_set_icon(GtkWidget* btn, FmIcon* icon, gint size)
897 {
898 /* Locate the image within the button. */
899 GtkWidget * child = gtk_bin_get_child(GTK_BIN(btn));
900 GtkWidget * img = NULL;
901 if (GTK_IS_IMAGE(child))
902 img = child;
903 else if (GTK_IS_BOX(child))
904 {
905 GList * children = gtk_container_get_children(GTK_CONTAINER(child));
906 img = GTK_WIDGET(GTK_IMAGE(children->data));
907 g_list_free(children);
908 }
909
910 if (img != NULL)
911 {
912 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
913
914 if (icon != data->icon || size != data->size) /* something was changed */
915 {
916 g_object_unref(data->icon);
917 data->icon = icon;
918 data->size = size;
919 _gtk_image_set_from_file_scaled(img, data);
920 }
921 else
922 g_object_unref(icon);
923 }
924 else
925 g_object_unref(icon);
926 }
927
928 void lxpanel_button_set_icon(GtkWidget* btn, const gchar *name, gint size)
929 {
930 _lxpanel_button_set_icon(btn, fm_icon_from_name(name), size);
931 }
932
933 void lxpanel_button_update_icon(GtkWidget* btn, FmIcon *icon, gint size)
934 {
935 _lxpanel_button_set_icon(btn, g_object_ref(icon), size);
936 }
937
938 /* parameters width and keep_ratio are unused, kept for backward compatibility */
939 void fb_button_set_from_file(GtkWidget * btn, const char * img_file, gint width, gint height, gboolean keep_ratio)
940 {
941 lxpanel_button_set_icon(btn, img_file, height);
942 }
943
944 static void _gtk_image_set_from_file_scaled(GtkWidget * img, ImgData * data)
945 {
946 if (data->pixbuf != NULL)
947 {
948 g_object_unref(data->pixbuf);
949 data->pixbuf = NULL;
950 }
951
952 /* if there is a cached hilighted version of this pixbuf, free it */
953 if (data->hilight != NULL)
954 {
955 g_object_unref(data->hilight);
956 data->hilight = NULL;
957 }
958
959 if (G_LIKELY(G_IS_THEMED_ICON(data->icon)))
960 data->pixbuf = fm_pixbuf_from_icon_with_fallback(data->icon, data->size,
961 "application-x-executable");
962 else
963 {
964 char *file = g_icon_to_string(fm_icon_get_gicon(data->icon));
965 data->pixbuf = gdk_pixbuf_new_from_file_at_scale(file, -1, data->size, TRUE, NULL);
966 g_free(file);
967 }
968
969 if (data->pixbuf != NULL)
970 {
971 /* Set the pixbuf into the image widget. */
972 gtk_image_set_from_pixbuf((GtkImage *)img, data->pixbuf);
973 }
974 else
975 {
976 /* No pixbuf available. Set the "missing image" icon. */
977 gtk_image_set_from_stock(GTK_IMAGE(img), GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON);
978 }
979 }
980
981 /* consumes reference on icon */
982 static GtkWidget *_gtk_image_new_for_icon(FmIcon *icon, gint size)
983 {
984 GtkWidget * img = gtk_image_new();
985 ImgData * data = g_new0(ImgData, 1);
986
987 data->icon = icon;
988 data->size = size;
989 if (img_data_id == 0)
990 img_data_id = g_quark_from_static_string("ImgData");
991 g_object_set_qdata_full(G_OBJECT(img), img_data_id, data, (GDestroyNotify) img_data_free);
992 _gtk_image_set_from_file_scaled(img, data);
993 if (G_IS_THEMED_ICON(data->icon))
994 {
995 /* This image is loaded from icon theme. Update the image if the icon theme is changed. */
996 data->theme_changed_handler = g_signal_connect(gtk_icon_theme_get_default(), "changed", G_CALLBACK(on_theme_changed), img);
997 }
998 return img;
999 }
1000
1001 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1002 GtkWidget * _gtk_image_new_from_file_scaled(const gchar * file, gint width, gint height, gboolean keep_ratio)
1003 {
1004 return _gtk_image_new_for_icon(fm_icon_from_name(file), height);
1005 }
1006
1007 void
1008 get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
1009 {
1010 GtkWidget *b;
1011 //gint focus_width;
1012 //gint focus_pad;
1013
1014 ENTER;
1015 b = gtk_button_new();
1016 gtk_widget_set_name(GTK_WIDGET(b), name);
1017 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1018 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_DEFAULT);
1019 gtk_container_set_border_width (GTK_CONTAINER (b), 0);
1020
1021 if (parent)
1022 gtk_container_add(parent, b);
1023
1024 gtk_widget_show(b);
1025 gtk_widget_size_request(b, req);
1026
1027 gtk_widget_destroy(b);
1028 RET();
1029 }
1030
1031
1032 guint32 gcolor2rgb24(GdkColor *color)
1033 {
1034 guint32 i;
1035
1036 ENTER;
1037
1038 i = (color->red * 0xFF / 0xFFFF) & 0xFF;
1039 i <<= 8;
1040 i |= (color->green * 0xFF / 0xFFFF) & 0xFF;
1041 i <<= 8;
1042 i |= (color->blue * 0xFF / 0xFFFF) & 0xFF;
1043 DBG("i=%x\n", i);
1044 RET(i);
1045 }
1046
1047 /* Handler for "enter-notify-event" signal on image that has highlighting requested. */
1048 static gboolean fb_button_enter(GtkImage * widget, GdkEventCrossing * event)
1049 {
1050 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1051 {
1052 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1053 if (data != NULL)
1054 {
1055 if (data->hilight == NULL)
1056 {
1057 GdkPixbuf * dark = data->pixbuf;
1058 int height = gdk_pixbuf_get_height(dark);
1059 int rowstride = gdk_pixbuf_get_rowstride(dark);
1060 gulong hicolor = data->hicolor;
1061
1062 GdkPixbuf * light = gdk_pixbuf_add_alpha(dark, FALSE, 0, 0, 0);
1063 if (light != NULL)
1064 {
1065 guchar extra[3];
1066 int i;
1067 for (i = 2; i >= 0; i--, hicolor >>= 8)
1068 extra[i] = hicolor & 0xFF;
1069
1070 guchar * src = gdk_pixbuf_get_pixels(light);
1071 guchar * up;
1072 for (up = src + height * rowstride; src < up; src += 4)
1073 {
1074 if (src[3] != 0)
1075 {
1076 for (i = 0; i < 3; i++)
1077 {
1078 int value = src[i] + extra[i];
1079 if (value > 255) value = 255;
1080 src[i] = value;
1081 }
1082 }
1083 }
1084 data->hilight = light;
1085 }
1086 }
1087
1088 if (data->hilight != NULL)
1089 gtk_image_set_from_pixbuf(widget, data->hilight);
1090 }
1091 }
1092 return TRUE;
1093 }
1094
1095 /* Handler for "leave-notify-event" signal on image that has highlighting requested. */
1096 static gboolean fb_button_leave(GtkImage * widget, GdkEventCrossing * event, gpointer user_data)
1097 {
1098 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1099 {
1100 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1101 if ((data != NULL) && (data->pixbuf != NULL))
1102 gtk_image_set_from_pixbuf(widget, data->pixbuf);
1103 }
1104 return TRUE;
1105 }
1106
1107
1108 /* consumes reference on icon */
1109 static GtkWidget *_lxpanel_button_new_for_icon(Panel *panel, FmIcon *icon,
1110 gint size, gulong highlight_color,
1111 gchar *label)
1112 {
1113 GtkWidget * event_box = gtk_event_box_new();
1114 gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
1115 GTK_WIDGET_UNSET_FLAGS(event_box, GTK_CAN_FOCUS);
1116
1117 GtkWidget * image = _gtk_image_new_for_icon(icon, size);
1118 gtk_misc_set_padding(GTK_MISC(image), 0, 0);
1119 gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
1120 if (highlight_color != 0)
1121 {
1122 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(image), img_data_id);
1123 data->hicolor = highlight_color;
1124
1125 gtk_widget_add_events(event_box, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1126 g_signal_connect_swapped(G_OBJECT(event_box), "enter-notify-event", G_CALLBACK(fb_button_enter), image);
1127 g_signal_connect_swapped(G_OBJECT(event_box), "leave-notify-event", G_CALLBACK(fb_button_leave), image);
1128 }
1129
1130 if (label == NULL)
1131 gtk_container_add(GTK_CONTAINER(event_box), image);
1132 else
1133 {
1134 GtkWidget * inner = gtk_hbox_new(FALSE, 0);
1135 gtk_container_set_border_width(GTK_CONTAINER(inner), 0);
1136 GTK_WIDGET_UNSET_FLAGS (inner, GTK_CAN_FOCUS);
1137 gtk_container_add(GTK_CONTAINER(event_box), inner);
1138
1139 gtk_box_pack_start(GTK_BOX(inner), image, FALSE, FALSE, 0);
1140
1141 GtkWidget * lbl = gtk_label_new("");
1142 panel_draw_label_text(panel, lbl, label, FALSE, 1, TRUE);
1143 gtk_misc_set_padding(GTK_MISC(lbl), 2, 0);
1144 gtk_box_pack_end(GTK_BOX(inner), lbl, FALSE, FALSE, 0);
1145 }
1146
1147 gtk_widget_show_all(event_box);
1148 return event_box;
1149 }
1150
1151 GtkWidget *lxpanel_button_new_for_icon(Panel *panel, const gchar *name, GdkColor *color, gchar *label)
1152 {
1153 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1154 return _lxpanel_button_new_for_icon(panel, fm_icon_from_name(name),
1155 panel->icon_size, highlight_color, label);
1156 }
1157
1158 GtkWidget *lxpanel_button_new_for_fm_icon(Panel *panel, FmIcon *icon, GdkColor *color, gchar *label)
1159 {
1160 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1161 return _lxpanel_button_new_for_icon(panel, g_object_ref(icon),
1162 panel->icon_size, highlight_color, label);
1163 }
1164
1165 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1166 GtkWidget * fb_button_new_from_file(
1167 const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio)
1168 {
1169 return fb_button_new_from_file_with_label(image_file, width, height, highlight_color, keep_ratio, NULL, NULL);
1170 }
1171
1172 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1173 GtkWidget * fb_button_new_from_file_with_label(
1174 const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio, Panel * panel, gchar * label)
1175 {
1176 return _lxpanel_button_new_for_icon(panel, fm_icon_from_name(image_file), height, highlight_color, label);
1177 }
1178
1179 char* translate_exec_to_cmd( const char* exec, const char* icon,
1180 const char* title, const char* fpath )
1181 {
1182 GString* cmd = g_string_sized_new( 256 );
1183 if (!exec)
1184 return NULL;
1185 for( ; *exec; ++exec )
1186 {
1187 if( G_UNLIKELY(*exec == '%') )
1188 {
1189 ++exec;
1190 if( !*exec )
1191 break;
1192 switch( *exec )
1193 {
1194 case 'c':
1195 if( title )
1196 {
1197 g_string_append( cmd, title );
1198 }
1199 break;
1200 case 'i':
1201 if( icon )
1202 {
1203 g_string_append( cmd, "--icon " );
1204 g_string_append( cmd, icon );
1205 }
1206 break;
1207 case 'k':
1208 if( fpath )
1209 {
1210 char* uri = g_filename_to_uri( fpath, NULL, NULL );
1211 g_string_append( cmd, uri );
1212 g_free( uri );
1213 }
1214 break;
1215 case '%':
1216 g_string_append_c( cmd, '%' );
1217 break;
1218 }
1219 }
1220 else
1221 g_string_append_c( cmd, *exec );
1222 }
1223 return g_string_free( cmd, FALSE );
1224 }
1225
1226 /*
1227 This function is used to re-create a new box with different
1228 orientation from the old one, add all children of the old one to
1229 the new one, and then destroy the old box.
1230 It's mainly used when we need to change the orientation of the panel or
1231 any plugin with a layout box. Since GtkHBox cannot be changed to GtkVBox,
1232 recreating a new box to replace the old one is required.
1233 */
1234 /* for compatibility with old plugins */
1235 GtkWidget* recreate_box( GtkBox* oldbox, GtkOrientation orientation )
1236 {
1237 gtk_orientable_set_orientation(GTK_ORIENTABLE(oldbox), orientation);
1238 return GTK_WIDGET(oldbox);
1239 }
1240
1241 /* for compatibility with old plugins */
1242 void show_error( GtkWindow* parent_win, const char* msg )
1243 {
1244 fm_show_error(parent_win, NULL, msg);
1245 }
1246
1247 /* old plugins compatibility mode, use fm_pixbuf_from_icon_with_fallback() instead */
1248 GdkPixbuf * lxpanel_load_icon(const char * name, int width, int height, gboolean use_fallback)
1249 {
1250 FmIcon * fm_icon;
1251 GdkPixbuf * icon = NULL;
1252
1253 fm_icon = fm_icon_from_name(name ? name : "application-x-executable");
1254 /* well, we don't use parameter width and not really use cache here */
1255 icon = fm_pixbuf_from_icon_with_fallback(fm_icon, height,
1256 use_fallback ? "application-x-executable" : NULL);
1257 g_object_unref(fm_icon);
1258 return icon;
1259 }
1260
1261 /*
1262 * Taken from pcmanfm:
1263 * Parse Exec command line of app desktop file, and translate
1264 * it into a real command which can be passed to g_spawn_command_line_async().
1265 * file_list is a null-terminated file list containing full
1266 * paths of the files passed to app.
1267 * returned char* should be freed when no longer needed.
1268 */
1269 static char* translate_app_exec_to_command_line( const char* pexec,
1270 GList* file_list )
1271 {
1272 char* file;
1273 GList* l;
1274 gchar *tmp;
1275 GString* cmd = g_string_new("");
1276 gboolean add_files = FALSE;
1277
1278 for( ; *pexec; ++pexec )
1279 {
1280 if( *pexec == '%' )
1281 {
1282 ++pexec;
1283 switch( *pexec )
1284 {
1285 case 'U':
1286 for( l = file_list; l; l = l->next )
1287 {
1288 tmp = g_filename_to_uri( (char*)l->data, NULL, NULL );
1289 file = g_shell_quote( tmp );
1290 g_free( tmp );
1291 g_string_append( cmd, file );
1292 if (l->next)
1293 g_string_append_c( cmd, ' ' );
1294 g_free( file );
1295 }
1296 add_files = TRUE;
1297 break;
1298 case 'u':
1299 if( file_list && file_list->data )
1300 {
1301 file = (char*)file_list->data;
1302 tmp = g_filename_to_uri( file, NULL, NULL );
1303 file = g_shell_quote( tmp );
1304 g_free( tmp );
1305 g_string_append( cmd, file );
1306 g_free( file );
1307 add_files = TRUE;
1308 }
1309 break;
1310 case 'F':
1311 case 'N':
1312 for( l = file_list; l; l = l->next )
1313 {
1314 file = (char*)l->data;
1315 tmp = g_shell_quote( file );
1316 g_string_append( cmd, tmp );
1317 if (l->next)
1318 g_string_append_c( cmd, ' ' );
1319 g_free( tmp );
1320 }
1321 add_files = TRUE;
1322 break;
1323 case 'f':
1324 case 'n':
1325 if( file_list && file_list->data )
1326 {
1327 file = (char*)file_list->data;
1328 tmp = g_shell_quote( file );
1329 g_string_append( cmd, tmp );
1330 g_free( tmp );
1331 add_files = TRUE;
1332 }
1333 break;
1334 case 'D':
1335 for( l = file_list; l; l = l->next )
1336 {
1337 tmp = g_path_get_dirname( (char*)l->data );
1338 file = g_shell_quote( tmp );
1339 g_free( tmp );
1340 g_string_append( cmd, file );
1341 if (l->next)
1342 g_string_append_c( cmd, ' ' );
1343 g_free( file );
1344 }
1345 add_files = TRUE;
1346 break;
1347 case 'd':
1348 if( file_list && file_list->data )
1349 {
1350 tmp = g_path_get_dirname( (char*)file_list->data );
1351 file = g_shell_quote( tmp );
1352 g_free( tmp );
1353 g_string_append( cmd, file );
1354 g_free( tmp );
1355 add_files = TRUE;
1356 }
1357 break;
1358 case 'c':
1359 #if 0
1360 g_string_append( cmd, vfs_app_desktop_get_disp_name( app ) );
1361 #endif
1362 break;
1363 case 'i':
1364 /* Add icon name */
1365 #if 0
1366 if( vfs_app_desktop_get_icon_name( app ) )
1367 {
1368 g_string_append( cmd, "--icon " );
1369 g_string_append( cmd, vfs_app_desktop_get_icon_name( app ) );
1370 }
1371 #endif
1372 break;
1373 case 'k':
1374 /* Location of the desktop file */
1375 break;
1376 case 'v':
1377 /* Device name */
1378 break;
1379 case '%':
1380 g_string_append_c ( cmd, '%' );
1381 break;
1382 case '\0':
1383 goto _finish;
1384 break;
1385 }
1386 }
1387 else /* not % escaped part */
1388 {
1389 g_string_append_c ( cmd, *pexec );
1390 }
1391 }
1392 _finish:
1393 if( ! add_files )
1394 {
1395 for( l = file_list; l; l = l->next )
1396 {
1397 g_string_append_c( cmd, ' ' );
1398 file = (char*)l->data;
1399 tmp = g_shell_quote( file );
1400 g_string_append( cmd, tmp );
1401 g_free( tmp );
1402 }
1403 }
1404
1405 return g_string_free( cmd, FALSE );
1406 }
1407
1408 gboolean spawn_command_async(GtkWindow *parent_window, gchar const* workdir,
1409 gchar const* cmd)
1410 {
1411 GError* err = NULL;
1412 gchar** argv = NULL;
1413
1414 LOG(LOG_INFO, "lxpanel: spawning \"%s\"...\n", cmd);
1415
1416 g_shell_parse_argv(cmd, NULL, &argv, &err);
1417 if (!err)
1418 g_spawn_async(workdir, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
1419
1420 if (err)
1421 {
1422 ERR("%s\n", err->message);
1423 fm_show_error(parent_window, NULL, err->message);
1424 g_error_free(err);
1425 }
1426
1427 g_strfreev(argv);
1428
1429 return !err;
1430 }
1431
1432 /* FIXME: this should be replaced with fm_launch_file_simple() */
1433 gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal, char const* in_workdir)
1434 {
1435 GError *error = NULL;
1436 char* cmd;
1437 if( ! exec )
1438 return FALSE;
1439 cmd = translate_app_exec_to_command_line(exec, files);
1440 if( in_terminal )
1441 {
1442 char * escaped_cmd = g_shell_quote(cmd);
1443 char* term_cmd;
1444 const char* term = fm_config->terminal ? fm_config->terminal : "lxterminal";
1445 if( strstr(term, "%s") )
1446 term_cmd = g_strdup_printf(term, escaped_cmd);
1447 else
1448 term_cmd = g_strconcat( term, " -e ", escaped_cmd, NULL );
1449 g_free(escaped_cmd);
1450 if( cmd != exec )
1451 g_free(cmd);
1452 cmd = term_cmd;
1453 }
1454
1455 spawn_command_async(NULL, in_workdir, cmd);
1456
1457 g_free(cmd);
1458
1459 return (error == NULL);
1460 }
1461
1462 /* vim: set sw=4 et sts=4 : */