Adding upstream version 0.9.0.
[debian/lxpanel.git] / src / misc.c
CommitLineData
0688b017
AG
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.
6cc5e1a6
DB
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>
6b775dbb 41#include <libfm/fm-gtk.h>
6cc5e1a6
DB
42
43#include "misc.h"
6b775dbb 44#include "private.h"
6cc5e1a6 45
6cc5e1a6
DB
46#include "dbg.h"
47
96dc8a45 48/* data used by themed images buttons */
2ba86315 49typedef struct {
6b775dbb 50 FmIcon *icon;
96dc8a45 51 guint theme_changed_handler;
f7ecd6ce
AG
52 guint icon_changed_handler;
53 guint font_changed_handler;
96dc8a45
DB
54 GdkPixbuf* pixbuf;
55 GdkPixbuf* hilight;
56 gulong hicolor;
6b775dbb 57 gint size; /* desired size */
f7ecd6ce
AG
58 LXPanel *panel;
59 char *fallback;
2ba86315 60} ImgData;
96dc8a45
DB
61
62static GQuark img_data_id = 0;
6cc5e1a6 63
6b775dbb 64static void _gtk_image_set_from_file_scaled(GtkWidget *img, ImgData *data);
7486d297 65
6cc5e1a6
DB
66/* X11 data types */
67Atom a_UTF8_STRING;
68Atom a_XROOTPMAP_ID;
69
70/* old WM spec */
71Atom a_WM_STATE;
7a1c5048 72Atom a_WM_CLASS = XA_WM_CLASS;
6cc5e1a6
DB
73Atom a_WM_DELETE_WINDOW;
74Atom a_WM_PROTOCOLS;
75
76/* new NET spec */
77Atom a_NET_WORKAREA;
78Atom a_NET_CLIENT_LIST;
79Atom a_NET_CLIENT_LIST_STACKING;
80Atom a_NET_NUMBER_OF_DESKTOPS;
81Atom a_NET_CURRENT_DESKTOP;
82Atom a_NET_DESKTOP_VIEWPORT;
83Atom a_NET_DESKTOP_NAMES;
84Atom a_NET_ACTIVE_WINDOW;
85Atom a_NET_CLOSE_WINDOW;
aa0e9095 86Atom a_NET_SHOWING_DESKTOP;
6cc5e1a6
DB
87Atom a_NET_SUPPORTED;
88Atom a_NET_WM_STATE;
89Atom a_NET_WM_STATE_SKIP_TASKBAR;
90Atom a_NET_WM_STATE_SKIP_PAGER;
91Atom a_NET_WM_STATE_STICKY;
92Atom a_NET_WM_STATE_HIDDEN;
93Atom a_NET_WM_STATE_SHADED;
94Atom a_NET_WM_WINDOW_TYPE;
95Atom a_NET_WM_WINDOW_TYPE_DESKTOP;
96Atom a_NET_WM_WINDOW_TYPE_DOCK;
97Atom a_NET_WM_WINDOW_TYPE_TOOLBAR;
98Atom a_NET_WM_WINDOW_TYPE_MENU;
99Atom a_NET_WM_WINDOW_TYPE_UTILITY;
100Atom a_NET_WM_WINDOW_TYPE_SPLASH;
101Atom a_NET_WM_WINDOW_TYPE_DIALOG;
102Atom a_NET_WM_WINDOW_TYPE_NORMAL;
103Atom a_NET_WM_DESKTOP;
104Atom a_NET_WM_PID;
105Atom a_NET_WM_NAME;
106Atom a_NET_WM_VISIBLE_NAME;
107Atom a_NET_WM_STRUT;
108Atom a_NET_WM_STRUT_PARTIAL;
109Atom a_NET_WM_ICON;
110Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
111
2ba86315
DB
112/* SYSTEM TRAY spec */
113Atom a_NET_SYSTEM_TRAY_OPCODE;
114Atom a_NET_SYSTEM_TRAY_MESSAGE_DATA;
115Atom a_NET_SYSTEM_TRAY_ORIENTATION;
116Atom a_MANAGER;
117
6cc5e1a6
DB
118Atom a_LXPANEL_CMD; /* for private client message */
119
aa0e9095
DB
120/* if current window manager is EWMH conforming. */
121gboolean is_ewmh_supported;
122
6cc5e1a6
DB
123enum{
124 I_UTF8_STRING,
125 I_XROOTPMAP_ID,
126 I_WM_STATE,
6cc5e1a6
DB
127 I_WM_DELETE_WINDOW,
128 I_WM_PROTOCOLS,
aa0e9095 129
6cc5e1a6
DB
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,
2ba86315 135 I_NET_DESKTOP_VIEWPORT,
6cc5e1a6
DB
136 I_NET_DESKTOP_NAMES,
137 I_NET_ACTIVE_WINDOW,
aa0e9095 138 I_NET_SHOWING_DESKTOP,
6cc5e1a6
DB
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
2ba86315
DB
166 I_NET_SYSTEM_TRAY_OPCODE,
167 I_NET_SYSTEM_TRAY_MESSAGE_DATA,
168 I_NET_SYSTEM_TRAY_ORIENTATION,
169 I_MANAGER,
170
6cc5e1a6
DB
171 I_LXPANEL_CMD,
172 N_ATOMS
173};
174
175pair allign_pair[] = {
f7ecd6ce
AG
176 { ALIGN_NONE, "none" },
177 { ALIGN_LEFT, "left" },
178 { ALIGN_RIGHT, "right" },
179 { ALIGN_CENTER, "center"},
6cc5e1a6
DB
180 { 0, NULL },
181};
182
183pair 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
192pair width_pair[] = {
193 { WIDTH_NONE, "none" },
194 { WIDTH_REQUEST, "request" },
195 { WIDTH_PIXEL, "pixel" },
196 { WIDTH_PERCENT, "percent" },
197 { 0, NULL },
198};
199
200pair height_pair[] = {
201 { HEIGHT_NONE, "none" },
202 { HEIGHT_PIXEL, "pixel" },
203 { 0, NULL },
204};
205
206pair bool_pair[] = {
207 { 0, "0" },
208 { 1, "1" },
209 { 0, NULL },
210};
6cc5e1a6
DB
211
212int
6b775dbb 213str2num(pair *p, const gchar *str, int defval)
6cc5e1a6
DB
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
6b775dbb
AG
223const gchar *
224num2str(pair *p, int num, const gchar *defval)
6cc5e1a6
DB
225{
226 ENTER;
227 for (;p && p->str; p++) {
228 if (num == p->num)
229 RET(p->str);
230 }
231 RET(defval);
232}
233
234int 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
262extern int
263lxpanel_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
6cc5e1a6
DB
285extern int
286lxpanel_get_line(char**fp, line *s)
287{
288 gchar *tmp, *tmp2;
96dc8a45 289
6cc5e1a6
DB
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 {
6b775dbb 317 g_warning( "parser: unknown token: '%c'", *tmp2);
6cc5e1a6
DB
318 }
319 break;
320 }
96dc8a45 321 return s->type;
6cc5e1a6
DB
322}
323
6cc5e1a6
DB
324void 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";
6cc5e1a6
DB
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";
2ba86315 338 atom_names[ I_NET_DESKTOP_VIEWPORT ] = "_NET_DESKTOP_VIEWPORT";
6cc5e1a6
DB
339 atom_names[ I_NET_DESKTOP_NAMES ] = "_NET_DESKTOP_NAMES";
340 atom_names[ I_NET_ACTIVE_WINDOW ] = "_NET_ACTIVE_WINDOW";
aa0e9095 341 atom_names[ I_NET_SHOWING_DESKTOP ] = "_NET_SHOWING_DESKTOP";
6cc5e1a6
DB
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";
2ba86315
DB
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
6cc5e1a6
DB
374 atom_names[ I_LXPANEL_CMD ] = "_LXPANEL_CMD";
375
376 Atom atoms[ N_ATOMS ];
377
378 ENTER;
6b775dbb
AG
379
380 if( ! XInternAtoms( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), (char**)atom_names,
7486d297 381 N_ATOMS, False, atoms ) )
6cc5e1a6
DB
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 ];
6cc5e1a6
DB
390 a_WM_DELETE_WINDOW = atoms[ I_WM_DELETE_WINDOW ];
391 a_WM_PROTOCOLS = atoms[ I_WM_PROTOCOLS ];
aa0e9095 392
6cc5e1a6
DB
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 ];
2ba86315 398 a_NET_DESKTOP_VIEWPORT = atoms[ I_NET_DESKTOP_VIEWPORT ];
6cc5e1a6
DB
399 a_NET_DESKTOP_NAMES = atoms[ I_NET_DESKTOP_NAMES ];
400 a_NET_ACTIVE_WINDOW = atoms[ I_NET_ACTIVE_WINDOW ];
aa0e9095 401 a_NET_SHOWING_DESKTOP = atoms[ I_NET_SHOWING_DESKTOP ];
6cc5e1a6
DB
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 ];
2ba86315 420 a_NET_WM_PID = atoms[ I_NET_WM_PID ];
6cc5e1a6
DB
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
2ba86315
DB
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
6cc5e1a6
DB
433 a_LXPANEL_CMD = atoms[ I_LXPANEL_CMD ];
434
435 RET();
436}
437
438
439void
7a1c5048 440Xclimsgx(Screen *screen, Window win, Atom type, long l0, long l1, long l2, long l3, long l4)
6cc5e1a6 441{
7a1c5048 442 Display *display = DisplayOfScreen(screen);
6cc5e1a6 443 XClientMessageEvent xev;
6cc5e1a6 444 xev.type = ClientMessage;
7a1c5048
AG
445 xev.serial = 0;
446 xev.send_event = True;
447 xev.display = display;
6cc5e1a6
DB
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;
7a1c5048
AG
456 // _error_trap_push (display);
457 XSendEvent(display, RootWindowOfScreen(screen), False,
458 (SubstructureNotifyMask | SubstructureRedirectMask),
459 (XEvent *) &xev);
460 // _error_trap_pop (display);
461}
462
463void
464Xclimsg(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);
6cc5e1a6
DB
468}
469
470void
471Xclimsgwm(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;
6b775dbb 481 XSendEvent(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, False, 0L, (XEvent *) &xev);
6cc5e1a6
DB
482}
483
484
485void *
486get_utf8_property(Window win, Atom atom)
487{
6cc5e1a6
DB
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;
6b775dbb 498 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, atom, 0, G_MAXLONG, False,
6cc5e1a6
DB
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
513char **
514get_utf8_property_list(Window win, Atom atom, int *count)
515{
516 Atom type;
6b775dbb
AG
517 int format;
518 gulong nitems, i;
6cc5e1a6
DB
519 gulong bytes_after;
520 gchar *s, **retval = NULL;
521 int result;
522 guchar *tmp = NULL;
523
524 *count = 0;
6b775dbb 525 result = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, atom, 0, G_MAXLONG, False,
6cc5e1a6
DB
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);
6b775dbb 539 for (i = 0, s = val; (int)i < *count; i++, s = s + strlen (s) + 1) {
6cc5e1a6
DB
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
558void *
559get_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;
7dd482c5 569 if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, prop, 0, G_MAXLONG, False,
6cc5e1a6 570 type, &type_ret, &format_ret, &items_ret,
f7ecd6ce 571 &after_ret, &prop_data) != Success || items_ret == 0)
6cc5e1a6
DB
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
584static char*
585text_property_to_utf8 (const XTextProperty *prop)
586{
587 char **list;
588 int count;
589 char *retval;
590
591 ENTER;
592 list = NULL;
6b775dbb
AG
593 count = gdk_text_property_to_utf8_list_for_display (gdk_display_get_default(),
594 gdk_x11_xatom_to_atom (prop->encoding),
6cc5e1a6
DB
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
612char *
613get_textproperty(Window win, Atom atom)
614{
615 XTextProperty text_prop;
616 char *retval;
617
618 ENTER;
6b775dbb 619 if (XGetTextProperty(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, &text_prop, atom)) {
6cc5e1a6
DB
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
635int
636get_net_number_of_desktops()
637{
638 int desknum;
7dd482c5 639 gulong *data;
6cc5e1a6
DB
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
653int
654get_net_current_desktop ()
655{
656 int desk;
7dd482c5 657 gulong *data;
6cc5e1a6
DB
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
669int
670get_net_wm_desktop(Window win)
671{
672 int desk = 0;
7dd482c5 673 gulong *data;
6cc5e1a6
DB
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
684GPid
685get_net_wm_pid(Window win)
686{
687 GPid pid = 0;
7dd482c5 688 gulong *data;
6cc5e1a6
DB
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
699void
700get_net_wm_state(Window win, NetWMState *nws)
701{
702 Atom *state;
703 int num3;
704
705
706 ENTER;
aaccad27 707 memset(nws, 0, sizeof(*nws));
6cc5e1a6
DB
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;
7a1c5048 728 //FIXME: modal maximized_vert maximized_horz fullscreen above below demands_attention
6cc5e1a6
DB
729 } else {
730 DBG( "... ");
731 }
732 }
733 XFree(state);
734 DBG( "}\n");
735 RET();
736}
737
6cc5e1a6
DB
738void
739get_net_wm_window_type(Window win, NetWMWindowType *nwwt)
740{
741 Atom *state;
742 int num3;
743
744
745 ENTER;
aaccad27 746 memset(nwwt, 0, sizeof(*nwwt));
6cc5e1a6
DB
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
6cc5e1a6
DB
785int
786get_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
6b775dbb
AG
800int 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
809int 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
6cc5e1a6 816static void
7a1c5048 817calculate_width(int scrw, int pw, int wtype, int align, int margin,
6cc5e1a6
DB
818 int *panw, int *x)
819{
820 ENTER;
821 DBG("scrw=%d\n", scrw);
2ba86315 822 DBG("IN panw=%d, margin=%d\n", *panw, margin);
6cc5e1a6 823 //scrw -= 2;
7a1c5048
AG
824 if (wtype != WIDTH_REQUEST)
825 *panw = pw;
6cc5e1a6
DB
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 }
f7ecd6ce 834 if (align != ALIGN_CENTER) {
6cc5e1a6 835 if (margin > scrw) {
6b775dbb 836 g_warning( "margin is bigger then edge size %d > %d. Ignoring margin",
6cc5e1a6
DB
837 margin, scrw);
838 margin = 0;
839 }
2ba86315 840 *panw = MIN(scrw - margin, *panw);
6cc5e1a6
DB
841 }
842 DBG("OUT panw=%d\n", *panw);
f7ecd6ce 843 if (align == ALIGN_LEFT)
6cc5e1a6 844 *x += margin;
f7ecd6ce 845 else if (align == ALIGN_RIGHT) {
6cc5e1a6
DB
846 *x += scrw - *panw - margin;
847 if (*x < 0)
848 *x = 0;
f7ecd6ce 849 } else if (align == ALIGN_CENTER)
6cc5e1a6
DB
850 *x += (scrw - *panw) / 2;
851 RET();
852}
853
854
f7ecd6ce 855void _calculate_position(LXPanel *panel, GdkRectangle *rect)
6cc5e1a6 856{
6b775dbb
AG
857 Panel *np = panel->priv;
858 GdkScreen *screen;
859 GdkRectangle marea;
6cc5e1a6
DB
860
861 ENTER;
7a1c5048 862 screen = gtk_widget_get_screen(GTK_WIDGET(panel));
f7ecd6ce
AG
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))
6b775dbb 871 gdk_screen_get_monitor_geometry(screen,np->monitor,&marea);
f7ecd6ce
AG
872 else
873 {
874 marea.x = 0;
875 marea.y = 0;
876 marea.width = 0;
877 marea.height = 0;
6cc5e1a6
DB
878 }
879
880 if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
f7ecd6ce 881 rect->x = marea.x;
7a1c5048 882 calculate_width(marea.width, np->width, np->widthtype, np->align, np->margin,
f7ecd6ce
AG
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));
6cc5e1a6
DB
886
887 } else {
f7ecd6ce 888 rect->y = marea.y;
7a1c5048 889 calculate_width(marea.height, np->width, np->widthtype, np->align, np->margin,
f7ecd6ce
AG
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));
6cc5e1a6
DB
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
6b775dbb
AG
898void calculate_position(Panel *np)
899{
f7ecd6ce
AG
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;
6b775dbb
AG
909}
910
6cc5e1a6 911gchar *
6b775dbb 912expand_tilda(const gchar *file)
6cc5e1a6
DB
913{
914 ENTER;
915 RET((file[0] == '~') ?
916 g_strdup_printf("%s%s", getenv("HOME"), file+1)
917 : g_strdup(file));
918
919}
920
6cc5e1a6
DB
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
2ba86315
DB
933/* DestroyNotify handler for image data in _gtk_image_new_from_file_scaled. */
934static void img_data_free(ImgData * data)
96dc8a45 935{
6b775dbb 936 g_object_unref(data->icon);
2ba86315
DB
937 if (data->theme_changed_handler != 0)
938 g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
f7ecd6ce
AG
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 }
2ba86315
DB
946 if (data->pixbuf != NULL)
947 g_object_unref(data->pixbuf);
948 if (data->hilight != NULL)
949 g_object_unref(data->hilight);
f7ecd6ce
AG
950 if (data->fallback != NULL)
951 g_free(data->fallback);
2ba86315 952 g_free(data);
96dc8a45 953}
6cc5e1a6 954
2ba86315 955/* Handler for "changed" signal in _gtk_image_new_from_file_scaled. */
f7ecd6ce 956static void on_theme_changed(GtkWidget * img, GObject * object)
6cc5e1a6 957{
2ba86315 958 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
6b775dbb 959 _gtk_image_set_from_file_scaled(img, data);
7486d297
DB
960}
961
6b775dbb
AG
962/* consumes reference on icon */
963static void _lxpanel_button_set_icon(GtkWidget* btn, FmIcon* icon, gint size)
7486d297 964{
2ba86315
DB
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))
7486d297 969 img = child;
2ba86315 970 else if (GTK_IS_BOX(child))
7486d297 971 {
2ba86315 972 GList * children = gtk_container_get_children(GTK_CONTAINER(child));
7486d297 973 img = GTK_WIDGET(GTK_IMAGE(children->data));
2ba86315 974 g_list_free(children);
7486d297
DB
975 }
976
2ba86315 977 if (img != NULL)
7486d297 978 {
2ba86315 979 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
6b775dbb 980
f7ecd6ce
AG
981 if (size == 0) /* 0: set as before; -1: reset to panel icon size */
982 size = data->size;
6b775dbb
AG
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);
7486d297 992 }
6b775dbb
AG
993 else
994 g_object_unref(icon);
96dc8a45
DB
995}
996
6b775dbb 997void lxpanel_button_set_icon(GtkWidget* btn, const gchar *name, gint size)
96dc8a45 998{
6b775dbb
AG
999 _lxpanel_button_set_icon(btn, fm_icon_from_name(name), size);
1000}
6cc5e1a6 1001
6b775dbb
AG
1002void lxpanel_button_update_icon(GtkWidget* btn, FmIcon *icon, gint size)
1003{
1004 _lxpanel_button_set_icon(btn, g_object_ref(icon), size);
1005}
1006
f7ecd6ce
AG
1007gboolean 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
6b775dbb
AG
1039/* parameters width and keep_ratio are unused, kept for backward compatibility */
1040void 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
1045static void _gtk_image_set_from_file_scaled(GtkWidget * img, ImgData * data)
1046{
f7ecd6ce
AG
1047 gint size = data->size;
1048
1049 if (size < 0 && data->panel)
1050 size = data->panel->priv->icon_size;
1051
2ba86315 1052 if (data->pixbuf != NULL)
96dc8a45 1053 {
2ba86315 1054 g_object_unref(data->pixbuf);
96dc8a45
DB
1055 data->pixbuf = NULL;
1056 }
2ba86315 1057
96dc8a45 1058 /* if there is a cached hilighted version of this pixbuf, free it */
2ba86315 1059 if (data->hilight != NULL)
96dc8a45 1060 {
2ba86315 1061 g_object_unref(data->hilight);
96dc8a45
DB
1062 data->hilight = NULL;
1063 }
1064
6b775dbb 1065 if (G_LIKELY(G_IS_THEMED_ICON(data->icon)))
f7ecd6ce
AG
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 }
6b775dbb 1073 else
96dc8a45 1074 {
6b775dbb 1075 char *file = g_icon_to_string(fm_icon_get_gicon(data->icon));
f7ecd6ce 1076 data->pixbuf = gdk_pixbuf_new_from_file_at_scale(file, -1, size, TRUE, NULL);
6b775dbb 1077 g_free(file);
7486d297 1078 }
6cc5e1a6 1079
f7ecd6ce
AG
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
2ba86315 1086 if (data->pixbuf != NULL)
96dc8a45 1087 {
2ba86315
DB
1088 /* Set the pixbuf into the image widget. */
1089 gtk_image_set_from_pixbuf((GtkImage *)img, data->pixbuf);
96dc8a45 1090 }
2ba86315
DB
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 }
96dc8a45
DB
1096}
1097
6b775dbb 1098/* consumes reference on icon */
f7ecd6ce
AG
1099static GtkWidget *_gtk_image_new_for_icon(LXPanel *p, FmIcon *icon, gint size,
1100 const char *fallback)
96dc8a45 1101{
2ba86315
DB
1102 GtkWidget * img = gtk_image_new();
1103 ImgData * data = g_new0(ImgData, 1);
6b775dbb
AG
1104
1105 data->icon = icon;
1106 data->size = size;
f7ecd6ce 1107 data->fallback = g_strdup(fallback);
2ba86315 1108 if (img_data_id == 0)
96dc8a45 1109 img_data_id = g_quark_from_static_string("ImgData");
2ba86315 1110 g_object_set_qdata_full(G_OBJECT(img), img_data_id, data, (GDestroyNotify) img_data_free);
f7ecd6ce
AG
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 }
6b775dbb
AG
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. */
f7ecd6ce
AG
1123 data->theme_changed_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(),
1124 "changed", G_CALLBACK(on_theme_changed), img);
6b775dbb 1125 }
96dc8a45 1126 return img;
6cc5e1a6
DB
1127}
1128
6b775dbb
AG
1129/* parameters width and keep_ratio are unused, kept for backward compatibility */
1130GtkWidget * _gtk_image_new_from_file_scaled(const gchar * file, gint width, gint height, gboolean keep_ratio)
1131{
f7ecd6ce
AG
1132 return _gtk_image_new_for_icon(NULL, fm_icon_from_name(file), height, NULL);
1133}
1134
1135GtkWidget *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
1141GtkWidget *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
1147gboolean 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;
6b775dbb 1170}
6cc5e1a6
DB
1171
1172void
1173get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
1174{
1175 GtkWidget *b;
6cc5e1a6
DB
1176
1177 ENTER;
1178 b = gtk_button_new();
1179 gtk_widget_set_name(GTK_WIDGET(b), name);
6b775dbb
AG
1180 gtk_widget_set_can_focus(b, FALSE);
1181 gtk_widget_set_can_default(b, FALSE);
6cc5e1a6
DB
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
1195guint32 gcolor2rgb24(GdkColor *color)
1196{
1197 guint32 i;
6cc5e1a6
DB
1198
1199 ENTER;
1200
6cc5e1a6
DB
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
2ba86315
DB
1210/* Handler for "enter-notify-event" signal on image that has highlighting requested. */
1211static gboolean fb_button_enter(GtkImage * widget, GdkEventCrossing * event)
6cc5e1a6 1212{
2ba86315 1213 if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
96dc8a45 1214 {
2ba86315
DB
1215 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
1216 if (data != NULL)
96dc8a45 1217 {
2ba86315 1218 if (data->hilight == NULL)
96dc8a45 1219 {
2ba86315
DB
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 }
6cc5e1a6 1249 }
2ba86315
DB
1250
1251 if (data->hilight != NULL)
1252 gtk_image_set_from_pixbuf(widget, data->hilight);
6cc5e1a6 1253 }
6cc5e1a6 1254 }
96dc8a45 1255 return TRUE;
6cc5e1a6
DB
1256}
1257
2ba86315
DB
1258/* Handler for "leave-notify-event" signal on image that has highlighting requested. */
1259static gboolean fb_button_leave(GtkImage * widget, GdkEventCrossing * event, gpointer user_data)
6cc5e1a6 1260{
2ba86315
DB
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 }
96dc8a45 1267 return TRUE;
6cc5e1a6
DB
1268}
1269
f7ecd6ce
AG
1270static 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}
6cc5e1a6 1275
f7ecd6ce
AG
1276static GtkWidget *_lxpanel_button_compose(GtkWidget *event_box, GtkWidget *image,
1277 gulong highlight_color, const gchar *label)
9a3614f5 1278{
f7ecd6ce 1279 ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(image), img_data_id);
2ba86315 1280
2ba86315 1281 gtk_misc_set_padding(GTK_MISC(image), 0, 0);
6b775dbb 1282 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
f7ecd6ce 1283 if (highlight_color != 0 && data != NULL)
96dc8a45 1284 {
2ba86315 1285 data->hicolor = highlight_color;
2ba86315
DB
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 }
6cc5e1a6 1290
2ba86315
DB
1291 if (label == NULL)
1292 gtk_container_add(GTK_CONTAINER(event_box), image);
1293 else
1294 {
f7ecd6ce
AG
1295 GtkWidget *inner, *lbl;
1296
1297 inner = gtk_hbox_new(FALSE, 0);
2ba86315 1298 gtk_container_set_border_width(GTK_CONTAINER(inner), 0);
6b775dbb 1299 gtk_widget_set_can_focus(inner, FALSE);
2ba86315 1300 gtk_container_add(GTK_CONTAINER(event_box), inner);
6cc5e1a6 1301
2ba86315 1302 gtk_box_pack_start(GTK_BOX(inner), image, FALSE, FALSE, 0);
6cc5e1a6 1303
f7ecd6ce
AG
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);
2ba86315
DB
1315 gtk_misc_set_padding(GTK_MISC(lbl), 2, 0);
1316 gtk_box_pack_end(GTK_BOX(inner), lbl, FALSE, FALSE, 0);
6cc5e1a6 1317 }
2ba86315
DB
1318
1319 gtk_widget_show_all(event_box);
1320 return event_box;
6cc5e1a6
DB
1321}
1322
f7ecd6ce
AG
1323GtkWidget *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 */
1331static 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
6b775dbb
AG
1343GtkWidget *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;
f7ecd6ce
AG
1346 return _lxpanel_button_new_for_icon(panel, fm_icon_from_name(name), -1,
1347 highlight_color, label);
6b775dbb
AG
1348}
1349
1350GtkWidget *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;
f7ecd6ce
AG
1353 return _lxpanel_button_new_for_icon(panel, g_object_ref(icon), -1,
1354 highlight_color, label);
6b775dbb
AG
1355}
1356
1357/* parameters width and keep_ratio are unused, kept for backward compatibility */
1358GtkWidget * 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 */
1365GtkWidget * 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
6cc5e1a6
DB
1371char* 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 );
6b775dbb
AG
1375 if (!exec)
1376 return NULL;
6cc5e1a6
DB
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':
6b775dbb
AG
1387 if( title )
1388 {
1389 g_string_append( cmd, title );
1390 }
6cc5e1a6
DB
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':
6b775dbb
AG
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 }
6cc5e1a6 1406 break;
6cc5e1a6
DB
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*/
6b775dbb 1426/* for compatibility with old plugins */
6cc5e1a6
DB
1427GtkWidget* recreate_box( GtkBox* oldbox, GtkOrientation orientation )
1428{
6b775dbb
AG
1429 gtk_orientable_set_orientation(GTK_ORIENTABLE(oldbox), orientation);
1430 return GTK_WIDGET(oldbox);
6cc5e1a6
DB
1431}
1432
6b775dbb 1433/* for compatibility with old plugins */
6cc5e1a6
DB
1434void show_error( GtkWindow* parent_win, const char* msg )
1435{
6b775dbb 1436 fm_show_error(parent_win, NULL, msg);
7486d297
DB
1437}
1438
6b775dbb 1439/* old plugins compatibility mode, use fm_pixbuf_from_icon_with_fallback() instead */
2ba86315 1440GdkPixbuf * lxpanel_load_icon(const char * name, int width, int height, gboolean use_fallback)
7486d297 1441{
6b775dbb 1442 FmIcon * fm_icon;
2ba86315
DB
1443 GdkPixbuf * icon = NULL;
1444
6b775dbb
AG
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);
7486d297
DB
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 */
1461static 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 );
aaccad27
AL
1484 if (l->next)
1485 g_string_append_c( cmd, ' ' );
7486d297
DB
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 );
aaccad27
AL
1509 if (l->next)
1510 g_string_append_c( cmd, ' ' );
7486d297
DB
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 );
aaccad27
AL
1533 if (l->next)
1534 g_string_append_c( cmd, ' ' );
7486d297
DB
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 {
7486d297
DB
1587 for( l = file_list; l; l = l->next )
1588 {
aaccad27 1589 g_string_append_c( cmd, ' ' );
7486d297
DB
1590 file = (char*)l->data;
1591 tmp = g_shell_quote( file );
1592 g_string_append( cmd, tmp );
7486d297
DB
1593 g_free( tmp );
1594 }
1595 }
1596
1597 return g_string_free( cmd, FALSE );
1598}
1599
6b775dbb
AG
1600gboolean 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() */
1625gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal, char const* in_workdir)
7486d297
DB
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 {
4652f59b 1634 char * escaped_cmd = g_shell_quote(cmd);
7486d297 1635 char* term_cmd;
6b775dbb 1636 const char* term = fm_config->terminal ? fm_config->terminal : "lxterminal";
7486d297 1637 if( strstr(term, "%s") )
4652f59b 1638 term_cmd = g_strdup_printf(term, escaped_cmd);
7486d297 1639 else
4652f59b
DB
1640 term_cmd = g_strconcat( term, " -e ", escaped_cmd, NULL );
1641 g_free(escaped_cmd);
7486d297
DB
1642 if( cmd != exec )
1643 g_free(cmd);
1644 cmd = term_cmd;
1645 }
6b775dbb
AG
1646
1647 spawn_command_async(NULL, in_workdir, cmd);
1648
7486d297
DB
1649 g_free(cmd);
1650
1651 return (error == NULL);
1652}
6b775dbb
AG
1653
1654/* vim: set sw=4 et sts=4 : */