Merging upstream version 0.5.9.
[debian/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
31 #include "misc.h"
32 #include "panel.h"
33
34 #include "dbg.h"
35
36 /* data used by themed images buttons */
37 typedef struct {
38 char* fname;
39 guint theme_changed_handler;
40 GdkPixbuf* pixbuf;
41 GdkPixbuf* hilight;
42 gulong hicolor;
43 int dw, dh; /* desired size */
44 gboolean keep_ratio;
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, const gchar *file, gint width, gint height, gboolean keep_ratio);
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 pair pos_pair[] = {
199 { POS_NONE, "none" },
200 { POS_START, "start" },
201 { POS_END, "end" },
202 { 0, NULL},
203 };
204
205
206 int
207 str2num(pair *p, gchar *str, int defval)
208 {
209 ENTER;
210 for (;p && p->str; p++) {
211 if (!g_ascii_strcasecmp(str, p->str))
212 RET(p->num);
213 }
214 RET(defval);
215 }
216
217 gchar *
218 num2str(pair *p, int num, gchar *defval)
219 {
220 ENTER;
221 for (;p && p->str; p++) {
222 if (num == p->num)
223 RET(p->str);
224 }
225 RET(defval);
226 }
227
228 int buf_gets( char* buf, int len, char **fp )
229 {
230 char* p;
231 int i = 0;
232 if( !fp || !(p = *fp) || !**fp )
233 {
234 buf[0] = '\0';
235 return 0;
236 }
237
238 do
239 {
240 if( G_LIKELY( i < len ) )
241 {
242 buf[i] = *p;
243 ++i;
244 }
245 if( G_UNLIKELY(*p == '\n') )
246 {
247 ++p;
248 break;
249 }
250 }while( *(++p) );
251 buf[i] = '\0';
252 *fp = p;
253 return i;
254 }
255
256 extern int
257 lxpanel_put_line(FILE* fp, const char* format, ...)
258 {
259 static int indent = 0;
260 int i, ret;
261 va_list args;
262
263 if( strchr(format, '}') )
264 --indent;
265
266 for( i = 0; i < indent; ++i )
267 fputs( " ", fp );
268
269 va_start (args, format);
270 ret = vfprintf (fp, format, args);
271 va_end (args);
272
273 if( strchr(format, '{') )
274 ++indent;
275 fputc( '\n', fp ); /* add line break */
276 return (ret + 1);
277 }
278
279 extern int
280 lxpanel_get_line(char**fp, line *s)
281 {
282 gchar *tmp, *tmp2;
283
284 s->type = LINE_NONE;
285 if (!fp)
286 RET(s->type);
287 while (buf_gets(s->str, s->len, fp)) {
288
289 g_strstrip(s->str);
290
291 if (s->str[0] == '#' || s->str[0] == 0) {
292 continue;
293 }
294 if (!g_ascii_strcasecmp(s->str, "}")) {
295 s->type = LINE_BLOCK_END;
296 break;
297 }
298
299 s->t[0] = s->str;
300 for (tmp = s->str; isalnum(*tmp); tmp++);
301 for (tmp2 = tmp; isspace(*tmp2); tmp2++);
302 if (*tmp2 == '=') {
303 for (++tmp2; isspace(*tmp2); tmp2++);
304 s->t[1] = tmp2;
305 *tmp = 0;
306 s->type = LINE_VAR;
307 } else if (*tmp2 == '{') {
308 *tmp = 0;
309 s->type = LINE_BLOCK_START;
310 } else {
311 ERR( "parser: unknown token: '%c'\n", *tmp2);
312 }
313 break;
314 }
315 return s->type;
316 }
317
318 int
319 get_line_as_is(char** fp, line *s)
320 {
321 gchar *tmp, *tmp2;
322
323 ENTER;
324 if (!fp) {
325 s->type = LINE_NONE;
326 RET(s->type);
327 }
328 s->type = LINE_NONE;
329 while (buf_gets(s->str, s->len, fp)) {
330 g_strstrip(s->str);
331 if (s->str[0] == '#' || s->str[0] == 0)
332 continue;
333 DBG( ">> %s\n", s->str);
334 if (!g_ascii_strcasecmp(s->str, "}")) {
335 s->type = LINE_BLOCK_END;
336 DBG( " : line_block_end\n");
337 break;
338 }
339 for (tmp = s->str; isalnum(*tmp); tmp++);
340 for (tmp2 = tmp; isspace(*tmp2); tmp2++);
341 if (*tmp2 == '=') {
342 s->type = LINE_VAR;
343 } else if (*tmp2 == '{') {
344 s->type = LINE_BLOCK_START;
345 } else {
346 DBG( " : ? <%c>\n", *tmp2);
347 }
348 break;
349 }
350 RET(s->type);
351
352 }
353
354 void resolve_atoms()
355 {
356 static const char* atom_names[ N_ATOMS ];
357
358 atom_names[ I_UTF8_STRING ] = "UTF8_STRING";
359 atom_names[ I_XROOTPMAP_ID ] = "_XROOTPMAP_ID";
360 atom_names[ I_WM_STATE ] = "WM_STATE";
361 atom_names[ I_WM_CLASS ] = "WM_CLASS";
362 atom_names[ I_WM_DELETE_WINDOW ] = "WM_DELETE_WINDOW";
363 atom_names[ I_WM_PROTOCOLS ] = "WM_PROTOCOLS";
364 atom_names[ I_NET_WORKAREA ] = "_NET_WORKAREA";
365 atom_names[ I_NET_CLIENT_LIST ] = "_NET_CLIENT_LIST";
366 atom_names[ I_NET_CLIENT_LIST_STACKING ] = "_NET_CLIENT_LIST_STACKING";
367 atom_names[ I_NET_NUMBER_OF_DESKTOPS ] = "_NET_NUMBER_OF_DESKTOPS";
368 atom_names[ I_NET_CURRENT_DESKTOP ] = "_NET_CURRENT_DESKTOP";
369 atom_names[ I_NET_DESKTOP_VIEWPORT ] = "_NET_DESKTOP_VIEWPORT";
370 atom_names[ I_NET_DESKTOP_NAMES ] = "_NET_DESKTOP_NAMES";
371 atom_names[ I_NET_ACTIVE_WINDOW ] = "_NET_ACTIVE_WINDOW";
372 atom_names[ I_NET_SHOWING_DESKTOP ] = "_NET_SHOWING_DESKTOP";
373 atom_names[ I_NET_SUPPORTED ] = "_NET_SUPPORTED";
374 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
375 atom_names[ I_NET_WM_STATE ] = "_NET_WM_STATE";
376 atom_names[ I_NET_WM_STATE_SKIP_TASKBAR ] = "_NET_WM_STATE_SKIP_TASKBAR";
377 atom_names[ I_NET_WM_STATE_SKIP_PAGER ] = "_NET_WM_STATE_SKIP_PAGER";
378 atom_names[ I_NET_WM_STATE_STICKY ] = "_NET_WM_STATE_STICKY";
379 atom_names[ I_NET_WM_STATE_HIDDEN ] = "_NET_WM_STATE_HIDDEN";
380 atom_names[ I_NET_WM_STATE_SHADED ] = "_NET_WM_STATE_SHADED";
381 atom_names[ I_NET_WM_WINDOW_TYPE ] = "_NET_WM_WINDOW_TYPE";
382
383 atom_names[ I_NET_WM_WINDOW_TYPE_DESKTOP ] = "_NET_WM_WINDOW_TYPE_DESKTOP";
384 atom_names[ I_NET_WM_WINDOW_TYPE_DOCK ] = "_NET_WM_WINDOW_TYPE_DOCK";
385 atom_names[ I_NET_WM_WINDOW_TYPE_TOOLBAR ] = "_NET_WM_WINDOW_TYPE_TOOLBAR";
386 atom_names[ I_NET_WM_WINDOW_TYPE_MENU ] = "_NET_WM_WINDOW_TYPE_MENU";
387 atom_names[ I_NET_WM_WINDOW_TYPE_UTILITY ] = "_NET_WM_WINDOW_TYPE_UTILITY";
388 atom_names[ I_NET_WM_WINDOW_TYPE_SPLASH ] = "_NET_WM_WINDOW_TYPE_SPLASH";
389 atom_names[ I_NET_WM_WINDOW_TYPE_DIALOG ] = "_NET_WM_WINDOW_TYPE_DIALOG";
390 atom_names[ I_NET_WM_WINDOW_TYPE_NORMAL ] = "_NET_WM_WINDOW_TYPE_NORMAL";
391 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
392 atom_names[ I_NET_WM_PID ] = "_NET_WM_PID";
393 atom_names[ I_NET_WM_NAME ] = "_NET_WM_NAME";
394 atom_names[ I_NET_WM_VISIBLE_NAME ] = "_NET_WM_VISIBLE_NAME";
395 atom_names[ I_NET_WM_STRUT ] = "_NET_WM_STRUT";
396 atom_names[ I_NET_WM_STRUT_PARTIAL ] = "_NET_WM_STRUT_PARTIAL";
397 atom_names[ I_NET_WM_ICON ] = "_NET_WM_ICON";
398 atom_names[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ] = "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR";
399
400 atom_names[ I_NET_SYSTEM_TRAY_OPCODE ] = "_NET_SYSTEM_TRAY_OPCODE";
401 atom_names[ I_NET_SYSTEM_TRAY_MESSAGE_DATA ] = "_NET_SYSTEM_TRAY_MESSAGE_DATA";
402 atom_names[ I_NET_SYSTEM_TRAY_ORIENTATION ] = "_NET_SYSTEM_TRAY_ORIENTATION";
403 atom_names[ I_MANAGER ] = "MANAGER";
404
405 atom_names[ I_LXPANEL_CMD ] = "_LXPANEL_CMD";
406
407 Atom atoms[ N_ATOMS ];
408
409 ENTER;
410
411 if( ! XInternAtoms( GDK_DISPLAY(), (char**)atom_names,
412 N_ATOMS, False, atoms ) )
413 {
414 g_warning( "Error: unable to return Atoms" );
415 return;
416 }
417
418 a_UTF8_STRING = atoms[ I_UTF8_STRING ];
419 a_XROOTPMAP_ID = atoms[ I_XROOTPMAP_ID ];
420 a_WM_STATE = atoms[ I_WM_STATE ];
421 a_WM_CLASS = atoms[ I_WM_CLASS ];
422 a_WM_DELETE_WINDOW = atoms[ I_WM_DELETE_WINDOW ];
423 a_WM_PROTOCOLS = atoms[ I_WM_PROTOCOLS ];
424
425 a_NET_WORKAREA = atoms[ I_NET_WORKAREA ];
426 a_NET_CLIENT_LIST = atoms[ I_NET_CLIENT_LIST ];
427 a_NET_CLIENT_LIST_STACKING = atoms[ I_NET_CLIENT_LIST_STACKING ];
428 a_NET_NUMBER_OF_DESKTOPS = atoms[ I_NET_NUMBER_OF_DESKTOPS ];
429 a_NET_CURRENT_DESKTOP = atoms[ I_NET_CURRENT_DESKTOP ];
430 a_NET_DESKTOP_VIEWPORT = atoms[ I_NET_DESKTOP_VIEWPORT ];
431 a_NET_DESKTOP_NAMES = atoms[ I_NET_DESKTOP_NAMES ];
432 a_NET_ACTIVE_WINDOW = atoms[ I_NET_ACTIVE_WINDOW ];
433 a_NET_SHOWING_DESKTOP = atoms[ I_NET_SHOWING_DESKTOP ];
434 a_NET_SUPPORTED = atoms[ I_NET_SUPPORTED ];
435 a_NET_WM_STATE = atoms[ I_NET_WM_STATE ];
436 a_NET_WM_STATE_SKIP_TASKBAR = atoms[ I_NET_WM_STATE_SKIP_TASKBAR ];
437 a_NET_WM_STATE_SKIP_PAGER = atoms[ I_NET_WM_STATE_SKIP_PAGER ];
438 a_NET_WM_STATE_STICKY = atoms[ I_NET_WM_STATE_STICKY ];
439 a_NET_WM_STATE_HIDDEN = atoms[ I_NET_WM_STATE_HIDDEN ];
440 a_NET_WM_STATE_SHADED = atoms[ I_NET_WM_STATE_SHADED ];
441 a_NET_WM_WINDOW_TYPE = atoms[ I_NET_WM_WINDOW_TYPE ];
442
443 a_NET_WM_WINDOW_TYPE_DESKTOP = atoms[ I_NET_WM_WINDOW_TYPE_DESKTOP ];
444 a_NET_WM_WINDOW_TYPE_DOCK = atoms[ I_NET_WM_WINDOW_TYPE_DOCK ];
445 a_NET_WM_WINDOW_TYPE_TOOLBAR = atoms[ I_NET_WM_WINDOW_TYPE_TOOLBAR ];
446 a_NET_WM_WINDOW_TYPE_MENU = atoms[ I_NET_WM_WINDOW_TYPE_MENU ];
447 a_NET_WM_WINDOW_TYPE_UTILITY = atoms[ I_NET_WM_WINDOW_TYPE_UTILITY ];
448 a_NET_WM_WINDOW_TYPE_SPLASH = atoms[ I_NET_WM_WINDOW_TYPE_SPLASH ];
449 a_NET_WM_WINDOW_TYPE_DIALOG = atoms[ I_NET_WM_WINDOW_TYPE_DIALOG ];
450 a_NET_WM_WINDOW_TYPE_NORMAL = atoms[ I_NET_WM_WINDOW_TYPE_NORMAL ];
451 a_NET_WM_DESKTOP = atoms[ I_NET_WM_DESKTOP ];
452 a_NET_WM_PID = atoms[ I_NET_WM_PID ];
453 a_NET_WM_NAME = atoms[ I_NET_WM_NAME ];
454 a_NET_WM_VISIBLE_NAME = atoms[ I_NET_WM_VISIBLE_NAME ];
455 a_NET_WM_STRUT = atoms[ I_NET_WM_STRUT ];
456 a_NET_WM_STRUT_PARTIAL = atoms[ I_NET_WM_STRUT_PARTIAL ];
457 a_NET_WM_ICON = atoms[ I_NET_WM_ICON ];
458 a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR = atoms[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ];
459
460 a_NET_SYSTEM_TRAY_OPCODE = atoms[ I_NET_SYSTEM_TRAY_OPCODE ];
461 a_NET_SYSTEM_TRAY_MESSAGE_DATA = atoms [ I_NET_SYSTEM_TRAY_MESSAGE_DATA ];
462 a_NET_SYSTEM_TRAY_ORIENTATION = atoms[ I_NET_SYSTEM_TRAY_ORIENTATION ];
463 a_MANAGER = atoms[ I_MANAGER ];
464
465 a_LXPANEL_CMD = atoms[ I_LXPANEL_CMD ];
466
467 RET();
468 }
469
470
471 void
472 Xclimsg(Window win, Atom type, long l0, long l1, long l2, long l3, long l4)
473 {
474 XClientMessageEvent xev;
475 xev.type = ClientMessage;
476 xev.window = win;
477 xev.message_type = type;
478 xev.format = 32;
479 xev.data.l[0] = l0;
480 xev.data.l[1] = l1;
481 xev.data.l[2] = l2;
482 xev.data.l[3] = l3;
483 xev.data.l[4] = l4;
484 XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
485 (SubstructureNotifyMask | SubstructureRedirectMask),
486 (XEvent *) &xev);
487 }
488
489 void
490 Xclimsgwm(Window win, Atom type, Atom arg)
491 {
492 XClientMessageEvent xev;
493
494 xev.type = ClientMessage;
495 xev.window = win;
496 xev.message_type = type;
497 xev.format = 32;
498 xev.data.l[0] = arg;
499 xev.data.l[1] = GDK_CURRENT_TIME;
500 XSendEvent(GDK_DISPLAY(), win, False, 0L, (XEvent *) &xev);
501 }
502
503
504 void *
505 get_utf8_property(Window win, Atom atom)
506 {
507 Atom type;
508 int format;
509 gulong nitems;
510 gulong bytes_after;
511 gchar *val, *retval;
512 int result;
513 guchar *tmp = NULL;
514
515 type = None;
516 retval = NULL;
517 result = XGetWindowProperty (GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
518 a_UTF8_STRING, &type, &format, &nitems,
519 &bytes_after, &tmp);
520 if (result != Success || type == None)
521 return NULL;
522 val = (gchar *) tmp;
523 if (val) {
524 if (type == a_UTF8_STRING && format == 8 && nitems != 0)
525 retval = g_strndup (val, nitems);
526 XFree (val);
527 }
528 return retval;
529
530 }
531
532 char **
533 get_utf8_property_list(Window win, Atom atom, int *count)
534 {
535 Atom type;
536 int format, i;
537 gulong nitems;
538 gulong bytes_after;
539 gchar *s, **retval = NULL;
540 int result;
541 guchar *tmp = NULL;
542
543 *count = 0;
544 result = XGetWindowProperty(GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
545 a_UTF8_STRING, &type, &format, &nitems,
546 &bytes_after, &tmp);
547 if (result != Success || type != a_UTF8_STRING || tmp == NULL)
548 return NULL;
549
550 if (nitems) {
551 gchar *val = (gchar *) tmp;
552 DBG("res=%d(%d) nitems=%d val=%s\n", result, Success, nitems, val);
553 for (i = 0; i < nitems; i++) {
554 if (!val[i])
555 (*count)++;
556 }
557 retval = g_new0 (char*, *count + 2);
558 for (i = 0, s = val; i < *count; i++, s = s + strlen (s) + 1) {
559 retval[i] = g_strdup(s);
560 }
561 if (val[nitems-1]) {
562 result = nitems - (s - val);
563 DBG("val does not ends by 0, moving last %d bytes\n", result);
564 g_memmove(s - 1, s, result);
565 val[nitems-1] = 0;
566 DBG("s=%s\n", s -1);
567 retval[i] = g_strdup(s - 1);
568 (*count)++;
569 }
570 }
571 XFree (tmp);
572
573 return retval;
574
575 }
576
577 void *
578 get_xaproperty (Window win, Atom prop, Atom type, int *nitems)
579 {
580 Atom type_ret;
581 int format_ret;
582 unsigned long items_ret;
583 unsigned long after_ret;
584 unsigned char *prop_data;
585
586 ENTER;
587 prop_data = NULL;
588 if (XGetWindowProperty (GDK_DISPLAY(), win, prop, 0, 0x7fffffff, False,
589 type, &type_ret, &format_ret, &items_ret,
590 &after_ret, &prop_data) != Success)
591 {
592 if( G_UNLIKELY(prop_data) )
593 XFree( prop_data );
594 if( nitems )
595 *nitems = 0;
596 RET(NULL);
597 }
598 if (nitems)
599 *nitems = items_ret;
600 RET(prop_data);
601 }
602
603 static char*
604 text_property_to_utf8 (const XTextProperty *prop)
605 {
606 char **list;
607 int count;
608 char *retval;
609
610 ENTER;
611 list = NULL;
612 count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding),
613 prop->format,
614 prop->value,
615 prop->nitems,
616 &list);
617
618 DBG("count=%d\n", count);
619 if (count == 0)
620 return NULL;
621
622 retval = list[0];
623 list[0] = g_strdup (""); /* something to free */
624
625 g_strfreev (list);
626
627 RET(retval);
628 }
629
630 char *
631 get_textproperty(Window win, Atom atom)
632 {
633 XTextProperty text_prop;
634 char *retval;
635
636 ENTER;
637 if (XGetTextProperty(GDK_DISPLAY(), win, &text_prop, atom)) {
638 DBG("format=%d enc=%d nitems=%d value=%s \n",
639 text_prop.format,
640 text_prop.encoding,
641 text_prop.nitems,
642 text_prop.value);
643 retval = text_property_to_utf8 (&text_prop);
644 if (text_prop.nitems > 0)
645 XFree (text_prop.value);
646 RET(retval);
647
648 }
649 RET(NULL);
650 }
651
652
653 int
654 get_net_number_of_desktops()
655 {
656 int desknum;
657 guint32 *data;
658
659 ENTER;
660 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_NUMBER_OF_DESKTOPS,
661 XA_CARDINAL, 0);
662 if (!data)
663 RET(0);
664
665 desknum = *data;
666 XFree (data);
667 RET(desknum);
668 }
669
670
671 int
672 get_net_current_desktop ()
673 {
674 int desk;
675 guint32 *data;
676
677 ENTER;
678 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, XA_CARDINAL, 0);
679 if (!data)
680 RET(0);
681
682 desk = *data;
683 XFree (data);
684 RET(desk);
685 }
686
687 int
688 get_net_wm_desktop(Window win)
689 {
690 int desk = 0;
691 guint32 *data;
692
693 ENTER;
694 data = get_xaproperty (win, a_NET_WM_DESKTOP, XA_CARDINAL, 0);
695 if (data) {
696 desk = *data;
697 XFree (data);
698 }
699 RET(desk);
700 }
701
702 GPid
703 get_net_wm_pid(Window win)
704 {
705 GPid pid = 0;
706 guint32 *data;
707
708 ENTER;
709 data = get_xaproperty (win, a_NET_WM_PID, XA_CARDINAL, 0);
710 if (data) {
711 pid = *data;
712 XFree (data);
713 }
714 RET(pid);
715 }
716
717 void
718 get_net_wm_state(Window win, NetWMState *nws)
719 {
720 Atom *state;
721 int num3;
722
723
724 ENTER;
725 bzero(nws, sizeof(nws));
726 if (!(state = get_xaproperty(win, a_NET_WM_STATE, XA_ATOM, &num3)))
727 RET();
728
729 DBG( "%x: netwm state = { ", (unsigned int)win);
730 while (--num3 >= 0) {
731 if (state[num3] == a_NET_WM_STATE_SKIP_PAGER) {
732 DBG("NET_WM_STATE_SKIP_PAGER ");
733 nws->skip_pager = 1;
734 } else if (state[num3] == a_NET_WM_STATE_SKIP_TASKBAR) {
735 DBG( "NET_WM_STATE_SKIP_TASKBAR ");
736 nws->skip_taskbar = 1;
737 } else if (state[num3] == a_NET_WM_STATE_STICKY) {
738 DBG( "NET_WM_STATE_STICKY ");
739 nws->sticky = 1;
740 } else if (state[num3] == a_NET_WM_STATE_HIDDEN) {
741 DBG( "NET_WM_STATE_HIDDEN ");
742 nws->hidden = 1;
743 } else if (state[num3] == a_NET_WM_STATE_SHADED) {
744 DBG( "NET_WM_STATE_SHADED ");
745 nws->shaded = 1;
746 } else {
747 DBG( "... ");
748 }
749 }
750 XFree(state);
751 DBG( "}\n");
752 RET();
753 }
754
755 void
756 get_net_wm_window_type(Window win, NetWMWindowType *nwwt)
757 {
758 Atom *state;
759 int num3;
760
761
762 ENTER;
763 bzero(nwwt, sizeof(*nwwt));
764 if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3)))
765 RET();
766
767 DBG( "%x: netwm state = { ", (unsigned int)win);
768 while (--num3 >= 0) {
769 if (state[num3] == a_NET_WM_WINDOW_TYPE_DESKTOP) {
770 DBG("NET_WM_WINDOW_TYPE_DESKTOP ");
771 nwwt->desktop = 1;
772 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DOCK) {
773 DBG( "NET_WM_WINDOW_TYPE_DOCK ");
774 nwwt->dock = 1;
775 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_TOOLBAR) {
776 DBG( "NET_WM_WINDOW_TYPE_TOOLBAR ");
777 nwwt->toolbar = 1;
778 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_MENU) {
779 DBG( "NET_WM_WINDOW_TYPE_MENU ");
780 nwwt->menu = 1;
781 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_UTILITY) {
782 DBG( "NET_WM_WINDOW_TYPE_UTILITY ");
783 nwwt->utility = 1;
784 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_SPLASH) {
785 DBG( "NET_WM_WINDOW_TYPE_SPLASH ");
786 nwwt->splash = 1;
787 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DIALOG) {
788 DBG( "NET_WM_WINDOW_TYPE_DIALOG ");
789 nwwt->dialog = 1;
790 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_NORMAL) {
791 DBG( "NET_WM_WINDOW_TYPE_NORMAL ");
792 nwwt->normal = 1;
793 } else {
794 DBG( "... ");
795 }
796 }
797 XFree(state);
798 DBG( "}\n");
799 RET();
800 }
801
802 int
803 get_wm_state (Window win)
804 {
805 unsigned long *data;
806 int ret = 0;
807
808 ENTER;
809 data = get_xaproperty (win, a_WM_STATE, a_WM_STATE, 0);
810 if (data) {
811 ret = data[0];
812 XFree (data);
813 }
814 RET(ret);
815 }
816
817 static void
818 calculate_width(int scrw, int wtype, int allign, int margin,
819 int *panw, int *x)
820 {
821 ENTER;
822 DBG("scrw=%d\n", scrw);
823 DBG("IN panw=%d, margin=%d\n", *panw, margin);
824 //scrw -= 2;
825 if (wtype == WIDTH_PERCENT) {
826 /* sanity check */
827 if (*panw > 100)
828 *panw = 100;
829 else if (*panw < 0)
830 *panw = 1;
831 *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0;
832 }
833 if (allign != ALLIGN_CENTER) {
834 if (margin > scrw) {
835 ERR( "margin is bigger then edge size %d > %d. Ignoring margin\n",
836 margin, scrw);
837 margin = 0;
838 }
839 *panw = MIN(scrw - margin, *panw);
840 }
841 DBG("OUT panw=%d\n", *panw);
842 if (allign == ALLIGN_LEFT)
843 *x += margin;
844 else if (allign == ALLIGN_RIGHT) {
845 *x += scrw - *panw - margin;
846 if (*x < 0)
847 *x = 0;
848 } else if (allign == ALLIGN_CENTER)
849 *x += (scrw - *panw) / 2;
850 RET();
851 }
852
853
854 void
855 calculate_position(Panel *np)
856 {
857 int sswidth, ssheight, minx, miny;
858
859 ENTER;
860 /* FIXME: Why this doesn't work? */
861 if (0) {
862 // if (np->curdesk < np->wa_len/4) {
863 minx = np->workarea[np->curdesk*4 + 0];
864 miny = np->workarea[np->curdesk*4 + 1];
865 sswidth = np->workarea[np->curdesk*4 + 2];
866 ssheight = np->workarea[np->curdesk*4 + 3];
867 } else {
868 minx = miny = 0;
869 sswidth = gdk_screen_get_width( gtk_widget_get_screen(np->topgwin) );
870 ssheight = gdk_screen_get_height( gtk_widget_get_screen(np->topgwin) );
871 }
872
873 if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
874 np->aw = np->width;
875 np->ax = minx;
876 calculate_width(sswidth, np->widthtype, np->allign, np->margin,
877 &np->aw, &np->ax);
878 np->ah = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
879 np->ay = miny + ((np->edge == EDGE_TOP) ? 0 : (ssheight - np->ah));
880
881 } else {
882 np->ah = np->width;
883 np->ay = miny;
884 calculate_width(ssheight, np->widthtype, np->allign, np->margin,
885 &np->ah, &np->ay);
886 np->aw = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
887 np->ax = minx + ((np->edge == EDGE_LEFT) ? 0 : (sswidth - np->aw));
888 }
889 //g_debug("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah);
890 RET();
891 }
892
893 gchar *
894 expand_tilda(gchar *file)
895 {
896 ENTER;
897 RET((file[0] == '~') ?
898 g_strdup_printf("%s%s", getenv("HOME"), file+1)
899 : g_strdup(file));
900
901 }
902
903 /*
904 * SuxPanel version 0.1
905 * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
906 *
907 * This program may be distributed under the terms of GNU General
908 * Public License version 2. You should have received a copy of the
909 * license with this program; if not, please consult http://www.fsf.org/.
910 *
911 * This program comes with no warranty. Use at your own risk.
912 *
913 */
914
915 /* DestroyNotify handler for image data in _gtk_image_new_from_file_scaled. */
916 static void img_data_free(ImgData * data)
917 {
918 g_free(data->fname);
919 if (data->theme_changed_handler != 0)
920 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
921 if (data->pixbuf != NULL)
922 g_object_unref(data->pixbuf);
923 if (data->hilight != NULL)
924 g_object_unref(data->hilight);
925 g_free(data);
926 }
927
928 /* Handler for "changed" signal in _gtk_image_new_from_file_scaled. */
929 static void on_theme_changed(GtkIconTheme * theme, GtkWidget * img)
930 {
931 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
932 _gtk_image_set_from_file_scaled(img, data->fname, data->dw, data->dh, data->keep_ratio);
933 }
934
935 void fb_button_set_from_file(GtkWidget * btn, const char * img_file, gint width, gint height, gboolean keep_ratio)
936 {
937 /* Locate the image within the button. */
938 GtkWidget * child = gtk_bin_get_child(GTK_BIN(btn));
939 GtkWidget * img = NULL;
940 if (GTK_IS_IMAGE(child))
941 img = child;
942 else if (GTK_IS_BOX(child))
943 {
944 GList * children = gtk_container_get_children(GTK_CONTAINER(child));
945 img = GTK_WIDGET(GTK_IMAGE(children->data));
946 g_list_free(children);
947 }
948
949 if (img != NULL)
950 {
951 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
952 g_free(data->fname);
953 data->fname = g_strdup(img_file);
954 data->dw = width;
955 data->dh = height;
956 data->keep_ratio = keep_ratio;
957 _gtk_image_set_from_file_scaled(img, data->fname, data->dw, data->dh, data->keep_ratio);
958 }
959 }
960
961 static void _gtk_image_set_from_file_scaled(GtkWidget * img, const gchar * file, gint width, gint height, gboolean keep_ratio)
962 {
963 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
964 data->dw = width;
965 data->dh = height;
966 data->keep_ratio = keep_ratio;
967
968 if (data->pixbuf != NULL)
969 {
970 g_object_unref(data->pixbuf);
971 data->pixbuf = NULL;
972 }
973
974 /* if there is a cached hilighted version of this pixbuf, free it */
975 if (data->hilight != NULL)
976 {
977 g_object_unref(data->hilight);
978 data->hilight = NULL;
979 }
980
981 /* if they are the same string, eliminate unnecessary copy. */
982 gboolean themed = FALSE;
983 if (file != NULL)
984 {
985 if (data->fname != file)
986 {
987 g_free(data->fname);
988 data->fname = g_strdup(file);
989 }
990
991 if (g_file_test(file, G_FILE_TEST_EXISTS))
992 {
993 GdkPixbuf * pb_scaled = gdk_pixbuf_new_from_file_at_scale(file, width, height, keep_ratio, NULL);
994 if (pb_scaled != NULL)
995 data->pixbuf = pb_scaled;
996 }
997 else
998 {
999 data->pixbuf = lxpanel_load_icon(file, width, height, keep_ratio);
1000 themed = TRUE;
1001 }
1002 }
1003
1004 if (data->pixbuf != NULL)
1005 {
1006 /* Set the pixbuf into the image widget. */
1007 gtk_image_set_from_pixbuf((GtkImage *)img, data->pixbuf);
1008 if (themed)
1009 {
1010 /* This image is loaded from icon theme. Update the image if the icon theme is changed. */
1011 if (data->theme_changed_handler == 0)
1012 data->theme_changed_handler = g_signal_connect(gtk_icon_theme_get_default(), "changed", G_CALLBACK(on_theme_changed), img);
1013 }
1014 else
1015 {
1016 /* This is not loaded from icon theme. Disconnect the signal handler. */
1017 if (data->theme_changed_handler != 0)
1018 {
1019 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
1020 data->theme_changed_handler = 0;
1021 }
1022 }
1023 }
1024 else
1025 {
1026 /* No pixbuf available. Set the "missing image" icon. */
1027 gtk_image_set_from_stock(GTK_IMAGE(img), GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON);
1028 }
1029 return;
1030 }
1031
1032 GtkWidget * _gtk_image_new_from_file_scaled(const gchar * file, gint width, gint height, gboolean keep_ratio)
1033 {
1034 GtkWidget * img = gtk_image_new();
1035 ImgData * data = g_new0(ImgData, 1);
1036 if (img_data_id == 0)
1037 img_data_id = g_quark_from_static_string("ImgData");
1038 g_object_set_qdata_full(G_OBJECT(img), img_data_id, data, (GDestroyNotify) img_data_free);
1039 _gtk_image_set_from_file_scaled(img, file, width, height, keep_ratio);
1040 return img;
1041 }
1042
1043
1044 void
1045 get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
1046 {
1047 GtkWidget *b;
1048 //gint focus_width;
1049 //gint focus_pad;
1050
1051 ENTER;
1052 b = gtk_button_new();
1053 gtk_widget_set_name(GTK_WIDGET(b), name);
1054 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1055 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_DEFAULT);
1056 gtk_container_set_border_width (GTK_CONTAINER (b), 0);
1057
1058 if (parent)
1059 gtk_container_add(parent, b);
1060
1061 gtk_widget_show(b);
1062 gtk_widget_size_request(b, req);
1063
1064 gtk_widget_destroy(b);
1065 RET();
1066 }
1067
1068
1069 guint32 gcolor2rgb24(GdkColor *color)
1070 {
1071 guint32 i;
1072 guint16 r, g, b;
1073
1074 ENTER;
1075
1076 r = color->red * 0xFF / 0xFFFF;
1077 g = color->green * 0xFF / 0xFFFF;
1078 b = color->blue * 0xFF / 0xFFFF;
1079 DBG("%x %x %x ==> %x %x %x\n", color->red, color->green, color->blue, r, g, b);
1080
1081 i = (color->red * 0xFF / 0xFFFF) & 0xFF;
1082 i <<= 8;
1083 i |= (color->green * 0xFF / 0xFFFF) & 0xFF;
1084 i <<= 8;
1085 i |= (color->blue * 0xFF / 0xFFFF) & 0xFF;
1086 DBG("i=%x\n", i);
1087 RET(i);
1088 }
1089
1090 /* Handler for "enter-notify-event" signal on image that has highlighting requested. */
1091 static gboolean fb_button_enter(GtkImage * widget, GdkEventCrossing * event)
1092 {
1093 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1094 {
1095 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1096 if (data != NULL)
1097 {
1098 if (data->hilight == NULL)
1099 {
1100 GdkPixbuf * dark = data->pixbuf;
1101 int height = gdk_pixbuf_get_height(dark);
1102 int rowstride = gdk_pixbuf_get_rowstride(dark);
1103 gulong hicolor = data->hicolor;
1104
1105 GdkPixbuf * light = gdk_pixbuf_add_alpha(dark, FALSE, 0, 0, 0);
1106 if (light != NULL)
1107 {
1108 guchar extra[3];
1109 int i;
1110 for (i = 2; i >= 0; i--, hicolor >>= 8)
1111 extra[i] = hicolor & 0xFF;
1112
1113 guchar * src = gdk_pixbuf_get_pixels(light);
1114 guchar * up;
1115 for (up = src + height * rowstride; src < up; src += 4)
1116 {
1117 if (src[3] != 0)
1118 {
1119 for (i = 0; i < 3; i++)
1120 {
1121 int value = src[i] + extra[i];
1122 if (value > 255) value = 255;
1123 src[i] = value;
1124 }
1125 }
1126 }
1127 data->hilight = light;
1128 }
1129 }
1130
1131 if (data->hilight != NULL)
1132 gtk_image_set_from_pixbuf(widget, data->hilight);
1133 }
1134 }
1135 return TRUE;
1136 }
1137
1138 /* Handler for "leave-notify-event" signal on image that has highlighting requested. */
1139 static gboolean fb_button_leave(GtkImage * widget, GdkEventCrossing * event, gpointer user_data)
1140 {
1141 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1142 {
1143 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1144 if ((data != NULL) && (data->pixbuf != NULL))
1145 gtk_image_set_from_pixbuf(widget, data->pixbuf);
1146 }
1147 return TRUE;
1148 }
1149
1150
1151 GtkWidget * fb_button_new_from_file(
1152 gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio)
1153 {
1154 return fb_button_new_from_file_with_label(image_file, width, height, highlight_color, keep_ratio, NULL, NULL);
1155 }
1156
1157 GtkWidget * fb_button_new_from_file_with_label(
1158 gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio, Panel * panel, gchar * label)
1159 {
1160 GtkWidget * event_box = gtk_event_box_new();
1161 gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
1162 GTK_WIDGET_UNSET_FLAGS(event_box, GTK_CAN_FOCUS);
1163
1164 GtkWidget * image = _gtk_image_new_from_file_scaled(image_file, width, height, keep_ratio);
1165 gtk_misc_set_padding(GTK_MISC(image), 0, 0);
1166 gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
1167 if (highlight_color != 0)
1168 {
1169 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(image), img_data_id);
1170 data->hicolor = highlight_color;
1171
1172 gtk_widget_add_events(event_box, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1173 g_signal_connect_swapped(G_OBJECT(event_box), "enter-notify-event", G_CALLBACK(fb_button_enter), image);
1174 g_signal_connect_swapped(G_OBJECT(event_box), "leave-notify-event", G_CALLBACK(fb_button_leave), image);
1175 }
1176
1177 if (label == NULL)
1178 gtk_container_add(GTK_CONTAINER(event_box), image);
1179 else
1180 {
1181 GtkWidget * inner = gtk_hbox_new(FALSE, 0);
1182 gtk_container_set_border_width(GTK_CONTAINER(inner), 0);
1183 GTK_WIDGET_UNSET_FLAGS (inner, GTK_CAN_FOCUS);
1184 gtk_container_add(GTK_CONTAINER(event_box), inner);
1185
1186 gtk_box_pack_start(GTK_BOX(inner), image, FALSE, FALSE, 0);
1187
1188 GtkWidget * lbl = gtk_label_new("");
1189 panel_draw_label_text(panel, lbl, label, FALSE, TRUE);
1190 gtk_misc_set_padding(GTK_MISC(lbl), 2, 0);
1191 gtk_box_pack_end(GTK_BOX(inner), lbl, FALSE, FALSE, 0);
1192 }
1193
1194 gtk_widget_show_all(event_box);
1195 return event_box;
1196 }
1197
1198 char* translate_exec_to_cmd( const char* exec, const char* icon,
1199 const char* title, const char* fpath )
1200 {
1201 GString* cmd = g_string_sized_new( 256 );
1202 for( ; *exec; ++exec )
1203 {
1204 if( G_UNLIKELY(*exec == '%') )
1205 {
1206 ++exec;
1207 if( !*exec )
1208 break;
1209 switch( *exec )
1210 {
1211 case 'c':
1212 g_string_append( cmd, title );
1213 break;
1214 case 'i':
1215 if( icon )
1216 {
1217 g_string_append( cmd, "--icon " );
1218 g_string_append( cmd, icon );
1219 }
1220 break;
1221 case 'k':
1222 {
1223 char* uri = g_filename_to_uri( fpath, NULL, NULL );
1224 g_string_append( cmd, uri );
1225 g_free( uri );
1226 break;
1227 }
1228 case '%':
1229 g_string_append_c( cmd, '%' );
1230 break;
1231 }
1232 }
1233 else
1234 g_string_append_c( cmd, *exec );
1235 }
1236 return g_string_free( cmd, FALSE );
1237 }
1238
1239 /*
1240 This function is used to re-create a new box with different
1241 orientation from the old one, add all children of the old one to
1242 the new one, and then destroy the old box.
1243 It's mainly used when we need to change the orientation of the panel or
1244 any plugin with a layout box. Since GtkHBox cannot be changed to GtkVBox,
1245 recreating a new box to replace the old one is required.
1246 */
1247 GtkWidget* recreate_box( GtkBox* oldbox, GtkOrientation orientation )
1248 {
1249 GtkBox* newbox;
1250 GList *child, *children;
1251 GtkWidget* (*my_box_new) (gboolean homogeneous, gint spacing);
1252
1253 if( GTK_IS_HBOX(oldbox) ) {
1254 if( orientation == GTK_ORIENTATION_HORIZONTAL )
1255 return GTK_WIDGET(oldbox);
1256 }
1257 else {
1258 if( orientation == GTK_ORIENTATION_VERTICAL )
1259 return GTK_WIDGET(oldbox);
1260 }
1261 my_box_new = (orientation == GTK_ORIENTATION_HORIZONTAL ? gtk_hbox_new : gtk_vbox_new);
1262
1263 newbox = GTK_BOX(my_box_new( gtk_box_get_homogeneous(oldbox),
1264 gtk_box_get_spacing(oldbox) ));
1265 gtk_container_set_border_width (GTK_CONTAINER (newbox),
1266 gtk_container_get_border_width(GTK_CONTAINER(oldbox)) );
1267 children = gtk_container_get_children( GTK_CONTAINER (oldbox) );
1268 for( child = children; child; child = child->next ) {
1269 gboolean expand, fill;
1270 guint padding;
1271 GtkWidget* w = GTK_WIDGET(child->data);
1272 gtk_box_query_child_packing( oldbox, w,
1273 &expand, &fill, &padding, NULL );
1274 /* g_debug( "repack %s, expand=%d, fill=%d", gtk_widget_get_name(w), expand, fill ); */
1275 g_object_ref( w );
1276 gtk_container_remove( GTK_CONTAINER (oldbox), w );
1277 gtk_box_pack_start( newbox, w, expand, fill, padding );
1278 g_object_unref( w );
1279 }
1280 g_list_free( children );
1281 gtk_widget_show_all( GTK_WIDGET(newbox) );
1282 gtk_widget_destroy( GTK_WIDGET(oldbox) );
1283 return GTK_WIDGET(newbox);
1284 }
1285
1286 void show_error( GtkWindow* parent_win, const char* msg )
1287 {
1288 GtkWidget* dlg = gtk_message_dialog_new( parent_win,
1289 GTK_DIALOG_MODAL,
1290 GTK_MESSAGE_ERROR,
1291 GTK_BUTTONS_OK, "%s", msg );
1292 gtk_dialog_run( (GtkDialog*)dlg );
1293 gtk_widget_destroy( dlg );
1294 }
1295
1296 /* Try to load an icon from a named file via the freedesktop.org data directories path.
1297 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html */
1298 static GdkPixbuf * load_icon_file(const char * file_name, int height, int width)
1299 {
1300 GdkPixbuf * icon = NULL;
1301 const gchar ** dirs = (const gchar **) g_get_system_data_dirs();
1302 const gchar ** dir;
1303 for (dir = dirs; ((*dir != NULL) && (icon == NULL)); dir++)
1304 {
1305 char * file_path = g_build_filename(*dir, "pixmaps", file_name, NULL);
1306 icon = gdk_pixbuf_new_from_file_at_scale(file_path, height, width, TRUE, NULL);
1307 g_free(file_path);
1308 }
1309 return icon;
1310 }
1311
1312 /* Try to load an icon from the current theme. */
1313 static GdkPixbuf * load_icon_from_theme(GtkIconTheme * theme, const char * icon_name, int width, int height)
1314 {
1315 GdkPixbuf * icon = NULL;
1316
1317 /* Look up the icon in the current theme. */
1318 GtkIconInfo * icon_info = gtk_icon_theme_lookup_icon(theme, icon_name, height, GTK_ICON_LOOKUP_USE_BUILTIN);
1319 if (icon_info != NULL)
1320 {
1321 /* If that succeeded, get the filename of the icon.
1322 * If that succeeds, load the icon from the specified file.
1323 * Otherwise, try to get the builtin icon. */
1324 const char * file = gtk_icon_info_get_filename(icon_info);
1325 if (file != NULL)
1326 icon = gdk_pixbuf_new_from_file(file, NULL);
1327 else
1328 {
1329 icon = gtk_icon_info_get_builtin_pixbuf(icon_info);
1330 g_object_ref(icon);
1331 }
1332 gtk_icon_info_free(icon_info);
1333
1334 /* If the icon is not sized properly, take a trip through the scaler.
1335 * The lookup above takes the desired size, so we get the closest result possible. */
1336 if (icon != NULL)
1337 {
1338 if ((height != gdk_pixbuf_get_height(icon)) || (width != gdk_pixbuf_get_width(icon)))
1339 {
1340 /* Handle case of unspecified width; gdk_pixbuf_scale_simple does not. */
1341 if (width < 0)
1342 {
1343 int pixbuf_width = gdk_pixbuf_get_width(icon);
1344 int pixbuf_height = gdk_pixbuf_get_height(icon);
1345 width = height * pixbuf_width / pixbuf_height;
1346 }
1347 GdkPixbuf * scaled = gdk_pixbuf_scale_simple(icon, width, height, GDK_INTERP_BILINEAR);
1348 g_object_unref(icon);
1349 icon = scaled;
1350 }
1351 }
1352 }
1353 return icon;
1354 }
1355
1356 GdkPixbuf * lxpanel_load_icon(const char * name, int width, int height, gboolean use_fallback)
1357 {
1358 GdkPixbuf * icon = NULL;
1359
1360 if (name != NULL)
1361 {
1362 if (g_path_is_absolute(name))
1363 {
1364 /* Absolute path. */
1365 icon = gdk_pixbuf_new_from_file_at_scale(name, width, height, TRUE, NULL);
1366 }
1367 else
1368 {
1369 /* Relative path. */
1370 GtkIconTheme * theme = gtk_icon_theme_get_default();
1371 char * suffix = strrchr(name, '.');
1372 if ((suffix != NULL)
1373 && ((g_strcasecmp(&suffix[1], "png") == 0)
1374 || (g_strcasecmp(&suffix[1], "svg") == 0)
1375 || (g_strcasecmp(&suffix[1], "xpm") == 0)))
1376 {
1377 /* The file extension indicates it could be in the system pixmap directories. */
1378 icon = load_icon_file(name, width, height);
1379 if (icon == NULL)
1380 {
1381 /* Not found.
1382 * Let's remove the suffix, and see if this name can match an icon in the current icon theme. */
1383 char * icon_name = g_strndup(name, suffix - name);
1384 icon = load_icon_from_theme(theme, icon_name, width, height);
1385 g_free(icon_name);
1386 }
1387 }
1388 else
1389 {
1390 /* No file extension. It could be an icon name in the icon theme. */
1391 icon = load_icon_from_theme(theme, name, width, height);
1392 }
1393 }
1394 }
1395
1396 /* Fall back to generic icons. */
1397 if ((icon == NULL) && (use_fallback))
1398 {
1399 GtkIconTheme * theme = gtk_icon_theme_get_default();
1400 icon = load_icon_from_theme(theme, "application-x-executable", width, height);
1401 if (icon == NULL)
1402 icon = load_icon_from_theme(theme, "gnome-mime-application-x-executable", width, height);
1403 }
1404 return icon;
1405 }
1406
1407 /*
1408 * Taken from pcmanfm:
1409 * Parse Exec command line of app desktop file, and translate
1410 * it into a real command which can be passed to g_spawn_command_line_async().
1411 * file_list is a null-terminated file list containing full
1412 * paths of the files passed to app.
1413 * returned char* should be freed when no longer needed.
1414 */
1415 static char* translate_app_exec_to_command_line( const char* pexec,
1416 GList* file_list )
1417 {
1418 char* file;
1419 GList* l;
1420 gchar *tmp;
1421 GString* cmd = g_string_new("");
1422 gboolean add_files = FALSE;
1423
1424 for( ; *pexec; ++pexec )
1425 {
1426 if( *pexec == '%' )
1427 {
1428 ++pexec;
1429 switch( *pexec )
1430 {
1431 case 'U':
1432 for( l = file_list; l; l = l->next )
1433 {
1434 tmp = g_filename_to_uri( (char*)l->data, NULL, NULL );
1435 file = g_shell_quote( tmp );
1436 g_free( tmp );
1437 g_string_append( cmd, file );
1438 g_string_append_c( cmd, ' ' );
1439 g_free( file );
1440 }
1441 add_files = TRUE;
1442 break;
1443 case 'u':
1444 if( file_list && file_list->data )
1445 {
1446 file = (char*)file_list->data;
1447 tmp = g_filename_to_uri( file, NULL, NULL );
1448 file = g_shell_quote( tmp );
1449 g_free( tmp );
1450 g_string_append( cmd, file );
1451 g_free( file );
1452 add_files = TRUE;
1453 }
1454 break;
1455 case 'F':
1456 case 'N':
1457 for( l = file_list; l; l = l->next )
1458 {
1459 file = (char*)l->data;
1460 tmp = g_shell_quote( file );
1461 g_string_append( cmd, tmp );
1462 g_string_append_c( cmd, ' ' );
1463 g_free( tmp );
1464 }
1465 add_files = TRUE;
1466 break;
1467 case 'f':
1468 case 'n':
1469 if( file_list && file_list->data )
1470 {
1471 file = (char*)file_list->data;
1472 tmp = g_shell_quote( file );
1473 g_string_append( cmd, tmp );
1474 g_free( tmp );
1475 add_files = TRUE;
1476 }
1477 break;
1478 case 'D':
1479 for( l = file_list; l; l = l->next )
1480 {
1481 tmp = g_path_get_dirname( (char*)l->data );
1482 file = g_shell_quote( tmp );
1483 g_free( tmp );
1484 g_string_append( cmd, file );
1485 g_string_append_c( cmd, ' ' );
1486 g_free( file );
1487 }
1488 add_files = TRUE;
1489 break;
1490 case 'd':
1491 if( file_list && file_list->data )
1492 {
1493 tmp = g_path_get_dirname( (char*)file_list->data );
1494 file = g_shell_quote( tmp );
1495 g_free( tmp );
1496 g_string_append( cmd, file );
1497 g_free( tmp );
1498 add_files = TRUE;
1499 }
1500 break;
1501 case 'c':
1502 #if 0
1503 g_string_append( cmd, vfs_app_desktop_get_disp_name( app ) );
1504 #endif
1505 break;
1506 case 'i':
1507 /* Add icon name */
1508 #if 0
1509 if( vfs_app_desktop_get_icon_name( app ) )
1510 {
1511 g_string_append( cmd, "--icon " );
1512 g_string_append( cmd, vfs_app_desktop_get_icon_name( app ) );
1513 }
1514 #endif
1515 break;
1516 case 'k':
1517 /* Location of the desktop file */
1518 break;
1519 case 'v':
1520 /* Device name */
1521 break;
1522 case '%':
1523 g_string_append_c ( cmd, '%' );
1524 break;
1525 case '\0':
1526 goto _finish;
1527 break;
1528 }
1529 }
1530 else /* not % escaped part */
1531 {
1532 g_string_append_c ( cmd, *pexec );
1533 }
1534 }
1535 _finish:
1536 if( ! add_files )
1537 {
1538 g_string_append_c ( cmd, ' ' );
1539 for( l = file_list; l; l = l->next )
1540 {
1541 file = (char*)l->data;
1542 tmp = g_shell_quote( file );
1543 g_string_append( cmd, tmp );
1544 g_string_append_c( cmd, ' ' );
1545 g_free( tmp );
1546 }
1547 }
1548
1549 return g_string_free( cmd, FALSE );
1550 }
1551
1552 gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal)
1553 {
1554 GError *error = NULL;
1555 char* cmd;
1556 if( ! exec )
1557 return FALSE;
1558 cmd = translate_app_exec_to_command_line(exec, files);
1559 if( in_terminal )
1560 {
1561 char * escaped_cmd = g_shell_quote(cmd);
1562 char* term_cmd;
1563 const char* term = lxpanel_get_terminal();
1564 if( strstr(term, "%s") )
1565 term_cmd = g_strdup_printf(term, escaped_cmd);
1566 else
1567 term_cmd = g_strconcat( term, " -e ", escaped_cmd, NULL );
1568 g_free(escaped_cmd);
1569 if( cmd != exec )
1570 g_free(cmd);
1571 cmd = term_cmd;
1572 }
1573 if (! g_spawn_command_line_async(cmd, &error) ) {
1574 ERR("can't spawn %s\nError is %s\n", cmd, error->message);
1575 g_error_free (error);
1576 }
1577 g_free(cmd);
1578
1579 return (error == NULL);
1580 }