Rename panel, plugin, and plugin_class to Panel, Plugin, and
[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 //#define DEBUG
36 #include "dbg.h"
37
38 #include "glib-mem.h"
39
40 typedef enum {
41 CURSOR_STANDARD,
42 CURSOR_DND
43 } CursorType;
44
45 enum {
46 TARGET_URILIST,
47 TARGET_UTF8_STRING,
48 TARGET_STRING,
49 TARGET_TEXT,
50 TARGET_COMPOUND_TEXT
51 };
52
53 enum {
54 COL_ICON = 0,
55 COL_TITLE,
56 COL_BTN,
57 N_COLS
58 };
59
60 static 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
68 static const char desktop_ent[] = "Desktop Entry";
69
70 typedef struct btn_t {
71 GtkWidget* widget;
72 gchar *desktop_id;
73 gchar *image;
74 gchar *action;
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;
82
83 typedef struct launchbar {
84 GtkWidget *box;
85 GtkTooltips *tips;
86 GSList* btns;
87 int iconsize;
88 GtkWidget* config_dlg;
89 } launchbar;
90
91 void 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
100 static gboolean
101 on_button_pressed(GtkWidget *widget, GdkEventButton *event, btn_t *b )
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)) {
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 RET(TRUE);
123 }
124
125 static void
126 launchbar_destructor(Plugin *p)
127 {
128 launchbar *lb = (launchbar *)p->priv;
129
130 ENTER;
131 /* g_object_unref( lb->tips ); */
132
133 gtk_widget_destroy(lb->box);
134 g_slist_foreach( lb->btns, (GFunc)btn_free, NULL );
135 g_slice_free(launchbar, lb);
136 RET();
137 }
138
139
140 static void
141 drag_data_received_cb (GtkWidget *widget,
142 GdkDragContext *context,
143 gint x,
144 gint y,
145 GtkSelectionData *sd,
146 guint info,
147 guint time,
148 btn_t *b)
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);
183
184 //gtk_drag_finish (context, TRUE, FALSE, time);
185 }
186 RET();
187 }
188
189 static int
190 read_button(Plugin *p, char** fp)
191 {
192 launchbar *lb = (launchbar *)p->priv;
193 gchar *fname;
194 GtkWidget *button;
195 line s;
196 int w, h;
197 btn_t* btn;
198
199 ENTER;
200
201 btn = g_slice_new0( btn_t );
202
203 s.len = 256;
204 fname= NULL;
205
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") )
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]);
219 fname = expand_tilda(s.t[1]);
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 }
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);
235 goto error;
236 }
237 }
238 DBG("action=%s\n", action);
239 }
240
241 if( btn->desktop_id ) {
242 gchar *desktop_file = NULL;
243 gchar *full_id = NULL;
244 GKeyFile* desktop = g_key_file_new();
245 full_id = g_strconcat( "applications/", btn->desktop_id, NULL );
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 ){
254 gchar* sep;
255 /* not a full path, remove the extension */
256 if( icon[0] != '/' && (sep = strchr( icon, '.' )) )
257 fname = g_strndup( icon, (sep - icon) );
258 else
259 fname = icon;
260 }
261 if( ! btn->customize_action ) {
262 gchar* exec;
263 exec = g_key_file_get_string( desktop, desktop_ent, "Exec", NULL);
264 btn->action = translate_exec_to_cmd( exec, icon, title, desktop_file );
265 g_free( exec );
266 }
267 if( ! btn->customize_tooltip )
268 btn->tooltip = title;
269 if( fname != icon )
270 g_free( icon );
271 if( btn->tooltip != title )
272 g_free( title );
273 }
274 g_free( full_id );
275 g_free( desktop_file );
276 g_key_file_free( desktop );
277 }
278
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 }
289
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 );
298
299 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
300
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),
305 GDK_ACTION_COPY);
306 g_signal_connect ( button, "drag_data_received",
307 G_CALLBACK (drag_data_received_cb), (gpointer) btn );
308
309 gtk_box_pack_start(GTK_BOX(lb->box), button, FALSE, FALSE, 0);
310
311 /* append is more time-consuming, but we really care about the order. */
312 lb->btns = g_slist_append( lb->btns, btn );
313
314 gtk_widget_show(button);
315
316 g_free(fname);
317
318 // tooltip
319 if ( btn->tooltip ) {
320 gtk_tooltips_set_tip(GTK_TOOLTIPS (lb->tips), button, btn->tooltip, NULL);
321 }
322 RET(1);
323
324 error:
325 g_free(fname);
326 btn_free( btn );
327 RET(0);
328 }
329
330 static int
331 launchbar_constructor(Plugin *p, char **fp)
332 {
333 launchbar *lb;
334 line s;
335 GtkRequisition req;
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";
347 char *config_default = default_config;
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'";
356
357 ENTER;
358 gtk_rc_parse_string(launchbar_rc);
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");
364 get_button_spacing(&req, GTK_CONTAINER(p->pwid), "");
365
366 lb = g_slice_new0(launchbar);
367 g_return_val_if_fail(lb != NULL, 0);
368 p->priv = lb;
369 lb->box = p->panel->my_box_new(FALSE, 0);
370
371 gtk_container_add( (GtkContainer*)p->pwid, lb->box );
372
373 gtk_container_set_border_width (GTK_CONTAINER (lb->box), 0);
374 gtk_widget_show(lb->box);
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;
379
380 if (p->panel->orientation == ORIENT_HORIZ)
381 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.height;
382 else
383 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.width;
384
385 if( ! fp )
386 fp = &config_default;
387
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");
398 goto error;
399 }
400 } else {
401 ERR( "launchbar: unknown var %s\n", s.t[0]);
402 goto error;
403 }
404 } else {
405 ERR( "launchbar: illegal in this context %s\n", s.str);
406 goto error;
407 }
408 }
409
410 RET(1);
411
412 error:
413 launchbar_destructor(p);
414 RET(0);
415
416 }
417
418 static void save_config( Plugin* p, FILE* fp )
419 {
420 launchbar *lb = (launchbar *)p->priv;
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, "}" );
434 }
435 }
436
437 static void orientation_changed( Plugin* p )
438 {
439 launchbar *lb = (launchbar *)p->priv;
440 GtkBox* newbox;
441 newbox = GTK_BOX(recreate_box( GTK_BOX(lb->box), p->panel->orientation ));
442 if( GTK_WIDGET(newbox) != lb->box ) {
443 /* Since the old box has been destroyed,
444 we need to re-add the new box to the container */
445 lb->box = GTK_WIDGET(newbox);
446 gtk_container_add(GTK_CONTAINER(p->pwid), lb->box);
447 }
448 }
449
450 static void
451 on_response( GtkDialog* dlg, int response, Plugin* p )
452 {
453 launchbar *lb = (launchbar *)p->priv;
454 gtk_widget_destroy( GTK_WIDGET(dlg) );
455 lb->config_dlg = NULL;
456 }
457
458 static void on_add_btn_response( GtkDialog* dlg, int response, int* ret )
459 {
460 *ret = response;
461 gtk_main_quit();
462 }
463
464 static void on_add_btn( GtkButton* widget, Plugin* p )
465 {
466 launchbar *lb = (launchbar *)p->priv;
467 GtkTreeView* view = (GtkTreeView*)g_object_get_data( (GObject *) lb->config_dlg, "view" );
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.
482 1. We can borrow the menu from menu plugin (PtkAppMenu).
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
537 static void on_remove_btn( GtkButton* widget, Plugin* p )
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
558 static void on_up_btn( GtkButton* widget, Plugin* p )
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
587 static void on_down_btn( GtkButton* widget, Plugin* p )
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
617 static void init_btn_list( Plugin* p, GtkTreeView* view )
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
678 static void launchbar_config( Plugin *p, GtkWindow* parent )
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
742 PluginClass launchbar_plugin_class = {
743 fname: NULL,
744 count: 0,
745
746 type : "launchbar",
747 name : N_("Application Launch Bar"),
748 version: "1.0",
749 description : N_("Bar with buttons to launch application"),
750
751 constructor : launchbar_constructor,
752 destructor : launchbar_destructor,
753 config : launchbar_config,
754 save : save_config,
755 orientation : orientation_changed
756 };