a22beeb419d4eb773953d0c6c8ce23fd357f8da2
[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
31 #include "misc.h"
32 #include "panel.h"
33
34 //#define DEBUG
35 #include "dbg.h"
36
37
38 /* X11 data types */
39 Atom a_UTF8_STRING;
40 Atom a_XROOTPMAP_ID;
41
42 /* old WM spec */
43 Atom a_WM_STATE;
44 Atom a_WM_CLASS;
45 Atom a_WM_DELETE_WINDOW;
46 Atom a_WM_PROTOCOLS;
47
48 /* new NET spec */
49 Atom a_NET_WORKAREA;
50 Atom a_NET_CLIENT_LIST;
51 Atom a_NET_CLIENT_LIST_STACKING;
52 Atom a_NET_NUMBER_OF_DESKTOPS;
53 Atom a_NET_CURRENT_DESKTOP;
54 Atom a_NET_DESKTOP_VIEWPORT;
55 Atom a_NET_DESKTOP_NAMES;
56 Atom a_NET_ACTIVE_WINDOW;
57 Atom a_NET_CLOSE_WINDOW;
58 Atom a_NET_SUPPORTED;
59 Atom a_NET_WM_STATE;
60 Atom a_NET_WM_STATE_SKIP_TASKBAR;
61 Atom a_NET_WM_STATE_SKIP_PAGER;
62 Atom a_NET_WM_STATE_STICKY;
63 Atom a_NET_WM_STATE_HIDDEN;
64 Atom a_NET_WM_STATE_SHADED;
65 Atom a_NET_WM_WINDOW_TYPE;
66 Atom a_NET_WM_WINDOW_TYPE_DESKTOP;
67 Atom a_NET_WM_WINDOW_TYPE_DOCK;
68 Atom a_NET_WM_WINDOW_TYPE_TOOLBAR;
69 Atom a_NET_WM_WINDOW_TYPE_MENU;
70 Atom a_NET_WM_WINDOW_TYPE_UTILITY;
71 Atom a_NET_WM_WINDOW_TYPE_SPLASH;
72 Atom a_NET_WM_WINDOW_TYPE_DIALOG;
73 Atom a_NET_WM_WINDOW_TYPE_NORMAL;
74 Atom a_NET_WM_DESKTOP;
75 Atom a_NET_WM_PID;
76 Atom a_NET_WM_NAME;
77 Atom a_NET_WM_VISIBLE_NAME;
78 Atom a_NET_WM_STRUT;
79 Atom a_NET_WM_STRUT_PARTIAL;
80 Atom a_NET_WM_ICON;
81 Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
82
83 Atom a_LXPANEL_CMD; /* for private client message */
84
85 enum{
86 I_UTF8_STRING,
87 I_XROOTPMAP_ID,
88 I_WM_STATE,
89 I_WM_CLASS,
90 I_WM_DELETE_WINDOW,
91 I_WM_PROTOCOLS,
92 I_NET_WORKAREA,
93 I_NET_CLIENT_LIST,
94 I_NET_CLIENT_LIST_STACKING,
95 I_NET_NUMBER_OF_DESKTOPS,
96 I_NET_CURRENT_DESKTOP,
97 I_NET_DESKTOP_NAMES,
98 I_NET_ACTIVE_WINDOW,
99 I_NET_SUPPORTED,
100
101 I_NET_WM_STATE,
102 I_NET_WM_STATE_SKIP_TASKBAR,
103 I_NET_WM_STATE_SKIP_PAGER,
104 I_NET_WM_STATE_STICKY,
105 I_NET_WM_STATE_HIDDEN,
106 I_NET_WM_STATE_SHADED,
107 I_NET_WM_WINDOW_TYPE,
108
109 I_NET_WM_WINDOW_TYPE_DESKTOP,
110 I_NET_WM_WINDOW_TYPE_DOCK,
111 I_NET_WM_WINDOW_TYPE_TOOLBAR,
112 I_NET_WM_WINDOW_TYPE_MENU,
113 I_NET_WM_WINDOW_TYPE_UTILITY,
114 I_NET_WM_WINDOW_TYPE_SPLASH,
115 I_NET_WM_WINDOW_TYPE_DIALOG,
116 I_NET_WM_WINDOW_TYPE_NORMAL,
117 I_NET_WM_DESKTOP,
118 I_NET_WM_PID,
119 I_NET_WM_NAME,
120 I_NET_WM_VISIBLE_NAME,
121 I_NET_WM_STRUT,
122 I_NET_WM_STRUT_PARTIAL,
123 I_NET_WM_ICON,
124 I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR,
125
126 I_LXPANEL_CMD,
127 N_ATOMS
128 };
129
130 pair allign_pair[] = {
131 { ALLIGN_NONE, "none" },
132 { ALLIGN_LEFT, "left" },
133 { ALLIGN_RIGHT, "right" },
134 { ALLIGN_CENTER, "center"},
135 { 0, NULL },
136 };
137
138 pair edge_pair[] = {
139 { EDGE_NONE, "none" },
140 { EDGE_LEFT, "left" },
141 { EDGE_RIGHT, "right" },
142 { EDGE_TOP, "top" },
143 { EDGE_BOTTOM, "bottom" },
144 { 0, NULL },
145 };
146
147 pair width_pair[] = {
148 { WIDTH_NONE, "none" },
149 { WIDTH_REQUEST, "request" },
150 { WIDTH_PIXEL, "pixel" },
151 { WIDTH_PERCENT, "percent" },
152 { 0, NULL },
153 };
154
155 pair height_pair[] = {
156 { HEIGHT_NONE, "none" },
157 { HEIGHT_PIXEL, "pixel" },
158 { 0, NULL },
159 };
160
161 pair bool_pair[] = {
162 { 0, "0" },
163 { 1, "1" },
164 { 0, NULL },
165 };
166 pair pos_pair[] = {
167 { POS_NONE, "none" },
168 { POS_START, "start" },
169 { POS_END, "end" },
170 { 0, NULL},
171 };
172
173
174 int
175 str2num(pair *p, gchar *str, int defval)
176 {
177 ENTER;
178 for (;p && p->str; p++) {
179 if (!g_ascii_strcasecmp(str, p->str))
180 RET(p->num);
181 }
182 RET(defval);
183 }
184
185 gchar *
186 num2str(pair *p, int num, gchar *defval)
187 {
188 ENTER;
189 for (;p && p->str; p++) {
190 if (num == p->num)
191 RET(p->str);
192 }
193 RET(defval);
194 }
195
196 int buf_gets( char* buf, int len, char **fp )
197 {
198 char* p;
199 int i = 0;
200 if( !fp || !(p = *fp) || !**fp )
201 {
202 buf[0] = '\0';
203 return 0;
204 }
205
206 do
207 {
208 if( G_LIKELY( i < len ) )
209 {
210 buf[i] = *p;
211 ++i;
212 }
213 if( G_UNLIKELY(*p == '\n') )
214 {
215 ++p;
216 break;
217 }
218 }while( *(++p) );
219 buf[i] = '\0';
220 *fp = p;
221 return i;
222 }
223
224 extern int
225 lxpanel_put_line(FILE* fp, const char* format, ...)
226 {
227 static int indent = 0;
228 int i, ret;
229 va_list args;
230
231 if( strchr(format, '}') )
232 --indent;
233
234 for( i = 0; i < indent; ++i )
235 fputs( " ", fp );
236
237 va_start (args, format);
238 ret = vfprintf (fp, format, args);
239 va_end (args);
240
241 if( strchr(format, '{') )
242 ++indent;
243 fputc( '\n', fp ); /* add line break */
244 return (ret + 1);
245 }
246
247 extern int
248 lxpanel_put_str( FILE* fp, const char* name, const char* val )
249 {
250 if( G_UNLIKELY( !val || !*val ) )
251 return 0;
252 return lxpanel_put_line( fp, "%s=%s", name, val );
253 }
254
255 extern int
256 lxpanel_put_bool( FILE* fp, const char* name, gboolean val )
257 {
258 return lxpanel_put_line( fp, "%s=%c", name, val ? '1' : '0' );
259 }
260
261 extern int
262 lxpanel_put_int( FILE* fp, const char* name, int val )
263 {
264 return lxpanel_put_line( fp, "%s=%d", name, val );
265 }
266
267 extern int
268 lxpanel_get_line(char**fp, line *s)
269 {
270 gchar *tmp, *tmp2;
271 ENTER;
272 s->type = LINE_NONE;
273 if (!fp)
274 RET(s->type);
275 while (buf_gets(s->str, s->len, fp)) {
276
277 g_strstrip(s->str);
278
279 if (s->str[0] == '#' || s->str[0] == 0) {
280 continue;
281 }
282 if (!g_ascii_strcasecmp(s->str, "}")) {
283 s->type = LINE_BLOCK_END;
284 break;
285 }
286
287 s->t[0] = s->str;
288 for (tmp = s->str; isalnum(*tmp); tmp++);
289 for (tmp2 = tmp; isspace(*tmp2); tmp2++);
290 if (*tmp2 == '=') {
291 for (++tmp2; isspace(*tmp2); tmp2++);
292 s->t[1] = tmp2;
293 *tmp = 0;
294 s->type = LINE_VAR;
295 } else if (*tmp2 == '{') {
296 *tmp = 0;
297 s->type = LINE_BLOCK_START;
298 } else {
299 ERR( "parser: unknown token: '%c'\n", *tmp2);
300 }
301 break;
302 }
303 RET(s->type);
304 }
305
306 int
307 get_line_as_is(char** fp, line *s)
308 {
309 gchar *tmp, *tmp2;
310
311 ENTER;
312 if (!fp) {
313 s->type = LINE_NONE;
314 RET(s->type);
315 }
316 s->type = LINE_NONE;
317 while (buf_gets(s->str, s->len, fp)) {
318 g_strstrip(s->str);
319 if (s->str[0] == '#' || s->str[0] == 0)
320 continue;
321 DBG( ">> %s\n", s->str);
322 if (!g_ascii_strcasecmp(s->str, "}")) {
323 s->type = LINE_BLOCK_END;
324 DBG( " : line_block_end\n");
325 break;
326 }
327 for (tmp = s->str; isalnum(*tmp); tmp++);
328 for (tmp2 = tmp; isspace(*tmp2); tmp2++);
329 if (*tmp2 == '=') {
330 s->type = LINE_VAR;
331 } else if (*tmp2 == '{') {
332 s->type = LINE_BLOCK_START;
333 } else {
334 DBG( " : ? <%c>\n", *tmp2);
335 }
336 break;
337 }
338 RET(s->type);
339
340 }
341
342 void resolve_atoms()
343 {
344 static const char* atom_names[ N_ATOMS ];
345
346 atom_names[ I_UTF8_STRING ] = "UTF8_STRING";
347 atom_names[ I_XROOTPMAP_ID ] = "_XROOTPMAP_ID";
348 atom_names[ I_WM_STATE ] = "WM_STATE";
349 atom_names[ I_WM_CLASS ] = "WM_CLASS";
350 atom_names[ I_WM_DELETE_WINDOW ] = "WM_DELETE_WINDOW";
351 atom_names[ I_WM_PROTOCOLS ] = "WM_PROTOCOLS";
352 atom_names[ I_NET_WORKAREA ] = "_NET_WORKAREA";
353 atom_names[ I_NET_CLIENT_LIST ] = "_NET_CLIENT_LIST";
354 atom_names[ I_NET_CLIENT_LIST_STACKING ] = "_NET_CLIENT_LIST_STACKING";
355 atom_names[ I_NET_NUMBER_OF_DESKTOPS ] = "_NET_NUMBER_OF_DESKTOPS";
356 atom_names[ I_NET_CURRENT_DESKTOP ] = "_NET_CURRENT_DESKTOP";
357 atom_names[ I_NET_DESKTOP_NAMES ] = "_NET_DESKTOP_NAMES";
358 atom_names[ I_NET_ACTIVE_WINDOW ] = "_NET_ACTIVE_WINDOW";
359 atom_names[ I_NET_SUPPORTED ] = "_NET_SUPPORTED";
360 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
361 atom_names[ I_NET_WM_STATE ] = "_NET_WM_STATE";
362 atom_names[ I_NET_WM_STATE_SKIP_TASKBAR ] = "_NET_WM_STATE_SKIP_TASKBAR";
363 atom_names[ I_NET_WM_STATE_SKIP_PAGER ] = "_NET_WM_STATE_SKIP_PAGER";
364 atom_names[ I_NET_WM_STATE_STICKY ] = "_NET_WM_STATE_STICKY";
365 atom_names[ I_NET_WM_STATE_HIDDEN ] = "_NET_WM_STATE_HIDDEN";
366 atom_names[ I_NET_WM_STATE_SHADED ] = "_NET_WM_STATE_SHADED";
367 atom_names[ I_NET_WM_WINDOW_TYPE ] = "_NET_WM_WINDOW_TYPE";
368
369 atom_names[ I_NET_WM_WINDOW_TYPE_DESKTOP ] = "_NET_WM_WINDOW_TYPE_DESKTOP";
370 atom_names[ I_NET_WM_WINDOW_TYPE_DOCK ] = "_NET_WM_WINDOW_TYPE_DOCK";
371 atom_names[ I_NET_WM_WINDOW_TYPE_TOOLBAR ] = "_NET_WM_WINDOW_TYPE_TOOLBAR";
372 atom_names[ I_NET_WM_WINDOW_TYPE_MENU ] = "_NET_WM_WINDOW_TYPE_MENU";
373 atom_names[ I_NET_WM_WINDOW_TYPE_UTILITY ] = "_NET_WM_WINDOW_TYPE_UTILITY";
374 atom_names[ I_NET_WM_WINDOW_TYPE_SPLASH ] = "_NET_WM_WINDOW_TYPE_SPLASH";
375 atom_names[ I_NET_WM_WINDOW_TYPE_DIALOG ] = "_NET_WM_WINDOW_TYPE_DIALOG";
376 atom_names[ I_NET_WM_WINDOW_TYPE_NORMAL ] = "_NET_WM_WINDOW_TYPE_NORMAL";
377 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
378 atom_names[ I_NET_WM_PID ] = "_NET_WM_PID";
379 atom_names[ I_NET_WM_NAME ] = "_NET_WM_NAME";
380 atom_names[ I_NET_WM_VISIBLE_NAME ] = "_NET_WM_VISIBLE_NAME";
381 atom_names[ I_NET_WM_STRUT ] = "_NET_WM_STRUT";
382 atom_names[ I_NET_WM_STRUT_PARTIAL ] = "_NET_WM_STRUT_PARTIAL";
383 atom_names[ I_NET_WM_ICON ] = "_NET_WM_ICON";
384 atom_names[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ] = "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR";
385 atom_names[ I_LXPANEL_CMD ] = "_LXPANEL_CMD";
386
387 Atom atoms[ N_ATOMS ];
388
389 ENTER;
390
391 if( ! XInternAtoms( GDK_DISPLAY(), atom_names, N_ATOMS, False, atoms ) )
392 {
393 g_warning( "Error: unable to return Atoms" );
394 return;
395 }
396
397 a_UTF8_STRING = atoms[ I_UTF8_STRING ];
398 a_XROOTPMAP_ID = atoms[ I_XROOTPMAP_ID ];
399 a_WM_STATE = atoms[ I_WM_STATE ];
400 a_WM_CLASS = atoms[ I_WM_CLASS ];
401 a_WM_DELETE_WINDOW = atoms[ I_WM_DELETE_WINDOW ];
402 a_WM_PROTOCOLS = atoms[ I_WM_PROTOCOLS ];
403 a_NET_WORKAREA = atoms[ I_NET_WORKAREA ];
404 a_NET_CLIENT_LIST = atoms[ I_NET_CLIENT_LIST ];
405 a_NET_CLIENT_LIST_STACKING = atoms[ I_NET_CLIENT_LIST_STACKING ];
406 a_NET_NUMBER_OF_DESKTOPS = atoms[ I_NET_NUMBER_OF_DESKTOPS ];
407 a_NET_CURRENT_DESKTOP = atoms[ I_NET_CURRENT_DESKTOP ];
408 a_NET_DESKTOP_NAMES = atoms[ I_NET_DESKTOP_NAMES ];
409 a_NET_ACTIVE_WINDOW = atoms[ I_NET_ACTIVE_WINDOW ];
410 a_NET_SUPPORTED = atoms[ I_NET_SUPPORTED ];
411 a_NET_WM_STATE = atoms[ I_NET_WM_STATE ];
412 a_NET_WM_STATE_SKIP_TASKBAR = atoms[ I_NET_WM_STATE_SKIP_TASKBAR ];
413 a_NET_WM_STATE_SKIP_PAGER = atoms[ I_NET_WM_STATE_SKIP_PAGER ];
414 a_NET_WM_STATE_STICKY = atoms[ I_NET_WM_STATE_STICKY ];
415 a_NET_WM_STATE_HIDDEN = atoms[ I_NET_WM_STATE_HIDDEN ];
416 a_NET_WM_STATE_SHADED = atoms[ I_NET_WM_STATE_SHADED ];
417 a_NET_WM_WINDOW_TYPE = atoms[ I_NET_WM_WINDOW_TYPE ];
418
419 a_NET_WM_WINDOW_TYPE_DESKTOP = atoms[ I_NET_WM_WINDOW_TYPE_DESKTOP ];
420 a_NET_WM_WINDOW_TYPE_DOCK = atoms[ I_NET_WM_WINDOW_TYPE_DOCK ];
421 a_NET_WM_WINDOW_TYPE_TOOLBAR = atoms[ I_NET_WM_WINDOW_TYPE_TOOLBAR ];
422 a_NET_WM_WINDOW_TYPE_MENU = atoms[ I_NET_WM_WINDOW_TYPE_MENU ];
423 a_NET_WM_WINDOW_TYPE_UTILITY = atoms[ I_NET_WM_WINDOW_TYPE_UTILITY ];
424 a_NET_WM_WINDOW_TYPE_SPLASH = atoms[ I_NET_WM_WINDOW_TYPE_SPLASH ];
425 a_NET_WM_WINDOW_TYPE_DIALOG = atoms[ I_NET_WM_WINDOW_TYPE_DIALOG ];
426 a_NET_WM_WINDOW_TYPE_NORMAL = atoms[ I_NET_WM_WINDOW_TYPE_NORMAL ];
427 a_NET_WM_DESKTOP = atoms[ I_NET_WM_DESKTOP ];
428 a_NET_WM_NAME = atoms[ I_NET_WM_NAME ];
429 a_NET_WM_VISIBLE_NAME = atoms[ I_NET_WM_VISIBLE_NAME ];
430 a_NET_WM_STRUT = atoms[ I_NET_WM_STRUT ];
431 a_NET_WM_STRUT_PARTIAL = atoms[ I_NET_WM_STRUT_PARTIAL ];
432 a_NET_WM_ICON = atoms[ I_NET_WM_ICON ];
433 a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR = atoms[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ];
434
435 a_LXPANEL_CMD = atoms[ I_LXPANEL_CMD ];
436
437 RET();
438 }
439
440
441 void
442 Xclimsg(Window win, long type, long l0, long l1, long l2, long l3, long l4)
443 {
444 XClientMessageEvent xev;
445
446 xev.type = ClientMessage;
447 xev.window = win;
448 xev.message_type = type;
449 xev.format = 32;
450 xev.data.l[0] = l0;
451 xev.data.l[1] = l1;
452 xev.data.l[2] = l2;
453 xev.data.l[3] = l3;
454 xev.data.l[4] = l4;
455 XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
456 (SubstructureNotifyMask | SubstructureRedirectMask),
457 (XEvent *) & xev);
458 }
459
460 void
461 Xclimsgwm(Window win, Atom type, Atom arg)
462 {
463 XClientMessageEvent xev;
464
465 xev.type = ClientMessage;
466 xev.window = win;
467 xev.message_type = type;
468 xev.format = 32;
469 xev.data.l[0] = arg;
470 xev.data.l[1] = GDK_CURRENT_TIME;
471 XSendEvent(GDK_DISPLAY(), win, False, 0L, (XEvent *) &xev);
472 }
473
474
475 void *
476 get_utf8_property(Window win, Atom atom)
477 {
478
479 Atom type;
480 int format;
481 gulong nitems;
482 gulong bytes_after;
483 gchar *val, *retval;
484 int result;
485 guchar *tmp = NULL;
486
487 type = None;
488 retval = NULL;
489 result = XGetWindowProperty (GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
490 a_UTF8_STRING, &type, &format, &nitems,
491 &bytes_after, &tmp);
492 if (result != Success || type == None)
493 return NULL;
494 val = (gchar *) tmp;
495 if (val) {
496 if (type == a_UTF8_STRING && format == 8 && nitems != 0)
497 retval = g_strndup (val, nitems);
498 XFree (val);
499 }
500 return retval;
501
502 }
503
504 char **
505 get_utf8_property_list(Window win, Atom atom, int *count)
506 {
507 Atom type;
508 int format, i;
509 gulong nitems;
510 gulong bytes_after;
511 gchar *s, **retval = NULL;
512 int result;
513 guchar *tmp = NULL;
514
515 *count = 0;
516 result = XGetWindowProperty(GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
517 a_UTF8_STRING, &type, &format, &nitems,
518 &bytes_after, &tmp);
519 if (result != Success || type != a_UTF8_STRING || tmp == NULL)
520 return NULL;
521
522 if (nitems) {
523 gchar *val = (gchar *) tmp;
524 DBG("res=%d(%d) nitems=%d val=%s\n", result, Success, nitems, val);
525 for (i = 0; i < nitems; i++) {
526 if (!val[i])
527 (*count)++;
528 }
529 retval = g_new0 (char*, *count + 2);
530 for (i = 0, s = val; i < *count; i++, s = s + strlen (s) + 1) {
531 retval[i] = g_strdup(s);
532 }
533 if (val[nitems-1]) {
534 result = nitems - (s - val);
535 DBG("val does not ends by 0, moving last %d bytes\n", result);
536 g_memmove(s - 1, s, result);
537 val[nitems-1] = 0;
538 DBG("s=%s\n", s -1);
539 retval[i] = g_strdup(s - 1);
540 (*count)++;
541 }
542 }
543 XFree (tmp);
544
545 return retval;
546
547 }
548
549 void *
550 get_xaproperty (Window win, Atom prop, Atom type, int *nitems)
551 {
552 Atom type_ret;
553 int format_ret;
554 unsigned long items_ret;
555 unsigned long after_ret;
556 unsigned char *prop_data;
557
558 ENTER;
559 prop_data = NULL;
560 if (XGetWindowProperty (GDK_DISPLAY(), win, prop, 0, 0x7fffffff, False,
561 type, &type_ret, &format_ret, &items_ret,
562 &after_ret, &prop_data) != Success)
563 {
564 if( G_UNLIKELY(prop_data) )
565 XFree( prop_data );
566 if( nitems )
567 *nitems = 0;
568 RET(NULL);
569 }
570 if (nitems)
571 *nitems = items_ret;
572 RET(prop_data);
573 }
574
575 static char*
576 text_property_to_utf8 (const XTextProperty *prop)
577 {
578 char **list;
579 int count;
580 char *retval;
581
582 ENTER;
583 list = NULL;
584 count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding),
585 prop->format,
586 prop->value,
587 prop->nitems,
588 &list);
589
590 DBG("count=%d\n", count);
591 if (count == 0)
592 return NULL;
593
594 retval = list[0];
595 list[0] = g_strdup (""); /* something to free */
596
597 g_strfreev (list);
598
599 RET(retval);
600 }
601
602 char *
603 get_textproperty(Window win, Atom atom)
604 {
605 XTextProperty text_prop;
606 char *retval;
607
608 ENTER;
609 if (XGetTextProperty(GDK_DISPLAY(), win, &text_prop, atom)) {
610 DBG("format=%d enc=%d nitems=%d value=%s \n",
611 text_prop.format,
612 text_prop.encoding,
613 text_prop.nitems,
614 text_prop.value);
615 retval = text_property_to_utf8 (&text_prop);
616 if (text_prop.nitems > 0)
617 XFree (text_prop.value);
618 RET(retval);
619
620 }
621 RET(NULL);
622 }
623
624
625 int
626 get_net_number_of_desktops()
627 {
628 int desknum;
629 guint32 *data;
630
631 ENTER;
632 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_NUMBER_OF_DESKTOPS,
633 XA_CARDINAL, 0);
634 if (!data)
635 RET(0);
636
637 desknum = *data;
638 XFree (data);
639 RET(desknum);
640 }
641
642
643 int
644 get_net_current_desktop ()
645 {
646 int desk;
647 guint32 *data;
648
649 ENTER;
650 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, XA_CARDINAL, 0);
651 if (!data)
652 RET(0);
653
654 desk = *data;
655 XFree (data);
656 RET(desk);
657 }
658
659 int
660 get_net_wm_desktop(Window win)
661 {
662 int desk = 0;
663 guint32 *data;
664
665 ENTER;
666 data = get_xaproperty (win, a_NET_WM_DESKTOP, XA_CARDINAL, 0);
667 if (data) {
668 desk = *data;
669 XFree (data);
670 }
671 RET(desk);
672 }
673
674 GPid
675 get_net_wm_pid(Window win)
676 {
677 GPid pid = 0;
678 guint32 *data;
679
680 ENTER;
681 data = get_xaproperty (win, a_NET_WM_PID, XA_CARDINAL, 0);
682 if (data) {
683 pid = *data;
684 XFree (data);
685 }
686 RET(pid);
687 }
688
689 void
690 get_net_wm_state(Window win, NetWMState *nws)
691 {
692 Atom *state;
693 int num3;
694
695
696 ENTER;
697 bzero(nws, sizeof(nws));
698 if (!(state = get_xaproperty(win, a_NET_WM_STATE, XA_ATOM, &num3)))
699 RET();
700
701 DBG( "%x: netwm state = { ", (unsigned int)win);
702 while (--num3 >= 0) {
703 if (state[num3] == a_NET_WM_STATE_SKIP_PAGER) {
704 DBG("NET_WM_STATE_SKIP_PAGER ");
705 nws->skip_pager = 1;
706 } else if (state[num3] == a_NET_WM_STATE_SKIP_TASKBAR) {
707 DBG( "NET_WM_STATE_SKIP_TASKBAR ");
708 nws->skip_taskbar = 1;
709 } else if (state[num3] == a_NET_WM_STATE_STICKY) {
710 DBG( "NET_WM_STATE_STICKY ");
711 nws->sticky = 1;
712 } else if (state[num3] == a_NET_WM_STATE_HIDDEN) {
713 DBG( "NET_WM_STATE_HIDDEN ");
714 nws->hidden = 1;
715 } else if (state[num3] == a_NET_WM_STATE_SHADED) {
716 DBG( "NET_WM_STATE_SHADED ");
717 nws->shaded = 1;
718 } else {
719 DBG( "... ");
720 }
721 }
722 XFree(state);
723 DBG( "}\n");
724 RET();
725 }
726
727
728
729
730 void
731 get_net_wm_window_type(Window win, NetWMWindowType *nwwt)
732 {
733 Atom *state;
734 int num3;
735
736
737 ENTER;
738 bzero(nwwt, sizeof(nwwt));
739 if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3)))
740 RET();
741
742 DBG( "%x: netwm state = { ", (unsigned int)win);
743 while (--num3 >= 0) {
744 if (state[num3] == a_NET_WM_WINDOW_TYPE_DESKTOP) {
745 DBG("NET_WM_WINDOW_TYPE_DESKTOP ");
746 nwwt->desktop = 1;
747 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DOCK) {
748 DBG( "NET_WM_WINDOW_TYPE_DOCK ");
749 nwwt->dock = 1;
750 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_TOOLBAR) {
751 DBG( "NET_WM_WINDOW_TYPE_TOOLBAR ");
752 nwwt->toolbar = 1;
753 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_MENU) {
754 DBG( "NET_WM_WINDOW_TYPE_MENU ");
755 nwwt->menu = 1;
756 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_UTILITY) {
757 DBG( "NET_WM_WINDOW_TYPE_UTILITY ");
758 nwwt->utility = 1;
759 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_SPLASH) {
760 DBG( "NET_WM_WINDOW_TYPE_SPLASH ");
761 nwwt->splash = 1;
762 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DIALOG) {
763 DBG( "NET_WM_WINDOW_TYPE_DIALOG ");
764 nwwt->dialog = 1;
765 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_NORMAL) {
766 DBG( "NET_WM_WINDOW_TYPE_NORMAL ");
767 nwwt->normal = 1;
768 } else {
769 DBG( "... ");
770 }
771 }
772 XFree(state);
773 DBG( "}\n");
774 RET();
775 }
776
777
778
779
780
781 int
782 get_wm_state (Window win)
783 {
784 unsigned long *data;
785 int ret = 0;
786
787 ENTER;
788 data = get_xaproperty (win, a_WM_STATE, a_WM_STATE, 0);
789 if (data) {
790 ret = data[0];
791 XFree (data);
792 }
793 RET(ret);
794 }
795
796 static void
797 calculate_width(int scrw, int wtype, int allign, int margin,
798 int *panw, int *x)
799 {
800 ENTER;
801 DBG("scrw=%d\n", scrw);
802 DBG("IN panw=%d\n", *panw);
803 //scrw -= 2;
804 if (wtype == WIDTH_PERCENT) {
805 /* sanity check */
806 if (*panw > 100)
807 *panw = 100;
808 else if (*panw < 0)
809 *panw = 1;
810 *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0;
811 }
812 if (allign != ALLIGN_CENTER) {
813 if (margin > scrw) {
814 ERR( "margin is bigger then edge size %d > %d. Ignoring margin\n",
815 margin, scrw);
816 margin = 0;
817 }
818 if (wtype == WIDTH_PERCENT)
819 //*panw = MAX(scrw - margin, *panw);
820 ;
821 else
822 *panw = MIN(scrw - margin, *panw);
823 }
824 DBG("OUT panw=%d\n", *panw);
825 if (allign == ALLIGN_LEFT)
826 *x += margin;
827 else if (allign == ALLIGN_RIGHT) {
828 *x += scrw - *panw - margin;
829 if (*x < 0)
830 *x = 0;
831 } else if (allign == ALLIGN_CENTER)
832 *x += (scrw - *panw) / 2;
833 RET();
834 }
835
836
837 void
838 calculate_position(Panel *np)
839 {
840 int sswidth, ssheight, minx, miny;
841
842 ENTER;
843 /* FIXME: Why this doesn't work? */
844 if (0) {
845 // if (np->curdesk < np->wa_len/4) {
846 minx = np->workarea[np->curdesk*4 + 0];
847 miny = np->workarea[np->curdesk*4 + 1];
848 sswidth = np->workarea[np->curdesk*4 + 2];
849 ssheight = np->workarea[np->curdesk*4 + 3];
850 } else {
851 minx = miny = 0;
852 sswidth = gdk_screen_get_width( gtk_widget_get_screen(np->topgwin) );
853 ssheight = gdk_screen_get_height( gtk_widget_get_screen(np->topgwin) );
854 }
855
856 if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
857 np->aw = np->width;
858 np->ax = minx;
859 calculate_width(sswidth, np->widthtype, np->allign, np->margin,
860 &np->aw, &np->ax);
861 np->ah = np->height;
862 np->ah = MIN(PANEL_HEIGHT_MAX, np->ah);
863 np->ah = MAX(PANEL_HEIGHT_MIN, np->ah);
864 np->ay = miny + ((np->edge == EDGE_TOP) ? 0 : (ssheight - np->ah));
865
866 } else {
867 np->ah = np->width;
868 np->ay = miny;
869 calculate_width(ssheight, np->widthtype, np->allign, np->margin,
870 &np->ah, &np->ay);
871 np->aw = np->height;
872 np->aw = MIN(PANEL_HEIGHT_MAX, np->aw);
873 np->aw = MAX(PANEL_HEIGHT_MIN, np->aw);
874 np->ax = minx + ((np->edge == EDGE_LEFT) ? 0 : (sswidth - np->aw));
875 }
876 //g_debug("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah);
877 RET();
878 }
879
880
881
882 gchar *
883 expand_tilda(gchar *file)
884 {
885 ENTER;
886 RET((file[0] == '~') ?
887 g_strdup_printf("%s%s", getenv("HOME"), file+1)
888 : g_strdup(file));
889
890 }
891
892
893
894
895 #if 0
896 Window
897 Select_Window(Display *dpy)
898 {
899 int status;
900 Cursor cursor;
901 XEvent event;
902 Window target_win = None, root = RootWindow(dpy,DefaultScreen(dpy));
903 int buttons = 0;
904
905 ENTER;
906 /* Make the target cursor */
907 cursor = XCreateFontCursor(dpy, XC_crosshair);
908
909 /* Grab the pointer using target cursor, letting it room all over */
910 status = XGrabPointer(dpy, root, False,
911 ButtonPressMask|ButtonReleaseMask, GrabModeSync,
912 GrabModeAsync, root, cursor, CurrentTime);
913 if (status != GrabSuccess) {
914 ERR("Can't grab the mouse.");
915 RET(None);
916 }
917 /* Let the user select a window... */
918 while ((target_win == None) || (buttons != 0)) {
919 /* allow one more event */
920 XAllowEvents(dpy, SyncPointer, CurrentTime);
921 XWindowEvent(dpy, root, ButtonPressMask|ButtonReleaseMask, &event);
922 switch (event.type) {
923 case ButtonPress:
924 if (target_win == None) {
925 target_win = event.xbutton.subwindow; /* window selected */
926 DBG("target win = 0x%x\n", target_win);
927 if (target_win == None) target_win = root;
928 }
929 buttons++;
930 break;
931 case ButtonRelease:
932 if (buttons > 0) /* there may have been some down before we started */
933 buttons--;
934 break;
935 }
936 }
937
938 XUngrabPointer(dpy, CurrentTime); /* Done with pointer */
939 RET(target_win);
940 }
941 #endif
942
943 /*
944 * SuxPanel version 0.1
945 * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
946 *
947 * This program may be distributed under the terms of GNU General
948 * Public License version 2. You should have received a copy of the
949 * license with this program; if not, please consult http://www.fsf.org/.
950 *
951 * This program comes with no warranty. Use at your own risk.
952 *
953 */
954
955 GdkPixbuf *
956 gdk_pixbuf_scale_ratio(GdkPixbuf *p, int width, int height, GdkInterpType itype, gboolean keep_ratio)
957 {
958 gfloat w, h, rw, rh;
959
960 if (keep_ratio) {
961 w = gdk_pixbuf_get_width(p);
962 h = gdk_pixbuf_get_height(p);
963 rw = w / width;
964 rh = h / height;
965 if (rw > rh)
966 height = h / rw;
967 else
968 width = w / rh;
969 }
970 return gdk_pixbuf_scale_simple(p, width, height, itype);
971
972 }
973
974
975 GtkWidget *
976 gtk_image_new_from_file_scaled(const gchar *file, gint width,
977 gint height, gboolean keep_ratio)
978 {
979 GtkWidget *img;
980 GdkPixbuf /*- *pb, -*/ *pb_scaled;
981 // gfloat w, h, rw, rh;
982 GtkIconInfo *inf = NULL;
983
984 ENTER;
985
986 if( G_UNLIKELY( ! file ) )
987 goto err;
988
989 if ( ! g_file_test(file, G_FILE_TEST_EXISTS))
990 {
991 /* FIXME: should reload icon when theme gets changed */
992 inf = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(),
993 file, MAX(width, height), 0);
994 if( ! inf )
995 goto err;
996 file = gtk_icon_info_get_filename(inf);
997 }
998
999 #if GTK_CHECK_VERSION( 2, 6, 0 )
1000 pb_scaled = gdk_pixbuf_new_from_file_at_scale( file, width, height,
1001 keep_ratio, NULL );
1002 if( !pb_scaled )
1003 goto err;
1004 #else
1005 if (!(pb = gdk_pixbuf_new_from_file(file, NULL)))
1006 goto err;
1007
1008 if (keep_ratio) {
1009 w = gdk_pixbuf_get_width(pb);
1010 h = gdk_pixbuf_get_height(pb);
1011 rw = w / width;
1012 rh = h / height;
1013 if (rw > rh)
1014 height = h / rw;
1015 else
1016 width = w / rh;
1017 }
1018 pb_scaled = gdk_pixbuf_scale_simple(pb, width, height,
1019 GDK_INTERP_BILINEAR);
1020 g_object_unref(pb);
1021 #endif
1022 img = gtk_image_new_from_pixbuf(pb_scaled);
1023 g_object_unref(pb_scaled);
1024
1025 if( inf )
1026 gtk_icon_info_free ( inf );
1027
1028 RET(img);
1029
1030 err:
1031 img = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE,
1032 GTK_ICON_SIZE_BUTTON);
1033 RET(img);
1034 }
1035
1036
1037 void
1038 get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
1039 {
1040 GtkWidget *b;
1041 //gint focus_width;
1042 //gint focus_pad;
1043
1044 ENTER;
1045 b = gtk_button_new();
1046 gtk_widget_set_name(GTK_WIDGET(b), name);
1047 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1048 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_DEFAULT);
1049 gtk_container_set_border_width (GTK_CONTAINER (b), 0);
1050
1051 if (parent)
1052 gtk_container_add(parent, b);
1053
1054 gtk_widget_show(b);
1055 gtk_widget_size_request(b, req);
1056
1057 gtk_widget_destroy(b);
1058 RET();
1059 }
1060
1061
1062 guint32 gcolor2rgb24(GdkColor *color)
1063 {
1064 guint32 i;
1065 guint16 r, g, b;
1066
1067 ENTER;
1068
1069 r = color->red * 0xFF / 0xFFFF;
1070 g = color->green * 0xFF / 0xFFFF;
1071 b = color->blue * 0xFF / 0xFFFF;
1072 DBG("%x %x %x ==> %x %x %x\n", color->red, color->green, color->blue, r, g, b);
1073
1074 i = (color->red * 0xFF / 0xFFFF) & 0xFF;
1075 i <<= 8;
1076 i |= (color->green * 0xFF / 0xFFFF) & 0xFF;
1077 i <<= 8;
1078 i |= (color->blue * 0xFF / 0xFFFF) & 0xFF;
1079 DBG("i=%x\n", i);
1080 RET(i);
1081 }
1082
1083
1084 static gboolean
1085 fb_button_enter (GtkImage *widget, GdkEventCrossing *event)
1086 {
1087 GdkPixbuf *dark, *light;
1088 int i;
1089 gulong hicolor;
1090 guchar *src, *up, extra[3];
1091
1092 ENTER;
1093 if (gtk_image_get_storage_type(widget) != GTK_IMAGE_PIXBUF)
1094 RET(TRUE);
1095 light = g_object_get_data(G_OBJECT(widget), "light");
1096 dark = gtk_image_get_pixbuf(widget);
1097 if (!light) {
1098 hicolor = (gulong) g_object_get_data(G_OBJECT(widget), "hicolor");
1099 light = gdk_pixbuf_add_alpha(dark, FALSE, 0, 0, 0);
1100 if (!light)
1101 RET(TRUE);
1102 src = gdk_pixbuf_get_pixels (light);
1103 for (i = 2; i >= 0; i--, hicolor >>= 8)
1104 extra[i] = hicolor & 0xFF;
1105 for (up = src + gdk_pixbuf_get_height(light) * gdk_pixbuf_get_rowstride (light);
1106 src < up; src+=4) {
1107 if (src[3] == 0)
1108 continue;
1109 for (i = 0; i < 3; i++) {
1110 if (src[i] + extra[i] >= 255)
1111 src[i] = 255;
1112 else
1113 src[i] += extra[i];
1114 }
1115 }
1116 g_object_set_data_full (G_OBJECT(widget), "light", light, g_object_unref);
1117 }
1118 g_object_ref(dark);
1119 g_object_set_data_full (G_OBJECT(widget), "dark", dark, g_object_unref);
1120 gtk_image_set_from_pixbuf(widget, light);
1121 RET(TRUE);
1122
1123 }
1124
1125 static gboolean
1126 fb_button_leave (GtkImage *widget, GdkEventCrossing *event, gpointer user_data)
1127 {
1128 GdkPixbuf *dark;
1129
1130 ENTER;
1131 if (gtk_image_get_storage_type(widget) != GTK_IMAGE_PIXBUF)
1132 RET(TRUE);
1133 dark = g_object_get_data(G_OBJECT(widget), "dark");
1134 if (dark)
1135 gtk_image_set_from_pixbuf(widget, dark);
1136 RET(TRUE);
1137 }
1138
1139
1140 GtkWidget *
1141 fb_button_new_from_file(gchar *fname, int width, int height, gulong hicolor, gboolean keep_ratio)
1142 {
1143 GtkWidget *b, *image;
1144 ENTER;
1145 // b = gtk_vbox_new(FALSE, 0); //gtk_bgbox_new();
1146 b = gtk_event_box_new();
1147 gtk_container_set_border_width(GTK_CONTAINER(b), 0);
1148 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1149
1150 image = gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
1151 gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
1152 g_object_set_data(G_OBJECT(image), "hicolor", (gpointer)hicolor);
1153 gtk_misc_set_padding (GTK_MISC(image), 0, 0);
1154 if (hicolor > 0) {
1155 gtk_widget_add_events(b, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1156 g_signal_connect_swapped (G_OBJECT (b), "enter-notify-event",
1157 G_CALLBACK (fb_button_enter), image);
1158 g_signal_connect_swapped (G_OBJECT (b), "leave-notify-event",
1159 G_CALLBACK (fb_button_leave), image);
1160 }
1161 gtk_container_add(GTK_CONTAINER(b), image);
1162 gtk_widget_show(image);
1163 gtk_widget_show(b);
1164 RET(b);
1165 }
1166
1167 GtkWidget *
1168 fb_button_new_from_file_with_colorlabel(gchar *fname, int width, int height,
1169 gulong hicolor, gulong fcolor, gboolean keep_ratio, gchar *name)
1170 {
1171 GtkWidget *b, *image, *box, *label;
1172
1173 ENTER;
1174 b = gtk_event_box_new();
1175 gtk_container_set_border_width(GTK_CONTAINER(b), 0);
1176 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1177
1178 box = gtk_hbox_new(FALSE, 0);
1179 gtk_container_set_border_width(GTK_CONTAINER(box), 0);
1180 GTK_WIDGET_UNSET_FLAGS (box, GTK_CAN_FOCUS);
1181 gtk_container_add(GTK_CONTAINER(b), box);
1182
1183 image = gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
1184 g_object_set_data(G_OBJECT(image), "hicolor", (gpointer)hicolor);
1185 gtk_misc_set_padding (GTK_MISC(image), 0, 0);
1186 if (hicolor > 0) {
1187 gtk_widget_add_events(b, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1188 g_signal_connect_swapped (G_OBJECT (b), "enter-notify-event",
1189 G_CALLBACK (fb_button_enter), image);
1190 g_signal_connect_swapped (G_OBJECT (b), "leave-notify-event",
1191 G_CALLBACK (fb_button_leave), image);
1192 }
1193 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
1194 if (name) {
1195 label = gtk_label_new("");
1196 char *lname = g_strdup_printf("<span color=\"#%06x\">%s</span>", fcolor, name);
1197 gtk_label_set_markup(GTK_LABEL(label), lname);
1198 gtk_misc_set_padding(GTK_MISC(label), 2, 0);
1199 g_free(lname);
1200 gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 0);
1201 }
1202 gtk_widget_show_all(b);
1203 RET(b);
1204 }
1205
1206 GtkWidget *
1207 fb_button_new_from_file_with_label(gchar *fname, int width, int height,
1208 gulong hicolor, gboolean keep_ratio, gchar *name)
1209 {
1210 GtkWidget *b, *image, *box, *label;
1211
1212 ENTER;
1213 b = gtk_event_box_new();
1214 gtk_container_set_border_width(GTK_CONTAINER(b), 0);
1215 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1216
1217 box = gtk_hbox_new(FALSE, 0);
1218 gtk_container_set_border_width(GTK_CONTAINER(box), 0);
1219 GTK_WIDGET_UNSET_FLAGS (box, GTK_CAN_FOCUS);
1220 gtk_container_add(GTK_CONTAINER(b), box);
1221
1222 image = gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
1223 g_object_set_data(G_OBJECT(image), "hicolor", (gpointer)hicolor);
1224 gtk_misc_set_padding (GTK_MISC(image), 0, 0);
1225 if (hicolor > 0) {
1226 gtk_widget_add_events(b, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1227 g_signal_connect_swapped (G_OBJECT (b), "enter-notify-event",
1228 G_CALLBACK (fb_button_enter), image);
1229 g_signal_connect_swapped (G_OBJECT (b), "leave-notify-event",
1230 G_CALLBACK (fb_button_leave), image);
1231 }
1232 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
1233 if (name) {
1234 label = gtk_label_new(name);
1235 gtk_misc_set_padding(GTK_MISC(label), 2, 0);
1236 gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 0);
1237 }
1238 gtk_widget_show_all(b);
1239 RET(b);
1240 }
1241
1242 char* translate_exec_to_cmd( const char* exec, const char* icon,
1243 const char* title, const char* fpath )
1244 {
1245 GString* cmd = g_string_sized_new( 256 );
1246 for( ; *exec; ++exec )
1247 {
1248 if( G_UNLIKELY(*exec == '%') )
1249 {
1250 ++exec;
1251 if( !*exec )
1252 break;
1253 switch( *exec )
1254 {
1255 case 'c':
1256 g_string_append( cmd, title );
1257 break;
1258 case 'i':
1259 if( icon )
1260 {
1261 g_string_append( cmd, "--icon " );
1262 g_string_append( cmd, icon );
1263 }
1264 break;
1265 case 'k':
1266 {
1267 char* uri = g_filename_to_uri( fpath, NULL, NULL );
1268 g_string_append( cmd, uri );
1269 g_free( uri );
1270 break;
1271 }
1272 case '%':
1273 g_string_append_c( cmd, '%' );
1274 break;
1275 }
1276 }
1277 else
1278 g_string_append_c( cmd, *exec );
1279 }
1280 return g_string_free( cmd, FALSE );
1281 }
1282
1283 /*
1284 This function is used to re-create a new box with different
1285 orientation from the old one, add all children of the old one to
1286 the new one, and then destroy the old box.
1287 It's mainly used when we need to change the orientation of the panel or
1288 any plugin with a layout box. Since GtkHBox cannot be changed to GtkVBox,
1289 recreating a new box to replace the old one is required.
1290 */
1291 GtkWidget* recreate_box( GtkBox* oldbox, GtkOrientation orientation )
1292 {
1293 GtkBox* newbox;
1294 GList *child, *children;
1295 GtkWidget* (*my_box_new) (gboolean homogeneous, gint spacing);
1296
1297 if( GTK_IS_HBOX(oldbox) ) {
1298 if( orientation == GTK_ORIENTATION_HORIZONTAL )
1299 return GTK_WIDGET(oldbox);
1300 }
1301 else {
1302 if( orientation == GTK_ORIENTATION_VERTICAL )
1303 return GTK_WIDGET(oldbox);
1304 }
1305 my_box_new = (orientation == GTK_ORIENTATION_HORIZONTAL ? gtk_hbox_new : gtk_vbox_new);
1306
1307 newbox = GTK_BOX(my_box_new( gtk_box_get_homogeneous(oldbox),
1308 gtk_box_get_spacing(oldbox) ));
1309 gtk_container_set_border_width (GTK_CONTAINER (newbox),
1310 gtk_container_get_border_width(GTK_CONTAINER(oldbox)) );
1311 children = gtk_container_get_children( GTK_CONTAINER (oldbox) );
1312 for( child = children; child; child = child->next ) {
1313 gboolean expand, fill;
1314 guint padding;
1315 GtkWidget* w = GTK_WIDGET(child->data);
1316 gtk_box_query_child_packing( oldbox, w,
1317 &expand, &fill, &padding, NULL );
1318 /* g_debug( "repack %s, expand=%d, fill=%d", gtk_widget_get_name(w), expand, fill ); */
1319 g_object_ref( w );
1320 gtk_container_remove( GTK_CONTAINER (oldbox), w );
1321 gtk_box_pack_start( newbox, w, expand, fill, padding );
1322 g_object_unref( w );
1323 }
1324 g_list_free( children );
1325 gtk_widget_show_all( GTK_WIDGET(newbox) );
1326 gtk_widget_destroy( GTK_WIDGET(oldbox) );
1327 return GTK_WIDGET(newbox);
1328 }
1329
1330 void show_error( GtkWindow* parent_win, const char* msg )
1331 {
1332 GtkWidget* dlg = gtk_message_dialog_new( parent_win,
1333 GTK_DIALOG_MODAL,
1334 GTK_MESSAGE_ERROR,
1335 GTK_BUTTONS_OK, msg );
1336 gtk_dialog_run( (GtkDialog*)dlg );
1337 gtk_widget_destroy( dlg );
1338 }