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