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