bfc16dbc155cd8d2e8321954dea41bf5ca6ee24a
[lxde/lxpanel.git] / src / misc.c
1 /*
2 * Copyright (C) 2006-2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
3 * 2006-2008 Jim Huang <jserv.tw@gmail.com>
4 * 2008-2009 Fred Chien <fred@lxde.org>
5 * 2009 Ying-Chun Liu (PaulLiu) <grandpaul@gmail.com>
6 * 2009-2010 Marty Jack <martyj19@comcast.net>
7 * 2011 Julien Lavergne <julien.lavergne@gmail.com>
8 * 2012-2013 Henry Gebhardt <hsggebhardt@gmail.com>
9 * 2013 Rouslan <rouslan-k@users.sourceforge.net>
10 * 2014 SHiNE CsyFeK <csyfek@users.sourceforge.net>
11 * 2014 Andriy Grytsenko <andrej@rep.kiev.ua>
12 *
13 * This file is a part of LXPanel project.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software Foundation,
27 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 */
29
30 #include <X11/Xatom.h>
31 #include <X11/cursorfont.h>
32
33 #include <gtk/gtk.h>
34 #include <gdk/gdk.h>
35 #include <gdk/gdkx.h>
36 #include <ctype.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <libfm/fm-gtk.h>
42
43 #include "misc.h"
44 #include "private.h"
45
46 #include "dbg.h"
47
48 /* data used by themed images buttons */
49 typedef struct {
50 FmIcon *icon;
51 guint theme_changed_handler;
52 guint icon_changed_handler;
53 guint font_changed_handler;
54 GdkPixbuf* pixbuf;
55 GdkPixbuf* hilight;
56 gulong hicolor;
57 gint size; /* desired size */
58 LXPanel *panel;
59 char *fallback;
60 } ImgData;
61
62 static GQuark img_data_id = 0;
63
64 static void _gtk_image_set_from_file_scaled(GtkWidget *img, ImgData *data);
65
66 /* X11 data types */
67 Atom a_UTF8_STRING;
68 Atom a_XROOTPMAP_ID;
69
70 /* old WM spec */
71 Atom a_WM_STATE;
72 Atom a_WM_CLASS;
73 Atom a_WM_DELETE_WINDOW;
74 Atom a_WM_PROTOCOLS;
75
76 /* new NET spec */
77 Atom a_NET_WORKAREA;
78 Atom a_NET_CLIENT_LIST;
79 Atom a_NET_CLIENT_LIST_STACKING;
80 Atom a_NET_NUMBER_OF_DESKTOPS;
81 Atom a_NET_CURRENT_DESKTOP;
82 Atom a_NET_DESKTOP_VIEWPORT;
83 Atom a_NET_DESKTOP_NAMES;
84 Atom a_NET_ACTIVE_WINDOW;
85 Atom a_NET_CLOSE_WINDOW;
86 Atom a_NET_SHOWING_DESKTOP;
87 Atom a_NET_SUPPORTED;
88 Atom a_NET_WM_STATE;
89 Atom a_NET_WM_STATE_SKIP_TASKBAR;
90 Atom a_NET_WM_STATE_SKIP_PAGER;
91 Atom a_NET_WM_STATE_STICKY;
92 Atom a_NET_WM_STATE_HIDDEN;
93 Atom a_NET_WM_STATE_SHADED;
94 Atom a_NET_WM_WINDOW_TYPE;
95 Atom a_NET_WM_WINDOW_TYPE_DESKTOP;
96 Atom a_NET_WM_WINDOW_TYPE_DOCK;
97 Atom a_NET_WM_WINDOW_TYPE_TOOLBAR;
98 Atom a_NET_WM_WINDOW_TYPE_MENU;
99 Atom a_NET_WM_WINDOW_TYPE_UTILITY;
100 Atom a_NET_WM_WINDOW_TYPE_SPLASH;
101 Atom a_NET_WM_WINDOW_TYPE_DIALOG;
102 Atom a_NET_WM_WINDOW_TYPE_NORMAL;
103 Atom a_NET_WM_DESKTOP;
104 Atom a_NET_WM_PID;
105 Atom a_NET_WM_NAME;
106 Atom a_NET_WM_VISIBLE_NAME;
107 Atom a_NET_WM_STRUT;
108 Atom a_NET_WM_STRUT_PARTIAL;
109 Atom a_NET_WM_ICON;
110 Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
111
112 /* SYSTEM TRAY spec */
113 Atom a_NET_SYSTEM_TRAY_OPCODE;
114 Atom a_NET_SYSTEM_TRAY_MESSAGE_DATA;
115 Atom a_NET_SYSTEM_TRAY_ORIENTATION;
116 Atom a_MANAGER;
117
118 Atom a_LXPANEL_CMD; /* for private client message */
119
120 /* if current window manager is EWMH conforming. */
121 gboolean is_ewmh_supported;
122
123 enum{
124 I_UTF8_STRING,
125 I_XROOTPMAP_ID,
126 I_WM_STATE,
127 I_WM_CLASS,
128 I_WM_DELETE_WINDOW,
129 I_WM_PROTOCOLS,
130
131 I_NET_WORKAREA,
132 I_NET_CLIENT_LIST,
133 I_NET_CLIENT_LIST_STACKING,
134 I_NET_NUMBER_OF_DESKTOPS,
135 I_NET_CURRENT_DESKTOP,
136 I_NET_DESKTOP_VIEWPORT,
137 I_NET_DESKTOP_NAMES,
138 I_NET_ACTIVE_WINDOW,
139 I_NET_SHOWING_DESKTOP,
140 I_NET_SUPPORTED,
141
142 I_NET_WM_STATE,
143 I_NET_WM_STATE_SKIP_TASKBAR,
144 I_NET_WM_STATE_SKIP_PAGER,
145 I_NET_WM_STATE_STICKY,
146 I_NET_WM_STATE_HIDDEN,
147 I_NET_WM_STATE_SHADED,
148 I_NET_WM_WINDOW_TYPE,
149
150 I_NET_WM_WINDOW_TYPE_DESKTOP,
151 I_NET_WM_WINDOW_TYPE_DOCK,
152 I_NET_WM_WINDOW_TYPE_TOOLBAR,
153 I_NET_WM_WINDOW_TYPE_MENU,
154 I_NET_WM_WINDOW_TYPE_UTILITY,
155 I_NET_WM_WINDOW_TYPE_SPLASH,
156 I_NET_WM_WINDOW_TYPE_DIALOG,
157 I_NET_WM_WINDOW_TYPE_NORMAL,
158 I_NET_WM_DESKTOP,
159 I_NET_WM_PID,
160 I_NET_WM_NAME,
161 I_NET_WM_VISIBLE_NAME,
162 I_NET_WM_STRUT,
163 I_NET_WM_STRUT_PARTIAL,
164 I_NET_WM_ICON,
165 I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR,
166
167 I_NET_SYSTEM_TRAY_OPCODE,
168 I_NET_SYSTEM_TRAY_MESSAGE_DATA,
169 I_NET_SYSTEM_TRAY_ORIENTATION,
170 I_MANAGER,
171
172 I_LXPANEL_CMD,
173 N_ATOMS
174 };
175
176 pair allign_pair[] = {
177 { ALIGN_NONE, "none" },
178 { ALIGN_LEFT, "left" },
179 { ALIGN_RIGHT, "right" },
180 { ALIGN_CENTER, "center"},
181 { 0, NULL },
182 };
183
184 pair edge_pair[] = {
185 { EDGE_NONE, "none" },
186 { EDGE_LEFT, "left" },
187 { EDGE_RIGHT, "right" },
188 { EDGE_TOP, "top" },
189 { EDGE_BOTTOM, "bottom" },
190 { 0, NULL },
191 };
192
193 pair width_pair[] = {
194 { WIDTH_NONE, "none" },
195 { WIDTH_REQUEST, "request" },
196 { WIDTH_PIXEL, "pixel" },
197 { WIDTH_PERCENT, "percent" },
198 { 0, NULL },
199 };
200
201 pair height_pair[] = {
202 { HEIGHT_NONE, "none" },
203 { HEIGHT_PIXEL, "pixel" },
204 { 0, NULL },
205 };
206
207 pair bool_pair[] = {
208 { 0, "0" },
209 { 1, "1" },
210 { 0, NULL },
211 };
212
213 int
214 str2num(pair *p, const gchar *str, int defval)
215 {
216 ENTER;
217 for (;p && p->str; p++) {
218 if (!g_ascii_strcasecmp(str, p->str))
219 RET(p->num);
220 }
221 RET(defval);
222 }
223
224 const gchar *
225 num2str(pair *p, int num, const gchar *defval)
226 {
227 ENTER;
228 for (;p && p->str; p++) {
229 if (num == p->num)
230 RET(p->str);
231 }
232 RET(defval);
233 }
234
235 int buf_gets( char* buf, int len, char **fp )
236 {
237 char* p;
238 int i = 0;
239 if( !fp || !(p = *fp) || !**fp )
240 {
241 buf[0] = '\0';
242 return 0;
243 }
244
245 do
246 {
247 if( G_LIKELY( i < len ) )
248 {
249 buf[i] = *p;
250 ++i;
251 }
252 if( G_UNLIKELY(*p == '\n') )
253 {
254 ++p;
255 break;
256 }
257 }while( *(++p) );
258 buf[i] = '\0';
259 *fp = p;
260 return i;
261 }
262
263 extern int
264 lxpanel_put_line(FILE* fp, const char* format, ...)
265 {
266 static int indent = 0;
267 int i, ret;
268 va_list args;
269
270 if( strchr(format, '}') )
271 --indent;
272
273 for( i = 0; i < indent; ++i )
274 fputs( " ", fp );
275
276 va_start (args, format);
277 ret = vfprintf (fp, format, args);
278 va_end (args);
279
280 if( strchr(format, '{') )
281 ++indent;
282 fputc( '\n', fp ); /* add line break */
283 return (ret + 1);
284 }
285
286 extern int
287 lxpanel_get_line(char**fp, line *s)
288 {
289 gchar *tmp, *tmp2;
290
291 s->type = LINE_NONE;
292 if (!fp)
293 RET(s->type);
294 while (buf_gets(s->str, s->len, fp)) {
295
296 g_strstrip(s->str);
297
298 if (s->str[0] == '#' || s->str[0] == 0) {
299 continue;
300 }
301 if (!g_ascii_strcasecmp(s->str, "}")) {
302 s->type = LINE_BLOCK_END;
303 break;
304 }
305
306 s->t[0] = s->str;
307 for (tmp = s->str; isalnum(*tmp); tmp++);
308 for (tmp2 = tmp; isspace(*tmp2); tmp2++);
309 if (*tmp2 == '=') {
310 for (++tmp2; isspace(*tmp2); tmp2++);
311 s->t[1] = tmp2;
312 *tmp = 0;
313 s->type = LINE_VAR;
314 } else if (*tmp2 == '{') {
315 *tmp = 0;
316 s->type = LINE_BLOCK_START;
317 } else {
318 g_warning( "parser: unknown token: '%c'", *tmp2);
319 }
320 break;
321 }
322 return s->type;
323 }
324
325 void resolve_atoms()
326 {
327 static const char* atom_names[ N_ATOMS ];
328
329 atom_names[ I_UTF8_STRING ] = "UTF8_STRING";
330 atom_names[ I_XROOTPMAP_ID ] = "_XROOTPMAP_ID";
331 atom_names[ I_WM_STATE ] = "WM_STATE";
332 atom_names[ I_WM_CLASS ] = "WM_CLASS";
333 atom_names[ I_WM_DELETE_WINDOW ] = "WM_DELETE_WINDOW";
334 atom_names[ I_WM_PROTOCOLS ] = "WM_PROTOCOLS";
335 atom_names[ I_NET_WORKAREA ] = "_NET_WORKAREA";
336 atom_names[ I_NET_CLIENT_LIST ] = "_NET_CLIENT_LIST";
337 atom_names[ I_NET_CLIENT_LIST_STACKING ] = "_NET_CLIENT_LIST_STACKING";
338 atom_names[ I_NET_NUMBER_OF_DESKTOPS ] = "_NET_NUMBER_OF_DESKTOPS";
339 atom_names[ I_NET_CURRENT_DESKTOP ] = "_NET_CURRENT_DESKTOP";
340 atom_names[ I_NET_DESKTOP_VIEWPORT ] = "_NET_DESKTOP_VIEWPORT";
341 atom_names[ I_NET_DESKTOP_NAMES ] = "_NET_DESKTOP_NAMES";
342 atom_names[ I_NET_ACTIVE_WINDOW ] = "_NET_ACTIVE_WINDOW";
343 atom_names[ I_NET_SHOWING_DESKTOP ] = "_NET_SHOWING_DESKTOP";
344 atom_names[ I_NET_SUPPORTED ] = "_NET_SUPPORTED";
345 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
346 atom_names[ I_NET_WM_STATE ] = "_NET_WM_STATE";
347 atom_names[ I_NET_WM_STATE_SKIP_TASKBAR ] = "_NET_WM_STATE_SKIP_TASKBAR";
348 atom_names[ I_NET_WM_STATE_SKIP_PAGER ] = "_NET_WM_STATE_SKIP_PAGER";
349 atom_names[ I_NET_WM_STATE_STICKY ] = "_NET_WM_STATE_STICKY";
350 atom_names[ I_NET_WM_STATE_HIDDEN ] = "_NET_WM_STATE_HIDDEN";
351 atom_names[ I_NET_WM_STATE_SHADED ] = "_NET_WM_STATE_SHADED";
352 atom_names[ I_NET_WM_WINDOW_TYPE ] = "_NET_WM_WINDOW_TYPE";
353
354 atom_names[ I_NET_WM_WINDOW_TYPE_DESKTOP ] = "_NET_WM_WINDOW_TYPE_DESKTOP";
355 atom_names[ I_NET_WM_WINDOW_TYPE_DOCK ] = "_NET_WM_WINDOW_TYPE_DOCK";
356 atom_names[ I_NET_WM_WINDOW_TYPE_TOOLBAR ] = "_NET_WM_WINDOW_TYPE_TOOLBAR";
357 atom_names[ I_NET_WM_WINDOW_TYPE_MENU ] = "_NET_WM_WINDOW_TYPE_MENU";
358 atom_names[ I_NET_WM_WINDOW_TYPE_UTILITY ] = "_NET_WM_WINDOW_TYPE_UTILITY";
359 atom_names[ I_NET_WM_WINDOW_TYPE_SPLASH ] = "_NET_WM_WINDOW_TYPE_SPLASH";
360 atom_names[ I_NET_WM_WINDOW_TYPE_DIALOG ] = "_NET_WM_WINDOW_TYPE_DIALOG";
361 atom_names[ I_NET_WM_WINDOW_TYPE_NORMAL ] = "_NET_WM_WINDOW_TYPE_NORMAL";
362 atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
363 atom_names[ I_NET_WM_PID ] = "_NET_WM_PID";
364 atom_names[ I_NET_WM_NAME ] = "_NET_WM_NAME";
365 atom_names[ I_NET_WM_VISIBLE_NAME ] = "_NET_WM_VISIBLE_NAME";
366 atom_names[ I_NET_WM_STRUT ] = "_NET_WM_STRUT";
367 atom_names[ I_NET_WM_STRUT_PARTIAL ] = "_NET_WM_STRUT_PARTIAL";
368 atom_names[ I_NET_WM_ICON ] = "_NET_WM_ICON";
369 atom_names[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ] = "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR";
370
371 atom_names[ I_NET_SYSTEM_TRAY_OPCODE ] = "_NET_SYSTEM_TRAY_OPCODE";
372 atom_names[ I_NET_SYSTEM_TRAY_MESSAGE_DATA ] = "_NET_SYSTEM_TRAY_MESSAGE_DATA";
373 atom_names[ I_NET_SYSTEM_TRAY_ORIENTATION ] = "_NET_SYSTEM_TRAY_ORIENTATION";
374 atom_names[ I_MANAGER ] = "MANAGER";
375
376 atom_names[ I_LXPANEL_CMD ] = "_LXPANEL_CMD";
377
378 Atom atoms[ N_ATOMS ];
379
380 ENTER;
381
382 if( ! XInternAtoms( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), (char**)atom_names,
383 N_ATOMS, False, atoms ) )
384 {
385 g_warning( "Error: unable to return Atoms" );
386 return;
387 }
388
389 a_UTF8_STRING = atoms[ I_UTF8_STRING ];
390 a_XROOTPMAP_ID = atoms[ I_XROOTPMAP_ID ];
391 a_WM_STATE = atoms[ I_WM_STATE ];
392 a_WM_CLASS = atoms[ I_WM_CLASS ];
393 a_WM_DELETE_WINDOW = atoms[ I_WM_DELETE_WINDOW ];
394 a_WM_PROTOCOLS = atoms[ I_WM_PROTOCOLS ];
395
396 a_NET_WORKAREA = atoms[ I_NET_WORKAREA ];
397 a_NET_CLIENT_LIST = atoms[ I_NET_CLIENT_LIST ];
398 a_NET_CLIENT_LIST_STACKING = atoms[ I_NET_CLIENT_LIST_STACKING ];
399 a_NET_NUMBER_OF_DESKTOPS = atoms[ I_NET_NUMBER_OF_DESKTOPS ];
400 a_NET_CURRENT_DESKTOP = atoms[ I_NET_CURRENT_DESKTOP ];
401 a_NET_DESKTOP_VIEWPORT = atoms[ I_NET_DESKTOP_VIEWPORT ];
402 a_NET_DESKTOP_NAMES = atoms[ I_NET_DESKTOP_NAMES ];
403 a_NET_ACTIVE_WINDOW = atoms[ I_NET_ACTIVE_WINDOW ];
404 a_NET_SHOWING_DESKTOP = atoms[ I_NET_SHOWING_DESKTOP ];
405 a_NET_SUPPORTED = atoms[ I_NET_SUPPORTED ];
406 a_NET_WM_STATE = atoms[ I_NET_WM_STATE ];
407 a_NET_WM_STATE_SKIP_TASKBAR = atoms[ I_NET_WM_STATE_SKIP_TASKBAR ];
408 a_NET_WM_STATE_SKIP_PAGER = atoms[ I_NET_WM_STATE_SKIP_PAGER ];
409 a_NET_WM_STATE_STICKY = atoms[ I_NET_WM_STATE_STICKY ];
410 a_NET_WM_STATE_HIDDEN = atoms[ I_NET_WM_STATE_HIDDEN ];
411 a_NET_WM_STATE_SHADED = atoms[ I_NET_WM_STATE_SHADED ];
412 a_NET_WM_WINDOW_TYPE = atoms[ I_NET_WM_WINDOW_TYPE ];
413
414 a_NET_WM_WINDOW_TYPE_DESKTOP = atoms[ I_NET_WM_WINDOW_TYPE_DESKTOP ];
415 a_NET_WM_WINDOW_TYPE_DOCK = atoms[ I_NET_WM_WINDOW_TYPE_DOCK ];
416 a_NET_WM_WINDOW_TYPE_TOOLBAR = atoms[ I_NET_WM_WINDOW_TYPE_TOOLBAR ];
417 a_NET_WM_WINDOW_TYPE_MENU = atoms[ I_NET_WM_WINDOW_TYPE_MENU ];
418 a_NET_WM_WINDOW_TYPE_UTILITY = atoms[ I_NET_WM_WINDOW_TYPE_UTILITY ];
419 a_NET_WM_WINDOW_TYPE_SPLASH = atoms[ I_NET_WM_WINDOW_TYPE_SPLASH ];
420 a_NET_WM_WINDOW_TYPE_DIALOG = atoms[ I_NET_WM_WINDOW_TYPE_DIALOG ];
421 a_NET_WM_WINDOW_TYPE_NORMAL = atoms[ I_NET_WM_WINDOW_TYPE_NORMAL ];
422 a_NET_WM_DESKTOP = atoms[ I_NET_WM_DESKTOP ];
423 a_NET_WM_PID = atoms[ I_NET_WM_PID ];
424 a_NET_WM_NAME = atoms[ I_NET_WM_NAME ];
425 a_NET_WM_VISIBLE_NAME = atoms[ I_NET_WM_VISIBLE_NAME ];
426 a_NET_WM_STRUT = atoms[ I_NET_WM_STRUT ];
427 a_NET_WM_STRUT_PARTIAL = atoms[ I_NET_WM_STRUT_PARTIAL ];
428 a_NET_WM_ICON = atoms[ I_NET_WM_ICON ];
429 a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR = atoms[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ];
430
431 a_NET_SYSTEM_TRAY_OPCODE = atoms[ I_NET_SYSTEM_TRAY_OPCODE ];
432 a_NET_SYSTEM_TRAY_MESSAGE_DATA = atoms [ I_NET_SYSTEM_TRAY_MESSAGE_DATA ];
433 a_NET_SYSTEM_TRAY_ORIENTATION = atoms[ I_NET_SYSTEM_TRAY_ORIENTATION ];
434 a_MANAGER = atoms[ I_MANAGER ];
435
436 a_LXPANEL_CMD = atoms[ I_LXPANEL_CMD ];
437
438 RET();
439 }
440
441
442 void
443 Xclimsg(Window win, Atom type, long l0, long l1, long l2, long l3, long l4)
444 {
445 XClientMessageEvent xev;
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_XDISPLAY(gdk_display_get_default()), 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_XDISPLAY(gdk_display_get_default()), win, False, 0L, (XEvent *) &xev);
472 }
473
474
475 void *
476 get_utf8_property(Window win, Atom atom)
477 {
478 Atom type;
479 int format;
480 gulong nitems;
481 gulong bytes_after;
482 gchar *val, *retval;
483 int result;
484 guchar *tmp = NULL;
485
486 type = None;
487 retval = NULL;
488 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, atom, 0, G_MAXLONG, False,
489 a_UTF8_STRING, &type, &format, &nitems,
490 &bytes_after, &tmp);
491 if (result != Success || type == None)
492 return NULL;
493 val = (gchar *) tmp;
494 if (val) {
495 if (type == a_UTF8_STRING && format == 8 && nitems != 0)
496 retval = g_strndup (val, nitems);
497 XFree (val);
498 }
499 return retval;
500
501 }
502
503 char **
504 get_utf8_property_list(Window win, Atom atom, int *count)
505 {
506 Atom type;
507 int format;
508 gulong nitems, i;
509 gulong bytes_after;
510 gchar *s, **retval = NULL;
511 int result;
512 guchar *tmp = NULL;
513
514 *count = 0;
515 result = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, atom, 0, G_MAXLONG, False,
516 a_UTF8_STRING, &type, &format, &nitems,
517 &bytes_after, &tmp);
518 if (result != Success || type != a_UTF8_STRING || tmp == NULL)
519 return NULL;
520
521 if (nitems) {
522 gchar *val = (gchar *) tmp;
523 DBG("res=%d(%d) nitems=%d val=%s\n", result, Success, nitems, val);
524 for (i = 0; i < nitems; i++) {
525 if (!val[i])
526 (*count)++;
527 }
528 retval = g_new0 (char*, *count + 2);
529 for (i = 0, s = val; (int)i < *count; i++, s = s + strlen (s) + 1) {
530 retval[i] = g_strdup(s);
531 }
532 if (val[nitems-1]) {
533 result = nitems - (s - val);
534 DBG("val does not ends by 0, moving last %d bytes\n", result);
535 g_memmove(s - 1, s, result);
536 val[nitems-1] = 0;
537 DBG("s=%s\n", s -1);
538 retval[i] = g_strdup(s - 1);
539 (*count)++;
540 }
541 }
542 XFree (tmp);
543
544 return retval;
545
546 }
547
548 void *
549 get_xaproperty (Window win, Atom prop, Atom type, int *nitems)
550 {
551 Atom type_ret;
552 int format_ret;
553 unsigned long items_ret;
554 unsigned long after_ret;
555 unsigned char *prop_data;
556
557 ENTER;
558 prop_data = NULL;
559 if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, prop, 0, G_MAXLONG, False,
560 type, &type_ret, &format_ret, &items_ret,
561 &after_ret, &prop_data) != Success || items_ret == 0)
562 {
563 if( G_UNLIKELY(prop_data) )
564 XFree( prop_data );
565 if( nitems )
566 *nitems = 0;
567 RET(NULL);
568 }
569 if (nitems)
570 *nitems = items_ret;
571 RET(prop_data);
572 }
573
574 static char*
575 text_property_to_utf8 (const XTextProperty *prop)
576 {
577 char **list;
578 int count;
579 char *retval;
580
581 ENTER;
582 list = NULL;
583 count = gdk_text_property_to_utf8_list_for_display (gdk_display_get_default(),
584 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_XDISPLAY(gdk_display_get_default()), 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 gulong *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 gulong *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 gulong *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 gulong *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 memset(nws, 0, 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 void
728 get_net_wm_window_type(Window win, NetWMWindowType *nwwt)
729 {
730 Atom *state;
731 int num3;
732
733
734 ENTER;
735 memset(nwwt, 0, sizeof(*nwwt));
736 if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3)))
737 RET();
738
739 DBG( "%x: netwm state = { ", (unsigned int)win);
740 while (--num3 >= 0) {
741 if (state[num3] == a_NET_WM_WINDOW_TYPE_DESKTOP) {
742 DBG("NET_WM_WINDOW_TYPE_DESKTOP ");
743 nwwt->desktop = 1;
744 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DOCK) {
745 DBG( "NET_WM_WINDOW_TYPE_DOCK ");
746 nwwt->dock = 1;
747 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_TOOLBAR) {
748 DBG( "NET_WM_WINDOW_TYPE_TOOLBAR ");
749 nwwt->toolbar = 1;
750 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_MENU) {
751 DBG( "NET_WM_WINDOW_TYPE_MENU ");
752 nwwt->menu = 1;
753 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_UTILITY) {
754 DBG( "NET_WM_WINDOW_TYPE_UTILITY ");
755 nwwt->utility = 1;
756 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_SPLASH) {
757 DBG( "NET_WM_WINDOW_TYPE_SPLASH ");
758 nwwt->splash = 1;
759 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_DIALOG) {
760 DBG( "NET_WM_WINDOW_TYPE_DIALOG ");
761 nwwt->dialog = 1;
762 } else if (state[num3] == a_NET_WM_WINDOW_TYPE_NORMAL) {
763 DBG( "NET_WM_WINDOW_TYPE_NORMAL ");
764 nwwt->normal = 1;
765 } else {
766 DBG( "... ");
767 }
768 }
769 XFree(state);
770 DBG( "}\n");
771 RET();
772 }
773
774 int
775 get_wm_state (Window win)
776 {
777 unsigned long *data;
778 int ret = 0;
779
780 ENTER;
781 data = get_xaproperty (win, a_WM_STATE, a_WM_STATE, 0);
782 if (data) {
783 ret = data[0];
784 XFree (data);
785 }
786 RET(ret);
787 }
788
789 int panel_handle_x_error(Display * d, XErrorEvent * ev)
790 {
791 char buf[256];
792
793 XGetErrorText(d, ev->error_code, buf, 256);
794 g_warning("lxpanel : X error: %s", buf);
795 return 0; /* Ignored */
796 }
797
798 int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
799 {
800 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
801 panel_handle_x_error(d, ev);
802 return 0; /* Ignored */
803 }
804
805 static void
806 calculate_width(int scrw, int pw, int wtype, int align, int margin,
807 int *panw, int *x)
808 {
809 ENTER;
810 DBG("scrw=%d\n", scrw);
811 DBG("IN panw=%d, margin=%d\n", *panw, margin);
812 //scrw -= 2;
813 if (wtype != WIDTH_REQUEST)
814 *panw = pw;
815 if (wtype == WIDTH_PERCENT) {
816 /* sanity check */
817 if (*panw > 100)
818 *panw = 100;
819 else if (*panw < 0)
820 *panw = 1;
821 *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0;
822 }
823 if (align != ALIGN_CENTER) {
824 if (margin > scrw) {
825 g_warning( "margin is bigger then edge size %d > %d. Ignoring margin",
826 margin, scrw);
827 margin = 0;
828 }
829 *panw = MIN(scrw - margin, *panw);
830 }
831 DBG("OUT panw=%d\n", *panw);
832 if (align == ALIGN_LEFT)
833 *x += margin;
834 else if (align == ALIGN_RIGHT) {
835 *x += scrw - *panw - margin;
836 if (*x < 0)
837 *x = 0;
838 } else if (align == ALIGN_CENTER)
839 *x += (scrw - *panw) / 2;
840 RET();
841 }
842
843
844 void _calculate_position(LXPanel *panel, GdkRectangle *rect)
845 {
846 Panel *np = panel->priv;
847 GdkScreen *screen;
848 GdkRectangle marea;
849
850 ENTER;
851 screen = gtk_widget_get_screen(GTK_WIDGET(panel));
852 if (np->monitor < 0) /* all monitors */
853 {
854 marea.x = 0;
855 marea.y = 0;
856 marea.width = gdk_screen_get_width(screen);
857 marea.height = gdk_screen_get_height(screen);
858 }
859 else if (np->monitor < gdk_screen_get_n_monitors(screen))
860 gdk_screen_get_monitor_geometry(screen,np->monitor,&marea);
861 else
862 {
863 marea.x = 0;
864 marea.y = 0;
865 marea.width = 0;
866 marea.height = 0;
867 }
868
869 if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
870 rect->x = marea.x;
871 calculate_width(marea.width, np->width, np->widthtype, np->align, np->margin,
872 &rect->width, &rect->x);
873 rect->height = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
874 rect->y = marea.y + ((np->edge == EDGE_TOP) ? 0 : (marea.height - rect->height));
875
876 } else {
877 rect->y = marea.y;
878 calculate_width(marea.height, np->width, np->widthtype, np->align, np->margin,
879 &rect->height, &rect->y);
880 rect->width = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
881 rect->x = marea.x + ((np->edge == EDGE_LEFT) ? 0 : (marea.width - rect->width));
882 }
883 //g_debug("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah);
884 RET();
885 }
886
887 void calculate_position(Panel *np)
888 {
889 GdkRectangle rect;
890
891 rect.width = np->aw;
892 rect.height = np->ah;
893 _calculate_position(np->topgwin, &rect);
894 np->aw = rect.width;
895 np->ah = rect.height;
896 np->ax = rect.x;
897 np->ay = rect.y;
898 }
899
900 gchar *
901 expand_tilda(const gchar *file)
902 {
903 ENTER;
904 RET((file[0] == '~') ?
905 g_strdup_printf("%s%s", getenv("HOME"), file+1)
906 : g_strdup(file));
907
908 }
909
910 /*
911 * SuxPanel version 0.1
912 * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
913 *
914 * This program may be distributed under the terms of GNU General
915 * Public License version 2. You should have received a copy of the
916 * license with this program; if not, please consult http://www.fsf.org/.
917 *
918 * This program comes with no warranty. Use at your own risk.
919 *
920 */
921
922 /* DestroyNotify handler for image data in _gtk_image_new_from_file_scaled. */
923 static void img_data_free(ImgData * data)
924 {
925 g_object_unref(data->icon);
926 if (data->theme_changed_handler != 0)
927 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
928 if (data->panel != NULL)
929 {
930 g_object_remove_weak_pointer(G_OBJECT(data->panel), (gpointer *)&data->panel);
931 g_signal_handler_disconnect(data->panel, data->icon_changed_handler);
932 if (data->font_changed_handler != 0)
933 g_signal_handler_disconnect(data->panel, data->font_changed_handler);
934 }
935 if (data->pixbuf != NULL)
936 g_object_unref(data->pixbuf);
937 if (data->hilight != NULL)
938 g_object_unref(data->hilight);
939 if (data->fallback != NULL)
940 g_free(data->fallback);
941 g_free(data);
942 }
943
944 /* Handler for "changed" signal in _gtk_image_new_from_file_scaled. */
945 static void on_theme_changed(GtkWidget * img, GObject * object)
946 {
947 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
948 _gtk_image_set_from_file_scaled(img, data);
949 }
950
951 /* consumes reference on icon */
952 static void _lxpanel_button_set_icon(GtkWidget* btn, FmIcon* icon, gint size)
953 {
954 /* Locate the image within the button. */
955 GtkWidget * child = gtk_bin_get_child(GTK_BIN(btn));
956 GtkWidget * img = NULL;
957 if (GTK_IS_IMAGE(child))
958 img = child;
959 else if (GTK_IS_BOX(child))
960 {
961 GList * children = gtk_container_get_children(GTK_CONTAINER(child));
962 img = GTK_WIDGET(GTK_IMAGE(children->data));
963 g_list_free(children);
964 }
965
966 if (img != NULL)
967 {
968 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
969
970 if (size == 0) /* 0: set as before; -1: reset to panel icon size */
971 size = data->size;
972 if (icon != data->icon || size != data->size) /* something was changed */
973 {
974 g_object_unref(data->icon);
975 data->icon = icon;
976 data->size = size;
977 _gtk_image_set_from_file_scaled(img, data);
978 }
979 else
980 g_object_unref(icon);
981 }
982 else
983 g_object_unref(icon);
984 }
985
986 void lxpanel_button_set_icon(GtkWidget* btn, const gchar *name, gint size)
987 {
988 _lxpanel_button_set_icon(btn, fm_icon_from_name(name), size);
989 }
990
991 void lxpanel_button_update_icon(GtkWidget* btn, FmIcon *icon, gint size)
992 {
993 _lxpanel_button_set_icon(btn, g_object_ref(icon), size);
994 }
995
996 gboolean lxpanel_button_set_label(GtkWidget *btn, const char *label)
997 {
998 /* Locate the image within the button. */
999 GtkWidget * child = gtk_bin_get_child(GTK_BIN(btn));
1000 GtkWidget * lbl = NULL;
1001 GtkWidget * img = NULL;
1002 ImgData * data = NULL;
1003
1004 if (GTK_IS_BOX(child))
1005 {
1006 GList * children = gtk_container_get_children(GTK_CONTAINER(child)), *l;
1007 for (l = children; l; l = l->next)
1008 if (GTK_IS_LABEL(l->data))
1009 lbl = l->data;
1010 else if (GTK_IS_IMAGE(l->data))
1011 img = l->data;
1012 g_list_free(children);
1013 }
1014
1015 if (G_UNLIKELY(lbl == NULL))
1016 return FALSE;
1017
1018 if (G_LIKELY(img != NULL))
1019 data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
1020
1021 if (G_LIKELY(data != NULL && data->panel != NULL))
1022 lxpanel_draw_label_text(data->panel, lbl, label, FALSE, 1, TRUE);
1023 else
1024 gtk_label_set_text(GTK_LABEL(lbl), label);
1025 return TRUE;
1026 }
1027
1028 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1029 void fb_button_set_from_file(GtkWidget * btn, const char * img_file, gint width, gint height, gboolean keep_ratio)
1030 {
1031 lxpanel_button_set_icon(btn, img_file, height);
1032 }
1033
1034 static void _gtk_image_set_from_file_scaled(GtkWidget * img, ImgData * data)
1035 {
1036 gint size = data->size;
1037
1038 if (size < 0 && data->panel)
1039 size = data->panel->priv->icon_size;
1040
1041 if (data->pixbuf != NULL)
1042 {
1043 g_object_unref(data->pixbuf);
1044 data->pixbuf = NULL;
1045 }
1046
1047 /* if there is a cached hilighted version of this pixbuf, free it */
1048 if (data->hilight != NULL)
1049 {
1050 g_object_unref(data->hilight);
1051 data->hilight = NULL;
1052 }
1053
1054 if (G_LIKELY(G_IS_THEMED_ICON(data->icon)))
1055 {
1056 const char *fallback = data->fallback;
1057
1058 if (fallback == NULL)
1059 fallback = "application-x-executable";
1060 data->pixbuf = fm_pixbuf_from_icon_with_fallback(data->icon, size, fallback);
1061 }
1062 else
1063 {
1064 char *file = g_icon_to_string(fm_icon_get_gicon(data->icon));
1065 data->pixbuf = gdk_pixbuf_new_from_file_at_scale(file, -1, size, TRUE, NULL);
1066 g_free(file);
1067 }
1068
1069 if (data->pixbuf == NULL && data->fallback != NULL && data->fallback[0] == '/')
1070 {
1071 /* if fallback was provided as a file path */
1072 data->pixbuf = gdk_pixbuf_new_from_file_at_scale(data->fallback, -1, size, TRUE, NULL);
1073 }
1074
1075 if (data->pixbuf != NULL)
1076 {
1077 /* Set the pixbuf into the image widget. */
1078 gtk_image_set_from_pixbuf((GtkImage *)img, data->pixbuf);
1079 }
1080 else
1081 {
1082 /* No pixbuf available. Set the "missing image" icon. */
1083 gtk_image_set_from_stock(GTK_IMAGE(img), GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON);
1084 }
1085 }
1086
1087 /* consumes reference on icon */
1088 static GtkWidget *_gtk_image_new_for_icon(LXPanel *p, FmIcon *icon, gint size,
1089 const char *fallback)
1090 {
1091 GtkWidget * img = gtk_image_new();
1092 ImgData * data = g_new0(ImgData, 1);
1093
1094 data->icon = icon;
1095 data->size = size;
1096 data->fallback = g_strdup(fallback);
1097 if (img_data_id == 0)
1098 img_data_id = g_quark_from_static_string("ImgData");
1099 g_object_set_qdata_full(G_OBJECT(img), img_data_id, data, (GDestroyNotify) img_data_free);
1100 if (p && size < 0)
1101 {
1102 data->panel = p;
1103 data->icon_changed_handler = g_signal_connect_swapped(p, "icon-size-changed",
1104 G_CALLBACK(on_theme_changed), img);
1105 /* it is in fact not required if image is panel child but let be safe */
1106 g_object_add_weak_pointer(G_OBJECT(p), (gpointer *)&data->panel);
1107 }
1108 _gtk_image_set_from_file_scaled(img, data);
1109 if (G_IS_THEMED_ICON(data->icon))
1110 {
1111 /* This image is loaded from icon theme. Update the image if the icon theme is changed. */
1112 data->theme_changed_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(),
1113 "changed", G_CALLBACK(on_theme_changed), img);
1114 }
1115 return img;
1116 }
1117
1118 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1119 GtkWidget * _gtk_image_new_from_file_scaled(const gchar * file, gint width, gint height, gboolean keep_ratio)
1120 {
1121 return _gtk_image_new_for_icon(NULL, fm_icon_from_name(file), height, NULL);
1122 }
1123
1124 GtkWidget *lxpanel_image_new_for_icon(LXPanel *panel, const gchar *name,
1125 gint height, const char *fallback)
1126 {
1127 return _gtk_image_new_for_icon(panel, fm_icon_from_name(name), height, fallback);
1128 }
1129
1130 GtkWidget *lxpanel_image_new_for_fm_icon(LXPanel *panel, FmIcon *icon,
1131 gint height, const char *fallback)
1132 {
1133 return _gtk_image_new_for_icon(panel, g_object_ref(icon), height, fallback);
1134 }
1135
1136 gboolean lxpanel_image_change_icon(GtkWidget *img, const gchar *name, const char *fallback)
1137 {
1138 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
1139
1140 g_return_val_if_fail(data != NULL && name != NULL, FALSE);
1141 g_object_unref(data->icon);
1142 g_free(data->fallback);
1143 data->icon = fm_icon_from_name(name);
1144 data->fallback = g_strdup(fallback);
1145 if (!G_IS_THEMED_ICON(data->icon))
1146 {
1147 if (data->theme_changed_handler != 0)
1148 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
1149 data->theme_changed_handler = 0;
1150 }
1151 else if (data->theme_changed_handler == 0)
1152 {
1153 /* This image is loaded from icon theme. Update the image if the icon theme is changed. */
1154 data->theme_changed_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(),
1155 "changed", G_CALLBACK(on_theme_changed), img);
1156 }
1157 _gtk_image_set_from_file_scaled(img, data);
1158 return TRUE;
1159 }
1160
1161 void
1162 get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
1163 {
1164 GtkWidget *b;
1165
1166 ENTER;
1167 b = gtk_button_new();
1168 gtk_widget_set_name(GTK_WIDGET(b), name);
1169 gtk_widget_set_can_focus(b, FALSE);
1170 gtk_widget_set_can_default(b, FALSE);
1171 gtk_container_set_border_width (GTK_CONTAINER (b), 0);
1172
1173 if (parent)
1174 gtk_container_add(parent, b);
1175
1176 gtk_widget_show(b);
1177 gtk_widget_size_request(b, req);
1178
1179 gtk_widget_destroy(b);
1180 RET();
1181 }
1182
1183
1184 guint32 gcolor2rgb24(GdkColor *color)
1185 {
1186 guint32 i;
1187
1188 ENTER;
1189
1190 i = (color->red * 0xFF / 0xFFFF) & 0xFF;
1191 i <<= 8;
1192 i |= (color->green * 0xFF / 0xFFFF) & 0xFF;
1193 i <<= 8;
1194 i |= (color->blue * 0xFF / 0xFFFF) & 0xFF;
1195 DBG("i=%x\n", i);
1196 RET(i);
1197 }
1198
1199 /* Handler for "enter-notify-event" signal on image that has highlighting requested. */
1200 static gboolean fb_button_enter(GtkImage * widget, GdkEventCrossing * event)
1201 {
1202 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1203 {
1204 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1205 if (data != NULL)
1206 {
1207 if (data->hilight == NULL)
1208 {
1209 GdkPixbuf * dark = data->pixbuf;
1210 int height = gdk_pixbuf_get_height(dark);
1211 int rowstride = gdk_pixbuf_get_rowstride(dark);
1212 gulong hicolor = data->hicolor;
1213
1214 GdkPixbuf * light = gdk_pixbuf_add_alpha(dark, FALSE, 0, 0, 0);
1215 if (light != NULL)
1216 {
1217 guchar extra[3];
1218 int i;
1219 for (i = 2; i >= 0; i--, hicolor >>= 8)
1220 extra[i] = hicolor & 0xFF;
1221
1222 guchar * src = gdk_pixbuf_get_pixels(light);
1223 guchar * up;
1224 for (up = src + height * rowstride; src < up; src += 4)
1225 {
1226 if (src[3] != 0)
1227 {
1228 for (i = 0; i < 3; i++)
1229 {
1230 int value = src[i] + extra[i];
1231 if (value > 255) value = 255;
1232 src[i] = value;
1233 }
1234 }
1235 }
1236 data->hilight = light;
1237 }
1238 }
1239
1240 if (data->hilight != NULL)
1241 gtk_image_set_from_pixbuf(widget, data->hilight);
1242 }
1243 }
1244 return TRUE;
1245 }
1246
1247 /* Handler for "leave-notify-event" signal on image that has highlighting requested. */
1248 static gboolean fb_button_leave(GtkImage * widget, GdkEventCrossing * event, gpointer user_data)
1249 {
1250 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
1251 {
1252 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1253 if ((data != NULL) && (data->pixbuf != NULL))
1254 gtk_image_set_from_pixbuf(widget, data->pixbuf);
1255 }
1256 return TRUE;
1257 }
1258
1259 static void on_font_changed(LXPanel * panel, GtkLabel * lbl)
1260 {
1261 const char *label = gtk_label_get_text(lbl);
1262 lxpanel_draw_label_text(panel, GTK_WIDGET(lbl), label, FALSE, 1, TRUE);
1263 }
1264
1265 static GtkWidget *_lxpanel_button_compose(GtkWidget *event_box, GtkWidget *image,
1266 gulong highlight_color, const gchar *label)
1267 {
1268 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(image), img_data_id);
1269
1270 gtk_misc_set_padding(GTK_MISC(image), 0, 0);
1271 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
1272 if (highlight_color != 0 && data != NULL)
1273 {
1274 data->hicolor = highlight_color;
1275 gtk_widget_add_events(event_box, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1276 g_signal_connect_swapped(G_OBJECT(event_box), "enter-notify-event", G_CALLBACK(fb_button_enter), image);
1277 g_signal_connect_swapped(G_OBJECT(event_box), "leave-notify-event", G_CALLBACK(fb_button_leave), image);
1278 }
1279
1280 if (label == NULL)
1281 gtk_container_add(GTK_CONTAINER(event_box), image);
1282 else
1283 {
1284 GtkWidget *inner, *lbl;
1285
1286 inner = gtk_hbox_new(FALSE, 0);
1287 gtk_container_set_border_width(GTK_CONTAINER(inner), 0);
1288 gtk_widget_set_can_focus(inner, FALSE);
1289 gtk_container_add(GTK_CONTAINER(event_box), inner);
1290
1291 gtk_box_pack_start(GTK_BOX(inner), image, FALSE, FALSE, 0);
1292
1293 lbl = gtk_label_new("");
1294 if (G_LIKELY(data != NULL && data->panel != NULL))
1295 {
1296 lxpanel_draw_label_text(data->panel, lbl, label, FALSE, 1, TRUE);
1297 data->font_changed_handler = g_signal_connect(data->panel,
1298 "panel-font-changed",
1299 G_CALLBACK(on_font_changed),
1300 lbl);
1301 }
1302 else
1303 gtk_label_set_text(GTK_LABEL(lbl), label);
1304 gtk_misc_set_padding(GTK_MISC(lbl), 2, 0);
1305 gtk_box_pack_end(GTK_BOX(inner), lbl, FALSE, FALSE, 0);
1306 }
1307
1308 gtk_widget_show_all(event_box);
1309 return event_box;
1310 }
1311
1312 GtkWidget *lxpanel_button_compose(GtkWidget *event_box, GtkWidget *image,
1313 GdkColor *color, const gchar *label)
1314 {
1315 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1316 return _lxpanel_button_compose(event_box, image, highlight_color, label);
1317 }
1318
1319 /* consumes reference on icon */
1320 static GtkWidget *_lxpanel_button_new_for_icon(LXPanel *panel, FmIcon *icon,
1321 gint size, gulong highlight_color,
1322 const gchar *label)
1323 {
1324 GtkWidget * event_box = gtk_event_box_new();
1325 gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
1326 gtk_widget_set_can_focus(event_box, FALSE);
1327
1328 GtkWidget * image = _gtk_image_new_for_icon(panel, icon, size, NULL);
1329 return _lxpanel_button_compose(event_box, image, highlight_color, label);
1330 }
1331
1332 GtkWidget *lxpanel_button_new_for_icon(LXPanel *panel, const gchar *name, GdkColor *color, const gchar *label)
1333 {
1334 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1335 return _lxpanel_button_new_for_icon(panel, fm_icon_from_name(name), -1,
1336 highlight_color, label);
1337 }
1338
1339 GtkWidget *lxpanel_button_new_for_fm_icon(LXPanel *panel, FmIcon *icon, GdkColor *color, const gchar *label)
1340 {
1341 gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
1342 return _lxpanel_button_new_for_icon(panel, g_object_ref(icon), -1,
1343 highlight_color, label);
1344 }
1345
1346 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1347 GtkWidget * fb_button_new_from_file(
1348 const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio)
1349 {
1350 return fb_button_new_from_file_with_label(image_file, width, height, highlight_color, keep_ratio, NULL, NULL);
1351 }
1352
1353 /* parameters width and keep_ratio are unused, kept for backward compatibility */
1354 GtkWidget * fb_button_new_from_file_with_label(
1355 const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio, Panel * panel, const gchar * label)
1356 {
1357 return _lxpanel_button_new_for_icon(panel->topgwin, fm_icon_from_name(image_file), height, highlight_color, label);
1358 }
1359
1360 char* translate_exec_to_cmd( const char* exec, const char* icon,
1361 const char* title, const char* fpath )
1362 {
1363 GString* cmd = g_string_sized_new( 256 );
1364 if (!exec)
1365 return NULL;
1366 for( ; *exec; ++exec )
1367 {
1368 if( G_UNLIKELY(*exec == '%') )
1369 {
1370 ++exec;
1371 if( !*exec )
1372 break;
1373 switch( *exec )
1374 {
1375 case 'c':
1376 if( title )
1377 {
1378 g_string_append( cmd, title );
1379 }
1380 break;
1381 case 'i':
1382 if( icon )
1383 {
1384 g_string_append( cmd, "--icon " );
1385 g_string_append( cmd, icon );
1386 }
1387 break;
1388 case 'k':
1389 if( fpath )
1390 {
1391 char* uri = g_filename_to_uri( fpath, NULL, NULL );
1392 g_string_append( cmd, uri );
1393 g_free( uri );
1394 }
1395 break;
1396 case '%':
1397 g_string_append_c( cmd, '%' );
1398 break;
1399 }
1400 }
1401 else
1402 g_string_append_c( cmd, *exec );
1403 }
1404 return g_string_free( cmd, FALSE );
1405 }
1406
1407 /*
1408 This function is used to re-create a new box with different
1409 orientation from the old one, add all children of the old one to
1410 the new one, and then destroy the old box.
1411 It's mainly used when we need to change the orientation of the panel or
1412 any plugin with a layout box. Since GtkHBox cannot be changed to GtkVBox,
1413 recreating a new box to replace the old one is required.
1414 */
1415 /* for compatibility with old plugins */
1416 GtkWidget* recreate_box( GtkBox* oldbox, GtkOrientation orientation )
1417 {
1418 gtk_orientable_set_orientation(GTK_ORIENTABLE(oldbox), orientation);
1419 return GTK_WIDGET(oldbox);
1420 }
1421
1422 /* for compatibility with old plugins */
1423 void show_error( GtkWindow* parent_win, const char* msg )
1424 {
1425 fm_show_error(parent_win, NULL, msg);
1426 }
1427
1428 /* old plugins compatibility mode, use fm_pixbuf_from_icon_with_fallback() instead */
1429 GdkPixbuf * lxpanel_load_icon(const char * name, int width, int height, gboolean use_fallback)
1430 {
1431 FmIcon * fm_icon;
1432 GdkPixbuf * icon = NULL;
1433
1434 fm_icon = fm_icon_from_name(name ? name : "application-x-executable");
1435 /* well, we don't use parameter width and not really use cache here */
1436 icon = fm_pixbuf_from_icon_with_fallback(fm_icon, height,
1437 use_fallback ? "application-x-executable" : NULL);
1438 g_object_unref(fm_icon);
1439 return icon;
1440 }
1441
1442 /*
1443 * Taken from pcmanfm:
1444 * Parse Exec command line of app desktop file, and translate
1445 * it into a real command which can be passed to g_spawn_command_line_async().
1446 * file_list is a null-terminated file list containing full
1447 * paths of the files passed to app.
1448 * returned char* should be freed when no longer needed.
1449 */
1450 static char* translate_app_exec_to_command_line( const char* pexec,
1451 GList* file_list )
1452 {
1453 char* file;
1454 GList* l;
1455 gchar *tmp;
1456 GString* cmd = g_string_new("");
1457 gboolean add_files = FALSE;
1458
1459 for( ; *pexec; ++pexec )
1460 {
1461 if( *pexec == '%' )
1462 {
1463 ++pexec;
1464 switch( *pexec )
1465 {
1466 case 'U':
1467 for( l = file_list; l; l = l->next )
1468 {
1469 tmp = g_filename_to_uri( (char*)l->data, NULL, NULL );
1470 file = g_shell_quote( tmp );
1471 g_free( tmp );
1472 g_string_append( cmd, file );
1473 if (l->next)
1474 g_string_append_c( cmd, ' ' );
1475 g_free( file );
1476 }
1477 add_files = TRUE;
1478 break;
1479 case 'u':
1480 if( file_list && file_list->data )
1481 {
1482 file = (char*)file_list->data;
1483 tmp = g_filename_to_uri( file, NULL, NULL );
1484 file = g_shell_quote( tmp );
1485 g_free( tmp );
1486 g_string_append( cmd, file );
1487 g_free( file );
1488 add_files = TRUE;
1489 }
1490 break;
1491 case 'F':
1492 case 'N':
1493 for( l = file_list; l; l = l->next )
1494 {
1495 file = (char*)l->data;
1496 tmp = g_shell_quote( file );
1497 g_string_append( cmd, tmp );
1498 if (l->next)
1499 g_string_append_c( cmd, ' ' );
1500 g_free( tmp );
1501 }
1502 add_files = TRUE;
1503 break;
1504 case 'f':
1505 case 'n':
1506 if( file_list && file_list->data )
1507 {
1508 file = (char*)file_list->data;
1509 tmp = g_shell_quote( file );
1510 g_string_append( cmd, tmp );
1511 g_free( tmp );
1512 add_files = TRUE;
1513 }
1514 break;
1515 case 'D':
1516 for( l = file_list; l; l = l->next )
1517 {
1518 tmp = g_path_get_dirname( (char*)l->data );
1519 file = g_shell_quote( tmp );
1520 g_free( tmp );
1521 g_string_append( cmd, file );
1522 if (l->next)
1523 g_string_append_c( cmd, ' ' );
1524 g_free( file );
1525 }
1526 add_files = TRUE;
1527 break;
1528 case 'd':
1529 if( file_list && file_list->data )
1530 {
1531 tmp = g_path_get_dirname( (char*)file_list->data );
1532 file = g_shell_quote( tmp );
1533 g_free( tmp );
1534 g_string_append( cmd, file );
1535 g_free( tmp );
1536 add_files = TRUE;
1537 }
1538 break;
1539 case 'c':
1540 #if 0
1541 g_string_append( cmd, vfs_app_desktop_get_disp_name( app ) );
1542 #endif
1543 break;
1544 case 'i':
1545 /* Add icon name */
1546 #if 0
1547 if( vfs_app_desktop_get_icon_name( app ) )
1548 {
1549 g_string_append( cmd, "--icon " );
1550 g_string_append( cmd, vfs_app_desktop_get_icon_name( app ) );
1551 }
1552 #endif
1553 break;
1554 case 'k':
1555 /* Location of the desktop file */
1556 break;
1557 case 'v':
1558 /* Device name */
1559 break;
1560 case '%':
1561 g_string_append_c ( cmd, '%' );
1562 break;
1563 case '\0':
1564 goto _finish;
1565 break;
1566 }
1567 }
1568 else /* not % escaped part */
1569 {
1570 g_string_append_c ( cmd, *pexec );
1571 }
1572 }
1573 _finish:
1574 if( ! add_files )
1575 {
1576 for( l = file_list; l; l = l->next )
1577 {
1578 g_string_append_c( cmd, ' ' );
1579 file = (char*)l->data;
1580 tmp = g_shell_quote( file );
1581 g_string_append( cmd, tmp );
1582 g_free( tmp );
1583 }
1584 }
1585
1586 return g_string_free( cmd, FALSE );
1587 }
1588
1589 gboolean spawn_command_async(GtkWindow *parent_window, gchar const* workdir,
1590 gchar const* cmd)
1591 {
1592 GError* err = NULL;
1593 gchar** argv = NULL;
1594
1595 g_info("lxpanel: spawning \"%s\"...", cmd);
1596
1597 g_shell_parse_argv(cmd, NULL, &argv, &err);
1598 if (!err)
1599 g_spawn_async(workdir, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
1600
1601 if (err)
1602 {
1603 g_warning("%s\n", err->message);
1604 fm_show_error(parent_window, NULL, err->message);
1605 g_error_free(err);
1606 }
1607
1608 g_strfreev(argv);
1609
1610 return !err;
1611 }
1612
1613 /* FIXME: this should be replaced with fm_launch_file_simple() */
1614 gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal, char const* in_workdir)
1615 {
1616 GError *error = NULL;
1617 char* cmd;
1618 if( ! exec )
1619 return FALSE;
1620 cmd = translate_app_exec_to_command_line(exec, files);
1621 if( in_terminal )
1622 {
1623 char * escaped_cmd = g_shell_quote(cmd);
1624 char* term_cmd;
1625 const char* term = fm_config->terminal ? fm_config->terminal : "lxterminal";
1626 if( strstr(term, "%s") )
1627 term_cmd = g_strdup_printf(term, escaped_cmd);
1628 else
1629 term_cmd = g_strconcat( term, " -e ", escaped_cmd, NULL );
1630 g_free(escaped_cmd);
1631 if( cmd != exec )
1632 g_free(cmd);
1633 cmd = term_cmd;
1634 }
1635
1636 spawn_command_async(NULL, in_workdir, cmd);
1637
1638 g_free(cmd);
1639
1640 return (error == NULL);
1641 }
1642
1643 /* vim: set sw=4 et sts=4 : */