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