Adding debian version 0.3.6.3-1.
[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);
05ddbe60 342 plugin_widget_set_background( button, p->panel );
6cc5e1a6
DB
343
344 g_free(fname);
345
346 // tooltip
347 if ( btn->tooltip ) {
348 gtk_tooltips_set_tip(GTK_TOOLTIPS (lb->tips), button, btn->tooltip, NULL);
349 }
350 RET(1);
351
352 error:
353 g_free(fname);
354 btn_free( btn );
355 RET(0);
356}
357
358static int
359launchbar_constructor(Plugin *p, char **fp)
360{
361 launchbar *lb;
362 line s;
363 GtkRequisition req;
364 static char default_config[] =
365 "button {\n"
366 "id=pcmanfm.desktop\n"
367 "}\n"
368 "button {\n"
369 "id=gnome-terminal.desktop\n"
370 "}\n"
371 "button {\n"
372 "id=firefox.desktop\n"
373 "}\n"
374 "}\n";
375 char *config_default = default_config;
376 static gchar *launchbar_rc = "style 'launchbar-style'\n"
377 "{\n"
378 "GtkWidget::focus-line-width = 0\n"
379 "GtkWidget::focus-padding = 0\n"
380 "GtkButton::default-border = { 0, 0, 0, 0 }\n"
381 "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
382 "}\n"
383 "widget '*launchbar*' style 'launchbar-style'";
384
385 ENTER;
386 gtk_rc_parse_string(launchbar_rc);
387
388 p->pwid = gtk_event_box_new();
389 GTK_WIDGET_SET_FLAGS( p->pwid, GTK_NO_WINDOW );
390
391 gtk_widget_set_name(p->pwid, "launchbar");
392 get_button_spacing(&req, GTK_CONTAINER(p->pwid), "");
393
394 lb = g_slice_new0(launchbar);
395 g_return_val_if_fail(lb != NULL, 0);
396 p->priv = lb;
397 lb->box = p->panel->my_box_new(FALSE, 0);
398
399 gtk_container_add( (GtkContainer*)p->pwid, lb->box );
400
401 gtk_container_set_border_width (GTK_CONTAINER (lb->box), 0);
402 gtk_widget_show(lb->box);
403
404 /* Use the shared tooltip object provided by the panel, and
405 we don't need to create a new one. */
406 lb->tips = p->panel->tooltips;
407
408 if (p->panel->orientation == ORIENT_HORIZ)
409 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.height;
410 else
411 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.width;
412
413 if( ! fp )
414 fp = &config_default;
415
416 s.len = 256;
417 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
418 if (s.type == LINE_NONE) {
419 ERR( "launchbar: illegal token %s\n", s.str);
420 goto error;
421 }
422 if (s.type == LINE_BLOCK_START) {
423 if (!g_ascii_strcasecmp(s.t[0], "button")) {
424 if (!read_button(p, fp)) {
425 ERR( "launchbar: can't init button\n");
426 goto error;
427 }
428 } else {
429 ERR( "launchbar: unknown var %s\n", s.t[0]);
430 goto error;
431 }
432 } else {
433 ERR( "launchbar: illegal in this context %s\n", s.str);
434 goto error;
435 }
436 }
437
438 RET(1);
439
440 error:
441 launchbar_destructor(p);
442 RET(0);
443
444}
445
446static void save_config( Plugin* p, FILE* fp )
447{
448 launchbar *lb = (launchbar *)p->priv;
449 GSList* l;
450 for( l = lb->btns; l; l = l->next ) {
451 btn_t* btn = (btn_t*)l->data;
452 lxpanel_put_line( fp, "Button {" );
453 if( btn->desktop_id )
454 lxpanel_put_str( fp, "id", btn->desktop_id );
455 if( btn->customize_image )
456 lxpanel_put_str( fp, "image", btn->image );
457 if( btn->customize_tooltip )
458 lxpanel_put_str( fp, "tooltip", btn->tooltip );
459 if( btn->customize_action )
460 lxpanel_put_str( fp, "action", btn->action );
461 lxpanel_put_line( fp, "}" );
462 }
463}
464
465static void orientation_changed( Plugin* p )
466{
467 launchbar *lb = (launchbar *)p->priv;
468 GtkBox* newbox;
469 newbox = GTK_BOX(recreate_box( GTK_BOX(lb->box), p->panel->orientation ));
470 if( GTK_WIDGET(newbox) != lb->box ) {
471 /* Since the old box has been destroyed,
472 we need to re-add the new box to the container */
473 lb->box = GTK_WIDGET(newbox);
474 gtk_container_add(GTK_CONTAINER(p->pwid), lb->box);
475 }
476}
477
478static void
479on_response( GtkDialog* dlg, int response, Plugin* p )
480{
481 launchbar *lb = (launchbar *)p->priv;
482 gtk_widget_destroy( GTK_WIDGET(dlg) );
483 lb->config_dlg = NULL;
484}
485
486static void on_add_btn_response( GtkDialog* dlg, int response, int* ret )
487{
488 *ret = response;
489 gtk_main_quit();
490}
491
492static void on_add_btn( GtkButton* widget, Plugin* p )
493{
494 launchbar *lb = (launchbar *)p->priv;
495 GtkTreeView* view = (GtkTreeView*)g_object_get_data( (GObject *) lb->config_dlg, "view" );
496 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
497 GtkTreeIter it;
498 GtkListStore* list;
499 GtkFileChooserDialog* dlg;
500 GtkFileFilter* filter;
501 int response;
502
503 /*
504 if( !gtk_tree_selection_get_selected( tree_sel, &list, &it ) )
505 return;
506 */
507 list = (GtkListStore*)gtk_tree_view_get_model( view );
508
509 /* FIXME: We should have a better interface for this in the fututure.
510 1. We can borrow the menu from menu plugin (PtkAppMenu).
511 2. We can borrow the app chooser from PCManFM.
512 */
513 dlg = gtk_file_chooser_dialog_new(_("Select Application"),
514 lb->config_dlg,
515 GTK_FILE_CHOOSER_ACTION_OPEN,
516 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
517 GTK_STOCK_ADD, GTK_RESPONSE_OK, NULL );
518 filter = gtk_file_filter_new();
519 gtk_file_filter_set_name( filter, "*.desktop" );
520 gtk_file_filter_add_pattern( filter, "*.desktop" );
521 gtk_file_chooser_add_filter( dlg, filter );
522 g_object_unref( filter );
523 gtk_file_chooser_set_local_only( dlg, TRUE );
524 gtk_file_chooser_set_current_folder( dlg, "/usr/share/applications" );
525
526 gtk_widget_set_sensitive( lb->config_dlg, FALSE );
527 g_signal_connect( dlg, "response", on_add_btn_response, &response );
528 gtk_window_present( dlg );
529 gtk_main();
530 gtk_widget_set_sensitive( lb->config_dlg, TRUE );
531
532 if( response == GTK_RESPONSE_OK ) {
533 char* filename = gtk_file_chooser_get_filename( dlg );
534 if( filename ) {
535 if( g_str_has_suffix( filename, ".desktop" ) ) {
536 char* desktop_id = g_path_get_basename( filename );
537 char *config, *pconfig;
538 config = pconfig = g_strdup_printf( "id=%s\n}\n", desktop_id );
539 g_free( desktop_id );
540 /* Make a fake config entry, and let read_button() parst it. */
541 /* FIXME: This is a quick hack, which is dirty but easy and useful.
542 Need to be re-written in the future.
543 */
544 if( read_button( p, &pconfig ) ) {
545 GSList* l;
546 btn_t* btn;
547 l = g_slist_last( lb->btns );
548 btn = (btn_t*)l->data;
549 gtk_list_store_append( list, &it );
550 gtk_list_store_set( list, &it,
551 COL_ICON, NULL, /* FIXME: need to be implemented */
552 COL_TITLE, (btn->tooltip ? btn->tooltip : btn->action),
553 COL_BTN, btn,
554 -1 );
555 }
556 g_free( config );
557 }
558 g_free( filename );
559 }
560 }
561
562 gtk_widget_destroy( dlg );
563}
564
565static void on_remove_btn( GtkButton* widget, Plugin* p )
566{
567 launchbar *lb = (launchbar *)p->priv;
568 GtkTreeView* view = (GtkTreeView*)g_object_get_data( lb->config_dlg, "view" );
569 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
570 GtkTreeIter it;
571 GtkListStore* list;
572 btn_t* btn;
573
574 if( !gtk_tree_selection_get_selected( tree_sel, &list, &it ) )
575 return;
576 gtk_tree_model_get( (GtkTreeModel*)list, &it,
577 COL_BTN, &btn, -1 );
578 gtk_list_store_remove( list, &it );
579 if( btn ) {
580 lb->btns = g_slist_remove( lb->btns, btn );
581 gtk_widget_destroy( btn->widget );
582 btn_free( btn );
583 }
584}
585
586static void on_up_btn( GtkButton* widget, Plugin* p )
587{
588 launchbar *lb = (launchbar *)p->priv;
589 btn_t *btn;
590 GtkTreeView* view = (GtkTreeView*)g_object_get_data( lb->config_dlg, "view" );
591 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
592 GtkTreeIter it;
593 GtkTreePath* path;
594 GtkListStore* list;
595
596 if( !gtk_tree_selection_get_selected( tree_sel, &list, &it ) )
597 return;
598 gtk_tree_model_get( (GtkTreeModel*)list, &it, COL_BTN, &btn, -1 );
599 path = gtk_tree_model_get_path( (GtkTreeModel*)list, &it );
600 if( gtk_tree_path_get_indices(path)[0] > 0 ) {
601 if( gtk_tree_path_prev(path) ) {
602 GtkTreeIter it2;
603 if( gtk_tree_model_get_iter( (GtkTreeModel*)list, &it2, path ) ) {
604 int i = gtk_tree_path_get_indices(path)[0];
605 lb->btns = g_slist_remove( lb->btns, btn );
606 lb->btns = g_slist_insert( lb->btns, btn, i );
607 gtk_list_store_move_before( (GtkTreeModel*)list, &it, &it2 );
608 gtk_box_reorder_child( lb->box, btn->widget, i );
609 }
610 }
611 }
612 gtk_tree_path_free( path );
613}
614
615static void on_down_btn( GtkButton* widget, Plugin* p )
616{
617 launchbar *lb = (launchbar *)p->priv;
618 btn_t *btn;
619 GtkTreeView* view = (GtkTreeView*)g_object_get_data( lb->config_dlg, "view" );
620 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
621 GtkTreeIter it;
622 GtkTreePath* path;
623 GtkListStore* list;
624 int n;
625
626 if( !gtk_tree_selection_get_selected( tree_sel, &list, &it ) )
627 return;
628 gtk_tree_model_get( (GtkTreeModel*)list, &it, COL_BTN, &btn, -1 );
629 path = gtk_tree_model_get_path( (GtkTreeModel*)list, &it );
630 n = gtk_tree_model_iter_n_children( (GtkTreeModel*)list, NULL );
631 if( gtk_tree_path_get_indices(path)[0] < n - 1 ) {
632 GtkTreeIter it2;
633 gtk_tree_path_next(path);
634 if( gtk_tree_model_get_iter( (GtkTreeModel*)list, &it2, path ) ) {
635 int i = gtk_tree_path_get_indices(path)[0];
636 lb->btns = g_slist_insert( lb->btns, btn, i + 1 );
637 lb->btns = g_slist_remove( lb->btns, btn );
638 gtk_list_store_move_after( (GtkTreeModel*)list, &it, &it2 );
639 gtk_box_reorder_child( lb->box, btn->widget, i );
640 }
641 }
642 gtk_tree_path_free( path );
643}
644
645static void init_btn_list( Plugin* p, GtkTreeView* view )
646{
647 launchbar *lb = (launchbar *)p->priv;
648 GtkListStore *list;
649 GSList* l;
650 GtkTreeViewColumn* col;
651 GtkCellRenderer* render;
652 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(view);
653
654 gtk_tree_selection_set_mode(tree_sel, GTK_SELECTION_BROWSE);
655
656 list = gtk_list_store_new( N_COLS,
657 GDK_TYPE_PIXBUF,
658 G_TYPE_STRING,
659 G_TYPE_POINTER,
660 G_TYPE_POINTER );
661 col = gtk_tree_view_column_new();
662 gtk_tree_view_column_set_title( col, _("Buttons") );
663
664 render = gtk_cell_renderer_pixbuf_new();
665 gtk_tree_view_column_pack_start( col, render, FALSE );
666 gtk_tree_view_column_set_attributes( col, render, "pixbuf", COL_ICON, NULL );
667
668 render = gtk_cell_renderer_text_new();
669 gtk_tree_view_column_pack_start( col, render, TRUE );
670 gtk_tree_view_column_add_attribute( col, render, "text", COL_TITLE );
671
672 gtk_tree_view_append_column( view, col );
673
674 for( l = lb->btns; l; l = l->next ) {
675 GtkTreeIter it;
676 GdkPixbuf* pix;
677 char* fname;
678
679 btn_t* btn = (btn_t*)l->data;
680#if 0
681 fname = expand_tilda( btn->image );
682 if( fname ) {
683 if( fname[0] == '/' ) /* file */
684 pix = gdk_pixbuf_new_from_file( fname, NULL );
685 else {
686 //pix =
687 }
688 }
689 else
690 pix = NULL;
691 g_free( fname );
692#endif
693 gtk_list_store_append( list, &it );
694 gtk_list_store_set( list, &it,
695 COL_ICON, NULL,
696 COL_TITLE, (btn->tooltip ? btn->tooltip : btn->action),
697 COL_BTN, btn, -1 );
698 }
699
700 gtk_tree_view_set_model( view, (GtkTreeModel*)list );
701 g_object_unref( list );
702
703 g_object_set_data( lb->config_dlg, "view", view );
704}
705
706static void launchbar_config( Plugin *p, GtkWindow* parent )
707{
708 GtkWidget *dlg, *hbox, *vbox, *scroll, *view, *btn, *img;
709 launchbar *lb = (launchbar *)p->priv;
710
711 if( !lb->config_dlg )
712 {
713 dlg = gtk_dialog_new_with_buttons( _(p->class->name),
714 GTK_WIDGET(parent), 0,
715 GTK_STOCK_CLOSE,
716 GTK_RESPONSE_CLOSE,
717 NULL );
718 lb->config_dlg = dlg;
719
720 hbox = gtk_hbox_new( FALSE, 4 );
721 gtk_box_pack_start( (GtkBox*)GTK_DIALOG(dlg)->vbox, hbox, TRUE, TRUE, 2 );
722
723 scroll = gtk_scrolled_window_new( NULL, NULL );
724 gtk_scrolled_window_set_policy( (GtkScrolledWindow*)scroll,
725 GTK_POLICY_AUTOMATIC,
726 GTK_POLICY_AUTOMATIC );
727 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)scroll,
728 GTK_SHADOW_IN );
729 gtk_box_pack_start( (GtkBox*)hbox, scroll, TRUE, TRUE, 2 );
730
731 view = gtk_tree_view_new();
732 gtk_container_add( (GtkContainer*)scroll, view );
733
734 vbox = gtk_vbox_new( FALSE, 2 );
735 gtk_box_pack_start( (GtkBox*)hbox, vbox, FALSE, FALSE, 2 );
736
737 btn = gtk_button_new_from_stock( GTK_STOCK_ADD );
738 g_signal_connect( btn, "clicked", G_CALLBACK( on_add_btn ), p );
739 gtk_box_pack_start( (GtkBox*)vbox, btn, FALSE, FALSE, 2 );
740
741 btn = gtk_button_new_from_stock( GTK_STOCK_REMOVE );
742 g_signal_connect( btn, "clicked", G_CALLBACK( on_remove_btn ), p );
743 gtk_box_pack_start( (GtkBox*)vbox, btn, FALSE, FALSE, 2 );
744
745 btn = gtk_button_new();
746 gtk_container_add( GTK_CONTAINER(btn),
747 gtk_image_new_from_stock(GTK_STOCK_GO_UP,
748 GTK_ICON_SIZE_BUTTON) );
749 g_signal_connect( btn, "clicked", G_CALLBACK( on_up_btn ), p );
750 gtk_box_pack_start( (GtkBox*)vbox, btn, FALSE, FALSE, 2 );
751
752 btn = gtk_button_new();
753 gtk_container_add( GTK_CONTAINER(btn),
754 gtk_image_new_from_stock(GTK_STOCK_GO_DOWN,
755 GTK_ICON_SIZE_BUTTON) );
756 g_signal_connect( btn, "clicked", G_CALLBACK( on_down_btn ), p );
757 gtk_box_pack_start( (GtkBox*)vbox, btn, FALSE, FALSE, 2 );
758
759 g_signal_connect( dlg, "response", G_CALLBACK(on_response), p );
760
761 gtk_window_set_default_size( (GtkWindow*)dlg, 320, 400 );
762
763 init_btn_list( p, view );
764
765 gtk_widget_show_all( dlg );
766 }
767 gtk_window_present( GTK_WINDOW(lb->config_dlg) );
768}
769
770PluginClass launchbar_plugin_class = {
771 fname: NULL,
772 count: 0,
773
774 type : "launchbar",
775 name : N_("Application Launch Bar"),
776 version: "1.0",
777 description : N_("Bar with buttons to launch application"),
778
779 constructor : launchbar_constructor,
780 destructor : launchbar_destructor,
781 config : launchbar_config,
782 save : save_config,
783 orientation : orientation_changed
784};