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