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