Rename panel, plugin, and plugin_class to Panel, Plugin, and
[lxde/lxpanel.git] / src / plugins / dirmenu.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 */
18
f9dfb60b
HJYP
19#include <stdlib.h>
20#include <unistd.h>
21
22#include <gdk-pixbuf/gdk-pixbuf.h>
23#include <glib/gi18n.h>
239cb032 24#include <string.h>
f9dfb60b
HJYP
25
26#include "panel.h"
27#include "misc.h"
28#include "plugin.h"
f9dfb60b
HJYP
29#include "dbg.h"
30
239cb032
HJYP
31/* NOTE: dirty hack for g_quark_from_static_string */
32#define NAME_ID GPOINTER_TO_UINT("name")
33#define PATH_ID GPOINTER_TO_UINT("path")
34
f9dfb60b 35typedef struct {
22242ed4 36 Panel* panel;
f9dfb60b
HJYP
37 char* image;
38 char* path;
f9dfb60b
HJYP
39 GtkWidget *button;
40} dirmenu;
41
ab77df37
HJYP
42static GdkPixbuf* folder_icon = NULL;
43
22242ed4 44static GtkWidget* create_menu( Plugin* p,
239cb032
HJYP
45 const char* path,
46 gboolean open_at_top );
f9dfb60b 47
22242ed4 48static void open_dir( Plugin* p, const char* path )
f9dfb60b 49{
239cb032
HJYP
50 char* cmd;
51 char* quote = g_shell_quote( path );
faa2693d
HJYP
52 const char* fm = lxpanel_get_file_manager( p->panel );
53 if( strstr( fm, "%s" ) )
54 cmd = g_strdup_printf( fm, quote );
239cb032 55 else
faa2693d 56 cmd = g_strdup_printf( "%s %s", fm, quote );
239cb032 57 g_free( quote );
f9dfb60b 58 g_spawn_command_line_async( cmd, NULL );
239cb032
HJYP
59 g_free( cmd );
60}
61
22242ed4 62static void on_open_dir( GtkWidget* item, Plugin* p )
239cb032
HJYP
63{
64 GtkWidget* menu = gtk_widget_get_parent(item);
65 const char* path = g_object_get_qdata( menu, PATH_ID );
66 open_dir( p, path );
f9dfb60b
HJYP
67}
68
22242ed4 69static void open_in_term( Plugin* p, const char* path )
f9dfb60b 70{
faa2693d 71 const char* term = lxpanel_get_terminal( p->panel );
f9dfb60b 72 chdir( path );
faa2693d 73 g_spawn_command_line_async( term, NULL );
239cb032
HJYP
74}
75
22242ed4 76static void on_open_in_term( GtkWidget* item, Plugin* p )
239cb032
HJYP
77{
78 GtkWidget* menu = gtk_widget_get_parent(item);
79 const char* path = g_object_get_qdata( menu, PATH_ID );
80 open_in_term( p, path );
f9dfb60b
HJYP
81}
82
83static void
22242ed4 84menu_pos( GtkMenu *menu, gint *x, gint *y, gboolean *push_in, Plugin* p )
f9dfb60b
HJYP
85{
86 int ox, oy, w, h;
87 dirmenu *dm = (dirmenu *)p->priv;
88
89 ENTER;
90 gdk_window_get_origin( dm->button->window, &ox, &oy );
91 w = GTK_WIDGET(menu)->requisition.width;
92 h = GTK_WIDGET(menu)->requisition.height;
93 if (p->panel->orientation == ORIENT_HORIZ) {
94 *x = ox;
95 if (*x + w > gdk_screen_width())
96 *x = ox + dm->button->allocation.width - w;
97 *y = oy - h;
98 if (*y < 0)
99 *y = oy + dm->button->allocation.height;
100 } else {
101 *x = ox + dm->button->allocation.width;
102 if (*x > gdk_screen_width())
103 *x = ox - w;
104 *y = oy;
105 if (*y + h > gdk_screen_height())
106 *y = oy + dm->button->allocation.height - h;
107 }
108 *push_in = TRUE;
109 RET();
110}
111
22242ed4 112static void on_select( GtkMenuItem* item, Plugin* p )
f9dfb60b 113{
239cb032 114 GtkMenu* parent;
f9dfb60b
HJYP
115 GtkWidget* sub = gtk_menu_item_get_submenu( item );
116 char* path;
117 if( !sub )
118 return;
239cb032
HJYP
119 parent = (GtkMenu*)gtk_widget_get_parent( (GtkWidget*)item );
120 path = (char*)g_object_get_qdata( sub, PATH_ID );
f9dfb60b 121 if( !path ){
239cb032
HJYP
122 path = g_build_filename( (char*)g_object_get_qdata( parent, PATH_ID ),
123 (char*)g_object_get_qdata( item, NAME_ID ), NULL );
124 sub = create_menu( p, path, TRUE );
f9dfb60b
HJYP
125 g_free( path );
126 gtk_menu_item_set_submenu( item, sub );
127 }
128}
129
239cb032
HJYP
130#if GTK_CHECK_VERSION(2, 10, 0)
131/* NOTE: It seems that this doesn't work in older versions of gtk+?? */
22242ed4 132static void on_deselect( GtkMenuItem* item, Plugin* p )
f9dfb60b
HJYP
133{
134 /* delete old menu on deselect to save resource */
135 gtk_menu_item_set_submenu( item, gtk_menu_new() );
136}
239cb032 137#endif
f9dfb60b 138
22242ed4 139void on_sel_done( GtkWidget *menu, Plugin* p )
ab77df37
HJYP
140{
141 gtk_widget_destroy( menu );
142 if( folder_icon )
143 {
144 g_object_unref( folder_icon );
145 folder_icon = NULL;
146 }
147}
148
22242ed4 149static GtkWidget* create_menu( Plugin* p,
239cb032
HJYP
150 const char* path,
151 gboolean open_at_top )
f9dfb60b
HJYP
152{
153 GDir* dir;
154 GtkWidget *menu = gtk_menu_new();
155 GtkWidget *item, *term;
156 /* GList *list = NULL; */
157
ab77df37
HJYP
158 if( G_UNLIKELY(NULL == folder_icon) )
159 {
160 int w, h;
161 gtk_icon_size_lookup_for_settings( gtk_widget_get_settings(menu),
162 GTK_ICON_SIZE_MENU, &w, &h );
163 folder_icon = gtk_icon_theme_load_icon( gtk_icon_theme_get_default(),
164 "gnome-fs-directory", MAX( w, h ), 0, NULL );
165
166 if( ! folder_icon )
167 folder_icon = gtk_widget_render_icon( menu, GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL );
168 }
169
239cb032 170 g_object_set_qdata_full( menu, PATH_ID, g_strdup(path), g_free );
f9dfb60b
HJYP
171
172 if( dir = g_dir_open( path, 0, NULL ) ) {
173 const char* name;
174 while( name = g_dir_read_name( dir ) ) {
175 if( name[0] == '.' )
176 continue;
177 char* full = g_build_filename( path, name, NULL );
178 if( g_file_test( full, G_FILE_TEST_IS_DIR) ) {
179 char* disp = g_filename_display_name( name );
180 GtkWidget *dummy;
181 item = gtk_image_menu_item_new_with_label( disp );
182 g_free( disp );
239cb032 183 g_object_set_qdata_full( item, NAME_ID, g_strdup(name), g_free );
f9dfb60b
HJYP
184 gtk_image_menu_item_set_image( (GtkImageMenuItem*)item,
185 gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU) );
186 dummy = gtk_menu_new();
187 gtk_menu_item_set_submenu( item, dummy );
188 gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
239cb032
HJYP
189 g_signal_connect( item, "select",
190 G_CALLBACK(on_select), p );
191#if GTK_CHECK_VERSION(2, 10, 0)
192 /* NOTE: It seems that this doesn't work in older
193 versions of gtk+?? */
194 g_signal_connect( item, "deselect",
195 G_CALLBACK(on_deselect), p);
196#endif
f9dfb60b
HJYP
197 }
198 g_free( full );
199 }
200 g_dir_close( dir );
201 }
202
239cb032
HJYP
203 item = gtk_image_menu_item_new_from_stock( GTK_STOCK_OPEN, NULL );
204 g_signal_connect( item, "activate",
205 G_CALLBACK( on_open_dir ), p );
f9dfb60b 206 term = gtk_menu_item_new_with_mnemonic( _("Open in _Terminal") );
239cb032
HJYP
207 g_signal_connect( term, "activate",
208 G_CALLBACK( on_open_in_term ), p );
209
f9dfb60b
HJYP
210 if( open_at_top ) {
211 gtk_menu_shell_insert( GTK_MENU_SHELL(menu), gtk_separator_menu_item_new(), 0 );
212 gtk_menu_shell_insert( GTK_MENU_SHELL(menu), term, 0 );
213 gtk_menu_shell_insert( GTK_MENU_SHELL(menu), item, 0 );
214 }
215 else {
216 gtk_menu_shell_append( GTK_MENU_SHELL(menu), gtk_separator_menu_item_new() );
217 gtk_menu_shell_append( GTK_MENU_SHELL(menu), term );
218 gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
219 }
220
221 gtk_widget_show_all( menu );
222 return menu;
223}
224
22242ed4 225static void show_menu( GtkWidget* widget, Plugin *p, int btn, guint32 time )
f9dfb60b
HJYP
226{
227 dirmenu *dm = (dirmenu *)p->priv;
228 char* path = dm->path ? expand_tilda(dm->path) : NULL;
239cb032
HJYP
229 GtkWidget* menu = create_menu( p,
230 path ? path : g_get_home_dir(),
231 FALSE );
f9dfb60b
HJYP
232 g_free( path );
233
ab77df37 234 g_signal_connect( menu, "selection-done", G_CALLBACK(on_sel_done), NULL );
f9dfb60b
HJYP
235 gtk_menu_popup( GTK_MENU(menu),
236 NULL, NULL,
237 (GtkMenuPositionFunc)menu_pos, p,
238 btn, time);
239}
240
241static gint
242clicked (GtkWidget *widget, GdkEventButton *event, gpointer data)
243{
22242ed4 244 Plugin *p = (Plugin*)data;
f9dfb60b
HJYP
245 dirmenu *dm = (dirmenu *)p->priv;
246
247 ENTER;
248 if (event->type != GDK_BUTTON_PRESS)
249 RET(FALSE);
250
251 if (event->button == 1) {
252 show_menu( widget, p, event->button, event->time );
253 } else {
239cb032
HJYP
254 char* path = dm->path ? expand_tilda( dm->path ) : NULL;
255 const char* ppath = path ? path : g_get_home_dir();
256 if( event->button == 2 )
257 open_in_term( p, ppath );
258 else
259 open_dir( p, ppath );
260 g_free( path );
f9dfb60b
HJYP
261 }
262
263 RET(FALSE);
264}
265
266static void
22242ed4 267dirmenu_destructor(Plugin *p)
f9dfb60b
HJYP
268{
269 dirmenu *dm = (dirmenu *)p->priv;
270 ENTER;
271 g_free( dm->image );
272 g_free( dm->path );
f9dfb60b
HJYP
273 g_free(dm);
274 RET();
275}
276
277static int
22242ed4 278dirmenu_constructor(Plugin *p, char **fp)
f9dfb60b
HJYP
279{
280 line s;
281 gchar *fname;
282 dirmenu *dm;
283 int w, h;
284
285 ENTER;
286 s.len = 256;
287 dm = g_new0(dirmenu, 1);
288 g_return_val_if_fail(dm != NULL, 0);
239cb032
HJYP
289
290 dm->panel = p->panel;
f9dfb60b
HJYP
291 p->priv = dm;
292 fname = NULL;
293 if( fp )
294 {
295 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
296 if (s.type == LINE_NONE) {
297 ERR( "dirmenu: illegal token %s\n", s.str);
298 goto error;
299 }
300 if (s.type == LINE_VAR) {
301 if (!g_ascii_strcasecmp(s.t[0], "image")) {
302 dm->image = g_strdup( s.t[1] );
303 fname = expand_tilda(s.t[1]);
304 }
f9dfb60b
HJYP
305 else if (!g_ascii_strcasecmp(s.t[0], "path")) {
306 dm->path = g_strdup( s.t[1] );
307 }
308 else {
309 ERR( "dirmenu: unknown var %s\n", s.t[0]);
310 goto error;
311 }
312 } else {
313 ERR( "dirmenu: illegal in this context %s\n", s.str);
314 goto error;
315 }
316 }
317 }
318 if (p->panel->orientation == ORIENT_HORIZ) {
319 w = 10000;
320 h = p->panel->ah;
321 } else {
322 w = p->panel->aw;
323 h = 10000;
324 }
325
5034c4c0 326 if (! fname)
239cb032 327 fname = strdup("file-manager");
5034c4c0 328
f9dfb60b 329 dm->button = fb_button_new_from_file(fname, w, h, 0x202020, TRUE);
4542c20d 330
f9dfb60b
HJYP
331 gtk_container_set_border_width( GTK_CONTAINER(dm->button), 0 );
332 g_signal_connect( dm->button, "button_press_event",
333 G_CALLBACK(clicked), p );
334
335 gtk_widget_show( dm->button );
f9dfb60b 336 g_free(fname);
239cb032
HJYP
337
338 fname = dm->path ? expand_tilda(dm->path) : NULL;
339 gtk_tooltips_set_tip(GTK_TOOLTIPS (dm->panel->tooltips),
340 dm->button,
341 fname ? fname : g_get_home_dir(), NULL);
342 g_free( fname );
343
4542c20d
HJYP
344 /* store the created plugin widget in plugin->pwid */
345 p->pwid = dm->button;
346
f9dfb60b
HJYP
347 RET(1);
348
349 error:
350 g_free(fname);
351 dirmenu_destructor(p);
352 ERR( "%s - exit\n", __FUNCTION__);
353 RET(0);
354}
355
22242ed4 356static void save_config( Plugin* p, FILE* fp )
f9dfb60b
HJYP
357{
358 dirmenu* dm = (dirmenu*)p->priv;
359 lxpanel_put_str( fp, "path", dm->path );
360 lxpanel_put_str( fp, "image", dm->image );
f9dfb60b
HJYP
361}
362
22242ed4 363PluginClass dirmenu_plugin_class = {
f9dfb60b
HJYP
364 fname: NULL,
365 count: 0,
366
367 type : "dirmenu",
368 name : N_("Directory Menu"),
369 version: "1.0",
370 description : N_("Browse directory tree via menu (Author: PCMan)"),
371
372 constructor : dirmenu_constructor,
373 destructor : dirmenu_destructor,
374 config : NULL,
375 save : save_config
376};