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