Support desktop entry in launchbar.
[lxde/lxpanel.git] / src / plugins / launchbar.c
1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include <signal.h>
9 #include <errno.h>
10
11 #include <gdk-pixbuf/gdk-pixbuf.h>
12 #include <glib/gi18n.h>
13
14 #include "panel.h"
15 #include "misc.h"
16 #include "plugin.h"
17 #include "gtkbgbox.h"
18
19 //#define DEBUG
20 #include "dbg.h"
21
22
23 typedef enum {
24 CURSOR_STANDARD,
25 CURSOR_DND
26 } CursorType;
27
28 enum {
29 TARGET_URILIST,
30 TARGET_UTF8_STRING,
31 TARGET_STRING,
32 TARGET_TEXT,
33 TARGET_COMPOUND_TEXT
34 };
35
36 static const GtkTargetEntry target_table[] = {
37 { "text/uri-list", 0, TARGET_URILIST},
38 { "UTF8_STRING", 0, TARGET_UTF8_STRING },
39 { "COMPOUND_TEXT", 0, 0 },
40 { "TEXT", 0, 0 },
41 { "STRING", 0, 0 }
42 };
43
44 static const char desktop_ent[] = "Desktop Entry";
45
46 typedef struct btn {
47 //GtkWidget *button, *pixmap;
48 gchar *action;
49 } btn;
50
51 #define MAXBUTTONS 20
52 typedef struct launchbar {
53 GtkWidget *box;
54 GtkTooltips *tips;
55 btn btns[MAXBUTTONS];
56 int btn_num;
57 int iconsize;
58 } launchbar;
59
60
61
62 static gboolean
63 my_button_pressed(GtkWidget *widget, GdkEventButton *event, btn *b )
64 {
65 GtkWidget *image;
66
67 ENTER;
68 image = gtk_bin_get_child(GTK_BIN(widget));
69 g_assert(b != NULL);
70 if (event->type == GDK_BUTTON_RELEASE) {
71 if ((event->x >=0 && event->x < widget->allocation.width)
72 && (event->y >=0 && event->y < widget->allocation.height)) {
73
74 g_spawn_command_line_async(b->action, NULL);
75 }
76 gtk_misc_set_padding (GTK_MISC(image), 0, 0);
77
78 //system(b->action);
79 } else if (event->type == GDK_BUTTON_PRESS) {
80
81 gtk_misc_set_padding (GTK_MISC(image), 0, 3);
82 //ERR("here\n");
83 }
84 RET(TRUE);
85 }
86
87 static void
88 launchbar_destructor(plugin *p)
89 {
90 launchbar *lb = (launchbar *)p->priv;
91 int i;
92
93 ENTER;
94 gtk_widget_destroy(lb->box);
95 for (i = 0; i < lb->btn_num; i++) {
96 g_free(lb->btns[i].action);
97 }
98 g_free(lb);
99 RET();
100 }
101
102
103 static void
104 drag_data_received_cb (GtkWidget *widget,
105 GdkDragContext *context,
106 gint x,
107 gint y,
108 GtkSelectionData *sd,
109 guint info,
110 guint time,
111 btn *b)
112 {
113 gchar *s, *e, *end, *str, *tmp;
114
115 ENTER;
116 if (sd->length <= 0)
117 RET();
118 if (info == TARGET_URILIST) {
119 DBG("uri drag received: info=%d len=%d data=%s\n", info, sd->length, sd->data);
120 s = (gchar *)sd->data;
121 end = s + sd->length;
122 str = g_strdup(b->action);
123 while (s < end) {
124 while (s < end && g_ascii_isspace(*s))
125 s++;
126 e = s;
127 while (e < end && !g_ascii_isspace(*e))
128 e++;
129 if (s != e) {
130 *e = 0;
131 s = g_filename_from_uri(s, NULL, NULL);
132 if (s) {
133 //strlen(s);
134 //strlen(str);
135 tmp = g_strconcat(str, " '", s, "'", NULL);
136 g_free(str);
137 g_free(s);
138 str = tmp;
139 }
140 }
141 s = e+1;
142 }
143 DBG("cmd=<%s>\n", str);
144 g_spawn_command_line_async(str, NULL);
145 g_free(str);
146
147 //gtk_drag_finish (context, TRUE, FALSE, time);
148 }
149 RET();
150 }
151
152 static int
153 read_button(plugin *p)
154 {
155 launchbar *lb = (launchbar *)p->priv;
156 gchar *fname, *tooltip, *action, *desktop_id;
157 //GdkPixbuf *gp, *gps;
158 GtkWidget *button;
159 line s;
160 //GError *err = NULL;
161 int w, h;
162
163 ENTER;
164 s.len = 256;
165 if (lb->btn_num >= MAXBUTTONS) {
166 ERR("launchbar: max number of buttons (%d) was reached. skipping the rest\n",
167 lb->btn_num );
168 RET(0);
169 }
170
171 tooltip = fname = action = desktop_id = NULL;
172 while (get_line(p->fp, &s) != LINE_BLOCK_END) {
173 if (s.type == LINE_NONE) {
174 ERR( "launchbar: illegal token %s\n", s.str);
175 RET(0);
176 }
177 if (s.type == LINE_VAR) {
178 if( !g_ascii_strcasecmp(s.t[0], "id") )
179 desktop_id = g_strdup(s.t[1]);
180 else if (!g_ascii_strcasecmp(s.t[0], "image"))
181 fname = expand_tilda(s.t[1]);
182 else if (!g_ascii_strcasecmp(s.t[0], "tooltip"))
183 tooltip = g_strdup(s.t[1]);
184 else if (!g_ascii_strcasecmp(s.t[0], "action"))
185 action = g_strdup(s.t[1]);
186 else {
187 ERR( "launchbar: unknown var %s\n", s.t[0]);
188 goto error;
189 }
190 } else {
191 ERR( "launchbar: illegal in this context %s\n", s.str);
192 goto error;
193 }
194 }
195 DBG("action=%s\n", action);
196
197 if( desktop_id ) {
198 gchar *desktop_file = NULL;
199 gchar *full_id = NULL;
200 GKeyFile* desktop = g_key_file_new();
201 full_id = g_strconcat( "applications/", desktop_id, NULL );
202 if( g_key_file_load_from_data_dirs( desktop, full_id, &desktop_file,
203 G_KEY_FILE_NONE, NULL ) )
204 {
205 gchar *icon = NULL, *title = NULL;
206 icon = g_key_file_get_string( desktop, desktop_ent, "Icon", NULL);
207 title = g_key_file_get_locale_string( desktop, desktop_ent,
208 "Name", NULL, NULL);
209 if( !fname && icon ){
210 gchar* sep = strchr( icon, '.' );
211 if( sep )
212 fname = g_strndup( icon, (sep - icon) );
213 else
214 fname = icon;
215 }
216 if( ! action ) {
217 gchar* exec;
218 exec = g_key_file_get_string( desktop, desktop_ent, "Exec", NULL);
219 action = translate_exec_to_cmd( exec, icon, title, desktop_file );
220 g_free( exec );
221 }
222 if( ! tooltip )
223 tooltip = title;
224 if( fname != icon )
225 g_free( icon );
226 if( tooltip != title )
227 g_free( title );
228 }
229 g_free( full_id );
230 g_free( desktop_file );
231 g_key_file_free( desktop );
232 }
233
234 // button
235 if (p->panel->orientation == ORIENT_HORIZ) {
236 w = 10000;
237 //h = GTK_WIDGET(p->panel->box)->allocation.height;
238 h = p->panel->ah;
239 } else {
240 //w = GTK_WIDGET(p->panel->box)->allocation.width;
241 w = p->panel->aw;
242 h = 10000;
243 }
244 button = fb_button_new_from_file(fname, w, h, 0x202020, TRUE);
245 //gtk_container_set_border_width(GTK_CONTAINER(button), 0);
246 g_signal_connect (G_OBJECT (button), "button-release-event",
247 G_CALLBACK (my_button_pressed), (gpointer) &lb->btns[lb->btn_num]);
248 g_signal_connect (G_OBJECT (button), "button-press-event",
249 G_CALLBACK (my_button_pressed), (gpointer) &lb->btns[lb->btn_num]);
250
251
252 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
253 // DnD support
254 gtk_drag_dest_set (GTK_WIDGET(button),
255 GTK_DEST_DEFAULT_ALL, //GTK_DEST_DEFAULT_HIGHLIGHT,
256 target_table, G_N_ELEMENTS (target_table),
257 GDK_ACTION_COPY);
258 g_signal_connect (G_OBJECT(button), "drag_data_received",
259 G_CALLBACK (drag_data_received_cb), (gpointer) &lb->btns[lb->btn_num]);
260
261 gtk_box_pack_start(GTK_BOX(lb->box), button, FALSE, FALSE, 0);
262 gtk_widget_show(button);
263 //gtk_bgbox_set_background(button, BG_ROOT, 0xFFFFFF, 20);
264
265 if (p->panel->transparent)
266 gtk_bgbox_set_background(button, BG_ROOT, p->panel->tintcolor, p->panel->alpha);
267
268 g_free(fname);
269 // tooltip
270 if (tooltip) {
271 gtk_tooltips_set_tip(GTK_TOOLTIPS (lb->tips), button, tooltip, NULL);
272 g_free(tooltip);
273 }
274
275 //gtk_container_add(GTK_CONTAINER(eb), button);
276 lb->btns[lb->btn_num].action = action;
277 lb->btn_num++;
278
279 RET(1);
280
281 error:
282 g_free(fname);
283 g_free(tooltip);
284 g_free(action);
285 RET(0);
286 }
287
288 static int
289 launchbar_constructor(plugin *p)
290 {
291 launchbar *lb;
292 line s;
293 GtkRequisition req;
294 static gchar *launchbar_rc = "style 'launchbar-style'\n"
295 "{\n"
296 "GtkWidget::focus-line-width = 0\n"
297 "GtkWidget::focus-padding = 0\n"
298 "GtkButton::default-border = { 0, 0, 0, 0 }\n"
299 "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
300 "}\n"
301 "widget '*' style 'launchbar-style'";
302
303 ENTER;
304 gtk_widget_set_name(p->pwid, "launchbar");
305 gtk_rc_parse_string(launchbar_rc);
306 get_button_spacing(&req, GTK_CONTAINER(p->pwid), "");
307
308 lb = g_new0(launchbar, 1);
309 g_return_val_if_fail(lb != NULL, 0);
310 p->priv = lb;
311 lb->box = p->panel->my_box_new(FALSE, 0);
312 gtk_container_add(GTK_CONTAINER(p->pwid), lb->box);
313 gtk_container_set_border_width (GTK_CONTAINER (lb->box), 0);
314 gtk_widget_show(lb->box);
315 lb->tips = gtk_tooltips_new();
316
317 if (p->panel->orientation == ORIENT_HORIZ)
318 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.height;
319 else
320 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.width;
321 DBG("button: req width=%d height=%d\n", req.width, req.height);
322 DBG("iconsize=%d\n", lb->iconsize);
323
324 s.len = 256;
325 while (get_line(p->fp, &s) != LINE_BLOCK_END) {
326 if (s.type == LINE_NONE) {
327 ERR( "launchbar: illegal token %s\n", s.str);
328 goto error;
329 }
330 if (s.type == LINE_BLOCK_START) {
331 if (!g_ascii_strcasecmp(s.t[0], "button")) {
332 if (!read_button(p)) {
333 ERR( "launchbar: can't init button\n");
334 goto error;
335 }
336 } else {
337 ERR( "launchbar: unknown var %s\n", s.t[0]);
338 goto error;
339 }
340 } else {
341 ERR( "launchbar: illegal in this context %s\n", s.str);
342 goto error;
343 }
344 }
345
346
347 RET(1);
348
349 error:
350 launchbar_destructor(p);
351 RET(0);
352
353 }
354
355
356
357 plugin_class launchbar_plugin_class = {
358 fname: NULL,
359 count: 0,
360
361 type : "launchbar",
362 name : "launchbar",
363 version: "1.0",
364 description : N_("Bar with buttons to launch application"),
365
366 constructor : launchbar_constructor,
367 destructor : launchbar_destructor,
368 };