Rename panel, plugin, and plugin_class to Panel, Plugin, and
[lxde/lxpanel.git] / src / plugins / launchbar.c
CommitLineData
239cb032 1/**
a99ee9e1
JH
2 * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
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 */
a52c2257
HJYP
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <sys/types.h>
24#include <sys/wait.h>
25#include <signal.h>
26#include <errno.h>
27
a52c2257 28#include <gdk-pixbuf/gdk-pixbuf.h>
e7cb732b 29#include <glib/gi18n.h>
a52c2257
HJYP
30
31#include "panel.h"
32#include "misc.h"
33#include "plugin.h"
a52c2257
HJYP
34
35//#define DEBUG
36#include "dbg.h"
37
1dac04fb 38#include "glib-mem.h"
a52c2257
HJYP
39
40typedef enum {
41 CURSOR_STANDARD,
42 CURSOR_DND
43} CursorType;
44
45enum {
46 TARGET_URILIST,
47 TARGET_UTF8_STRING,
48 TARGET_STRING,
49 TARGET_TEXT,
50 TARGET_COMPOUND_TEXT
51};
52
1dac04fb
HJYP
53enum {
54 COL_ICON = 0,
55 COL_TITLE,
56 COL_BTN,
57 N_COLS
58};
59
a52c2257
HJYP
60static const GtkTargetEntry target_table[] = {
61 { "text/uri-list", 0, TARGET_URILIST},
62 { "UTF8_STRING", 0, TARGET_UTF8_STRING },
63 { "COMPOUND_TEXT", 0, 0 },
64 { "TEXT", 0, 0 },
65 { "STRING", 0, 0 }
66};
67
08ea5341
HJYP
68static const char desktop_ent[] = "Desktop Entry";
69
1dac04fb
HJYP
70typedef struct btn_t {
71 GtkWidget* widget;
72 gchar *desktop_id;
73 gchar *image;
a52c2257 74 gchar *action;
1dac04fb
HJYP
75 gchar *tooltip;
76/* NOTE: Users can override the values specified in desktop file,
77 and we should process these special cease. */
78 guchar customize_image : 1;
79 guchar customize_action : 1;
80 guchar customize_tooltip : 1;
81} btn_t;
a52c2257 82
a52c2257
HJYP
83typedef struct launchbar {
84 GtkWidget *box;
85 GtkTooltips *tips;
1dac04fb 86 GSList* btns;
a52c2257 87 int iconsize;
1dac04fb 88 GtkWidget* config_dlg;
a52c2257
HJYP
89} launchbar;
90
1dac04fb
HJYP
91void btn_free( btn_t* btn )
92{
93 g_free( btn->desktop_id );
94 g_free( btn->image );
95 g_free( btn->action );
96 g_free( btn->tooltip );
97 g_slice_free( btn_t, btn );
98}
99
a52c2257 100static gboolean
1dac04fb 101on_button_pressed(GtkWidget *widget, GdkEventButton *event, btn_t *b )
a52c2257
HJYP
102{
103 GtkWidget *image;
104
105 ENTER;
106 image = gtk_bin_get_child(GTK_BIN(widget));
107 g_assert(b != NULL);
108 if (event->type == GDK_BUTTON_RELEASE) {
109 if ((event->x >=0 && event->x < widget->allocation.width)
110 && (event->y >=0 && event->y < widget->allocation.height)) {
da76d5cf 111
a52c2257
HJYP
112 g_spawn_command_line_async(b->action, NULL);
113 }
114 gtk_misc_set_padding (GTK_MISC(image), 0, 0);
da76d5cf 115
a52c2257
HJYP
116 //system(b->action);
117 } else if (event->type == GDK_BUTTON_PRESS) {
da76d5cf 118
a52c2257
HJYP
119 gtk_misc_set_padding (GTK_MISC(image), 0, 3);
120 //ERR("here\n");
121 }
122 RET(TRUE);
123}
124
125static void
22242ed4 126launchbar_destructor(Plugin *p)
a52c2257
HJYP
127{
128 launchbar *lb = (launchbar *)p->priv;
a52c2257
HJYP
129
130 ENTER;
239cb032 131 /* g_object_unref( lb->tips ); */
3806c5aa 132
a52c2257 133 gtk_widget_destroy(lb->box);
1dac04fb
HJYP
134 g_slist_foreach( lb->btns, (GFunc)btn_free, NULL );
135 g_slice_free(launchbar, lb);
a52c2257
HJYP
136 RET();
137}
138
139
140static void
141drag_data_received_cb (GtkWidget *widget,
142 GdkDragContext *context,
143 gint x,
144 gint y,
145 GtkSelectionData *sd,
146 guint info,
147 guint time,
1dac04fb 148 btn_t *b)
a52c2257
HJYP
149{
150 gchar *s, *e, *end, *str, *tmp;
151
152 ENTER;
153 if (sd->length <= 0)
154 RET();
155 if (info == TARGET_URILIST) {
156 DBG("uri drag received: info=%d len=%d data=%s\n", info, sd->length, sd->data);
157 s = (gchar *)sd->data;
158 end = s + sd->length;
159 str = g_strdup(b->action);
160 while (s < end) {
161 while (s < end && g_ascii_isspace(*s))
162 s++;
163 e = s;
164 while (e < end && !g_ascii_isspace(*e))
165 e++;
166 if (s != e) {
167 *e = 0;
168 s = g_filename_from_uri(s, NULL, NULL);
169 if (s) {
170 //strlen(s);
171 //strlen(str);
172 tmp = g_strconcat(str, " '", s, "'", NULL);
173 g_free(str);
174 g_free(s);
175 str = tmp;
176 }
177 }
178 s = e+1;
179 }
180 DBG("cmd=<%s>\n", str);
181 g_spawn_command_line_async(str, NULL);
182 g_free(str);
da76d5cf 183
a52c2257
HJYP
184 //gtk_drag_finish (context, TRUE, FALSE, time);
185 }
186 RET();
187}
188
189static int
22242ed4 190read_button(Plugin *p, char** fp)
a52c2257
HJYP
191{
192 launchbar *lb = (launchbar *)p->priv;
1dac04fb 193 gchar *fname;
a52c2257
HJYP
194 GtkWidget *button;
195 line s;
a52c2257 196 int w, h;
1dac04fb 197 btn_t* btn;
9c97f69e 198
a52c2257 199 ENTER;
1dac04fb
HJYP
200
201 btn = g_slice_new0( btn_t );
202
a52c2257 203 s.len = 256;
1dac04fb 204 fname= NULL;
a52c2257 205
db449f6e
HJYP
206 if( fp )
207 {
208 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
209 if (s.type == LINE_NONE) {
210 ERR( "launchbar: illegal token %s\n", s.str);
211 RET(0);
212 }
213 if (s.type == LINE_VAR) {
214 if( !g_ascii_strcasecmp(s.t[0], "id") )
1dac04fb
HJYP
215 btn->desktop_id = g_strdup(s.t[1]);
216 else if (!g_ascii_strcasecmp(s.t[0], "image")) {
217 btn->customize_image = 1;
218 btn->image = g_strdup(s.t[1]);
db449f6e 219 fname = expand_tilda(s.t[1]);
1dac04fb
HJYP
220 }
221 else if (!g_ascii_strcasecmp(s.t[0], "tooltip")) {
222 btn->customize_tooltip = 1;
223 btn->tooltip = g_strdup(s.t[1]);
224 }
225 else if (!g_ascii_strcasecmp(s.t[0], "action")) {
226 btn->customize_action = 1;
227 btn->action = g_strdup(s.t[1]);
228 }
db449f6e
HJYP
229 else {
230 ERR( "launchbar: unknown var %s\n", s.t[0]);
231 goto error;
232 }
233 } else {
234 ERR( "launchbar: illegal in this context %s\n", s.str);
a52c2257
HJYP
235 goto error;
236 }
a52c2257 237 }
db449f6e 238 DBG("action=%s\n", action);
a52c2257 239 }
a52c2257 240
1dac04fb 241 if( btn->desktop_id ) {
08ea5341
HJYP
242 gchar *desktop_file = NULL;
243 gchar *full_id = NULL;
244 GKeyFile* desktop = g_key_file_new();
1dac04fb 245 full_id = g_strconcat( "applications/", btn->desktop_id, NULL );
08ea5341
HJYP
246 if( g_key_file_load_from_data_dirs( desktop, full_id, &desktop_file,
247 G_KEY_FILE_NONE, NULL ) )
248 {
249 gchar *icon = NULL, *title = NULL;
250 icon = g_key_file_get_string( desktop, desktop_ent, "Icon", NULL);
251 title = g_key_file_get_locale_string( desktop, desktop_ent,
252 "Name", NULL, NULL);
253 if( !fname && icon ){
389975e0
HJYP
254 gchar* sep;
255 /* not a full path, remove the extension */
256 if( icon[0] != '/' && (sep = strchr( icon, '.' )) )
08ea5341
HJYP
257 fname = g_strndup( icon, (sep - icon) );
258 else
259 fname = icon;
260 }
1dac04fb 261 if( ! btn->customize_action ) {
08ea5341
HJYP
262 gchar* exec;
263 exec = g_key_file_get_string( desktop, desktop_ent, "Exec", NULL);
1dac04fb 264 btn->action = translate_exec_to_cmd( exec, icon, title, desktop_file );
08ea5341
HJYP
265 g_free( exec );
266 }
1dac04fb
HJYP
267 if( ! btn->customize_tooltip )
268 btn->tooltip = title;
08ea5341
HJYP
269 if( fname != icon )
270 g_free( icon );
1dac04fb 271 if( btn->tooltip != title )
08ea5341
HJYP
272 g_free( title );
273 }
274 g_free( full_id );
275 g_free( desktop_file );
276 g_key_file_free( desktop );
277 }
278
a52c2257
HJYP
279 // button
280 if (p->panel->orientation == ORIENT_HORIZ) {
281 w = 10000;
282 //h = GTK_WIDGET(p->panel->box)->allocation.height;
283 h = p->panel->ah;
284 } else {
285 //w = GTK_WIDGET(p->panel->box)->allocation.width;
286 w = p->panel->aw;
287 h = 10000;
288 }
a52c2257 289
1dac04fb
HJYP
290 button = fb_button_new_from_file( fname, w, h, 0x202020, TRUE );
291 btn->widget = button;
292
293 //gtk_container_set_border_width(GTK_CONTAINER(button), 0);
294 g_signal_connect ( button, "button-release-event",
295 G_CALLBACK (on_button_pressed), (gpointer) btn );
296 g_signal_connect ( button, "button-press-event",
297 G_CALLBACK (on_button_pressed), (gpointer) btn );
a52c2257 298
a52c2257 299 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
1dac04fb 300
a52c2257
HJYP
301 // DnD support
302 gtk_drag_dest_set (GTK_WIDGET(button),
303 GTK_DEST_DEFAULT_ALL, //GTK_DEST_DEFAULT_HIGHLIGHT,
304 target_table, G_N_ELEMENTS (target_table),
08ea5341 305 GDK_ACTION_COPY);
1dac04fb
HJYP
306 g_signal_connect ( button, "drag_data_received",
307 G_CALLBACK (drag_data_received_cb), (gpointer) btn );
a52c2257 308
a52c2257 309 gtk_box_pack_start(GTK_BOX(lb->box), button, FALSE, FALSE, 0);
1dac04fb
HJYP
310
311 /* append is more time-consuming, but we really care about the order. */
312 lb->btns = g_slist_append( lb->btns, btn );
313
a52c2257 314 gtk_widget_show(button);
08ea5341 315
a52c2257 316 g_free(fname);
1dac04fb 317
a52c2257 318 // tooltip
1dac04fb
HJYP
319 if ( btn->tooltip ) {
320 gtk_tooltips_set_tip(GTK_TOOLTIPS (lb->tips), button, btn->tooltip, NULL);
a52c2257 321 }
a52c2257
HJYP
322 RET(1);
323
324 error:
325 g_free(fname);
1dac04fb 326 btn_free( btn );
a52c2257
HJYP
327 RET(0);
328}
329
330static int
22242ed4 331launchbar_constructor(Plugin *p, char **fp)
a52c2257 332{
08ea5341 333 launchbar *lb;
a52c2257
HJYP
334 line s;
335 GtkRequisition req;
5297da29
HJYP
336 static char default_config[] =
337 "button {\n"
338 "id=pcmanfm.desktop\n"
339 "}\n"
340 "button {\n"
341 "id=gnome-terminal.desktop\n"
342 "}\n"
343 "button {\n"
344 "id=firefox.desktop\n"
345 "}\n"
346 "}\n";
1dac04fb 347 char *config_default = default_config;
a52c2257
HJYP
348 static gchar *launchbar_rc = "style 'launchbar-style'\n"
349 "{\n"
350 "GtkWidget::focus-line-width = 0\n"
351 "GtkWidget::focus-padding = 0\n"
352 "GtkButton::default-border = { 0, 0, 0, 0 }\n"
353 "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
354 "}\n"
355 "widget '*' style 'launchbar-style'";
da76d5cf 356
a52c2257 357 ENTER;
a52c2257 358 gtk_rc_parse_string(launchbar_rc);
4542c20d
HJYP
359
360 p->pwid = gtk_event_box_new();
361 GTK_WIDGET_SET_FLAGS( p->pwid, GTK_NO_WINDOW );
362
363 gtk_widget_set_name(p->pwid, "launchbar");
a52c2257 364 get_button_spacing(&req, GTK_CONTAINER(p->pwid), "");
da76d5cf 365
1dac04fb 366 lb = g_slice_new0(launchbar);
a52c2257
HJYP
367 g_return_val_if_fail(lb != NULL, 0);
368 p->priv = lb;
369 lb->box = p->panel->my_box_new(FALSE, 0);
4542c20d
HJYP
370
371 gtk_container_add( (GtkContainer*)p->pwid, lb->box );
372
a52c2257
HJYP
373 gtk_container_set_border_width (GTK_CONTAINER (lb->box), 0);
374 gtk_widget_show(lb->box);
239cb032
HJYP
375
376 /* Use the shared tooltip object provided by the panel, and
377 we don't need to create a new one. */
378 lb->tips = p->panel->tooltips;
4542c20d 379
08ea5341 380 if (p->panel->orientation == ORIENT_HORIZ)
a52c2257
HJYP
381 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.height;
382 else
383 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.width;
da76d5cf 384
5297da29
HJYP
385 if( ! fp )
386 fp = &config_default;
387
5297da29
HJYP
388 s.len = 256;
389 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
390 if (s.type == LINE_NONE) {
391 ERR( "launchbar: illegal token %s\n", s.str);
392 goto error;
393 }
394 if (s.type == LINE_BLOCK_START) {
395 if (!g_ascii_strcasecmp(s.t[0], "button")) {
396 if (!read_button(p, fp)) {
397 ERR( "launchbar: can't init button\n");
a52c2257
HJYP
398 goto error;
399 }
400 } else {
5297da29 401 ERR( "launchbar: unknown var %s\n", s.t[0]);
a52c2257
HJYP
402 goto error;
403 }
5297da29
HJYP
404 } else {
405 ERR( "launchbar: illegal in this context %s\n", s.str);
406 goto error;
a52c2257 407 }
da76d5cf 408 }
a52c2257
HJYP
409
410 RET(1);
411
412 error:
413 launchbar_destructor(p);
414 RET(0);
da76d5cf 415
a52c2257
HJYP
416}
417
22242ed4 418static void save_config( Plugin* p, FILE* fp )
9c97f69e 419{
da76d5cf 420 launchbar *lb = (launchbar *)p->priv;
1dac04fb
HJYP
421 GSList* l;
422 for( l = lb->btns; l; l = l->next ) {
423 btn_t* btn = (btn_t*)l->data;
424 lxpanel_put_line( fp, "Button {" );
425 if( btn->desktop_id )
426 lxpanel_put_str( fp, "id", btn->desktop_id );
427 if( btn->customize_image )
428 lxpanel_put_str( fp, "image", btn->image );
429 if( btn->customize_tooltip )
430 lxpanel_put_str( fp, "tooltip", btn->tooltip );
431 if( btn->customize_action )
432 lxpanel_put_str( fp, "action", btn->action );
433 lxpanel_put_line( fp, "}" );
da76d5cf 434 }
9c97f69e 435}
a52c2257 436
22242ed4 437static void orientation_changed( Plugin* p )
a97d06a6
HJYP
438{
439 launchbar *lb = (launchbar *)p->priv;
440 GtkBox* newbox;
5a343ad5
JH
441 newbox = GTK_BOX(recreate_box( GTK_BOX(lb->box), p->panel->orientation ));
442 if( GTK_WIDGET(newbox) != lb->box ) {
a97d06a6
HJYP
443 /* Since the old box has been destroyed,
444 we need to re-add the new box to the container */
5a343ad5 445 lb->box = GTK_WIDGET(newbox);
a97d06a6
HJYP
446 gtk_container_add(GTK_CONTAINER(p->pwid), lb->box);
447 }
448}
449
1dac04fb 450static void
22242ed4 451on_response( GtkDialog* dlg, int response, Plugin* p )
1dac04fb
HJYP
452{
453 launchbar *lb = (launchbar *)p->priv;
460e2e78 454 gtk_widget_destroy( GTK_WIDGET(dlg) );
1dac04fb
HJYP
455 lb->config_dlg = NULL;
456}
457
458static void on_add_btn_response( GtkDialog* dlg, int response, int* ret )
459{
460 *ret = response;
461 gtk_main_quit();
462}
463
22242ed4 464static void on_add_btn( GtkButton* widget, Plugin* p )
1dac04fb
HJYP
465{
466 launchbar *lb = (launchbar *)p->priv;
460e2e78 467 GtkTreeView* view = (GtkTreeView*)g_object_get_data( (GObject *) lb->config_dlg, "view" );
1dac04fb
HJYP
468 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
469 GtkTreeIter it;
470 GtkListStore* list;
471 GtkFileChooserDialog* dlg;
472 GtkFileFilter* filter;
473 int response;
474
475 /*
476 if( !gtk_tree_selection_get_selected( tree_sel, &list, &it ) )
477 return;
478 */
479 list = (GtkListStore*)gtk_tree_view_get_model( view );
480
481 /* FIXME: We should have a better interface for this in the fututure.
460e2e78 482 1. We can borrow the menu from menu plugin (PtkAppMenu).
1dac04fb
HJYP
483 2. We can borrow the app chooser from PCManFM.
484 */
485 dlg = gtk_file_chooser_dialog_new(_("Select Application"),
486 lb->config_dlg,
487 GTK_FILE_CHOOSER_ACTION_OPEN,
488 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
489 GTK_STOCK_ADD, GTK_RESPONSE_OK, NULL );
490 filter = gtk_file_filter_new();
491 gtk_file_filter_set_name( filter, "*.desktop" );
492 gtk_file_filter_add_pattern( filter, "*.desktop" );
493 gtk_file_chooser_add_filter( dlg, filter );
494 g_object_unref( filter );
495 gtk_file_chooser_set_local_only( dlg, TRUE );
496 gtk_file_chooser_set_current_folder( dlg, "/usr/share/applications" );
497
498 gtk_widget_set_sensitive( lb->config_dlg, FALSE );
499 g_signal_connect( dlg, "response", on_add_btn_response, &response );
500 gtk_window_present( dlg );
501 gtk_main();
502 gtk_widget_set_sensitive( lb->config_dlg, TRUE );
503
504 if( response == GTK_RESPONSE_OK ) {
505 char* filename = gtk_file_chooser_get_filename( dlg );
506 if( filename ) {
507 if( g_str_has_suffix( filename, ".desktop" ) ) {
508 char* desktop_id = g_path_get_basename( filename );
509 char *config, *pconfig;
510 config = pconfig = g_strdup_printf( "id=%s\n}\n", desktop_id );
511 g_free( desktop_id );
512 /* Make a fake config entry, and let read_button() parst it. */
513 /* FIXME: This is a quick hack, which is dirty but easy and useful.
514 Need to be re-written in the future.
515 */
516 if( read_button( p, &pconfig ) ) {
517 GSList* l;
518 btn_t* btn;
519 l = g_slist_last( lb->btns );
520 btn = (btn_t*)l->data;
521 gtk_list_store_append( list, &it );
522 gtk_list_store_set( list, &it,
523 COL_ICON, NULL, /* FIXME: need to be implemented */
524 COL_TITLE, (btn->tooltip ? btn->tooltip : btn->action),
525 COL_BTN, btn,
526 -1 );
527 }
528 g_free( config );
529 }
530 g_free( filename );
531 }
532 }
533
534 gtk_widget_destroy( dlg );
535}
536
22242ed4 537static void on_remove_btn( GtkButton* widget, Plugin* p )
1dac04fb
HJYP
538{
539 launchbar *lb = (launchbar *)p->priv;
540 GtkTreeView* view = (GtkTreeView*)g_object_get_data( lb->config_dlg, "view" );
541 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
542 GtkTreeIter it;
543 GtkListStore* list;
544 btn_t* btn;
545
546 if( !gtk_tree_selection_get_selected( tree_sel, &list, &it ) )
547 return;
548 gtk_tree_model_get( (GtkTreeModel*)list, &it,
549 COL_BTN, &btn, -1 );
550 gtk_list_store_remove( list, &it );
551 if( btn ) {
552 lb->btns = g_slist_remove( lb->btns, btn );
553 gtk_widget_destroy( btn->widget );
554 btn_free( btn );
555 }
556}
557
22242ed4 558static void on_up_btn( GtkButton* widget, Plugin* p )
1dac04fb
HJYP
559{
560 launchbar *lb = (launchbar *)p->priv;
561 btn_t *btn;
562 GtkTreeView* view = (GtkTreeView*)g_object_get_data( lb->config_dlg, "view" );
563 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
564 GtkTreeIter it;
565 GtkTreePath* path;
566 GtkListStore* list;
567
568 if( !gtk_tree_selection_get_selected( tree_sel, &list, &it ) )
569 return;
570 gtk_tree_model_get( (GtkTreeModel*)list, &it, COL_BTN, &btn, -1 );
571 path = gtk_tree_model_get_path( (GtkTreeModel*)list, &it );
572 if( gtk_tree_path_get_indices(path)[0] > 0 ) {
573 if( gtk_tree_path_prev(path) ) {
574 GtkTreeIter it2;
575 if( gtk_tree_model_get_iter( (GtkTreeModel*)list, &it2, path ) ) {
576 int i = gtk_tree_path_get_indices(path)[0];
577 lb->btns = g_slist_remove( lb->btns, btn );
578 lb->btns = g_slist_insert( lb->btns, btn, i );
579 gtk_list_store_move_before( (GtkTreeModel*)list, &it, &it2 );
580 gtk_box_reorder_child( lb->box, btn->widget, i );
581 }
582 }
583 }
584 gtk_tree_path_free( path );
585}
586
22242ed4 587static void on_down_btn( GtkButton* widget, Plugin* p )
1dac04fb
HJYP
588{
589 launchbar *lb = (launchbar *)p->priv;
590 btn_t *btn;
591 GtkTreeView* view = (GtkTreeView*)g_object_get_data( lb->config_dlg, "view" );
592 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
593 GtkTreeIter it;
594 GtkTreePath* path;
595 GtkListStore* list;
596 int n;
597
598 if( !gtk_tree_selection_get_selected( tree_sel, &list, &it ) )
599 return;
600 gtk_tree_model_get( (GtkTreeModel*)list, &it, COL_BTN, &btn, -1 );
601 path = gtk_tree_model_get_path( (GtkTreeModel*)list, &it );
602 n = gtk_tree_model_iter_n_children( (GtkTreeModel*)list, NULL );
603 if( gtk_tree_path_get_indices(path)[0] < n - 1 ) {
604 GtkTreeIter it2;
605 gtk_tree_path_next(path);
606 if( gtk_tree_model_get_iter( (GtkTreeModel*)list, &it2, path ) ) {
607 int i = gtk_tree_path_get_indices(path)[0];
608 lb->btns = g_slist_insert( lb->btns, btn, i + 1 );
609 lb->btns = g_slist_remove( lb->btns, btn );
610 gtk_list_store_move_after( (GtkTreeModel*)list, &it, &it2 );
611 gtk_box_reorder_child( lb->box, btn->widget, i );
612 }
613 }
614 gtk_tree_path_free( path );
615}
616
22242ed4 617static void init_btn_list( Plugin* p, GtkTreeView* view )
1dac04fb
HJYP
618{
619 launchbar *lb = (launchbar *)p->priv;
620 GtkListStore *list;
621 GSList* l;
622 GtkTreeViewColumn* col;
623 GtkCellRenderer* render;
624 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
625
626 gtk_tree_selection_set_mode(tree_sel, GTK_SELECTION_BROWSE);
627
628 list = gtk_list_store_new( N_COLS,
629 GDK_TYPE_PIXBUF,
630 G_TYPE_STRING,
631 G_TYPE_POINTER,
632 G_TYPE_POINTER );
633 col = gtk_tree_view_column_new();
634 gtk_tree_view_column_set_title( col, _("Buttons") );
635
636 render = gtk_cell_renderer_pixbuf_new();
637 gtk_tree_view_column_pack_start( col, render, FALSE );
638 gtk_tree_view_column_set_attributes( col, render, "pixbuf", COL_ICON, NULL );
639
640 render = gtk_cell_renderer_text_new();
641 gtk_tree_view_column_pack_start( col, render, TRUE );
642 gtk_tree_view_column_add_attribute( col, render, "text", COL_TITLE );
643
644 gtk_tree_view_append_column( view, col );
645
646 for( l = lb->btns; l; l = l->next ) {
647 GtkTreeIter it;
648 GdkPixbuf* pix;
649 char* fname;
650
651 btn_t* btn = (btn_t*)l->data;
652#if 0
653 fname = expand_tilda( btn->image );
654 if( fname ) {
655 if( fname[0] == '/' ) /* file */
656 pix = gdk_pixbuf_new_from_file( fname, NULL );
657 else {
658 //pix =
659 }
660 }
661 else
662 pix = NULL;
663 g_free( fname );
664#endif
665 gtk_list_store_append( list, &it );
666 gtk_list_store_set( list, &it,
667 COL_ICON, NULL,
668 COL_TITLE, (btn->tooltip ? btn->tooltip : btn->action),
669 COL_BTN, btn, -1 );
670 }
671
672 gtk_tree_view_set_model( view, (GtkTreeModel*)list );
673 g_object_unref( list );
674
675 g_object_set_data( lb->config_dlg, "view", view );
676}
677
22242ed4 678static void launchbar_config( Plugin *p, GtkWindow* parent )
1dac04fb
HJYP
679{
680 GtkWidget *dlg, *hbox, *vbox, *scroll, *view, *btn, *img;
681 launchbar *lb = (launchbar *)p->priv;
682
683 if( !lb->config_dlg )
684 {
685 dlg = gtk_dialog_new_with_buttons( _(p->class->name),
686 GTK_WIDGET(parent), 0,
687 GTK_STOCK_CLOSE,
688 GTK_RESPONSE_CLOSE,
689 NULL );
690 lb->config_dlg = dlg;
691
692 hbox = gtk_hbox_new( FALSE, 4 );
693 gtk_box_pack_start( (GtkBox*)GTK_DIALOG(dlg)->vbox, hbox, TRUE, TRUE, 2 );
694
695 scroll = gtk_scrolled_window_new( NULL, NULL );
696 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)scroll,
697 GTK_POLICY_AUTOMATIC,
698 GTK_POLICY_AUTOMATIC );
699 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)scroll,
700 GTK_SHADOW_IN );
701 gtk_box_pack_start( (GtkBox*)hbox, scroll, TRUE, TRUE, 2 );
702
703 view = gtk_tree_view_new();
704 gtk_container_add( (GtkContainer*)scroll, view );
705
706 vbox = gtk_vbox_new( FALSE, 2 );
707 gtk_box_pack_start( (GtkBox*)hbox, vbox, FALSE, FALSE, 2 );
708
709 btn = gtk_button_new_from_stock( GTK_STOCK_ADD );
710 g_signal_connect( btn, "clicked", G_CALLBACK( on_add_btn ), p );
711 gtk_box_pack_start( (GtkBox*)vbox, btn, FALSE, FALSE, 2 );
712
713 btn = gtk_button_new_from_stock( GTK_STOCK_REMOVE );
714 g_signal_connect( btn, "clicked", G_CALLBACK( on_remove_btn ), p );
715 gtk_box_pack_start( (GtkBox*)vbox, btn, FALSE, FALSE, 2 );
716
717 btn = gtk_button_new();
718 gtk_container_add( GTK_CONTAINER(btn),
719 gtk_image_new_from_stock(GTK_STOCK_GO_UP,
720 GTK_ICON_SIZE_BUTTON) );
721 g_signal_connect( btn, "clicked", G_CALLBACK( on_up_btn ), p );
722 gtk_box_pack_start( (GtkBox*)vbox, btn, FALSE, FALSE, 2 );
723
724 btn = gtk_button_new();
725 gtk_container_add( GTK_CONTAINER(btn),
726 gtk_image_new_from_stock(GTK_STOCK_GO_DOWN,
727 GTK_ICON_SIZE_BUTTON) );
728 g_signal_connect( btn, "clicked", G_CALLBACK( on_down_btn ), p );
729 gtk_box_pack_start( (GtkBox*)vbox, btn, FALSE, FALSE, 2 );
730
731 g_signal_connect( dlg, "response", G_CALLBACK(on_response), p );
732
733 gtk_window_set_default_size( (GtkWindow*)dlg, 320, 400 );
734
735 init_btn_list( p, view );
736
737 gtk_widget_show_all( dlg );
738 }
739 gtk_window_present( GTK_WINDOW(lb->config_dlg) );
740}
741
22242ed4 742PluginClass launchbar_plugin_class = {
a52c2257
HJYP
743 fname: NULL,
744 count: 0,
745
746 type : "launchbar",
ce522551 747 name : N_("Application Launch Bar"),
a52c2257 748 version: "1.0",
e7cb732b 749 description : N_("Bar with buttons to launch application"),
a52c2257
HJYP
750
751 constructor : launchbar_constructor,
752 destructor : launchbar_destructor,
1dac04fb 753 config : launchbar_config,
a97d06a6
HJYP
754 save : save_config,
755 orientation : orientation_changed
a52c2257 756};