* Fix drawing bugs in desktop window.
[lxde/pcmanfm.git] / src / desktop.c
CommitLineData
8505a8ef
HJYP
1/*
2 * desktop.c
3 *
4 * Copyright 2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22#include "desktop.h"
f8f2bfad 23#include "pcmanfm.h"
8505a8ef
HJYP
24#include "app-config.h"
25
26#include <gdk/gdkx.h>
27#include <X11/Xlib.h>
28#include <X11/Xatom.h>
29
30#define SPACING 2
31#define PADDING 6
83418aeb 32#define MARGIN 2
8505a8ef
HJYP
33
34struct _FmDesktopItem
35{
36 GtkTreeIter it;
37 FmFileInfo* fi;
38 GdkPixbuf* icon;
495bd587
HJYP
39 int x; /* position of the item on the desktop */
40 int y;
8505a8ef
HJYP
41 GdkRectangle icon_rect;
42 GdkRectangle text_rect;
495bd587
HJYP
43 gboolean is_special : 1; /* is this a special item like "My Computer", mounted volume, or "Trash" */
44 gboolean is_mount : 1; /* is this a mounted volume*/
8505a8ef
HJYP
45 gboolean is_selected : 1;
46 gboolean is_prelight : 1;
47 gboolean custom_pos : 1;
48};
49
f8f2bfad
HJYP
50/* static void fm_desktop_finalize (GObject *object); */
51static void fm_desktop_destroy (GtkObject *object);
8505a8ef
HJYP
52
53static FmDesktopItem* hit_test(FmDesktop* self, int x, int y);
495bd587 54static void calc_item_size(FmDesktop* desktop, FmDesktopItem* item);
8505a8ef
HJYP
55static void layout_items(FmDesktop* self);
56static void queue_layout_items(FmDesktop* desktop);
57static void paint_item(FmDesktop* self, FmDesktopItem* item, cairo_t* cr, GdkRectangle* expose_area);
58static void redraw_item(FmDesktop* desktop, FmDesktopItem* item);
59static void calc_rubber_banding_rect(FmDesktop* self, int x, int y, GdkRectangle* rect);
60static void update_rubberbanding(FmDesktop* self, int newx, int newy );
61static void paint_rubber_banding_rect(FmDesktop* self, cairo_t* cr, GdkRectangle* expose_area);
62static void update_background(FmDesktop* desktop);
63static void update_working_area(FmDesktop* desktop);
495bd587
HJYP
64static GList* get_selected_items(FmDesktop* desktop, int* n_items);
65static void activate_selected_items(FmDesktop* desktop);
8505a8ef 66
f8f2bfad 67static FmDesktopItem* desktop_item_new(GtkTreeIter* it);
8505a8ef
HJYP
68static void desktop_item_free(FmDesktopItem* item);
69
70static gboolean on_expose( GtkWidget* w, GdkEventExpose* evt );
71static void on_size_allocate( GtkWidget* w, GtkAllocation* alloc );
72static void on_size_request( GtkWidget* w, GtkRequisition* req );
73static gboolean on_button_press( GtkWidget* w, GdkEventButton* evt );
74static gboolean on_button_release( GtkWidget* w, GdkEventButton* evt );
75static gboolean on_motion( GtkWidget* w, GdkEventMotion* evt );
76static gboolean on_key_press( GtkWidget* w, GdkEventKey* evt );
77static void on_style_set( GtkWidget* w, GtkStyle* prev );
78static void on_realize( GtkWidget* w );
79static gboolean on_focus_in( GtkWidget* w, GdkEventFocus* evt );
80static gboolean on_focus_out( GtkWidget* w, GdkEventFocus* evt );
495bd587 81static void on_drag_leave(GtkWidget* w, GdkDragContext* drag_ctx, guint time);
8505a8ef 82
f970e846
HJYP
83static void on_wallpaper_changed(FmConfig* cfg, gpointer user_data);
84
8505a8ef
HJYP
85static void on_row_inserted(GtkTreeModel* mod, GtkTreePath* tp, GtkTreeIter* it, FmDesktop* desktop);
86static void on_row_deleted(GtkTreeModel* mod, GtkTreePath* tp, FmDesktop* desktop);
87static void on_row_changed(GtkTreeModel* mod, GtkTreePath* tp, GtkTreeIter* it, FmDesktop* desktop);
88
f8f2bfad
HJYP
89static void on_dnd_src_data_get(FmDndSrc* ds, FmDesktop* desktop);
90static gboolean on_dnd_dest_query_info(FmDndDest* dd, int x, int y,
91 GdkDragAction* action, FmDesktop* desktop);
92static void on_dnd_dest_files_dropped(FmDndDest* dd, GdkDragAction action,
93 int info_type, FmList* files, FmDesktop* desktop);
94
95
8505a8ef
HJYP
96static GdkFilterReturn on_root_event(GdkXEvent *xevent, GdkEvent *event, gpointer data);
97static void on_screen_size_changed(GdkScreen* screen, FmDesktop* desktop);
98
99G_DEFINE_TYPE(FmDesktop, fm_desktop, GTK_TYPE_WINDOW);
100
101static GtkWindowGroup* win_group = NULL;
102static GtkWidget **desktops = NULL;
103static gint n_screens = 0;
f970e846 104static guint wallpaper_changed = 0;
8505a8ef
HJYP
105
106static FmFolderModel* model = NULL;
107
108static Atom XA_NET_WORKAREA = 0;
109static Atom XA_NET_NUMBER_OF_DESKTOPS = 0;
110static Atom XA_NET_CURRENT_DESKTOP = 0;
111static Atom XA_XROOTMAP_ID= 0;
112
f8f2bfad
HJYP
113enum {
114 FM_DND_DEST_DESKTOP_ITEM = N_FM_DND_DEST_DEFAULT_TARGETS + 1
115};
116
117GtkTargetEntry dnd_targets[] =
118{
119 {"application/x-desktop-item", GTK_TARGET_SAME_WIDGET, FM_DND_DEST_DESKTOP_ITEM}
120};
121
122
8505a8ef
HJYP
123static void fm_desktop_class_init(FmDesktopClass *klass)
124{
125 GObjectClass *g_object_class;
f8f2bfad 126 GtkObjectClass *gtk_object_class;
8505a8ef
HJYP
127 GtkWidgetClass* wc;
128 typedef gboolean (*DeleteEvtHandler) (GtkWidget*, GdkEvent*);
129 const char* atom_names[] = {"_NET_WORKAREA", "_NET_NUMBER_OF_DESKTOPS", "_NET_CURRENT_DESKTOP", "_XROOTMAP_ID"};
130 Atom atoms[G_N_ELEMENTS(atom_names)] = {0};
131
f8f2bfad
HJYP
132 /* g_object_class = G_OBJECT_CLASS(klass);
133 g_object_class->finalize = fm_desktop_finalize; */
134 gtk_object_class = GTK_OBJECT_CLASS(klass);
135 gtk_object_class->destroy = fm_desktop_destroy;
8505a8ef
HJYP
136
137 wc = GTK_WIDGET_CLASS(klass);
138 wc->expose_event = on_expose;
139 wc->size_allocate = on_size_allocate;
140 wc->size_request = on_size_request;
141 wc->button_press_event = on_button_press;
142 wc->button_release_event = on_button_release;
143 wc->motion_notify_event = on_motion;
144 wc->key_press_event = on_key_press;
145 wc->style_set = on_style_set;
146 wc->realize = on_realize;
147 wc->focus_in_event = on_focus_in;
148 wc->focus_out_event = on_focus_out;
149 /* wc->scroll_event = on_scroll; */
150 wc->delete_event = (DeleteEvtHandler)gtk_true;
495bd587 151 wc->drag_leave = on_drag_leave;
8505a8ef
HJYP
152
153 if(XInternAtoms(GDK_DISPLAY(), atom_names, G_N_ELEMENTS(atom_names), False, atoms))
154 {
155 XA_NET_WORKAREA = atoms[0];
156 XA_NET_NUMBER_OF_DESKTOPS = atoms[1];
157 XA_NET_CURRENT_DESKTOP = atoms[2];
158 XA_XROOTMAP_ID= atoms[3];
159 }
160}
161
162static void desktop_item_free(FmDesktopItem* item)
163{
164 if(item->icon)
165 g_object_unref(item->icon);
166 g_slice_free(FmDesktopItem, item);
167}
168
f8f2bfad 169static void fm_desktop_destroy(GtkObject *object)
8505a8ef
HJYP
170{
171 FmDesktop *self;
172 GdkScreen* screen;
173
8505a8ef
HJYP
174 self = FM_DESKTOP(object);
175 screen = gtk_widget_get_screen((GtkWidget*)self);
176 gdk_window_remove_filter(gdk_screen_get_root_window(screen), on_root_event, self);
177
178 g_list_foreach(self->items, (GFunc)desktop_item_free, NULL);
179 g_list_free(self->items);
f8f2bfad 180 self->items = NULL;
8505a8ef
HJYP
181
182 g_object_unref(self->icon_render);
183 g_object_unref(self->pl);
184
185 g_signal_handlers_disconnect_by_func(model, on_row_inserted, self);
186 g_signal_handlers_disconnect_by_func(model, on_row_deleted, self);
187 g_signal_handlers_disconnect_by_func(model, on_row_changed, self);
188
189 if(self->idle_layout)
190 g_source_remove(self->idle_layout);
191
f8f2bfad 192 G_OBJECT_CLASS(fm_desktop_parent_class)->dispose(object);
8505a8ef
HJYP
193}
194
8505a8ef
HJYP
195static void fm_desktop_init(FmDesktop *self)
196{
197 GdkScreen* screen = gtk_widget_get_screen((GtkWidget*)self);
83418aeb 198 GdkWindow* root;
8505a8ef 199 PangoContext* pc;
f8f2bfad
HJYP
200 GtkTreeIter it;
201 GtkCellRenderer* renderer;
202 GtkTargetList* targets;
8505a8ef
HJYP
203
204 gtk_window_set_default_size((GtkWindow*)self, gdk_screen_get_width(screen), gdk_screen_get_height(screen));
205 gtk_window_move(self, 0, 0);
206 gtk_widget_set_app_paintable((GtkWidget*)self, TRUE);
207 gtk_window_set_type_hint(self, GDK_WINDOW_TYPE_HINT_DESKTOP);
208 gtk_widget_add_events((GtkWidget*)self,
209 GDK_POINTER_MOTION_MASK |
210 GDK_BUTTON_PRESS_MASK |
211 GDK_BUTTON_RELEASE_MASK |
212 GDK_KEY_PRESS_MASK|
213 GDK_PROPERTY_CHANGE_MASK);
214
215 self->icon_render = gtk_cell_renderer_pixbuf_new();
216 g_object_set( self->icon_render, "follow-state", TRUE, NULL);
217 g_object_ref_sink(self->icon_render);
218
219 /* FIXME: call pango_layout_context_changed() on the layout in response to the
220 * "style-set" and "direction-changed" signals for the widget. */
221 pc = gtk_widget_get_pango_context( (GtkWidget*)self );
222 self->pl = gtk_widget_create_pango_layout( (GtkWidget*)self, NULL );
223 pango_layout_set_alignment( self->pl, PANGO_ALIGN_CENTER );
224 pango_layout_set_ellipsize( self->pl, PANGO_ELLIPSIZE_END );
225 pango_layout_set_wrap(self->pl, PANGO_WRAP_WORD_CHAR);
8505a8ef
HJYP
226
227 g_signal_connect(model, "row-inserted", G_CALLBACK(on_row_inserted), self);
228 g_signal_connect(model, "row-deleted", G_CALLBACK(on_row_deleted), self);
229 g_signal_connect(model, "row-changed", G_CALLBACK(on_row_changed), self);
230
83418aeb
HJYP
231 root = gdk_screen_get_root_window(screen);
232 gdk_window_set_events(root, gdk_window_get_events(root)|GDK_PROPERTY_CHANGE_MASK);
233 gdk_window_add_filter(root, on_root_event, self);
8505a8ef 234 g_signal_connect(screen, "size-changed", G_CALLBACK(on_screen_size_changed), self);
f8f2bfad
HJYP
235
236 /* init dnd support */
237 gtk_drag_source_set(self, 0,
238 fm_default_dnd_dest_targets, N_FM_DND_DEST_DEFAULT_TARGETS,
239 GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK|GDK_ACTION_ASK);
240 targets = gtk_drag_source_get_target_list((GtkWidget*)self);
241 /* add our own targets */
242 gtk_target_list_add_table(targets, dnd_targets, G_N_ELEMENTS(dnd_targets));
243 self->dnd_src = fm_dnd_src_new((GtkWidget*)self);
244 g_signal_connect(self->dnd_src, "data-get", G_CALLBACK(on_dnd_src_data_get), self);
245
246 gtk_drag_dest_set(self, 0, NULL, 0,
247 GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK|GDK_ACTION_ASK);
19abb8bf 248 gtk_drag_dest_set_target_list(self, targets);
f8f2bfad
HJYP
249
250 self->dnd_dest = fm_dnd_dest_new((GtkWidget*)self);
251 g_signal_connect(self->dnd_dest, "query-info", G_CALLBACK(on_dnd_dest_query_info), self);
252 g_signal_connect(self->dnd_dest, "files_dropped", G_CALLBACK(on_dnd_dest_files_dropped), self);
253
254 /* add items */
255 if(gtk_tree_model_get_iter_first(model, &it))
256 {
257 do{
258 FmDesktopItem* item = desktop_item_new(&it);
259 self->items = g_list_prepend(self->items, item);
260 }while(gtk_tree_model_iter_next(model, &it));
261 self->items = g_list_reverse(self->items);
262 }
8505a8ef
HJYP
263}
264
265
266GtkWidget *fm_desktop_new(void)
267{
268 return (GtkWidget*)g_object_new(FM_TYPE_DESKTOP, NULL);
269}
270
271
272void fm_desktop_manager_init()
273{
274 GdkDisplay * gdpy;
275 gint i;
276
277 if( ! win_group )
278 win_group = gtk_window_group_new();
279
280 if(!model)
281 {
282 FmFolder* folder = fm_folder_get_for_path(fm_path_get_desktop());
283 if(folder)
284 model = fm_folder_model_new(folder, FALSE);
285 }
286
287 gdpy = gdk_display_get_default();
288 n_screens = gdk_display_get_n_screens(gdpy);
289 desktops = g_new(GtkWidget*, n_screens);
290 for( i = 0; i < n_screens; i++ )
291 {
292 GtkWidget* desktop = fm_desktop_new();
293 desktops[i] = desktop;
294 gtk_widget_realize(desktop); /* without this, setting wallpaper won't work */
295 gtk_widget_show_all(desktop);
296 gdk_window_lower(desktop ->window);
297 gtk_window_group_add_window( GTK_WINDOW_GROUP(win_group), desktop );
298 }
f8f2bfad 299
f970e846
HJYP
300 wallpaper_changed = g_signal_connect(app_config, "changed::wallpaper", G_CALLBACK(on_wallpaper_changed), NULL);
301
f8f2bfad 302 pcmanfm_ref();
8505a8ef
HJYP
303}
304
305void fm_desktop_manager_finalize()
306{
307 int i;
308 for( i = 0; i < n_screens; i++ )
309 {
310 gtk_widget_destroy(desktops[i]);
311 }
312 g_free(desktops);
313 g_object_unref(win_group);
314 win_group = NULL;
315
316 if(model)
317 {
318 g_object_unref(model);
319 model = NULL;
320 }
f8f2bfad 321
f970e846
HJYP
322 g_signal_handler_disconnect(app_config, wallpaper_changed);
323
f8f2bfad 324 pcmanfm_unref();
8505a8ef
HJYP
325}
326
495bd587
HJYP
327void activate_selected_items(FmDesktop* desktop)
328{
329 int n_sels;
330 GList* items = get_selected_items(desktop, &n_sels);
331 GList* l;
332
333 if(!items)
334 return;
335
336 for(l=items;l;l=l->next)
337 {
338 FmDesktopItem* item = (FmDesktopItem*)l->data;
339
340 }
341 g_list_free(items);
342}
8505a8ef
HJYP
343
344gboolean on_button_press( GtkWidget* w, GdkEventButton* evt )
345{
346 FmDesktop* self = (FmDesktop*)w;
347 FmDesktopItem *item, *clicked_item = NULL;
348 GList* l;
349
350 clicked_item = hit_test( FM_DESKTOP(w), (int)evt->x, (int)evt->y );
351
352 if( evt->type == GDK_BUTTON_PRESS )
353 {
354 if( evt->button == 1 ) /* left button */
355 {
356 self->button_pressed = TRUE; /* store button state for drag & drop */
357 self->drag_start_x = evt->x;
358 self->drag_start_y = evt->y;
359 }
360
361 /* if ctrl / shift is not pressed, deselect all. */
362 if( ! (evt->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) )
363 {
364 /* don't cancel selection if clicking on selected items */
365 if( !( (evt->button == 1 || evt->button == 3) && clicked_item && clicked_item->is_selected) )
366 {
367 for( l = self->items; l ;l = l->next )
368 {
369 item = (FmDesktopItem*) l->data;
370 if( item->is_selected )
371 {
372 item->is_selected = FALSE;
373 redraw_item( self, item );
374 }
375 }
376 }
377 }
378
379 if( clicked_item )
380 {
381 if( evt->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK) )
382 clicked_item->is_selected = ! clicked_item->is_selected;
383 else
384 clicked_item->is_selected = TRUE;
385
386 if( self->focus && self->focus != item )
387 {
388 FmDesktopItem* old_focus = self->focus;
389 if( old_focus )
390 redraw_item( FM_DESKTOP(w), old_focus );
391 }
392 self->focus = clicked_item;
393 redraw_item( self, clicked_item );
394
495bd587
HJYP
395 /* left single click */
396 if( evt->button == 1 && fm_config->single_click && clicked_item->is_selected)
8505a8ef 397 {
495bd587
HJYP
398 fm_launch_file(w, NULL, clicked_item->fi);
399 goto out;
400 }
401 if( evt->button == 3 ) /* right click, context menu */
402 {
403 int n_sels;
404 GList* items = get_selected_items(self, &n_sels);
405 if( items )
8505a8ef 406 {
495bd587
HJYP
407 GList* sel;
408
8505a8ef
HJYP
409 }
410 }
8505a8ef
HJYP
411 goto out;
412 }
413 else /* no item is clicked */
414 {
495bd587 415 if( evt->button == 3 ) /* right click on the blank area => desktop popup menu */
8505a8ef
HJYP
416 {
417#if 0
418 if( ! app_settings.show_wm_menu ) /* if our desktop menu is used */
419 {
420 GtkWidget *popup, *sort_by_items[ 4 ], *sort_type_items[ 2 ];
421 int i;
422 /* show the desktop menu */
423 for( i = 0; i < 4; ++i )
424 icon_menu[ i ].ret = &sort_by_items[ i ];
425 for( i = 0; i < 2; ++i )
426 icon_menu[ 5 + i ].ret = &sort_type_items[ i ];
427 popup = ptk_menu_new_from_data( (PtkMenuItemEntry*)&desktop_menu, self, NULL );
428 //gtk_check_menu_item_set_active( (GtkCheckMenuItem*)sort_by_items[ self->sort_by ], TRUE );
429 //gtk_check_menu_item_set_active( (GtkCheckMenuItem*)sort_type_items[ self->sort_type ], TRUE );
430 gtk_widget_show_all(popup);
431 g_signal_connect( popup, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
432
433 gtk_menu_popup( GTK_MENU(popup), NULL, NULL, NULL, NULL, evt->button, evt->time );
434 goto out; /* don't forward the event to root win */
435 }
436#endif
437 }
438 else if( evt->button == 1 )
439 {
440 self->rubber_bending = TRUE;
441
442 /* FIXME: if you foward the event here, this will break rubber bending... */
443 /* forward the event to root window */
444 /* forward_event_to_rootwin( gtk_widget_get_screen(w), evt ); */
445
446 gtk_grab_add( w );
447 self->rubber_bending_x = evt->x;
448 self->rubber_bending_y = evt->y;
449 goto out;
450 }
451 }
452 }
495bd587 453 else if( evt->type == GDK_2BUTTON_PRESS ) /* activate items */
8505a8ef
HJYP
454 {
455 if( clicked_item && evt->button == 1) /* left double click */
456 {
495bd587 457 fm_launch_file(w, NULL, clicked_item->fi);
8505a8ef
HJYP
458 goto out;
459 }
460 }
461 /* forward the event to root window */
462// forward_event_to_rootwin( gtk_widget_get_screen(w), evt );
463
464out:
465 if( ! GTK_WIDGET_HAS_FOCUS(w) )
466 {
467 /* g_debug( "we don't have the focus, grab it!" ); */
468 gtk_widget_grab_focus( w );
469 }
470 return TRUE;
471}
472
473gboolean on_button_release( GtkWidget* w, GdkEventButton* evt )
474{
475 FmDesktop* self = (FmDesktop*)w;
476 FmDesktopItem* clicked_item = hit_test( self, evt->x, evt->y );
477
478 self->button_pressed = FALSE;
479
480 if( self->rubber_bending )
481 {
482 update_rubberbanding( self, evt->x, evt->y );
483 gtk_grab_remove( w );
484 self->rubber_bending = FALSE;
485 }
486 else if( self->dragging )
487 {
488 self->dragging = FALSE;
489 }
490 else if( fm_config->single_click && evt->button == 1 )
491 {
492 if( clicked_item )
493 {
494// open_clicked_item( clicked_item );
495 return TRUE;
496 }
497 }
498
499 /* forward the event to root window */
500// if( ! clicked_item )
501// forward_event_to_rootwin( gtk_widget_get_screen(w), evt );
502
503 return TRUE;
504}
505
506static gboolean on_single_click_timeout( FmDesktop* self )
507{
508 GtkWidget* w = (GtkWidget*)self;
509 GdkEventButton evt;
510 int x, y;
511#if 0
512 /* generate a fake button press */
513 /* FIXME: will this cause any problem? */
514 evt.type = GDK_BUTTON_PRESS;
515 evt.window = w->window;
516 gdk_window_get_pointer( w->window, &x, &y, &evt.state );
517 evt.x = x;
518 evt.y = y;
519 evt.state |= GDK_BUTTON_PRESS_MASK;
520 evt.state &= ~GDK_BUTTON_MOTION_MASK;
521 on_button_press( GTK_WIDGET(self), &evt );
522#endif
523
524 return FALSE;
525}
526
527gboolean on_motion( GtkWidget* w, GdkEventMotion* evt )
528{
529 FmDesktop* self = (FmDesktop*)w;
530 if( ! self->button_pressed )
531 {
532#if 0
533 if( self->single_click )
534 {
535 FmDesktopItem* item = hit_test( self, evt->x, evt->y );
536 if( item != self->hover_item )
537 {
538 if( 0 != self->single_click_timeout_handler )
539 {
540 g_source_remove( self->single_click_timeout_handler );
541 self->single_click_timeout_handler = 0;
542 }
543 }
544 if( item )
545 {
546 gdk_window_set_cursor( w->window, self->hand_cursor );
547 /* FIXME: timeout should be customizable */
548 if( self->single_click_timeout_handler == 0)
549 self->single_click_timeout_handler = g_timeout_add( -1, on_single_click_timeout, self ); //400 ms
550 /* Making a loop to aviod the selection of the item */
551 /* on_single_click_timeout( self ); */
552 }
553 else
554 {
555 gdk_window_set_cursor( w->window, NULL );
556 }
557 self->hover_item = item;
558 }
559#endif
560 return TRUE;
561 }
562
563 if( self->dragging )
564 {
565 }
566 else if( self->rubber_bending )
567 {
568 update_rubberbanding( self, evt->x, evt->y );
569 }
570 else
571 {
572 if ( gtk_drag_check_threshold( w,
573 self->drag_start_x,
574 self->drag_start_y,
575 evt->x, evt->y))
576 {
19abb8bf 577 FmFileInfoList* files = fm_desktop_get_selected_files(self);
8505a8ef 578 GtkTargetList* target_list;
19abb8bf 579 if(files)
8505a8ef 580 {
19abb8bf
HJYP
581 self->dragging = TRUE;
582 target_list = gtk_drag_source_get_target_list(w);
583 gtk_drag_begin( w, target_list,
584 GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK,
585 1, evt );
8505a8ef 586 }
8505a8ef
HJYP
587 }
588 }
589
590 return TRUE;
591}
592
593gboolean on_key_press( GtkWidget* w, GdkEventKey* evt )
594{
595 GList* sels;
596 FmDesktop* self = (FmDesktop*)w;
597 int modifier = ( evt->state & ( GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK ) );
598#if 0
599 sels = fm_desktop_win_get_selected_files( self );
600
601 if ( modifier == GDK_CONTROL_MASK )
602 {
603 switch ( evt->keyval )
604 {
605 case GDK_x:
606 if( sels )
607 ptk_clipboard_cut_or_copy_files( vfs_get_desktop_dir(), sels, FALSE );
608 break;
609 case GDK_c:
610 if( sels )
611 ptk_clipboard_cut_or_copy_files( vfs_get_desktop_dir(), sels, TRUE );
612 break;
613 case GDK_v:
614 on_paste( NULL, self );
615 break;
616/*
617 case GDK_i:
618 ptk_file_browser_invert_selection( file_browser );
619 break;
620 case GDK_a:
621 ptk_file_browser_select_all( file_browser );
622 break;
623*/
624 }
625 }
626 else if ( modifier == GDK_MOD1_MASK )
627 {
628 switch ( evt->keyval )
629 {
630 case GDK_Return:
631 if( sels )
632 ptk_show_file_properties( NULL, vfs_get_desktop_dir(), sels );
633 break;
634 }
635 }
636 else if ( modifier == GDK_SHIFT_MASK )
637 {
638 switch ( evt->keyval )
639 {
640 case GDK_Delete:
641 if( sels )
642 ptk_delete_files( NULL, vfs_get_desktop_dir(), sels );
643 break;
644 }
645 }
646 else if ( modifier == 0 )
647 {
648 switch ( evt->keyval )
649 {
650 case GDK_F2:
651 if( sels )
652 ptk_rename_file( NULL, vfs_get_desktop_dir(), (FmFileInfo*)sels->data );
653 break;
654 case GDK_Delete:
655 if( sels )
656 ptk_delete_files( NULL, vfs_get_desktop_dir(), sels );
657 break;
658 }
659 }
660
661 if( sels )
662 vfs_file_info_list_free( sels );
663#endif
664
665 return TRUE;
666}
667
668void on_style_set( GtkWidget* w, GtkStyle* prev )
669{
670 FmDesktop* self = (FmDesktop*)w;
671#if 0
672 PangoContext* pc;
673 PangoFontMetrics *metrics;
674 int font_h;
675 pc = gtk_widget_get_pango_context( (GtkWidget*)self );
676
677 metrics = pango_context_get_metrics(
678 pc, ((GtkWidget*)self)->style->font_desc,
679 pango_context_get_language(pc));
680
681 font_h = pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent (metrics);
682 font_h /= PANGO_SCALE;
683#endif
684}
685
686void on_realize( GtkWidget* w )
687{
688 FmDesktop* self = (FmDesktop*)w;
689
690 GTK_WIDGET_CLASS(fm_desktop_parent_class)->realize( w );
691 gtk_window_set_skip_pager_hint( GTK_WINDOW(w), TRUE );
692 gtk_window_set_skip_taskbar_hint( GTK_WINDOW(w), TRUE );
693 gtk_window_set_resizable( (GtkWindow*)w, FALSE );
694
695 if( ! self->gc )
696 self->gc = gdk_gc_new( w->window );
697
8505a8ef
HJYP
698 update_background(self);
699}
700
701gboolean on_focus_in( GtkWidget* w, GdkEventFocus* evt )
702{
703 FmDesktop* self = (FmDesktop*) w;
704 GTK_WIDGET_SET_FLAGS( w, GTK_HAS_FOCUS );
705 if( self->focus )
706 redraw_item( self, self->focus );
707 return FALSE;
708}
709
710gboolean on_focus_out( GtkWidget* w, GdkEventFocus* evt )
711{
712 FmDesktop* self = (FmDesktop*) w;
713 if( self->focus )
714 {
715 GTK_WIDGET_UNSET_FLAGS( w, GTK_HAS_FOCUS );
716 redraw_item( self, self->focus );
717 }
718 return FALSE;
719}
720
495bd587
HJYP
721void on_drag_leave(GtkWidget* w, GdkDragContext* drag_ctx, guint time)
722{
723 FmDesktop* desktop = (FmDesktop*)w;
724 if(desktop->drop_hilight)
725 {
726 FmDesktopItem* old_drop = desktop->drop_hilight;
727 desktop->drop_hilight = NULL;
728 redraw_item(desktop, old_drop);
729 }
730}
8505a8ef
HJYP
731
732gboolean on_expose( GtkWidget* w, GdkEventExpose* evt )
733{
734 FmDesktop* self = (FmDesktop*)w;
735 GList* l;
736 cairo_t* cr;
737
738 if( G_UNLIKELY( ! GTK_WIDGET_VISIBLE (w) || ! GTK_WIDGET_MAPPED (w) ) )
739 return TRUE;
740
741 cr = gdk_cairo_create(w->window);
742 if( self->rubber_bending )
743 paint_rubber_banding_rect( self, cr, &evt->area );
744
745 for( l = self->items; l; l = l->next )
746 {
747 FmDesktopItem* item = (FmDesktopItem*)l->data;
748 GdkRectangle* intersect, tmp, tmp2;
749 if(gdk_rectangle_intersect( &evt->area, &item->icon_rect, &tmp ))
750 intersect = &tmp;
751 else
752 intersect = NULL;
753
754 if(gdk_rectangle_intersect( &evt->area, &item->text_rect, &tmp2 ))
755 {
756 if(intersect)
757 gdk_rectangle_union(intersect, &tmp2, intersect);
758 else
759 intersect = &tmp2;
760 }
761
762 if(intersect)
763 paint_item( self, item, cr, intersect );
764 }
765 cairo_destroy(cr);
766
767 return TRUE;
768}
769
770void on_size_allocate( GtkWidget* w, GtkAllocation* alloc )
771{
772 GdkPixbuf* pix;
773 FmDesktop* self = (FmDesktop*)w;
774 GdkRectangle wa;
775
776 /* calculate item size */
777 PangoContext* pc;
778 PangoFontMetrics *metrics;
779 int font_h;
780 pc = gtk_widget_get_pango_context( (GtkWidget*)self );
781
782 metrics = pango_context_get_metrics(
783 pc, ((GtkWidget*)self)->style->font_desc,
784 pango_context_get_language(pc));
785
786 font_h = pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent (metrics);
787 font_h /= PANGO_SCALE;
788
789 self->spacing = SPACING;
790 self->xpad = self->ypad = PADDING;
791 self->xmargin = self->ymargin = MARGIN;
495bd587
HJYP
792 self->text_h = font_h * 2 + 4;
793 self->text_w = 100 + 4; /* 4 is for drawing border */
794 self->pango_text_h = self->text_h * PANGO_SCALE;
795 self->pango_text_w = self->text_w * PANGO_SCALE;
796 self->cell_h = fm_config->big_icon_size + self->spacing + self->text_h + self->ypad * 2;
797 self->cell_w = MAX(self->text_w, fm_config->big_icon_size) + self->xpad * 2;
8505a8ef
HJYP
798
799 update_working_area(self);
800 /* queue_layout_items(self); this is called in update_working_area */
801
802 GTK_WIDGET_CLASS(fm_desktop_parent_class)->size_allocate( w, alloc );
803}
804
805void on_size_request( GtkWidget* w, GtkRequisition* req )
806{
807 GdkScreen* scr = gtk_widget_get_screen( w );
808 req->width = gdk_screen_get_width( scr );
809 req->height = gdk_screen_get_height( scr );
810}
811
812static gboolean is_point_in_rect( GdkRectangle* rect, int x, int y )
813{
814 return rect->x < x && x < (rect->x + rect->width) && y > rect->y && y < (rect->y + rect->height);
815}
816
817FmDesktopItem* hit_test(FmDesktop* self, int x, int y)
818{
819 FmDesktopItem* item;
820 GList* l;
821 for( l = self->items; l; l = l->next )
822 {
823 item = (FmDesktopItem*) l->data;
824 if( is_point_in_rect( &item->icon_rect, x, y )
825 || is_point_in_rect( &item->text_rect, x, y ) )
826 return item;
827 }
828 return NULL;
829}
830
f8f2bfad 831inline FmDesktopItem* desktop_item_new(GtkTreeIter* it)
8505a8ef
HJYP
832{
833 FmDesktopItem* item = g_slice_new0(FmDesktopItem);
834 item->it = *it;
f8f2bfad
HJYP
835 gtk_tree_model_get(model, it, COL_FILE_BIG_ICON, &item->icon, COL_FILE_INFO, &item->fi, -1);
836 return item;
837}
8505a8ef 838
f8f2bfad
HJYP
839void on_row_inserted(GtkTreeModel* mod, GtkTreePath* tp, GtkTreeIter* it, FmDesktop* desktop)
840{
841 FmDesktopItem* item = desktop_item_new(it);
842 desktop->items = g_list_insert(desktop->items, item, gtk_tree_path_get_indices(tp)[0]);
8505a8ef
HJYP
843 queue_layout_items(desktop);
844}
845
846void on_row_deleted(GtkTreeModel* mod, GtkTreePath* tp, FmDesktop* desktop)
847{
848 GList* l;
849 int i = 0, idx = gtk_tree_path_get_indices(tp)[0];
850 for(l=desktop->items;l;l=l->next, ++i)
851 {
852 FmDesktopItem* item = (FmDesktopItem*)l->data;
853 if(i == idx)
854 {
855 desktop_item_free(item);
856 desktop->items = g_list_delete_link(desktop->items, l);
857 break;
858 }
859 }
860
861 queue_layout_items(desktop);
862}
863
864void on_row_changed(GtkTreeModel* mod, GtkTreePath* tp, GtkTreeIter* it, FmDesktop* desktop)
865{
866 queue_layout_items(desktop);
867}
868
495bd587
HJYP
869void calc_item_size(FmDesktop* desktop, FmDesktopItem* item)
870{
871 int text_x, text_y, text_w, text_h;
872 PangoRectangle rc;
873
874 /* icon rect */
875 if(item->icon)
876 {
877 item->icon_rect.width = gdk_pixbuf_get_width(item->icon);
878 item->icon_rect.height = gdk_pixbuf_get_height(item->icon);
879 item->icon_rect.x = item->x + (desktop->cell_w - item->icon_rect.width) / 2;
880 item->icon_rect.y = item->y + desktop->ypad + (fm_config->big_icon_size - item->icon_rect.height) / 2;
881 item->icon_rect.height += desktop->spacing;
882 }
883 else
884 {
885 item->icon_rect.width = fm_config->big_icon_size;
886 item->icon_rect.height = fm_config->big_icon_size;
887 item->icon_rect.x = item->x + desktop->ypad;
888 item->icon_rect.y = item->y + desktop->ypad;
889 item->icon_rect.height += desktop->spacing;
890 }
891
892 /* text label rect */
893 pango_layout_set_text(desktop->pl, NULL, 0);
894 /* FIXME: we should cache text_h * PANGO_SCALE and text_w * PANGO_SCALE */
895 pango_layout_set_height(desktop->pl, desktop->pango_text_h);
896 pango_layout_set_width(desktop->pl, desktop->pango_text_w);
897 pango_layout_set_text(desktop->pl, fm_file_info_get_disp_name(item->fi), -1);
898
899 pango_layout_get_pixel_extents(desktop->pl, &rc, NULL);
900 pango_layout_set_text(desktop->pl, NULL, 0);
901
902 item->text_rect.x = item->x + (desktop->cell_w - rc.width) / 2;
903 item->text_rect.y = item->icon_rect.y + item->icon_rect.height;
904 item->text_rect.width = rc.width + 4;
905 item->text_rect.height = rc.height + 4;
906}
8505a8ef
HJYP
907
908void layout_items(FmDesktop* self)
909{
910 GList* l;
911 FmDesktopItem* item;
912 GtkWidget* widget = (GtkWidget*)self;
913 int x, y, w, bottom, right;
8505a8ef
HJYP
914
915 x = self->working_area.x + self->xmargin;
916 y = self->working_area.y + self->ymargin;
83418aeb 917 bottom = self->working_area.y + self->working_area.height - self->ymargin - self->cell_h;
8505a8ef
HJYP
918
919 for( l = self->items; l; l = l->next )
920 {
921 item = (FmDesktopItem*)l->data;
495bd587
HJYP
922 item->x = x;
923 item->y = y;
924 calc_item_size(self, item);
8505a8ef
HJYP
925 y += self->cell_h;
926 if(y > bottom)
927 {
928 x += self->cell_w;
929 y = self->working_area.y + self->ymargin;
930 }
931 }
932 gtk_widget_queue_draw( GTK_WIDGET(self) );
933}
934
935static gboolean on_idle_layout(FmDesktop* desktop)
936{
937 desktop->idle_layout = 0;
938 layout_items(desktop);
939 return FALSE;
940}
941
942void queue_layout_items(FmDesktop* desktop)
943{
83418aeb 944 if(0 == desktop->idle_layout)
8505a8ef
HJYP
945 desktop->idle_layout = g_idle_add((GSourceFunc)on_idle_layout, desktop);
946}
947
948void paint_item(FmDesktop* self, FmDesktopItem* item, cairo_t* cr, GdkRectangle* expose_area)
949{
950 GtkWidget* widget = (GtkWidget*)self;
951 GtkCellRendererState state = 0;
952 GdkColor* fg;
953 /* g_debug("%s, %d, %d, %d, %d", item->fi->path->name, expose_area->x, expose_area->y, expose_area->width, expose_area->height); */
954
955 pango_layout_set_text(self->pl, NULL, 0);
495bd587
HJYP
956 pango_layout_set_height(self->pl, item->text_rect.height * PANGO_SCALE);
957 pango_layout_set_width(self->pl, item->text_rect.width * PANGO_SCALE);
8505a8ef
HJYP
958 pango_layout_set_text(self->pl, fm_file_info_get_disp_name(item->fi), -1);
959
495bd587 960 if(item->is_selected || item == self->drop_hilight) /* draw background for text label */
8505a8ef 961 {
495bd587 962 GdkRectangle rc;
8505a8ef
HJYP
963 int text_x, text_y, text_w, text_h;
964 state = GTK_CELL_RENDERER_SELECTED;
8505a8ef
HJYP
965
966 cairo_save(cr);
495bd587 967 gdk_cairo_rectangle(cr, &item->text_rect);
8505a8ef
HJYP
968 gdk_cairo_set_source_color(cr, &widget->style->bg[GTK_STATE_SELECTED]);
969 cairo_clip(cr);
970 cairo_paint(cr);
971 cairo_restore(cr);
972 fg = &widget->style->fg[GTK_STATE_SELECTED];
973 }
974 else
975 {
976 /* the shadow */
977 gdk_gc_set_rgb_fg_color(self->gc, &app_config->desktop_shadow);
978 gdk_draw_layout( widget->window, self->gc, item->text_rect.x+1, item->text_rect.y+1, self->pl );
979 fg = &app_config->desktop_fg;
980 }
981 /* real text */
982 gdk_gc_set_rgb_fg_color(self->gc, fg);
983 gdk_draw_layout( widget->window, self->gc, item->text_rect.x, item->text_rect.y, self->pl );
495bd587 984 pango_layout_set_text(self->pl, NULL, 0);
8505a8ef
HJYP
985
986 /* draw the icon */
987 g_object_set( self->icon_render, "pixbuf", item->icon, NULL );
988 gtk_cell_renderer_render(self->icon_render, widget->window, widget, &item->icon_rect, &item->icon_rect, expose_area, state);
989}
990
991void redraw_item(FmDesktop* desktop, FmDesktopItem* item)
992{
993 GdkRectangle rect;
994 gdk_rectangle_union(&item->icon_rect, &item->text_rect, &rect);
995 --rect.x;
996 --rect.y;
997 rect.width += 2;
998 rect.height += 2;
999 gdk_window_invalidate_rect( ((GtkWidget*)desktop)->window, &rect, FALSE );
1000}
1001
1002void calc_rubber_banding_rect( FmDesktop* self, int x, int y, GdkRectangle* rect )
1003{
1004 int x1, x2, y1, y2, w, h;
1005 if( self->drag_start_x < x )
1006 {
1007 x1 = self->drag_start_x;
1008 x2 = x;
1009 }
1010 else
1011 {
1012 x1 = x;
1013 x2 = self->drag_start_x;
1014 }
1015
1016 if( self->drag_start_y < y )
1017 {
1018 y1 = self->drag_start_y;
1019 y2 = y;
1020 }
1021 else
1022 {
1023 y1 = y;
1024 y2 = self->drag_start_y;
1025 }
1026
1027 rect->x = x1;
1028 rect->y = y1;
1029 rect->width = x2 - x1;
1030 rect->height = y2 - y1;
1031}
1032
1033void update_rubberbanding( FmDesktop* self, int newx, int newy )
1034{
1035 GList* l;
1036 GdkRectangle old_rect, new_rect;
1037 GdkRegion *region;
1038
1039 calc_rubber_banding_rect(self, self->rubber_bending_x, self->rubber_bending_y, &old_rect );
1040 calc_rubber_banding_rect(self, newx, newy, &new_rect );
1041
1042 gdk_window_invalidate_rect(((GtkWidget*)self)->window, &old_rect, FALSE );
1043 gdk_window_invalidate_rect(((GtkWidget*)self)->window, &new_rect, FALSE );
1044// gdk_window_clear_area(((GtkWidget*)self)->window, new_rect.x, new_rect.y, new_rect.width, new_rect.height );
1045/*
1046 region = gdk_region_rectangle( &old_rect );
1047 gdk_region_union_with_rect( region, &new_rect );
1048
1049// gdk_window_invalidate_region( ((GtkWidget*)self)->window, &region, TRUE );
1050
1051 gdk_region_destroy( region );
1052*/
1053 self->rubber_bending_x = newx;
1054 self->rubber_bending_y = newy;
1055
1056 /* update selection */
1057 for( l = self->items; l; l = l->next )
1058 {
1059 FmDesktopItem* item = (FmDesktopItem*)l->data;
1060 gboolean selected;
1061 if( gdk_rectangle_intersect( &new_rect, &item->icon_rect, NULL ) ||
1062 gdk_rectangle_intersect( &new_rect, &item->text_rect, NULL ) )
1063 selected = TRUE;
1064 else
1065 selected = FALSE;
1066
1067 if( item->is_selected != selected )
1068 {
1069 item->is_selected = selected;
1070 redraw_item( self, item );
1071 }
1072 }
1073}
1074
1075
1076void paint_rubber_banding_rect(FmDesktop* self, cairo_t* cr, GdkRectangle* expose_area)
1077{
1078 GtkWidget* widget = (GtkWidget*)self;
1079 GdkRectangle rect;
1080 GdkColor clr;
1081 guchar alpha;
1082
1083 calc_rubber_banding_rect( self, self->rubber_bending_x, self->rubber_bending_y, &rect );
1084
1085 if( rect.width <= 0 || rect.height <= 0 )
1086 return;
1087
1088 if(!gdk_rectangle_intersect(expose_area, &rect, &rect))
1089 return;
1090/*
1091 gtk_widget_style_get( icon_view,
1092 "selection-box-color", &clr,
1093 "selection-box-alpha", &alpha,
1094 NULL);
1095*/
1096 clr = widget->style->base[GTK_STATE_SELECTED];
1097 alpha = 64; /* FIXME: should be themable in the future */
1098
1099 cairo_save(cr);
1100 cairo_set_source_rgba(cr, (gdouble)clr.red/65535, (gdouble)clr.green/65536, (gdouble)clr.blue/65535, (gdouble)alpha/100);
1101 gdk_cairo_rectangle(cr, &rect);
1102 cairo_clip (cr);
1103 cairo_paint (cr);
1104 gdk_cairo_set_source_color(cr, &clr);
1105 cairo_rectangle (cr, rect.x + 0.5, rect.y + 0.5, rect.width - 1, rect.height - 1);
1106 cairo_stroke(cr);
1107 cairo_restore(cr);
1108}
1109
1110static void update_background(FmDesktop* desktop)
1111{
1112 GtkWidget* widget = (GtkWidget*)desktop;
1113 GdkPixbuf* pix, *scaled;
1114 GdkPixmap* pixmap;
1115 Pixmap pixmap_id;
1116 int dest_w, dest_h;
1117 GdkWindow* root = gdk_screen_get_root_window(gtk_widget_get_screen(widget));
1118
1119 if(app_config->wallpaper_mode == FM_WP_COLOR
1120 || !app_config->wallpaper
f8f2bfad 1121 || !*app_config->wallpaper
8505a8ef
HJYP
1122 || ! (pix = gdk_pixbuf_new_from_file(app_config->wallpaper, NULL)) ) /* solid color only */
1123 {
1124 GdkColor bg = app_config->desktop_bg;
1125 gdk_rgb_find_color(gdk_drawable_get_colormap(widget->window), &bg);
1126 gdk_window_set_back_pixmap(widget->window, NULL, FALSE);
1127 gdk_window_set_background(widget->window, &bg);
1128 gdk_window_set_back_pixmap(root, NULL, FALSE);
1129 gdk_window_set_background(root, &bg);
f970e846
HJYP
1130 gdk_window_clear(root);
1131 gdk_window_clear(widget->window);
1132 gdk_window_invalidate_rect(widget->window, NULL, TRUE);
8505a8ef
HJYP
1133 return;
1134 }
1135
1136 if(app_config->wallpaper_mode == FM_WP_TILE)
1137 {
1138 dest_w = gdk_pixbuf_get_width(pix);
1139 dest_h = gdk_pixbuf_get_height(pix);
1140 pixmap = gdk_pixmap_new(widget->window, dest_w, dest_h, -1);
1141 }
1142 else
1143 {
1144 GdkScreen* screen = gtk_widget_get_screen(widget);
1145 dest_w = gdk_screen_get_width(screen);
1146 dest_h = gdk_screen_get_height(screen);
1147 pixmap = gdk_pixmap_new(widget->window, dest_w, dest_h, -1);
1148 }
1149
1150 if(gdk_pixbuf_get_has_alpha(pix)
1151 || app_config->wallpaper_mode == FM_WP_CENTER
1152 || app_config->wallpaper_mode == FM_WP_FULL)
1153 {
1154 gdk_gc_set_rgb_fg_color(desktop->gc, &app_config->desktop_bg);
1155 gdk_draw_rectangle(pixmap, desktop->gc, TRUE, 0, 0, dest_w, dest_h);
1156 }
1157
1158 switch(app_config->wallpaper_mode)
1159 {
1160 case FM_WP_TILE:
1161 gdk_draw_pixbuf(pixmap, desktop->gc, pix, 0, 0, 0, 0, dest_w, dest_h, GDK_RGB_DITHER_NORMAL, 0, 0);
1162 break;
1163 case FM_WP_FULL:
1164 /* FIXME: this is not implemented */
1165 scaled = gdk_pixbuf_scale_simple(pix, dest_w, dest_h, GDK_INTERP_BILINEAR);
1166 gdk_draw_pixbuf(pixmap, desktop->gc, scaled, 0, 0, 0, 0, dest_w, dest_h, GDK_RGB_DITHER_NORMAL, 0, 0);
1167 g_object_unref(scaled);
1168 break;
1169 case FM_WP_STRETCH:
1170 scaled = gdk_pixbuf_scale_simple(pix, dest_w, dest_h, GDK_INTERP_BILINEAR);
1171 gdk_draw_pixbuf(pixmap, desktop->gc, scaled, 0, 0, 0, 0, dest_w, dest_h, GDK_RGB_DITHER_NORMAL, 0, 0);
1172 g_object_unref(scaled);
1173 break;
1174 case FM_WP_CENTER:
1175 {
1176 int x, y;
1177 x = (dest_w - gdk_pixbuf_get_width(pix))/2;
1178 y = (dest_h - gdk_pixbuf_get_height(pix))/2;
1179 gdk_draw_pixbuf(pixmap, desktop->gc, pix, 0, 0, x, y, -1, -1, GDK_RGB_DITHER_NORMAL, 0, 0);
1180 }
1181 break;
1182 }
1183 gdk_window_set_back_pixmap(root, pixmap, FALSE);
1184 gdk_window_set_back_pixmap(widget->window, NULL, TRUE);
1185
1186 pixmap_id = GDK_DRAWABLE_XID(pixmap);
1187 XChangeProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
1188 XA_XROOTMAP_ID, XA_PIXMAP, 32, PropModeReplace, (guchar*)&pixmap_id, 1);
1189
1190 g_object_unref(pixmap);
1191 if(pix)
1192 g_object_unref(pix);
f970e846
HJYP
1193
1194 gdk_window_clear(root);
8505a8ef 1195 gdk_window_clear(widget->window);
f970e846 1196 gdk_window_invalidate_rect(widget->window, NULL, TRUE);
8505a8ef
HJYP
1197}
1198
1199GdkFilterReturn on_root_event(GdkXEvent *xevent, GdkEvent *event, gpointer data)
1200{
1201 XPropertyEvent * evt = ( XPropertyEvent* ) xevent;
1202 FmDesktop* self = (FmDesktop*)data;
1203 if ( evt->type == PropertyNotify )
1204 {
1205 if(evt->atom == XA_NET_WORKAREA)
1206 update_working_area(self);
1207 }
1208 return GDK_FILTER_TRANSLATE;
1209}
1210
1211void update_working_area(FmDesktop* desktop)
1212{
1213 GdkScreen* screen = gtk_widget_get_screen((GtkWidget*)desktop);
1214 GdkWindow* root = gdk_screen_get_root_window(screen);
1215 Atom ret_type;
1216 gulong len, after;
1217 int format;
1218 guchar* prop;
1219 guint32 n_desktops, cur_desktop;
1220 gulong* working_area;
1221
1222 /* default to screen size */
1223 desktop->working_area.x = 0;
1224 desktop->working_area.y = 0;
1225 desktop->working_area.width = gdk_screen_get_width(screen);
1226 desktop->working_area.height = gdk_screen_get_height(screen);
1227
1228 if( XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
1229 XA_NET_NUMBER_OF_DESKTOPS, 0, 1, False, XA_CARDINAL, &ret_type,
1230 &format, &len, &after, &prop) != Success)
1231 goto _out;
1232 n_desktops = *(guint32*)prop;
1233 XFree(prop);
1234
1235 if( XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
1236 XA_NET_CURRENT_DESKTOP, 0, 1, False, XA_CARDINAL, &ret_type,
1237 &format, &len, &after, &prop) != Success)
1238 goto _out;
1239 cur_desktop = *(guint32*)prop;
1240 XFree(prop);
1241
1242 if( XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
1243 XA_NET_WORKAREA, 0, 4 * 32, False, AnyPropertyType, &ret_type,
1244 &format, &len, &after, &prop) != Success)
1245 goto _out;
1246 if(ret_type == None || format == 0 || len != n_desktops*4 )
1247 {
1248 if(prop)
1249 XFree(prop);
1250 goto _out;
1251 }
1252 working_area = ((gulong*)prop) + cur_desktop * 4;
1253
1254 desktop->working_area.x = (gint)working_area[0];
1255 desktop->working_area.y = (gint)working_area[1];
1256 desktop->working_area.width = (gint)working_area[2];
1257 desktop->working_area.height = (gint)working_area[3];
1258
1259 XFree(prop);
1260_out:
1261 queue_layout_items(desktop);
1262 return;
1263}
1264
1265void on_screen_size_changed(GdkScreen* screen, FmDesktop* desktop)
1266{
1267 gtk_window_resize((GtkWindow*)desktop, gdk_screen_get_width(screen), gdk_screen_get_height(screen));
1268}
f8f2bfad
HJYP
1269
1270void on_dnd_src_data_get(FmDndSrc* ds, FmDesktop* desktop)
1271{
19abb8bf 1272 FmFileInfoList* files = fm_desktop_get_selected_files(desktop);
f8f2bfad
HJYP
1273 if(files)
1274 {
1275 fm_dnd_src_set_files(ds, files);
1276 fm_list_unref(files);
1277 }
f8f2bfad
HJYP
1278}
1279
1280gboolean on_dnd_dest_query_info(FmDndDest* dd, int x, int y,
1281 GdkDragAction* action, FmDesktop* desktop)
1282{
19abb8bf
HJYP
1283 FmDesktopItem* item = hit_test(desktop, x, y);
1284 if(item)
1285 {
1286 *action = GDK_ACTION_COPY;
1287 fm_dnd_dest_set_dest_file(dd, item->fi);
19abb8bf
HJYP
1288 }
1289 else
1290 {
1291 *action = GDK_ACTION_COPY;
1292 /* FIXME: prevent direct access to data member */
1293 fm_dnd_dest_set_dest_file(dd, model->dir->dir_fi);
495bd587
HJYP
1294 }
1295
1296 if(desktop->drop_hilight != item)
1297 {
1298 FmDesktopItem* old_drop = desktop->drop_hilight;
1299 desktop->drop_hilight = item;
1300 if(old_drop)
1301 redraw_item(desktop, old_drop);
1302 if(item)
1303 redraw_item(desktop, item);
19abb8bf
HJYP
1304 }
1305 return TRUE;
f8f2bfad
HJYP
1306}
1307
1308void on_dnd_dest_files_dropped(FmDndDest* dd, GdkDragAction action,
1309 int info_type, FmList* files, FmDesktop* desktop)
1310{
495bd587 1311
f8f2bfad 1312}
f970e846
HJYP
1313
1314void on_wallpaper_changed(FmConfig* cfg, gpointer user_data)
1315{
1316 int i;
1317 for(i=0; i < n_screens; ++i)
1318 update_background(desktops[i]);
1319}
19abb8bf 1320
495bd587
HJYP
1321GList* get_selected_items(FmDesktop* desktop, int* n_items)
1322{
1323 GList* items = NULL;
1324 GList* l;
1325 int n = 0;
1326 for(l=desktop->items; l; l=l->next)
1327 {
1328 FmDesktopItem* item = (FmDesktopItem*)l->data;
1329 if(item->is_selected && item != desktop->focus)
1330 {
1331 items = g_list_prepend(items, item);
1332 ++n;
1333 }
1334 }
1335 items = g_list_reverse(items);
1336 if(desktop->focus)
1337 items = g_list_prepend(items, desktop->focus);
1338 if(n_items)
1339 *n_items = n;
1340 return items;
1341}
1342
19abb8bf
HJYP
1343FmFileInfoList* fm_desktop_get_selected_files(FmDesktop* desktop)
1344{
1345 GList* l;
1346 FmFileInfoList* files = fm_file_info_list_new();
1347 for(l=desktop->items; l; l=l->next)
1348 {
1349 FmDesktopItem* item = (FmDesktopItem*)l->data;
1350 if(item->is_selected)
1351 fm_list_push_tail(files, item->fi);
1352 }
1353 if(fm_list_is_empty(files))
1354 {
1355 fm_list_unref(files);
1356 files = NULL;
1357 }
1358 return files;
1359}