2
* fm-desktop.c: Desktop integration for PCManFM
4
* Copyright (c) 2004 Brian Tarricone, <bjt23@cornell.edu>
5
* Copyright (c) 2006 Hong Jen Yee (PCMan), <pcman.tw@gmail.com>
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU Library General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
* Random portions taken from or inspired by the original xfdesktop for xfce4:
22
* Copyright (C) 2002-2003 Jasper Huijsmans (huysmans@users.sourceforge.net)
23
* Copyright (C) 2003 Benedikt Meurer <benedikt.meurer@unix-ag.uni-siegen.de>
24
* X event forwarding code:
25
* Copyright (c) 2004 Nils Rennebarth
27
* This file is modified form xfdesktop.c of XFCE 4.4
28
* to be used in PCMan File Manager.
40
#include <X11/Xatom.h>
44
#include <gdk/gdkkeysyms.h>
47
#include "fm-desktop.h"
48
#include "working-area.h"
49
#include "ptk-file-browser.h"
50
#include "ptk-icon-view.h"
51
#include "main-window.h"
54
static GtkWidget **desktops;
55
static gint n_screens;
56
static guint busy_cursor = 0;
58
static void resize_desktops();
59
static GdkPixmap* set_bg_pixmap( GdkScreen* screen,
61
GdkRectangle* working_area );
64
event_forward_to_rootwin( GdkScreen *gscreen, GdkEvent *event )
66
XButtonEvent xev, xev2;
67
Display *dpy = GDK_DISPLAY_XDISPLAY( gdk_screen_get_display( gscreen ) );
69
if ( event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE )
71
if ( event->type == GDK_BUTTON_PRESS )
73
xev.type = ButtonPress;
75
* rox has an option to disable the next
76
* instruction. it is called "blackbox_hack". Does
77
* anyone know why exactly it is needed?
79
XUngrabPointer( dpy, event->button.time );
82
xev.type = ButtonRelease;
84
xev.button = event->button.button;
85
xev.x = event->button.x; /* Needed for icewm */
86
xev.y = event->button.y;
87
xev.x_root = event->button.x_root;
88
xev.y_root = event->button.y_root;
89
xev.state = event->button.state;
93
else if ( event->type == GDK_SCROLL )
95
xev.type = ButtonPress;
96
xev.button = event->scroll.direction + 4;
97
xev.x = event->scroll.x; /* Needed for icewm */
98
xev.y = event->scroll.y;
99
xev.x_root = event->scroll.x_root;
100
xev.y_root = event->scroll.y_root;
101
xev.state = event->scroll.state;
103
xev2.type = ButtonRelease;
104
xev2.button = xev.button;
108
xev.window = GDK_WINDOW_XWINDOW( gdk_screen_get_root_window( gscreen ) );
109
xev.root = xev.window;
110
xev.subwindow = None;
111
xev.time = event->button.time;
112
xev.same_screen = True;
114
XSendEvent( dpy, xev.window, False, ButtonPressMask | ButtonReleaseMask,
115
( XEvent * ) & xev );
116
if ( xev2.type == 0 )
119
/* send button release for scroll event */
120
xev2.window = xev.window;
121
xev2.root = xev.root;
122
xev2.subwindow = xev.subwindow;
123
xev2.time = xev.time;
126
xev2.x_root = xev.x_root;
127
xev2.y_root = xev.y_root;
128
xev2.state = xev.state;
129
xev2.same_screen = xev.same_screen;
131
XSendEvent( dpy, xev2.window, False, ButtonPressMask | ButtonReleaseMask,
132
( XEvent * ) & xev2 );
136
scroll_cb( GtkWidget *w, GdkEventScroll *evt, gpointer user_data )
138
event_forward_to_rootwin( gtk_widget_get_screen( w ), ( GdkEvent* ) evt );
143
button_cb( GtkWidget *w, GdkEventButton *evt, gpointer user_data )
145
GdkScreen * gscreen = gtk_widget_get_screen( w );
146
gint button = evt->button;
147
gint state = evt->state;
149
if ( evt->type == GDK_BUTTON_PRESS )
151
if ( button == 2 || ( button == 1 && ( state & GDK_SHIFT_MASK )
152
&& ( state & GDK_CONTROL_MASK ) ) )
156
else if ( button == 3 || ( button == 1 && ( state & GDK_SHIFT_MASK ) ) )
158
// popup_desktop_menu(gscreen, button, evt->time);
167
reload_idle_cb( gpointer data )
174
client_message_received( GtkWidget *w, GdkEventClient *evt, gpointer user_data )
176
if ( evt->data_format == 8 )
178
if ( !strcmp( RELOAD_MESSAGE, evt->data.b ) )
180
g_idle_add ( ( GSourceFunc ) reload_idle_cb, NULL );
183
else if ( !strcmp( MENU_MESSAGE, evt->data.b ) )
185
popup_desktop_menu( gtk_widget_get_screen( w ), 0, GDK_CURRENT_TIME );
188
else if ( !strcmp( WINDOWLIST_MESSAGE, evt->data.b ) )
190
popup_windowlist( gtk_widget_get_screen( w ), 0, GDK_CURRENT_TIME );
193
else if ( !strcmp( QUIT_MESSAGE, evt->data.b ) )
206
sighandler_cb( int sig )
211
g_idle_add ( ( GSourceFunc ) reload_idle_cb, NULL );
220
GdkFilterReturn on_rootwin_event ( GdkXEvent *xevent,
224
XPropertyEvent * evt = ( XPropertyEvent* ) xevent;
225
/* If the size of working area changed */
226
/* g_debug("%s\n", gdk_x11_get_xatom_name(evt->atom)); */
227
if ( evt->type == PropertyNotify &&
228
evt->atom == XInternAtom( evt->display,
229
"_NET_WORKAREA", False ) )
233
return GDK_FILTER_TRANSLATE;
236
int fm_desktop_init()
241
GtkSettings *settings;
242
const gchar *message = NULL;
243
gboolean already_running;
245
gdpy = gdk_display_get_default();
247
n_screens = gdk_display_get_n_screens( gdpy );
248
desktops = g_new( GtkWidget *, n_screens );
249
for ( i = 0; i < n_screens; i++ )
251
desktops[ i ] = fm_desktop_new( gdk_display_get_screen( gdpy, i ) );
252
gtk_widget_add_events( desktops[ i ],
253
GDK_BUTTON_PRESS_MASK |
254
GDK_BUTTON_RELEASE_MASK |
256
g_signal_connect( G_OBJECT( desktops[ i ] ), "scroll-event",
257
G_CALLBACK( scroll_cb ), NULL );
258
g_signal_connect_after( G_OBJECT( desktops[ i ] ),
259
"button-press-event",
260
G_CALLBACK( button_cb ), NULL );
261
gtk_widget_show_all( desktops[ i ] );
262
gdk_window_lower( desktops[ i ] ->window );
265
signal( SIGPIPE, SIG_IGN );
267
signal( SIGHUP, sighandler_cb );
268
signal( SIGINT, sighandler_cb );
269
signal( SIGTERM, sighandler_cb );
270
signal( SIGUSR1, sighandler_cb );
275
void fm_desktop_cleanup()
278
for ( i = 0; i < n_screens; i++ )
279
gtk_widget_destroy( desktops[ i ] );
281
if ( busy_cursor > 0 )
282
g_source_remove( busy_cursor );
285
static gboolean remove_busy_cursor()
288
for ( i = 0; i < n_screens; i++ )
289
gdk_window_set_cursor ( desktops[ i ] ->window, NULL );
294
static void show_busy_cursor()
296
if ( busy_cursor > 0 )
297
g_source_remove( busy_cursor );
301
for ( i = 0; i < n_screens; i++ )
304
cursor = gdk_cursor_new( GDK_WATCH );
305
gdk_window_set_cursor ( desktops[ i ] ->window, cursor );
306
gdk_cursor_unref( cursor );
309
busy_cursor = g_timeout_add( 1000,
310
remove_busy_cursor, NULL );
313
static void on_open_item ( PtkFileBrowser* file_browser,
315
PtkOpenAction action,
318
FMMainWindow * main_window = NULL;
322
case PTK_OPEN_NEW_TAB:
323
main_window = fm_main_window_get_last_active();
325
case PTK_OPEN_NEW_WINDOW:
327
main_window = FM_MAIN_WINDOW(fm_main_window_new());
328
gtk_window_set_default_size( GTK_WINDOW( main_window ),
330
appSettings.height );
331
fm_main_window_add_new_tab( main_window, path,
332
appSettings.showSidePane,
333
appSettings.sidePaneMode );
334
gtk_window_present ( GTK_WINDOW( main_window ) );
336
case PTK_OPEN_TERMINAL:
337
fm_main_window_open_terminal( GTK_WINDOW( user_data ), path );
341
static void remove_filter( GObject* desktop, gpointer screen )
343
gdk_window_remove_filter(
344
gdk_screen_get_root_window( GDK_SCREEN( screen ) ),
345
on_rootwin_event, desktop );
348
static gboolean on_desktop_key_press( GtkWidget* desktop,
352
if( evt->keyval == GDK_F4 )
354
PtkFileBrowser* browser = (PtkFileBrowser*)user_data;
355
fm_main_window_open_terminal( GTK_WINDOW(desktop),
356
ptk_file_browser_get_cwd( browser ));
362
static gboolean on_desktop_expose( GtkWidget* desktop,
363
GdkEventExpose* event,
366
GtkWidget * align, *browser, *view;
368
align = gtk_bin_get_child( GTK_BIN( desktop ) );
369
browser = gtk_bin_get_child( GTK_BIN( align ) );
370
view = ptk_file_browser_get_folder_view( PTK_FILE_BROWSER(browser) );
371
pix = ptk_icon_view_get_background( PTK_ICON_VIEW(view) );
374
gdk_draw_drawable( event->window,
375
desktop->style->fg_gc[ GTK_STATE_NORMAL ],
376
pix, event->area.x, event->area.y,
377
event->area.x, event->area.y,
378
event->area.width, event->area.height );
384
static gboolean on_icon_view_button_press( PtkIconView* view,
388
GtkTreePath * tree_path;
389
GtkWidget* popup = NULL;
390
tree_path = ptk_icon_view_get_path_at_pos( view, evt->x, evt->y );
393
/* TODO: Do some special processig on mounted devices. */
394
gtk_tree_path_free( tree_path );
396
else /* pressed on desktop */
398
if( evt->button == 3 ) /* right button */
400
/* g_debug("no item, btn = %d", evt->button); */
402
popup = gtk_menu_new();
403
GtkWidget* tmp = gtk_menu_item_new_with_label("Test");
404
gtk_menu_shell_append( popup, tmp );
405
gtk_widget_show_all( popup );
411
g_signal_connect( popup, "selection-done",
412
G_CALLBACK(gtk_widget_destroy), NULL );
413
gtk_menu_popup( GTK_MENU(popup), NULL, NULL, NULL, NULL, evt->button, evt->time );
414
g_signal_stop_emission_by_name( view, "button-press-event" );
420
GtkWidget* fm_desktop_new( GdkScreen* screen )
423
PtkFileBrowser *browser;
425
GtkScrolledWindow *scroll;
427
GtkWidget* alignment;
433
desktop = gtk_window_new( GTK_WINDOW_TOPLEVEL );
434
get_working_area( screen, &area );
435
gtk_window_move( GTK_WINDOW( desktop ), 0, 0 );
436
gtk_widget_set_size_request( desktop,
437
gdk_screen_get_width( screen ),
438
gdk_screen_get_height( screen ) );
440
alignment = gtk_alignment_new( 0.5, 0.5, 1, 1 );
441
gtk_container_add( GTK_CONTAINER(desktop), alignment );
443
browser = PTK_FILE_BROWSER(ptk_file_browser_new( alignment, FBVM_ICON_VIEW ));
445
g_signal_connect( browser, "open-item",
446
G_CALLBACK( on_open_item ), desktop );
448
ptk_file_browser_show_hidden_files( browser,
449
appSettings.showHiddenFiles );
450
ptk_file_browser_show_thumbnails( browser,
451
appSettings.showThumbnail ? appSettings.maxThumbSize : 0 );
452
desktop_dir = g_build_filename( g_get_home_dir(), "Desktop", NULL );
453
/* FIXME: add histroy should be true because
454
current working directory is stored in history list. */
455
ptk_file_browser_chdir( browser, desktop_dir, TRUE );
456
g_free( desktop_dir );
458
gtk_widget_set_size_request( GTK_WIDGET(browser),
459
area.width, area.height );
460
rpad = gdk_screen_get_width( screen );
461
rpad -= ( area.x + area.width );
462
bpad = gdk_screen_get_height( screen );
463
bpad -= ( area.y + area.height );
464
gtk_alignment_set_padding( GTK_ALIGNMENT(alignment),
465
area.y, bpad, area.x, rpad );
466
gtk_container_add( GTK_CONTAINER(alignment), GTK_WIDGET(browser) );
468
gtk_widget_show_all( alignment );
469
gtk_widget_realize( desktop );
470
gdk_window_set_type_hint( desktop->window,
471
GDK_WINDOW_TYPE_HINT_DESKTOP );
473
view = ptk_file_browser_get_folder_view( browser );
474
g_signal_connect( view, "button-press-event",
475
G_CALLBACK( on_icon_view_button_press ), desktop );
476
scroll = GTK_SCROLLED_WINDOW( gtk_widget_get_parent( view ) );
477
gtk_scrolled_window_set_policy( scroll,
478
GTK_POLICY_NEVER, GTK_POLICY_NEVER );
479
gtk_scrolled_window_set_shadow_type( scroll, GTK_SHADOW_NONE );
481
root = gdk_screen_get_root_window( screen );
482
gdk_window_set_events( root, gdk_window_get_events( root )
483
| GDK_PROPERTY_CHANGE_MASK );
484
gdk_window_add_filter( root, on_rootwin_event, desktop );
485
g_signal_connect( desktop, "destroy", remove_filter, screen );
486
g_signal_connect( desktop, "expose-event",
487
on_desktop_expose, NULL );
488
g_signal_connect( desktop, "key-press-event",
489
on_desktop_key_press, browser );
491
gtk_widget_set_double_buffered( desktop, FALSE );
492
set_bg_pixmap( screen, PTK_ICON_VIEW(view), &area );
493
gtk_window_set_skip_pager_hint( GTK_WINDOW(desktop), TRUE );
494
gtk_window_set_skip_taskbar_hint( GTK_WINDOW(desktop), TRUE );
496
/* This is borrowed from fbpanel */
497
#define WIN_HINTS_SKIP_FOCUS (1<<0) /* skip "alt-tab" */
498
val = WIN_HINTS_SKIP_FOCUS;
499
XChangeProperty(GDK_DISPLAY(), GDK_WINDOW_XWINDOW(desktop->window),
500
XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
501
PropModeReplace, (unsigned char *) &val, 1);
503
gdk_rgb_find_color( gtk_widget_get_colormap( view ),
504
&appSettings.desktopText );
505
gdk_rgb_find_color( gtk_widget_get_colormap( view ),
506
&appSettings.desktopBg1 );
507
gdk_rgb_find_color( gtk_widget_get_colormap( view ),
508
&appSettings.desktopBg2 );
510
gtk_widget_modify_text( view,
512
&appSettings.desktopText );
513
gtk_widget_modify_base( view,
515
&appSettings.desktopBg1 );
516
gdk_window_set_background( root, &appSettings.desktopBg1 );
520
GdkPixmap* set_bg_pixmap( GdkScreen* screen,
522
GdkRectangle* working_area )
525
GdkPixmap* pix = NULL;
528
root = gdk_screen_get_root_window( screen );
529
if ( appSettings.showWallpaper )
531
int screenw, screenh;
532
int imgw, imgh, x = 0, y = 0;
533
screenw = gdk_screen_get_width( screen );
534
screenh = gdk_screen_get_height( screen );
535
img = gdk_pixbuf_new_from_file_at_scale(
536
appSettings.wallpaper,
542
pix = gdk_pixmap_new( root, screenw, screenh, -1 );
543
imgw = gdk_pixbuf_get_width( img );
544
imgh = gdk_pixbuf_get_height( img );
545
if ( imgw == screenw )
547
/* center vertically */
548
y = ( screenh - imgh ) / 2;
552
/* center horizontally */
553
x = ( screenw - imgw ) / 2;
555
/* FIXME: fill the blank area with bg color.*/
556
gc = gdk_gc_new( pix );
557
gdk_gc_set_rgb_fg_color( gc, &appSettings.desktopBg1 );
558
gdk_gc_set_fill( gc, GDK_SOLID );
559
/* fill the whole pixmap is not efficient at all!!! */
560
gdk_draw_rectangle( pix, gc, TRUE,
561
0, 0, screenw, screenh );
562
g_object_unref( G_OBJECT( gc ) );
563
gdk_draw_pixbuf( pix, NULL, img, 0, 0, x, y,
565
GDK_RGB_DITHER_NONE, 0, 0 );
566
gdk_pixbuf_unref( img );
570
ptk_icon_view_set_background( PTK_ICON_VIEW( view ), pix,
571
working_area->x, working_area->y );
572
gdk_window_set_back_pixmap( root, pix, FALSE );
574
g_object_unref( G_OBJECT( pix ) );
576
gdk_window_clear( root );
580
void resize_desktops()
584
GdkDisplay* display = gdk_display_get_default();
587
for ( i = 0; i < n_screens; ++i )
589
GtkWidget *view, *alignment, *browser;
593
screen = gdk_display_get_screen( display, i );
594
get_working_area( screen, &area );
595
gtk_window_move( GTK_WINDOW( desktops[ i ] ), 0, 0 );
596
gtk_widget_set_size_request( desktops[ i ],
597
gdk_screen_get_width( screen ),
598
gdk_screen_get_height( screen ) );
599
alignment = gtk_bin_get_child( GTK_BIN( desktops[ i ] ) );
600
browser = gtk_bin_get_child( GTK_BIN( alignment ) );
601
view = ptk_file_browser_get_folder_view( PTK_FILE_BROWSER( browser ) );
602
gtk_widget_set_size_request( browser,
603
area.width, area.height );
604
rpad = gdk_screen_get_width( screen );
605
rpad -= ( area.x + area.width );
606
bpad = gdk_screen_get_height( screen );
607
bpad -= ( area.y + area.height );
608
gtk_alignment_set_padding( GTK_ALIGNMENT(alignment),
609
area.y, bpad, area.x, rpad );
611
set_bg_pixmap( screen, PTK_ICON_VIEW(view), &area );
615
void fm_desktop_update_wallpaper()
620
void fm_desktop_update_colors()
623
for ( i = 0; i < n_screens; ++i )
626
GtkWidget *view, *alignment, *browser;
627
screen = gdk_display_get_screen( gdk_display_get_default(), i );
628
alignment = gtk_bin_get_child( GTK_BIN( desktops[ i ] ) );
629
browser = gtk_bin_get_child( GTK_BIN( alignment ) );
630
view = ptk_file_browser_get_folder_view( PTK_FILE_BROWSER( browser ) );
631
gdk_rgb_find_color( gtk_widget_get_colormap( view ),
632
&appSettings.desktopText );
633
gdk_rgb_find_color( gtk_widget_get_colormap( view ),
634
&appSettings.desktopBg1 );
635
gdk_rgb_find_color( gtk_widget_get_colormap( view ),
636
&appSettings.desktopBg2 );
637
gdk_window_set_background(
638
gdk_screen_get_root_window( screen ),
639
&appSettings.desktopBg1 );
640
gtk_widget_modify_text( view,
642
&appSettings.desktopText );
643
gtk_widget_modify_base( view,
645
&appSettings.desktopBg1 );
647
if ( appSettings.showWallpaper )
648
fm_desktop_update_wallpaper();
651
static PtkFileBrowser* fm_desktop_get_browser( GtkWidget* desktop )
654
align = gtk_bin_get_child( ( GtkBin* ) desktop );
655
return ( PtkFileBrowser* ) gtk_bin_get_child( ( GtkBin* ) align );
658
void fm_desktop_update_thumbnails()
661
PtkFileBrowser* browser;
662
for ( i = 0; i < n_screens; ++i )
664
browser = fm_desktop_get_browser( desktops[ i ] );
665
ptk_file_browser_show_thumbnails( browser,
666
appSettings.showThumbnail ? appSettings.maxThumbSize : 0 );
670
void fm_desktop_update_view()
673
PtkFileBrowser* browser;
674
for ( i = 0; i < n_screens; ++i )
676
browser = fm_desktop_get_browser( desktops[ i ] );
677
ptk_file_browser_update_display( browser );