Fix over previous commit.
[lxde/pcmanfm.git] / src / desktop.c
1 /*
2 * desktop.c
3 *
4 * Copyright 2010 - 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
5 * Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "desktop.h"
28 #include "pcmanfm.h"
29
30 #include <glib/gi18n.h>
31
32 #include <gdk/gdkx.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <X11/Xlib.h>
35 #include <X11/Xatom.h>
36 #include <math.h>
37
38 #include <cairo-xlib.h>
39
40 #include "pref.h"
41 #include "main-win.h"
42
43 #include "gseal-gtk-compat.h"
44
45 /* compatibility with LibFM < 1.0.2 */
46 #if !FM_CHECK_VERSION(1, 0, 2)
47 # define FM_FOLDER_MODEL_COL_INFO COL_FILE_INFO
48 # define FM_FOLDER_MODEL_COL_ICON COL_FILE_ICON
49 #endif
50
51 #define SPACING 2
52 #define PADDING 6
53 #define MARGIN 2
54
55 /* the search dialog timeout (in ms) */
56 #define DESKTOP_SEARCH_DIALOG_TIMEOUT (5000)
57
58 struct _FmDesktopItem
59 {
60 FmFileInfo* fi;
61 GdkRectangle area; /* position of the item on the desktop */
62 GdkRectangle icon_rect;
63 GdkRectangle text_rect;
64 gboolean is_special : 1; /* is this a special item like "My Computer", mounted volume, or "Trash" */
65 gboolean is_mount : 1; /* is this a mounted volume*/
66 gboolean is_selected : 1;
67 gboolean is_rubber_banded : 1;
68 gboolean is_prelight : 1;
69 gboolean fixed_pos : 1;
70 };
71
72 struct _FmBackgroundCache
73 {
74 FmBackgroundCache *next;
75 char *filename;
76 #if GTK_CHECK_VERSION(3, 0, 0)
77 cairo_surface_t *bg;
78 #else
79 GdkPixmap *bg;
80 #endif
81 FmWallpaperMode wallpaper_mode;
82 time_t mtime;
83 };
84
85 static void queue_layout_items(FmDesktop* desktop);
86 static void redraw_item(FmDesktop* desktop, FmDesktopItem* item);
87
88 static FmFileInfoList* _dup_selected_files(FmFolderView* fv);
89 static FmPathList* _dup_selected_file_paths(FmFolderView* fv);
90 static void _select_all(FmFolderView* fv);
91 static void _unselect_all(FmFolderView* fv);
92
93 static FmDesktopItem* hit_test(FmDesktop* self, GtkTreeIter *it, int x, int y);
94
95 static void fm_desktop_view_init(FmFolderViewInterface* iface);
96
97 G_DEFINE_TYPE_WITH_CODE(FmDesktop, fm_desktop, GTK_TYPE_WINDOW,
98 G_IMPLEMENT_INTERFACE(FM_TYPE_FOLDER_VIEW, fm_desktop_view_init))
99
100 static GtkWindowGroup* win_group = NULL;
101 static FmDesktop **desktops = NULL;
102 static int n_screens = 0;
103 static guint icon_theme_changed = 0;
104 static GtkAccelGroup* acc_grp = NULL;
105
106 static Atom XA_NET_WORKAREA = 0;
107 static Atom XA_NET_NUMBER_OF_DESKTOPS = 0;
108 static Atom XA_NET_CURRENT_DESKTOP = 0;
109 static Atom XA_XROOTMAP_ID = 0;
110 static Atom XA_XROOTPMAP_ID = 0;
111
112 static GdkCursor* hand_cursor = NULL;
113
114 static guint idle_config_save = 0;
115
116 enum {
117 #if N_FM_DND_DEST_DEFAULT_TARGETS > N_FM_DND_SRC_DEFAULT_TARGETS
118 FM_DND_DEST_DESKTOP_ITEM = N_FM_DND_DEST_DEFAULT_TARGETS
119 #else
120 FM_DND_DEST_DESKTOP_ITEM = N_FM_DND_SRC_DEFAULT_TARGETS
121 #endif
122 };
123
124 static GtkTargetEntry dnd_targets[] =
125 {
126 {"application/x-desktop-item", GTK_TARGET_SAME_WIDGET, FM_DND_DEST_DESKTOP_ITEM}
127 };
128
129 static GdkAtom desktop_atom;
130
131 enum
132 {
133 PROP_0,
134 PROP_MONITOR,
135 N_PROPERTIES
136 };
137
138 /* popup menu callbacks */
139 static void on_open_in_new_tab(GtkAction* act, gpointer user_data);
140 static void on_open_in_new_win(GtkAction* act, gpointer user_data);
141 static void on_open_folder_in_terminal(GtkAction* act, gpointer user_data);
142
143 static void on_fix_pos(GtkToggleAction* act, gpointer user_data);
144 static void on_snap_to_grid(GtkAction* act, gpointer user_data);
145
146 #if FM_CHECK_VERSION(1, 2, 0)
147 static void on_disable(GtkAction* act, gpointer user_data);
148 #endif
149
150 /* insert GtkUIManager XML definitions */
151 #include "desktop-ui.c"
152
153
154 /* ---------------------------------------------------------------------
155 Items management and common functions */
156
157 static char* get_config_file(FmDesktop* desktop, gboolean create_dir)
158 {
159 char *dir, *path;
160 int i;
161
162 for(i = 0; i < n_screens; i++)
163 if(desktops[i] == desktop)
164 break;
165 if(i >= n_screens)
166 return NULL;
167 dir = pcmanfm_get_profile_dir(create_dir);
168 path = g_strdup_printf("%s/desktop-items-%u.conf", dir, i);
169 g_free(dir);
170 return path;
171 }
172
173 static inline FmDesktopItem* desktop_item_new(FmFolderModel* model, GtkTreeIter* it)
174 {
175 FmDesktopItem* item = g_slice_new0(FmDesktopItem);
176 fm_folder_model_set_item_userdata(model, it, item);
177 gtk_tree_model_get(GTK_TREE_MODEL(model), it, FM_FOLDER_MODEL_COL_INFO, &item->fi, -1);
178 fm_file_info_ref(item->fi);
179 return item;
180 }
181
182 static inline void desktop_item_free(FmDesktopItem* item)
183 {
184 if(item->fi)
185 fm_file_info_unref(item->fi);
186 g_slice_free(FmDesktopItem, item);
187 }
188
189 static void calc_item_size(FmDesktop* desktop, FmDesktopItem* item, GdkPixbuf* icon)
190 {
191 PangoRectangle rc2;
192
193 /* icon rect */
194 if(icon)
195 {
196 item->icon_rect.width = gdk_pixbuf_get_width(icon);
197 item->icon_rect.height = gdk_pixbuf_get_height(icon);
198 }
199 else
200 {
201 item->icon_rect.width = fm_config->big_icon_size;
202 item->icon_rect.height = fm_config->big_icon_size;
203 }
204 item->icon_rect.x = item->area.x + (desktop->cell_w - item->icon_rect.width) / 2;
205 item->icon_rect.y = item->area.y + desktop->ypad + (fm_config->big_icon_size - item->icon_rect.height) / 2;
206 item->icon_rect.height += desktop->spacing;
207
208 /* text label rect */
209 pango_layout_set_text(desktop->pl, NULL, 0);
210 pango_layout_set_height(desktop->pl, desktop->pango_text_h);
211 pango_layout_set_width(desktop->pl, desktop->pango_text_w);
212 pango_layout_set_text(desktop->pl, fm_file_info_get_disp_name(item->fi), -1);
213
214 pango_layout_get_pixel_extents(desktop->pl, NULL, &rc2);
215 pango_layout_set_text(desktop->pl, NULL, 0);
216
217 /* FIXME: RTL */
218 item->text_rect.x = item->area.x + (desktop->cell_w - rc2.width - 4) / 2;
219 item->text_rect.y = item->area.y + desktop->ypad + fm_config->big_icon_size + desktop->spacing + rc2.y;
220 item->text_rect.width = rc2.width + 4;
221 item->text_rect.height = rc2.height + 4;
222 item->area.width = (desktop->cell_w + MAX(item->icon_rect.width, item->text_rect.width)) / 2;
223 item->area.height = item->text_rect.y + item->text_rect.height - item->area.y;
224 }
225
226 /* unfortunately we cannot load the "*" together with items because
227 otherwise we will update pango layout on each load_items() which
228 is resource wasting so we load config file once more instead */
229 static inline void load_config(FmDesktop* desktop)
230 {
231 char* path;
232 GKeyFile* kf;
233
234 path = get_config_file(desktop, FALSE);
235 if(!path)
236 return;
237 kf = g_key_file_new();
238 if(g_key_file_load_from_file(kf, path, 0, NULL))
239 /* item "*" is desktop config */
240 fm_app_config_load_desktop_config(kf, "*", &desktop->conf);
241 g_free(path);
242 g_key_file_free(kf);
243 }
244
245 static inline void load_items(FmDesktop* desktop)
246 {
247 GtkTreeIter it;
248 char* path;
249 GtkTreeModel* model;
250 GKeyFile* kf;
251
252 if (desktop->model == NULL)
253 return;
254 model = GTK_TREE_MODEL(desktop->model);
255 if (!gtk_tree_model_get_iter_first(model, &it))
256 return;
257 path = get_config_file(desktop, FALSE);
258 if(!path)
259 return;
260 kf = g_key_file_new();
261 if(g_key_file_load_from_file(kf, path, 0, NULL))
262 {
263 do
264 {
265 FmDesktopItem* item;
266 const char* name;
267 GdkPixbuf* icon = NULL;
268 int out; /* out of bounds */
269
270 item = fm_folder_model_get_item_userdata(desktop->model, &it);
271 name = fm_file_info_get_name(item->fi);
272 if(g_key_file_has_group(kf, name))
273 {
274 gtk_tree_model_get(model, &it, FM_FOLDER_MODEL_COL_ICON, &icon, -1);
275 desktop->fixed_items = g_list_prepend(desktop->fixed_items, item);
276 item->fixed_pos = TRUE;
277 item->area.x = g_key_file_get_integer(kf, name, "x", NULL);
278 item->area.y = g_key_file_get_integer(kf, name, "y", NULL);
279 /* pull item into screen bounds */
280 if (item->area.x < desktop->xmargin)
281 item->area.x = desktop->xmargin;
282 if (item->area.y < desktop->ymargin)
283 item->area.y = desktop->ymargin;
284 calc_item_size(desktop, item, icon);
285 /* check if item is in screen bounds and pull it if it's not */
286 out = item->area.x + item->area.width + desktop->xmargin - desktop->working_area.width;
287 if (out > 0)
288 {
289 if (out > item->area.x - desktop->xmargin)
290 out = item->area.x - desktop->xmargin;
291 item->area.x -= out;
292 item->icon_rect.x -= out;
293 item->text_rect.x -= out;
294 }
295 out = item->area.y + item->area.height + desktop->ymargin - desktop->working_area.height;
296 if (out > 0)
297 {
298 if (out > item->area.y - desktop->ymargin)
299 out = item->area.y - desktop->ymargin;
300 item->area.y -= out;
301 item->icon_rect.y -= out;
302 item->text_rect.y -= out;
303 }
304 if(icon)
305 g_object_unref(icon);
306 }
307 }
308 while(gtk_tree_model_iter_next(model, &it));
309 }
310 g_free(path);
311 g_key_file_free(kf);
312 queue_layout_items(desktop);
313 }
314
315 static inline void unload_items(FmDesktop* desktop)
316 {
317 /* remove existing fixed items */
318 g_list_free(desktop->fixed_items);
319 desktop->fixed_items = NULL;
320 desktop->focus = NULL;
321 desktop->drop_hilight = NULL;
322 desktop->hover_item = NULL;
323 g_object_set(G_OBJECT(desktop), "tooltip-text", NULL, NULL);
324 }
325
326 static inline void reload_items(FmDesktop *desktop)
327 {
328 unload_items(desktop);
329 load_items(desktop);
330 }
331
332 static gint get_desktop_for_root_window(GdkWindow *root)
333 {
334 gint desktop = -1;
335 Atom ret_type;
336 gulong len, after;
337 int format;
338 guchar* prop;
339
340 if(XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
341 XA_NET_CURRENT_DESKTOP, 0, 1, False, XA_CARDINAL, &ret_type,
342 &format, &len, &after, &prop) == Success &&
343 prop != NULL)
344 {
345 desktop = (gint)*(guint32*)prop;
346 XFree(prop);
347 }
348 return desktop;
349 }
350
351 /* save position of desktop icons */
352 static void save_item_pos(FmDesktop* desktop)
353 {
354 GList* l;
355 GString* buf;
356 char* path = get_config_file(desktop, TRUE);
357
358 if(!path)
359 return;
360 buf = g_string_sized_new(1024);
361
362 /* save desktop config */
363 if (desktop->conf.configured)
364 {
365 fm_app_config_save_desktop_config(buf, "*", &desktop->conf);
366 g_string_append_c(buf, '\n');
367 }
368
369 /* save all items positions */
370 for(l = desktop->fixed_items; l; l=l->next)
371 {
372 FmDesktopItem* item = (FmDesktopItem*)l->data;
373 FmPath* fi_path = fm_file_info_get_path(item->fi);
374 const char* p;
375 /* write the file basename as group name */
376 g_string_append_c(buf, '[');
377 for(p = fm_path_get_basename(fi_path); *p; ++p)
378 {
379 switch(*p)
380 {
381 case '\r':
382 g_string_append(buf, "\\r");
383 break;
384 case '\n':
385 g_string_append(buf, "\\n");
386 break;
387 case '\\':
388 g_string_append(buf, "\\\\");
389 break;
390 case ']':
391 g_string_append(buf, "\\]");
392 break;
393 default:
394 g_string_append_c(buf, *p);
395 }
396 }
397 g_string_append(buf, "]\n");
398 g_string_append_printf(buf, "x=%d\n"
399 "y=%d\n\n",
400 item->area.x, item->area.y);
401 }
402 g_file_set_contents(path, buf->str, buf->len, NULL);
403 g_free(path);
404 g_string_free(buf, TRUE);
405 desktop->conf.changed = FALSE; /* reset it since we saved it */
406 }
407
408 static gboolean on_config_save_idle(gpointer _unused)
409 {
410 int i;
411
412 if (g_source_is_destroyed(g_main_current_source()))
413 return FALSE;
414
415 for (i = 0; i < n_screens; i++)
416 if (desktops[i]->conf.changed)
417 save_item_pos(desktops[i]);
418 idle_config_save = 0;
419 return FALSE;
420 }
421
422 static void queue_config_save(FmDesktop *desktop)
423 {
424 desktop->conf.configured = TRUE;
425 desktop->conf.changed = TRUE;
426 if (idle_config_save == 0)
427 idle_config_save = gdk_threads_add_idle(on_config_save_idle, NULL);
428 }
429
430 static GList* get_selected_items(FmDesktop* desktop, int* n_items)
431 {
432 GList* items = NULL;
433 int n = 0;
434 FmDesktopItem* focus = NULL;
435 GtkTreeModel* model = desktop->model ? GTK_TREE_MODEL(desktop->model) : NULL;
436 GtkTreeIter it;
437 if(model && gtk_tree_model_get_iter_first(model, &it)) do
438 {
439 FmDesktopItem* item = fm_folder_model_get_item_userdata(desktop->model, &it);
440 if(item->is_selected)
441 {
442 if(G_LIKELY(item != desktop->focus))
443 {
444 items = g_list_prepend(items, item);
445 ++n;
446 }
447 else
448 focus = item;
449 }
450 }
451 while(gtk_tree_model_iter_next(model, &it));
452 items = g_list_reverse(items);
453 if(focus)
454 {
455 items = g_list_prepend(items, focus);
456 ++n;
457 }
458 if(n_items)
459 *n_items = n;
460 return items;
461 }
462
463 static void copy_desktop_config(FmDesktopConfig *dst, FmDesktopConfig *src)
464 {
465 int i;
466
467 dst->wallpaper_mode = src->wallpaper_mode;
468 dst->wallpaper = g_strdup(src->wallpaper);
469 if (src->wallpapers_configured > 0)
470 {
471 dst->wallpapers = g_new(char *, src->wallpapers_configured);
472 for (i = 0; i < src->wallpapers_configured; i++)
473 dst->wallpapers[i] = g_strdup(src->wallpapers[i]);
474 }
475 else
476 dst->wallpapers = NULL;
477 dst->wallpapers_configured = src->wallpapers_configured;
478 dst->wallpaper_common = src->wallpaper_common;
479 dst->show_wm_menu = src->show_wm_menu;
480 dst->configured = TRUE;
481 dst->changed = FALSE;
482 dst->desktop_bg = src->desktop_bg;
483 dst->desktop_fg = src->desktop_fg;
484 dst->desktop_shadow = src->desktop_shadow;
485 dst->desktop_font = g_strdup(src->desktop_font);
486 dst->desktop_sort_type = src->desktop_sort_type;
487 dst->desktop_sort_by = src->desktop_sort_by;
488 dst->folder = g_strdup(src->folder);
489 }
490
491 #if FM_CHECK_VERSION(1, 2, 0)
492 /* ---------------------------------------------------------------------
493 mounts handlers */
494
495 typedef struct
496 {
497 GMount *mount; /* NULL for non-mounts */
498 FmPath *path;
499 FmFileInfo *fi;
500 FmFileInfoJob *job;
501 } FmDesktopExtraItem;
502
503 static FmDesktopExtraItem *documents = NULL;
504 //static FmDesktopExtraItem *computer = NULL;
505 static FmDesktopExtraItem *trash_can = NULL;
506 //static FmDesktopExtraItem *applications = NULL;
507
508 static GVolumeMonitor *vol_mon = NULL;
509 /* under GDK lock */
510 static GSList *mounts = NULL;
511
512 static void _free_extra_item(FmDesktopExtraItem *item);
513
514 static gboolean on_idle_extra_item_add(gpointer user_data)
515 {
516 FmDesktopExtraItem *item = user_data;
517 int i;
518
519 /* if mount is not NULL then it's new mount so add it to the list */
520 if (item->mount)
521 {
522 mounts = g_slist_append(mounts, item);
523 /* add it to all models that watch mounts */
524 for (i = 0; i < n_screens; i++)
525 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_mounts
526 && desktops[i]->model)
527 fm_folder_model_extra_file_add(desktops[i]->model, item->fi,
528 FM_FOLDER_MODEL_ITEMPOS_POST);
529 }
530 else if (item == documents)
531 {
532 /* add it to all models that watch documents */
533 for (i = 0; i < n_screens; i++)
534 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_documents
535 && desktops[i]->model)
536 {
537 fm_folder_model_extra_file_add(desktops[i]->model, item->fi,
538 FM_FOLDER_MODEL_ITEMPOS_PRE);
539 /* if this is extra item it might be loaded after the folder
540 therefore we have to reload fixed positions again to apply */
541 reload_items(desktops[i]);
542 }
543 }
544 else if (item == trash_can)
545 {
546 /* add it to all models that watch trash can */
547 for (i = 0; i < n_screens; i++)
548 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_trash
549 && desktops[i]->model)
550 {
551 fm_folder_model_extra_file_add(desktops[i]->model, item->fi,
552 FM_FOLDER_MODEL_ITEMPOS_PRE);
553 reload_items(desktops[i]);
554 }
555 }
556 else
557 {
558 g_critical("got file info for unknown desktop item %s",
559 fm_path_get_basename(item->path));
560 _free_extra_item(item);
561 }
562 return FALSE;
563 }
564
565 static gboolean trash_is_empty = FALSE; /* startup default */
566
567 /* returns TRUE if model should be updated */
568 static gboolean _update_trash_icon(FmDesktopExtraItem *item)
569 {
570 GFile *gf = fm_file_new_for_uri("trash:///");
571 GFileInfo *inf = g_file_query_info(gf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, 0, NULL, NULL);
572 const char *icon_name;
573 GIcon *icon;
574 guint32 n;
575
576 g_object_unref(gf);
577 if (!inf)
578 return FALSE;
579
580 n = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
581 g_object_unref(inf);
582 if (n > 0 && trash_is_empty)
583 icon_name = "user-trash-full";
584 else if (n == 0 && !trash_is_empty)
585 icon_name = "user-trash";
586 else /* not changed */
587 return FALSE;
588 trash_is_empty = (n == 0);
589 icon = g_themed_icon_new_with_default_fallbacks(icon_name);
590 fm_file_info_set_icon(item->fi, icon);
591 g_object_unref(icon);
592 return TRUE;
593 }
594
595 static void on_file_info_job_finished(FmFileInfoJob* job, gpointer user_data)
596 {
597 FmDesktopExtraItem *item = user_data;
598 FmFileInfo *fi;
599 char *name;
600 GIcon *icon;
601
602 if(fm_file_info_list_is_empty(job->file_infos))
603 {
604 /* failed */
605 g_critical("FmFileInfoJob failed on desktop mount update");
606 _free_extra_item(item);
607 return;
608 }
609 fi = fm_file_info_list_peek_head(job->file_infos);
610 /* FIXME: check for duplicates? */
611 item->fi = fm_file_info_ref(fi);
612 item->job = NULL;
613 g_object_unref(job);
614 /* update some data with those from the mount */
615 if (item->mount)
616 {
617 name = g_mount_get_name(item->mount);
618 fm_file_info_set_disp_name(fi, name);
619 g_free(name);
620 icon = g_mount_get_icon(item->mount);
621 fm_file_info_set_icon(fi, icon);
622 g_object_unref(icon);
623 }
624 /* update trash can icon */
625 else if (item == trash_can)
626 _update_trash_icon(item);
627 /* queue adding item to the list and folder models */
628 gdk_threads_add_idle(on_idle_extra_item_add, item);
629 }
630
631 static void _free_extra_item(FmDesktopExtraItem *item)
632 {
633 if (item->mount)
634 g_object_unref(item->mount);
635 fm_path_unref(item->path);
636 if (item->fi)
637 fm_file_info_unref(item->fi);
638 if (item->job)
639 {
640 g_signal_handlers_disconnect_by_func(item->job, on_file_info_job_finished, item);
641 fm_job_cancel(FM_JOB(item->job));
642 g_object_unref(item->job);
643 }
644 g_slice_free(FmDesktopExtraItem, item);
645 }
646
647 static void on_mount_added(GVolumeMonitor *volume_monitor, GMount *mount,
648 gpointer _unused)
649 {
650 GFile *file;
651 FmDesktopExtraItem *item;
652
653 /* get file info for the mount point */
654 item = g_slice_new(FmDesktopExtraItem);
655 item->mount = g_object_ref(mount);
656 file = g_mount_get_root(mount);
657 item->path = fm_path_new_for_gfile(file);
658 g_object_unref(file);
659 item->fi = NULL;
660 item->job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
661 fm_file_info_job_add(item->job, item->path);
662 g_signal_connect(item->job, "finished", G_CALLBACK(on_file_info_job_finished), item);
663 if (!fm_job_run_async(FM_JOB(item->job)))
664 {
665 g_critical("fm_job_run_async() failed on desktop mount update");
666 _free_extra_item(item);
667 }
668 }
669
670 static gboolean on_idle_extra_item_remove(gpointer user_data)
671 {
672 GMount *mount = user_data;
673 GSList *sl;
674 FmDesktopExtraItem *item;
675 int i;
676
677 for (sl = mounts; sl; sl = sl->next)
678 {
679 item = sl->data;
680 if (item->mount == mount)
681 break;
682 }
683 if (sl)
684 {
685 for (i = 0; i < n_screens; i++)
686 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_mounts
687 && desktops[i]->model)
688 fm_folder_model_extra_file_remove(desktops[i]->model, item->fi);
689 mounts = g_slist_delete_link(mounts, sl);
690 _free_extra_item(item);
691 }
692 else
693 g_warning("got unmount for unknown desktop item");
694 g_object_unref(mount);
695 return FALSE;
696 }
697
698 static void on_mount_removed(GVolumeMonitor *volume_monitor, GMount *mount,
699 gpointer _unused)
700 {
701 gdk_threads_add_idle(on_idle_extra_item_remove, g_object_ref(mount));
702 }
703
704
705 /* ---------------------------------------------------------------------
706 special items handlers */
707
708 static GFileMonitor *trash_monitor = NULL;
709
710 static void on_trash_changed(GFileMonitor *monitor, GFile *gf, GFile *other,
711 GFileMonitorEvent evt, FmDesktopExtraItem *item)
712 {
713 int i;
714
715 if (!_update_trash_icon(item))
716 return;
717 for (i = 0; i < n_screens; i++)
718 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_trash
719 && desktops[i]->model)
720 fm_folder_model_file_changed(desktops[i]->model, item->fi);
721 }
722
723 static FmDesktopExtraItem *_add_extra_item(const char *path_str)
724 {
725 FmDesktopExtraItem *item;
726
727 if (path_str == NULL) /* special directory does not exist */
728 return NULL;
729
730 item = g_slice_new(FmDesktopExtraItem);
731 item->mount = NULL;
732 item->path = fm_path_new_for_str(path_str);
733 item->fi = NULL;
734 item->job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
735 fm_file_info_job_add(item->job, item->path);
736 g_signal_connect(item->job, "finished", G_CALLBACK(on_file_info_job_finished), item);
737 if (!fm_job_run_async(FM_JOB(item->job)))
738 {
739 g_critical("fm_job_run_async() failed on desktop special item update");
740 _free_extra_item(item);
741 item = NULL;
742 }
743 return item;
744 }
745 #endif
746
747 /* ---------------------------------------------------------------------
748 accessibility handlers */
749
750 static void set_focused_item(FmDesktop* desktop, FmDesktopItem* item);
751 static inline gint fm_desktop_accessible_index(GtkWidget *desktop, gpointer item);
752
753 /* ---- accessible item mirror ---- */
754 typedef struct _FmDesktopItemAccessible FmDesktopItemAccessible;
755 typedef struct _FmDesktopItemAccessibleClass FmDesktopItemAccessibleClass;
756 #define FM_TYPE_DESKTOP_ITEM_ACCESSIBLE (fm_desktop_item_accessible_get_type ())
757 #define FM_DESKTOP_ITEM_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
758 FM_TYPE_DESKTOP_ITEM_ACCESSIBLE, FmDesktopItemAccessible))
759 #define FM_IS_DESKTOP_ITEM_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
760 FM_TYPE_DESKTOP_ITEM_ACCESSIBLE))
761
762 static GType fm_desktop_item_accessible_get_type (void);
763
764 struct _FmDesktopItemAccessible
765 {
766 AtkObject parent;
767 FmDesktopItem *item;
768 GtkWidget *widget;
769 AtkStateSet *state_set;
770 guint action_idle_handler;
771 gint action_type;
772 };
773
774 struct _FmDesktopItemAccessibleClass
775 {
776 AtkObjectClass parent_class;
777 };
778
779 static void atk_component_item_interface_init(AtkComponentIface *iface);
780 static void atk_action_item_interface_init(AtkActionIface *iface);
781 static void atk_image_item_interface_init(AtkImageIface *iface);
782
783 G_DEFINE_TYPE_WITH_CODE(FmDesktopItemAccessible, fm_desktop_item_accessible, ATK_TYPE_OBJECT,
784 G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT, atk_component_item_interface_init)
785 G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION, atk_action_item_interface_init)
786 G_IMPLEMENT_INTERFACE(ATK_TYPE_IMAGE, atk_image_item_interface_init))
787
788 static void fm_desktop_item_accessible_init(FmDesktopItemAccessible *item)
789 {
790 item->state_set = atk_state_set_new();
791 atk_state_set_add_state(item->state_set, ATK_STATE_ENABLED);
792 atk_state_set_add_state(item->state_set, ATK_STATE_FOCUSABLE);
793 atk_state_set_add_state(item->state_set, ATK_STATE_SENSITIVE);
794 atk_state_set_add_state(item->state_set, ATK_STATE_SELECTABLE);
795 atk_state_set_add_state(item->state_set, ATK_STATE_VISIBLE);
796 item->action_idle_handler = 0;
797 }
798
799 static void fm_desktop_item_accessible_finalize(GObject *object)
800 {
801 FmDesktopItemAccessible *item = (FmDesktopItemAccessible *)object;
802
803 g_return_if_fail(item != NULL);
804 g_return_if_fail(FM_IS_DESKTOP_ITEM_ACCESSIBLE(item));
805
806 if (item->widget)
807 g_object_remove_weak_pointer(G_OBJECT(item->widget), (gpointer)&item->widget);
808 if (item->state_set)
809 g_object_unref (item->state_set);
810 if (item->action_idle_handler)
811 {
812 g_source_remove(item->action_idle_handler);
813 item->action_idle_handler = 0;
814 }
815 }
816
817 static FmDesktopItemAccessible *fm_desktop_item_accessible_new(FmDesktop *desktop,
818 FmDesktopItem *item)
819 {
820 const char *name;
821 FmDesktopItemAccessible *atk_item;
822
823 name = fm_file_info_get_disp_name(item->fi);
824 atk_item = g_object_new(FM_TYPE_DESKTOP_ITEM_ACCESSIBLE,
825 "accessible-name", name, NULL);
826 atk_item->item = item;
827 atk_item->widget = GTK_WIDGET(desktop);
828 g_object_add_weak_pointer(G_OBJECT(desktop), (gpointer)&atk_item->widget);
829 return atk_item;
830 }
831
832 /* item interfaces */
833 static void fm_desktop_item_accessible_get_extents(AtkComponent *component,
834 gint *x, gint *y,
835 gint *width, gint *height,
836 AtkCoordType coord_type)
837 {
838 FmDesktopItemAccessible *item;
839 AtkObject *parent_obj;
840 gint l_x, l_y;
841
842 g_return_if_fail(FM_IS_DESKTOP_ITEM_ACCESSIBLE(component));
843 item = FM_DESKTOP_ITEM_ACCESSIBLE(component);
844 if (item->widget == NULL)
845 return;
846 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
847 return;
848
849 *width = item->item->area.width;
850 *height = item->item->area.height;
851 parent_obj = gtk_widget_get_accessible(item->widget);
852 atk_component_get_position(ATK_COMPONENT(parent_obj), &l_x, &l_y, coord_type);
853 *x = l_x + item->item->area.x;
854 *y = l_y + item->item->area.y;
855 }
856
857 static gboolean fm_desktop_item_accessible_grab_focus(AtkComponent *component)
858 {
859 FmDesktopItemAccessible *item;
860
861 g_return_val_if_fail(FM_IS_DESKTOP_ITEM_ACCESSIBLE(component), FALSE);
862 item = FM_DESKTOP_ITEM_ACCESSIBLE(component);
863 if (item->widget == NULL)
864 return FALSE;
865 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
866 return FALSE;
867
868 gtk_widget_grab_focus(item->widget);
869 set_focused_item(FM_DESKTOP(item->widget), item->item);
870 return TRUE;
871 }
872
873 static void atk_component_item_interface_init(AtkComponentIface *iface)
874 {
875 iface->get_extents = fm_desktop_item_accessible_get_extents;
876 iface->grab_focus = fm_desktop_item_accessible_grab_focus;
877 }
878
879 /* NOTE: this is not very fast */
880 static GtkTreePath *fm_desktop_item_get_tree_path(FmDesktop *self, FmDesktopItem *item)
881 {
882 GtkTreeModel *model = self->model ? GTK_TREE_MODEL(self->model) : NULL;
883 GtkTreeIter it;
884
885 if(model && gtk_tree_model_get_iter_first(model, &it)) do
886 {
887 if (fm_folder_model_get_item_userdata(self->model, &it) == item)
888 return gtk_tree_model_get_path(model, &it);
889 }
890 while (gtk_tree_model_iter_next(model, &it));
891 return NULL;
892 }
893
894 static gboolean fm_desktop_item_accessible_idle_do_action(gpointer data)
895 {
896 FmDesktopItemAccessible *item;
897 GtkTreePath *tp;
898
899 if(g_source_is_destroyed(g_main_current_source()))
900 return FALSE;
901
902 item = FM_DESKTOP_ITEM_ACCESSIBLE(data);
903 item->action_idle_handler = 0;
904 if (item->widget != NULL)
905 {
906 tp = fm_desktop_item_get_tree_path(FM_DESKTOP(item->widget), item->item);
907 if (tp)
908 {
909 fm_folder_view_item_clicked(FM_FOLDER_VIEW(item->widget), tp,
910 item->action_type == 0 ? FM_FV_ACTIVATED : FM_FV_CONTEXT_MENU);
911 gtk_tree_path_free(tp);
912 }
913 }
914 return FALSE;
915 }
916
917 static gboolean fm_desktop_item_accessible_action_do_action(AtkAction *action,
918 gint i)
919 {
920 FmDesktopItemAccessible *item;
921
922 if (i != 0 && i != 1)
923 return FALSE;
924
925 item = FM_DESKTOP_ITEM_ACCESSIBLE(action);
926 if (item->widget == NULL)
927 return FALSE;
928 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
929 return FALSE;
930 if (!item->action_idle_handler)
931 {
932 item->action_type = i;
933 item->action_idle_handler = gdk_threads_add_idle(fm_desktop_item_accessible_idle_do_action, item);
934 }
935 return TRUE;
936 }
937
938 static gint fm_desktop_item_accessible_action_get_n_actions(AtkAction *action)
939 {
940 return 2;
941 }
942
943 static const gchar *fm_desktop_item_accessible_action_get_description(AtkAction *action, gint i)
944 {
945 if (i == 0)
946 return _("Activate file");
947 else if (i == 1)
948 return _("Show file menu");
949 return NULL;
950 }
951
952 static const gchar *fm_desktop_item_accessible_action_get_name(AtkAction *action, gint i)
953 {
954 if (i == 0)
955 return "activate";
956 if (i == 1)
957 return "menu";
958 return NULL;
959 }
960
961 static void atk_action_item_interface_init(AtkActionIface *iface)
962 {
963 iface->do_action = fm_desktop_item_accessible_action_do_action;
964 iface->get_n_actions = fm_desktop_item_accessible_action_get_n_actions;
965 iface->get_description = fm_desktop_item_accessible_action_get_description;
966 iface->get_name = fm_desktop_item_accessible_action_get_name;
967 /* NOTE: we don't support descriptions change */
968 }
969
970 static const gchar *fm_desktop_item_accessible_image_get_image_description(AtkImage *image)
971 {
972 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(image);
973
974 /* FIXME: is there a better way to handle this? */
975 return fm_file_info_get_desc(item->item->fi);
976 }
977
978 static void fm_desktop_item_accessible_image_get_image_size(AtkImage *image,
979 gint *width,
980 gint *height)
981 {
982 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(image);
983
984 if (item->widget == NULL)
985 return;
986 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
987 return;
988
989 *width = item->item->icon_rect.width;
990 *height = item->item->icon_rect.height;
991 }
992
993 static void fm_desktop_item_accessible_image_get_image_position(AtkImage *image,
994 gint *x,
995 gint *y,
996 AtkCoordType coord_type)
997 {
998 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(image);
999
1000 if (item->widget == NULL)
1001 return;
1002 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
1003 return;
1004
1005 atk_component_get_position(ATK_COMPONENT(image), x, y, coord_type);
1006 *x += item->item->icon_rect.x - item->item->area.x;
1007 *y += item->item->icon_rect.y - item->item->area.y;
1008 }
1009
1010 static void atk_image_item_interface_init(AtkImageIface *iface)
1011 {
1012 iface->get_image_description = fm_desktop_item_accessible_image_get_image_description;
1013 /* NOTE: we don't support descriptions change */
1014 iface->get_image_size = fm_desktop_item_accessible_image_get_image_size;
1015 iface->get_image_position = fm_desktop_item_accessible_image_get_image_position;
1016 }
1017
1018 static AtkObject *fm_desktop_item_accessible_get_parent(AtkObject *obj)
1019 {
1020 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(obj);
1021
1022 return item->widget ? gtk_widget_get_accessible(item->widget) : NULL;
1023 }
1024
1025 static gint fm_desktop_item_accessible_get_index_in_parent(AtkObject *obj)
1026 {
1027 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(obj);
1028
1029 return item->widget ? fm_desktop_accessible_index(item->widget, item) : -1;
1030 }
1031
1032 static AtkStateSet *fm_desktop_item_accessible_ref_state_set(AtkObject *obj)
1033 {
1034 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(obj);
1035 FmDesktop *desktop;
1036
1037 g_return_val_if_fail(item->state_set, NULL);
1038
1039 /* update states first */
1040 if (item->widget != NULL)
1041 {
1042 desktop = (FmDesktop *)item->widget;
1043 if (desktop->focus == item->item)
1044 atk_state_set_add_state(item->state_set, ATK_STATE_FOCUSED);
1045 else
1046 atk_state_set_remove_state(item->state_set, ATK_STATE_FOCUSED);
1047 }
1048 if (item->item->is_selected)
1049 atk_state_set_add_state(item->state_set, ATK_STATE_SELECTED);
1050 else
1051 atk_state_set_remove_state(item->state_set, ATK_STATE_SELECTED);
1052 return g_object_ref(item->state_set);
1053 }
1054
1055 static void fm_desktop_item_accessible_class_init(FmDesktopItemAccessibleClass *klass)
1056 {
1057 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1058 AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
1059
1060 gobject_class->finalize = fm_desktop_item_accessible_finalize;
1061
1062 atk_class->get_index_in_parent = fm_desktop_item_accessible_get_index_in_parent;
1063 atk_class->get_parent = fm_desktop_item_accessible_get_parent;
1064 atk_class->ref_state_set = fm_desktop_item_accessible_ref_state_set;
1065 }
1066
1067 static gboolean fm_desktop_item_accessible_add_state(FmDesktopItemAccessible *item,
1068 AtkStateType state_type)
1069 {
1070 gboolean rc;
1071
1072 rc = atk_state_set_add_state(item->state_set, state_type);
1073 atk_object_notify_state_change(ATK_OBJECT (item), state_type, TRUE);
1074 /* If state_type is ATK_STATE_VISIBLE, additional notification */
1075 if (state_type == ATK_STATE_VISIBLE)
1076 g_signal_emit_by_name(item, "visible-data-changed");
1077 return rc;
1078 }
1079
1080 /* ---- accessible widget mirror ---- */
1081 typedef struct _FmDesktopAccessible FmDesktopAccessible;
1082 typedef struct _FmDesktopAccessibleClass FmDesktopAccessibleClass;
1083 #define FM_TYPE_DESKTOP_ACCESSIBLE (fm_desktop_accessible_get_type())
1084 #define FM_DESKTOP_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
1085 FM_TYPE_DESKTOP_ACCESSIBLE, FmDesktopAccessible))
1086 #define FM_IS_DESKTOP_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
1087 FM_TYPE_DESKTOP_ACCESSIBLE))
1088
1089 static GType fm_desktop_accessible_get_type (void);
1090
1091 typedef struct _FmDesktopAccessiblePriv FmDesktopAccessiblePriv;
1092 struct _FmDesktopAccessiblePriv
1093 {
1094 /* we don't catch model index but have own index in items list instead */
1095 GList *items;
1096 guint action_idle_handler;
1097 };
1098
1099 #define FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(_d_) G_TYPE_INSTANCE_GET_PRIVATE(_d_, FM_TYPE_DESKTOP_ACCESSIBLE, FmDesktopAccessiblePriv)
1100
1101 static void atk_component_interface_init(AtkComponentIface *iface);
1102 static void atk_selection_interface_init(AtkSelectionIface *iface);
1103 static void atk_action_interface_init(AtkActionIface *iface);
1104
1105 /* we cannot use G_DEFINE_TYPE_WITH_CODE - no GtkWindowAccessible before 3.2.0 */
1106 static void fm_desktop_accessible_init(FmDesktopAccessible *self);
1107 static void fm_desktop_accessible_class_init(FmDesktopAccessibleClass *klass);
1108 static gpointer fm_desktop_accessible_parent_class = NULL;
1109
1110 static void fm_desktop_accessible_class_intern_init(gpointer klass)
1111 {
1112 fm_desktop_accessible_parent_class = g_type_class_peek_parent(klass);
1113 fm_desktop_accessible_class_init((FmDesktopAccessibleClass*)klass);
1114 }
1115
1116 GType fm_desktop_accessible_get_type(void)
1117 {
1118 static volatile gsize type_id_volatile = 0;
1119
1120 if (g_once_init_enter(&type_id_volatile))
1121 {
1122 /*
1123 * Figure out the size of the class and instance
1124 * we are deriving from
1125 */
1126 AtkObjectFactory *factory;
1127 GTypeQuery query;
1128 GType derived_atk_type;
1129 GType g_define_type_id;
1130
1131 factory = atk_registry_get_factory(atk_get_default_registry(),
1132 g_type_parent(FM_TYPE_DESKTOP));
1133 derived_atk_type = atk_object_factory_get_accessible_type(factory);
1134 g_type_query(derived_atk_type, &query);
1135 g_define_type_id = g_type_register_static_simple(derived_atk_type,
1136 g_intern_static_string("FmDesktopAccessible"),
1137 query.class_size,
1138 (GClassInitFunc)fm_desktop_accessible_class_intern_init,
1139 query.instance_size,
1140 (GInstanceInitFunc)fm_desktop_accessible_init,
1141 0);
1142 G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT, atk_component_interface_init)
1143 G_IMPLEMENT_INTERFACE(ATK_TYPE_SELECTION, atk_selection_interface_init)
1144 G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION, atk_action_interface_init)
1145 g_once_init_leave(&type_id_volatile, g_define_type_id);
1146 }
1147 return type_id_volatile;
1148 }
1149
1150 static inline GList *fm_desktop_find_accessible_for_item(FmDesktopAccessiblePriv *priv, FmDesktopItem *item)
1151 {
1152 GList *items = priv->items;
1153
1154 while (items)
1155 {
1156 if (((FmDesktopItemAccessible *)items->data)->item == item)
1157 return items;
1158 items = items->next;
1159 }
1160 return NULL;
1161 }
1162
1163 /* widget interfaces */
1164 static AtkObject *fm_desktop_accessible_ref_accessible_at_point(AtkComponent *component,
1165 gint x, gint y,
1166 AtkCoordType coord_type)
1167 {
1168 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(component));
1169 FmDesktop *desktop;
1170 gint x_pos, y_pos;
1171 FmDesktopItem *item;
1172 GList *obj_l = NULL;
1173 GtkTreeIter it;
1174
1175 if (widget == NULL)
1176 return NULL;
1177 desktop = FM_DESKTOP(widget);
1178 atk_component_get_extents(component, &x_pos, &y_pos, NULL, NULL, coord_type);
1179 item = hit_test(desktop, &it, x - x_pos, y - y_pos);
1180 if (item)
1181 obj_l = fm_desktop_find_accessible_for_item(FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(component), item);
1182 if (obj_l)
1183 return g_object_ref(obj_l->data);
1184 return NULL;
1185 }
1186
1187 static void atk_component_interface_init(AtkComponentIface *iface)
1188 {
1189 iface->ref_accessible_at_point = fm_desktop_accessible_ref_accessible_at_point;
1190 }
1191
1192 static gboolean fm_desktop_accessible_add_selection(AtkSelection *selection, gint i)
1193 {
1194 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(selection));
1195 FmDesktop *desktop;
1196 FmDesktopAccessiblePriv *priv;
1197 FmDesktopItemAccessible *item;
1198
1199 if (widget == NULL)
1200 return FALSE;
1201
1202 desktop = FM_DESKTOP(widget);
1203 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1204 item = g_list_nth_data(priv->items, i);
1205 if (!item)
1206 return FALSE;
1207 item->item->is_selected = TRUE;
1208 redraw_item(desktop, item->item);
1209 atk_object_notify_state_change(ATK_OBJECT(item), ATK_STATE_SELECTED, TRUE);
1210 return TRUE;
1211 }
1212
1213 static gboolean fm_desktop_accessible_clear_selection(AtkSelection *selection)
1214 {
1215 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(selection));
1216
1217 if (widget == NULL)
1218 return FALSE;
1219
1220 _unselect_all(FM_FOLDER_VIEW(widget));
1221 return TRUE;
1222 }
1223
1224 static AtkObject *fm_desktop_accessible_ref_selection(AtkSelection *selection,
1225 gint i)
1226 {
1227 FmDesktopAccessiblePriv *priv;
1228 FmDesktopItemAccessible *item;
1229 GList *items;
1230
1231 if (i < 0)
1232 return NULL;
1233
1234 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1235 for (items = priv->items; items; items = items->next)
1236 {
1237 item = items->data;
1238 if (item->item->is_selected)
1239 if (i-- == 0)
1240 return g_object_ref(item);
1241 }
1242 return NULL;
1243 }
1244
1245 static gint fm_desktop_accessible_get_selection_count(AtkSelection *selection)
1246 {
1247 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1248 FmDesktopItemAccessible *item;
1249 GList *items;
1250 gint i = 0;
1251
1252 for (items = priv->items; items; items = items->next)
1253 {
1254 item = items->data;
1255 if (item->item->is_selected)
1256 i++;
1257 }
1258 return i;
1259 }
1260
1261 static gboolean fm_desktop_accessible_is_child_selected(AtkSelection *selection,
1262 gint i)
1263 {
1264 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1265 FmDesktopItemAccessible *item = g_list_nth_data(priv->items, i);
1266
1267 if (item == NULL)
1268 return FALSE;
1269 return item->item->is_selected;
1270 }
1271
1272 static gboolean fm_desktop_accessible_remove_selection(AtkSelection *selection,
1273 gint i)
1274 {
1275 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(selection));
1276 FmDesktop *desktop;
1277 FmDesktopAccessiblePriv *priv;
1278 FmDesktopItemAccessible *item;
1279 GList *items;
1280
1281 if (i < 0)
1282 return FALSE;
1283 if (widget == NULL)
1284 return FALSE;
1285 desktop = FM_DESKTOP(widget);
1286
1287 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1288 for (items = priv->items; items; items = items->next)
1289 {
1290 item = items->data;
1291 if (item->item->is_selected)
1292 if (i-- == 0)
1293 {
1294 item->item->is_selected = FALSE;
1295 redraw_item(desktop, item->item);
1296 atk_object_notify_state_change(ATK_OBJECT(item), ATK_STATE_SELECTED, FALSE);
1297 return TRUE;
1298 }
1299 }
1300 return FALSE;
1301 }
1302
1303 static gboolean fm_desktop_accessible_select_all_selection(AtkSelection *selection)
1304 {
1305 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(selection));
1306
1307 if (widget == NULL)
1308 return FALSE;
1309
1310 _select_all(FM_FOLDER_VIEW(widget));
1311 return TRUE;
1312 }
1313
1314 static void atk_selection_interface_init(AtkSelectionIface *iface)
1315 {
1316 iface->add_selection = fm_desktop_accessible_add_selection;
1317 iface->clear_selection = fm_desktop_accessible_clear_selection;
1318 iface->ref_selection = fm_desktop_accessible_ref_selection;
1319 iface->get_selection_count = fm_desktop_accessible_get_selection_count;
1320 iface->is_child_selected = fm_desktop_accessible_is_child_selected;
1321 iface->remove_selection = fm_desktop_accessible_remove_selection;
1322 iface->select_all_selection = fm_desktop_accessible_select_all_selection;
1323 }
1324
1325 static gboolean fm_desktop_accessible_idle_do_action(gpointer data)
1326 {
1327 GtkWidget *widget;
1328 FmDesktopAccessiblePriv *priv;
1329
1330 if(g_source_is_destroyed(g_main_current_source()))
1331 return FALSE;
1332
1333 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(data);
1334 priv->action_idle_handler = 0;
1335 widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(data));
1336 if (widget)
1337 fm_folder_view_item_clicked(FM_FOLDER_VIEW(widget), NULL, FM_FV_CONTEXT_MENU);
1338 return FALSE;
1339 }
1340
1341 static gboolean fm_desktop_accessible_action_do_action(AtkAction *action, gint i)
1342 {
1343 FmDesktopAccessiblePriv *priv;
1344
1345 if (i != 0)
1346 return FALSE;
1347 if (gtk_accessible_get_widget(GTK_ACCESSIBLE(action)) == NULL)
1348 return FALSE;
1349
1350 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(action);
1351 if (!priv->action_idle_handler)
1352 priv->action_idle_handler = gdk_threads_add_idle(fm_desktop_accessible_idle_do_action, action);
1353 return TRUE;
1354 }
1355
1356 static gint fm_desktop_accessible_action_get_n_actions(AtkAction *action)
1357 {
1358 return 1;
1359 }
1360
1361 static const gchar *fm_desktop_accessible_action_get_description(AtkAction *action, gint i)
1362 {
1363 if (i == 0)
1364 return _("Show desktop menu");
1365 return NULL;
1366 }
1367
1368 static const gchar *fm_desktop_accessible_action_get_name(AtkAction *action, gint i)
1369 {
1370 if (i == 0)
1371 return "menu";
1372 return NULL;
1373 }
1374
1375 static void atk_action_interface_init(AtkActionIface *iface)
1376 {
1377 iface->do_action = fm_desktop_accessible_action_do_action;
1378 iface->get_n_actions = fm_desktop_accessible_action_get_n_actions;
1379 iface->get_description = fm_desktop_accessible_action_get_description;
1380 iface->get_name = fm_desktop_accessible_action_get_name;
1381 /* NOTE: we don't support descriptions change */
1382 }
1383
1384 static void fm_desktop_accessible_finalize(GObject *object)
1385 {
1386 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(object);
1387 FmDesktopItemAccessible *item;
1388
1389 /* FIXME: should we g_assert(priv->items) here instead? */
1390 while (priv->items)
1391 {
1392 item = priv->items->data;
1393 item->item = NULL;
1394 fm_desktop_item_accessible_add_state(item, ATK_STATE_DEFUNCT);
1395 g_signal_emit_by_name(item, "children-changed::remove", 0, NULL, NULL);
1396 priv->items = g_list_remove_link(priv->items, priv->items);
1397 g_object_unref(item);
1398 }
1399 if (priv->action_idle_handler)
1400 {
1401 g_source_remove(priv->action_idle_handler);
1402 priv->action_idle_handler = 0;
1403 }
1404 G_OBJECT_CLASS(fm_desktop_accessible_parent_class)->finalize(object);
1405 }
1406
1407 static gint fm_desktop_accessible_get_n_children(AtkObject *accessible)
1408 {
1409 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(accessible);
1410
1411 return g_list_length(priv->items);
1412 }
1413
1414 static AtkObject *fm_desktop_accessible_ref_child(AtkObject *accessible,
1415 gint index)
1416 {
1417 FmDesktopAccessiblePriv *priv;
1418 FmDesktopItemAccessible *item;
1419
1420 if (index < 0)
1421 return NULL;
1422
1423 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(accessible);
1424 item = g_list_nth_data(priv->items, index);
1425 if (!item)
1426 return NULL;
1427 return g_object_ref(item);
1428 }
1429
1430 static void fm_desktop_accessible_initialize(AtkObject *accessible, gpointer data)
1431 {
1432 if (ATK_OBJECT_CLASS(fm_desktop_accessible_parent_class)->initialize)
1433 ATK_OBJECT_CLASS(fm_desktop_accessible_parent_class)->initialize(accessible, data);
1434 atk_object_set_role(accessible, ATK_ROLE_WINDOW);
1435 /* FIXME: set name by monitor */
1436 atk_object_set_name(accessible, _("Desktop"));
1437 }
1438
1439 static void fm_desktop_accessible_init(FmDesktopAccessible *object)
1440 {
1441 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(object);
1442
1443 priv->items = NULL;
1444 }
1445
1446 static void fm_desktop_accessible_class_init(FmDesktopAccessibleClass *klass)
1447 {
1448 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1449 AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
1450
1451 gobject_class->finalize = fm_desktop_accessible_finalize;
1452
1453 atk_class->get_n_children = fm_desktop_accessible_get_n_children;
1454 atk_class->ref_child = fm_desktop_accessible_ref_child;
1455 atk_class->initialize = fm_desktop_accessible_initialize;
1456
1457 g_type_class_add_private(klass, sizeof(FmDesktopAccessiblePriv));
1458 }
1459
1460 /* ---- interface implementation ---- */
1461 /* handy ATK support is added only in 3.2.0 so we should handle it manually */
1462 static GType fm_desktop_accessible_factory_get_type (void);
1463
1464 typedef struct {
1465 AtkObjectFactory parent;
1466 } FmDesktopAccessibleFactory;
1467
1468 typedef struct {
1469 AtkObjectFactoryClass parent_class;
1470 } FmDesktopAccessibleFactoryClass;
1471
1472 G_DEFINE_TYPE_WITH_CODE(FmDesktopAccessibleFactory, fm_desktop_accessible_factory, ATK_TYPE_OBJECT_FACTORY,
1473 atk_registry_set_factory_type(atk_get_default_registry(),
1474 FM_TYPE_DESKTOP,
1475 g_define_type_id); )
1476
1477 static GType fm_desktop_accessible_factory_get_accessible_type(void)
1478 {
1479 return FM_TYPE_DESKTOP_ACCESSIBLE;
1480 }
1481
1482 static AtkObject *fm_desktop_accessible_factory_create_accessible(GObject *object)
1483 {
1484 AtkObject *accessible;
1485
1486 g_return_val_if_fail(FM_IS_DESKTOP(object), NULL);
1487
1488 accessible = g_object_new(FM_TYPE_DESKTOP_ACCESSIBLE, NULL);
1489 atk_object_initialize(accessible, object);
1490 return accessible;
1491 }
1492
1493 static void fm_desktop_accessible_factory_class_init(FmDesktopAccessibleFactoryClass *klass)
1494 {
1495 AtkObjectFactoryClass *factory_class = (AtkObjectFactoryClass *)klass;
1496
1497 factory_class->create_accessible = fm_desktop_accessible_factory_create_accessible;
1498 factory_class->get_accessible_type = fm_desktop_accessible_factory_get_accessible_type;
1499 }
1500
1501 static void fm_desktop_accessible_factory_init(FmDesktopAccessibleFactory *factory)
1502 {
1503 }
1504
1505 static AtkObject *fm_desktop_get_accessible(GtkWidget *widget)
1506 {
1507 fm_desktop_accessible_factory_get_type(); /* just to activate it */
1508 return GTK_WIDGET_CLASS(fm_desktop_parent_class)->get_accessible(widget);
1509 }
1510
1511 static inline gint fm_desktop_accessible_index(GtkWidget *desktop, gpointer item)
1512 {
1513 AtkObject *desktop_atk = gtk_widget_get_accessible(desktop);
1514 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(desktop_atk);
1515
1516 return g_list_index(priv->items, item);
1517 }
1518
1519 static void fm_desktop_accessible_item_deleted(FmDesktop *desktop, FmDesktopItem *item)
1520 {
1521 AtkObject *obj;
1522 FmDesktopAccessiblePriv *priv;
1523 GList *item_atk_l;
1524 FmDesktopItemAccessible *item_atk;
1525 gint index;
1526
1527 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1528 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1529 {
1530 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1531 item_atk_l = fm_desktop_find_accessible_for_item(priv, item);
1532 g_return_if_fail(item_atk_l != NULL);
1533 item_atk = item_atk_l->data;
1534 item_atk->item = NULL;
1535 index = g_list_position(priv->items, item_atk_l);
1536 fm_desktop_item_accessible_add_state(item_atk, ATK_STATE_DEFUNCT);
1537 g_signal_emit_by_name(obj, "children-changed::remove", index, NULL, NULL);
1538 priv->items = g_list_remove_link(priv->items, item_atk_l);
1539 g_object_unref(item_atk);
1540 }
1541 }
1542
1543 static void fm_desktop_accessible_item_added(FmDesktop *desktop, FmDesktopItem *item,
1544 guint index)
1545 {
1546 AtkObject *obj;
1547 FmDesktopAccessiblePriv *priv;
1548 FmDesktopItemAccessible *item_atk;
1549
1550 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1551 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1552 {
1553 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1554 item_atk = fm_desktop_item_accessible_new(desktop, item);
1555 g_warn_if_fail(index <= g_list_length(priv->items));
1556 priv->items = g_list_insert(priv->items, g_object_ref(item_atk), index);
1557 g_signal_emit_by_name(obj, "children-changed::add", index, NULL, NULL);
1558 }
1559 }
1560
1561 static void fm_desktop_accessible_items_reordered(FmDesktop *desktop,
1562 GtkTreeModel *model,
1563 gint *new_order)
1564 {
1565 AtkObject *obj;
1566 FmDesktopAccessiblePriv *priv;
1567 GList *new_list = NULL;
1568 int length, i;
1569
1570 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1571 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1572 {
1573 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1574 length = gtk_tree_model_iter_n_children(model, NULL);
1575 g_return_if_fail(length == (gint)g_list_length(priv->items));
1576 for (i = 0; i < length; i++)
1577 {
1578 g_assert(new_order[i] >= 0 && new_order[i] < length);
1579 new_list = g_list_prepend(new_list,
1580 g_list_nth_data(priv->items, new_order[i]));
1581 }
1582 g_list_free(priv->items);
1583 priv->items = g_list_reverse(new_list);
1584 }
1585 }
1586
1587 static void fm_desktop_item_selected_changed(FmDesktop *desktop, FmDesktopItem *item)
1588 {
1589 AtkObject *obj;
1590 FmDesktopAccessiblePriv *priv;
1591 GList *item_atk_l;
1592
1593 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1594 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1595 {
1596 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1597 item_atk_l = fm_desktop_find_accessible_for_item(priv, item);
1598 g_return_if_fail(item_atk_l != NULL);
1599 atk_object_notify_state_change(item_atk_l->data, ATK_STATE_SELECTED,
1600 item->is_selected);
1601 }
1602 }
1603
1604 static void fm_desktop_accessible_focus_set(FmDesktop *desktop, FmDesktopItem *item)
1605 {
1606 AtkObject *obj;
1607 FmDesktopAccessiblePriv *priv;
1608 GList *item_atk_l;
1609
1610 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1611 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1612 {
1613 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1614 item_atk_l = fm_desktop_find_accessible_for_item(priv, item);
1615 g_return_if_fail(item_atk_l != NULL);
1616 atk_object_notify_state_change(item_atk_l->data, ATK_STATE_FOCUSED, TRUE);
1617 }
1618 }
1619
1620 static void fm_desktop_accessible_focus_unset(FmDesktop *desktop, FmDesktopItem *item)
1621 {
1622 AtkObject *obj;
1623 FmDesktopAccessiblePriv *priv;
1624 GList *item_atk_l;
1625
1626 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1627 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1628 {
1629 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1630 item_atk_l = fm_desktop_find_accessible_for_item(priv, item);
1631 g_return_if_fail(item_atk_l != NULL);
1632 atk_object_notify_state_change(item_atk_l->data, ATK_STATE_FOCUSED, FALSE);
1633 }
1634 }
1635
1636 static void fm_desktop_accessible_model_removed(FmDesktop *desktop)
1637 {
1638 AtkObject *obj;
1639 FmDesktopAccessiblePriv *priv;
1640 FmDesktopItemAccessible *item_atk;
1641
1642 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1643 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1644 {
1645 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1646 while (priv->items)
1647 {
1648 item_atk = priv->items->data;
1649 item_atk->item = NULL;
1650 fm_desktop_item_accessible_add_state(item_atk, ATK_STATE_DEFUNCT);
1651 g_signal_emit_by_name(obj, "children-changed::remove", 0, NULL, NULL);
1652 priv->items = g_list_remove_link(priv->items, priv->items);
1653 g_object_unref(item_atk);
1654 }
1655 }
1656 }
1657
1658
1659 /* ---------------------------------------------------------------------
1660 Desktop drawing */
1661
1662 static inline void get_item_rect(FmDesktopItem* item, GdkRectangle* rect)
1663 {
1664 gdk_rectangle_union(&item->icon_rect, &item->text_rect, rect);
1665 }
1666
1667 static gboolean is_pos_occupied(FmDesktop* desktop, FmDesktopItem* item)
1668 {
1669 GList* l;
1670 for(l = desktop->fixed_items; l; l=l->next)
1671 {
1672 FmDesktopItem* fixed = (FmDesktopItem*)l->data;
1673 GdkRectangle rect;
1674 get_item_rect(fixed, &rect);
1675 if(gdk_rectangle_intersect(&rect, &item->icon_rect, NULL)
1676 ||gdk_rectangle_intersect(&rect, &item->text_rect, NULL))
1677 return TRUE;
1678 }
1679 return FALSE;
1680 }
1681
1682 static void layout_items(FmDesktop* self)
1683 {
1684 FmDesktopItem* item;
1685 GtkTreeModel* model = self->model ? GTK_TREE_MODEL(self->model) : NULL;
1686 GdkPixbuf* icon;
1687 GtkTreeIter it;
1688 int x, y, bottom;
1689 GtkTextDirection direction = gtk_widget_get_direction(GTK_WIDGET(self));
1690
1691 y = self->ymargin;
1692 bottom = self->working_area.height - self->ymargin;
1693
1694 if(!model || !gtk_tree_model_get_iter_first(model, &it))
1695 {
1696 gtk_widget_queue_draw(GTK_WIDGET(self));
1697 return;
1698 }
1699 if(direction != GTK_TEXT_DIR_RTL) /* LTR or NONE */
1700 {
1701 x = self->xmargin;
1702 do
1703 {
1704 item = fm_folder_model_get_item_userdata(self->model, &it);
1705 icon = NULL;
1706 gtk_tree_model_get(model, &it, FM_FOLDER_MODEL_COL_ICON, &icon, -1);
1707 if(item->fixed_pos)
1708 calc_item_size(self, item, icon);
1709 else
1710 {
1711 _next_position:
1712 item->area.x = self->working_area.x + x;
1713 item->area.y = self->working_area.y + y;
1714 calc_item_size(self, item, icon);
1715 while (y < item->area.y + item->area.height)
1716 y += self->cell_h;
1717 if(y > bottom)
1718 {
1719 x += self->cell_w;
1720 y = self->ymargin;
1721 }
1722 /* check if this position is occupied by a fixed item */
1723 /* or its height does not fit into space that left */
1724 if(item->area.y + item->area.height > bottom ||
1725 is_pos_occupied(self, item))
1726 goto _next_position;
1727 }
1728 if(icon)
1729 g_object_unref(icon);
1730 }
1731 while(gtk_tree_model_iter_next(model, &it));
1732 }
1733 else /* RTL */
1734 {
1735 x = self->working_area.width - self->xmargin - self->cell_w;
1736 do
1737 {
1738 item = fm_folder_model_get_item_userdata(self->model, &it);
1739 icon = NULL;
1740 gtk_tree_model_get(model, &it, FM_FOLDER_MODEL_COL_ICON, &icon, -1);
1741 if(item->fixed_pos)
1742 calc_item_size(self, item, icon);
1743 else
1744 {
1745 _next_position_rtl:
1746 item->area.x = self->working_area.x + x;
1747 item->area.y = self->working_area.y + y;
1748 calc_item_size(self, item, icon);
1749 while (y < item->area.y + item->area.height)
1750 y += self->cell_h;
1751 if(y > bottom)
1752 {
1753 x -= self->cell_w;
1754 y = self->ymargin;
1755 }
1756 /* check if this position is occupied by a fixed item */
1757 /* or its height does not fit into space that left */
1758 if(item->area.y + item->area.height > bottom ||
1759 is_pos_occupied(self, item))
1760 goto _next_position_rtl;
1761 }
1762 if(icon)
1763 g_object_unref(icon);
1764 }
1765 while(gtk_tree_model_iter_next(model, &it));
1766 }
1767 gtk_widget_queue_draw(GTK_WIDGET(self));
1768 }
1769
1770 static gboolean on_idle_layout(FmDesktop* desktop)
1771 {
1772 desktop->idle_layout = 0;
1773 layout_items(desktop);
1774 return FALSE;
1775 }
1776
1777 static void queue_layout_items(FmDesktop* desktop)
1778 {
1779 if(0 == desktop->idle_layout)
1780 desktop->idle_layout = gdk_threads_add_idle((GSourceFunc)on_idle_layout, desktop);
1781 }
1782
1783 static void paint_item(FmDesktop* self, FmDesktopItem* item, cairo_t* cr, GdkRectangle* expose_area, GdkPixbuf* icon)
1784 {
1785 #if GTK_CHECK_VERSION(3, 0, 0)
1786 GtkStyleContext* style;
1787 #else
1788 GtkStyle* style;
1789 #endif
1790 GtkWidget* widget = (GtkWidget*)self;
1791 GtkCellRendererState state = 0;
1792 #if GTK_CHECK_VERSION(3, 0, 0)
1793 GdkRGBA rgba;
1794 #else
1795 GdkWindow* window;
1796 #endif
1797 int text_x, text_y;
1798
1799 #if GTK_CHECK_VERSION(3, 0, 0)
1800 style = gtk_widget_get_style_context(widget);
1801 #else
1802 style = gtk_widget_get_style(widget);
1803 window = gtk_widget_get_window(widget);
1804 #endif
1805
1806 pango_layout_set_text(self->pl, NULL, 0);
1807 pango_layout_set_width(self->pl, self->pango_text_w);
1808 pango_layout_set_height(self->pl, self->pango_text_h);
1809
1810 pango_layout_set_text(self->pl, fm_file_info_get_disp_name(item->fi), -1);
1811
1812 /* FIXME: do we need to cache this? */
1813 text_x = item->area.x + (self->cell_w - self->text_w)/2 + 2;
1814 text_y = item->icon_rect.y + item->icon_rect.height + 2;
1815
1816 if(item->is_selected || item == self->drop_hilight) /* draw background for text label */
1817 {
1818 state = GTK_CELL_RENDERER_SELECTED;
1819
1820 cairo_save(cr);
1821 gdk_cairo_rectangle(cr, &item->text_rect);
1822 #if GTK_CHECK_VERSION(3, 0, 0)
1823 gtk_style_context_get_background_color(style, GTK_STATE_FLAG_SELECTED, &rgba);
1824 gdk_cairo_set_source_rgba(cr, &rgba);
1825 #else
1826 gdk_cairo_set_source_color(cr, &style->bg[GTK_STATE_SELECTED]);
1827 #endif
1828 cairo_clip(cr);
1829 cairo_paint(cr);
1830 cairo_restore(cr);
1831 #if GTK_CHECK_VERSION(3, 0, 0)
1832 gtk_style_context_get_color(style, GTK_STATE_FLAG_SELECTED, &rgba);
1833 gdk_cairo_set_source_rgba(cr, &rgba);
1834 #else
1835 gdk_cairo_set_source_color(cr, &style->fg[GTK_STATE_SELECTED]);
1836 #endif
1837 }
1838 else
1839 {
1840 /* the shadow */
1841 gdk_cairo_set_source_color(cr, &self->conf.desktop_shadow);
1842 cairo_move_to(cr, text_x + 1, text_y + 1);
1843 pango_cairo_show_layout(cr, self->pl);
1844 gdk_cairo_set_source_color(cr, &self->conf.desktop_fg);
1845 }
1846 /* real text */
1847 cairo_move_to(cr, text_x, text_y);
1848 /* FIXME: should we check if pango is 1.10 at least? */
1849 pango_cairo_show_layout(cr, self->pl);
1850 pango_layout_set_text(self->pl, NULL, 0);
1851
1852 if(item == self->focus && gtk_widget_has_focus(widget))
1853 #if GTK_CHECK_VERSION(3, 0, 0)
1854 gtk_render_focus(style, cr,
1855 #else
1856 gtk_paint_focus(style, window, gtk_widget_get_state(widget),
1857 expose_area, widget, "icon_view",
1858 #endif
1859 item->text_rect.x, item->text_rect.y, item->text_rect.width, item->text_rect.height);
1860
1861 if(item == self->hover_item) /* hovered */
1862 g_object_set(G_OBJECT(self), "tooltip-text", fm_file_info_get_disp_name(item->fi), NULL);
1863 else
1864 g_object_set(G_OBJECT(self), "tooltip-text", NULL, NULL);
1865
1866 /* draw the icon */
1867 g_object_set(self->icon_render, "pixbuf", icon, "info", item->fi, NULL);
1868 #if GTK_CHECK_VERSION(3, 0, 0)
1869 gtk_cell_renderer_render(GTK_CELL_RENDERER(self->icon_render), cr, widget, &item->icon_rect, &item->icon_rect, state);
1870 #else
1871 gtk_cell_renderer_render(GTK_CELL_RENDERER(self->icon_render), window, widget, &item->icon_rect, &item->icon_rect, expose_area, state);
1872 #endif
1873 }
1874
1875 static void redraw_item(FmDesktop* desktop, FmDesktopItem* item)
1876 {
1877 GdkRectangle rect;
1878 gdk_rectangle_union(&item->icon_rect, &item->text_rect, &rect);
1879 --rect.x;
1880 --rect.y;
1881 rect.width += 2;
1882 rect.height += 2;
1883 gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(desktop)), &rect, FALSE);
1884 }
1885
1886 static void move_item(FmDesktop* desktop, FmDesktopItem* item, int x, int y, gboolean redraw)
1887 {
1888 int dx, dy;
1889 /* this call invalid the area occupied by the item and a redraw
1890 * is queued. */
1891 if(redraw)
1892 redraw_item(desktop, item);
1893
1894 /* correct coords to put item within working area still */
1895 if (x > desktop->working_area.width - desktop->xmargin - item->area.width)
1896 x = desktop->working_area.width - desktop->xmargin - item->area.width;
1897 if (x < desktop->xmargin)
1898 x = desktop->xmargin;
1899 if (y > desktop->working_area.height - desktop->ymargin - item->area.height)
1900 y = desktop->working_area.height - desktop->ymargin - item->area.height;
1901 if (y < desktop->ymargin)
1902 y = desktop->ymargin;
1903
1904 dx = x - item->area.x;
1905 dy = y - item->area.y;
1906
1907 item->area.x = x;
1908 item->area.y = y;
1909
1910 /* calc_item_size(desktop, item); */
1911 item->icon_rect.x += dx;
1912 item->icon_rect.y += dy;
1913 item->text_rect.x += dx;
1914 item->text_rect.y += dy;
1915
1916 /* make the item use customized fixed position. */
1917 if(!item->fixed_pos)
1918 {
1919 item->fixed_pos = TRUE;
1920 desktop->fixed_items = g_list_prepend(desktop->fixed_items, item);
1921 }
1922
1923 /* move the item to a new place, and queue a redraw for the new rect. */
1924 if(redraw)
1925 redraw_item(desktop, item);
1926 }
1927
1928 static void calc_rubber_banding_rect(FmDesktop* self, int x, int y, GdkRectangle* rect)
1929 {
1930 int x1, x2, y1, y2;
1931 if(self->drag_start_x < x)
1932 {
1933 x1 = self->drag_start_x;
1934 x2 = x;
1935 }
1936 else
1937 {
1938 x1 = x;
1939 x2 = self->drag_start_x;
1940 }
1941
1942 if(self->drag_start_y < y)
1943 {
1944 y1 = self->drag_start_y;
1945 y2 = y;
1946 }
1947 else
1948 {
1949 y1 = y;
1950 y2 = self->drag_start_y;
1951 }
1952
1953 rect->x = x1;
1954 rect->y = y1;
1955 rect->width = x2 - x1;
1956 rect->height = y2 - y1;
1957 }
1958
1959 static void update_rubberbanding(FmDesktop* self, int newx, int newy)
1960 {
1961 GtkTreeModel* model = self->model ? GTK_TREE_MODEL(self->model) : NULL;
1962 GtkTreeIter it;
1963 GdkRectangle old_rect, new_rect;
1964 //GdkRegion *region;
1965 GdkWindow *window;
1966
1967 window = gtk_widget_get_window(GTK_WIDGET(self));
1968
1969 calc_rubber_banding_rect(self, self->rubber_bending_x, self->rubber_bending_y, &old_rect);
1970 calc_rubber_banding_rect(self, newx, newy, &new_rect);
1971
1972 gdk_window_invalidate_rect(window, &old_rect, FALSE);
1973 gdk_window_invalidate_rect(window, &new_rect, FALSE);
1974 // gdk_window_clear_area(((GtkWidget*)self)->window, new_rect.x, new_rect.y, new_rect.width, new_rect.height);
1975 /*
1976 region = gdk_region_rectangle(&old_rect);
1977 gdk_region_union_with_rect(region, &new_rect);
1978
1979 // gdk_window_invalidate_region(((GtkWidget*)self)->window, &region, TRUE);
1980
1981 gdk_region_destroy(region);
1982 */
1983 self->rubber_bending_x = newx;
1984 self->rubber_bending_y = newy;
1985
1986 /* update selection */
1987 if(model && gtk_tree_model_get_iter_first(model, &it)) do
1988 {
1989 FmDesktopItem* item = fm_folder_model_get_item_userdata(self->model, &it);
1990 gboolean selected;
1991 if(gdk_rectangle_intersect(&new_rect, &item->icon_rect, NULL) ||
1992 gdk_rectangle_intersect(&new_rect, &item->text_rect, NULL))
1993 selected = TRUE;
1994 else
1995 selected = FALSE;
1996
1997 /* we cannot compare booleans, TRUE may be 1 or -1 */
1998 if ((item->is_rubber_banded && !selected) ||
1999 (!item->is_rubber_banded && selected))
2000 {
2001 item->is_selected = selected;
2002 redraw_item(self, item);
2003 fm_desktop_item_selected_changed(self, item);
2004 }
2005 item->is_rubber_banded = self->rubber_bending && selected;
2006 }
2007 while(gtk_tree_model_iter_next(model, &it));
2008 }
2009
2010
2011 static void paint_rubber_banding_rect(FmDesktop* self, cairo_t* cr, GdkRectangle* expose_area)
2012 {
2013 GtkWidget* widget = (GtkWidget*)self;
2014 GdkRectangle rect;
2015 GdkColor clr;
2016 guchar alpha;
2017
2018 calc_rubber_banding_rect(self, self->rubber_bending_x, self->rubber_bending_y, &rect);
2019
2020 if(rect.width <= 0 || rect.height <= 0)
2021 return;
2022
2023 if(!gdk_rectangle_intersect(expose_area, &rect, &rect))
2024 return;
2025 /*
2026 gtk_widget_style_get(icon_view,
2027 "selection-box-color", &clr,
2028 "selection-box-alpha", &alpha,
2029 NULL);
2030 */
2031 clr = gtk_widget_get_style (widget)->base[GTK_STATE_SELECTED];
2032 alpha = 64; /* FIXME: should be themable in the future */
2033
2034 cairo_save(cr);
2035 cairo_set_source_rgba(cr, (gdouble)clr.red/65535, (gdouble)clr.green/65536, (gdouble)clr.blue/65535, (gdouble)alpha/100);
2036 gdk_cairo_rectangle(cr, &rect);
2037 cairo_clip (cr);
2038 cairo_paint (cr);
2039 gdk_cairo_set_source_color(cr, &clr);
2040 cairo_rectangle (cr, rect.x + 0.5, rect.y + 0.5, rect.width - 1, rect.height - 1);
2041 cairo_stroke(cr);
2042 cairo_restore(cr);
2043 }
2044
2045 static void update_background(FmDesktop* desktop, int is_it)
2046 {
2047 GtkWidget* widget = (GtkWidget*)desktop;
2048 GdkPixbuf* pix, *scaled;
2049 cairo_t* cr;
2050 GdkScreen *screen = gtk_widget_get_screen(widget);
2051 GdkWindow* root = gdk_screen_get_root_window(screen);
2052 GdkWindow *window = gtk_widget_get_window(widget);
2053 FmBackgroundCache *cache;
2054 #if GTK_CHECK_VERSION(3, 0, 0)
2055 cairo_pattern_t *pattern;
2056 #endif
2057
2058 Display* xdisplay;
2059 Pixmap xpixmap;
2060 Window xroot;
2061 int screen_num = gdk_screen_get_number(screen);
2062
2063 char *wallpaper;
2064
2065 if (!desktop->conf.wallpaper_common)
2066 {
2067 guint32 cur_desktop = desktop->cur_desktop;
2068
2069 if(is_it >= 0) /* signal "changed::wallpaper" */
2070 {
2071 wallpaper = desktop->conf.wallpaper;
2072 if((gint)cur_desktop >= desktop->conf.wallpapers_configured)
2073 {
2074 int i;
2075
2076 desktop->conf.wallpapers = g_renew(char *, desktop->conf.wallpapers, cur_desktop + 1);
2077 /* fill the gap with current wallpaper */
2078 for(i = MAX(desktop->conf.wallpapers_configured,0); i < (int)cur_desktop; i++)
2079 desktop->conf.wallpapers[i] = g_strdup(wallpaper);
2080 desktop->conf.wallpapers[cur_desktop] = NULL;
2081 desktop->conf.wallpapers_configured = cur_desktop + 1;
2082 }
2083 g_free(desktop->conf.wallpapers[cur_desktop]);
2084 desktop->conf.wallpapers[cur_desktop] = g_strdup(wallpaper);
2085 }
2086 else /* desktop refresh */
2087 {
2088 if((gint)cur_desktop < desktop->conf.wallpapers_configured)
2089 wallpaper = desktop->conf.wallpapers[cur_desktop];
2090 else
2091 wallpaper = NULL;
2092 if (wallpaper == NULL && desktop->conf.wallpaper != NULL)
2093 {
2094 /* if we have wallpaper set for previous desktop but have not
2095 for current one, it may mean one of two cases:
2096 - we expanded number of desktops;
2097 - we recently switched wallpaper_common off.
2098 If we selected to use wallpaper image but current desktop
2099 has no image set (i.e. one of cases above is happening),
2100 it is reasonable and correct to use last selected image for
2101 newly selected desktop instead of show plain color on it */
2102 wallpaper = desktop->conf.wallpaper;
2103 if ((gint)cur_desktop < desktop->conf.wallpapers_configured)
2104 /* this means desktop->conf.wallpapers[cur_desktop] is NULL,
2105 see above, we have to update it too in this case */
2106 desktop->conf.wallpapers[cur_desktop] = g_strdup(wallpaper);
2107 }
2108 else
2109 {
2110 g_free(desktop->conf.wallpaper); /* update to current desktop */
2111 desktop->conf.wallpaper = g_strdup(wallpaper);
2112 }
2113 }
2114 }
2115 else
2116 wallpaper = desktop->conf.wallpaper;
2117
2118 if(desktop->conf.wallpaper_mode != FM_WP_COLOR && wallpaper && *wallpaper)
2119 {
2120 struct stat st; /* for mtime */
2121
2122 /* bug #3613571 - replacing the file will not affect the desktop
2123 we will call stat on each desktop change but it's inevitable */
2124 if (stat(wallpaper, &st) < 0)
2125 st.st_mtime = 0;
2126 for(cache = desktop->cache; cache; cache = cache->next)
2127 if(strcmp(wallpaper, cache->filename) == 0)
2128 break;
2129 if(cache && cache->wallpaper_mode == desktop->conf.wallpaper_mode
2130 && st.st_mtime == cache->mtime)
2131 pix = NULL; /* no new pix for it */
2132 else if((pix = gdk_pixbuf_new_from_file(wallpaper, NULL)))
2133 {
2134 if(cache)
2135 {
2136 /* the same file but mode was changed */
2137 #if GTK_CHECK_VERSION(3, 0, 0)
2138 XFreePixmap(cairo_xlib_surface_get_display(cache->bg),
2139 cairo_xlib_surface_get_drawable(cache->bg));
2140 cairo_surface_destroy(cache->bg);
2141 #else
2142 g_object_unref(cache->bg);
2143 #endif
2144 cache->bg = NULL;
2145 }
2146 else if(desktop->cache)
2147 {
2148 for(cache = desktop->cache; cache->next; )
2149 cache = cache->next;
2150 cache->next = g_new0(FmBackgroundCache, 1);
2151 cache = cache->next;
2152 }
2153 else
2154 desktop->cache = cache = g_new0(FmBackgroundCache, 1);
2155 if(!cache->filename)
2156 cache->filename = g_strdup(wallpaper);
2157 cache->mtime = st.st_mtime;
2158 g_debug("adding new FmBackgroundCache for %s", wallpaper);
2159 }
2160 else
2161 /* if there is a cached image but with another mode and we cannot
2162 get it from file for new mode then just leave it in cache as is */
2163 cache = NULL;
2164 }
2165 else
2166 cache = NULL;
2167
2168 if(!cache) /* solid color only */
2169 {
2170 #if GTK_CHECK_VERSION(3, 0, 0)
2171 pattern = cairo_pattern_create_rgb(desktop->conf.desktop_bg.red / 65535.0,
2172 desktop->conf.desktop_bg.green / 65535.0,
2173 desktop->conf.desktop_bg.blue / 65535.0);
2174 gdk_window_set_background_pattern(window, pattern);
2175 cairo_pattern_destroy(pattern);
2176 #else
2177 GdkColor bg = desktop->conf.desktop_bg;
2178
2179 gdk_colormap_alloc_color(gdk_drawable_get_colormap(window), &bg, FALSE, TRUE);
2180 gdk_window_set_back_pixmap(window, NULL, FALSE);
2181 gdk_window_set_background(window, &bg);
2182 #endif
2183 gdk_window_invalidate_rect(window, NULL, TRUE);
2184 return;
2185 }
2186
2187 if(!cache->bg) /* no cached image found */
2188 {
2189 int src_w, src_h;
2190 int dest_w, dest_h;
2191 int x = 0, y = 0;
2192 src_w = gdk_pixbuf_get_width(pix);
2193 src_h = gdk_pixbuf_get_height(pix);
2194 if(desktop->conf.wallpaper_mode == FM_WP_TILE)
2195 {
2196 dest_w = src_w;
2197 dest_h = src_h;
2198 }
2199 else
2200 {
2201 GdkRectangle geom;
2202 gdk_screen_get_monitor_geometry(screen, desktop->monitor, &geom);
2203 if (desktop->conf.wallpaper_mode == FM_WP_SCREEN)
2204 {
2205 dest_w = gdk_screen_get_width(screen);
2206 dest_h = gdk_screen_get_height(screen);
2207 x = -geom.x;
2208 y = -geom.y;
2209 }
2210 else
2211 {
2212 dest_w = geom.width;
2213 dest_h = geom.height;
2214 }
2215 }
2216 #if GTK_CHECK_VERSION(3, 0, 0)
2217 xdisplay = GDK_WINDOW_XDISPLAY(root);
2218 /* this code is taken from libgnome-desktop */
2219 xpixmap = XCreatePixmap(xdisplay, RootWindow(xdisplay, screen_num),
2220 dest_w, dest_h, DefaultDepth(xdisplay, screen_num));
2221 cache->bg = cairo_xlib_surface_create(xdisplay, xpixmap,
2222 GDK_VISUAL_XVISUAL(gdk_screen_get_system_visual(screen)),
2223 dest_w, dest_h);
2224 cr = cairo_create(cache->bg);
2225 #else
2226 cache->bg = gdk_pixmap_new(window, dest_w, dest_h, -1);
2227 cr = gdk_cairo_create(cache->bg);
2228 #endif
2229 if(gdk_pixbuf_get_has_alpha(pix)
2230 || desktop->conf.wallpaper_mode == FM_WP_CENTER
2231 || desktop->conf.wallpaper_mode == FM_WP_FIT)
2232 {
2233 gdk_cairo_set_source_color(cr, &desktop->conf.desktop_bg);
2234 cairo_rectangle(cr, 0, 0, dest_w, dest_h);
2235 cairo_fill(cr);
2236 }
2237
2238 switch(desktop->conf.wallpaper_mode)
2239 {
2240 case FM_WP_TILE:
2241 break;
2242 case FM_WP_STRETCH:
2243 case FM_WP_SCREEN:
2244 if(dest_w == src_w && dest_h == src_h)
2245 scaled = (GdkPixbuf*)g_object_ref(pix);
2246 else
2247 scaled = gdk_pixbuf_scale_simple(pix, dest_w, dest_h, GDK_INTERP_BILINEAR);
2248 g_object_unref(pix);
2249 pix = scaled;
2250 break;
2251 case FM_WP_FIT:
2252 case FM_WP_CROP:
2253 if(dest_w != src_w || dest_h != src_h)
2254 {
2255 gdouble w_ratio = (float)dest_w / src_w;
2256 gdouble h_ratio = (float)dest_h / src_h;
2257 gdouble ratio = (desktop->conf.wallpaper_mode == FM_WP_FIT)
2258 ? MIN(w_ratio, h_ratio)
2259 : MAX(w_ratio, h_ratio);
2260 if(ratio != 1.0)
2261 {
2262 src_w *= ratio;
2263 src_h *= ratio;
2264 scaled = gdk_pixbuf_scale_simple(pix, src_w, src_h, GDK_INTERP_BILINEAR);
2265 g_object_unref(pix);
2266 pix = scaled;
2267 }
2268 }
2269 /* continue to execute code in case FM_WP_CENTER */
2270 case FM_WP_CENTER:
2271 x = (dest_w - src_w)/2;
2272 y = (dest_h - src_h)/2;
2273 break;
2274 case FM_WP_COLOR: ; /* handled above */
2275 }
2276 gdk_cairo_set_source_pixbuf(cr, pix, x, y);
2277 cairo_paint(cr);
2278 cairo_destroy(cr);
2279 cache->wallpaper_mode = desktop->conf.wallpaper_mode;
2280 }
2281 #if GTK_CHECK_VERSION(3, 0, 0)
2282 pattern = cairo_pattern_create_for_surface(cache->bg);
2283 gdk_window_set_background_pattern(window, pattern);
2284 cairo_pattern_destroy(pattern);
2285 #else
2286 gdk_window_set_back_pixmap(window, cache->bg, FALSE);
2287 #endif
2288
2289 /* set root map here */
2290 xdisplay = GDK_WINDOW_XDISPLAY(root);
2291 xroot = RootWindow(xdisplay, screen_num);
2292
2293 #if GTK_CHECK_VERSION(3, 0, 0)
2294 xpixmap = cairo_xlib_surface_get_drawable(cache->bg);
2295 #else
2296 xpixmap = GDK_WINDOW_XWINDOW(cache->bg);
2297 #endif
2298
2299 XChangeProperty(xdisplay, GDK_WINDOW_XID(root),
2300 XA_XROOTMAP_ID, XA_PIXMAP, 32, PropModeReplace, (guchar*)&xpixmap, 1);
2301
2302 XGrabServer (xdisplay);
2303
2304 #if 0
2305 result = XGetWindowProperty (display,
2306 RootWindow (display, screen_num),
2307 gdk_x11_get_xatom_by_name ("ESETROOT_PMAP_ID"),
2308 0L, 1L, False, XA_PIXMAP,
2309 &type, &format, &nitems,
2310 &bytes_after,
2311 &data_esetroot);
2312
2313 if (data_esetroot != NULL) {
2314 if (result == Success && type == XA_PIXMAP &&
2315 format == 32 &&
2316 nitems == 1) {
2317 gdk_error_trap_push ();
2318 XKillClient (display, *(Pixmap *)data_esetroot);
2319 gdk_error_trap_pop_ignored ();
2320 }
2321 XFree (data_esetroot);
2322 }
2323
2324 XChangeProperty (display, RootWindow (display, screen_num),
2325 gdk_x11_get_xatom_by_name ("ESETROOT_PMAP_ID"),
2326 XA_PIXMAP, 32, PropModeReplace,
2327 (guchar *) &xpixmap, 1);
2328 #endif
2329
2330 XChangeProperty(xdisplay, xroot, XA_XROOTPMAP_ID, XA_PIXMAP, 32,
2331 PropModeReplace, (guchar*)&xpixmap, 1);
2332
2333 XSetWindowBackgroundPixmap(xdisplay, xroot, xpixmap);
2334 XClearWindow(xdisplay, xroot);
2335
2336 XFlush(xdisplay);
2337 XUngrabServer(xdisplay);
2338
2339 if(pix)
2340 g_object_unref(pix);
2341
2342 gdk_window_invalidate_rect(window, NULL, TRUE);
2343 }
2344
2345
2346 /* ---------------------------------------------------------------------
2347 FmFolderModel signal handlers */
2348
2349 static void on_row_deleting(FmFolderModel* model, GtkTreePath* tp,
2350 GtkTreeIter* iter, gpointer data, FmDesktop* desktop)
2351 {
2352 GList *l;
2353
2354 for(l = desktop->fixed_items; l; l = l->next)
2355 if(l->data == data)
2356 {
2357 desktop->fixed_items = g_list_delete_link(desktop->fixed_items, l);
2358 break;
2359 }
2360 if((gpointer)desktop->focus == data)
2361 {
2362 GtkTreeIter it = *iter;
2363 fm_desktop_accessible_focus_unset(desktop, data);
2364 if(gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &it))
2365 desktop->focus = fm_folder_model_get_item_userdata(model, &it);
2366 else
2367 {
2368 if(gtk_tree_path_prev(tp))
2369 {
2370 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &it, tp);
2371 gtk_tree_path_next(tp);
2372 desktop->focus = fm_folder_model_get_item_userdata(model, &it);
2373 }
2374 else
2375 desktop->focus = NULL;
2376 }
2377 if (desktop->focus)
2378 fm_desktop_accessible_focus_set(desktop, desktop->focus);
2379 }
2380 if((gpointer)desktop->drop_hilight == data)
2381 desktop->drop_hilight = NULL;
2382 if((gpointer)desktop->hover_item == data)
2383 {
2384 desktop->hover_item = NULL;
2385 /* bug #3615015: after deleting the item tooltip stuck on the desktop */
2386 g_object_set(G_OBJECT(desktop), "tooltip-text", NULL, NULL);
2387 }
2388 fm_desktop_accessible_item_deleted(desktop, data);
2389 desktop_item_free(data);
2390 }
2391
2392 static void on_row_inserted(FmFolderModel* mod, GtkTreePath* tp, GtkTreeIter* it, FmDesktop* desktop)
2393 {
2394 FmDesktopItem* item = desktop_item_new(mod, it);
2395 gint *indices = gtk_tree_path_get_indices(tp);
2396 fm_desktop_accessible_item_added(desktop, item, indices[0]);
2397 fm_folder_model_set_item_userdata(mod, it, item);
2398 queue_layout_items(desktop);
2399 }
2400
2401 static void on_row_deleted(FmFolderModel* mod, GtkTreePath* tp, FmDesktop* desktop)
2402 {
2403 queue_layout_items(desktop);
2404 }
2405
2406 static void on_row_changed(FmFolderModel* model, GtkTreePath* tp, GtkTreeIter* it, FmDesktop* desktop)
2407 {
2408 FmDesktopItem* item = fm_folder_model_get_item_userdata(model, it);
2409 GdkPixbuf *icon;
2410
2411 fm_file_info_unref(item->fi);
2412 gtk_tree_model_get(GTK_TREE_MODEL(model), it,
2413 FM_FOLDER_MODEL_COL_INFO, &item->fi,
2414 FM_FOLDER_MODEL_COL_ICON, &icon, -1);
2415 fm_file_info_ref(item->fi);
2416
2417 /* we need to redraw old area as we changing data */
2418 redraw_item(desktop, item);
2419 calc_item_size(desktop, item, icon);
2420 if (icon)
2421 g_object_unref(icon);
2422 redraw_item(desktop, item);
2423 /* queue_layout_items(desktop); */
2424 }
2425
2426 static void on_rows_reordered(FmFolderModel* model, GtkTreePath* parent_tp, GtkTreeIter* parent_it, gpointer new_order, FmDesktop* desktop)
2427 {
2428 fm_desktop_accessible_items_reordered(desktop, GTK_TREE_MODEL(model), new_order);
2429 queue_layout_items(desktop);
2430 }
2431
2432
2433 /* ---------------------------------------------------------------------
2434 Events handlers */
2435
2436 static void _clear_bg_cache(FmDesktop *self)
2437 {
2438 while(self->cache)
2439 {
2440 FmBackgroundCache *bg = self->cache;
2441
2442 self->cache = bg->next;
2443 #if GTK_CHECK_VERSION(3, 0, 0)
2444 XFreePixmap(cairo_xlib_surface_get_display(bg->bg),
2445 cairo_xlib_surface_get_drawable(bg->bg));
2446 cairo_surface_destroy(bg->bg);
2447 #else
2448 g_object_unref(bg->bg);
2449 #endif
2450 g_free(bg->filename);
2451 g_free(bg);
2452 }
2453 }
2454
2455 static void update_working_area(FmDesktop* desktop)
2456 {
2457 GdkScreen* screen = gtk_widget_get_screen((GtkWidget*)desktop);
2458 #if GTK_CHECK_VERSION(3, 4, 0)
2459 gdk_screen_get_monitor_workarea(screen, desktop->monitor, &desktop->working_area);
2460 #else
2461 GdkWindow* root = gdk_screen_get_root_window(screen);
2462 Atom ret_type;
2463 gulong len, after;
2464 int format;
2465 guchar* prop;
2466 guint32 n_desktops, cur_desktop;
2467 gulong* working_area;
2468
2469 /* default to screen size */
2470 gdk_screen_get_monitor_geometry(screen, desktop->monitor, &desktop->working_area);
2471
2472 if(XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
2473 XA_NET_NUMBER_OF_DESKTOPS, 0, 1, False, XA_CARDINAL, &ret_type,
2474 &format, &len, &after, &prop) != Success)
2475 goto _out;
2476 if(!prop)
2477 goto _out;
2478 n_desktops = *(guint32*)prop;
2479 XFree(prop);
2480
2481 if(XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
2482 XA_NET_CURRENT_DESKTOP, 0, 1, False, XA_CARDINAL, &ret_type,
2483 &format, &len, &after, &prop) != Success)
2484 goto _out;
2485 if(!prop)
2486 goto _out;
2487 cur_desktop = *(guint32*)prop;
2488 XFree(prop);
2489
2490 if(XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
2491 XA_NET_WORKAREA, 0, 4 * 32, False, AnyPropertyType, &ret_type,
2492 &format, &len, &after, &prop) != Success)
2493 goto _out;
2494 if(ret_type == None || format == 0 || len != n_desktops*4)
2495 {
2496 if(prop)
2497 XFree(prop);
2498 goto _out;
2499 }
2500 working_area = ((gulong*)prop) + cur_desktop * 4;
2501
2502 if((gint)working_area[0] > desktop->working_area.x &&
2503 (gint)working_area[0] < desktop->working_area.x + desktop->working_area.width)
2504 {
2505 desktop->working_area.width -= (working_area[0] - desktop->working_area.x);
2506 desktop->working_area.x = (gint)working_area[0];
2507 }
2508 if((gint)working_area[1] > desktop->working_area.y &&
2509 (gint)working_area[1] < desktop->working_area.y + desktop->working_area.height)
2510 {
2511 desktop->working_area.height -= (working_area[1] - desktop->working_area.y);
2512 desktop->working_area.y = (gint)working_area[1];
2513 }
2514 if((gint)(working_area[0] + working_area[2]) < desktop->working_area.x + desktop->working_area.width)
2515 desktop->working_area.width = working_area[0] + working_area[2] - desktop->working_area.x;
2516 if((gint)(working_area[1] + working_area[3]) < desktop->working_area.y + desktop->working_area.height)
2517 desktop->working_area.height = working_area[1] + working_area[3] - desktop->working_area.y;
2518 g_debug("got working area: %d.%d.%d.%d", desktop->working_area.x, desktop->working_area.y,
2519 desktop->working_area.width, desktop->working_area.height);
2520
2521 XFree(prop);
2522 _out:
2523 #endif
2524 queue_layout_items(desktop);
2525 return;
2526 }
2527
2528 static GdkFilterReturn on_root_event(GdkXEvent *xevent, GdkEvent *event, gpointer data)
2529 {
2530 XPropertyEvent * evt = (XPropertyEvent*) xevent;
2531 FmDesktop* self = (FmDesktop*)data;
2532 if (evt->type == PropertyNotify)
2533 {
2534 if(evt->atom == XA_NET_WORKAREA)
2535 update_working_area(self);
2536 else if(evt->atom == XA_NET_CURRENT_DESKTOP)
2537 {
2538 gint desktop = get_desktop_for_root_window(gdk_screen_get_root_window(
2539 gtk_widget_get_screen(GTK_WIDGET(data))));
2540 if(desktop >= 0)
2541 {
2542 self->cur_desktop = (guint)desktop;
2543 if(!self->conf.wallpaper_common)
2544 update_background(self, -1);
2545 }
2546 }
2547 }
2548 return GDK_FILTER_CONTINUE;
2549 }
2550
2551 static void on_screen_size_changed(GdkScreen* screen, FmDesktop* desktop)
2552 {
2553 GdkRectangle geom;
2554 if (desktop->monitor >= gdk_screen_get_n_monitors(screen))
2555 {
2556 gint i;
2557 /* our monitor was disconnected... remove FmDesktop now! */
2558 for (i = 0; i < n_screens; i++)
2559 if (desktops[i] == desktop)
2560 break;
2561 if (i < n_screens)
2562 desktops[i] = fm_desktop_new(screen, desktop->monitor ? -2 : -1);
2563 gtk_widget_destroy(GTK_WIDGET(desktop));
2564 return;
2565 }
2566 gdk_screen_get_monitor_geometry(screen, desktop->monitor, &geom);
2567 gtk_window_resize((GtkWindow*)desktop, geom.width, geom.height);
2568 /* bug #3614780: if monitor was moved desktop should be moved too */
2569 gtk_window_move((GtkWindow*)desktop, geom.x, geom.y);
2570 /* FIXME: check if new monitor was added! */
2571 }
2572
2573 static void reload_icons()
2574 {
2575 int i;
2576 for(i=0; i < n_screens; ++i)
2577 if(desktops[i]->monitor >= 0)
2578 gtk_widget_queue_resize(GTK_WIDGET(desktops[i]));
2579 }
2580
2581 static void on_big_icon_size_changed(FmConfig* cfg, FmFolderModel* model)
2582 {
2583 fm_folder_model_set_icon_size(model, fm_config->big_icon_size);
2584 reload_icons();
2585 }
2586
2587 static void on_icon_theme_changed(GtkIconTheme* theme, gpointer user_data)
2588 {
2589 reload_icons();
2590 }
2591
2592
2593 /* ---------------------------------------------------------------------
2594 Popup handlers */
2595
2596 static void fm_desktop_update_popup(FmFolderView* fv, GtkWindow* window,
2597 GtkUIManager* ui, GtkActionGroup* act_grp,
2598 FmFileInfoList* files)
2599 {
2600 GtkAction* act;
2601
2602 /* remove 'Rename' item and accelerator */
2603 act = gtk_action_group_get_action(act_grp, "Rename");
2604 gtk_action_set_visible(act, FALSE);
2605 gtk_action_set_sensitive(act, FALSE);
2606 /* hide 'Show Hidden' item */
2607 act = gtk_action_group_get_action(act_grp, "ShowHidden");
2608 gtk_action_set_visible(act, FALSE);
2609 /* add 'Configure desktop' item replacing 'Properties' */
2610 act = gtk_action_group_get_action(act_grp, "Prop");
2611 gtk_action_set_visible(act, FALSE);
2612 //gtk_action_group_remove_action(act_grp, act);
2613 #if !FM_CHECK_VERSION(1, 2, 0)
2614 if(fm_folder_view_get_model(fv) == NULL)
2615 {
2616 /* hide folder-oriented actions if there is no folder */
2617 act = gtk_action_group_get_action(act_grp, "SelAll");
2618 gtk_action_set_visible(act, FALSE);
2619 act = gtk_action_group_get_action(act_grp, "InvSel");
2620 gtk_action_set_visible(act, FALSE);
2621 act = gtk_action_group_get_action(act_grp, "Sort");
2622 gtk_action_set_visible(act, FALSE);
2623 }
2624 #endif
2625 gtk_action_group_set_translation_domain(act_grp, NULL);
2626 gtk_action_group_add_actions(act_grp, desktop_actions,
2627 G_N_ELEMENTS(desktop_actions), window);
2628 gtk_ui_manager_add_ui_from_string(ui, desktop_menu_xml, -1, NULL);
2629 }
2630
2631 static void fm_desktop_update_item_popup(FmFolderView* fv, GtkWindow* window,
2632 GtkUIManager* ui, GtkActionGroup* act_grp,
2633 FmFileInfoList* files)
2634 {
2635 FmFileInfo* fi;
2636 GList* sel_items, *l;
2637 GtkAction* act;
2638 gboolean all_fixed = TRUE, has_fixed = FALSE;
2639
2640 sel_items = get_selected_items(FM_DESKTOP(fv), NULL);
2641 for(l = sel_items; l; l=l->next)
2642 {
2643 FmDesktopItem* item = (FmDesktopItem*)l->data;
2644 if(item->fixed_pos)
2645 has_fixed = TRUE;
2646 else
2647 all_fixed = FALSE;
2648 }
2649 g_list_free(sel_items);
2650
2651 fi = (FmFileInfo*)fm_file_info_list_peek_head(files);
2652
2653 /* merge some specific menu items for folders */
2654 gtk_action_group_set_translation_domain(act_grp, NULL);
2655 if(fm_file_info_list_get_length(files) == 1 && fm_file_info_is_dir(fi))
2656 {
2657 gtk_action_group_add_actions(act_grp, folder_menu_actions,
2658 G_N_ELEMENTS(folder_menu_actions), fv);
2659 gtk_ui_manager_add_ui_from_string(ui, folder_menu_xml, -1, NULL);
2660 }
2661 #if FM_CHECK_VERSION(1, 2, 0)
2662 if (fm_file_info_list_get_length(files) == 1 &&
2663 ((trash_can && trash_can->fi == fi) ||
2664 (documents && documents->fi == fi)))
2665 {
2666 gtk_action_group_add_actions(act_grp, extra_item_menu_actions,
2667 G_N_ELEMENTS(extra_item_menu_actions), fv);
2668 gtk_ui_manager_add_ui_from_string(ui, extra_item_menu_xml, -1, NULL);
2669 /* some menu items should be never available for extra items */
2670 act = gtk_action_group_get_action(act_grp, "Cut");
2671 gtk_action_set_visible(act, FALSE);
2672 act = gtk_action_group_get_action(act_grp, "Del");
2673 gtk_action_set_visible(act, FALSE);
2674 act = gtk_action_group_get_action(act_grp, "Rename");
2675 gtk_action_set_visible(act, FALSE);
2676 }
2677 #endif
2678
2679 /* merge desktop icon specific items */
2680 gtk_action_group_add_actions(act_grp, desktop_icon_actions,
2681 G_N_ELEMENTS(desktop_icon_actions), fv);
2682 act = gtk_action_group_get_action(act_grp, "Snap");
2683 gtk_action_set_sensitive(act, has_fixed);
2684
2685 gtk_action_group_add_toggle_actions(act_grp, desktop_icon_toggle_actions,
2686 G_N_ELEMENTS(desktop_icon_toggle_actions),
2687 fv);
2688 act = gtk_action_group_get_action(act_grp, "Fix");
2689 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), all_fixed);
2690
2691 gtk_ui_manager_add_ui_from_string(ui, desktop_icon_menu_xml, -1, NULL);
2692 }
2693
2694 /* folder options work only with single folder - see above */
2695 static void on_open_in_new_tab(GtkAction* act, gpointer user_data)
2696 {
2697 FmDesktop* desktop = FM_DESKTOP(user_data);
2698
2699 if(desktop->focus)
2700 fm_main_win_open_in_last_active(fm_file_info_get_path(desktop->focus->fi));
2701 }
2702
2703 static void on_open_in_new_win(GtkAction* act, gpointer user_data)
2704 {
2705 FmDesktop* desktop = FM_DESKTOP(user_data);
2706
2707 if(desktop->focus)
2708 fm_main_win_add_win(NULL, fm_file_info_get_path(desktop->focus->fi));
2709 }
2710
2711 static void on_open_folder_in_terminal(GtkAction* act, gpointer user_data)
2712 {
2713 FmDesktop* desktop = FM_DESKTOP(user_data);
2714
2715 if(desktop->focus /*&& !fm_file_info_is_virtual(fi)*/)
2716 pcmanfm_open_folder_in_terminal(NULL, fm_file_info_get_path(desktop->focus->fi));
2717 }
2718
2719 static void on_fix_pos(GtkToggleAction* act, gpointer user_data)
2720 {
2721 FmDesktop* desktop = FM_DESKTOP(user_data);
2722 GList* items = get_selected_items(desktop, NULL);
2723 GList* l;
2724 if(gtk_toggle_action_get_active(act))
2725 {
2726 for(l = items; l; l=l->next)
2727 {
2728 FmDesktopItem* item = (FmDesktopItem*)l->data;
2729 if(!item->fixed_pos)
2730 {
2731 item->fixed_pos = TRUE;
2732 desktop->fixed_items = g_list_prepend(desktop->fixed_items, item);
2733 }
2734 }
2735 }
2736 else
2737 {
2738 for(l = items; l; l=l->next)
2739 {
2740 FmDesktopItem* item = (FmDesktopItem*)l->data;
2741 item->fixed_pos = FALSE;
2742 desktop->fixed_items = g_list_remove(desktop->fixed_items, item);
2743 }
2744 queue_layout_items(desktop);
2745 }
2746 g_list_free(items);
2747 queue_config_save(desktop);
2748 }
2749
2750 #if FM_CHECK_VERSION(1, 2, 0)
2751 static void on_disable(GtkAction* act, gpointer user_data)
2752 {
2753 FmDesktop *desktop = FM_DESKTOP(user_data);
2754 GList *items = get_selected_items(desktop, NULL);
2755 FmDesktopItem *item = (FmDesktopItem*)items->data;
2756
2757 g_list_free(items);
2758 if (trash_can && trash_can->fi == item->fi)
2759 {
2760 desktop->conf.show_trash = FALSE;
2761 }
2762 else if (documents && documents->fi == item->fi)
2763 {
2764 desktop->conf.show_documents = FALSE;
2765 }
2766 else /* else is error */
2767 {
2768 g_warning("invalid item to remove from desktop");
2769 return;
2770 }
2771 queue_config_save(desktop);
2772 fm_folder_model_extra_file_remove(desktop->model, item->fi);
2773 }
2774 #endif
2775
2776 /* round() is only available in C99. Don't use it now for portability. */
2777 static inline double _round(double x)
2778 {
2779 return (x > 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
2780 }
2781
2782 static void on_snap_to_grid(GtkAction* act, gpointer user_data)
2783 {
2784 FmDesktop* desktop = FM_DESKTOP(user_data);
2785 FmDesktopItem* item;
2786 GList* items = get_selected_items(desktop, NULL);
2787 GList* l;
2788 int x, y;
2789 GtkTextDirection direction = gtk_widget_get_direction(GTK_WIDGET(desktop));
2790
2791 y = desktop->working_area.y + desktop->ymargin;
2792 //bottom = desktop->working_area.y + desktop->working_area.height - desktop->ymargin - desktop->cell_h;
2793
2794 if(direction != GTK_TEXT_DIR_RTL) /* LTR or NONE */
2795 x = desktop->working_area.x + desktop->xmargin;
2796 else /* RTL */
2797 x = desktop->working_area.x + desktop->working_area.width - desktop->xmargin - desktop->cell_w;
2798
2799 for(l = items; l; l = l->next)
2800 {
2801 int new_x, new_y;
2802 item = (FmDesktopItem*)l->data;
2803 if(!item->fixed_pos)
2804 continue;
2805 new_x = x + _round((double)(item->area.x - x) / desktop->cell_w) * desktop->cell_w;
2806 new_y = y + _round((double)(item->area.y - y) / desktop->cell_h) * desktop->cell_h;
2807 move_item(desktop, item, new_x, new_y, FALSE);
2808 }
2809 g_list_free(items);
2810
2811 queue_layout_items(desktop);
2812 }
2813
2814
2815 /* ---------------------------------------------------------------------
2816 GtkWidget class default signal handlers */
2817
2818 static gboolean is_point_in_rect(GdkRectangle* rect, int x, int y)
2819 {
2820 return rect->x < x && x < (rect->x + rect->width) && y > rect->y && y < (rect->y + rect->height);
2821 }
2822
2823 static FmDesktopItem* hit_test(FmDesktop* self, GtkTreeIter *it, int x, int y)
2824 {
2825 FmDesktopItem* item;
2826 GtkTreeModel* model;
2827
2828 if (!self->model)
2829 return NULL;
2830 model = GTK_TREE_MODEL(self->model);
2831 if(model && gtk_tree_model_get_iter_first(model, it)) do
2832 {
2833 item = fm_folder_model_get_item_userdata(self->model, it);
2834 if(is_point_in_rect(&item->icon_rect, x, y)
2835 || is_point_in_rect(&item->text_rect, x, y))
2836 return item;
2837 }
2838 while(gtk_tree_model_iter_next(model, it));
2839 return NULL;
2840 }
2841
2842 static FmDesktopItem* get_nearest_item(FmDesktop* desktop, FmDesktopItem* item, GtkDirectionType dir)
2843 {
2844 GtkTreeModel* model;
2845 FmDesktopItem* item2, *ret = NULL;
2846 guint min_x_dist, min_y_dist, dist;
2847 GtkTreeIter it;
2848
2849 if (!desktop->model)
2850 return NULL;
2851 model = GTK_TREE_MODEL(desktop->model);
2852 if(!gtk_tree_model_get_iter_first(model, &it))
2853 return NULL;
2854 if(!item) /* there is no focused item yet, select first one then */
2855 return fm_folder_model_get_item_userdata(desktop->model, &it);
2856
2857 min_x_dist = min_y_dist = (guint)-1;
2858 item2 = NULL;
2859
2860 switch(dir)
2861 {
2862 case GTK_DIR_LEFT:
2863 do
2864 {
2865 item2 = fm_folder_model_get_item_userdata(desktop->model, &it);
2866 if(item2->area.x >= item->area.x)
2867 continue;
2868 dist = item->area.x - item2->area.x;
2869 if(dist < min_x_dist)
2870 {
2871 ret = item2;
2872 min_x_dist = dist;
2873 min_y_dist = ABS(item->area.y - item2->area.y);
2874 }
2875 else if(dist == min_x_dist && item2 != ret) /* if there is another item of the same x distance */
2876 {
2877 /* get the one with smaller y distance */
2878 dist = ABS(item2->area.y - item->area.y);
2879 if(dist < min_y_dist)
2880 {
2881 ret = item2;
2882 min_y_dist = dist;
2883 }
2884 }
2885 }
2886 while(gtk_tree_model_iter_next(model, &it));
2887 break;
2888 case GTK_DIR_RIGHT:
2889 do
2890 {
2891 item2 = fm_folder_model_get_item_userdata(desktop->model, &it);
2892 if(item2->area.x <= item->area.x)
2893 continue;
2894 dist = item2->area.x - item->area.x;
2895 if(dist < min_x_dist)
2896 {
2897 ret = item2;
2898 min_x_dist = dist;
2899 min_y_dist = ABS(item->area.y - item2->area.y);
2900 }
2901 else if(dist == min_x_dist && item2 != ret) /* if there is another item of the same x distance */
2902 {
2903 /* get the one with smaller y distance */
2904 dist = ABS(item2->area.y - item->area.y);
2905 if(dist < min_y_dist)
2906 {
2907 ret = item2;
2908 min_y_dist = dist;
2909 }
2910 }
2911 }
2912 while(gtk_tree_model_iter_next(model, &it));
2913 break;
2914 case GTK_DIR_UP:
2915 do
2916 {
2917 item2 = fm_folder_model_get_item_userdata(desktop->model, &it);
2918 if(item2->area.y >= item->area.y)
2919 continue;
2920 dist = item->area.y - item2->area.y;
2921 if(dist < min_y_dist)
2922 {
2923 ret = item2;
2924 min_y_dist = dist;
2925 min_x_dist = ABS(item->area.x - item2->area.x);
2926 }
2927 else if(dist == min_y_dist && item2 != ret) /* if there is another item of the same y distance */
2928 {
2929 /* get the one with smaller x distance */
2930 dist = ABS(item2->area.x - item->area.x);
2931 if(dist < min_x_dist)
2932 {
2933 ret = item2;
2934 min_x_dist = dist;
2935 }
2936 }
2937 }
2938 while(gtk_tree_model_iter_next(model, &it));
2939 break;
2940 case GTK_DIR_DOWN:
2941 do
2942 {
2943 item2 = fm_folder_model_get_item_userdata(desktop->model, &it);
2944 if(item2->area.y <= item->area.y)
2945 continue;
2946 dist = item2->area.y - item->area.y;
2947 if(dist < min_y_dist)
2948 {
2949 ret = item2;
2950 min_y_dist = dist;
2951 min_x_dist = ABS(item->area.x - item2->area.x);
2952 }
2953 else if(dist == min_y_dist && item2 != ret) /* if there is another item of the same y distance */
2954 {
2955 /* get the one with smaller x distance */
2956 dist = ABS(item2->area.x - item->area.x);
2957 if(dist < min_x_dist)
2958 {
2959 ret = item2;
2960 min_x_dist = dist;
2961 }
2962 }
2963 }
2964 while(gtk_tree_model_iter_next(model, &it));
2965 break;
2966 case GTK_DIR_TAB_FORWARD: /* FIXME */
2967 break;
2968 case GTK_DIR_TAB_BACKWARD: /* FIXME */
2969 ;
2970 }
2971 return ret;
2972 }
2973
2974 static gboolean has_selected_item(FmDesktop* desktop)
2975 {
2976 GtkTreeModel* model;
2977 GtkTreeIter it;
2978
2979 if (!desktop->model)
2980 return FALSE;
2981 model = GTK_TREE_MODEL(desktop->model);
2982 if(gtk_tree_model_get_iter_first(model, &it)) do
2983 {
2984 FmDesktopItem* item = fm_folder_model_get_item_userdata(desktop->model, &it);
2985 if(item->is_selected)
2986 return TRUE;
2987 }
2988 while(gtk_tree_model_iter_next(model, &it));
2989 return FALSE;
2990 }
2991
2992 static void set_focused_item(FmDesktop* desktop, FmDesktopItem* item)
2993 {
2994 if(item != desktop->focus)
2995 {
2996 FmDesktopItem* old_focus = desktop->focus;
2997 desktop->focus = item;
2998 if(old_focus)
2999 {
3000 redraw_item(desktop, old_focus);
3001 fm_desktop_accessible_focus_unset(desktop, old_focus);
3002 }
3003 if(item)
3004 {
3005 redraw_item(desktop, item);
3006 fm_desktop_accessible_focus_set(desktop, item);
3007 }
3008 }
3009 }
3010
3011 static void _focus_and_select_focused_item(FmDesktop *desktop, FmDesktopItem *item)
3012 {
3013 item->is_selected = TRUE;
3014 fm_desktop_item_selected_changed(desktop, item);
3015 set_focused_item(desktop, item);
3016 }
3017
3018 /* This function is taken from xfdesktop */
3019 static void forward_event_to_rootwin(GdkScreen *gscreen, GdkEvent *event)
3020 {
3021 XButtonEvent xev, xev2;
3022 Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(gscreen));
3023
3024 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE)
3025 {
3026 if (event->type == GDK_BUTTON_PRESS)
3027 {
3028 xev.type = ButtonPress;
3029 /*
3030 * rox has an option to disable the next
3031 * instruction. it is called "blackbox_hack". Does
3032 * anyone know why exactly it is needed?
3033 */
3034 XUngrabPointer(dpy, event->button.time);
3035 }
3036 else
3037 xev.type = ButtonRelease;
3038
3039 xev.button = event->button.button;
3040 xev.x = event->button.x; /* Needed for icewm */
3041 xev.y = event->button.y;
3042 xev.x_root = event->button.x_root;
3043 xev.y_root = event->button.y_root;
3044 xev.state = event->button.state;
3045
3046 xev2.type = 0;
3047 }
3048 else if (event->type == GDK_SCROLL)
3049 {
3050 xev.type = ButtonPress;
3051 xev.button = event->scroll.direction + 4;
3052 xev.x = event->scroll.x; /* Needed for icewm */
3053 xev.y = event->scroll.y;
3054 xev.x_root = event->scroll.x_root;
3055 xev.y_root = event->scroll.y_root;
3056 xev.state = event->scroll.state;
3057
3058 xev2.type = ButtonRelease;
3059 xev2.button = xev.button;
3060 }
3061 else
3062 return ;
3063 xev.window = GDK_WINDOW_XID(gdk_screen_get_root_window(gscreen));
3064 xev.root = xev.window;
3065 xev.subwindow = None;
3066 xev.time = event->button.time;
3067 xev.same_screen = True;
3068
3069 XSendEvent(dpy, xev.window, False, ButtonPressMask | ButtonReleaseMask,
3070 (XEvent *) & xev);
3071 if (xev2.type == 0)
3072 return ;
3073
3074 /* send button release for scroll event */
3075 xev2.window = xev.window;
3076 xev2.root = xev.root;
3077 xev2.subwindow = xev.subwindow;
3078 xev2.time = xev.time;
3079 xev2.x = xev.x;
3080 xev2.y = xev.y;
3081 xev2.x_root = xev.x_root;
3082 xev2.y_root = xev.y_root;
3083 xev2.state = xev.state;
3084 xev2.same_screen = xev.same_screen;
3085
3086 XSendEvent(dpy, xev2.window, False, ButtonPressMask | ButtonReleaseMask,
3087 (XEvent *) & xev2);
3088 }
3089
3090
3091 #if GTK_CHECK_VERSION(3, 0, 0)
3092 static gboolean on_draw(GtkWidget* w, cairo_t* cr)
3093 #else
3094 static gboolean on_expose(GtkWidget* w, GdkEventExpose* evt)
3095 #endif
3096 {
3097 FmDesktop* self = (FmDesktop*)w;
3098 #if !GTK_CHECK_VERSION(3, 0, 0)
3099 cairo_t* cr;
3100 #endif
3101 GtkTreeModel* model = self->model ? GTK_TREE_MODEL(self->model) : NULL;
3102 GtkTreeIter it;
3103 GdkRectangle area;
3104
3105 #if GTK_CHECK_VERSION(3, 0, 0)
3106 if(G_UNLIKELY(!gtk_cairo_should_draw_window(cr, gtk_widget_get_window(w))))
3107 return FALSE;
3108
3109 cairo_save(cr);
3110 gtk_cairo_transform_to_window(cr, w, gtk_widget_get_window(w));
3111 gdk_cairo_get_clip_rectangle(cr, &area);
3112 #else
3113 if(G_UNLIKELY(! gtk_widget_get_visible (w) || ! gtk_widget_get_mapped (w)))
3114 return TRUE;
3115
3116 cr = gdk_cairo_create(gtk_widget_get_window(w));
3117 area = evt->area;
3118 #endif
3119 if(self->rubber_bending)
3120 paint_rubber_banding_rect(self, cr, &area);
3121
3122 if(model && gtk_tree_model_get_iter_first(model, &it)) do
3123 {
3124 FmDesktopItem* item = fm_folder_model_get_item_userdata(self->model, &it);
3125 GdkRectangle* intersect, tmp, tmp2;
3126 GdkPixbuf* icon = NULL;
3127 if(gdk_rectangle_intersect(&area, &item->icon_rect, &tmp))
3128 intersect = &tmp;
3129 else
3130 intersect = NULL;
3131
3132 if(gdk_rectangle_intersect(&area, &item->text_rect, &tmp2))
3133 {
3134 if(intersect)
3135 gdk_rectangle_union(intersect, &tmp2, intersect);
3136 else
3137 intersect = &tmp2;
3138 }
3139
3140 if(intersect)
3141 {
3142 gtk_tree_model_get(model, &it, FM_FOLDER_MODEL_COL_ICON, &icon, -1);
3143 paint_item(self, item, cr, intersect, icon);
3144 if(icon)
3145 g_object_unref(icon);
3146 }
3147