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