b9abe39d0206c327db6c05e8a00dc5a9d7907d50
[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 "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 int dw, dh; /* desired size */
46 gboolean keep_ratio;
47 }ImgData;
48
49 static GQuark img_data_id = 0;
50
51 static void on_theme_changed(GtkIconTheme* theme, GtkWidget* img);
52 void
53 _gtk_image_set_from_file_scaled( GtkWidget* img, const gchar *file, gint width,
54 gint height, gboolean keep_ratio);
55
56 /* X11 data types */
57 Atom a_UTF8_STRING;
58 Atom a_XROOTPMAP_ID;
59
60 /* old WM spec */
61 Atom a_WM_STATE;
62 Atom a_WM_CLASS;
63 Atom a_WM_DELETE_WINDOW;
64 Atom a_WM_PROTOCOLS;
65
66 /* new NET spec */
67 Atom a_NET_WORKAREA;
68 Atom a_NET_CLIENT_LIST;
69 Atom a_NET_CLIENT_LIST_STACKING;
70 Atom a_NET_NUMBER_OF_DESKTOPS;
71 Atom a_NET_CURRENT_DESKTOP;
72 Atom a_NET_DESKTOP_VIEWPORT;
73 Atom a_NET_DESKTOP_NAMES;
74 Atom a_NET_ACTIVE_WINDOW;
75 Atom a_NET_CLOSE_WINDOW;
76 Atom a_NET_SUPPORTED;
77 Atom a_NET_WM_STATE;
78 Atom a_NET_WM_STATE_SKIP_TASKBAR;
79 Atom a_NET_WM_STATE_SKIP_PAGER;
80 Atom a_NET_WM_STATE_STICKY;
81 Atom a_NET_WM_STATE_HIDDEN;
82 Atom a_NET_WM_STATE_SHADED;
83 Atom a_NET_WM_WINDOW_TYPE;
84 Atom a_NET_WM_WINDOW_TYPE_DESKTOP;
85 Atom a_NET_WM_WINDOW_TYPE_DOCK;
86 Atom a_NET_WM_WINDOW_TYPE_TOOLBAR;
87 Atom a_NET_WM_WINDOW_TYPE_MENU;
88 Atom a_NET_WM_WINDOW_TYPE_UTILITY;
89 Atom a_NET_WM_WINDOW_TYPE_SPLASH;
90 Atom a_NET_WM_WINDOW_TYPE_DIALOG;
91 Atom a_NET_WM_WINDOW_TYPE_NORMAL;
92 Atom a_NET_WM_DESKTOP;
93 Atom a_NET_WM_PID;
94 Atom a_NET_WM_NAME;
95 Atom a_NET_WM_VISIBLE_NAME;
96 Atom a_NET_WM_STRUT;
97 Atom a_NET_WM_STRUT_PARTIAL;
98 Atom a_NET_WM_ICON;
99 Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
100
101 Atom a_LXPANEL_CMD; /* for private client message */
102
103 enum{
104 I_UTF8_STRING,
105 I_XROOTPMAP_ID,
106 I_WM_STATE,
107 I_WM_CLASS,
108 I_WM_DELETE_WINDOW,
109 I_WM_PROTOCOLS,
110 I_NET_WORKAREA,
111 I_NET_CLIENT_LIST,
112 I_NET_CLIENT_LIST_STACKING,
113 I_NET_NUMBER_OF_DESKTOPS,
114 I_NET_CURRENT_DESKTOP,
115 I_NET_DESKTOP_NAMES,
116 I_NET_ACTIVE_WINDOW,
117 I_NET_SUPPORTED,
118
119 I_NET_WM_STATE,
120 I_NET_WM_STATE_SKIP_TASKBAR,
121 I_NET_WM_STATE_SKIP_PAGER,
122 I_NET_WM_STATE_STICKY,
123 I_NET_WM_STATE_HIDDEN,
124 I_NET_WM_STATE_SHADED,
125 I_NET_WM_WINDOW_TYPE,
126
127 I_NET_WM_WINDOW_TYPE_DESKTOP,
128 I_NET_WM_WINDOW_TYPE_DOCK,
129 I_NET_WM_WINDOW_TYPE_TOOLBAR,
130 I_NET_WM_WINDOW_TYPE_MENU,
131 I_NET_WM_WINDOW_TYPE_UTILITY,
132 I_NET_WM_WINDOW_TYPE_SPLASH,
133 I_NET_WM_WINDOW_TYPE_DIALOG,
134 I_NET_WM_WINDOW_TYPE_NORMAL,
135 I_NET_WM_DESKTOP,
136 I_NET_WM_PID,
137 I_NET_WM_NAME,
138 I_NET_WM_VISIBLE_NAME,
139 I_NET_WM_STRUT,
140 I_NET_WM_STRUT_PARTIAL,
141 I_NET_WM_ICON,
142 I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR,
143
144 I_LXPANEL_CMD,
145 N_ATOMS
146 };
147
148 pair allign_pair[] = {
149 { ALLIGN_NONE, "none" },
150 { ALLIGN_LEFT, "left" },
151 { ALLIGN_RIGHT, "right" },
152 { ALLIGN_CENTER, "center"},
153 { 0, NULL },
154 };
155
156 pair edge_pair[] = {
157 { EDGE_NONE, "none" },
158 { EDGE_LEFT, "left" },
159 { EDGE_RIGHT, "right" },
160 { EDGE_TOP, "top" },
161 { EDGE_BOTTOM, "bottom" },
162 { 0, NULL },
163 };
164
165 pair width_pair[] = {
166 { WIDTH_NONE, "none" },
167 { WIDTH_REQUEST, "request" },
168 { WIDTH_PIXEL, "pixel" },
169 { WIDTH_PERCENT, "percent" },
170 { 0, NULL },
171 };
172
173 pair height_pair[] = {
174 { HEIGHT_NONE, "none" },
175 { HEIGHT_PIXEL, "pixel" },
176 { 0, NULL },
177 };
178
179 pair bool_pair[] = {
180 { 0, "0" },
181 { 1, "1" },
182 { 0, NULL },
183 };
184 pair pos_pair[] = {
185 { POS_NONE, "none" },
186 { POS_START, "start" },
187 { POS_END, "end" },
188 { 0, NULL},
189 };
190
191
192 int
193 str2num(pair *p, gchar *str, int defval)
194 {
195 ENTER;
196 for (;p && p->str; p++) {
197 if (!g_ascii_strcasecmp(str, p->str))
198 RET(p->num);
199 }
200 RET(defval);
201 }
202
203 gchar *
204 num2str(pair *p, int num, gchar *defval)
205 {
206 ENTER;
207 for (;p && p->str; p++) {
208 if (num == p->num)
209 RET(p->str);
210 }
211 RET(defval);
212 }
213
214 int buf_gets( char* buf, int len, char **fp )
215 {
216 char* p;
217 int i = 0;
218 if( !fp || !(p = *fp) || !**fp )
219 {
220 buf[0] = '\0';
221 return 0;
222 }
223
224 do
225 {
226 if( G_LIKELY( i < len ) )
227 {
228 buf[i] = *p;
229 ++i;
230 }
231 if( G_UNLIKELY(*p == '\n') )
232 {
233 ++p;
234 break;
235 }
236 }while( *(++p) );
237 buf[i] = '\0';
238 *fp = p;
239 return i;
240 }
241
242 extern int
243 lxpanel_put_line(FILE* fp, const char* format, ...)
244 {
245 static int indent = 0;
246 int i, ret;
247 va_list args;
248
249 if( strchr(format, '}') )
250 --indent;
251
252 for( i = 0; i < indent; ++i )
253 fputs( " ", fp );
254
255 va_start (args, format);
256 ret = vfprintf (fp, format, args);
257 va_end (args);
258
259 if( strchr(format, '{') )
260 ++indent;
261 fputc( '\n', fp ); /* add line break */
262 return (ret + 1);
263 }
264
265 extern int
266 lxpanel_get_line(char**fp, line *s)
267 {
268 gchar *tmp, *tmp2;
269
270 s->type = LINE_NONE;
271 if (!fp)
272 RET(s->type);
273 while (buf_gets(s->str, s->len, fp)) {
274
275 g_strstrip(s->str);
276
277 if (s->str[0] == '#' || s->str[0] == 0) {
278 continue;
279 }
280 if (!g_ascii_strcasecmp(s->str, "}")) {
281 s->type = LINE_BLOCK_END;
282 break;
283 }
284
285 s->t[0] = s->str;
286 for (tmp = s->str; isalnum(*tmp); tmp++);
287 for (tmp2 = tmp; isspace(*tmp2); tmp2++);
288 if (*tmp2 == '=') {
289 for (++tmp2; isspace(*tmp2); tmp2++);
290 s->t[1] = tmp2;
291 *tmp = 0;
292 s->type = LINE_VAR;
293 } else if (*tmp2 == '{') {
294 *tmp = 0;
295 s->type = LINE_BLOCK_START;
296 } else {
297 ERR( "parser: unknown token: '%c'\n", *tmp2);
298 }
299 break;
300 }
301 return s->type;
302 }
303
304 int
305 get_line_as_is(char** fp, line *s)
306 {
307 gchar *tmp, *tmp2;
308
309 ENTER;
310 if (!fp) {
311 s->type = LINE_NONE;
312 RET(s->type);
313 }
314 s->type = LINE_NONE;
315 while (buf_gets(s->str, s->len, fp)) {
316 g_strstrip(s->str);
317 if (s->str[0] == '#' || s->str[0] == 0)
318 continue;
319 DBG( ">> %s\n", s->str);
320 if (!g_ascii_strcasecmp(s->str, "}")) {
321 s->type = LINE_BLOCK_END;
322 DBG( " : line_block_end\n");
323 break;
324 }
325 for (tmp = s->str; isalnum(*tmp); tmp++);
326 for (tmp2 = tmp; isspace(*tmp2); tmp2++);
327 if (*tmp2 == '=') {
328 s->type = LINE_VAR;
329 } else if (*tmp2 == '{') {
330 s->type = LINE_BLOCK_START;
331 } else {
332 DBG( " : ? <%c>\n", *tmp2);
333 }
334 break;
335 }
336 RET(s->type);
337
338 }
339
340 void resolve_atoms()
341 {
342 static const char* atom_names[ N_ATOMS ];
343
344 atom_names[ I_UTF8_STRING ] = "UTF8_STRING";
345 atom_names[ I_XROOTPMAP_ID ] = "_XROOTPMAP_ID";
346 atom_names[ I_WM_STATE ] = "WM_STATE";
347 atom_names[ I_WM_CLASS ] = "WM_CLASS";
348 atom_names[ I_WM_DELETE_WINDOW ] = "WM_DELETE_WINDOW";
349 atom_names[ I_WM_PROTOCOLS ] = "WM_PROTOCOLS";
350 atom_names[ I_NET_WORKAREA ] = "_NET_WORKAREA";
351 atom_names[ I_NET_CLIENT_LIST ] = "_NET_CLIENT_LIST";
352 atom_names[ I_NET_CLIENT_LIST_STACKING ] = "_NET_CLIENT_LIST_STACKING";
353 atom_names[ I_NET_NUMBER_OF_DESKTOPS ] = "_NET_NUMBER_OF_DESKTOPS";
354 atom_names[ I_NET_CURRENT_DESKTOP ] = "_NET_CURRENT_DESKTOP";
355 atom_names[ I_NET_DESKTOP_NAMES ] = "_NET_DESKTOP_NAMES";
356 atom_names[ I_NET_ACTIVE_WINDOW ] = "_NET_ACTIVE_WINDOW";
357 atom_names[ I_NET_SUPPORTED ] = "_NET_SUPPORTED";
358 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
359 atom_names[ I_NET_WM_STATE ] = "_NET_WM_STATE";
360 atom_names[ I_NET_WM_STATE_SKIP_TASKBAR ] = "_NET_WM_STATE_SKIP_TASKBAR";
361 atom_names[ I_NET_WM_STATE_SKIP_PAGER ] = "_NET_WM_STATE_SKIP_PAGER";
362 atom_names[ I_NET_WM_STATE_STICKY ] = "_NET_WM_STATE_STICKY";
363 atom_names[ I_NET_WM_STATE_HIDDEN ] = "_NET_WM_STATE_HIDDEN";
364 atom_names[ I_NET_WM_STATE_SHADED ] = "_NET_WM_STATE_SHADED";
365 atom_names[ I_NET_WM_WINDOW_TYPE ] = "_NET_WM_WINDOW_TYPE";
366
367 atom_names[ I_NET_WM_WINDOW_TYPE_DESKTOP ] = "_NET_WM_WINDOW_TYPE_DESKTOP";
368 atom_names[ I_NET_WM_WINDOW_TYPE_DOCK ] = "_NET_WM_WINDOW_TYPE_DOCK";
369 atom_names[ I_NET_WM_WINDOW_TYPE_TOOLBAR ] = "_NET_WM_WINDOW_TYPE_TOOLBAR";
370 atom_names[ I_NET_WM_WINDOW_TYPE_MENU ] = "_NET_WM_WINDOW_TYPE_MENU";
371 atom_names[ I_NET_WM_WINDOW_TYPE_UTILITY ] = "_NET_WM_WINDOW_TYPE_UTILITY";
372 atom_names[ I_NET_WM_WINDOW_TYPE_SPLASH ] = "_NET_WM_WINDOW_TYPE_SPLASH";
373 atom_names[ I_NET_WM_WINDOW_TYPE_DIALOG ] = "_NET_WM_WINDOW_TYPE_DIALOG";
374 atom_names[ I_NET_WM_WINDOW_TYPE_NORMAL ] = "_NET_WM_WINDOW_TYPE_NORMAL";
375 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
376 atom_names[ I_NET_WM_PID ] = "_NET_WM_PID";
377 atom_names[ I_NET_WM_NAME ] = "_NET_WM_NAME";
378 atom_names[ I_NET_WM_VISIBLE_NAME ] = "_NET_WM_VISIBLE_NAME";
379 atom_names[ I_NET_WM_STRUT ] = "_NET_WM_STRUT";
380 atom_names[ I_NET_WM_STRUT_PARTIAL ] = "_NET_WM_STRUT_PARTIAL";
381 atom_names[ I_NET_WM_ICON ] = "_NET_WM_ICON";
382 atom_names[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ] = "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR";
383 atom_names[ I_LXPANEL_CMD ] = "_LXPANEL_CMD";
384
385 Atom atoms[ N_ATOMS ];
386
387 ENTER;
388
389 if( ! XInternAtoms( GDK_DISPLAY(), (char**)atom_names,
390 N_ATOMS, False, atoms ) )
391 {
392 g_warning( "Error: unable to return Atoms" );
393 return;
394 }
395
396 a_UTF8_STRING = atoms[ I_UTF8_STRING ];
397 a_XROOTPMAP_ID = atoms[ I_XROOTPMAP_ID ];
398 a_WM_STATE = atoms[ I_WM_STATE ];
399 a_WM_CLASS = atoms[ I_WM_CLASS ];
400 a_WM_DELETE_WINDOW = atoms[ I_WM_DELETE_WINDOW ];
401 a_WM_PROTOCOLS = atoms[ I_WM_PROTOCOLS ];
402 a_NET_WORKAREA = atoms[ I_NET_WORKAREA ];
403 a_NET_CLIENT_LIST = atoms[ I_NET_CLIENT_LIST ];
404 a_NET_CLIENT_LIST_STACKING = atoms[ I_NET_CLIENT_LIST_STACKING ];
405 a_NET_NUMBER_OF_DESKTOPS = atoms[ I_NET_NUMBER_OF_DESKTOPS ];
406 a_NET_CURRENT_DESKTOP = atoms[ I_NET_CURRENT_DESKTOP ];
407 a_NET_DESKTOP_NAMES = atoms[ I_NET_DESKTOP_NAMES ];
408 a_NET_ACTIVE_WINDOW = atoms[ I_NET_ACTIVE_WINDOW ];
409 a_NET_SUPPORTED = atoms[ I_NET_SUPPORTED ];
410 a_NET_WM_STATE = atoms[ I_NET_WM_STATE ];
411 a_NET_WM_STATE_SKIP_TASKBAR = atoms[ I_NET_WM_STATE_SKIP_TASKBAR ];
412 a_NET_WM_STATE_SKIP_PAGER = atoms[ I_NET_WM_STATE_SKIP_PAGER ];
413 a_NET_WM_STATE_STICKY = atoms[ I_NET_WM_STATE_STICKY ];
414 a_NET_WM_STATE_HIDDEN = atoms[ I_NET_WM_STATE_HIDDEN ];
415 a_NET_WM_STATE_SHADED = atoms[ I_NET_WM_STATE_SHADED ];
416 a_NET_WM_WINDOW_TYPE = atoms[ I_NET_WM_WINDOW_TYPE ];
417
418 a_NET_WM_WINDOW_TYPE_DESKTOP = atoms[ I_NET_WM_WINDOW_TYPE_DESKTOP ];
419 a_NET_WM_WINDOW_TYPE_DOCK = atoms[ I_NET_WM_WINDOW_TYPE_DOCK ];
420 a_NET_WM_WINDOW_TYPE_TOOLBAR = atoms[ I_NET_WM_WINDOW_TYPE_TOOLBAR ];
421 a_NET_WM_WINDOW_TYPE_MENU = atoms[ I_NET_WM_WINDOW_TYPE_MENU ];
422 a_NET_WM_WINDOW_TYPE_UTILITY = atoms[ I_NET_WM_WINDOW_TYPE_UTILITY ];
423 a_NET_WM_WINDOW_TYPE_SPLASH = atoms[ I_NET_WM_WINDOW_TYPE_SPLASH ];
424 a_NET_WM_WINDOW_TYPE_DIALOG = atoms[ I_NET_WM_WINDOW_TYPE_DIALOG ];
425 a_NET_WM_WINDOW_TYPE_NORMAL = atoms[ I_NET_WM_WINDOW_TYPE_NORMAL ];
426 a_NET_WM_DESKTOP = atoms[ I_NET_WM_DESKTOP ];
427 a_NET_WM_NAME = atoms[ I_NET_WM_NAME ];
428 a_NET_WM_VISIBLE_NAME = atoms[ I_NET_WM_VISIBLE_NAME ];
429 a_NET_WM_STRUT = atoms[ I_NET_WM_STRUT ];
430 a_NET_WM_STRUT_PARTIAL = atoms[ I_NET_WM_STRUT_PARTIAL ];
431 a_NET_WM_ICON = atoms[ I_NET_WM_ICON ];
432 a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR = atoms[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ];
433
434 a_LXPANEL_CMD = atoms[ I_LXPANEL_CMD ];
435
436 RET();
437 }
438
439
440 void
441 Xclimsg(Window win, long type, long l0, long l1, long l2, long l3, long l4)
442 {
443 XClientMessageEvent xev;
444
445 xev.type = ClientMessage;
446 xev.window = win;
447 xev.message_type = type;
448 xev.format = 32;
449 xev.data.l[0] = l0;
450 xev.data.l[1] = l1;
451 xev.data.l[2] = l2;
452 xev.data.l[3] = l3;
453 xev.data.l[4] = l4;
454 XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
455 (SubstructureNotifyMask | SubstructureRedirectMask),
456 (XEvent *) & xev);
457 }
458
459 void
460 Xclimsgwm(Window win, Atom type, Atom arg)
461 {
462 XClientMessageEvent xev;
463
464 xev.type = ClientMessage;
465 xev.window = win;
466 xev.message_type = type;
467 xev.format = 32;
468 xev.data.l[0] = arg;
469 xev.data.l[1] = GDK_CURRENT_TIME;
470 XSendEvent(GDK_DISPLAY(), win, False, 0L, (XEvent *) &xev);
471 }
472
473
474 void *
475 get_utf8_property(Window win, Atom atom)
476 {
477 Atom type;
478 int format;
479 gulong nitems;
480 gulong bytes_after;
481 gchar *val, *retval;
482 int result;
483 guchar *tmp = NULL;
484
485 type = None;
486 retval = NULL;
487 result = XGetWindowProperty (GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
488 a_UTF8_STRING, &type, &format, &nitems,
489 &bytes_after, &tmp);
490 if (result != Success || type == None)
491 return NULL;
492 val = (gchar *) tmp;
493 if (val) {
494 if (type == a_UTF8_STRING && format == 8 && nitems != 0)
495 retval = g_strndup (val, nitems);
496 XFree (val);
497 }
498 return retval;
499
500 }
501
502 char **
503 get_utf8_property_list(Window win, Atom atom, int *count)
504 {
505 Atom type;
506 int format, i;
507 gulong nitems;
508 gulong bytes_after;
509 gchar *s, **retval = NULL;
510 int result;
511 guchar *tmp = NULL;
512
513 *count = 0;
514 result = XGetWindowProperty(GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
515 a_UTF8_STRING, &type, &format, &nitems,
516 &bytes_after, &tmp);
517 if (result != Success || type != a_UTF8_STRING || tmp == NULL)
518 return NULL;
519
520 if (nitems) {
521 gchar *val = (gchar *) tmp;
522 DBG("res=%d(%d) nitems=%d val=%s\n", result, Success, nitems, val);
523 for (i = 0; i < nitems; i++) {
524 if (!val[i])
525 (*count)++;
526 }
527 retval = g_new0 (char*, *count + 2);
528 for (i = 0, s = val; i < *count; i++, s = s + strlen (s) + 1) {
529 retval[i] = g_strdup(s);
530 }
531 if (val[nitems-1]) {
532 result = nitems - (s - val);
533 DBG("val does not ends by 0, moving last %d bytes\n", result);
534 g_memmove(s - 1, s, result);
535 val[nitems-1] = 0;
536 DBG("s=%s\n", s -1);
537 retval[i] = g_strdup(s - 1);
538 (*count)++;
539 }
540 }
541 XFree (tmp);
542
543 return retval;
544
545 }
546
547 void *
548 get_xaproperty (Window win, Atom prop, Atom type, int *nitems)
549 {
550 Atom type_ret;
551 int format_ret;
552 unsigned long items_ret;
553 unsigned long after_ret;
554 unsigned char *prop_data;
555
556 ENTER;
557 prop_data = NULL;
558 if (XGetWindowProperty (GDK_DISPLAY(), win, prop, 0, 0x7fffffff, False,
559 type, &type_ret, &format_ret, &items_ret,
560 &after_ret, &prop_data) != Success)
561 {
562 if( G_UNLIKELY(prop_data) )
563 XFree( prop_data );
564 if( nitems )
565 *nitems = 0;
566 RET(NULL);
567 }
568 if (nitems)
569 *nitems = items_ret;
570 RET(prop_data);
571 }
572
573 static char*
574 text_property_to_utf8 (const XTextProperty *prop)
575 {
576 char **list;
577 int count;
578 char *retval;
579
580 ENTER;
581 list = NULL;
582 count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding),
583 prop->format,
584 prop->value,
585 prop->nitems,
586 &list);
587
588 DBG("count=%d\n", count);
589 if (count == 0)
590 return NULL;
591
592 retval = list[0];
593 list[0] = g_strdup (""); /* something to free */
594
595 g_strfreev (list);
596
597 RET(retval);
598 }
599
600 char *
601 get_textproperty(Window win, Atom atom)
602 {
603 XTextProperty text_prop;
604 char *retval;
605
606 ENTER;
607 if (XGetTextProperty(GDK_DISPLAY(), win, &text_prop, atom)) {
608 DBG("format=%d enc=%d nitems=%d value=%s \n",
609 text_prop.format,
610 text_prop.encoding,
611 text_prop.nitems,
612 text_prop.value);
613 retval = text_property_to_utf8 (&text_prop);
614 if (text_prop.nitems > 0)
615 XFree (text_prop.value);
616 RET(retval);
617
618 }
619 RET(NULL);
620 }
621
622
623 int
624 get_net_number_of_desktops()
625 {
626 int desknum;
627 guint32 *data;
628
629 ENTER;
630 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_NUMBER_OF_DESKTOPS,
631 XA_CARDINAL, 0);
632 if (!data)
633 RET(0);
634
635 desknum = *data;
636 XFree (data);
637 RET(desknum);
638 }
639
640
641 int
642 get_net_current_desktop ()
643 {
644 int desk;
645 guint32 *data;
646
647 ENTER;
648 data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, XA_CARDINAL, 0);
649 if (!data)
650 RET(0);
651
652 desk = *data;
653 XFree (data);
654 RET(desk);
655 }
656
657 int
658 get_net_wm_desktop(Window win)
659 {
660 int desk = 0;
661 guint32 *data;
662
663 ENTER;
664 data = get_xaproperty (win, a_NET_WM_DESKTOP, XA_CARDINAL, 0);
665 if (data) {
666 desk = *data;
667 XFree (data);
668 }
669 RET(desk);
670 }
671
672 GPid
673 get_net_wm_pid(Window win)
674 {
675 GPid pid = 0;
676 guint32 *data;
677
678 ENTER;
679 data = get_xaproperty (win, a_NET_WM_PID, XA_CARDINAL, 0);
680 if (data) {
681 pid = *data;
682 XFree (data);
683 }
684 RET(pid);
685 }
686
687 void
688 get_net_wm_state(Window win, NetWMState *nws)
689 {
690 Atom *state;
691 int num3;
692
693
694 ENTER;
695 bzero(nws, sizeof(nws));
696 if (!(state = get_xaproperty(win, a_NET_WM_STATE, XA_ATOM, &num3)))
697 RET();
698
699 DBG( "%x: netwm state = { ", (unsigned int)win);
700 while (--num3 >= 0) {
701 if (state[num3] == a_NET_WM_STATE_SKIP_PAGER) {
702 DBG("NET_WM_STATE_SKIP_PAGER ");
703 nws->skip_pager = 1;
704 } else if (state[num3] == a_NET_WM_STATE_SKIP_TASKBAR) {
705 DBG( "NET_WM_STATE_SKIP_TASKBAR ");
706 nws->skip_taskbar = 1;
707 } else if (state[num3] == a_NET_WM_STATE_STICKY) {
708 DBG( "NET_WM_STATE_STICKY ");
709 nws->sticky = 1;
710 } else if (state[num3] == a_NET_WM_STATE_HIDDEN) {
711 DBG( "NET_WM_STATE_HIDDEN ");
712 nws->hidden = 1;
713 } else if (state[num3] == a_NET_WM_STATE_SHADED) {
714 DBG( "NET_WM_STATE_SHADED ");
715 nws->shaded = 1;
716 } else {
717 DBG( "... ");
718 }
719 }
720 XFree(state);
721 DBG( "}\n");
722 RET();
723 }
724
725 void
726 get_net_wm_window_type(Window win, NetWMWindowType *nwwt)
727 {
728 Atom *state;
729 int num3;
730
731
732 ENTER;
733 bzero(nwwt, sizeof(nwwt));
734 if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3)))
735 RET();
736
737 DBG( "%x: netwm state = { ", (unsigned int)win);
738 while (--num3 >= 0) {
739 if (state[num3] == a_NET_WM_WINDOW_TYPE_DESKTOP) {
740 DBG("NET_WM_WINDOW_TYPE_DESKTOP ");
741 nwwt->desktop = 1;
742 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DOCK) {
743 DBG( "NET_WM_WINDOW_TYPE_DOCK ");
744 nwwt->dock = 1;
745 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_TOOLBAR) {
746 DBG( "NET_WM_WINDOW_TYPE_TOOLBAR ");
747 nwwt->toolbar = 1;
748 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_MENU) {
749 DBG( "NET_WM_WINDOW_TYPE_MENU ");
750 nwwt->menu = 1;
751 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_UTILITY) {
752 DBG( "NET_WM_WINDOW_TYPE_UTILITY ");
753 nwwt->utility = 1;
754 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_SPLASH) {
755 DBG( "NET_WM_WINDOW_TYPE_SPLASH ");
756 nwwt->splash = 1;
757 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DIALOG) {
758 DBG( "NET_WM_WINDOW_TYPE_DIALOG ");
759 nwwt->dialog = 1;
760 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_NORMAL) {
761 DBG( "NET_WM_WINDOW_TYPE_NORMAL ");
762 nwwt->normal = 1;
763 } else {
764 DBG( "... ");
765 }
766 }
767 XFree(state);
768 DBG( "}\n");
769 RET();
770 }
771
772 int
773 get_wm_state (Window win)
774 {
775 unsigned long *data;
776 int ret = 0;
777
778 ENTER;
779 data = get_xaproperty (win, a_WM_STATE, a_WM_STATE, 0);
780 if (data) {
781 ret = data[0];
782 XFree (data);
783 }
784 RET(ret);
785 }
786
787 static void
788 calculate_width(int scrw, int wtype, int allign, int margin,
789 int *panw, int *x)
790 {
791 ENTER;
792 DBG("scrw=%d\n", scrw);
793 DBG("IN panw=%d\n", *panw);
794 //scrw -= 2;
795 if (wtype == WIDTH_PERCENT) {
796 /* sanity check */
797 if (*panw > 100)
798 *panw = 100;
799 else if (*panw < 0)
800 *panw = 1;
801 *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0;
802 }
803 if (allign != ALLIGN_CENTER) {
804 if (margin > scrw) {
805 ERR( "margin is bigger then edge size %d > %d. Ignoring margin\n",
806 margin, scrw);
807 margin = 0;
808 }
809 if (wtype == WIDTH_PERCENT)
810 //*panw = MAX(scrw - margin, *panw);
811 ;
812 else
813 *panw = MIN(scrw - margin, *panw);
814 }
815 DBG("OUT panw=%d\n", *panw);
816 if (allign == ALLIGN_LEFT)
817 *x += margin;
818 else if (allign == ALLIGN_RIGHT) {
819 *x += scrw - *panw - margin;
820 if (*x < 0)
821 *x = 0;
822 } else if (allign == ALLIGN_CENTER)
823 *x += (scrw - *panw) / 2;
824 RET();
825 }
826
827
828 void
829 calculate_position(Panel *np)
830 {
831 int sswidth, ssheight, minx, miny;
832
833 ENTER;
834 /* FIXME: Why this doesn't work? */
835 if (0) {
836 // if (np->curdesk < np->wa_len/4) {
837 minx = np->workarea[np->curdesk*4 + 0];
838 miny = np->workarea[np->curdesk*4 + 1];
839 sswidth = np->workarea[np->curdesk*4 + 2];
840 ssheight = np->workarea[np->curdesk*4 + 3];
841 } else {
842 minx = miny = 0;
843 sswidth = gdk_screen_get_width( gtk_widget_get_screen(np->topgwin) );
844 ssheight = gdk_screen_get_height( gtk_widget_get_screen(np->topgwin) );
845 }
846
847 if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
848 np->aw = np->width;
849 np->ax = minx;
850 calculate_width(sswidth, np->widthtype, np->allign, np->margin,
851 &np->aw, &np->ax);
852 np->ah = np->height;
853 np->ah = MIN(PANEL_HEIGHT_MAX, np->ah);
854 np->ah = MAX(PANEL_HEIGHT_MIN, np->ah);
855 np->ay = miny + ((np->edge == EDGE_TOP) ? 0 : (ssheight - np->ah));
856
857 } else {
858 np->ah = np->width;
859 np->ay = miny;
860 calculate_width(ssheight, np->widthtype, np->allign, np->margin,
861 &np->ah, &np->ay);
862 np->aw = np->height;
863 np->aw = MIN(PANEL_HEIGHT_MAX, np->aw);
864 np->aw = MAX(PANEL_HEIGHT_MIN, np->aw);
865 np->ax = minx + ((np->edge == EDGE_LEFT) ? 0 : (sswidth - np->aw));
866 }
867 //g_debug("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah);
868 RET();
869 }
870
871 gchar *
872 expand_tilda(gchar *file)
873 {
874 ENTER;
875 RET((file[0] == '~') ?
876 g_strdup_printf("%s%s", getenv("HOME"), file+1)
877 : g_strdup(file));
878
879 }
880
881 #if 0
882 Window
883 Select_Window(Display *dpy)
884 {
885 int status;
886 Cursor cursor;
887 XEvent event;
888 Window target_win = None, root = RootWindow(dpy,DefaultScreen(dpy));
889 int buttons = 0;
890
891 ENTER;
892 /* Make the target cursor */
893 cursor = XCreateFontCursor(dpy, XC_crosshair);
894
895 /* Grab the pointer using target cursor, letting it room all over */
896 status = XGrabPointer(dpy, root, False,
897 ButtonPressMask|ButtonReleaseMask, GrabModeSync,
898 GrabModeAsync, root, cursor, CurrentTime);
899 if (status != GrabSuccess) {
900 ERR("Can't grab the mouse.");
901 RET(None);
902 }
903 /* Let the user select a window... */
904 while ((target_win == None) || (buttons != 0)) {
905 /* allow one more event */
906 XAllowEvents(dpy, SyncPointer, CurrentTime);
907 XWindowEvent(dpy, root, ButtonPressMask|ButtonReleaseMask, &event);
908 switch (event.type) {
909 case ButtonPress:
910 if (target_win == None) {
911 target_win = event.xbutton.subwindow; /* window selected */
912 DBG("target win = 0x%x\n", target_win);
913 if (target_win == None) target_win = root;
914 }
915 buttons++;
916 break;
917 case ButtonRelease:
918 if (buttons > 0) /* there may have been some down before we started */
919 buttons--;
920 break;
921 }
922 }
923
924 XUngrabPointer(dpy, CurrentTime); /* Done with pointer */
925 RET(target_win);
926 }
927 #endif
928
929 /*
930 * SuxPanel version 0.1
931 * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
932 *
933 * This program may be distributed under the terms of GNU General
934 * Public License version 2. You should have received a copy of the
935 * license with this program; if not, please consult http://www.fsf.org/.
936 *
937 * This program comes with no warranty. Use at your own risk.
938 *
939 */
940
941 GdkPixbuf *
942 gdk_pixbuf_scale_ratio(GdkPixbuf *p, int width, int height, GdkInterpType itype, gboolean keep_ratio)
943 {
944 gfloat w, h, rw, rh;
945
946 if (keep_ratio) {
947 w = gdk_pixbuf_get_width(p);
948 h = gdk_pixbuf_get_height(p);
949 rw = w / width;
950 rh = h / height;
951 if (rw > rh)
952 height = h / rw;
953 else
954 width = w / rh;
955 }
956 return gdk_pixbuf_scale_simple(p, width, height, itype);
957
958 }
959
960 void img_data_free( ImgData* data )
961 {
962 g_free( data->fname );
963 if( data->theme_changed_handler )
964 g_signal_handler_disconnect( gtk_icon_theme_get_default(), data->theme_changed_handler );
965 if( data->pixbuf )
966 g_object_unref( data->pixbuf );
967 if( data->hilight )
968 g_object_unref( data->hilight );
969 g_slice_free( ImgData, data );
970 }
971
972 static void on_theme_changed(GtkIconTheme* theme, GtkWidget* img)
973 {
974 ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(img), img_data_id );
975 /* g_debug("reload icon: %s", data->fname); */
976 _gtk_image_set_from_file_scaled(img, data->fname,
977 data->dw, data->dh, data->keep_ratio );
978 }
979
980 void fb_button_set_from_file(GtkWidget* btn, const char* img_file)
981 {
982 GtkWidget* child = gtk_bin_get_child(GTK_BIN(btn));
983 GtkWidget* img = NULL;
984
985 if( GTK_IS_IMAGE(child) )
986 img = child;
987 else if( GTK_IS_BOX(child) )
988 {
989 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
990 img = GTK_WIDGET(GTK_IMAGE(children->data));
991 g_list_free( children );
992 }
993
994 if(G_LIKELY(img))
995 {
996 ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(img), img_data_id );
997 g_free(data->fname);
998 data->fname = g_strdup(img_file);
999 _gtk_image_set_from_file_scaled(img, data->fname,
1000 data->dw, data->dh, data->keep_ratio );
1001 }
1002 }
1003
1004 /* FIXME: currently, the size of those images cannot be changed dynamically */
1005 static void on_img_size_allocated(GtkWidget* img, GtkAllocation *allocation, ImgData* data)
1006 {
1007 int size;
1008
1009 /* enlarge */
1010 if( allocation->width > data->dw ||
1011 allocation->height > data->dh )
1012 {
1013 size = MAX(allocation->width, allocation->height);
1014 }
1015 /* shrinkage */
1016 else if( allocation->width < data->dw ||
1017 allocation->height < data->dh )
1018 {
1019 size = MIN(allocation->width, allocation->height);
1020 }
1021 else
1022 return;
1023 data->dw = data->dh = size;
1024 /*
1025 g_debug("size = %d, pix: %d, %d, alloc:%d, %d", size,
1026 gdk_pixbuf_get_width(data->pixbuf), gdk_pixbuf_get_height(data->pixbuf),
1027 allocation->width, allocation->height );
1028 */
1029 g_signal_handlers_block_by_func( img, on_img_size_allocated, data );
1030
1031 if (gdk_pixbuf_get_width(data->pixbuf)!=gdk_pixbuf_get_height(data->pixbuf)) {
1032 gtk_widget_set_size_request(img, allocation->width, allocation->height);
1033 _gtk_image_set_from_file_scaled( img, data->fname,
1034 allocation->width, allocation->height, data->keep_ratio );
1035 } else {
1036 gtk_widget_set_size_request(img, size, size);
1037 _gtk_image_set_from_file_scaled( img, data->fname,
1038 size, size, data->keep_ratio );
1039 }
1040 // g_debug("size-allocated: %d, %d", allocation->width, allocation->height);
1041
1042 #if 0
1043 /* FIXME: This is a very bad dirty hack! */
1044 if( gtk_events_pending() )
1045 gtk_main_iteration();
1046 #endif
1047 g_signal_handlers_unblock_by_func( img, on_img_size_allocated, data );
1048 }
1049
1050 void
1051 _gtk_image_set_from_file_scaled( GtkWidget* img, const gchar *file, gint width,
1052 gint height, gboolean keep_ratio)
1053 {
1054 GdkPixbuf *pb_scaled;
1055 gboolean themed = TRUE;
1056 ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(img), img_data_id );
1057
1058 data->dw = width;
1059 data->dh = height;
1060
1061 if( data->pixbuf )
1062 {
1063 g_object_unref( data->pixbuf );
1064 data->pixbuf = NULL;
1065 }
1066 /* if there is a cached hilighted version of this pixbuf, free it */
1067 if( data->hilight )
1068 {
1069 g_object_unref( data->hilight );
1070 data->hilight = NULL;
1071 }
1072
1073 /* if they are the same string, eliminate unnecessary copy. */
1074 if( data->fname != file )
1075 {
1076 g_free( data->fname );
1077 data->fname = g_strdup(file);
1078 }
1079 data->keep_ratio = TRUE;
1080
1081 if( G_UNLIKELY( ! file ) )
1082 goto err;
1083
1084 if( g_file_test(file, G_FILE_TEST_EXISTS) )
1085 {
1086 pb_scaled = gdk_pixbuf_new_from_file_at_scale( file, width, height,
1087 keep_ratio, NULL );
1088 if( !pb_scaled )
1089 goto err;
1090 data->pixbuf = pb_scaled;
1091 themed = FALSE;
1092 }
1093 else
1094 {
1095 data->pixbuf = lxpanel_load_icon(file, MAX(width, height),TRUE);
1096 if( ! data->pixbuf )
1097 goto err;
1098 }
1099 gtk_image_set_from_pixbuf((GtkImage *)img, data->pixbuf);
1100
1101 if( themed ) /* This image is loaded from icon theme */
1102 {
1103 /* update the image when icon theme get changed */
1104 if( ! data->theme_changed_handler )
1105 {
1106 data->theme_changed_handler = g_signal_connect( gtk_icon_theme_get_default(), "changed",
1107 G_CALLBACK(on_theme_changed), img );
1108 }
1109 }
1110 else /* this is not loaded from icon theme */
1111 {
1112 if( data->theme_changed_handler )
1113 {
1114 g_signal_handler_disconnect( gtk_icon_theme_get_default(), data->theme_changed_handler );
1115 data->theme_changed_handler = 0;
1116 }
1117 }
1118 return;
1119
1120 err:
1121 gtk_image_set_from_stock((GtkImage *)img, GTK_STOCK_MISSING_IMAGE,
1122 GTK_ICON_SIZE_BUTTON);
1123 }
1124
1125 GtkWidget *
1126 _gtk_image_new_from_file_scaled(const gchar *file, gint width,
1127 gint height, gboolean keep_ratio)
1128 {
1129 GtkWidget *img;
1130 ImgData* data;
1131
1132 img = gtk_image_new();
1133 data = g_slice_new0( ImgData );
1134 if( G_UNLIKELY( 0 == img_data_id ) )
1135 img_data_id = g_quark_from_static_string("ImgData");
1136 g_object_set_qdata_full( G_OBJECT(img), img_data_id, data,
1137 (GDestroyNotify)img_data_free );
1138 _gtk_image_set_from_file_scaled( img, file, width, height, keep_ratio );
1139 // gtk_widget_set_size_request(img, width, height);
1140 g_signal_connect( img, "size-allocate", G_CALLBACK(on_img_size_allocated), data );
1141 return img;
1142 }
1143
1144
1145 void
1146 get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
1147 {
1148 GtkWidget *b;
1149 //gint focus_width;
1150 //gint focus_pad;
1151
1152 ENTER;
1153 b = gtk_button_new();
1154 gtk_widget_set_name(GTK_WIDGET(b), name);
1155 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1156 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_DEFAULT);
1157 gtk_container_set_border_width (GTK_CONTAINER (b), 0);
1158
1159 if (parent)
1160 gtk_container_add(parent, b);
1161
1162 gtk_widget_show(b);
1163 gtk_widget_size_request(b, req);
1164
1165 gtk_widget_destroy(b);
1166 RET();
1167 }
1168
1169
1170 guint32 gcolor2rgb24(GdkColor *color)
1171 {
1172 guint32 i;
1173 guint16 r, g, b;
1174
1175 ENTER;
1176
1177 r = color->red * 0xFF / 0xFFFF;
1178 g = color->green * 0xFF / 0xFFFF;
1179 b = color->blue * 0xFF / 0xFFFF;
1180 DBG("%x %x %x ==> %x %x %x\n", color->red, color->green, color->blue, r, g, b);
1181
1182 i = (color->red * 0xFF / 0xFFFF) & 0xFF;
1183 i <<= 8;
1184 i |= (color->green * 0xFF / 0xFFFF) & 0xFF;
1185 i <<= 8;
1186 i |= (color->blue * 0xFF / 0xFFFF) & 0xFF;
1187 DBG("i=%x\n", i);
1188 RET(i);
1189 }
1190
1191
1192 static gboolean
1193 fb_button_enter (GtkImage *widget, GdkEventCrossing *event)
1194 {
1195 GdkPixbuf *dark, *light;
1196 int i, height, rowstride;
1197 gulong hicolor;
1198 guchar *src, *up, extra[3];
1199 ImgData* data;
1200
1201 if (gtk_image_get_storage_type(widget) != GTK_IMAGE_PIXBUF)
1202 return TRUE;
1203
1204 data = (ImgData*)g_object_get_qdata( G_OBJECT(widget), img_data_id );
1205 if( G_UNLIKELY( ! data ) )
1206 return TRUE;
1207
1208 if( ! data->hilight )
1209 {
1210 dark = data->pixbuf;
1211 height = gdk_pixbuf_get_height( dark );
1212 rowstride = gdk_pixbuf_get_rowstride( dark );
1213 hicolor = data->hicolor;
1214
1215 light = gdk_pixbuf_add_alpha(dark, FALSE, 0, 0, 0);
1216 if( !light )
1217 return TRUE;
1218 src = gdk_pixbuf_get_pixels(light);
1219 for (i = 2; i >= 0; i--, hicolor >>= 8)
1220 extra[i] = hicolor & 0xFF;
1221 for( up = src + height * rowstride; src < up; src+=4 )
1222 {
1223 if (src[3] == 0)
1224 continue;
1225 for (i = 0; i < 3; i++)
1226 {
1227 if (src[i] + extra[i] >= 255)
1228 src[i] = 255;
1229 else
1230 src[i] += extra[i];
1231 }
1232 }
1233 data->hilight = light;
1234 }
1235
1236 if( G_LIKELY( data->hilight ) )
1237 gtk_image_set_from_pixbuf(widget, data->hilight);
1238 return TRUE;
1239 }
1240
1241 static gboolean
1242 fb_button_leave (GtkImage *widget, GdkEventCrossing *event, gpointer user_data)
1243 {
1244 ImgData* data;
1245
1246 if (gtk_image_get_storage_type(widget) != GTK_IMAGE_PIXBUF)
1247 return TRUE;
1248
1249 data = (ImgData*)g_object_get_qdata( G_OBJECT(widget), img_data_id );
1250 if( data && data->pixbuf )
1251 gtk_image_set_from_pixbuf(widget, data->pixbuf);
1252
1253 return TRUE;
1254 }
1255
1256
1257 GtkWidget *
1258 fb_button_new_from_file(gchar *fname, int width, int height, gulong hicolor, gboolean keep_ratio)
1259 {
1260 GtkWidget *b, *image;
1261 ENTER;
1262 b = gtk_event_box_new();
1263 gtk_container_set_border_width(GTK_CONTAINER(b), 0);
1264 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1265
1266 image = _gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
1267 gtk_misc_set_padding (GTK_MISC(image), 0, 0);
1268 gtk_misc_set_alignment( (GtkMisc*)image, 0, 0 );
1269
1270 if(hicolor > 0)
1271 {
1272 ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(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_container_add(GTK_CONTAINER(b), image);
1282 gtk_widget_show(image);
1283 gtk_widget_show(b);
1284 RET(b);
1285 }
1286
1287 GtkWidget *
1288 fb_button_new_from_file_with_colorlabel(gchar *fname, int width, int height,
1289 gulong hicolor, gulong fcolor, gboolean keep_ratio, gchar *name)
1290 {
1291 GtkWidget *b, *image, *box, *label;
1292
1293 ENTER;
1294 b = gtk_event_box_new();
1295 gtk_container_set_border_width(GTK_CONTAINER(b), 0);
1296 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1297
1298 box = gtk_hbox_new(FALSE, 0);
1299 gtk_container_set_border_width(GTK_CONTAINER(box), 0);
1300 GTK_WIDGET_UNSET_FLAGS (box, GTK_CAN_FOCUS);
1301 gtk_container_add(GTK_CONTAINER(b), box);
1302
1303 image = _gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
1304 gtk_misc_set_padding (GTK_MISC(image), 0, 0);
1305 if(hicolor > 0)
1306 {
1307 ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(image), img_data_id );
1308 data->hicolor = hicolor;
1309
1310 gtk_widget_add_events(b, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1311 g_signal_connect_swapped (G_OBJECT (b), "enter-notify-event",
1312 G_CALLBACK (fb_button_enter), image);
1313 g_signal_connect_swapped (G_OBJECT (b), "leave-notify-event",
1314 G_CALLBACK (fb_button_leave), image);
1315 }
1316 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
1317 if (name) {
1318 label = gtk_label_new("");
1319 char *lname = g_strdup_printf("<span color=\"#%06x\">%s</span>", fcolor, name);
1320 gtk_label_set_markup(GTK_LABEL(label), lname);
1321 gtk_misc_set_padding(GTK_MISC(label), 2, 0);
1322 g_free(lname);
1323 gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 0);
1324 }
1325 gtk_widget_show_all(b);
1326 RET(b);
1327 }
1328
1329 GtkWidget *
1330 fb_button_new_from_file_with_label(gchar *fname, int width, int height,
1331 gulong hicolor, gboolean keep_ratio, gchar *name)
1332 {
1333 GtkWidget *b, *image, *box, *label;
1334
1335 ENTER;
1336 b = gtk_event_box_new();
1337 gtk_container_set_border_width(GTK_CONTAINER(b), 0);
1338 GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
1339
1340 box = gtk_hbox_new(FALSE, 0);
1341 gtk_container_set_border_width(GTK_CONTAINER(box), 0);
1342 GTK_WIDGET_UNSET_FLAGS (box, GTK_CAN_FOCUS);
1343 gtk_container_add(GTK_CONTAINER(b), box);
1344
1345 image = _gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
1346 gtk_misc_set_padding (GTK_MISC(image), 0, 0);
1347 if(hicolor > 0)
1348 {
1349 ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(image), img_data_id );
1350 data->hicolor = hicolor;
1351
1352 gtk_widget_add_events(b, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1353 g_signal_connect_swapped (G_OBJECT (b), "enter-notify-event",
1354 G_CALLBACK (fb_button_enter), image);
1355 g_signal_connect_swapped (G_OBJECT (b), "leave-notify-event",
1356 G_CALLBACK (fb_button_leave), image);
1357 }
1358 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
1359 if (name) {
1360 label = gtk_label_new(name);
1361 gtk_misc_set_padding(GTK_MISC(label), 2, 0);
1362 gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 0);
1363 }
1364 gtk_widget_show_all(b);
1365 RET(b);
1366 }
1367
1368 char* translate_exec_to_cmd( const char* exec, const char* icon,
1369 const char* title, const char* fpath )
1370 {
1371 GString* cmd = g_string_sized_new( 256 );
1372 for( ; *exec; ++exec )
1373 {
1374 if( G_UNLIKELY(*exec == '%') )
1375 {
1376 ++exec;
1377 if( !*exec )
1378 break;
1379 switch( *exec )
1380 {
1381 case 'c':
1382 g_string_append( cmd, title );
1383 break;
1384 case 'i':
1385 if( icon )
1386 {
1387 g_string_append( cmd, "--icon " );
1388 g_string_append( cmd, icon );
1389 }
1390 break;
1391 case 'k':
1392 {
1393 char* uri = g_filename_to_uri( fpath, NULL, NULL );
1394 g_string_append( cmd, uri );
1395 g_free( uri );
1396 break;
1397 }
1398 case '%':
1399 g_string_append_c( cmd, '%' );
1400 break;
1401 }
1402 }
1403 else
1404 g_string_append_c( cmd, *exec );
1405 }
1406 return g_string_free( cmd, FALSE );
1407 }
1408
1409 /*
1410 This function is used to re-create a new box with different
1411 orientation from the old one, add all children of the old one to
1412 the new one, and then destroy the old box.
1413 It's mainly used when we need to change the orientation of the panel or
1414 any plugin with a layout box. Since GtkHBox cannot be changed to GtkVBox,
1415 recreating a new box to replace the old one is required.
1416 */
1417 GtkWidget* recreate_box( GtkBox* oldbox, GtkOrientation orientation )
1418 {
1419 GtkBox* newbox;
1420 GList *child, *children;
1421 GtkWidget* (*my_box_new) (gboolean homogeneous, gint spacing);
1422
1423 if( GTK_IS_HBOX(oldbox) ) {
1424 if( orientation == GTK_ORIENTATION_HORIZONTAL )
1425 return GTK_WIDGET(oldbox);
1426 }
1427 else {
1428 if( orientation == GTK_ORIENTATION_VERTICAL )
1429 return GTK_WIDGET(oldbox);
1430 }
1431 my_box_new = (orientation == GTK_ORIENTATION_HORIZONTAL ? gtk_hbox_new : gtk_vbox_new);
1432
1433 newbox = GTK_BOX(my_box_new( gtk_box_get_homogeneous(oldbox),
1434 gtk_box_get_spacing(oldbox) ));
1435 gtk_container_set_border_width (GTK_CONTAINER (newbox),
1436 gtk_container_get_border_width(GTK_CONTAINER(oldbox)) );
1437 children = gtk_container_get_children( GTK_CONTAINER (oldbox) );
1438 for( child = children; child; child = child->next ) {
1439 gboolean expand, fill;
1440 guint padding;
1441 GtkWidget* w = GTK_WIDGET(child->data);
1442 gtk_box_query_child_packing( oldbox, w,
1443 &expand, &fill, &padding, NULL );
1444 /* g_debug( "repack %s, expand=%d, fill=%d", gtk_widget_get_name(w), expand, fill ); */
1445 g_object_ref( w );
1446 gtk_container_remove( GTK_CONTAINER (oldbox), w );
1447 gtk_box_pack_start( newbox, w, expand, fill, padding );
1448 g_object_unref( w );
1449 }
1450 g_list_free( children );
1451 gtk_widget_show_all( GTK_WIDGET(newbox) );
1452 gtk_widget_destroy( GTK_WIDGET(oldbox) );
1453 return GTK_WIDGET(newbox);
1454 }
1455
1456 void show_error( GtkWindow* parent_win, const char* msg )
1457 {
1458 GtkWidget* dlg = gtk_message_dialog_new( parent_win,
1459 GTK_DIALOG_MODAL,
1460 GTK_MESSAGE_ERROR,
1461 GTK_BUTTONS_OK, msg );
1462 gtk_dialog_run( (GtkDialog*)dlg );
1463 gtk_widget_destroy( dlg );
1464 }
1465
1466 static GdkPixbuf* load_icon_file( const char* file_name, int size )
1467 {
1468 GdkPixbuf* icon = NULL;
1469 char* file_path;
1470 const gchar** dirs = (const gchar**) g_get_system_data_dirs();
1471 const gchar** dir;
1472 for( dir = dirs; *dir; ++dir )
1473 {
1474 file_path = g_build_filename( *dir, "pixmaps", file_name, NULL );
1475 icon = gdk_pixbuf_new_from_file_at_scale( file_path, size, size, TRUE, NULL );
1476 g_free( file_path );
1477 if( icon )
1478 break;
1479 }
1480 return icon;
1481 }
1482
1483 static GdkPixbuf* vfs_load_icon( GtkIconTheme* theme, const char* icon_name, int size )
1484 {
1485 GdkPixbuf* icon = NULL;
1486 const char* file;
1487 GtkIconInfo* inf = gtk_icon_theme_lookup_icon( theme, icon_name, size,
1488 GTK_ICON_LOOKUP_USE_BUILTIN );
1489 if( G_UNLIKELY( ! inf ) )
1490 return NULL;
1491 file = gtk_icon_info_get_filename( inf );
1492 if( G_LIKELY( file ) )
1493 {
1494 /* icon = gdk_pixbuf_new_from_file_at_scale( file, size, size, TRUE, NULL ); */
1495 icon = gdk_pixbuf_new_from_file( file, NULL );
1496 }
1497 else
1498 icon = gtk_icon_info_get_builtin_pixbuf( inf );
1499 gtk_icon_info_free( inf );
1500
1501 if( G_LIKELY( icon ) ) /* scale down the icon if it's too big */
1502 {
1503 int width, height;
1504 height = gdk_pixbuf_get_height(icon);
1505 width = gdk_pixbuf_get_width(icon);
1506 if( G_UNLIKELY( height > size || width > size ) )
1507 {
1508 GdkPixbuf* scaled;
1509 if( height > width )
1510 {
1511 width = size * height / width;
1512 height = size;
1513 }
1514 else if( height < width )
1515 {
1516 height = size * width / height;
1517 width = size;
1518 }
1519 else
1520 height = width = size;
1521 scaled = gdk_pixbuf_scale_simple( icon, width, height, GDK_INTERP_BILINEAR );
1522 g_object_unref( icon );
1523 icon = scaled;
1524 }
1525 }
1526 return icon;
1527 }
1528
1529 GdkPixbuf* lxpanel_load_icon( const char* name, int size, gboolean use_fallback )
1530 {
1531 GtkIconTheme* theme;
1532 char *icon_name = NULL, *suffix;
1533 GdkPixbuf* icon = NULL;
1534 if( name )
1535 {
1536 if( g_path_is_absolute( name) )
1537 {
1538 icon = gdk_pixbuf_new_from_file_at_scale( name,
1539 size, size, TRUE, NULL );
1540 }
1541 else
1542 {
1543 theme = gtk_icon_theme_get_default();
1544 suffix = strchr( name, '.' );
1545 if( suffix
1546 && (0 == g_strcasecmp(++suffix, "png")
1547 || 0 == g_strcasecmp(suffix, "svg")
1548 || 0 == g_strcasecmp(suffix, "xpm")) ) /* has file extension, it's a basename of icon file */
1549 {
1550 /* try to find it in pixmaps dirs */
1551 icon = load_icon_file( name, size );
1552 if( G_UNLIKELY( ! icon ) ) /* unfortunately, not found */
1553 {
1554 /* Let's remove the suffix, and see if this name can match an icon
1555 in current icon theme */
1556 icon_name = g_strndup( name,
1557 (suffix - name - 1) );
1558 icon = vfs_load_icon( theme, icon_name, size );
1559 g_free( icon_name );
1560 }
1561 }
1562 else /* no file extension, it could be an icon name in the icon theme */
1563 {
1564 icon = vfs_load_icon( theme, name, size );
1565 }
1566 }
1567 }
1568 if( G_UNLIKELY( ! icon ) && use_fallback ) /* fallback to generic icon */
1569 {
1570 theme = gtk_icon_theme_get_default();
1571 icon = vfs_load_icon( theme, "application-x-executable", size );
1572 if( G_UNLIKELY( ! icon ) ) /* fallback to generic icon */
1573 {
1574 icon = vfs_load_icon( theme, "gnome-mime-application-x-executable", size );
1575 }
1576 }
1577 return icon;
1578 }
1579
1580 /*
1581 * Taken from pcmanfm:
1582 * Parse Exec command line of app desktop file, and translate
1583 * it into a real command which can be passed to g_spawn_command_line_async().
1584 * file_list is a null-terminated file list containing full
1585 * paths of the files passed to app.
1586 * returned char* should be freed when no longer needed.
1587 */
1588 static char* translate_app_exec_to_command_line( const char* pexec,
1589 GList* file_list )
1590 {
1591 char* file;
1592 GList* l;
1593 gchar *tmp;
1594 GString* cmd = g_string_new("");
1595 gboolean add_files = FALSE;
1596
1597 for( ; *pexec; ++pexec )
1598 {
1599 if( *pexec == '%' )
1600 {
1601 ++pexec;
1602 switch( *pexec )
1603 {
1604 case 'U':
1605 for( l = file_list; l; l = l->next )
1606 {
1607 tmp = g_filename_to_uri( (char*)l->data, NULL, NULL );
1608 file = g_shell_quote( tmp );
1609 g_free( tmp );
1610 g_string_append( cmd, file );
1611 g_string_append_c( cmd, ' ' );
1612 g_free( file );
1613 }
1614 add_files = TRUE;
1615 break;
1616 case 'u':
1617 if( file_list && file_list->data )
1618 {
1619 file = (char*)file_list->data;
1620 tmp = g_filename_to_uri( file, NULL, NULL );
1621 file = g_shell_quote( tmp );
1622 g_free( tmp );
1623 g_string_append( cmd, file );
1624 g_free( file );
1625 add_files = TRUE;
1626 }
1627 break;
1628 case 'F':
1629 case 'N':
1630 for( l = file_list; l; l = l->next )
1631 {
1632 file = (char*)l->data;
1633 tmp = g_shell_quote( file );
1634 g_string_append( cmd, tmp );
1635 g_string_append_c( cmd, ' ' );
1636 g_free( tmp );
1637 }
1638 add_files = TRUE;
1639 break;
1640 case 'f':
1641 case 'n':
1642 if( file_list && file_list->data )
1643 {
1644 file = (char*)file_list->data;
1645 tmp = g_shell_quote( file );
1646 g_string_append( cmd, tmp );
1647 g_free( tmp );
1648 add_files = TRUE;
1649 }
1650 break;
1651 case 'D':
1652 for( l = file_list; l; l = l->next )
1653 {
1654 tmp = g_path_get_dirname( (char*)l->data );
1655 file = g_shell_quote( tmp );
1656 g_free( tmp );
1657 g_string_append( cmd, file );
1658 g_string_append_c( cmd, ' ' );
1659 g_free( file );
1660 }
1661 add_files = TRUE;
1662 break;
1663 case 'd':
1664 if( file_list && file_list->data )
1665 {
1666 tmp = g_path_get_dirname( (char*)file_list->data );
1667 file = g_shell_quote( tmp );
1668 g_free( tmp );
1669 g_string_append( cmd, file );
1670 g_free( tmp );
1671 add_files = TRUE;
1672 }
1673 break;
1674 case 'c':
1675 #if 0
1676 g_string_append( cmd, vfs_app_desktop_get_disp_name( app ) );
1677 #endif
1678 break;
1679 case 'i':
1680 /* Add icon name */
1681 #if 0
1682 if( vfs_app_desktop_get_icon_name( app ) )
1683 {
1684 g_string_append( cmd, "--icon " );
1685 g_string_append( cmd, vfs_app_desktop_get_icon_name( app ) );
1686 }
1687 #endif
1688 break;
1689 case 'k':
1690 /* Location of the desktop file */
1691 break;
1692 case 'v':
1693 /* Device name */
1694 break;
1695 case '%':
1696 g_string_append_c ( cmd, '%' );
1697 break;
1698 case '\0':
1699 goto _finish;
1700 break;
1701 }
1702 }
1703 else /* not % escaped part */
1704 {
1705 g_string_append_c ( cmd, *pexec );
1706 }
1707 }
1708 _finish:
1709 if( ! add_files )
1710 {
1711 g_string_append_c ( cmd, ' ' );
1712 for( l = file_list; l; l = l->next )
1713 {
1714 file = (char*)l->data;
1715 tmp = g_shell_quote( file );
1716 g_string_append( cmd, tmp );
1717 g_string_append_c( cmd, ' ' );
1718 g_free( tmp );
1719 }
1720 }
1721
1722 return g_string_free( cmd, FALSE );
1723 }
1724
1725 gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal)
1726 {
1727 GError *error = NULL;
1728 char* cmd;
1729 if( ! exec )
1730 return FALSE;
1731 cmd = translate_app_exec_to_command_line(exec, files);
1732 if( in_terminal )
1733 {
1734 char* term_cmd;
1735 const char* term = lxpanel_get_terminal();
1736 if( strstr(term, "%s") )
1737 term_cmd = g_strdup_printf(term, cmd);
1738 else
1739 term_cmd = g_strconcat( term, " -e ", cmd, NULL );
1740 if( cmd != exec )
1741 g_free(cmd);
1742 cmd = term_cmd;
1743 }
1744 if (! g_spawn_command_line_async(cmd, &error) ) {
1745 ERR("can't spawn %s\nError is %s\n", cmd, error->message);
1746 g_error_free (error);
1747 }
1748 g_free(cmd);
1749
1750 return (error == NULL);
1751 }
1752