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