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