Improve popup menu of plugins.
[lxde/lxpanel.git] / src / plugins / launchbar.c
CommitLineData
239cb032 1/**
a99ee9e1
JH
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 */
a52c2257
HJYP
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
a52c2257 28#include <gdk-pixbuf/gdk-pixbuf.h>
e7cb732b 29#include <glib/gi18n.h>
a52c2257
HJYP
30
31#include "panel.h"
32#include "misc.h"
33#include "plugin.h"
a52c2257 34
a52c2257
HJYP
35#include "dbg.h"
36
1dac04fb 37#include "glib-mem.h"
a52c2257
HJYP
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
1dac04fb
HJYP
52enum {
53 COL_ICON = 0,
54 COL_TITLE,
55 COL_BTN,
56 N_COLS
57};
58
a52c2257
HJYP
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
08ea5341
HJYP
67static const char desktop_ent[] = "Desktop Entry";
68
1dac04fb 69typedef struct btn_t {
3e7b8eb7 70 Plugin* plugin;
1dac04fb
HJYP
71 GtkWidget* widget;
72 gchar *desktop_id;
73 gchar *image;
a52c2257 74 gchar *action;
1dac04fb
HJYP
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;
a52c2257 82
a52c2257
HJYP
83typedef struct launchbar {
84 GtkWidget *box;
85 GtkTooltips *tips;
1dac04fb 86 GSList* btns;
a52c2257 87 int iconsize;
1dac04fb 88 GtkWidget* config_dlg;
a52c2257
HJYP
89} launchbar;
90
1dac04fb
HJYP
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
a52c2257 100static gboolean
8110399f 101on_button_event(GtkWidget *widget, GdkEventButton *event, btn_t *b )
a52c2257
HJYP
102{
103 GtkWidget *image;
104
3e7b8eb7 105 if( event->button == 1 ) /* left button */
592ce930 106 {
3e7b8eb7
HJYP
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;
a52c2257
HJYP
149}
150
151static void
22242ed4 152launchbar_destructor(Plugin *p)
a52c2257
HJYP
153{
154 launchbar *lb = (launchbar *)p->priv;
a52c2257
HJYP
155
156 ENTER;
239cb032 157 /* g_object_unref( lb->tips ); */
3806c5aa 158
a52c2257 159 gtk_widget_destroy(lb->box);
1dac04fb
HJYP
160 g_slist_foreach( lb->btns, (GFunc)btn_free, NULL );
161 g_slice_free(launchbar, lb);
a52c2257
HJYP
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,
1dac04fb 174 btn_t *b)
a52c2257
HJYP
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);
da76d5cf 209
a52c2257
HJYP
210 //gtk_drag_finish (context, TRUE, FALSE, time);
211 }
212 RET();
213}
214
215static int
22242ed4 216read_button(Plugin *p, char** fp)
a52c2257
HJYP
217{
218 launchbar *lb = (launchbar *)p->priv;
1dac04fb 219 gchar *fname;
a52c2257
HJYP
220 GtkWidget *button;
221 line s;
a52c2257 222 int w, h;
1dac04fb 223 btn_t* btn;
9c97f69e 224
a52c2257 225 ENTER;
1dac04fb
HJYP
226
227 btn = g_slice_new0( btn_t );
3e7b8eb7 228 btn->plugin = p;
1dac04fb 229
a52c2257 230 s.len = 256;
1dac04fb 231 fname= NULL;
a52c2257 232
db449f6e
HJYP
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") )
1dac04fb
HJYP
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]);
db449f6e 246 fname = expand_tilda(s.t[1]);
1dac04fb
HJYP
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 }
db449f6e
HJYP
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);
a52c2257
HJYP
262 goto error;
263 }
a52c2257 264 }
db449f6e 265 DBG("action=%s\n", action);
a52c2257 266 }
a52c2257 267
1dac04fb 268 if( btn->desktop_id ) {
08ea5341
HJYP
269 gchar *desktop_file = NULL;
270 gchar *full_id = NULL;
271 GKeyFile* desktop = g_key_file_new();
1dac04fb 272 full_id = g_strconcat( "applications/", btn->desktop_id, NULL );
08ea5341
HJYP
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 ){
389975e0
HJYP
281 gchar* sep;
282 /* not a full path, remove the extension */
283 if( icon[0] != '/' && (sep = strchr( icon, '.' )) )
08ea5341
HJYP
284 fname = g_strndup( icon, (sep - icon) );
285 else
286 fname = icon;
287 }
1dac04fb 288 if( ! btn->customize_action ) {
08ea5341
HJYP
289 gchar* exec;
290 exec = g_key_file_get_string( desktop, desktop_ent, "Exec", NULL);
1dac04fb 291 btn->action = translate_exec_to_cmd( exec, icon, title, desktop_file );
08ea5341
HJYP
292 g_free( exec );
293 }
1dac04fb
HJYP
294 if( ! btn->customize_tooltip )
295 btn->tooltip = title;
08ea5341
HJYP
296 if( fname != icon )
297 g_free( icon );
1dac04fb 298 if( btn->tooltip != title )
08ea5341
HJYP
299 g_free( title );
300 }
301 g_free( full_id );
302 g_free( desktop_file );
303 g_key_file_free( desktop );
304 }
305
a52c2257
HJYP
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 }
a52c2257 316
1dac04fb
HJYP
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",
8110399f 322 G_CALLBACK (on_button_event), (gpointer) btn );
1dac04fb 323 g_signal_connect ( button, "button-press-event",
8110399f 324 G_CALLBACK (on_button_event), (gpointer) btn );
a52c2257 325
a52c2257 326 GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS);
1dac04fb 327
a52c2257
HJYP
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),
08ea5341 332 GDK_ACTION_COPY);
1dac04fb
HJYP
333 g_signal_connect ( button, "drag_data_received",
334 G_CALLBACK (drag_data_received_cb), (gpointer) btn );
a52c2257 335
a52c2257 336 gtk_box_pack_start(GTK_BOX(lb->box), button, FALSE, FALSE, 0);
1dac04fb
HJYP
337
338 /* append is more time-consuming, but we really care about the order. */
339 lb->btns = g_slist_append( lb->btns, btn );
340
a52c2257 341 gtk_widget_show(button);
08ea5341 342
a52c2257 343 g_free(fname);
1dac04fb 344
a52c2257 345 // tooltip
1dac04fb
HJYP
346 if ( btn->tooltip ) {
347 gtk_tooltips_set_tip(GTK_TOOLTIPS (lb->tips), button, btn->tooltip, NULL);
a52c2257 348 }
a52c2257
HJYP
349 RET(1);
350
351 error:
352 g_free(fname);
1dac04fb 353 btn_free( btn );
a52c2257
HJYP
354 RET(0);
355}
356
357static int
22242ed4 358launchbar_constructor(Plugin *p, char **fp)
a52c2257 359{
08ea5341 360 launchbar *lb;
a52c2257
HJYP
361 line s;
362 GtkRequisition req;
5297da29
HJYP
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";
1dac04fb 374 char *config_default = default_config;
a52c2257
HJYP
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"
6e6ffd0e 382 "widget '*launchbar*' style 'launchbar-style'";
da76d5cf 383
a52c2257 384 ENTER;
a52c2257 385 gtk_rc_parse_string(launchbar_rc);
4542c20d
HJYP
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");
a52c2257 391 get_button_spacing(&req, GTK_CONTAINER(p->pwid), "");
da76d5cf 392
1dac04fb 393 lb = g_slice_new0(launchbar);
a52c2257
HJYP
394 g_return_val_if_fail(lb != NULL, 0);
395 p->priv = lb;
396 lb->box = p->panel->my_box_new(FALSE, 0);
4542c20d
HJYP
397
398 gtk_container_add( (GtkContainer*)p->pwid, lb->box );
399
a52c2257
HJYP
400 gtk_container_set_border_width (GTK_CONTAINER (lb->box), 0);
401 gtk_widget_show(lb->box);
239cb032
HJYP
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;
4542c20d 406
08ea5341 407 if (p->panel->orientation == ORIENT_HORIZ)
a52c2257
HJYP
408 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.height;
409 else
410 lb->iconsize = GTK_WIDGET(p->panel->box)->allocation.width;
da76d5cf 411
5297da29
HJYP
412 if( ! fp )
413 fp = &config_default;
414
5297da29
HJYP
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");
a52c2257
HJYP
425 goto error;
426 }
427 } else {
5297da29 428 ERR( "launchbar: unknown var %s\n", s.t[0]);
a52c2257
HJYP
429 goto error;
430 }
5297da29
HJYP
431 } else {
432 ERR( "launchbar: illegal in this context %s\n", s.str);
433 goto error;
a52c2257 434 }
da76d5cf 435 }
a52c2257
HJYP
436
437 RET(1);
438
439 error:
440 launchbar_destructor(p);
441 RET(0);
da76d5cf 442
a52c2257
HJYP
443}
444
22242ed4 445static void save_config( Plugin* p, FILE* fp )
9c97f69e 446{
da76d5cf 447 launchbar *lb = (launchbar *)p->priv;
1dac04fb
HJYP
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, "}" );
da76d5cf 461 }
9c97f69e 462}
a52c2257 463
22242ed4 464static void orientation_changed( Plugin* p )
a97d06a6
HJYP
465{
466 launchbar *lb = (launchbar *)p->priv;
467 GtkBox* newbox;
5a343ad5
JH
468 newbox = GTK_BOX(recreate_box( GTK_BOX(lb->box), p->panel->orientation ));
469 if( GTK_WIDGET(newbox) != lb->box ) {
a97d06a6
HJYP
470 /* Since the old box has been destroyed,
471 we need to re-add the new box to the container */
5a343ad5 472 lb->box = GTK_WIDGET(newbox);
a97d06a6
HJYP
473 gtk_container_add(GTK_CONTAINER(p->pwid), lb->box);
474 }
475}
476
1dac04fb 477static void
22242ed4 478on_response( GtkDialog* dlg, int response, Plugin* p )
1dac04fb
HJYP
479{
480 launchbar *lb = (launchbar *)p->priv;
460e2e78 481 gtk_widget_destroy( GTK_WIDGET(dlg) );
1dac04fb
HJYP
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
22242ed4 491static void on_add_btn( GtkButton* widget, Plugin* p )
1dac04fb
HJYP
492{
493 launchbar *lb = (launchbar *)p->priv;
460e2e78 494 GtkTreeView* view = (GtkTreeView*)g_object_get_data( (GObject *) lb->config_dlg, "view" );
1dac04fb
HJYP
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.
460e2e78 509 1. We can borrow the menu from menu plugin (PtkAppMenu).
1dac04fb
HJYP
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
22242ed4 564static void on_remove_btn( GtkButton* widget, Plugin* p )
1dac04fb
HJYP
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
22242ed4 585static void on_up_btn( GtkButton* widget, Plugin* p )
1dac04fb
HJYP
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
22242ed4 614static void on_down_btn( GtkButton* widget, Plugin* p )
1dac04fb
HJYP
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
22242ed4 644static void init_btn_list( Plugin* p, GtkTreeView* view )
1dac04fb
HJYP
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
22242ed4 705static void launchbar_config( Plugin *p, GtkWindow* parent )
1dac04fb
HJYP
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
22242ed4 769PluginClass launchbar_plugin_class = {
a52c2257
HJYP
770 fname: NULL,
771 count: 0,
772
773 type : "launchbar",
ce522551 774 name : N_("Application Launch Bar"),
a52c2257 775 version: "1.0",
e7cb732b 776 description : N_("Bar with buttons to launch application"),
a52c2257
HJYP
777
778 constructor : launchbar_constructor,
779 destructor : launchbar_destructor,
1dac04fb 780 config : launchbar_config,
a97d06a6
HJYP
781 save : save_config,
782 orientation : orientation_changed
a52c2257 783};