1 /*
2 * desktop.c
3 *
4 * Copyright 2010 - 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
5 * Copyright 2012-2017 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 #if FM_CHECK_VERSION(1, 2, 0)
154 /* ---------------------------------------------------------------------
155 mounts handlers */
156
157 #if FM_CHECK_VERSION(1, 2, 0)
158 typedef struct
159 {
160 GMount *mount; /* NULL for non-mounts */
161 FmPath *path;
162 FmFileInfo *fi;
163 FmFileInfoJob *job;
164 } FmDesktopExtraItem;
165
166 static FmDesktopExtraItem *documents = NULL;
167 //static FmDesktopExtraItem *computer = NULL;
168 static FmDesktopExtraItem *trash_can = NULL;
169 //static FmDesktopExtraItem *applications = NULL;
170
171 /* under GDK lock */
172 static GSList *mounts = NULL;
173 #endif
174
175
176 /* ---------------------------------------------------------------------
177 Items management and common functions */
178
get_config_file(FmDesktop * desktop,gboolean create_dir)179 static char* get_config_file(FmDesktop* desktop, gboolean create_dir)
180 {
181 char *dir, *path;
182 int i;
183
184 for(i = 0; i < n_screens; i++)
185 if(desktops[i] == desktop)
186 break;
187 if(i >= n_screens)
188 return NULL;
189 dir = pcmanfm_get_profile_dir(create_dir);
190 path = g_strdup_printf("%s/desktop-items-%u.conf", dir, i);
191 g_free(dir);
192 return path;
193 }
194
desktop_item_new(FmFolderModel * model,GtkTreeIter * it)195 static inline FmDesktopItem* desktop_item_new(FmFolderModel* model, GtkTreeIter* it)
196 {
197 FmDesktopItem* item = g_slice_new0(FmDesktopItem);
198 #if FM_CHECK_VERSION(1, 2, 0)
199 GSList *sl;
200 #endif
201 fm_folder_model_set_item_userdata(model, it, item);
202 gtk_tree_model_get(GTK_TREE_MODEL(model), it, FM_FOLDER_MODEL_COL_INFO, &item->fi, -1);
203 fm_file_info_ref(item->fi);
204 #if FM_CHECK_VERSION(1, 2, 0)
205 if ((trash_can && trash_can->fi == item->fi) ||
206 (documents && documents->fi == item->fi))
207 item->is_special = TRUE;
208 else for (sl = mounts; sl; sl = sl->next)
209 if (((FmDesktopExtraItem *)sl->data)->fi == item->fi)
210 {
211 item->is_special = TRUE;
212 item->is_mount = TRUE;
213 break;
214 }
215 #endif
216 return item;
217 }
218
desktop_item_free(FmDesktopItem * item)219 static inline void desktop_item_free(FmDesktopItem* item)
220 {
221 if(item->fi)
222 fm_file_info_unref(item->fi);
223 g_slice_free(FmDesktopItem, item);
224 }
225
calc_item_size(FmDesktop * desktop,FmDesktopItem * item,GdkPixbuf * icon)226 static void calc_item_size(FmDesktop* desktop, FmDesktopItem* item, GdkPixbuf* icon)
227 {
228 PangoRectangle rc2;
229
230 /* icon rect */
231 if(icon)
232 {
233 item->icon_rect.width = gdk_pixbuf_get_width(icon);
234 item->icon_rect.height = gdk_pixbuf_get_height(icon);
235 }
236 else
237 {
238 item->icon_rect.width = fm_config->big_icon_size;
239 item->icon_rect.height = fm_config->big_icon_size;
240 }
241 item->icon_rect.x = item->area.x + (desktop->cell_w - item->icon_rect.width) / 2;
242 item->icon_rect.y = item->area.y + desktop->ypad + (fm_config->big_icon_size - item->icon_rect.height) / 2;
243 item->icon_rect.height += desktop->spacing; // FIXME: this is probably wrong
244
245 /* text label rect */
246 pango_layout_set_text(desktop->pl, NULL, 0);
247 pango_layout_set_height(desktop->pl, desktop->pango_text_h);
248 pango_layout_set_width(desktop->pl, desktop->pango_text_w);
249 pango_layout_set_text(desktop->pl, fm_file_info_get_disp_name(item->fi), -1);
250
251 pango_layout_get_pixel_extents(desktop->pl, NULL, &rc2);
252 pango_layout_set_text(desktop->pl, NULL, 0);
253
254 /* FIXME: RTL */
255 item->text_rect.x = item->area.x + (desktop->cell_w - rc2.width - 4) / 2;
256 item->text_rect.y = item->area.y + desktop->ypad + fm_config->big_icon_size + desktop->spacing;
257 item->text_rect.width = rc2.width + 4;
258 item->text_rect.height = rc2.y + rc2.height + 4;
259 item->area.width = (desktop->cell_w + MAX(item->icon_rect.width, item->text_rect.width)) / 2;
260 item->area.height = item->text_rect.y + item->text_rect.height - item->area.y;
261 }
262
263 /* unfortunately we cannot load the "*" together with items because
264 otherwise we will update pango layout on each load_items() which
265 is resource wasting so we load config file once more instead */
load_config(FmDesktop * desktop)266 static inline void load_config(FmDesktop* desktop)
267 {
268 char* path;
269 GKeyFile* kf;
270
271 path = get_config_file(desktop, FALSE);
272 if(!path)
273 return;
274 kf = g_key_file_new();
275 if(g_key_file_load_from_file(kf, path, 0, NULL))
276 /* item "*" is desktop config */
277 fm_app_config_load_desktop_config(kf, "*", &desktop->conf);
278 g_free(path);
279 g_key_file_free(kf);
280 }
281
load_items(FmDesktop * desktop)282 static inline void load_items(FmDesktop* desktop)
283 {
284 GtkTreeIter it;
285 char* path;
286 GtkTreeModel* model;
287 GKeyFile* kf;
288
289 if (desktop->model == NULL)
290 return;
291 model = GTK_TREE_MODEL(desktop->model);
292 if (!gtk_tree_model_get_iter_first(model, &it))
293 return;
294 path = get_config_file(desktop, FALSE);
295 if(!path)
296 return;
297 kf = g_key_file_new();
298 if(g_key_file_load_from_file(kf, path, 0, NULL))
299 {
300 do
301 {
302 FmDesktopItem* item;
303 const char* name;
304 GdkPixbuf* icon = NULL;
305 int out; /* out of bounds */
306
307 item = fm_folder_model_get_item_userdata(desktop->model, &it);
308 name = fm_file_info_get_name(item->fi);
309 if(g_key_file_has_group(kf, name))
310 {
311 gtk_tree_model_get(model, &it, FM_FOLDER_MODEL_COL_ICON, &icon, -1);
312 desktop->fixed_items = g_list_prepend(desktop->fixed_items, item);
313 item->fixed_pos = TRUE;
314 item->area.x = g_key_file_get_integer(kf, name, "x", NULL);
315 item->area.y = g_key_file_get_integer(kf, name, "y", NULL);
316 /* pull item into screen bounds */
317 if (item->area.x < desktop->xmargin + desktop->working_area.x)
318 item->area.x = desktop->xmargin + desktop->working_area.x;
319 if (item->area.y < desktop->ymargin + desktop->working_area.y)
320 item->area.y = desktop->ymargin + desktop->working_area.y;
321 calc_item_size(desktop, item, icon);
322 /* check if item is in screen bounds and pull it if it's not */
323 out = item->area.x + item->area.width + desktop->xmargin - desktop->working_area.width - desktop->working_area.x;
324 if (out > 0)
325 {
326 if (out > item->area.x - desktop->xmargin)
327 out = item->area.x - desktop->xmargin;
328 item->area.x -= out;
329 item->icon_rect.x -= out;
330 item->text_rect.x -= out;
331 }
332 out = item->area.y + item->area.height + desktop->ymargin - desktop->working_area.height - desktop->working_area.y;
333 if (out > 0)
334 {
335 if (out > item->area.y - desktop->ymargin)
336 out = item->area.y - desktop->ymargin;
337 item->area.y -= out;
338 item->icon_rect.y -= out;
339 item->text_rect.y -= out;
340 }
341 if(icon)
342 g_object_unref(icon);
343 }
344 }
345 while(gtk_tree_model_iter_next(model, &it));
346 }
347 g_free(path);
348 g_key_file_free(kf);
349 queue_layout_items(desktop);
350 }
351
unload_items(FmDesktop * desktop)352 static inline void unload_items(FmDesktop* desktop)
353 {
354 /* remove existing fixed items */
355 g_list_free(desktop->fixed_items);
356 desktop->fixed_items = NULL;
357 desktop->focus = NULL;
358 desktop->drop_hilight = NULL;
359 desktop->hover_item = NULL;
360 g_object_set(G_OBJECT(desktop), "tooltip-text", NULL, NULL);
361 }
362
reload_items(FmDesktop * desktop)363 static inline void reload_items(FmDesktop *desktop)
364 {
365 unload_items(desktop);
366 load_items(desktop);
367 }
368
get_desktop_for_root_window(GdkWindow * root)369 static gint get_desktop_for_root_window(GdkWindow *root)
370 {
371 gint desktop = -1;
372 Atom ret_type;
373 gulong len, after;
374 int format;
375 guchar* prop;
376
377 if(XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
378 XA_NET_CURRENT_DESKTOP, 0, 1, False, XA_CARDINAL, &ret_type,
379 &format, &len, &after, &prop) == Success &&
380 prop != NULL)
381 {
382 desktop = (gint)*(guint32*)prop;
383 XFree(prop);
384 }
385 return desktop;
386 }
387
388 /* save position of desktop icons */
save_item_pos(FmDesktop * desktop)389 static void save_item_pos(FmDesktop* desktop)
390 {
391 GList* l;
392 GString* buf;
393 char* path = get_config_file(desktop, TRUE);
394
395 if(!path)
396 return;
397 buf = g_string_sized_new(1024);
398
399 /* save desktop config */
400 if (desktop->conf.configured)
401 {
402 fm_app_config_save_desktop_config(buf, "*", &desktop->conf);
403 g_string_append_c(buf, '\n');
404 }
405
406 /* save all items positions */
407 for(l = desktop->fixed_items; l; l=l->next)
408 {
409 FmDesktopItem* item = (FmDesktopItem*)l->data;
410 FmPath* fi_path = fm_file_info_get_path(item->fi);
411 const char* p;
412 /* write the file basename as group name */
413 g_string_append_c(buf, '[');
414 for(p = fm_path_get_basename(fi_path); *p; ++p)
415 {
416 switch(*p)
417 {
418 case '\r':
419 g_string_append(buf, "\\r");
420 break;
421 case '\n':
422 g_string_append(buf, "\\n");
423 break;
424 case '\\':
425 g_string_append(buf, "\\\\");
426 break;
427 case '[':
428 g_string_append(buf, "\\[");
429 break;
430 case ']':
431 g_string_append(buf, "\\]");
432 break;
433 default:
434 g_string_append_c(buf, *p);
435 }
436 }
437 g_string_append(buf, "]\n");
438 g_string_append_printf(buf, "x=%d\n"
439 "y=%d\n\n",
440 item->area.x, item->area.y);
441 }
442 g_file_set_contents(path, buf->str, buf->len, NULL);
443 g_free(path);
444 g_string_free(buf, TRUE);
445 desktop->conf.changed = FALSE; /* reset it since we saved it */
446 }
447
on_config_save_idle(gpointer _unused)448 static gboolean on_config_save_idle(gpointer _unused)
449 {
450 int i;
451
452 if (g_source_is_destroyed(g_main_current_source()))
453 return FALSE;
454
455 for (i = 0; i < n_screens; i++)
456 if (desktops[i]->conf.changed)
457 save_item_pos(desktops[i]);
458 idle_config_save = 0;
459 return FALSE;
460 }
461
queue_config_save(FmDesktop * desktop)462 static void queue_config_save(FmDesktop *desktop)
463 {
464 desktop->conf.configured = TRUE;
465 desktop->conf.changed = TRUE;
466 if (idle_config_save == 0)
467 idle_config_save = gdk_threads_add_idle(on_config_save_idle, NULL);
468 }
469
get_selected_items(FmDesktop * desktop,int * n_items)470 static GList* get_selected_items(FmDesktop* desktop, int* n_items)
471 {
472 GList* items = NULL;
473 int n = 0;
474 FmDesktopItem* focus = NULL;
475 GtkTreeModel* model = desktop->model ? GTK_TREE_MODEL(desktop->model) : NULL;
476 GtkTreeIter it;
477 if(model && gtk_tree_model_get_iter_first(model, &it)) do
478 {
479 FmDesktopItem* item = fm_folder_model_get_item_userdata(desktop->model, &it);
480 if(item->is_selected)
481 {
482 if(G_LIKELY(item != desktop->focus))
483 {
484 items = g_list_prepend(items, item);
485 ++n;
486 }
487 else
488 focus = item;
489 }
490 }
491 while(gtk_tree_model_iter_next(model, &it));
492 items = g_list_reverse(items);
493 if(focus)
494 {
495 items = g_list_prepend(items, focus);
496 ++n;
497 }
498 if(n_items)
499 *n_items = n;
500 return items;
501 }
502
copy_desktop_config(FmDesktopConfig * dst,FmDesktopConfig * src)503 static void copy_desktop_config(FmDesktopConfig *dst, FmDesktopConfig *src)
504 {
505 int i;
506
507 dst->wallpaper_mode = src->wallpaper_mode;
508 dst->wallpaper = g_strdup(src->wallpaper);
509 if (src->wallpapers_configured > 0)
510 {
511 dst->wallpapers = g_new(char *, src->wallpapers_configured);
512 for (i = 0; i < src->wallpapers_configured; i++)
513 dst->wallpapers[i] = g_strdup(src->wallpapers[i]);
514 }
515 else
516 dst->wallpapers = NULL;
517 dst->wallpapers_configured = src->wallpapers_configured;
518 dst->wallpaper_common = src->wallpaper_common;
519 dst->show_wm_menu = src->show_wm_menu;
520 dst->configured = TRUE;
521 dst->changed = FALSE;
522 dst->desktop_bg = src->desktop_bg;
523 dst->desktop_fg = src->desktop_fg;
524 dst->desktop_shadow = src->desktop_shadow;
525 dst->desktop_font = g_strdup(src->desktop_font);
526 dst->desktop_sort_type = src->desktop_sort_type;
527 dst->desktop_sort_by = src->desktop_sort_by;
528 dst->folder = g_strdup(src->folder);
529 #if FM_CHECK_VERSION(1, 2, 0)
530 dst->show_documents = src->show_documents;
531 dst->show_trash = src->show_trash;
532 dst->show_mounts = src->show_mounts;
533 #endif
534
535 }
536
537 static GVolumeMonitor *vol_mon = NULL;
538
539 static void _free_extra_item(FmDesktopExtraItem *item);
540
on_idle_extra_item_add(gpointer user_data)541 static gboolean on_idle_extra_item_add(gpointer user_data)
542 {
543 FmDesktopExtraItem *item = user_data;
544 int i;
545
546 /* if mount is not NULL then it's new mount so add it to the list */
547 if (item->mount)
548 {
549 mounts = g_slist_append(mounts, item);
550 /* add it to all models that watch mounts */
551 for (i = 0; i < n_screens; i++)
552 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_mounts
553 && desktops[i]->model)
554 fm_folder_model_extra_file_add(desktops[i]->model, item->fi,
555 FM_FOLDER_MODEL_ITEMPOS_POST);
556 }
557 else if (item == documents)
558 {
559 /* add it to all models that watch documents */
560 for (i = 0; i < n_screens; i++)
561 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_documents
562 && desktops[i]->model)
563 {
564 fm_folder_model_extra_file_add(desktops[i]->model, item->fi,
565 FM_FOLDER_MODEL_ITEMPOS_PRE);
566 /* if this is extra item it might be loaded after the folder
567 therefore we have to reload fixed positions again to apply */
568 reload_items(desktops[i]);
569 }
570 }
571 else if (item == trash_can)
572 {
573 /* add it to all models that watch trash can */
574 for (i = 0; i < n_screens; i++)
575 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_trash
576 && desktops[i]->model)
577 {
578 fm_folder_model_extra_file_add(desktops[i]->model, item->fi,
579 FM_FOLDER_MODEL_ITEMPOS_PRE);
580 reload_items(desktops[i]);
581 }
582 }
583 else
584 {
585 g_critical("got file info for unknown desktop item %s",
586 fm_path_get_basename(item->path));
587 _free_extra_item(item);
588 }
589 return FALSE;
590 }
591
592 static gboolean trash_is_empty = FALSE; /* startup default */
593
594 /* returns TRUE if model should be updated */
_update_trash_icon(FmDesktopExtraItem * item)595 static gboolean _update_trash_icon(FmDesktopExtraItem *item)
596 {
597 GFile *gf = fm_file_new_for_uri("trash:///");
598 GFileInfo *inf = g_file_query_info(gf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, 0, NULL, NULL);
599 const char *icon_name;
600 GIcon *icon;
601 guint32 n;
602
603 g_object_unref(gf);
604 if (!inf)
605 return FALSE;
606
607 n = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
608 g_object_unref(inf);
609 if (n > 0 && trash_is_empty)
610 icon_name = "user-trash-full";
611 else if (n == 0 && !trash_is_empty)
612 icon_name = "user-trash";
613 else /* not changed */
614 return FALSE;
615 trash_is_empty = (n == 0);
616 icon = g_themed_icon_new_with_default_fallbacks(icon_name);
617 fm_file_info_set_icon(item->fi, icon);
618 g_object_unref(icon);
619 return TRUE;
620 }
621
on_file_info_job_finished(FmFileInfoJob * job,gpointer user_data)622 static void on_file_info_job_finished(FmFileInfoJob* job, gpointer user_data)
623 {
624 FmDesktopExtraItem *item = user_data;
625 FmFileInfo *fi;
626 char *name;
627 GIcon *icon;
628
629 if(fm_file_info_list_is_empty(job->file_infos))
630 {
631 /* failed */
632 g_critical("FmFileInfoJob failed on desktop mount update");
633 _free_extra_item(item);
634 return;
635 }
636 fi = fm_file_info_list_peek_head(job->file_infos);
637 /* FIXME: check for duplicates? */
638 item->fi = fm_file_info_ref(fi);
639 item->job = NULL;
640 g_object_unref(job);
641 /* update some data with those from the mount */
642 if (item->mount)
643 {
644 name = g_mount_get_name(item->mount);
645 fm_file_info_set_disp_name(fi, name);
646 g_free(name);
647 icon = g_mount_get_icon(item->mount);
648 fm_file_info_set_icon(fi, icon);
649 g_object_unref(icon);
650 }
651 /* update trash can icon */
652 else if (item == trash_can)
653 _update_trash_icon(item);
654 /* queue adding item to the list and folder models */
655 gdk_threads_add_idle(on_idle_extra_item_add, item);
656 }
657
_free_extra_item(FmDesktopExtraItem * item)658 static void _free_extra_item(FmDesktopExtraItem *item)
659 {
660 if (item->mount)
661 g_object_unref(item->mount);
662 fm_path_unref(item->path);
663 if (item->fi)
664 fm_file_info_unref(item->fi);
665 if (item->job)
666 {
667 g_signal_handlers_disconnect_by_func(item->job, on_file_info_job_finished, item);
668 fm_job_cancel(FM_JOB(item->job));
669 g_object_unref(item->job);
670 }
671 g_slice_free(FmDesktopExtraItem, item);
672 }
673
on_mount_added(GVolumeMonitor * volume_monitor,GMount * mount,gpointer _unused)674 static void on_mount_added(GVolumeMonitor *volume_monitor, GMount *mount,
675 gpointer _unused)
676 {
677 GFile *file;
678 FmDesktopExtraItem *item;
679
680 /* get file info for the mount point */
681 item = g_slice_new(FmDesktopExtraItem);
682 item->mount = g_object_ref(mount);
683 file = g_mount_get_root(mount);
684 item->path = fm_path_new_for_gfile(file);
685 g_object_unref(file);
686 item->fi = NULL;
687 item->job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
688 fm_file_info_job_add(item->job, item->path);
689 g_signal_connect(item->job, "finished", G_CALLBACK(on_file_info_job_finished), item);
690 if (!fm_job_run_async(FM_JOB(item->job)))
691 {
692 g_critical("fm_job_run_async() failed on desktop mount update");
693 _free_extra_item(item);
694 }
695 }
696
on_idle_extra_item_remove(gpointer user_data)697 static gboolean on_idle_extra_item_remove(gpointer user_data)
698 {
699 GMount *mount = user_data;
700 GSList *sl;
701 FmDesktopExtraItem *item;
702 int i;
703
704 for (sl = mounts; sl; sl = sl->next)
705 {
706 item = sl->data;
707 if (item->mount == mount)
708 break;
709 }
710 if (sl)
711 {
712 for (i = 0; i < n_screens; i++)
713 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_mounts
714 && desktops[i]->model)
715 fm_folder_model_extra_file_remove(desktops[i]->model, item->fi);
716 mounts = g_slist_delete_link(mounts, sl);
717 _free_extra_item(item);
718 }
719 else
720 g_warning("got unmount for unknown desktop item");
721 g_object_unref(mount);
722 return FALSE;
723 }
724
on_mount_removed(GVolumeMonitor * volume_monitor,GMount * mount,gpointer _unused)725 static void on_mount_removed(GVolumeMonitor *volume_monitor, GMount *mount,
726 gpointer _unused)
727 {
728 gdk_threads_add_idle(on_idle_extra_item_remove, g_object_ref(mount));
729 }
730
731
732 /* ---------------------------------------------------------------------
733 special items handlers */
734
735 static GFileMonitor *trash_monitor = NULL;
736
on_trash_changed(GFileMonitor * monitor,GFile * gf,GFile * other,GFileMonitorEvent evt,FmDesktopExtraItem * item)737 static void on_trash_changed(GFileMonitor *monitor, GFile *gf, GFile *other,
738 GFileMonitorEvent evt, FmDesktopExtraItem *item)
739 {
740 int i;
741
742 if (!_update_trash_icon(item))
743 return;
744 for (i = 0; i < n_screens; i++)
745 if (desktops[i]->monitor >= 0 && desktops[i]->conf.show_trash
746 && desktops[i]->model)
747 fm_folder_model_file_changed(desktops[i]->model, item->fi);
748 }
749
_add_extra_item(const char * path_str)750 static FmDesktopExtraItem *_add_extra_item(const char *path_str)
751 {
752 FmDesktopExtraItem *item;
753
754 if (path_str == NULL) /* special directory does not exist */
755 return NULL;
756
757 item = g_slice_new(FmDesktopExtraItem);
758 item->mount = NULL;
759 item->path = fm_path_new_for_str(path_str);
760 item->fi = NULL;
761 item->job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
762 fm_file_info_job_add(item->job, item->path);
763 g_signal_connect(item->job, "finished", G_CALLBACK(on_file_info_job_finished), item);
764 if (!fm_job_run_async(FM_JOB(item->job)))
765 {
766 g_critical("fm_job_run_async() failed on desktop special item update");
767 _free_extra_item(item);
768 item = NULL;
769 }
770 return item;
771 }
772 #endif
773
774 /* ---------------------------------------------------------------------
775 accessibility handlers */
776
777 static void set_focused_item(FmDesktop* desktop, FmDesktopItem* item);
778 static inline gint fm_desktop_accessible_index(GtkWidget *desktop, gpointer item);
779
780 /* ---- accessible item mirror ---- */
781 typedef struct _FmDesktopItemAccessible FmDesktopItemAccessible;
782 typedef struct _FmDesktopItemAccessibleClass FmDesktopItemAccessibleClass;
783 #define FM_TYPE_DESKTOP_ITEM_ACCESSIBLE (fm_desktop_item_accessible_get_type ())
784 #define FM_DESKTOP_ITEM_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
785 FM_TYPE_DESKTOP_ITEM_ACCESSIBLE, FmDesktopItemAccessible))
786 #define FM_IS_DESKTOP_ITEM_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
787 FM_TYPE_DESKTOP_ITEM_ACCESSIBLE))
788
789 static GType fm_desktop_item_accessible_get_type (void);
790
791 struct _FmDesktopItemAccessible
792 {
793 AtkObject parent;
794 FmDesktopItem *item;
795 GtkWidget *widget;
796 AtkStateSet *state_set;
797 guint action_idle_handler;
798 gint action_type;
799 };
800
801 struct _FmDesktopItemAccessibleClass
802 {
803 AtkObjectClass parent_class;
804 };
805
806 static void atk_component_item_interface_init(AtkComponentIface *iface);
807 static void atk_action_item_interface_init(AtkActionIface *iface);
808 static void atk_image_item_interface_init(AtkImageIface *iface);
809
G_DEFINE_TYPE_WITH_CODE(FmDesktopItemAccessible,fm_desktop_item_accessible,ATK_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,atk_component_item_interface_init)G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION,atk_action_item_interface_init)G_IMPLEMENT_INTERFACE (ATK_TYPE_IMAGE,atk_image_item_interface_init))810 G_DEFINE_TYPE_WITH_CODE(FmDesktopItemAccessible, fm_desktop_item_accessible, ATK_TYPE_OBJECT,
811 G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT, atk_component_item_interface_init)
812 G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION, atk_action_item_interface_init)
813 G_IMPLEMENT_INTERFACE(ATK_TYPE_IMAGE, atk_image_item_interface_init))
814
815 static void fm_desktop_item_accessible_init(FmDesktopItemAccessible *item)
816 {
817 item->state_set = atk_state_set_new();
818 atk_state_set_add_state(item->state_set, ATK_STATE_ENABLED);
819 atk_state_set_add_state(item->state_set, ATK_STATE_FOCUSABLE);
820 atk_state_set_add_state(item->state_set, ATK_STATE_SENSITIVE);
821 atk_state_set_add_state(item->state_set, ATK_STATE_SELECTABLE);
822 atk_state_set_add_state(item->state_set, ATK_STATE_VISIBLE);
823 item->action_idle_handler = 0;
824 }
825
fm_desktop_item_accessible_finalize(GObject * object)826 static void fm_desktop_item_accessible_finalize(GObject *object)
827 {
828 FmDesktopItemAccessible *item = (FmDesktopItemAccessible *)object;
829
830 g_return_if_fail(item != NULL);
831 g_return_if_fail(FM_IS_DESKTOP_ITEM_ACCESSIBLE(item));
832
833 if (item->widget)
834 g_object_remove_weak_pointer(G_OBJECT(item->widget), (gpointer)&item->widget);
835 if (item->state_set)
836 g_object_unref (item->state_set);
837 if (item->action_idle_handler)
838 {
839 g_source_remove(item->action_idle_handler);
840 item->action_idle_handler = 0;
841 }
842 }
843
fm_desktop_item_accessible_new(FmDesktop * desktop,FmDesktopItem * item)844 static FmDesktopItemAccessible *fm_desktop_item_accessible_new(FmDesktop *desktop,
845 FmDesktopItem *item)
846 {
847 const char *name;
848 FmDesktopItemAccessible *atk_item;
849
850 name = fm_file_info_get_disp_name(item->fi);
851 atk_item = g_object_new(FM_TYPE_DESKTOP_ITEM_ACCESSIBLE,
852 "accessible-name", name, NULL);
853 atk_item->item = item;
854 atk_item->widget = GTK_WIDGET(desktop);
855 g_object_add_weak_pointer(G_OBJECT(desktop), (gpointer)&atk_item->widget);
856 return atk_item;
857 }
858
859 /* item interfaces */
fm_desktop_item_accessible_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)860 static void fm_desktop_item_accessible_get_extents(AtkComponent *component,
861 gint *x, gint *y,
862 gint *width, gint *height,
863 AtkCoordType coord_type)
864 {
865 FmDesktopItemAccessible *item;
866 AtkObject *parent_obj;
867 gint l_x, l_y;
868
869 g_return_if_fail(FM_IS_DESKTOP_ITEM_ACCESSIBLE(component));
870 item = FM_DESKTOP_ITEM_ACCESSIBLE(component);
871 if (item->widget == NULL)
872 return;
873 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
874 return;
875
876 *width = item->item->area.width;
877 *height = item->item->area.height;
878 parent_obj = gtk_widget_get_accessible(item->widget);
879 atk_component_get_extents(ATK_COMPONENT(parent_obj), &l_x, &l_y, NULL, NULL,
880 coord_type);
881 *x = l_x + item->item->area.x;
882 *y = l_y + item->item->area.y;
883 }
884
fm_desktop_item_accessible_grab_focus(AtkComponent * component)885 static gboolean fm_desktop_item_accessible_grab_focus(AtkComponent *component)
886 {
887 FmDesktopItemAccessible *item;
888
889 g_return_val_if_fail(FM_IS_DESKTOP_ITEM_ACCESSIBLE(component), FALSE);
890 item = FM_DESKTOP_ITEM_ACCESSIBLE(component);
891 if (item->widget == NULL)
892 return FALSE;
893 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
894 return FALSE;
895
896 gtk_widget_grab_focus(item->widget);
897 set_focused_item(FM_DESKTOP(item->widget), item->item);
898 return TRUE;
899 }
900
atk_component_item_interface_init(AtkComponentIface * iface)901 static void atk_component_item_interface_init(AtkComponentIface *iface)
902 {
903 iface->get_extents = fm_desktop_item_accessible_get_extents;
904 iface->grab_focus = fm_desktop_item_accessible_grab_focus;
905 }
906
907 /* NOTE: this is not very fast */
fm_desktop_item_get_tree_path(FmDesktop * self,FmDesktopItem * item)908 static GtkTreePath *fm_desktop_item_get_tree_path(FmDesktop *self, FmDesktopItem *item)
909 {
910 GtkTreeModel *model = self->model ? GTK_TREE_MODEL(self->model) : NULL;
911 GtkTreeIter it;
912
913 if(model && gtk_tree_model_get_iter_first(model, &it)) do
914 {
915 if (fm_folder_model_get_item_userdata(self->model, &it) == item)
916 return gtk_tree_model_get_path(model, &it);
917 }
918 while (gtk_tree_model_iter_next(model, &it));
919 return NULL;
920 }
921
fm_desktop_item_accessible_idle_do_action(gpointer data)922 static gboolean fm_desktop_item_accessible_idle_do_action(gpointer data)
923 {
924 FmDesktopItemAccessible *item;
925 GtkTreePath *tp;
926
927 if(g_source_is_destroyed(g_main_current_source()))
928 return FALSE;
929
930 item = FM_DESKTOP_ITEM_ACCESSIBLE(data);
931 item->action_idle_handler = 0;
932 if (item->widget != NULL)
933 {
934 tp = fm_desktop_item_get_tree_path(FM_DESKTOP(item->widget), item->item);
935 if (tp)
936 {
937 fm_folder_view_item_clicked(FM_FOLDER_VIEW(item->widget), tp,
938 item->action_type == 0 ? FM_FV_ACTIVATED : FM_FV_CONTEXT_MENU);
939 gtk_tree_path_free(tp);
940 }
941 }
942 return FALSE;
943 }
944
fm_desktop_item_accessible_action_do_action(AtkAction * action,gint i)945 static gboolean fm_desktop_item_accessible_action_do_action(AtkAction *action,
946 gint i)
947 {
948 FmDesktopItemAccessible *item;
949
950 if (i != 0 && i != 1)
951 return FALSE;
952
953 item = FM_DESKTOP_ITEM_ACCESSIBLE(action);
954 if (item->widget == NULL)
955 return FALSE;
956 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
957 return FALSE;
958 if (!item->action_idle_handler)
959 {
960 item->action_type = i;
961 item->action_idle_handler = gdk_threads_add_idle(fm_desktop_item_accessible_idle_do_action, item);
962 }
963 return TRUE;
964 }
965
fm_desktop_item_accessible_action_get_n_actions(AtkAction * action)966 static gint fm_desktop_item_accessible_action_get_n_actions(AtkAction *action)
967 {
968 return 2;
969 }
970
fm_desktop_item_accessible_action_get_description(AtkAction * action,gint i)971 static const gchar *fm_desktop_item_accessible_action_get_description(AtkAction *action, gint i)
972 {
973 if (i == 0)
974 return _("Activate file");
975 else if (i == 1)
976 return _("Show file menu");
977 return NULL;
978 }
979
fm_desktop_item_accessible_action_get_name(AtkAction * action,gint i)980 static const gchar *fm_desktop_item_accessible_action_get_name(AtkAction *action, gint i)
981 {
982 if (i == 0)
983 return "activate";
984 if (i == 1)
985 return "menu";
986 return NULL;
987 }
988
atk_action_item_interface_init(AtkActionIface * iface)989 static void atk_action_item_interface_init(AtkActionIface *iface)
990 {
991 iface->do_action = fm_desktop_item_accessible_action_do_action;
992 iface->get_n_actions = fm_desktop_item_accessible_action_get_n_actions;
993 iface->get_description = fm_desktop_item_accessible_action_get_description;
994 iface->get_name = fm_desktop_item_accessible_action_get_name;
995 /* NOTE: we don't support descriptions change */
996 }
997
fm_desktop_item_accessible_image_get_image_description(AtkImage * image)998 static const gchar *fm_desktop_item_accessible_image_get_image_description(AtkImage *image)
999 {
1000 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(image);
1001
1002 /* FIXME: is there a better way to handle this? */
1003 return fm_file_info_get_desc(item->item->fi);
1004 }
1005
fm_desktop_item_accessible_image_get_image_size(AtkImage * image,gint * width,gint * height)1006 static void fm_desktop_item_accessible_image_get_image_size(AtkImage *image,
1007 gint *width,
1008 gint *height)
1009 {
1010 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(image);
1011
1012 if (item->widget == NULL)
1013 return;
1014 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
1015 return;
1016
1017 *width = item->item->icon_rect.width;
1018 *height = item->item->icon_rect.height;
1019 }
1020
fm_desktop_item_accessible_image_get_image_position(AtkImage * image,gint * x,gint * y,AtkCoordType coord_type)1021 static void fm_desktop_item_accessible_image_get_image_position(AtkImage *image,
1022 gint *x,
1023 gint *y,
1024 AtkCoordType coord_type)
1025 {
1026 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(image);
1027 AtkObject *parent_obj;
1028
1029 if (item->widget == NULL)
1030 return;
1031 if (atk_state_set_contains_state(item->state_set, ATK_STATE_DEFUNCT))
1032 return;
1033
1034 parent_obj = gtk_widget_get_accessible(item->widget);
1035 atk_component_get_extents(ATK_COMPONENT(parent_obj), x, y, NULL, NULL, coord_type);
1036 *x += item->item->icon_rect.x - item->item->area.x;
1037 *y += item->item->icon_rect.y - item->item->area.y;
1038 }
1039
atk_image_item_interface_init(AtkImageIface * iface)1040 static void atk_image_item_interface_init(AtkImageIface *iface)
1041 {
1042 iface->get_image_description = fm_desktop_item_accessible_image_get_image_description;
1043 /* NOTE: we don't support descriptions change */
1044 iface->get_image_size = fm_desktop_item_accessible_image_get_image_size;
1045 iface->get_image_position = fm_desktop_item_accessible_image_get_image_position;
1046 }
1047
fm_desktop_item_accessible_get_parent(AtkObject * obj)1048 static AtkObject *fm_desktop_item_accessible_get_parent(AtkObject *obj)
1049 {
1050 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(obj);
1051
1052 return item->widget ? gtk_widget_get_accessible(item->widget) : NULL;
1053 }
1054
fm_desktop_item_accessible_get_index_in_parent(AtkObject * obj)1055 static gint fm_desktop_item_accessible_get_index_in_parent(AtkObject *obj)
1056 {
1057 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(obj);
1058
1059 return item->widget ? fm_desktop_accessible_index(item->widget, item) : -1;
1060 }
1061
fm_desktop_item_accessible_ref_state_set(AtkObject * obj)1062 static AtkStateSet *fm_desktop_item_accessible_ref_state_set(AtkObject *obj)
1063 {
1064 FmDesktopItemAccessible *item = FM_DESKTOP_ITEM_ACCESSIBLE(obj);
1065 FmDesktop *desktop;
1066
1067 g_return_val_if_fail(item->state_set, NULL);
1068
1069 /* update states first */
1070 if (item->widget != NULL)
1071 {
1072 desktop = (FmDesktop *)item->widget;
1073 if (desktop->focus == item->item)
1074 atk_state_set_add_state(item->state_set, ATK_STATE_FOCUSED);
1075 else
1076 atk_state_set_remove_state(item->state_set, ATK_STATE_FOCUSED);
1077 }
1078 if (item->item->is_selected)
1079 atk_state_set_add_state(item->state_set, ATK_STATE_SELECTED);
1080 else
1081 atk_state_set_remove_state(item->state_set, ATK_STATE_SELECTED);
1082 return g_object_ref(item->state_set);
1083 }
1084
fm_desktop_item_accessible_class_init(FmDesktopItemAccessibleClass * klass)1085 static void fm_desktop_item_accessible_class_init(FmDesktopItemAccessibleClass *klass)
1086 {
1087 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1088 AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
1089
1090 gobject_class->finalize = fm_desktop_item_accessible_finalize;
1091
1092 atk_class->get_index_in_parent = fm_desktop_item_accessible_get_index_in_parent;
1093 atk_class->get_parent = fm_desktop_item_accessible_get_parent;
1094 atk_class->ref_state_set = fm_desktop_item_accessible_ref_state_set;
1095 }
1096
fm_desktop_item_accessible_add_state(FmDesktopItemAccessible * item,AtkStateType state_type)1097 static gboolean fm_desktop_item_accessible_add_state(FmDesktopItemAccessible *item,
1098 AtkStateType state_type)
1099 {
1100 gboolean rc;
1101
1102 rc = atk_state_set_add_state(item->state_set, state_type);
1103 atk_object_notify_state_change(ATK_OBJECT (item), state_type, TRUE);
1104 /* If state_type is ATK_STATE_VISIBLE, additional notification */
1105 if (state_type == ATK_STATE_VISIBLE)
1106 g_signal_emit_by_name(item, "visible-data-changed");
1107 return rc;
1108 }
1109
1110 /* ---- accessible widget mirror ---- */
1111 typedef struct _FmDesktopAccessible FmDesktopAccessible;
1112 typedef struct _FmDesktopAccessibleClass FmDesktopAccessibleClass;
1113 #define FM_TYPE_DESKTOP_ACCESSIBLE (fm_desktop_accessible_get_type())
1114 #define FM_DESKTOP_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
1115 FM_TYPE_DESKTOP_ACCESSIBLE, FmDesktopAccessible))
1116 #define FM_IS_DESKTOP_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
1117 FM_TYPE_DESKTOP_ACCESSIBLE))
1118
1119 static GType fm_desktop_accessible_get_type (void);
1120
1121 typedef struct _FmDesktopAccessiblePriv FmDesktopAccessiblePriv;
1122 struct _FmDesktopAccessiblePriv
1123 {
1124 /* we don't catch model index but have own index in items list instead */
1125 GList *items;
1126 guint action_idle_handler;
1127 };
1128
1129 #define FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(_d_) G_TYPE_INSTANCE_GET_PRIVATE(_d_, FM_TYPE_DESKTOP_ACCESSIBLE, FmDesktopAccessiblePriv)
1130
1131 static void atk_component_interface_init(AtkComponentIface *iface);
1132 static void atk_selection_interface_init(AtkSelectionIface *iface);
1133 static void atk_action_interface_init(AtkActionIface *iface);
1134
1135 /* we cannot use G_DEFINE_TYPE_WITH_CODE - no GtkWindowAccessible before 3.2.0 */
1136 static void fm_desktop_accessible_init(FmDesktopAccessible *self);
1137 static void fm_desktop_accessible_class_init(FmDesktopAccessibleClass *klass);
1138 static gpointer fm_desktop_accessible_parent_class = NULL;
1139
fm_desktop_accessible_class_intern_init(gpointer klass)1140 static void fm_desktop_accessible_class_intern_init(gpointer klass)
1141 {
1142 fm_desktop_accessible_parent_class = g_type_class_peek_parent(klass);
1143 fm_desktop_accessible_class_init((FmDesktopAccessibleClass*)klass);
1144 }
1145
fm_desktop_accessible_get_type(void)1146 GType fm_desktop_accessible_get_type(void)
1147 {
1148 static volatile gsize type_id_volatile = 0;
1149
1150 if (g_once_init_enter(&type_id_volatile))
1151 {
1152 /*
1153 * Figure out the size of the class and instance
1154 * we are deriving from
1155 */
1156 AtkObjectFactory *factory;
1157 GTypeQuery query;
1158 GType derived_atk_type;
1159 GType g_define_type_id;
1160
1161 factory = atk_registry_get_factory(atk_get_default_registry(),
1162 g_type_parent(FM_TYPE_DESKTOP));
1163 derived_atk_type = atk_object_factory_get_accessible_type(factory);
1164 g_type_query(derived_atk_type, &query);
1165 g_define_type_id = g_type_register_static_simple(derived_atk_type,
1166 g_intern_static_string("FmDesktopAccessible"),
1167 query.class_size,
1168 (GClassInitFunc)fm_desktop_accessible_class_intern_init,
1169 query.instance_size,
1170 (GInstanceInitFunc)fm_desktop_accessible_init,
1171 0);
1172 G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT, atk_component_interface_init)
1173 G_IMPLEMENT_INTERFACE(ATK_TYPE_SELECTION, atk_selection_interface_init)
1174 G_IMPLEMENT_INTERFACE(ATK_TYPE_ACTION, atk_action_interface_init)
1175 g_once_init_leave(&type_id_volatile, g_define_type_id);
1176 }
1177 return type_id_volatile;
1178 }
1179
fm_desktop_find_accessible_for_item(FmDesktopAccessiblePriv * priv,FmDesktopItem * item)1180 static inline GList *fm_desktop_find_accessible_for_item(FmDesktopAccessiblePriv *priv, FmDesktopItem *item)
1181 {
1182 GList *items = priv->items;
1183
1184 while (items)
1185 {
1186 if (((FmDesktopItemAccessible *)items->data)->item == item)
1187 return items;
1188 items = items->next;
1189 }
1190 return NULL;
1191 }
1192
1193 /* widget interfaces */
fm_desktop_accessible_ref_accessible_at_point(AtkComponent * component,gint x,gint y,AtkCoordType coord_type)1194 static AtkObject *fm_desktop_accessible_ref_accessible_at_point(AtkComponent *component,
1195 gint x, gint y,
1196 AtkCoordType coord_type)
1197 {
1198 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(component));
1199 FmDesktop *desktop;
1200 gint x_pos, y_pos;
1201 FmDesktopItem *item;
1202 GList *obj_l = NULL;
1203 GtkTreeIter it;
1204
1205 if (widget == NULL)
1206 return NULL;
1207 desktop = FM_DESKTOP(widget);
1208 atk_component_get_extents(component, &x_pos, &y_pos, NULL, NULL, coord_type);
1209 item = hit_test(desktop, &it, x - x_pos, y - y_pos);
1210 if (item)
1211 obj_l = fm_desktop_find_accessible_for_item(FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(component), item);
1212 if (obj_l)
1213 return g_object_ref(obj_l->data);
1214 return NULL;
1215 }
1216
atk_component_interface_init(AtkComponentIface * iface)1217 static void atk_component_interface_init(AtkComponentIface *iface)
1218 {
1219 iface->ref_accessible_at_point = fm_desktop_accessible_ref_accessible_at_point;
1220 }
1221
fm_desktop_accessible_add_selection(AtkSelection * selection,gint i)1222 static gboolean fm_desktop_accessible_add_selection(AtkSelection *selection, gint i)
1223 {
1224 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(selection));
1225 FmDesktop *desktop;
1226 FmDesktopAccessiblePriv *priv;
1227 FmDesktopItemAccessible *item;
1228
1229 if (widget == NULL)
1230 return FALSE;
1231
1232 desktop = FM_DESKTOP(widget);
1233 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1234 item = g_list_nth_data(priv->items, i);
1235 if (!item)
1236 return FALSE;
1237 item->item->is_selected = TRUE;
1238 redraw_item(desktop, item->item);
1239 atk_object_notify_state_change(ATK_OBJECT(item), ATK_STATE_SELECTED, TRUE);
1240 return TRUE;
1241 }
1242
fm_desktop_accessible_clear_selection(AtkSelection * selection)1243 static gboolean fm_desktop_accessible_clear_selection(AtkSelection *selection)
1244 {
1245 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(selection));
1246
1247 if (widget == NULL)
1248 return FALSE;
1249
1250 _unselect_all(FM_FOLDER_VIEW(widget));
1251 return TRUE;
1252 }
1253
fm_desktop_accessible_ref_selection(AtkSelection * selection,gint i)1254 static AtkObject *fm_desktop_accessible_ref_selection(AtkSelection *selection,
1255 gint i)
1256 {
1257 FmDesktopAccessiblePriv *priv;
1258 FmDesktopItemAccessible *item;
1259 GList *items;
1260
1261 if (i < 0)
1262 return NULL;
1263
1264 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1265 for (items = priv->items; items; items = items->next)
1266 {
1267 item = items->data;
1268 if (item->item->is_selected)
1269 if (i-- == 0)
1270 return g_object_ref(item);
1271 }
1272 return NULL;
1273 }
1274
fm_desktop_accessible_get_selection_count(AtkSelection * selection)1275 static gint fm_desktop_accessible_get_selection_count(AtkSelection *selection)
1276 {
1277 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1278 FmDesktopItemAccessible *item;
1279 GList *items;
1280 gint i = 0;
1281
1282 for (items = priv->items; items; items = items->next)
1283 {
1284 item = items->data;
1285 if (item->item->is_selected)
1286 i++;
1287 }
1288 return i;
1289 }
1290
fm_desktop_accessible_is_child_selected(AtkSelection * selection,gint i)1291 static gboolean fm_desktop_accessible_is_child_selected(AtkSelection *selection,
1292 gint i)
1293 {
1294 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1295 FmDesktopItemAccessible *item = g_list_nth_data(priv->items, i);
1296
1297 if (item == NULL)
1298 return FALSE;
1299 return item->item->is_selected;
1300 }
1301
fm_desktop_accessible_remove_selection(AtkSelection * selection,gint i)1302 static gboolean fm_desktop_accessible_remove_selection(AtkSelection *selection,
1303 gint i)
1304 {
1305 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(selection));
1306 FmDesktop *desktop;
1307 FmDesktopAccessiblePriv *priv;
1308 FmDesktopItemAccessible *item;
1309 GList *items;
1310
1311 if (i < 0)
1312 return FALSE;
1313 if (widget == NULL)
1314 return FALSE;
1315 desktop = FM_DESKTOP(widget);
1316
1317 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(selection);
1318 for (items = priv->items; items; items = items->next)
1319 {
1320 item = items->data;
1321 if (item->item->is_selected)
1322 if (i-- == 0)
1323 {
1324 item->item->is_selected = FALSE;
1325 redraw_item(desktop, item->item);
1326 atk_object_notify_state_change(ATK_OBJECT(item), ATK_STATE_SELECTED, FALSE);
1327 return TRUE;
1328 }
1329 }
1330 return FALSE;
1331 }
1332
fm_desktop_accessible_select_all_selection(AtkSelection * selection)1333 static gboolean fm_desktop_accessible_select_all_selection(AtkSelection *selection)
1334 {
1335 GtkWidget *widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(selection));
1336
1337 if (widget == NULL)
1338 return FALSE;
1339
1340 _select_all(FM_FOLDER_VIEW(widget));
1341 return TRUE;
1342 }
1343
atk_selection_interface_init(AtkSelectionIface * iface)1344 static void atk_selection_interface_init(AtkSelectionIface *iface)
1345 {
1346 iface->add_selection = fm_desktop_accessible_add_selection;
1347 iface->clear_selection = fm_desktop_accessible_clear_selection;
1348 iface->ref_selection = fm_desktop_accessible_ref_selection;
1349 iface->get_selection_count = fm_desktop_accessible_get_selection_count;
1350 iface->is_child_selected = fm_desktop_accessible_is_child_selected;
1351 iface->remove_selection = fm_desktop_accessible_remove_selection;
1352 iface->select_all_selection = fm_desktop_accessible_select_all_selection;
1353 }
1354
fm_desktop_accessible_idle_do_action(gpointer data)1355 static gboolean fm_desktop_accessible_idle_do_action(gpointer data)
1356 {
1357 GtkWidget *widget;
1358 FmDesktopAccessiblePriv *priv;
1359
1360 if(g_source_is_destroyed(g_main_current_source()))
1361 return FALSE;
1362
1363 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(data);
1364 priv->action_idle_handler = 0;
1365 widget = gtk_accessible_get_widget(GTK_ACCESSIBLE(data));
1366 if (widget)
1367 fm_folder_view_item_clicked(FM_FOLDER_VIEW(widget), NULL, FM_FV_CONTEXT_MENU);
1368 return FALSE;
1369 }
1370
fm_desktop_accessible_action_do_action(AtkAction * action,gint i)1371 static gboolean fm_desktop_accessible_action_do_action(AtkAction *action, gint i)
1372 {
1373 FmDesktopAccessiblePriv *priv;
1374
1375 if (i != 0)
1376 return FALSE;
1377 if (gtk_accessible_get_widget(GTK_ACCESSIBLE(action)) == NULL)
1378 return FALSE;
1379
1380 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(action);
1381 if (!priv->action_idle_handler)
1382 priv->action_idle_handler = gdk_threads_add_idle(fm_desktop_accessible_idle_do_action, action);
1383 return TRUE;
1384 }
1385
fm_desktop_accessible_action_get_n_actions(AtkAction * action)1386 static gint fm_desktop_accessible_action_get_n_actions(AtkAction *action)
1387 {
1388 return 1;
1389 }
1390
fm_desktop_accessible_action_get_description(AtkAction * action,gint i)1391 static const gchar *fm_desktop_accessible_action_get_description(AtkAction *action, gint i)
1392 {
1393 if (i == 0)
1394 return _("Show desktop menu");
1395 return NULL;
1396 }
1397
fm_desktop_accessible_action_get_name(AtkAction * action,gint i)1398 static const gchar *fm_desktop_accessible_action_get_name(AtkAction *action, gint i)
1399 {
1400 if (i == 0)
1401 return "menu";
1402 return NULL;
1403 }
1404
atk_action_interface_init(AtkActionIface * iface)1405 static void atk_action_interface_init(AtkActionIface *iface)
1406 {
1407 iface->do_action = fm_desktop_accessible_action_do_action;
1408 iface->get_n_actions = fm_desktop_accessible_action_get_n_actions;
1409 iface->get_description = fm_desktop_accessible_action_get_description;
1410 iface->get_name = fm_desktop_accessible_action_get_name;
1411 /* NOTE: we don't support descriptions change */
1412 }
1413
fm_desktop_accessible_finalize(GObject * object)1414 static void fm_desktop_accessible_finalize(GObject *object)
1415 {
1416 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(object);
1417 FmDesktopItemAccessible *item;
1418
1419 /* FIXME: should we g_assert(priv->items) here instead? */
1420 while (priv->items)
1421 {
1422 item = priv->items->data;
1423 item->item = NULL;
1424 fm_desktop_item_accessible_add_state(item, ATK_STATE_DEFUNCT);
1425 g_signal_emit_by_name(item, "children-changed::remove", 0, NULL, NULL);
1426 priv->items = g_list_remove_link(priv->items, priv->items);
1427 g_object_unref(item);
1428 }
1429 if (priv->action_idle_handler)
1430 {
1431 g_source_remove(priv->action_idle_handler);
1432 priv->action_idle_handler = 0;
1433 }
1434 G_OBJECT_CLASS(fm_desktop_accessible_parent_class)->finalize(object);
1435 }
1436
fm_desktop_accessible_get_n_children(AtkObject * accessible)1437 static gint fm_desktop_accessible_get_n_children(AtkObject *accessible)
1438 {
1439 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(accessible);
1440
1441 return g_list_length(priv->items);
1442 }
1443
fm_desktop_accessible_ref_child(AtkObject * accessible,gint index)1444 static AtkObject *fm_desktop_accessible_ref_child(AtkObject *accessible,
1445 gint index)
1446 {
1447 FmDesktopAccessiblePriv *priv;
1448 FmDesktopItemAccessible *item;
1449
1450 if (index < 0)
1451 return NULL;
1452
1453 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(accessible);
1454 item = g_list_nth_data(priv->items, index);
1455 if (!item)
1456 return NULL;
1457 return g_object_ref(item);
1458 }
1459
fm_desktop_accessible_initialize(AtkObject * accessible,gpointer data)1460 static void fm_desktop_accessible_initialize(AtkObject *accessible, gpointer data)
1461 {
1462 if (ATK_OBJECT_CLASS(fm_desktop_accessible_parent_class)->initialize)
1463 ATK_OBJECT_CLASS(fm_desktop_accessible_parent_class)->initialize(accessible, data);
1464 atk_object_set_role(accessible, ATK_ROLE_WINDOW);
1465 /* FIXME: set name by monitor */
1466 atk_object_set_name(accessible, _("Desktop"));
1467 }
1468
fm_desktop_accessible_init(FmDesktopAccessible * object)1469 static void fm_desktop_accessible_init(FmDesktopAccessible *object)
1470 {
1471 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(object);
1472
1473 priv->items = NULL;
1474 }
1475
fm_desktop_accessible_class_init(FmDesktopAccessibleClass * klass)1476 static void fm_desktop_accessible_class_init(FmDesktopAccessibleClass *klass)
1477 {
1478 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
1479 AtkObjectClass *atk_class = ATK_OBJECT_CLASS(klass);
1480
1481 gobject_class->finalize = fm_desktop_accessible_finalize;
1482
1483 atk_class->get_n_children = fm_desktop_accessible_get_n_children;
1484 atk_class->ref_child = fm_desktop_accessible_ref_child;
1485 atk_class->initialize = fm_desktop_accessible_initialize;
1486
1487 g_type_class_add_private(klass, sizeof(FmDesktopAccessiblePriv));
1488 }
1489
1490 /* ---- interface implementation ---- */
1491 /* handy ATK support is added only in 3.2.0 so we should handle it manually */
1492 static GType fm_desktop_accessible_factory_get_type (void);
1493
1494 typedef struct {
1495 AtkObjectFactory parent;
1496 } FmDesktopAccessibleFactory;
1497
1498 typedef struct {
1499 AtkObjectFactoryClass parent_class;
1500 } FmDesktopAccessibleFactoryClass;
1501
G_DEFINE_TYPE_WITH_CODE(FmDesktopAccessibleFactory,fm_desktop_accessible_factory,ATK_TYPE_OBJECT_FACTORY,atk_registry_set_factory_type (atk_get_default_registry (),FM_TYPE_DESKTOP,g_define_type_id);)1502 G_DEFINE_TYPE_WITH_CODE(FmDesktopAccessibleFactory, fm_desktop_accessible_factory, ATK_TYPE_OBJECT_FACTORY,
1503 atk_registry_set_factory_type(atk_get_default_registry(),
1504 FM_TYPE_DESKTOP,
1505 g_define_type_id); )
1506
1507 static GType fm_desktop_accessible_factory_get_accessible_type(void)
1508 {
1509 return FM_TYPE_DESKTOP_ACCESSIBLE;
1510 }
1511
fm_desktop_accessible_factory_create_accessible(GObject * object)1512 static AtkObject *fm_desktop_accessible_factory_create_accessible(GObject *object)
1513 {
1514 AtkObject *accessible;
1515
1516 g_return_val_if_fail(FM_IS_DESKTOP(object), NULL);
1517
1518 accessible = g_object_new(FM_TYPE_DESKTOP_ACCESSIBLE, NULL);
1519 atk_object_initialize(accessible, object);
1520 return accessible;
1521 }
1522
fm_desktop_accessible_factory_class_init(FmDesktopAccessibleFactoryClass * klass)1523 static void fm_desktop_accessible_factory_class_init(FmDesktopAccessibleFactoryClass *klass)
1524 {
1525 AtkObjectFactoryClass *factory_class = (AtkObjectFactoryClass *)klass;
1526
1527 factory_class->create_accessible = fm_desktop_accessible_factory_create_accessible;
1528 factory_class->get_accessible_type = fm_desktop_accessible_factory_get_accessible_type;
1529 }
1530
fm_desktop_accessible_factory_init(FmDesktopAccessibleFactory * factory)1531 static void fm_desktop_accessible_factory_init(FmDesktopAccessibleFactory *factory)
1532 {
1533 }
1534
fm_desktop_get_accessible(GtkWidget * widget)1535 static AtkObject *fm_desktop_get_accessible(GtkWidget *widget)
1536 {
1537 fm_desktop_accessible_factory_get_type(); /* just to activate it */
1538 return GTK_WIDGET_CLASS(fm_desktop_parent_class)->get_accessible(widget);
1539 }
1540
fm_desktop_accessible_index(GtkWidget * desktop,gpointer item)1541 static inline gint fm_desktop_accessible_index(GtkWidget *desktop, gpointer item)
1542 {
1543 AtkObject *desktop_atk = gtk_widget_get_accessible(desktop);
1544 FmDesktopAccessiblePriv *priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(desktop_atk);
1545
1546 return g_list_index(priv->items, item);
1547 }
1548
fm_desktop_accessible_item_deleted(FmDesktop * desktop,FmDesktopItem * item)1549 static void fm_desktop_accessible_item_deleted(FmDesktop *desktop, FmDesktopItem *item)
1550 {
1551 AtkObject *obj;
1552 FmDesktopAccessiblePriv *priv;
1553 GList *item_atk_l;
1554 FmDesktopItemAccessible *item_atk;
1555 gint index;
1556
1557 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1558 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1559 {
1560 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1561 item_atk_l = fm_desktop_find_accessible_for_item(priv, item);
1562 g_return_if_fail(item_atk_l != NULL);
1563 item_atk = item_atk_l->data;
1564 item_atk->item = NULL;
1565 index = g_list_position(priv->items, item_atk_l);
1566 fm_desktop_item_accessible_add_state(item_atk, ATK_STATE_DEFUNCT);
1567 g_signal_emit_by_name(obj, "children-changed::remove", index, NULL, NULL);
1568 priv->items = g_list_remove_link(priv->items, item_atk_l);
1569 g_object_unref(item_atk);
1570 }
1571 }
1572
fm_desktop_accessible_item_added(FmDesktop * desktop,FmDesktopItem * item,guint index)1573 static void fm_desktop_accessible_item_added(FmDesktop *desktop, FmDesktopItem *item,
1574 guint index)
1575 {
1576 AtkObject *obj;
1577 FmDesktopAccessiblePriv *priv;
1578 FmDesktopItemAccessible *item_atk;
1579
1580 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1581 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1582 {
1583 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1584 item_atk = fm_desktop_item_accessible_new(desktop, item);
1585 g_warn_if_fail(index <= g_list_length(priv->items));
1586 priv->items = g_list_insert(priv->items, g_object_ref(item_atk), index);
1587 g_signal_emit_by_name(obj, "children-changed::add", index, NULL, NULL);
1588 }
1589 }
1590
fm_desktop_accessible_items_reordered(FmDesktop * desktop,GtkTreeModel * model,gint * new_order)1591 static void fm_desktop_accessible_items_reordered(FmDesktop *desktop,
1592 GtkTreeModel *model,
1593 gint *new_order)
1594 {
1595 AtkObject *obj;
1596 FmDesktopAccessiblePriv *priv;
1597 GList *new_list = NULL;
1598 int length, i;
1599
1600 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1601 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1602 {
1603 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1604 length = gtk_tree_model_iter_n_children(model, NULL);
1605 g_return_if_fail(length == (gint)g_list_length(priv->items));
1606 for (i = 0; i < length; i++)
1607 {
1608 g_assert(new_order[i] >= 0 && new_order[i] < length);
1609 new_list = g_list_prepend(new_list,
1610 g_list_nth_data(priv->items, new_order[i]));
1611 }
1612 g_list_free(priv->items);
1613 priv->items = g_list_reverse(new_list);
1614 }
1615 }
1616
fm_desktop_item_selected_changed(FmDesktop * desktop,FmDesktopItem * item)1617 static void fm_desktop_item_selected_changed(FmDesktop *desktop, FmDesktopItem *item)
1618 {
1619 AtkObject *obj;
1620 FmDesktopAccessiblePriv *priv;
1621 GList *item_atk_l;
1622
1623 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1624 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1625 {
1626 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1627 item_atk_l = fm_desktop_find_accessible_for_item(priv, item);
1628 g_return_if_fail(item_atk_l != NULL);
1629 atk_object_notify_state_change(item_atk_l->data, ATK_STATE_SELECTED,
1630 item->is_selected);
1631 }
1632 }
1633
fm_desktop_accessible_focus_set(FmDesktop * desktop,FmDesktopItem * item)1634 static void fm_desktop_accessible_focus_set(FmDesktop *desktop, FmDesktopItem *item)
1635 {
1636 AtkObject *obj;
1637 FmDesktopAccessiblePriv *priv;
1638 GList *item_atk_l;
1639
1640 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1641 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1642 {
1643 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1644 item_atk_l = fm_desktop_find_accessible_for_item(priv, item);
1645 g_return_if_fail(item_atk_l != NULL);
1646 atk_object_notify_state_change(item_atk_l->data, ATK_STATE_FOCUSED, TRUE);
1647 }
1648 }
1649
fm_desktop_accessible_focus_unset(FmDesktop * desktop,FmDesktopItem * item)1650 static void fm_desktop_accessible_focus_unset(FmDesktop *desktop, FmDesktopItem *item)
1651 {
1652 AtkObject *obj;
1653 FmDesktopAccessiblePriv *priv;
1654 GList *item_atk_l;
1655
1656 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1657 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1658 {
1659 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1660 item_atk_l = fm_desktop_find_accessible_for_item(priv, item);
1661 g_return_if_fail(item_atk_l != NULL);
1662 atk_object_notify_state_change(item_atk_l->data, ATK_STATE_FOCUSED, FALSE);
1663 }
1664 }
1665
fm_desktop_accessible_model_removed(FmDesktop * desktop)1666 static void fm_desktop_accessible_model_removed(FmDesktop *desktop)
1667 {
1668 AtkObject *obj;
1669 FmDesktopAccessiblePriv *priv;
1670 FmDesktopItemAccessible *item_atk;
1671
1672 obj = gtk_widget_get_accessible(GTK_WIDGET(desktop));
1673 if (obj != NULL && FM_IS_DESKTOP_ACCESSIBLE(obj))
1674 {
1675 priv = FM_DESKTOP_ACCESSIBLE_GET_PRIVATE(obj);
1676 while (priv->items)
1677 {
1678 item_atk = priv->items->data;
1679 item_atk->item = NULL;
1680 fm_desktop_item_accessible_add_state(item_atk, ATK_STATE_DEFUNCT);
1681 g_signal_emit_by_name(obj, "children-changed::remove", 0, NULL, NULL);
1682 priv->items = g_list_remove_link(priv->items, priv->items);
1683 g_object_unref(item_atk);
1684 }
1685 }
1686 }
1687
1688
1689 /* ---------------------------------------------------------------------
1690 Desktop drawing */
1691
get_item_rect(FmDesktopItem * item,GdkRectangle * rect)1692 static inline void get_item_rect(FmDesktopItem* item, GdkRectangle* rect)
1693 {
1694 gdk_rectangle_union(&item->icon_rect, &item->text_rect, rect);
1695 }
1696
is_pos_occupied(FmDesktop * desktop,FmDesktopItem * item)1697 static gboolean is_pos_occupied(FmDesktop* desktop, FmDesktopItem* item)
1698 {
1699 GList* l;
1700 for(l = desktop->fixed_items; l; l=l->next)
1701 {
1702 FmDesktopItem* fixed = (FmDesktopItem*)l->data;
1703 GdkRectangle rect;
1704 get_item_rect(fixed, &rect);
1705 if(gdk_rectangle_intersect(&rect, &item->icon_rect, NULL)
1706 ||gdk_rectangle_intersect(&rect, &item->text_rect, NULL))
1707 return TRUE;
1708 }
1709 return FALSE;
1710 }
1711
layout_items(FmDesktop * self)1712 static void layout_items(FmDesktop* self)
1713 {
1714 FmDesktopItem* item;
1715 GtkTreeModel* model = self->model ? GTK_TREE_MODEL(self->model) : NULL;
1716 GdkPixbuf* icon;
1717 GtkTreeIter it;
1718 int x, y, bottom;
1719 GtkTextDirection direction = gtk_widget_get_direction(GTK_WIDGET(self));
1720
1721 y = self->ymargin;
1722 bottom = self->working_area.height - self->ymargin;
1723
1724 if(!model || !gtk_tree_model_get_iter_first(model, &it))
1725 {
1726 gtk_widget_queue_draw(GTK_WIDGET(self));
1727 return;
1728 }
1729 if(direction != GTK_TEXT_DIR_RTL) /* LTR or NONE */
1730 {
1731 x = self->xmargin;
1732 do
1733 {
1734 item = fm_folder_model_get_item_userdata(self->model, &it);
1735 icon = NULL;
1736 gtk_tree_model_get(model, &it, FM_FOLDER_MODEL_COL_ICON, &icon, -1);
1737 if(item->fixed_pos)
1738 calc_item_size(self, item, icon);
1739 else
1740 {
1741 _next_position:
1742 item->area.x = self->working_area.x + x;
1743 item->area.y = self->working_area.y + y;
1744 calc_item_size(self, item, icon);
1745 /* check if item does not fit into space that left */
1746 if (item->area.y + item->area.height > bottom && y > self->ymargin)
1747 {
1748 x += self->cell_w;
1749 y = self->ymargin;
1750 goto _next_position;
1751 }
1752 /* prepare position for next item */
1753 while (self->working_area.y + y < item->area.y + item->area.height)
1754 y += self->cell_h;
1755 /* check if this position is occupied by a fixed item */
1756 if(is_pos_occupied(self, item))
1757 goto _next_position;
1758 }
1759 if(icon)
1760 g_object_unref(icon);
1761 }
1762 while(gtk_tree_model_iter_next(model, &it));
1763 }
1764 else /* RTL */
1765 {
1766 x = self->working_area.width - self->xmargin - self->cell_w;
1767 do
1768 {
1769 item = fm_folder_model_get_item_userdata(self->model, &it);
1770 icon = NULL;
1771 gtk_tree_model_get(model, &it, FM_FOLDER_MODEL_COL_ICON, &icon, -1);
1772 if(item->fixed_pos)
1773 calc_item_size(self, item, icon);
1774 else
1775 {
1776 _next_position_rtl:
1777 item->area.x = self->working_area.x + x;
1778 item->area.y = self->working_area.y + y;
1779 calc_item_size(self, item, icon);
1780 /* check if item does not fit into space that left */
1781 if (item->area.y + item->area.height > bottom && y > self->ymargin)
1782 {
1783 x -= self->cell_w;
1784 y = self->ymargin;
1785 goto _next_position_rtl;
1786 }
1787 /* prepare position for next item */
1788 while (self->working_area.y + y < item->area.y + item->area.height)
1789 y += self->cell_h;
1790 /* check if this position is occupied by a fixed item */
1791 if(is_pos_occupied(self, item))
1792 goto _next_position_rtl;
1793 }
1794 if(icon)
1795 g_object_unref(icon);
1796 }
1797 while(gtk_tree_model_iter_next(model, &it));
1798 }
1799 gtk_widget_queue_draw(GTK_WIDGET(self));
1800 }
1801
on_idle_layout(FmDesktop * desktop)1802 static gboolean on_idle_layout(FmDesktop* desktop)
1803 {
1804 desktop->idle_layout = 0;
1805 desktop->layout_pending = FALSE;
1806 layout_items(desktop);
1807 return FALSE;
1808 }
1809
queue_layout_items(FmDesktop * desktop)1810 static void queue_layout_items(FmDesktop* desktop)
1811 {
1812 /* don't try to layout items until config is loaded,
1813 this may be cause of the bug #927 on SF.net */
1814 if (!gtk_widget_get_realized(GTK_WIDGET(desktop)))
1815 desktop->layout_pending = TRUE;
1816 else if (0 == desktop->idle_layout)
1817 desktop->idle_layout = gdk_threads_add_idle((GSourceFunc)on_idle_layout, desktop);
1818 }
1819
paint_item(FmDesktop * self,FmDesktopItem * item,cairo_t * cr,GdkRectangle * expose_area,GdkPixbuf * icon)1820 static void paint_item(FmDesktop* self, FmDesktopItem* item, cairo_t* cr, GdkRectangle* expose_area, GdkPixbuf* icon)
1821 {
1822 #if GTK_CHECK_VERSION(3, 0, 0)
1823 GtkStyleContext* style;
1824 #else
1825 GtkStyle* style;
1826 #endif
1827 GtkWidget* widget = (GtkWidget*)self;
1828 GtkCellRendererState state = 0;
1829 #if GTK_CHECK_VERSION(3, 0, 0)
1830 GdkRGBA rgba;
1831 #else
1832 GdkWindow* window;
1833 #endif
1834 int text_x, text_y;
1835
1836 /* don't draw dragged items on desktop, they are moved with mouse */
1837 if (item->is_selected && self->dragging)
1838 return;
1839
1840 #if GTK_CHECK_VERSION(3, 0, 0)
1841 style = gtk_widget_get_style_context(widget);
1842 #else
1843 style = gtk_widget_get_style(widget);
1844 window = gtk_widget_get_window(widget);
1845 #endif
1846
1847 pango_layout_set_text(self->pl, NULL, 0);
1848 pango_layout_set_width(self->pl, self->pango_text_w);
1849 pango_layout_set_height(self->pl, self->pango_text_h);
1850
1851 pango_layout_set_text(self->pl, fm_file_info_get_disp_name(item->fi), -1);
1852
1853 /* FIXME: do we need to cache this? */
1854 text_x = item->area.x + (self->cell_w - self->text_w)/2 + 2;
1855 text_y = item->text_rect.y + 2;
1856
1857 if(item->is_selected || item == self->drop_hilight) /* draw background for text label */
1858 {
1859 state = GTK_CELL_RENDERER_SELECTED;
1860
1861 cairo_save(cr);
1862 gdk_cairo_rectangle(cr, &item->text_rect);
1863 #if GTK_CHECK_VERSION(3, 0, 0)
1864 gtk_style_context_get_background_color(style, GTK_STATE_FLAG_SELECTED, &rgba);
1865 gdk_cairo_set_source_rgba(cr, &rgba);
1866 #else
1867 gdk_cairo_set_source_color(cr, &style->bg[GTK_STATE_SELECTED]);
1868 #endif
1869 cairo_clip(cr);
1870 cairo_paint(cr);
1871 cairo_restore(cr);
1872 #if GTK_CHECK_VERSION(3, 0, 0)
1873 gtk_style_context_get_color(style, GTK_STATE_FLAG_SELECTED, &rgba);
1874 gdk_cairo_set_source_rgba(cr, &rgba);
1875 #else
1876 gdk_cairo_set_source_color(cr, &style->fg[GTK_STATE_SELECTED]);
1877 #endif
1878 }
1879 else
1880 {
1881 /* the shadow */
1882 gdk_cairo_set_source_color(cr, &self->conf.desktop_shadow);
1883 cairo_move_to(cr, text_x + 1, text_y + 1);
1884 pango_cairo_show_layout(cr, self->pl);
1885 gdk_cairo_set_source_color(cr, &self->conf.desktop_fg);
1886 }
1887 /* real text */
1888 cairo_move_to(cr, text_x, text_y);
1889 /* FIXME: should we check if pango is 1.10 at least? */
1890 pango_cairo_show_layout(cr, self->pl);
1891 pango_layout_set_text(self->pl, NULL, 0);
1892
1893 if(item == self->focus && gtk_widget_has_focus(widget))
1894 #if GTK_CHECK_VERSION(3, 0, 0)
1895 gtk_render_focus(style, cr,
1896 #else
1897 gtk_paint_focus(style, window, gtk_widget_get_state(widget),
1898 expose_area, widget, "icon_view",
1899 #endif
1900 item->text_rect.x, item->text_rect.y, item->text_rect.width, item->text_rect.height);
1901
1902 if(item == self->hover_item) /* hovered */
1903 g_object_set(G_OBJECT(self), "tooltip-text", fm_file_info_get_disp_name(item->fi), NULL);
1904 else
1905 g_object_set(G_OBJECT(self), "tooltip-text", NULL, NULL);
1906
1907 /* draw the icon */
1908 g_object_set(self->icon_render, "pixbuf", icon, "info", item->fi, NULL);
1909 #if GTK_CHECK_VERSION(3, 0, 0)
1910 gtk_cell_renderer_render(GTK_CELL_RENDERER(self->icon_render), cr, widget, &item->icon_rect, &item->icon_rect, state);
1911 #else
1912 gtk_cell_renderer_render(GTK_CELL_RENDERER(self->icon_render), window, widget, &item->icon_rect, &item->icon_rect, expose_area, state);
1913 #endif
1914 }
1915
redraw_item(FmDesktop * desktop,FmDesktopItem * item)1916 static void redraw_item(FmDesktop* desktop, FmDesktopItem* item)
1917 {
1918 GdkRectangle rect;
1919 get_item_rect(item, &rect);
1920 --rect.x;
1921 --rect.y;
1922 rect.width += 2;
1923 rect.height += 2;
1924 gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(desktop)), &rect, FALSE);
1925 }
1926
move_item(FmDesktop * desktop,FmDesktopItem * item,int x,int y,gboolean redraw)1927 static void move_item(FmDesktop* desktop, FmDesktopItem* item, int x, int y, gboolean redraw)
1928 {
1929 int dx, dy;
1930 /* this call invalid the area occupied by the item and a redraw
1931 * is queued. */
1932 if(redraw)
1933 redraw_item(desktop, item);
1934
1935 /* correct coords to put item within working area still */
1936 if (x > desktop->working_area.x + desktop->working_area.width - desktop->xmargin - item->area.width)
1937 x = desktop->working_area.x + desktop->working_area.width - desktop->xmargin - item->area.width;
1938 if (x < desktop->working_area.x + desktop->xmargin)
1939 x = desktop->working_area.x + desktop->xmargin;
1940 if (y > desktop->working_area.y + desktop->working_area.height - desktop->ymargin - item->area.height)
1941 y = desktop->working_area.y + desktop->working_area.height - desktop->ymargin - item->area.height;
1942 if (y < desktop->working_area.y + desktop->ymargin)
1943 y = desktop->working_area.y + desktop->ymargin;
1944
1945 dx = x - item->area.x;
1946 dy = y - item->area.y;
1947
1948 item->area.x = x;
1949 item->area.y = y;
1950
1951 /* calc_item_size(desktop, item); */
1952 item->icon_rect.x += dx;
1953 item->icon_rect.y += dy;
1954 item->text_rect.x += dx;
1955 item->text_rect.y += dy;
1956
1957 /* make the item use customized fixed position. */
1958 if(!item->fixed_pos)
1959 {
1960 item->fixed_pos = TRUE;
1961 desktop->fixed_items = g_list_prepend(desktop->fixed_items, item);
1962 }
1963
1964 /* move the item to a new place, and queue a redraw for the new rect. */
1965 if(redraw)
1966 redraw_item(desktop, item);
1967 }
1968
calc_rubber_banding_rect(FmDesktop * self,int x,int y,GdkRectangle * rect)1969 static void calc_rubber_banding_rect(FmDesktop* self, int x, int y, GdkRectangle* rect)
1970 {
1971 int x1, x2, y1, y2;
1972 if(self->drag_start_x < x)
1973 {
1974 x1 = self->drag_start_x;
1975 x2 = x;
1976 }
1977 else
1978 {
1979 x1 = x;
1980 x2 = self->drag_start_x;
1981 }
1982
1983 if(self->drag_start_y < y)
1984 {
1985 y1 = self->drag_start_y;
1986 y2 = y;
1987 }
1988 else
1989 {
1990 y1 = y;
1991 y2 = self->drag_start_y;
1992 }
1993
1994 rect->x = x1;
1995 rect->y = y1;
1996 rect->width = x2 - x1;
1997 rect->height = y2 - y1;
1998 }
1999
update_rubberbanding(FmDesktop * self,int newx,int newy)2000 static void update_rubberbanding(FmDesktop* self, int newx, int newy)
2001 {
2002 GtkTreeModel* model = self->model ? GTK_TREE_MODEL(self->model) : NULL;
2003 GtkTreeIter it;
2004 GdkRectangle old_rect, new_rect;
2005 //GdkRegion *region;
2006 GdkWindow *window;
2007
2008 window = gtk_widget_get_window(GTK_WIDGET(self));
2009
2010 calc_rubber_banding_rect(self, self->rubber_bending_x, self->rubber_bending_y, &old_rect);
2011 calc_rubber_banding_rect(self, newx, newy, &new_rect);
2012
2013 gdk_window_invalidate_rect(window, &old_rect, FALSE);
2014 gdk_window_invalidate_rect(window, &new_rect, FALSE);
2015 // gdk_window_clear_area(((GtkWidget*)self)->window, new_rect.x, new_rect.y, new_rect.width, new_rect.height);
2016 /*
2017 region = gdk_region_rectangle(&old_rect);
2018 gdk_region_union_with_rect(region, &new_rect);
2019
2020 // gdk_window_invalidate_region(((GtkWidget*)self)->window, ®ion, TRUE);
2021
2022 gdk_region_destroy(region);
2023 */
2024 self->rubber_bending_x = newx;
2025 self->rubber_bending_y = newy;
2026
2027 /* update selection */
2028 if(model && gtk_tree_model_get_iter_first(model, &it)) do
2029 {
2030 FmDesktopItem* item = fm_folder_model_get_item_userdata(self->model, &it);
2031 gboolean selected;
2032 if(gdk_rectangle_intersect(&new_rect, &item->icon_rect, NULL) ||
2033 gdk_rectangle_intersect(&new_rect, &item->text_rect, NULL))
2034 selected = TRUE;
2035 else
2036 selected = FALSE;
2037
2038 /* we cannot compare booleans, TRUE may be 1 or -1 */
2039 if ((item->is_rubber_banded && !selected) ||
2040 (!item->is_rubber_banded && selected))
2041 {
2042 item->is_selected = selected;
2043 redraw_item(self, item);
2044 fm_desktop_item_selected_changed(self, item);
2045 }
2046 item->is_rubber_banded = self->rubber_bending && selected;
2047 }
2048 while(gtk_tree_model_iter_next(model, &it));
2049 }
2050
2051
paint_rubber_banding_rect(FmDesktop * self,cairo_t * cr,GdkRectangle * expose_area)2052 static void paint_rubber_banding_rect(FmDesktop* self, cairo_t* cr, GdkRectangle* expose_area)
2053 {
2054 GtkWidget* widget = (GtkWidget*)self;
2055 GdkRectangle rect;
2056 GdkColor clr;
2057 guchar alpha;
2058
2059 calc_rubber_banding_rect(self, self->rubber_bending_x, self->rubber_bending_y, &rect);
2060
2061 if(rect.width <= 0 || rect.height <= 0)
2062 return;
2063
2064 if(!gdk_rectangle_intersect(expose_area, &rect, &rect))
2065 return;
2066 /*
2067 gtk_widget_style_get(icon_view,
2068 "selection-box-color", &clr,
2069 "selection-box-alpha", &alpha,
2070 NULL);
2071 */
2072 clr = gtk_widget_get_style (widget)->base[GTK_STATE_SELECTED];
2073 alpha = 64; /* FIXME: should be themable in the future */
2074
2075 cairo_save(cr);
2076 cairo_set_source_rgba(cr, (gdouble)clr.red/65535, (gdouble)clr.green/65536, (gdouble)clr.blue/65535, (gdouble)alpha/100);
2077 gdk_cairo_rectangle(cr, &rect);
2078 cairo_clip (cr);
2079 cairo_paint (cr);
2080 gdk_cairo_set_source_color(cr, &clr);
2081 cairo_rectangle (cr, rect.x + 0.5, rect.y + 0.5, rect.width - 1, rect.height - 1);
2082 cairo_stroke(cr);
2083 cairo_restore(cr);
2084 }
2085
_free_cache_image(FmBackgroundCache * cache)2086 static void _free_cache_image(FmBackgroundCache *cache)
2087 {
2088 #if GTK_CHECK_VERSION(3, 0, 0)
2089 XFreePixmap(cairo_xlib_surface_get_display(cache->bg),
2090 cairo_xlib_surface_get_drawable(cache->bg));
2091 cairo_surface_destroy(cache->bg);
2092 #else
2093 g_object_unref(cache->bg);
2094 #endif
2095 cache->bg = NULL;
2096 cache->wallpaper_mode = FM_WP_COLOR; /* for cache check */
2097 }
2098
_clear_bg_cache(FmDesktop * self)2099 static void _clear_bg_cache(FmDesktop *self)
2100 {
2101 while(self->cache)
2102 {
2103 FmBackgroundCache *bg = self->cache;
2104
2105 self->cache = bg->next;
2106 _free_cache_image(bg);
2107 g_free(bg->filename);
2108 g_free(bg);
2109 }
2110 }
2111
update_background(FmDesktop * desktop,int is_it)2112 static void update_background(FmDesktop* desktop, int is_it)
2113 {
2114 GtkWidget* widget = (GtkWidget*)desktop;
2115 GdkPixbuf* pix, *scaled;
2116 cairo_t* cr;
2117 GdkScreen *screen = gtk_widget_get_screen(widget);
2118 GdkWindow* root = gdk_screen_get_root_window(screen);
2119 GdkWindow *window = gtk_widget_get_window(widget);
2120 FmBackgroundCache *cache;
2121 #if GTK_CHECK_VERSION(3, 0, 0)
2122 cairo_pattern_t *pattern;
2123 #endif
2124
2125 Display* xdisplay;
2126 Pixmap xpixmap;
2127 Window xroot;
2128 int screen_num = gdk_screen_get_number(screen);
2129
2130 char *wallpaper;
2131
2132 if (!desktop->conf.wallpaper_common)
2133 {
2134 guint32 cur_desktop = desktop->cur_desktop;
2135
2136 if(is_it >= 0) /* signal "changed::wallpaper" */
2137 {
2138 int i;
2139
2140 wallpaper = desktop->conf.wallpaper;
2141 if((gint)cur_desktop >= desktop->conf.wallpapers_configured)
2142 {
2143 desktop->conf.wallpapers = g_renew(char *, desktop->conf.wallpapers, cur_desktop + 1);
2144 /* fill the gap with current wallpaper */
2145 for(i = MAX(desktop->conf.wallpapers_configured,0); i < (int)cur_desktop; i++)
2146 desktop->conf.wallpapers[i] = g_strdup(wallpaper);
2147 desktop->conf.wallpapers[cur_desktop] = NULL;
2148 desktop->conf.wallpapers_configured = cur_desktop + 1;
2149 }
2150 /* free old image if it's not used anymore */
2151 else if (g_strcmp0(desktop->conf.wallpapers[cur_desktop], wallpaper))
2152 {
2153 wallpaper = desktop->conf.wallpapers[cur_desktop];
2154 if (wallpaper)
2155 {
2156 for (i = 0; i < desktop->conf.wallpapers_configured; i++)
2157 if (i != (int)cur_desktop && /* test if it's used */
2158 g_strcmp0(desktop->conf.wallpapers[i], wallpaper) == 0)
2159 break;
2160 if (i == desktop->conf.wallpapers_configured) /* no matches */
2161 {
2162 for (cache = desktop->cache; cache; cache = cache->next)
2163 if (strcmp(wallpaper, cache->filename) == 0)
2164 break;
2165 if (cache)
2166 _free_cache_image(cache);
2167 }
2168 g_free(wallpaper);
2169 }
2170 wallpaper = desktop->conf.wallpaper;
2171 desktop->conf.wallpapers[cur_desktop] = g_strdup(wallpaper);
2172 }
2173 }
2174 else /* desktop refresh */
2175 {
2176 if((gint)cur_desktop < desktop->conf.wallpapers_configured)
2177 wallpaper = desktop->conf.wallpapers[cur_desktop];
2178 else
2179 wallpaper = NULL;
2180 if (wallpaper == NULL && desktop->conf.wallpaper != NULL)
2181 {
2182 /* if we have wallpaper set for previous desktop but have not
2183 for current one, it may mean one of two cases:
2184 - we expanded number of desktops;
2185 - we recently switched wallpaper_common off.
2186 If we selected to use wallpaper image but current desktop
2187 has no image set (i.e. one of cases above is happening),
2188 it is reasonable and correct to use last selected image for
2189 newly selected desktop instead of show plain color on it */
2190 wallpaper = desktop->conf.wallpaper;
2191 if ((gint)cur_desktop < desktop->conf.wallpapers_configured)
2192 /* this means desktop->conf.wallpapers[cur_desktop] is NULL,
2193 see above, we have to update it too in this case */
2194 desktop->conf.wallpapers[cur_desktop] = g_strdup(wallpaper);
2195 }
2196 else
2197 {
2198 g_free(desktop->conf.wallpaper); /* update to current desktop */
2199 desktop->conf.wallpaper = g_strdup(wallpaper);
2200 }
2201 }
2202 }
2203 else
2204 {
2205 _clear_bg_cache(desktop);
2206 wallpaper = desktop->conf.wallpaper;
2207 }
2208
2209 if(desktop->conf.wallpaper_mode != FM_WP_COLOR && wallpaper && *wallpaper)
2210 {
2211 struct stat st; /* for mtime */
2212
2213 /* bug #3613571 - replacing the file will not affect the desktop
2214 we will call stat on each desktop change but it's inevitable */
2215 if (stat(wallpaper, &st) < 0)
2216 st.st_mtime = 0;
2217 for(cache = desktop->cache; cache; cache = cache->next)
2218 if(strcmp(wallpaper, cache->filename) == 0)
2219 break;
2220 if(cache && cache->wallpaper_mode == desktop->conf.wallpaper_mode
2221 && st.st_mtime == cache->mtime)
2222 pix = NULL; /* no new pix for it */
2223 else if((pix = gdk_pixbuf_new_from_file(wallpaper, NULL)))
2224 {
2225 if(cache)
2226 {
2227 /* the same file but mode was changed */
2228 _free_cache_image(cache);
2229 }
2230 else if(desktop->cache)
2231 {
2232 for(cache = desktop->cache; cache->next; )
2233 cache = cache->next;
2234 cache->next = g_new0(FmBackgroundCache, 1);
2235 cache = cache->next;
2236 }
2237 else
2238 desktop->cache = cache = g_new0(FmBackgroundCache, 1);
2239 if(!cache->filename)
2240 cache->filename = g_strdup(wallpaper);
2241 cache->mtime = st.st_mtime;
2242 g_debug("adding new FmBackgroundCache for %s", wallpaper);
2243 }
2244 else
2245 /* if there is a cached image but with another mode and we cannot
2246 get it from file for new mode then just leave it in cache as is */
2247 cache = NULL;
2248 }
2249 else
2250 cache = NULL;
2251
2252 if(!cache) /* solid color only */
2253 {
2254 #if GTK_CHECK_VERSION(3, 0, 0)
2255 pattern = cairo_pattern_create_rgb(desktop->conf.desktop_bg.red / 65535.0,
2256 desktop->conf.desktop_bg.green / 65535.0,
2257 desktop->conf.desktop_bg.blue / 65535.0);
2258 gdk_window_set_background_pattern(window, pattern);
2259 cairo_pattern_destroy(pattern);
2260 #else
2261 GdkColor bg = desktop->conf.desktop_bg;
2262
2263 gdk_colormap_alloc_color(gdk_drawable_get_colormap(window), &bg, FALSE, TRUE);
2264 gdk_window_set_back_pixmap(window, NULL, FALSE);
2265 gdk_window_set_background(window, &bg);
2266 #endif
2267 gdk_window_invalidate_rect(window, NULL, TRUE);
2268 return;
2269 }
2270
2271 if(!cache->bg) /* no cached image found */
2272 {
2273 int src_w, src_h;
2274 int dest_w, dest_h;
2275 int x = 0, y = 0;
2276 src_w = gdk_pixbuf_get_width(pix);
2277 src_h = gdk_pixbuf_get_height(pix);
2278 if(desktop->conf.wallpaper_mode == FM_WP_TILE)
2279 {
2280 dest_w = src_w;
2281 dest_h = src_h;
2282 }
2283 else
2284 {
2285 GdkRectangle geom;
2286 gdk_screen_get_monitor_geometry(screen, desktop->monitor, &geom);
2287 if (desktop->conf.wallpaper_mode == FM_WP_SCREEN)
2288 {
2289 dest_w = gdk_screen_get_width(screen);
2290 dest_h = gdk_screen_get_height(screen);
2291 x = -geom.x;
2292 y = -geom.y;
2293 }
2294 else
2295 {
2296 dest_w = geom.width;
2297 dest_h = geom.height;
2298 }
2299 }
2300 #if GTK_CHECK_VERSION(3, 0, 0)
2301 xdisplay = GDK_WINDOW_XDISPLAY(root);
2302 /* this code is taken from libgnome-desktop */
2303 xpixmap = XCreatePixmap(xdisplay, RootWindow(xdisplay, screen_num),
2304 dest_w, dest_h, DefaultDepth(xdisplay, screen_num));
2305 cache->bg = cairo_xlib_surface_create(xdisplay, xpixmap,
2306 GDK_VISUAL_XVISUAL(gdk_screen_get_system_visual(screen)),
2307 dest_w, dest_h);
2308 cr = cairo_create(cache->bg);
2309 #else
2310 cache->bg = gdk_pixmap_new(window, dest_w, dest_h, -1);
2311 cr = gdk_cairo_create(cache->bg);
2312 #endif
2313 if(gdk_pixbuf_get_has_alpha(pix)
2314 || desktop->conf.wallpaper_mode == FM_WP_CENTER
2315 || desktop->conf.wallpaper_mode == FM_WP_FIT)
2316 {
2317 gdk_cairo_set_source_color(cr, &desktop->conf.desktop_bg);
2318 cairo_rectangle(cr, 0, 0, dest_w, dest_h);
2319 cairo_fill(cr);
2320 }
2321
2322 switch(desktop->conf.wallpaper_mode)
2323 {
2324 case FM_WP_TILE:
2325 break;
2326 case FM_WP_STRETCH:
2327 case FM_WP_SCREEN:
2328 if(dest_w == src_w && dest_h == src_h)
2329 scaled = (GdkPixbuf*)g_object_ref(pix);
2330 else
2331 scaled = gdk_pixbuf_scale_simple(pix, dest_w, dest_h, GDK_INTERP_BILINEAR);
2332 g_object_unref(pix);
2333 pix = scaled;
2334 break;
2335 case FM_WP_FIT:
2336 case FM_WP_CROP:
2337 if(dest_w != src_w || dest_h != src_h)
2338 {
2339 gdouble w_ratio = (float)dest_w / src_w;
2340 gdouble h_ratio = (float)dest_h / src_h;
2341 gdouble ratio = (desktop->conf.wallpaper_mode == FM_WP_FIT)
2342 ? MIN(w_ratio, h_ratio)
2343 : MAX(w_ratio, h_ratio);
2344 if(ratio != 1.0)
2345 {
2346 src_w *= ratio;
2347 src_h *= ratio;
2348 scaled = gdk_pixbuf_scale_simple(pix, src_w, src_h, GDK_INTERP_BILINEAR);
2349 g_object_unref(pix);
2350 pix = scaled;
2351 }
2352 }
2353 /* continue to execute code in case FM_WP_CENTER */
2354 case FM_WP_CENTER:
2355 x = (dest_w - src_w)/2;
2356 y = (dest_h - src_h)/2;
2357 break;
2358 case FM_WP_COLOR: ; /* handled above */
2359 }
2360 gdk_cairo_set_source_pixbuf(cr, pix, x, y);
2361 cairo_paint(cr);
2362 cairo_destroy(cr);
2363 cache->wallpaper_mode = desktop->conf.wallpaper_mode;
2364 }
2365 #if GTK_CHECK_VERSION(3, 0, 0)
2366 pattern = cairo_pattern_create_for_surface(cache->bg);
2367 gdk_window_set_background_pattern(window, pattern);
2368 cairo_pattern_destroy(pattern);
2369 #else
2370 gdk_window_set_back_pixmap(window, cache->bg, FALSE);
2371 #endif
2372
2373 /* set root map here */
2374 xdisplay = GDK_WINDOW_XDISPLAY(root);
2375 xroot = RootWindow(xdisplay, screen_num);
2376
2377 #if GTK_CHECK_VERSION(3, 0, 0)
2378 xpixmap = cairo_xlib_surface_get_drawable(cache->bg);
2379 #else
2380 xpixmap = GDK_WINDOW_XWINDOW(cache->bg);
2381 #endif
2382
2383 XChangeProperty(xdisplay, GDK_WINDOW_XID(root),
2384 XA_XROOTMAP_ID, XA_PIXMAP, 32, PropModeReplace, (guchar*)&xpixmap, 1);
2385
2386 XGrabServer (xdisplay);
2387
2388 #if 0
2389 result = XGetWindowProperty (display,
2390 RootWindow (display, screen_num),
2391 gdk_x11_get_xatom_by_name ("ESETROOT_PMAP_ID"),
2392 0L, 1L, False, XA_PIXMAP,
2393 &type, &format, &nitems,
2394 &bytes_after,
2395 &data_esetroot);
2396
2397 if (data_esetroot != NULL) {
2398 if (result == Success && type == XA_PIXMAP &&
2399 format == 32 &&
2400 nitems == 1) {
2401 gdk_error_trap_push ();
2402 XKillClient (display, *(Pixmap *)data_esetroot);
2403 gdk_error_trap_pop_ignored ();
2404 }
2405 XFree (data_esetroot);
2406 }
2407
2408 XChangeProperty (display, RootWindow (display, screen_num),
2409 gdk_x11_get_xatom_by_name ("ESETROOT_PMAP_ID"),
2410 XA_PIXMAP, 32, PropModeReplace,
2411 (guchar *) &xpixmap, 1);
2412 #endif
2413
2414 XChangeProperty(xdisplay, xroot, XA_XROOTPMAP_ID, XA_PIXMAP, 32,
2415 PropModeReplace, (guchar*)&xpixmap, 1);
2416
2417 XSetWindowBackgroundPixmap(xdisplay, xroot, xpixmap);
2418 XClearWindow(xdisplay, xroot);
2419
2420 XFlush(xdisplay);
2421 XUngrabServer(xdisplay);
2422
2423 if(pix)
2424 g_object_unref(pix);
2425
2426 gdk_window_invalidate_rect(window, NULL, TRUE);
2427 }
2428
2429
2430 /* ---------------------------------------------------------------------
2431 FmFolderModel signal handlers */
2432
on_row_deleting(FmFolderModel * model,GtkTreePath * tp,GtkTreeIter * iter,gpointer data,FmDesktop * desktop)2433 static void on_row_deleting(FmFolderModel* model, GtkTreePath* tp,
2434 GtkTreeIter* iter, gpointer data, FmDesktop* desktop)
2435 {
2436 GList *l;
2437
2438 for(l = desktop->fixed_items; l; l = l->next)
2439 if(l->data == data)
2440 {
2441 desktop->fixed_items = g_list_delete_link(desktop->fixed_items, l);
2442 break;
2443 }
2444 if((gpointer)desktop->focus == data)
2445 {
2446 GtkTreeIter it = *iter;
2447 fm_desktop_accessible_focus_unset(desktop, data);
2448 if(gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &it))
2449 desktop->focus = fm_folder_model_get_item_userdata(model, &it);
2450 else
2451 {
2452 if(gtk_tree_path_prev(tp))
2453 {
2454 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &it, tp);
2455 gtk_tree_path_next(tp);
2456 desktop->focus = fm_folder_model_get_item_userdata(model, &it);
2457 }
2458 else
2459 desktop->focus = NULL;
2460 }
2461 if (desktop->focus)
2462 fm_desktop_accessible_focus_set(desktop, desktop->focus);
2463 }
2464 if((gpointer)desktop->drop_hilight == data)
2465 desktop->drop_hilight = NULL;
2466 if((gpointer)desktop->hover_item == data)
2467 {
2468 desktop->hover_item = NULL;
2469 /* bug #3615015: after deleting the item tooltip stuck on the desktop */
2470 g_object_set(G_OBJECT(desktop), "tooltip-text", NULL, NULL);
2471 }
2472 fm_desktop_accessible_item_deleted(desktop, data);
2473 desktop_item_free(data);
2474 }
2475
on_row_inserted(FmFolderModel * mod,GtkTreePath * tp,GtkTreeIter * it,FmDesktop * desktop)2476 static void on_row_inserted(FmFolderModel* mod, GtkTreePath* tp, GtkTreeIter* it, FmDesktop* desktop)
2477 {
2478 FmDesktopItem* item = desktop_item_new(mod, it);
2479 gint *indices = gtk_tree_path_get_indices(tp);
2480 fm_desktop_accessible_item_added(desktop, item, indices[0]);
2481 fm_folder_model_set_item_userdata(mod, it, item);
2482 queue_layout_items(desktop);
2483 }
2484
on_row_deleted(FmFolderModel * mod,GtkTreePath * tp,FmDesktop * desktop)2485 static void on_row_deleted(FmFolderModel* mod, GtkTreePath* tp, FmDesktop* desktop)
2486 {
2487 queue_layout_items(desktop);
2488 }
2489
on_row_changed(FmFolderModel * model,GtkTreePath * tp,GtkTreeIter * it,FmDesktop * desktop)2490 static void on_row_changed(FmFolderModel* model, GtkTreePath* tp, GtkTreeIter* it, FmDesktop* desktop)
2491 {
2492 FmDesktopItem* item = fm_folder_model_get_item_userdata(model, it);
2493 GdkPixbuf *icon;
2494
2495 fm_file_info_unref(item->fi);
2496 gtk_tree_model_get(GTK_TREE_MODEL(model), it,
2497 FM_FOLDER_MODEL_COL_INFO, &item->fi,
2498 FM_FOLDER_MODEL_COL_ICON, &icon, -1);
2499 fm_file_info_ref(item->fi);
2500
2501 /* we need to redraw old area as we changing data */
2502 redraw_item(desktop, item);
2503 calc_item_size(desktop, item, icon);
2504 if (icon)
2505 g_object_unref(icon);
2506 redraw_item(desktop, item);
2507 /* queue_layout_items(desktop); */
2508 }
2509
on_rows_reordered(FmFolderModel * model,GtkTreePath * parent_tp,GtkTreeIter * parent_it,gpointer new_order,FmDesktop * desktop)2510 static void on_rows_reordered(FmFolderModel* model, GtkTreePath* parent_tp, GtkTreeIter* parent_it, gpointer new_order, FmDesktop* desktop)
2511 {
2512 fm_desktop_accessible_items_reordered(desktop, GTK_TREE_MODEL(model), new_order);
2513 queue_layout_items(desktop);
2514 }
2515
2516
2517 /* ---------------------------------------------------------------------
2518 Events handlers */
2519
update_working_area(FmDesktop * desktop)2520 static void update_working_area(FmDesktop* desktop)
2521 {
2522 GdkScreen* screen = gtk_widget_get_screen((GtkWidget*)desktop);
2523 GdkRectangle geom;
2524 #if GTK_CHECK_VERSION(3, 4, 0)
2525 gdk_screen_get_monitor_workarea(screen, desktop->monitor, &desktop->working_area);
2526 /* we need working area coordinates within the monitor not the screen */
2527 gdk_screen_get_monitor_geometry(screen, desktop->monitor, &geom);
2528 desktop->working_area.x -= geom.x;
2529 desktop->working_area.y -= geom.y;
2530 #else
2531 GdkWindow* root = gdk_screen_get_root_window(screen);
2532 Atom ret_type;
2533 gulong len, after;
2534 int format;
2535 guchar* prop;
2536 guint32 n_desktops, cur_desktop;
2537 gulong* working_area;
2538
2539 /* default to screen size */
2540 gdk_screen_get_monitor_geometry(screen, desktop->monitor, &geom);
2541 desktop->working_area.width = geom.width;
2542 desktop->working_area.height = geom.height;
2543
2544 if(XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
2545 XA_NET_NUMBER_OF_DESKTOPS, 0, 1, False, XA_CARDINAL, &ret_type,
2546 &format, &len, &after, &prop) != Success)
2547 goto _out;
2548 if(!prop)
2549 goto _out;
2550 n_desktops = *(guint32*)prop;
2551 XFree(prop);
2552
2553 if(XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
2554 XA_NET_CURRENT_DESKTOP, 0, 1, False, XA_CARDINAL, &ret_type,
2555 &format, &len, &after, &prop) != Success)
2556 goto _out;
2557 if(!prop)
2558 goto _out;
2559 cur_desktop = *(guint32*)prop;
2560 XFree(prop);
2561
2562 if(XGetWindowProperty(GDK_WINDOW_XDISPLAY(root), GDK_WINDOW_XID(root),
2563 XA_NET_WORKAREA, 0, 4 * 32, False, AnyPropertyType, &ret_type,
2564 &format, &len, &after, &prop) != Success)
2565 goto _out;
2566 if(ret_type == None || format == 0 || len != n_desktops*4)
2567 {
2568 if(prop)
2569 XFree(prop);
2570 goto _out;
2571 }
2572 working_area = ((gulong*)prop) + cur_desktop * 4;
2573
2574 desktop->working_area.x = (gint)working_area[0] - geom.x;
2575 desktop->working_area.y = (gint)working_area[1] - geom.y;
2576 if(desktop->working_area.x > 0 &&
2577 desktop->working_area.x < desktop->working_area.width)
2578 desktop->working_area.width -= desktop->working_area.x;
2579 if(desktop->working_area.y > 0 &&
2580 desktop->working_area.y < desktop->working_area.height)
2581 desktop->working_area.height -= desktop->working_area.y;
2582 if(desktop->working_area.x + (gint)working_area[2] < desktop->working_area.width)
2583 desktop->working_area.width = working_area[2] + desktop->working_area.x;
2584 if(desktop->working_area.y + (gint)working_area[3] < desktop->working_area.height)
2585 desktop->working_area.height = working_area[3] + desktop->working_area.y;
2586 g_debug("got working area: %d.%d.%d.%d", desktop->working_area.x, desktop->working_area.y,
2587 desktop->working_area.width, desktop->working_area.height);
2588 /* we need working area coordinates within the monitor not the screen */
2589 desktop->working_area.x = MAX(0, desktop->working_area.x);
2590 desktop->working_area.y = MAX(0, desktop->working_area.y);
2591
2592 XFree(prop);
2593 _out:
2594 #endif
2595 queue_layout_items(desktop);
2596 return;
2597 }
2598
on_root_event(GdkXEvent * xevent,GdkEvent * event,gpointer data)2599 static GdkFilterReturn on_root_event(GdkXEvent *xevent, GdkEvent *event, gpointer data)
2600 {
2601 XPropertyEvent * evt = (XPropertyEvent*) xevent;
2602 FmDesktop* self = (FmDesktop*)data;
2603 if (evt->type == PropertyNotify)
2604 {
2605 if(evt->atom == XA_NET_WORKAREA)
2606 update_working_area(self);
2607 else if(evt->atom == XA_NET_CURRENT_DESKTOP)
2608 {
2609 gint desktop = get_desktop_for_root_window(gdk_screen_get_root_window(
2610 gtk_widget_get_screen(GTK_WIDGET(data))));
2611 if(desktop >= 0)
2612 {
2613 self->cur_desktop = (guint)desktop;
2614 if(!self->conf.wallpaper_common)
2615 update_background(self, -1);
2616 }
2617 }
2618 }
2619 return GDK_FILTER_CONTINUE;
2620 }
2621
on_screen_size_changed(GdkScreen * screen,FmDesktop * desktop)2622 static void on_screen_size_changed(GdkScreen* screen, FmDesktop* desktop)
2623 {
2624 GdkRectangle geom;
2625 if (desktop->monitor >= gdk_screen_get_n_monitors(screen))
2626 {
2627 gint i;
2628 /* our monitor was disconnected... remove FmDesktop now! */
2629 for (i = 0; i < n_screens; i++)
2630 if (desktops[i] == desktop)
2631 break;
2632 if (i < n_screens)
2633 desktops[i] = fm_desktop_new(screen, desktop->monitor ? -2 : -1);
2634 gtk_widget_destroy(GTK_WIDGET(desktop));
2635 return;
2636 }
2637 gdk_screen_get_monitor_geometry(screen, desktop->monitor, &geom);
2638 gtk_window_resize((GtkWindow*)desktop, geom.width, geom.height);
2639 /* bug #3614780: if monitor was moved desktop should be moved too */
2640 gtk_window_move((GtkWindow*)desktop, geom.x, geom.y);
2641 /* FIXME: check if new monitor was added! */
2642 }
2643
reload_icons()2644 static void reload_icons()
2645 {
2646 int i;
2647 for(i=0; i < n_screens; ++i)
2648 if(desktops[i]->monitor >= 0)
2649 gtk_widget_queue_resize(GTK_WIDGET(desktops[i]));
2650 }
2651
on_big_icon_size_changed(FmConfig * cfg,FmFolderModel * model)2652 static void on_big_icon_size_changed(FmConfig* cfg, FmFolderModel* model)
2653 {
2654 fm_folder_model_set_icon_size(model, fm_config->big_icon_size);
2655 reload_icons();
2656 }
2657
on_icon_theme_changed(GtkIconTheme * theme,gpointer user_data)2658 static void on_icon_theme_changed(GtkIconTheme* theme, gpointer user_data)
2659 {
2660 reload_icons();
2661 }
2662
2663
2664 /* ---------------------------------------------------------------------
2665 Popup handlers */
2666
fm_desktop_update_popup(FmFolderView * fv,GtkWindow * window,GtkUIManager * ui,GtkActionGroup * act_grp,FmFileInfoList * files)2667 static void fm_desktop_update_popup(FmFolderView* fv, GtkWindow* window,
2668 GtkUIManager* ui, GtkActionGroup* act_grp,
2669 FmFileInfoList* files)
2670 {
2671 GtkAction* act;
2672
2673 /* remove 'Rename' item and accelerator */
2674 act = gtk_action_group_get_action(act_grp, "Rename");
2675 gtk_action_set_visible(act, FALSE);
2676 gtk_action_set_sensitive(act, FALSE);
2677 /* hide 'Show Hidden' item */
2678 act = gtk_action_group_get_action(act_grp, "ShowHidden");
2679 gtk_action_set_visible(act, FALSE);
2680 /* add 'Configure desktop' item replacing 'Properties' */
2681 act = gtk_action_group_get_action(act_grp, "Prop");
2682 gtk_action_set_visible(act, FALSE);
2683 //gtk_action_group_remove_action(act_grp, act);
2684 #if !FM_CHECK_VERSION(1, 2, 0)
2685 if(fm_folder_view_get_model(fv) == NULL)
2686 {
2687 /* hide folder-oriented actions if there is no folder */
2688 act = gtk_action_group_get_action(act_grp, "SelAll");
2689 gtk_action_set_visible(act, FALSE);
2690 act = gtk_action_group_get_action(act_grp, "InvSel");
2691 gtk_action_set_visible(act, FALSE);
2692 act = gtk_action_group_get_action(act_grp, "Sort");
2693 gtk_action_set_visible(act, FALSE);
2694 }
2695 #endif
2696 gtk_action_group_set_translation_domain(act_grp, NULL);
2697 gtk_action_group_add_actions(act_grp, desktop_actions,
2698 G_N_ELEMENTS(desktop_actions), window);
2699 gtk_ui_manager_add_ui_from_string(ui, desktop_menu_xml, -1, NULL);
2700 }
2701
fm_desktop_update_item_popup(FmFolderView * fv,GtkWindow * window,GtkUIManager * ui,GtkActionGroup * act_grp,FmFileInfoList * files)2702 static void fm_desktop_update_item_popup(FmFolderView* fv, GtkWindow* window,
2703 GtkUIManager* ui, GtkActionGroup* act_grp,
2704 FmFileInfoList* files)
2705 {
2706 FmFileInfo* fi;
2707 GList* sel_items, *l;
2708 GtkAction* act;
2709 gboolean all_fixed = TRUE, has_fixed = FALSE;
2710 gboolean all_native = TRUE;
2711 #if FM_CHECK_VERSION(1, 2, 0)
2712 gboolean has_extra = FALSE, has_mount = FALSE;
2713 #endif
2714
2715 sel_items = get_selected_items(FM_DESKTOP(fv), NULL);
2716 for(l = sel_items; l; l=l->next)
2717 {
2718 FmDesktopItem* item = (FmDesktopItem*)l->data;
2719 if(item->fixed_pos)
2720 has_fixed = TRUE;
2721 else
2722 all_fixed = FALSE;
2723 if (!pcmanfm_can_open_path_in_terminal(fm_file_info_get_path(item->fi)))
2724 all_native = FALSE;
2725 #if FM_CHECK_VERSION(1, 2, 0)
2726 if (item->is_special)
2727 has_extra = TRUE;
2728 if (item->is_mount)
2729 has_mount = TRUE;
2730 #endif
2731 }
2732 g_list_free(sel_items);
2733
2734 fi = (FmFileInfo*)fm_file_info_list_peek_head(files);
2735
2736 /* merge some specific menu items for folders */
2737 gtk_action_group_set_translation_domain(act_grp, NULL);
2738 if(fm_file_info_list_get_length(files) == 1 && fm_file_info_is_dir(fi))
2739 {
2740 gtk_action_group_add_actions(act_grp, folder_menu_actions,
2741 G_N_ELEMENTS(folder_menu_actions), fv);
2742 gtk_ui_manager_add_ui_from_string(ui, folder_menu_xml, -1, NULL);
2743 /* disable terminal for non-native folders */
2744 act = gtk_action_group_get_action(act_grp, "Term");
2745 gtk_action_set_visible(act, all_native);
2746 }
2747 #if FM_CHECK_VERSION(1, 2, 0)
2748 if (has_extra)
2749 {
2750 if (fm_file_info_list_get_length(files) == 1)
2751 {
2752 gtk_action_group_add_actions(act_grp, extra_item_menu_actions,
2753 G_N_ELEMENTS(extra_item_menu_actions), fv);
2754 gtk_ui_manager_add_ui_from_string(ui, extra_item_menu_xml, -1, NULL);
2755 if (has_mount)
2756 {
2757 act = gtk_action_group_get_action(act_grp, "Disable");
2758 gtk_action_set_visible(act, FALSE);
2759 }
2760 }
2761 /* some menu items should be never available for extra items */
2762 act = gtk_action_group_get_action(act_grp, "Cut");
2763 gtk_action_set_visible(act, FALSE);
2764 act = gtk_action_group_get_action(act_grp, "Del");
2765 gtk_action_set_visible(act, FALSE);
2766 act = gtk_action_group_get_action(act_grp, "Rename");
2767 gtk_action_set_visible(act, FALSE);
2768 }
2769 #endif
2770
2771 /* merge desktop icon specific items */
2772 gtk_action_group_add_actions(act_grp, desktop_icon_actions,
2773 G_N_ELEMENTS(desktop_icon_actions), fv);
2774 act = gtk_action_group_get_action(act_grp, "Snap");
2775 gtk_action_set_sensitive(act, has_fixed);
2776
2777 gtk_action_group_add_toggle_actions(act_grp, desktop_icon_toggle_actions,
2778 G_N_ELEMENTS(desktop_icon_toggle_actions),
2779 fv);
2780 act = gtk_action_group_get_action(act_grp, "Fix");
2781 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), all_fixed);
2782
2783 gtk_ui_manager_add_ui_from_string(ui, desktop_icon_menu_xml, -1, NULL);
2784 }
2785
2786 /* folder options work only with single folder - see above */
on_open_in_new_tab(GtkAction * act,gpointer user_data)2787 static void on_open_in_new_tab(GtkAction* act, gpointer user_data)
2788 {
2789 FmDesktop* desktop = FM_DESKTOP(user_data);
2790
2791 if(desktop->focus)
2792 fm_main_win_open_in_last_active(fm_file_info_get_path(desktop->focus->fi));
2793 }
2794
on_open_in_new_win(GtkAction * act,gpointer user_data)2795 static void on_open_in_new_win(GtkAction* act, gpointer user_data)
2796 {
2797 FmDesktop* desktop = FM_DESKTOP(user_data);
2798
2799 if(desktop->focus)
2800 fm_main_win_add_win(NULL, fm_file_info_get_path(desktop->focus->fi));
2801 }
2802
on_open_folder_in_terminal(GtkAction * act,gpointer user_data)2803 static void on_open_folder_in_terminal(GtkAction* act, gpointer user_data)
2804 {
2805 FmDesktop* desktop = FM_DESKTOP(user_data);
2806
2807 if(desktop->focus /*&& !fm_file_info_is_virtual(fi)*/)
2808 pcmanfm_open_folder_in_terminal(NULL, fm_file_info_get_path(desktop->focus->fi));
2809 }
2810
on_fix_pos(GtkToggleAction * act,gpointer user_data)2811 static void on_fix_pos(GtkToggleAction* act, gpointer user_data)
2812 {
2813 FmDesktop* desktop = FM_DESKTOP(user_data);
2814 GList* items = get_selected_items(desktop, NULL);
2815 GList* l;
2816 if(gtk_toggle_action_get_active(act))
2817 {
2818 for(l = items; l; l=l->next)
2819 {
2820 FmDesktopItem* item = (FmDesktopItem*)l->data;
2821 if(!item->fixed_pos)
2822 {
2823 item->fixed_pos = TRUE;
2824 desktop->fixed_items = g_list_prepend(desktop->fixed_items, item);
2825 }
2826 }
2827 }
2828 else
2829 {
2830 for(l = items; l; l=l->next)
2831 {
2832 FmDesktopItem* item = (FmDesktopItem*)l->data;
2833 item->fixed_pos = FALSE;
2834 desktop->fixed_items = g_list_remove(desktop->fixed_items, item);
2835 }
2836 queue_layout_items(desktop);
2837 }
2838 g_list_free(items);
2839 queue_config_save(desktop);
2840 }
2841
2842 #if FM_CHECK_VERSION(1, 2, 0)
on_disable(GtkAction * act,gpointer user_data)2843 static void on_disable(GtkAction* act, gpointer user_data)
2844 {
2845 FmDesktop *desktop = FM_DESKTOP(user_data);
2846 GList *items = get_selected_items(desktop, NULL);
2847 FmDesktopItem *item = (FmDesktopItem*)items->data;
2848
2849 g_list_free(items);
2850 if (trash_can && trash_can->fi == item->fi)
2851 {
2852 desktop->conf.show_trash = FALSE;
2853 }
2854 else if (documents && documents->fi == item->fi)
2855 {
2856 desktop->conf.show_documents = FALSE;
2857 }
2858 else /* else is error */
2859 {
2860 g_warning("invalid item to remove from desktop");
2861 return;
2862 }
2863 queue_config_save(desktop);
2864 fm_folder_model_extra_file_remove(desktop->model, item->fi);
2865 }
2866 #endif
2867
2868 /* round() is only available in C99. Don't use it now for portability. */
_round(double x)2869 static inline double _round(double x)
2870 {
2871 return (x > 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
2872 }
2873
on_snap_to_grid(GtkAction * act,gpointer user_data)2874 static void on_snap_to_grid(GtkAction* act, gpointer user_data)
2875 {
2876 FmDesktop* desktop = FM_DESKTOP(user_data);
2877 FmDesktopItem* item;
2878 GList* items = get_selected_items(desktop, NULL);
2879 GList* l;
2880 int x, y;
2881 GtkTextDirection direction = gtk_widget_get_direction(GTK_WIDGET(desktop));
2882
2883 y = desktop->working_area.y + desktop->ymargin;
2884 //bottom = desktop->working_area.y + desktop->working_area.height - desktop->ymargin - desktop->cell_h;
2885
2886 if(direction != GTK_TEXT_DIR_RTL) /* LTR or NONE */
2887 x = desktop->working_area.x + desktop->xmargin;
2888 else /* RTL */
2889 x = desktop->working_area.x + desktop->working_area.width - desktop->xmargin - desktop->cell_w;
2890
2891 for(l = items; l; l = l->next)
2892 {
2893 int new_x, new_y;
2894 item = (FmDesktopItem*)l->data;
2895 if(!item->fixed_pos)
2896 continue;
2897 new_x = x + _round((double)(item->area.x - x) / desktop->cell_w) * desktop->cell_w;
2898 new_y = y + _round((double)(item->area.y - y) / desktop->cell_h) * desktop->cell_h;
2899 move_item(desktop, item, new_x, new_y, FALSE);
2900 }
2901 g_list_free(items);
2902
2903 queue_layout_items(desktop);
2904 }
2905
2906
2907 /* ---------------------------------------------------------------------
2908 GtkWidget class default signal handlers */
2909
is_point_in_rect(GdkRectangle * rect,int x,int y)2910 static gboolean is_point_in_rect(GdkRectangle* rect, int x, int y)
2911 {
2912 return x >= rect->x && x < (rect->x + rect->width) && y >= rect->y && y < (rect->y + rect->height);
2913 }
2914
hit_test(FmDesktop * self,GtkTreeIter * it,int x,int y)2915 static FmDesktopItem* hit_test(FmDesktop* self, GtkTreeIter *it, int x, int y)
2916 {
2917 FmDesktopItem* item;
2918 GtkTreeModel* model;
2919
2920 if (!self->model)
2921 return NULL;
2922 model = GTK_TREE_MODEL(self->model);
2923 if(model && gtk_tree_model_get_iter_first(model, it)) do
2924 {
2925 GdkRectangle icon_rect;
2926 item = fm_folder_model_get_item_userdata(self->model, it);
2927 /* we cannot drop dragged items onto themselves */
2928 if (item->is_selected && self->dragging)
2929 continue;
2930 /* SF bug #963: icon_rect and text_rect may be not contiguous,
2931 so let expand icon test area up to text_rect */
2932 icon_rect = item->icon_rect;
2933 icon_rect.height = item->text_rect.y - icon_rect.y;
2934 if(is_point_in_rect(&icon_rect, x, y)
2935 || is_point_in_rect(&item->text_rect, x, y))
2936 return item;
2937 }
2938 while(gtk_tree_model_iter_next(model, it));
2939 return NULL;
2940 }
2941
get_nearest_item(FmDesktop * desktop,FmDesktopItem * item,GtkDirectionType dir)2942 static FmDesktopItem* get_nearest_item(FmDesktop* desktop, FmDesktopItem* item, GtkDirectionType dir)
2943 {
2944 GtkTreeModel* model;
2945 FmDesktopItem* item2, *ret = NULL;
2946 guint min_x_dist, min_y_dist, dist;
2947 GtkTreeIter it;
2948
2949 if (!desktop->model)
2950 return NULL;
2951 model = GTK_TREE_MODEL(desktop->model);
2952 if(!gtk_tree_model_get_iter_first(model, &it))
2953 return NULL;
2954 if(!item) /* there is no focused item yet, select first one then */
2955 return fm_folder_model_get_item_userdata(desktop->model, &it);
2956
2957 min_x_dist = min_y_dist = (guint)-1;
2958 item2 = NULL;
2959
2960 switch(dir)
2961 {
2962 case GTK_DIR_LEFT:
2963 do
2964 {
2965 item2 = fm_folder_model_get_item_userdata(desktop->model, &it);
2966 if(item2->area.x >= item->area.x)
2967 continue;
2968 dist = item->area.x - item2->area.x;
2969 if(dist < min_x_dist)
2970 {
2971 ret = item2;
2972 min_x_dist = dist;
2973 min_y_dist = ABS(item->area.y - item2->area.y);
2974 }
2975 else if(dist == min_x_dist && item2 != ret) /* if there is another item of the same x distance */
2976 {
2977 /* get the one with smaller y distance */
2978 dist = ABS(item2->area.y - item->area.y);
2979 if(dist < min_y_dist)
2980 {
2981 ret = item2;
2982 min_y_dist = dist;
2983 }
2984 }
2985 }
2986 while(gtk_tree_model_iter_next(model, &it));
2987 break;
2988 case GTK_DIR_RIGHT:
2989 do
2990 {
2991 item2 = fm_folder_model_get_item_userdata(desktop->model, &it);
2992 if(item2->area.x <= item->area.x)
2993 continue;
2994 dist = item2->area.x - item->area.x;
2995 if(dist < min_x_dist)
2996 {
2997 ret = item2;
2998 min_x_dist = dist;
2999 min_y_dist = ABS(item->area.y - item2->area.y);
3000 }
3001 else if(dist == min_x_dist && item2 != ret) /* if there is another item of the same x distance */
3002 {
3003 /* get the one with smaller y distance */
3004 dist = ABS(item2->area.y - item->area.y);
3005 if(dist < min_y_dist)
3006 {
3007 ret = item2;
3008 min_y_dist = dist;
3009 }
3010 }
3011 }
3012 while(gtk_tree_model_iter_next(model, &it));
3013 break;
3014 case GTK_DIR_UP:
3015 do
3016 {
3017 item2 = fm_folder_model_get_item_userdata(desktop->model, &it);
3018 if(item2->area.y >= item->area.y)
3019 continue;
3020 dist = item->area.y - item2->area.y;
3021 if(dist < min_y_dist)
3022 {
3023 ret = item2;
3024 min_y_dist = dist;
3025 min_x_dist = ABS(item->area.x - item2->area.x);
3026 }
3027 else if(dist == min_y_dist && item2 != ret) /* if there is another item of the same y distance */
3028 {
3029 /* get the one with smaller x distance */
3030 dist = ABS(item2->area.x - item->area.x);
3031 if(dist < min_x_dist)
3032 {
3033 ret = item2;
3034 min_x_dist = dist;
3035 }
3036 }
3037 }
3038 while(gtk_tree_model_iter_next(model, &it));
3039 break;
3040 case GTK_DIR_DOWN:
3041 do
3042 {
3043 item2 = fm_folder_model_get_item_userdata(desktop->model, &it);
3044 if(item2->area.y <= item->area.y)
3045 continue;
3046 dist = item2->area.y - item->area.y;
3047 if(dist < min_y_dist)
3048 {
3049 ret = item2;
3050 min_y_dist = dist;
3051 min_x_dist = ABS(item->area.x - item2->area.x);
3052 }
3053 else if(dist == min_y_dist && item2 != ret) /* if there is another item of the same y distance */
3054 {
3055 /* get the one with smaller x distance */
3056 dist = ABS(item2->area.x - item->area.x);
3057 if(dist < min_x_dist)
3058 {
3059 ret = item2;
3060 min_x_dist = dist;
3061 }
3062 }
3063 }
3064 while(gtk_tree_model_iter_next(model, &it));
3065 break;
3066 case GTK_DIR_TAB_FORWARD: /* FIXME */
3067 break;
3068 case GTK_DIR_TAB_BACKWARD: /* FIXME */
3069 ;
3070 }
3071 return ret;
3072 }
3073
set_focused_item(FmDesktop * desktop,FmDesktopItem * item)3074 static void set_focused_item(FmDesktop* desktop, FmDesktopItem* item)
3075 {
3076 if(item != desktop->focus)
3077 {
3078 FmDesktopItem* old_focus = desktop->focus;
3079 desktop->focus = item;
3080 if(old_focus)
3081 {
3082 redraw_item(desktop, old_focus);
3083 fm_desktop_accessible_focus_unset(desktop, old_focus);
3084 }
3085 if(item)
3086 {
3087 redraw_item(desktop, item);
3088 fm_desktop_accessible_focus_set(desktop, item);
3089 }
3090 }
3091 }
3092
_focus_and_select_focused_item(FmDesktop * desktop,FmDesktopItem * item)3093 static void _focus_and_select_focused_item(FmDesktop *desktop, FmDesktopItem *item)
3094 {
3095 item->is_selected = TRUE;
3096 fm_desktop_item_selected_changed(desktop, item);
3097 set_focused_item(desktop, item);
3098 }
3099
3100 /* This function is taken from xfdesktop */
forward_event_to_rootwin(GdkScreen * gscreen,GdkEvent * event)3101 static void forward_event_to_rootwin(GdkScreen *gscreen, GdkEvent *event)
3102 {
3103 XButtonEvent xev, xev2;
3104 Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_screen_get_display(gscreen));
3105
3106 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE)
3107 {
3108 if (event->type == GDK_BUTTON_PRESS)
3109 {
3110 xev.type = ButtonPress;
3111 /*
3112 * rox has an option to disable the next
3113 * instruction. it is called "blackbox_hack". Does
3114 * anyone know why exactly it is needed?
3115 */
3116 XUngrabPointer(dpy, event->button.time);
3117 }
3118 else
3119 xev.type = ButtonRelease;
3120
3121 xev.button = event->button.button;
3122 xev.x = event->button.x; /* Needed for icewm */
3123 xev.y = event->button.y;
3124 xev.x_root = event->button.x_root;
3125 xev.y_root = event->button.y_root;
3126 xev.state = event->button.state;
3127
3128 xev2.type = 0;
3129 }
3130 else if (event->type == GDK_SCROLL)
3131 {
3132 xev.type = ButtonPress;
3133 xev.button = event->scroll.direction + 4;
3134 xev.x = event->scroll.x; /* Needed for icewm */
3135 xev.y = event->scroll.y;
3136 xev.x_root = event->scroll.x_root;
3137 xev.y_root = event->scroll.y_root;
3138 xev.state = event->scroll.state;
3139
3140 xev2.type = ButtonRelease;
3141 xev2.button = xev.button;
3142 }
3143 else
3144 return ;
3145 xev.window = GDK_WINDOW_XID(gdk_screen_get_root_window(gscreen));
3146 xev.root = xev.window;
3147 xev.subwindow = None;
3148 xev.time = event->button.time;
3149 xev.same_screen = True;
3150
3151 XSendEvent(dpy, xev.window, False, ButtonPressMask | ButtonReleaseMask,
3152 (XEvent *) & xev);
3153 if (xev2.type == 0)
3154 return ;
3155
3156 /* send button release for scroll event */
3157 xev2.window = xev.window;
3158 xev2.root = xev.root;
3159 xev2.subwindow = xev.subwindow;
3160 xev2.time = xev.time;
3161 xev2.x = xev.x;
3162 xev2.y = xev.y;
3163 xev2.x_root = xev.x_root;
3164 xev2.y_root = xev.y_root;
3165 xev2.state = xev.state;
3166 xev2.same_screen = xev.same_screen;
3167
3168 XSendEvent(dpy, xev2.window, False, ButtonPressMask | ButtonReleaseMask,
3169 (XEvent *) & xev2);
3170 }
3171
3172
3173 #if GTK_CHECK_VERSION(3, 0, 0)
on_draw(GtkWidget * w,cairo_t * cr)3174 static gboolean on_draw(GtkWidget* w, cairo_t* cr)
3175 #else
3176 static gboolean on_expose(GtkWidget* w, GdkEventExpose* evt)
3177 #endif
3178 {
3179 FmDesktop* self = (FmDesktop*)w;
3180 #if !GTK_CHECK_VERSION(3, 0, 0)
3181 cairo_t* cr;
3182 #endif
3183 GtkTreeModel* model = self->model ? GTK_TREE_MODEL(self->model) : NULL;
3184 GtkTreeIter it;
3185 GdkRectangle area;
3186
3187 #if GTK_CHECK_VERSION(3, 0, 0)
3188 if(G_UNLIKELY(!gtk_cairo_should_draw_window(cr, gtk_widget_get_window(w))))
3189 return FALSE;
3190
3191 cairo_save(cr);
3192 gtk_cairo_transform_to_window(cr, w, gtk_widget_get_window(w));
3193 gdk_cairo_get_clip_rectangle(cr, &area);
3194 #else
3195 if(G_UNLIKELY(! gtk_widget_get_visible (w) || ! gtk_widget_get_mapped (w)))
3196 return TRUE;
3197
3198 cr = gdk_cairo_create(gtk_widget_get_window(w));
3199 area = evt->area;
3200 #endif
3201 if(self->rubber_bending)
3202 paint_rubber_banding_rect(self, cr, &area);
3203
3204 if(model && gtk_tree_model_get_iter_first(model, &it)) do
3205 {
3206 FmDesktopItem* item = fm_folder_model_get_item_userdata(self->model, &it);
3207 GdkRectangle* intersect, tmp, tmp2;
3208 GdkPixbuf* icon = NULL;
3209 if(gdk_rectangle_intersect(&area, &item->icon_rect, &tmp))
3210 intersect = &tmp;
3211 else
3212 intersect = NULL;
3213
3214 if(gdk_rectangle_intersect(&area, &item->text_rect, &tmp2))
3215 {
3216 if(intersect)
3217 gdk_rectangle_union(intersect, &tmp2, intersect);
3218 else
3219 intersect = &tmp2;
3220 }
3221
3222 if(intersect)
3223 {
3224 gtk_tree_model_get(model, &it, FM_FOLDER_MODEL_COL_ICON, &icon, -1);
3225 paint_item(self, item, cr, intersect, icon);
3226 if(icon)
3227 g_object_unref(icon);
3228 }
3229 }
3230 while(gtk_tree_model_iter_next(model, &it));
3231 #if GTK_CHECK_VERSION(3, 0, 0)
3232 cairo_restore(cr);
3233 #else
3234 cairo_destroy(cr);
3235 #endif
3236
3237 return TRUE;
3238 }
3239
on_size_allocate(GtkWidget * w,GtkAllocation * alloc)3240 static void on_size_allocate(GtkWidget* w, GtkAllocation* alloc)
3241 {
3242 FmDesktop* self = (FmDesktop*)w;
3243
3244 /* calculate item size */
3245 PangoContext* pc;
3246 PangoFontMetrics *metrics;
3247 int font_h;
3248
3249 pc = gtk_widget_get_pango_context((GtkWidget*)self);
3250
3251 metrics = pango_context_get_metrics(pc, NULL, NULL);
3252
3253 font_h = pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent (metrics);
3254 pango_font_metrics_unref(metrics);
3255
3256 font_h /= PANGO_SCALE;
3257
3258 self->spacing = SPACING;
3259 self->xpad = self->ypad = PADDING;
3260 self->xmargin = self->ymargin = MARGIN;
3261 self->text_h = font_h * 2;
3262 self->text_w = 100;
3263 #if FM_CHECK_VERSION(1, 2, 0)
3264 if (fm_config->show_full_names)
3265 self->pango_text_h = -1;
3266 else
3267 #endif
3268 self->pango_text_h = self->text_h * PANGO_SCALE;
3269 self->pango_text_w = self->text_w * PANGO_SCALE;
3270 self->text_h += 4;
3271 self->text_w += 4; /* 4 is for drawing border */
3272 self->cell_h = fm_config->big_icon_size + self->spacing + self->text_h + self->ypad * 2;
3273 self->cell_w = MAX((gint)self->text_w, fm_config->big_icon_size) + self->xpad * 2;
3274
3275 update_working_area(self);
3276 /* queue_layout_items(self); this is called in update_working_area */
3277
3278 /* scale the wallpaper */
3279 if(gtk_widget_get_realized(w))
3280 {
3281 #if GTK_CHECK_VERSION(3, 0, 0)
3282 /* bug SF#958: with GTK 3.8+ font is reset to default after realizing
3283 so let enforce font description on it right away */
3284 PangoFontDescription *font_desc = pango_font_description_from_string(self->conf.desktop_font);
3285 pango_context_set_font_description(pc, font_desc);
3286 pango_font_description_free(font_desc);
3287 #endif
3288 /* bug #3614866: after monitor geometry was changed we need to redraw
3289 the background invalidating all the cache */
3290 _clear_bg_cache(self);
3291 if(self->conf.wallpaper_mode != FM_WP_COLOR && self->conf.wallpaper_mode != FM_WP_TILE)
3292 update_background(self, -1);
3293 }
3294
3295 GTK_WIDGET_CLASS(fm_desktop_parent_class)->size_allocate(w, alloc);
3296 }
3297
3298 #if GTK_CHECK_VERSION(3, 0, 0)
on_get_preferred_width(GtkWidget * w,gint * minimal_width,gint * natural_width)3299 static void on_get_preferred_width(GtkWidget *w, gint *minimal_width, gint *natural_width)
3300 {
3301 GdkScreen* scr = gtk_widget_get_screen(w);
3302 gint monitor = FM_DESKTOP(w)->monitor;
3303 GdkRectangle geom;
3304 gdk_screen_get_monitor_geometry(scr, monitor, &geom);
3305 *minimal_width = *natural_width = geom.width;
3306 }
3307
on_get_preferred_height(GtkWidget * w,gint * minimal_height,gint * natural_height)3308 static void on_get_preferred_height(GtkWidget *w, gint *minimal_height, gint *natural_height)
3309 {
3310 GdkScreen* scr = gtk_widget_get_screen(w);
3311 gint monitor = FM_DESKTOP(w)->monitor;
3312 GdkRectangle geom;
3313 gdk_screen_get_monitor_geometry(scr, monitor, &geom);
3314 *minimal_height = *natural_height = geom.height;
3315 }
3316 #else
on_size_request(GtkWidget * w,GtkRequisition * req)3317 static void on_size_request(GtkWidget* w, GtkRequisition* req)
3318 {
3319 GdkScreen* scr = gtk_widget_get_screen(w);
3320 gint monitor = FM_DESKTOP(w)->monitor;
3321 GdkRectangle geom;
3322 gdk_screen_get_monitor_geometry(scr, monitor, &geom);
3323 req->width = geom.width;
3324 req->height = geom.height;
3325 }
3326 #endif
3327
_stop_rubberbanding(FmDesktop * self,gint x,gint y)3328 static void _stop_rubberbanding(FmDesktop *self, gint x, gint y)
3329 {
3330 /* re-enable Gtk+ DnD callbacks again */
3331 gpointer drag_data = g_object_get_data(G_OBJECT(self),
3332 g_intern_static_string("gtk-site-data"));
3333 if(G_LIKELY(drag_data != NULL))
3334 {
3335 g_signal_handlers_unblock_matched(G_OBJECT(self), G_SIGNAL_MATCH_DATA,
3336 0, 0, NULL, NULL, drag_data);
3337 }
3338 self->rubber_bending = FALSE;
3339 update_rubberbanding(self, x, y);
3340 gtk_grab_remove(GTK_WIDGET(self));
3341 }
3342
on_button_press(GtkWidget * w,GdkEventButton * evt)3343 static gboolean on_button_press(GtkWidget* w, GdkEventButton* evt)
3344 {
3345 FmDesktop* self = (FmDesktop*)w;
3346 FmDesktopItem *item = NULL, *clicked_item = NULL;
3347 GtkTreeIter it;
3348 FmFolderViewClickType clicked = FM_FV_CLICK_NONE;
3349
3350 clicked_item = hit_test(FM_DESKTOP(w), &it, (int)evt->x, (int)evt->y);
3351
3352 /* reset auto-selection now */
3353 if (self->single_click_timeout_handler != 0)
3354 {
3355 g_source_remove(self->single_click_timeout_handler);
3356 self->single_click_timeout_handler = 0;
3357 }
3358
3359 if(evt->type == GDK_BUTTON_PRESS)
3360 {
3361 /* ignore another buttons while some is in progress */
3362 if (self->button_pressed == 0)
3363 self->button_pressed = evt->button;
3364 if(evt->button == 1) /* left button */
3365 {
3366 /* store button state for drag & drop */
3367 self->drag_start_x = evt->x;
3368 self->drag_start_y = evt->y;
3369 }
3370 else if (self->rubber_bending)
3371 {
3372 /* LP #1071121: right click stops rubberbanding but
3373 leaves the selection area on the desktop.
3374 To avoid that weird thing we reset and stop rubberbanding now */
3375 _stop_rubberbanding(self, self->drag_start_x, self->drag_start_y);
3376 }
3377
3378 /* if ctrl / shift is not pressed, deselect all. */
3379 /* FIXME: do [un]selection on button release */
3380 if(! (evt->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)))
3381 {
3382 if (clicked_item == NULL)
3383 {
3384 if (evt->button == 1)
3385 /* SF bug #999: unselect all only on left button */
3386 _unselect_all(FM_FOLDER_VIEW(self));
3387 }
3388 /* don't cancel selection if clicking on selected items */
3389 else if (!((evt->button == 1 || evt->button == 3) && clicked_item->is_selected))
3390 _unselect_all(FM_FOLDER_VIEW(self));
3391 }
3392
3393 if(clicked_item)
3394 {
3395 if(evt->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
3396 clicked_item->is_selected = ! clicked_item->is_selected;
3397 else
3398 clicked_item->is_selected = TRUE;
3399 fm_desktop_item_selected_changed(self, clicked_item);
3400
3401 if(self->focus && self->focus != item)
3402 {
3403 FmDesktopItem* old_focus = self->focus;
3404 self->focus = NULL;
3405 if(old_focus)
3406 {
3407 redraw_item(self, old_focus);
3408 fm_desktop_accessible_focus_unset(self, old_focus);
3409 }
3410 }
3411 self->focus = clicked_item;
3412 fm_desktop_accessible_focus_set(self, clicked_item);
3413 redraw_item(self, clicked_item);
3414
3415 if(evt->button == 3) /* right click, context menu */
3416 clicked = FM_FV_CONTEXT_MENU;
3417 else if(evt->button == 2)
3418 clicked = FM_FV_MIDDLE_CLICK;
3419 }
3420 else /* no item is clicked */
3421 {
3422 if(evt->button == 3) /* right click on the blank area => desktop popup menu */
3423 {
3424 if(!self->conf.show_wm_menu)
3425 clicked = FM_FV_CONTEXT_MENU;
3426 }
3427 else if(evt->button == 1)
3428 {
3429 /* disable Gtk+ DnD callbacks, because else rubberbanding will be interrupted */
3430 gpointer drag_data = g_object_get_data(G_OBJECT(self),
3431 g_intern_static_string("gtk-site-data"));
3432 if(G_LIKELY(drag_data != NULL))
3433 {
3434 g_signal_handlers_block_matched(G_OBJECT(self),
3435 G_SIGNAL_MATCH_DATA, 0, 0,
3436 NULL, NULL, drag_data);
3437 }
3438 self->rubber_bending = TRUE;
3439
3440 /* FIXME: if you foward the event here, this will break rubber bending... */
3441 /* forward the event to root window */
3442 /* forward_event_to_rootwin(gtk_widget_get_screen(w), evt); */
3443
3444 gtk_grab_add(w);
3445 self->rubber_bending_x = evt->x;
3446 self->rubber_bending_y = evt->y;
3447 }
3448 }
3449 }
3450 else if(evt->type == GDK_2BUTTON_PRESS) /* activate items */
3451 {
3452 if(clicked_item && evt->button == 1) /* left double click */
3453 clicked = FM_FV_ACTIVATED;
3454 }
3455
3456 if(clicked != FM_FV_CLICK_NONE)
3457 {
3458 GtkTreePath* tp = NULL;
3459
3460 if(self->model && clicked_item)
3461 tp = gtk_tree_model_get_path(GTK_TREE_MODEL(self->model), &it);
3462 fm_folder_view_item_clicked(FM_FOLDER_VIEW(self), tp, clicked);
3463 if(tp)
3464 gtk_tree_path_free(tp);
3465 /* SF bug #929: after click the tooltip is still set to the item name */
3466 self->hover_item = NULL;
3467 }
3468 /* forward the event to root window */
3469 else if(evt->button != 1 && evt->button == self->button_pressed)
3470 {
3471 self->forward_pending = TRUE;
3472 forward_event_to_rootwin(gtk_widget_get_screen(w), (GdkEvent*)evt);
3473 }
3474
3475 if(! gtk_widget_has_focus(w))
3476 {
3477 /* g_debug("we don't have the focus, grab it!"); */
3478 gtk_widget_grab_focus(w);
3479 }
3480 return TRUE;
3481 }
3482
on_button_release(GtkWidget * w,GdkEventButton * evt)3483 static gboolean on_button_release(GtkWidget* w, GdkEventButton* evt)
3484 {
3485 FmDesktop* self = (FmDesktop*)w;
3486
3487 if(self->rubber_bending)
3488 {
3489 _stop_rubberbanding(self, evt->x, evt->y);
3490 }
3491 else if(self->dragging)
3492 {
3493 self->dragging = FALSE;
3494 /* restore after drag */
3495 queue_layout_items(self);
3496 }
3497 else if(fm_config->single_click && evt->button == 1)
3498 {
3499 GtkTreeIter it;
3500 FmDesktopItem* clicked_item = hit_test(self, &it, evt->x, evt->y);
3501 if(clicked_item)
3502 /* left single click */
3503 fm_launch_file_simple(GTK_WINDOW(w), NULL, clicked_item->fi, pcmanfm_open_folder, w);
3504 }
3505
3506 /* forward the event to root window */
3507 if (self->button_pressed == evt->button)
3508 {
3509 if (self->forward_pending)
3510 forward_event_to_rootwin(gtk_widget_get_screen(w), (GdkEvent*)evt);
3511 self->button_pressed = 0;
3512 self->forward_pending = FALSE;
3513 }
3514
3515 return TRUE;
3516 }
3517
on_single_click_timeout(gpointer user_data)3518 static gboolean on_single_click_timeout(gpointer user_data)
3519 {
3520 FmDesktop* self = (FmDesktop*)user_data;
3521 GtkWidget* w = (GtkWidget*)self;
3522 GdkWindow* window;
3523 int x, y;
3524 FmDesktopItem *item;
3525 GdkModifierType state;
3526 GtkTreeIter it;
3527
3528 if(g_source_is_destroyed(g_main_current_source()))
3529 return FALSE;
3530
3531 self->single_click_timeout_handler = 0;
3532 /* ensure we are still in single-click mode and have hovered item */
3533 if (!fm_config->single_click || !self->hover_item)
3534 return FALSE;
3535 /* ensure we are still on the same item */
3536 window = gtk_widget_get_window(w);
3537 gdk_window_get_pointer(window, &x, &y, &state);
3538 item = hit_test(self, &it, x, y);
3539 if (item != self->hover_item)
3540 return FALSE;
3541 /* ok, let select the item then */
3542 state &= gtk_accelerator_get_default_mod_mask();
3543 if (state == 0) /* no modifiers - drop selection and select this item */
3544 {
3545 _unselect_all(FM_FOLDER_VIEW(self));
3546 item->is_selected = TRUE;
3547 }
3548 else if (state == GDK_CONTROL_MASK) /* invert selection on the item */
3549 {
3550 item->is_selected = ! item->is_selected;
3551 }
3552 else /* ignore other modifiers */
3553 return FALSE;
3554 fm_desktop_item_selected_changed(self, item);
3555 set_focused_item(self, item);
3556
3557 return FALSE;
3558 }
3559
on_motion_notify(GtkWidget * w,GdkEventMotion * evt)3560 static gboolean on_motion_notify(GtkWidget* w, GdkEventMotion* evt)
3561 {
3562 FmDesktop* self = (FmDesktop*)w;
3563 if(! self->button_pressed)
3564 {
3565 if(fm_config->single_click)
3566 {
3567 GtkTreeIter it;
3568 FmDesktopItem* item = hit_test(self, &it, evt->x, evt->y);
3569 FmDesktopItem *hover_item = self->hover_item;
3570 GdkWindow* window;
3571
3572 if(item != hover_item)
3573 {
3574 if(0 != self->single_click_timeout_handler)
3575 {
3576 g_source_remove(self->single_click_timeout_handler);
3577 self->single_click_timeout_handler = 0;
3578 }
3579 window = gtk_widget_get_window(w);
3580 self->hover_item = item;
3581 if (hover_item)
3582 redraw_item(self, hover_item);
3583 if(item)
3584 {
3585 redraw_item(self, item);
3586 gdk_window_set_cursor(window, hand_cursor);
3587 #if FM_CHECK_VERSION(1, 2, 0)
3588 if(fm_config->auto_selection_delay > 0)
3589 self->single_click_timeout_handler = gdk_threads_add_timeout(fm_config->auto_selection_delay,
3590 on_single_click_timeout, self);
3591 #else
3592 self->single_click_timeout_handler = gdk_threads_add_timeout(400, on_single_click_timeout, self); //400 ms
3593 #endif
3594 /* Making a loop to aviod the selection of the item */
3595 /* on_single_click_timeout(self); */
3596 }
3597 else
3598 {
3599 gdk_window_set_cursor(window, NULL);
3600 }
3601 }
3602 }
3603 else
3604 {
3605 GtkTreeIter it;
3606 FmDesktopItem* item = hit_test(self, &it, evt->x, evt->y);
3607 FmDesktopItem *hover_item = self->hover_item;
3608
3609 if(item != hover_item)
3610 {
3611 self->hover_item = item;
3612 if (hover_item)
3613 redraw_item(self, hover_item);
3614 if(item)
3615 redraw_item(self, item);
3616 }
3617 }
3618 return TRUE;
3619 }
3620
3621 if(self->dragging)
3622 {
3623 /* FIXME: never reached */
3624 }
3625 else if(self->rubber_bending)
3626 {
3627 update_rubberbanding(self, evt->x, evt->y);
3628 }
3629 /* we use auto-DnD so no DnD check is possible here */
3630
3631 return TRUE;
3632 }
3633
on_leave_notify(GtkWidget * w,GdkEventCrossing * evt)3634 static gboolean on_leave_notify(GtkWidget* w, GdkEventCrossing *evt)
3635 {
3636 FmDesktop* self = (FmDesktop*)w;
3637 if(self->single_click_timeout_handler)
3638 {
3639 g_source_remove(self->single_click_timeout_handler);
3640 self->single_click_timeout_handler = 0;
3641 }
3642 return TRUE;
3643 }
3644
get_focused_item(FmDesktopItem * focus,GtkTreeModel * model,GtkTreeIter * it)3645 static gboolean get_focused_item(FmDesktopItem* focus, GtkTreeModel* model, GtkTreeIter* it)
3646 {
3647 FmDesktopItem* item;
3648 if(gtk_tree_model_get_iter_first(model, it)) do
3649 {
3650 item = fm_folder_model_get_item_userdata(FM_FOLDER_MODEL(model), it);
3651 if(item == focus)
3652 return item->is_selected;
3653 }
3654 while(gtk_tree_model_iter_next(model, it));
3655 return FALSE;
3656 }
3657
3658 /* ---- Interactive search funcs: mostly picked from ExoIconView ---- */
3659
3660 /* Cut and paste from gtkwindow.c & gtkwidget.c */
send_focus_change(GtkWidget * widget,gboolean in)3661 static void send_focus_change(GtkWidget *widget, gboolean in)
3662 {
3663 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
3664
3665 fevent->focus_change.type = GDK_FOCUS_CHANGE;
3666 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
3667 fevent->focus_change.in = in;
3668
3669 #if GTK_CHECK_VERSION(2, 22, 0)
3670 gtk_widget_send_focus_change (widget, fevent);
3671 #else
3672 g_object_ref (widget);
3673 if (in)
3674 GTK_OBJECT_FLAGS (widget) |= GTK_HAS_FOCUS;
3675 else
3676 GTK_OBJECT_FLAGS (widget) &= ~(GTK_HAS_FOCUS);
3677 gtk_widget_event (widget, fevent);
3678 g_object_notify (G_OBJECT (widget), "has-focus");
3679 g_object_unref (widget);
3680 #endif
3681
3682 gdk_event_free (fevent);
3683 }
3684
desktop_search_dialog_hide(GtkWidget * search_dialog,FmDesktop * desktop)3685 static void desktop_search_dialog_hide(GtkWidget *search_dialog, FmDesktop *desktop)
3686 {
3687 /* disconnect the "changed" signal handler */
3688 if (desktop->search_entry_changed_id != 0)
3689 {
3690 g_signal_handler_disconnect(desktop->search_entry, desktop->search_entry_changed_id);
3691 desktop->search_entry_changed_id = 0;
3692 }
3693
3694 /* disable the flush timeout */
3695 if (desktop->search_timeout_id != 0)
3696 {
3697 g_source_remove(desktop->search_timeout_id);
3698 desktop->search_timeout_id = 0;
3699 }
3700
3701 /* send focus-out event */
3702 send_focus_change(desktop->search_entry, FALSE);
3703 gtk_widget_hide(search_dialog);
3704 gtk_entry_set_text(GTK_ENTRY(desktop->search_entry), "");
3705 }
3706
desktop_search_delete_event(GtkWidget * widget,GdkEventAny * evt,FmDesktop * desktop)3707 static gboolean desktop_search_delete_event(GtkWidget *widget, GdkEventAny *evt,
3708 FmDesktop *desktop)
3709 {
3710 /* hide the search dialog */
3711 desktop_search_dialog_hide(widget, desktop);
3712 return TRUE;
3713 }
3714
desktop_search_timeout(gpointer user_data)3715 static gboolean desktop_search_timeout(gpointer user_data)
3716 {
3717 FmDesktop *desktop = FM_DESKTOP(user_data);
3718
3719 if (!g_source_is_destroyed(g_main_current_source()))
3720 desktop_search_dialog_hide(desktop->search_window, desktop);
3721 return FALSE;
3722 }
3723
desktop_search_timeout_destroy(gpointer user_data)3724 static void desktop_search_timeout_destroy(gpointer user_data)
3725 {
3726 FM_DESKTOP(user_data)->search_timeout_id = 0;
3727 }
3728
desktop_search_move(GtkWidget * widget,FmDesktop * desktop,gboolean move_up)3729 static void desktop_search_move(GtkWidget *widget, FmDesktop *desktop,
3730 gboolean move_up)
3731 {
3732 GtkTreeModel *model;
3733 const gchar *text;
3734 char *casefold, *key, *name;
3735 FmDesktopItem *item;
3736 GtkTreeIter it;
3737 gboolean found = FALSE;
3738
3739 /* check if we have a model */
3740 if (desktop->model == NULL)
3741 return;
3742 model = GTK_TREE_MODEL(desktop->model);
3743
3744 /* determine the current text for the search entry */
3745 text = gtk_entry_get_text(GTK_ENTRY(desktop->search_entry));
3746 if (G_UNLIKELY(text == NULL || text[0] == '\0'))
3747 return;
3748
3749 /* determine the iterator of the focused item */
3750 if (!get_focused_item(desktop->focus, model, &it))
3751 return;
3752
3753 /* normalize the pattern */
3754 casefold = g_utf8_casefold(text, -1);
3755 key = g_utf8_normalize(casefold, -1, G_NORMALIZE_ALL);
3756 g_free(casefold);
3757 /* let find matched item now */
3758 if (move_up)
3759 {
3760 #if GTK_CHECK_VERSION(3, 0, 0)
3761 while (!found && gtk_tree_model_iter_previous(model, &it))
3762 #else
3763 GtkTreePath *tp = gtk_tree_model_get_path(model, &it);
3764 while (!found && gtk_tree_path_prev(tp) && gtk_tree_model_get_iter(model, &it, tp))
3765 #endif
3766 {
3767 item = fm_folder_model_get_item_userdata(desktop->model, &it);
3768 casefold = g_utf8_casefold(fm_file_info_get_disp_name(item->fi), -1);
3769 name = g_utf8_normalize(casefold, -1, G_NORMALIZE_ALL);
3770 g_free(casefold);
3771 found = (strncmp(name, key, strlen(key)) == 0);
3772 g_free(name);
3773 }
3774 #if !GTK_CHECK_VERSION(3, 0, 0)
3775 gtk_tree_path_free(tp);
3776 #endif
3777 }
3778 else
3779 {
3780 while (!found && gtk_tree_model_iter_next(model, &it))
3781 {
3782 item = fm_folder_model_get_item_userdata(desktop->model, &it);
3783 casefold = g_utf8_casefold(fm_file_info_get_disp_name(item->fi), -1);
3784 name = g_utf8_normalize(casefold, -1, G_NORMALIZE_ALL);
3785 g_free(casefold);
3786 found = (strncmp(name, key, strlen(key)) == 0);
3787 g_free(name);
3788 }
3789 }
3790 g_free(key);
3791
3792 if (!found)
3793 return;
3794
3795 /* unselect all items */
3796 _unselect_all(FM_FOLDER_VIEW(desktop));
3797 /* focus found item */
3798 _focus_and_select_focused_item(desktop, item);
3799 }
3800
desktop_search_scroll_event(GtkWidget * widget,GdkEventScroll * evt,FmDesktop * desktop)3801 static gboolean desktop_search_scroll_event(GtkWidget *widget,
3802 GdkEventScroll *evt,
3803 FmDesktop *desktop)
3804 {
3805 g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
3806 g_return_val_if_fail(FM_IS_DESKTOP(desktop), FALSE);
3807
3808 if (evt->direction == GDK_SCROLL_UP)
3809 desktop_search_move(widget, desktop, TRUE);
3810 else if (evt->direction == GDK_SCROLL_DOWN)
3811 desktop_search_move(widget, desktop, FALSE);
3812 else
3813 return FALSE;
3814
3815 return TRUE;
3816 }
3817
desktop_search_update_timeout(FmDesktop * desktop)3818 static void desktop_search_update_timeout(FmDesktop *desktop)
3819 {
3820 if (desktop->search_timeout_id == 0)
3821 return;
3822 /* drop the previous timeout */
3823 g_source_remove(desktop->search_timeout_id);
3824 /* schedule a new timeout */
3825 desktop->search_timeout_id = gdk_threads_add_timeout_full(G_PRIORITY_LOW,
3826 DESKTOP_SEARCH_DIALOG_TIMEOUT,
3827 desktop_search_timeout,
3828 desktop,
3829 desktop_search_timeout_destroy);
3830 }
3831
desktop_search_key_press_event(GtkWidget * widget,GdkEventKey * evt,FmDesktop * desktop)3832 static gboolean desktop_search_key_press_event(GtkWidget *widget,
3833 GdkEventKey *evt,
3834 FmDesktop *desktop)
3835 {
3836 g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
3837 g_return_val_if_fail(FM_IS_DESKTOP(desktop), FALSE);
3838
3839 /* close window and cancel the search */
3840 if (evt->keyval == GDK_KEY_Escape || evt->keyval == GDK_KEY_Tab)
3841 desktop_search_dialog_hide(widget, desktop);
3842
3843 /* select previous matching iter */
3844 else if (evt->keyval == GDK_KEY_Up || evt->keyval == GDK_KEY_KP_Up)
3845 desktop_search_move(widget, desktop, TRUE);
3846
3847 else if (((evt->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
3848 && (evt->keyval == GDK_KEY_g || evt->keyval == GDK_KEY_G))
3849 desktop_search_move(widget, desktop, TRUE);
3850
3851 /* select next matching iter */
3852 else if (evt->keyval == GDK_KEY_Down || evt->keyval == GDK_KEY_KP_Down)
3853 desktop_search_move(widget, desktop, FALSE);
3854
3855 else if (((evt->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
3856 && (evt->keyval == GDK_KEY_g || evt->keyval == GDK_KEY_G))
3857 desktop_search_move(widget, desktop, FALSE);
3858
3859 else
3860 return FALSE;
3861
3862 /* renew the flush timeout */
3863 desktop_search_update_timeout(desktop);
3864
3865 return TRUE;
3866 }
3867
desktop_search_activate(GtkEntry * entry,FmDesktop * desktop)3868 static void desktop_search_activate(GtkEntry *entry, FmDesktop *desktop)
3869 {
3870 GtkTreeModel *model;
3871 GtkTreePath *tp;
3872 GtkTreeIter it;
3873
3874 /* hide the interactive search dialog */
3875 desktop_search_dialog_hide(desktop->search_window, desktop);
3876
3877 /* check if we have a cursor item, and if so, activate it */
3878 if (desktop->focus)
3879 {
3880 /* only activate the cursor item if it's selected */
3881 if (desktop->focus->is_selected)
3882 {
3883 model = GTK_TREE_MODEL(desktop->model);
3884 if(get_focused_item(desktop->focus, model, &it))
3885 {
3886 tp = gtk_tree_model_get_path(model, &it);
3887 fm_folder_view_item_clicked(FM_FOLDER_VIEW(desktop), tp, FM_FV_ACTIVATED);
3888 if(tp)
3889 gtk_tree_path_free(tp);
3890 }
3891 }
3892 }
3893 }
3894
3895 #if GTK_CHECK_VERSION(2, 20, 0)
desktop_search_preedit_changed(GtkEntry * entry,gchar * preedit,FmDesktop * desktop)3896 static void desktop_search_preedit_changed(GtkEntry *entry, gchar *preedit,
3897 FmDesktop *desktop)
3898 #else
3899 static void desktop_search_preedit_changed(GtkIMContext *im_context,
3900 FmDesktop *desktop)
3901 #endif
3902 {
3903 desktop->search_imcontext_changed = TRUE;
3904
3905 /* re-register the search timeout */
3906 desktop_search_update_timeout(desktop);
3907 }
3908
desktop_search_position(FmDesktop * desktop)3909 static void desktop_search_position(FmDesktop *desktop)
3910 {
3911 GtkRequisition requisition;
3912 GdkRectangle geom;
3913 gint x, y;
3914
3915 /* make sure the search dialog is realized */
3916 gtk_widget_realize(desktop->search_window);
3917
3918 #if GTK_CHECK_VERSION(3, 0, 0)
3919 gtk_widget_get_preferred_size(desktop->search_window, NULL, &requisition);
3920 #else
3921 gtk_widget_size_request(desktop->search_window, &requisition);
3922 #endif
3923
3924 /* put it into right upper corner */
3925 gdk_screen_get_monitor_geometry(gtk_widget_get_screen((GtkWidget*)desktop),
3926 desktop->monitor, &geom);
3927 x = geom.x + desktop->working_area.x + desktop->working_area.width - requisition.width;
3928 y = geom.y + desktop->working_area.y;
3929
3930 gtk_window_move(GTK_WINDOW(desktop->search_window), x, y);
3931 }
3932
desktop_search_init(GtkWidget * search_entry,FmDesktop * desktop)3933 static void desktop_search_init(GtkWidget *search_entry, FmDesktop *desktop)
3934 {
3935 GtkTreeModel *model;
3936 const gchar *text;
3937 char *casefold, *key, *name;
3938 FmDesktopItem *item;
3939 GtkTreeIter it;
3940 gboolean found = FALSE;
3941
3942 /* check if we have a model */
3943 if (desktop->model == NULL)
3944 return;
3945 model = GTK_TREE_MODEL(desktop->model);
3946
3947 /* renew the flush timeout */
3948 desktop_search_update_timeout(desktop);
3949
3950 /* determine the current text for the search entry */
3951 text = gtk_entry_get_text(GTK_ENTRY(desktop->search_entry));
3952 if (G_UNLIKELY(text == NULL || text[0] == '\0'))
3953 return;
3954
3955 /* unselect all items */
3956 _unselect_all(FM_FOLDER_VIEW(desktop));
3957
3958 /* normalize the pattern */
3959 casefold = g_utf8_casefold(text, -1);
3960 key = g_utf8_normalize(casefold, -1, G_NORMALIZE_ALL);
3961 g_free(casefold);
3962 /* find first matched item now */
3963 if (gtk_tree_model_get_iter_first(model, &it)) do
3964 {
3965 item = fm_folder_model_get_item_userdata(desktop->model, &it);
3966 casefold = g_utf8_casefold(fm_file_info_get_disp_name(item->fi), -1);
3967 name = g_utf8_normalize(casefold, -1, G_NORMALIZE_ALL);
3968 g_free(casefold);
3969 found = (strncmp(name, key, strlen(key)) == 0);
3970 g_free(name);
3971 }
3972 while (!found && gtk_tree_model_iter_next(model, &it));
3973 g_free(key);
3974
3975 /* focus found item */
3976 if (!found)
3977 return;
3978 _focus_and_select_focused_item(desktop, item);
3979 }
3980
desktop_search_ensure_window(FmDesktop * desktop)3981 static void desktop_search_ensure_window(FmDesktop *desktop)
3982 {
3983 GtkWindow *window;
3984 GtkWidget *frame;
3985 GtkWidget *vbox;
3986
3987 /* check if we already have a search window */
3988 if (G_LIKELY(desktop->search_window != NULL))
3989 return;
3990
3991 /* allocate a new search window */
3992 desktop->search_window = gtk_window_new(GTK_WINDOW_POPUP);
3993 window = GTK_WINDOW(desktop->search_window);
3994 gtk_window_group_add_window(win_group, window);
3995 gtk_window_set_modal(window, TRUE);
3996 gtk_window_set_screen(window, gtk_widget_get_screen(GTK_WIDGET(desktop)));
3997 /* connect signal handlers */
3998 g_signal_connect(window, "delete-event", G_CALLBACK(desktop_search_delete_event), desktop);
3999 g_signal_connect(window, "scroll-event", G_CALLBACK(desktop_search_scroll_event), desktop);
4000 g_signal_connect(window, "key-press-event", G_CALLBACK(desktop_search_key_press_event), desktop);
4001
4002 /* allocate the frame widget */
4003 frame = g_object_new(GTK_TYPE_FRAME, "shadow-type", GTK_SHADOW_ETCHED_IN, NULL);
4004 gtk_container_add(GTK_CONTAINER(window), frame);
4005 gtk_widget_show(frame);
4006
4007 /* allocate the vertical box */
4008 vbox = g_object_new(GTK_TYPE_VBOX, "border-width", 3, NULL);
4009 gtk_container_add(GTK_CONTAINER(frame), vbox);
4010 gtk_widget_show(vbox);
4011
4012 /* allocate the search entry widget */
4013 desktop->search_entry = gtk_entry_new();
4014 g_signal_connect(desktop->search_entry, "activate", G_CALLBACK(desktop_search_activate), desktop);
4015 #if GTK_CHECK_VERSION(2, 20, 0)
4016 g_signal_connect(desktop->search_entry, "preedit-changed",
4017 G_CALLBACK(desktop_search_preedit_changed), desktop);
4018 #else
4019 g_signal_connect(GTK_ENTRY(desktop->search_entry)->im_context, "preedit-changed",
4020 G_CALLBACK(desktop_search_preedit_changed), desktop);
4021 #endif
4022 gtk_box_pack_start(GTK_BOX(vbox), desktop->search_entry, TRUE, TRUE, 0);
4023 gtk_widget_realize(desktop->search_entry);
4024 gtk_widget_show(desktop->search_entry);
4025 }
4026
desktop_search_start(FmDesktop * desktop,gboolean keybinding)4027 static gboolean desktop_search_start(FmDesktop *desktop, gboolean keybinding)
4028 {
4029 GTypeClass *klass;
4030
4031 /* check if we already display the search window */
4032 if (desktop->search_window != NULL && gtk_widget_get_visible(desktop->search_window))
4033 return TRUE;
4034
4035 desktop_search_ensure_window(desktop);
4036
4037 /* clear search entry if we were started by a keybinding */
4038 if (G_UNLIKELY(keybinding))
4039 gtk_entry_set_text(GTK_ENTRY(desktop->search_entry), "");
4040
4041 /* determine the position for the search dialog */
4042 desktop_search_position(desktop);
4043
4044 /* display the search dialog */
4045 gtk_widget_show(desktop->search_window);
4046
4047 /* connect "changed" signal for the entry */
4048 if (G_UNLIKELY(desktop->search_entry_changed_id == 0))
4049 desktop->search_entry_changed_id = g_signal_connect(G_OBJECT(desktop->search_entry),
4050 "changed",
4051 G_CALLBACK(desktop_search_init),
4052 desktop);
4053
4054 /* start the search timeout */
4055 desktop->search_timeout_id = gdk_threads_add_timeout_full(G_PRIORITY_LOW,
4056 DESKTOP_SEARCH_DIALOG_TIMEOUT,
4057 desktop_search_timeout,
4058 desktop,
4059 desktop_search_timeout_destroy);
4060
4061 /* grab focus will select all the text, we don't want that to happen, so we
4062 * call the parent instance and bypass the selection change. This is probably
4063 * really hackish, but GtkTreeView does it as well *hrhr*
4064 */
4065 klass = g_type_class_peek_parent(GTK_ENTRY_GET_CLASS(desktop->search_entry));
4066 (*GTK_WIDGET_CLASS(klass)->grab_focus)(desktop->search_entry);
4067
4068 /* send focus-in event */
4069 send_focus_change(desktop->search_entry, TRUE);
4070
4071 /* search first matching iter */
4072 desktop_search_init(desktop->search_entry, desktop);
4073
4074 return TRUE;
4075 }
4076
on_key_press(GtkWidget * w,GdkEventKey * evt)4077 static gboolean on_key_press(GtkWidget* w, GdkEventKey* evt)
4078 {
4079 FmDesktop* desktop = (FmDesktop*)w;
4080 FmDesktopItem* item;
4081 int modifier = (evt->state & gtk_accelerator_get_default_mod_mask());
4082 FmPathList* sels;
4083 GtkTreeModel* model;
4084 GtkTreePath* tp = NULL;
4085 GtkTreeIter it;
4086 char *old_text, *new_text;
4087 GdkScreen *screen;
4088 GdkEvent *new_event;
4089 guint popup_menu_id;
4090 gboolean retval;
4091
4092 switch (evt->keyval)
4093 {
4094 case GDK_KEY_Escape:
4095 if (desktop->rubber_bending)
4096 {
4097 /* cancel rubberbanding now */
4098 _stop_rubberbanding(desktop, desktop->drag_start_x, desktop->drag_start_y);
4099 return TRUE;
4100 }
4101 break;
4102 case GDK_KEY_Left:
4103 item = get_nearest_item(desktop, desktop->focus, GTK_DIR_LEFT);
4104 if(item)
4105 {
4106 if(0 == modifier)
4107 {
4108 _unselect_all(FM_FOLDER_VIEW(desktop));
4109 item->is_selected = TRUE;
4110 fm_desktop_item_selected_changed(desktop, item);
4111 }
4112 set_focused_item(desktop, item);
4113 }
4114 return TRUE;
4115 case GDK_KEY_Right:
4116 item = get_nearest_item(desktop, desktop->focus, GTK_DIR_RIGHT);
4117 if(item)
4118 {
4119 if(0 == modifier)
4120 {
4121 _unselect_all(FM_FOLDER_VIEW(desktop));
4122 item->is_selected = TRUE;
4123 fm_desktop_item_selected_changed(desktop, item);
4124 }
4125 set_focused_item(desktop, item);
4126 }
4127 return TRUE;
4128 case GDK_KEY_Up:
4129 item = get_nearest_item(desktop, desktop->focus, GTK_DIR_UP);
4130 if(item)
4131 {
4132 if(0 == modifier)
4133 {
4134 _unselect_all(FM_FOLDER_VIEW(desktop));
4135 item->is_selected = TRUE;
4136 fm_desktop_item_selected_changed(desktop, item);
4137 }
4138 set_focused_item(desktop, item);
4139 }
4140 return TRUE;
4141 case GDK_KEY_Down:
4142 item = get_nearest_item(desktop, desktop->focus, GTK_DIR_DOWN);
4143 if(item)
4144 {
4145 if(0 == modifier)
4146 {
4147 _unselect_all(FM_FOLDER_VIEW(desktop));
4148 item->is_selected = TRUE;
4149 fm_desktop_item_selected_changed(desktop, item);
4150 }
4151 set_focused_item(desktop, item);
4152 }
4153 return TRUE;
4154 case GDK_KEY_space:
4155 if(modifier & GDK_CONTROL_MASK)
4156 {
4157 if(desktop->focus)
4158 {
4159 desktop->focus->is_selected = !desktop->focus->is_selected;
4160 redraw_item(desktop, desktop->focus);
4161 fm_desktop_item_selected_changed(desktop, desktop->focus);
4162 }
4163 return TRUE;
4164 }
4165 break;
4166 case GDK_KEY_F2:
4167 sels = _dup_selected_file_paths(FM_FOLDER_VIEW(desktop));
4168 if(sels)
4169 {
4170 fm_rename_file(GTK_WINDOW(desktop), fm_path_list_peek_head(sels));
4171 fm_path_list_unref(sels);
4172 return TRUE;
4173 }
4174 break;
4175 case GDK_KEY_Return:
4176 case GDK_KEY_ISO_Enter:
4177 case GDK_KEY_KP_Enter:
4178 if(modifier == 0 && desktop->focus)
4179 {
4180 model = GTK_TREE_MODEL(desktop->model);
4181 if(get_focused_item(desktop->focus, model, &it))
4182 {
4183 tp = gtk_tree_model_get_path(model, &it);
4184 fm_folder_view_item_clicked(FM_FOLDER_VIEW(desktop), tp, FM_FV_ACTIVATED);
4185 if(tp)
4186 gtk_tree_path_free(tp);
4187 }
4188 return TRUE;
4189 }
4190 break;
4191 case GDK_KEY_F:
4192 case GDK_KEY_f:
4193 if (modifier == GDK_CONTROL_MASK)
4194 return desktop_search_start(desktop, TRUE);
4195 break;
4196 }
4197 if (GTK_WIDGET_CLASS(fm_desktop_parent_class)->key_press_event(w, evt))
4198 return TRUE;
4199
4200 /* well, let try interactive search now */
4201 desktop_search_ensure_window(desktop);
4202
4203 /* make sure the search window is realized */
4204 gtk_widget_realize(desktop->search_window);
4205
4206 /* make a copy of the current text */
4207 old_text = gtk_editable_get_chars(GTK_EDITABLE(desktop->search_entry), 0, -1);
4208
4209 /* make sure we don't accidently popup the context menu */
4210 popup_menu_id = g_signal_connect(desktop->search_entry, "popup-menu", G_CALLBACK(gtk_true), NULL);
4211
4212 /* move the search window offscreen */
4213 screen = gtk_widget_get_screen(w);
4214 gtk_window_move(GTK_WINDOW(desktop->search_window),
4215 gdk_screen_get_width(screen) + 1,
4216 gdk_screen_get_height(screen) + 1);
4217 gtk_widget_show(desktop->search_window);
4218
4219 /* allocate a new event to forward */
4220 new_event = gdk_event_copy((GdkEvent *)evt);
4221 g_object_unref(new_event->key.window);
4222 new_event->key.window = g_object_ref(gtk_widget_get_window(desktop->search_entry));
4223
4224 /* send the event to the search entry. If the "preedit-changed" signal is
4225 * emitted during this event, priv->search_imcontext_changed will be set. */
4226 desktop->search_imcontext_changed = FALSE;
4227 retval = gtk_widget_event(desktop->search_entry, new_event);
4228 gtk_widget_hide(desktop->search_window);
4229
4230 /* release the temporary event */
4231 gdk_event_free(new_event);
4232
4233 /* disconnect the popup menu prevention */
4234 g_signal_handler_disconnect(desktop->search_entry, popup_menu_id);
4235
4236 /* we check to make sure that the entry tried to handle the,
4237 * and that the text has actually changed. */
4238 new_text = gtk_editable_get_chars(GTK_EDITABLE(desktop->search_entry), 0, -1);
4239 retval = retval && (strcmp(new_text, old_text) != 0);
4240 g_free(old_text);
4241 g_free(new_text);
4242
4243 /* if we're in a preedit or the text was modified */
4244 if (desktop->search_imcontext_changed || retval)
4245 {
4246 if (desktop_search_start(desktop, FALSE))
4247 {
4248 gtk_widget_grab_focus(w);
4249 return TRUE;
4250 }
4251 else
4252 {
4253 gtk_entry_set_text(GTK_ENTRY(desktop->search_entry), "");
4254 return FALSE;
4255 }
4256 }
4257 return FALSE;
4258 }
4259
4260 #if 0
4261 static void on_style_set(GtkWidget* w, GtkStyle* prev)
4262 {
4263 FmDesktop* self = (FmDesktop*)w;
4264 PangoContext* pc = gtk_widget_get_pango_context(w);
4265 if(font_desc)
4266 pango_context_set_font_description(pc, font_desc);
4267 pango_layout_context_changed(self->pl);
4268 }
4269 #endif
4270
on_direction_changed(GtkWidget * w,GtkTextDirection prev)4271 static void on_direction_changed(GtkWidget* w, GtkTextDirection prev)
4272 {
4273 FmDesktop* self = (FmDesktop*)w;
4274 pango_layout_context_changed(self->pl);
4275 queue_layout_items(self);
4276 }
4277
on_realize(GtkWidget * w)4278 static void on_realize(GtkWidget* w)
4279 {
4280 FmDesktop* self = (FmDesktop*)w;
4281 PangoFontDescription *font_desc;
4282 PangoContext* pc;
4283 #if GTK_CHECK_VERSION(3, 0, 0)
4284 char *css_data;
4285 #endif
4286
4287 GTK_WIDGET_CLASS(fm_desktop_parent_class)->realize(w);
4288 gtk_window_set_skip_pager_hint(GTK_WINDOW(w), TRUE);
4289 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(w), TRUE);
4290 gtk_window_set_resizable((GtkWindow*)w, FALSE);
4291
4292 load_config(self);
4293 /* setup self->conf now if it wasn't loaded above */
4294 if (!self->conf.configured)
4295 {
4296 copy_desktop_config(&self->conf, &app_config->desktop_section);
4297 queue_config_save(self);
4298 }
4299 /* copy found configuration to use by next monitor */
4300 else if (!app_config->desktop_section.configured)
4301 copy_desktop_config(&app_config->desktop_section, &self->conf);
4302 update_background(self, -1);
4303 /* set a proper desktop font if needed */
4304 if (self->conf.desktop_font == NULL)
4305 self->conf.desktop_font = g_strdup("Sans 12");
4306 font_desc = pango_font_description_from_string(self->conf.desktop_font);
4307 pc = gtk_widget_get_pango_context(w);
4308 pango_context_set_font_description(pc, font_desc);
4309 pango_font_description_free(font_desc);
4310 #if GTK_CHECK_VERSION(3, 0, 0)
4311 css_data = g_strdup_printf("FmDesktop {\n"
4312 "background-color: #%02x%02x%02x\n"
4313 "}",
4314 self->conf.desktop_bg.red/256,
4315 self->conf.desktop_bg.green/256,
4316 self->conf.desktop_bg.blue/256);
4317 gtk_css_provider_load_from_data(self->css, css_data, -1, NULL);
4318 g_free(css_data);
4319 #endif
4320 if (self->layout_pending)
4321 queue_layout_items(self);
4322 }
4323
on_focus_in(GtkWidget * w,GdkEventFocus * evt)4324 static gboolean on_focus_in(GtkWidget* w, GdkEventFocus* evt)
4325 {
4326 FmDesktop* self = (FmDesktop*) w;
4327 GtkTreeIter it;
4328 #if !GTK_CHECK_VERSION(2, 22, 0)
4329 GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS);
4330 #endif
4331 if(!self->focus && self->model
4332 && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(self->model), &it))
4333 {
4334 self->focus = fm_folder_model_get_item_userdata(self->model, &it);
4335 fm_desktop_accessible_focus_set(self, self->focus);
4336 }
4337 if(self->focus)
4338 redraw_item(self, self->focus);
4339 return FALSE;
4340 }
4341
on_focus_out(GtkWidget * w,GdkEventFocus * evt)4342 static gboolean on_focus_out(GtkWidget* w, GdkEventFocus* evt)
4343 {
4344 FmDesktop* self = (FmDesktop*) w;
4345 if(self->focus)
4346 {
4347 #if !GTK_CHECK_VERSION(2, 22, 0)
4348 GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS);
4349 #endif
4350 redraw_item(self, self->focus);
4351 }
4352 return FALSE;
4353 }
4354
4355 /* ---- Drag & Drop support ---- */
on_drag_motion(GtkWidget * dest_widget,GdkDragContext * drag_context,gint x,gint y,guint time)4356 static gboolean on_drag_motion (GtkWidget *dest_widget,
4357 GdkDragContext *drag_context,
4358 gint x, gint y, guint time)
4359 {
4360 GdkAtom target;
4361 GdkDragAction action = 0;
4362 FmDesktop* desktop = FM_DESKTOP(dest_widget);
4363 FmDesktopItem* item;
4364 GtkTreeIter it;
4365
4366 /* we don't support drag & drop if no model is set */
4367 if (desktop->model == NULL)
4368 {
4369 fm_dnd_dest_set_dest_file(desktop->dnd_dest, NULL);
4370 gdk_drag_status(drag_context, action, time);
4371 return FALSE;
4372 }
4373
4374 /* check if we're dragging over an item */
4375 item = hit_test(desktop, &it, x, y);
4376
4377 /* handle moving desktop items */
4378 if(!item)
4379 {
4380 if(fm_drag_context_has_target(drag_context, desktop_atom)
4381 && (gdk_drag_context_get_actions(drag_context) & GDK_ACTION_MOVE))
4382 {
4383 /* desktop item is being dragged */
4384 action = GDK_ACTION_MOVE; /* move desktop items */
4385 fm_dnd_dest_set_dest_file(desktop->dnd_dest, NULL);
4386 }
4387 }
4388
4389 /* FmDndDest will do the rest */
4390 if(!action)
4391 {
4392 fm_dnd_dest_set_dest_file(desktop->dnd_dest,
4393 item ? item->fi : fm_folder_get_info(fm_folder_model_get_folder(desktop->model)));
4394 target = fm_dnd_dest_find_target(desktop->dnd_dest, drag_context);
4395 if(target != GDK_NONE &&
4396 fm_dnd_dest_is_target_supported(desktop->dnd_dest, target))
4397 action = fm_dnd_dest_get_default_action(desktop->dnd_dest, drag_context, target);
4398 }
4399 gdk_drag_status(drag_context, action, time);
4400
4401 if(desktop->drop_hilight != item)
4402 {
4403 FmDesktopItem* old_drop = desktop->drop_hilight;
4404 if(action) /* don't hilight non-dropable item, see #3591767 */
4405 desktop->drop_hilight = item;
4406 if(old_drop)
4407 redraw_item(desktop, old_drop);
4408 if(item && action)
4409 redraw_item(desktop, item);
4410 }
4411
4412 return (action != 0);
4413 }
4414
on_drag_leave(GtkWidget * dest_widget,GdkDragContext * drag_context,guint time)4415 static void on_drag_leave (GtkWidget *dest_widget,
4416 GdkDragContext *drag_context,
4417 guint time)
4418 {
4419 FmDesktop* desktop = FM_DESKTOP(dest_widget);
4420
4421 if(desktop->drop_hilight)
4422 {
4423 FmDesktopItem* old_drop = desktop->drop_hilight;
4424 desktop->drop_hilight = NULL;
4425 redraw_item(desktop, old_drop);
4426 }
4427 }
4428
on_drag_drop(GtkWidget * dest_widget,GdkDragContext * drag_context,gint x,gint y,guint time)4429 static gboolean on_drag_drop (GtkWidget *dest_widget,
4430 GdkDragContext *drag_context,
4431 gint x, gint y, guint time)
4432 {
4433 FmDesktop* desktop = FM_DESKTOP(dest_widget);
4434 FmDesktopItem* item;
4435 GtkTreeIter it;
4436
4437 /* check if we're dropping on an item */
4438 item = hit_test(desktop, &it, x, y);
4439
4440 /* handle moving desktop items */
4441 if(!item)
4442 {
4443 if(fm_drag_context_has_target(drag_context, desktop_atom)
4444 && (gdk_drag_context_get_actions(drag_context) & GDK_ACTION_MOVE))
4445 {
4446 /* desktop item is being dragged */
4447 gtk_drag_get_data(dest_widget, drag_context, desktop_atom, time);
4448 return TRUE;
4449 }
4450 }
4451 return FALSE;
4452 }
4453
on_drag_data_received(GtkWidget * dest_widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * sel_data,guint info,guint time)4454 static void on_drag_data_received (GtkWidget *dest_widget,
4455 GdkDragContext *drag_context,
4456 gint x, gint y, GtkSelectionData *sel_data,
4457 guint info, guint time)
4458 {
4459 FmDesktop* desktop = FM_DESKTOP(dest_widget);
4460 GList *items, *l;
4461 int offset_x, offset_y;
4462
4463 if(info != FM_DND_DEST_DESKTOP_ITEM)
4464 return;
4465
4466 /* desktop items are being dragged */
4467 items = get_selected_items(desktop, NULL);
4468 offset_x = x - desktop->drag_start_x;
4469 offset_y = y - desktop->drag_start_y;
4470 for(l = items; l; l=l->next)
4471 {
4472 FmDesktopItem* item = (FmDesktopItem*)l->data;
4473 move_item(desktop, item, item->area.x + offset_x, item->area.y + offset_y, FALSE);
4474 }
4475 g_list_free(items);
4476
4477 /* save position of desktop icons on next idle */
4478 queue_config_save(desktop);
4479
4480 queue_layout_items(desktop);
4481
4482 gtk_drag_finish(drag_context, TRUE, FALSE, time);
4483 }
4484
_create_drag_icon(FmDesktop * desktop,gint * x,gint * y)4485 static GdkPixbuf *_create_drag_icon(FmDesktop *desktop, gint *x, gint *y)
4486 {
4487 GtkTreeModel *model;
4488 FmDesktopItem *item;
4489 cairo_surface_t *s;
4490 GdkPixbuf *pixbuf;
4491 cairo_t *cr;
4492 GdkPixbuf *icon;
4493 GtkTreeIter it;
4494 GdkRectangle area, icon_rect;
4495 #if !GTK_CHECK_VERSION(3, 0, 0)
4496 guchar *dest_data, *src_data;
4497 int dest_stride, src_stride, _x, _y;
4498 #endif
4499
4500 if (!desktop->model)
4501 return NULL;
4502 model = GTK_TREE_MODEL(desktop->model);
4503 if (!gtk_tree_model_get_iter_first(model, &it))
4504 return NULL;
4505
4506 /* determine the size of complete pixbuf */
4507 area.width = 0; /* mark it */
4508 do
4509 {
4510 item = fm_folder_model_get_item_userdata(desktop->model, &it);
4511 if (!item->is_selected)
4512 continue;
4513 if (area.width == 0)
4514 area = item->icon_rect;
4515 else
4516 {
4517 if (item->icon_rect.x < area.x)
4518 {
4519 area.width += (area.x - item->icon_rect.x);
4520 area.x = item->icon_rect.x;
4521 }
4522 if (item->icon_rect.x + item->icon_rect.width > area.x + area.width)
4523 area.width = item->icon_rect.x + item->icon_rect.width - area.x;
4524 if (item->icon_rect.y < area.y)
4525 {
4526 area.height += (area.y - item->icon_rect.y);
4527 area.y = item->icon_rect.y;
4528 }
4529 if (item->icon_rect.y + item->icon_rect.height > area.y + area.height)
4530 area.height = item->icon_rect.y + item->icon_rect.height - area.y;
4531 }
4532 }
4533 while(gtk_tree_model_iter_next(model, &it));
4534
4535 /* now create the pixbuf */
4536 if (area.width == 0) /* no selection??? */
4537 return NULL;
4538 area.width += 2;
4539 area.height += 2;
4540 s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, area.width, area.height);
4541 cr = cairo_create(s);
4542
4543 gtk_tree_model_get_iter_first(model, &it);
4544 do
4545 {
4546 item = fm_folder_model_get_item_userdata(desktop->model, &it);
4547 if (!item->is_selected)
4548 continue;
4549 /* FIXME: should we render name too, or is it too heavy? */
4550 icon = NULL;
4551 gtk_tree_model_get(model, &it, FM_FOLDER_MODEL_COL_ICON, &icon, -1);
4552 /* draw the icon */
4553 if (icon)
4554 {
4555 icon_rect.x = item->icon_rect.x - area.x + 1;
4556 icon_rect.width = item->icon_rect.width;
4557 icon_rect.y = item->icon_rect.y - area.y + 1;
4558 icon_rect.height = item->icon_rect.height;
4559 //g_debug("icon at %d %d %d %d",icon_rect.x,icon_rect.y,icon_rect.width,icon_rect.height);
4560 gdk_cairo_set_source_pixbuf(cr, icon, icon_rect.x, icon_rect.y);
4561 gdk_cairo_rectangle(cr, &icon_rect);
4562 cairo_fill(cr);
4563 g_object_unref(icon);
4564 }
4565 }
4566 while(gtk_tree_model_iter_next(model, &it));
4567
4568 cairo_destroy (cr);
4569 #if GTK_CHECK_VERSION(3, 0, 0)
4570 pixbuf = gdk_pixbuf_get_from_surface(s, 0, 0, area.width, area.height);
4571 #else
4572 /* GTK2 has no API gdk_pixbuf_get_from_surface() but we cannot
4573 preserve transparency using gdk_pixbuf_get_from_drawable() so
4574 therefore have to implement that API behavior here instead */
4575 pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, area.width, area.height);
4576 cairo_surface_flush(s);
4577 dest_data = gdk_pixbuf_get_pixels(pixbuf);
4578 dest_stride = gdk_pixbuf_get_rowstride(pixbuf);
4579 src_data = cairo_image_surface_get_data(s);
4580 src_stride = cairo_image_surface_get_stride(s);
4581
4582 /* convert alpha from cairo_surface_t into GdkPixbuf format */
4583 for (_y = 0; _y < area.height; _y++)
4584 {
4585 guint32 *src = (guint32 *) src_data;
4586
4587 for (_x = 0; _x < area.width; _x++)
4588 {
4589 guint alpha = src[_x] >> 24;
4590
4591 if (alpha == 0)
4592 {
4593 dest_data[_x * 4 + 0] = 0;
4594 dest_data[_x * 4 + 1] = 0;
4595 dest_data[_x * 4 + 2] = 0;
4596 }
4597 else
4598 {
4599 dest_data[_x * 4 + 0] = (((src[_x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
4600 dest_data[_x * 4 + 1] = (((src[_x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
4601 dest_data[_x * 4 + 2] = (((src[_x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
4602 }
4603 dest_data[_x * 4 + 3] = alpha;
4604 }
4605 src_data += src_stride;
4606 dest_data += dest_stride;
4607 }
4608 #endif
4609 cairo_surface_destroy(s);
4610 *x = area.x;
4611 *y = area.y;
4612 return pixbuf;
4613 }
4614
on_drag_begin(GtkWidget * widget,GdkDragContext * drag_context)4615 static void on_drag_begin (GtkWidget *widget, GdkDragContext *drag_context)
4616 {
4617 /* GTK auto-dragging started a drag, update state */
4618 FmDesktop *desktop = FM_DESKTOP(widget);
4619 gint x, y, icon_x, icon_y;
4620 GdkPixbuf *pix = _create_drag_icon(desktop, &x, &y);
4621
4622 desktop->dragging = TRUE;
4623
4624 /* set drag cursor to selected items */
4625 if (pix)
4626 {
4627 icon_x = desktop->drag_start_x - x;
4628 icon_y = desktop->drag_start_y - y;
4629 gtk_drag_set_icon_pixbuf(drag_context, pix, icon_x, icon_y);
4630 g_object_unref(pix);
4631 }
4632
4633 queue_layout_items(desktop);
4634 }
4635
on_dnd_src_data_get(FmDndSrc * ds,FmDesktop * desktop)4636 static void on_dnd_src_data_get(FmDndSrc* ds, FmDesktop* desktop)
4637 {
4638 FmFileInfoList* files = _dup_selected_files(FM_FOLDER_VIEW(desktop));
4639 if(files)
4640 {
4641 fm_dnd_src_set_files(ds, files);
4642 fm_file_info_list_unref(files);
4643 }
4644 }
4645
4646 /* ---------------------------------------------------------------------
4647 FmFolder signal handlers */
4648
on_folder_start_loading(FmFolder * folder,gpointer user_data)4649 static void on_folder_start_loading(FmFolder* folder, gpointer user_data)
4650 {
4651 /* FIXME: should we delete the model here? */
4652 }
4653
on_folder_finish_loading(FmFolder * folder,gpointer user_data)4654 static void on_folder_finish_loading(FmFolder* folder, gpointer user_data)
4655 {
4656 FmDesktop* desktop = user_data;
4657
4658 /* the desktop folder is just loaded, apply desktop items and positions */
4659 if(desktop->monitor < 0)
4660 return;
4661 fm_folder_view_add_popup(FM_FOLDER_VIEW(desktop), GTK_WINDOW(desktop),
4662 fm_desktop_update_popup);
4663 reload_items(desktop);
4664 }
4665
on_folder_error(FmFolder * folder,GError * err,FmJobErrorSeverity severity,gpointer user_data)4666 static FmJobErrorAction on_folder_error(FmFolder* folder, GError* err, FmJobErrorSeverity severity, gpointer user_data)
4667 {
4668 if(err->domain == G_IO_ERROR)
4669 {
4670 if(err->code == G_IO_ERROR_NOT_MOUNTED && severity < FM_JOB_ERROR_CRITICAL)
4671 {
4672 FmPath* path = fm_folder_get_path(folder);
4673 if(fm_mount_path(NULL, path, TRUE))
4674 return FM_JOB_RETRY;
4675 }
4676 }
4677 fm_show_error(NULL, NULL, err->message);
4678 return FM_JOB_CONTINUE;
4679 }
4680
4681
4682 #if !GTK_CHECK_VERSION(3, 0, 0)
4683 /* ---------------------------------------------------------------------
4684 We should not follow background changes on the style so we create
4685 a style class for the desktop with dummy set background operator.
4686 For GTK+ 3.0 we add a style provider with high priority instead. */
4687
_dummy_set_background(GtkStyle * style,GdkWindow * window,GtkStateType state_type)4688 static void _dummy_set_background(GtkStyle *style, GdkWindow *window, GtkStateType state_type)
4689 {
4690 }
4691
4692 #define FM_DESKTOP_TYPE_STYLE (fm_desktop_style_get_type())
4693
4694 typedef struct _FmDesktopStyle FmDesktopStyle;
4695 struct _FmDesktopStyle
4696 {
4697 GtkStyle parent;
4698 };
4699
4700 typedef struct _FmDesktopStyleClass FmDesktopStyleClass;
4701 struct _FmDesktopStyleClass
4702 {
4703 GtkStyleClass parent_class;
4704 };
4705
G_DEFINE_TYPE(FmDesktopStyle,fm_desktop_style,GTK_TYPE_STYLE)4706 G_DEFINE_TYPE(FmDesktopStyle, fm_desktop_style, GTK_TYPE_STYLE)
4707
4708 static void fm_desktop_style_class_init(FmDesktopStyleClass *klass)
4709 {
4710 GtkStyleClass *style_class = GTK_STYLE_CLASS(klass);
4711
4712 style_class->set_background = _dummy_set_background;
4713 }
4714
fm_desktop_style_init(FmDesktopStyle * self)4715 static void fm_desktop_style_init(FmDesktopStyle *self)
4716 {
4717 }
4718 #endif
4719
4720 /* ---------------------------------------------------------------------
4721 FmDesktop class main handlers */
4722
4723 #if 0
4724 static void on_desktop_model_destroy(gpointer data, GObject* model)
4725 {
4726 g_signal_handlers_disconnect_by_func(app_config, on_big_icon_size_changed, model);
4727 *(gpointer*)data = NULL;
4728 }
4729 #endif
4730
4731 #if FM_CHECK_VERSION(1, 0, 2)
on_sort_changed(GtkTreeSortable * model,FmDesktop * desktop)4732 static void on_sort_changed(GtkTreeSortable *model, FmDesktop *desktop)
4733 {
4734 FmFolderModelCol by;
4735 FmSortMode type;
4736
4737 if (!fm_folder_model_get_sort(FM_FOLDER_MODEL(model), &by, &type))
4738 /* FIXME: print error if failed */
4739 return;
4740 if (type == desktop->conf.desktop_sort_type &&
4741 by == desktop->conf.desktop_sort_by) /* not changed */
4742 return;
4743 desktop->conf.desktop_sort_type = type;
4744 desktop->conf.desktop_sort_by = by;
4745 queue_config_save(desktop);
4746 }
4747 #endif
4748
connect_model(FmDesktop * desktop,FmFolder * folder)4749 static inline void connect_model(FmDesktop *desktop, FmFolder *folder)
4750 {
4751 desktop->model = fm_folder_model_new(folder, FALSE);
4752 g_signal_connect(folder, "start-loading", G_CALLBACK(on_folder_start_loading), desktop);
4753 g_signal_connect(folder, "finish-loading", G_CALLBACK(on_folder_finish_loading), desktop);
4754 g_signal_connect(folder, "error", G_CALLBACK(on_folder_error), desktop);
4755 fm_folder_model_set_icon_size(desktop->model, fm_config->big_icon_size);
4756 g_signal_connect(app_config, "changed::big_icon_size",
4757 G_CALLBACK(on_big_icon_size_changed), desktop->model);
4758 g_signal_connect(desktop->model, "row-deleting", G_CALLBACK(on_row_deleting), desktop);
4759 g_signal_connect(desktop->model, "row-inserted", G_CALLBACK(on_row_inserted), desktop);
4760 g_signal_connect(desktop->model, "row-deleted", G_CALLBACK(on_row_deleted), desktop);
4761 g_signal_connect(desktop->model, "row-changed", G_CALLBACK(on_row_changed), desktop);
4762 g_signal_connect(desktop->model, "rows-reordered", G_CALLBACK(on_rows_reordered), desktop);
4763 #if FM_CHECK_VERSION(1, 0, 2)
4764 fm_folder_model_set_sort(desktop->model, desktop->conf.desktop_sort_by,
4765 desktop->conf.desktop_sort_type);
4766 g_signal_connect(desktop->model, "sort-column-changed", G_CALLBACK(on_sort_changed), desktop);
4767 #else
4768 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(desktop->model),
4769 desktop->conf.desktop_sort_by,
4770 desktop->conf.desktop_sort_type);
4771 #endif
4772 on_folder_start_loading(folder, desktop);
4773 if(fm_folder_is_loaded(folder))
4774 on_folder_finish_loading(folder, desktop);
4775 }
4776
disconnect_model(FmDesktop * desktop)4777 static inline void disconnect_model(FmDesktop* desktop)
4778 {
4779 FmFolder *folder;
4780
4781 if (desktop->model == NULL)
4782 return;
4783 folder = fm_folder_model_get_folder(desktop->model);
4784 g_signal_handlers_disconnect_by_func(folder, on_folder_start_loading, desktop);
4785 g_signal_handlers_disconnect_by_func(folder, on_folder_finish_loading, desktop);
4786 g_signal_handlers_disconnect_by_func(folder, on_folder_error, desktop);
4787 g_signal_handlers_disconnect_by_func(app_config, on_big_icon_size_changed, desktop->model);
4788 g_signal_handlers_disconnect_by_func(desktop->model, on_row_deleting, desktop);
4789 g_signal_handlers_disconnect_by_func(desktop->model, on_row_inserted, desktop);
4790 g_signal_handlers_disconnect_by_func(desktop->model, on_row_deleted, desktop);
4791 g_signal_handlers_disconnect_by_func(desktop->model, on_row_changed, desktop);
4792 g_signal_handlers_disconnect_by_func(desktop->model, on_rows_reordered, desktop);
4793 #if FM_CHECK_VERSION(1, 0, 2)
4794 g_signal_handlers_disconnect_by_func(desktop->model, on_sort_changed, desktop);
4795 #endif
4796 g_object_unref(desktop->model);
4797 desktop->model = NULL;
4798 fm_desktop_accessible_model_removed(desktop);
4799 /* update popup now */
4800 fm_folder_view_add_popup(FM_FOLDER_VIEW(desktop), GTK_WINDOW(desktop),
4801 fm_desktop_update_popup);
4802 }
4803
4804 #if FM_CHECK_VERSION(1, 2, 0)
on_show_full_names_changed(FmConfig * cfg,FmDesktop * self)4805 static void on_show_full_names_changed(FmConfig *cfg, FmDesktop *self)
4806 {
4807 if (fm_config->show_full_names)
4808 {
4809 self->pango_text_h = -1;
4810 pango_layout_set_ellipsize(self->pl, PANGO_ELLIPSIZE_NONE);
4811 }
4812 else
4813 {
4814 self->pango_text_h = self->text_h * PANGO_SCALE;
4815 pango_layout_set_ellipsize(self->pl, PANGO_ELLIPSIZE_END);
4816 }
4817 queue_layout_items(self);
4818 }
4819 #endif
4820
4821 #if GTK_CHECK_VERSION(3, 0, 0)
fm_desktop_destroy(GtkWidget * object)4822 static void fm_desktop_destroy(GtkWidget *object)
4823 #else
4824 static void fm_desktop_destroy(GtkObject *object)
4825 #endif
4826 {
4827 FmDesktop *self;
4828 GdkScreen* screen;
4829
4830 self = FM_DESKTOP(object);
4831 if(self->icon_render) /* see bug #3533958 by korzhpavel@SF */
4832 {
4833 screen = gtk_widget_get_screen((GtkWidget*)self);
4834 gdk_window_remove_filter(gdk_screen_get_root_window(screen), on_root_event, self);
4835
4836 g_signal_handlers_disconnect_by_func(screen, on_screen_size_changed, self);
4837 #if FM_CHECK_VERSION(1, 2, 0)
4838 g_signal_handlers_disconnect_by_func(app_config, on_show_full_names_changed, self);
4839 #endif
4840
4841 gtk_window_group_remove_window(win_group, (GtkWindow*)self);
4842
4843 if (self->model)
4844 disconnect_model(self);
4845
4846 unload_items(self);
4847
4848 g_object_unref(self->icon_render);
4849 self->icon_render = NULL;
4850 g_object_unref(self->pl);
4851
4852 if(self->single_click_timeout_handler)
4853 g_source_remove(self->single_click_timeout_handler);
4854
4855 if(self->idle_layout)
4856 g_source_remove(self->idle_layout);
4857
4858 g_signal_handlers_disconnect_by_func(self->dnd_src, on_dnd_src_data_get, self);
4859 g_object_unref(self->dnd_src);
4860 g_object_unref(self->dnd_dest);
4861 }
4862
4863 if (self->conf.configured)
4864 {
4865 self->conf.configured = FALSE;
4866 if (self->conf.changed) /* if config was changed then save it now */
4867 save_item_pos(self);
4868 g_free(self->conf.wallpaper);
4869 if (self->conf.wallpapers_configured > 0)
4870 {
4871 int i;
4872 for (i = 0; i < self->conf.wallpapers_configured; i++)
4873 g_free(self->conf.wallpapers[i]);
4874 g_free(self->conf.wallpapers);
4875 }
4876 g_free(self->conf.desktop_font);
4877 g_free(self->conf.folder);
4878 }
4879
4880 _clear_bg_cache(self);
4881
4882 /* cancel any pending search timeout */
4883 if (G_UNLIKELY(self->search_timeout_id))
4884 {
4885 g_source_remove(self->search_timeout_id);
4886 self->search_timeout_id = 0;
4887 }
4888
4889 /* destroy the interactive search dialog */
4890 if (G_UNLIKELY(self->search_window))
4891 {
4892 g_signal_handlers_disconnect_by_func(self->search_window, desktop_search_delete_event, self);
4893 g_signal_handlers_disconnect_by_func(self->search_window, desktop_search_scroll_event, self);
4894 g_signal_handlers_disconnect_by_func(self->search_window, desktop_search_key_press_event, self);
4895 gtk_widget_destroy(self->search_window);
4896 self->search_entry = NULL;
4897 self->search_window = NULL;
4898 }
4899
4900 #if GTK_CHECK_VERSION(3, 0, 0)
4901 GTK_WIDGET_CLASS(fm_desktop_parent_class)->destroy(object);
4902 #else
4903 GTK_OBJECT_CLASS(fm_desktop_parent_class)->destroy(object);
4904 #endif
4905 }
4906
fm_desktop_init(FmDesktop * self)4907 static void fm_desktop_init(FmDesktop *self)
4908 {
4909 #if GTK_CHECK_VERSION(3, 0, 0)
4910 self->css = gtk_css_provider_new();
4911 gtk_style_context_add_provider(gtk_widget_get_style_context((GtkWidget*)self),
4912 GTK_STYLE_PROVIDER(self->css),
4913 GTK_STYLE_PROVIDER_PRIORITY_USER);
4914 #else
4915 GtkStyle *style = g_object_new(FM_DESKTOP_TYPE_STYLE, NULL);
4916 gtk_widget_set_style((GtkWidget*)self, style);
4917 g_object_unref(style);
4918 #endif
4919 }
4920
4921 /* we should have a constructor to handle parameters */
fm_desktop_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)4922 static GObject* fm_desktop_constructor(GType type, guint n_construct_properties,
4923 GObjectConstructParam *construct_properties)
4924 {
4925 GObject* object = G_OBJECT_CLASS(fm_desktop_parent_class)->constructor(type, n_construct_properties, construct_properties);
4926 FmDesktop* self = (FmDesktop*)object;
4927 GdkScreen* screen = gtk_widget_get_screen((GtkWidget*)self);
4928 GdkWindow* root;
4929 guint i;
4930 gint n;
4931 GdkRectangle geom;
4932
4933 for(i = 0; i < n_construct_properties; i++)
4934 if(!strcmp(construct_properties[i].pspec->name, "monitor")
4935 && G_VALUE_HOLDS_INT(construct_properties[i].value))
4936 self->monitor = g_value_get_int(construct_properties[i].value);
4937 if(self->monitor < 0)
4938 return object; /* this monitor is disabled */
4939 g_debug("fm_desktop_constructor for monitor %d", self->monitor);
4940 gdk_screen_get_monitor_geometry(screen, self->monitor, &geom);
4941 gtk_window_set_default_size((GtkWindow*)self, geom.width, geom.height);
4942 gtk_window_move(GTK_WINDOW(self), geom.x, geom.y);
4943 gtk_widget_set_app_paintable((GtkWidget*)self, TRUE);
4944 gtk_window_set_type_hint(GTK_WINDOW(self), GDK_WINDOW_TYPE_HINT_DESKTOP);
4945 gtk_widget_add_events((GtkWidget*)self,
4946 GDK_POINTER_MOTION_MASK |
4947 GDK_BUTTON_PRESS_MASK |
4948 GDK_BUTTON_RELEASE_MASK |
4949 GDK_KEY_PRESS_MASK|
4950 GDK_PROPERTY_CHANGE_MASK);
4951
4952 self->icon_render = fm_cell_renderer_pixbuf_new();
4953 g_object_set(self->icon_render, "follow-state", TRUE, NULL);
4954 g_object_ref_sink(self->icon_render);
4955 fm_cell_renderer_pixbuf_set_fixed_size(FM_CELL_RENDERER_PIXBUF(self->icon_render), fm_config->big_icon_size, fm_config->big_icon_size);
4956
4957 /* FIXME: call pango_layout_context_changed() on the layout in response to the
4958 * "style-set" and "direction-changed" signals for the widget. */
4959 //pc = gtk_widget_get_pango_context((GtkWidget*)self);
4960 self->pl = gtk_widget_create_pango_layout((GtkWidget*)self, NULL);
4961 pango_layout_set_alignment(self->pl, PANGO_ALIGN_CENTER);
4962 #if FM_CHECK_VERSION(1, 2, 0)
4963 if (fm_config->show_full_names)
4964 pango_layout_set_ellipsize(self->pl, PANGO_ELLIPSIZE_NONE);
4965 else
4966 #endif
4967 pango_layout_set_ellipsize(self->pl, PANGO_ELLIPSIZE_END);
4968 pango_layout_set_wrap(self->pl, PANGO_WRAP_WORD_CHAR);
4969 #if FM_CHECK_VERSION(1, 2, 0)
4970 g_signal_connect(app_config, "changed::show_full_names",
4971 G_CALLBACK(on_show_full_names_changed), self);
4972 #endif
4973
4974 root = gdk_screen_get_root_window(screen);
4975 gdk_window_set_events(root, gdk_window_get_events(root)|GDK_PROPERTY_CHANGE_MASK);
4976 gdk_window_add_filter(root, on_root_event, self);
4977 g_signal_connect(screen, "monitors-changed", G_CALLBACK(on_screen_size_changed), self);
4978
4979 n = get_desktop_for_root_window(root);
4980 if(n < 0)
4981 n = 0;
4982 self->cur_desktop = (guint)n;
4983
4984 /* init dnd support */
4985 self->dnd_src = fm_dnd_src_new((GtkWidget*)self);
4986 #if !FM_CHECK_VERSION(1, 2, 1)
4987 /* override pre-1.2.1 handler from FmDndSrc to allow icon change */
4988 g_signal_connect_after(self, "drag-begin", G_CALLBACK(on_drag_begin), NULL);
4989 #endif
4990 /* add our own targets */
4991 fm_dnd_src_add_targets((GtkWidget*)self, dnd_targets, G_N_ELEMENTS(dnd_targets));
4992 g_signal_connect(self->dnd_src, "data-get", G_CALLBACK(on_dnd_src_data_get), self);
4993
4994 self->dnd_dest = fm_dnd_dest_new_with_handlers((GtkWidget*)self);
4995 fm_dnd_dest_add_targets((GtkWidget*)self, dnd_targets, G_N_ELEMENTS(dnd_targets));
4996
4997 gtk_window_group_add_window(win_group, GTK_WINDOW(self));
4998
4999 return object;
5000 }
5001
fm_desktop_new(GdkScreen * screen,gint monitor)5002 FmDesktop *fm_desktop_new(GdkScreen* screen, gint monitor)
5003 {
5004 return g_object_new(FM_TYPE_DESKTOP, "screen", screen, "monitor", monitor, NULL);
5005 }
5006
fm_desktop_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)5007 static void fm_desktop_set_property(GObject *object, guint property_id,
5008 const GValue *value, GParamSpec *pspec)
5009 {
5010 switch(property_id)
5011 {
5012 case PROP_MONITOR:
5013 FM_DESKTOP(object)->monitor = g_value_get_int(value);
5014 break;
5015 default:
5016 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
5017 }
5018 }
5019
fm_desktop_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)5020 static void fm_desktop_get_property(GObject *object, guint property_id,
5021 GValue *value, GParamSpec *pspec)
5022 {
5023 switch(property_id)
5024 {
5025 case PROP_MONITOR:
5026 g_value_set_int(value, FM_DESKTOP(object)->monitor);
5027 break;
5028 default:
5029 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
5030 }
5031 }
5032
5033 /* init for FmDesktop class */
fm_desktop_class_init(FmDesktopClass * klass)5034 static void fm_desktop_class_init(FmDesktopClass *klass)
5035 {
5036 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
5037 typedef gboolean (*DeleteEvtHandler) (GtkWidget*, GdkEventAny*);
5038 char* atom_names[] = {"_NET_WORKAREA", "_NET_NUMBER_OF_DESKTOPS",
5039 "_NET_CURRENT_DESKTOP", "_XROOTMAP_ID", "_XROOTPMAP_ID"};
5040 Atom atoms[G_N_ELEMENTS(atom_names)] = {0};
5041 GObjectClass* object_class = G_OBJECT_CLASS(klass);
5042
5043 #if GTK_CHECK_VERSION(3, 0, 0)
5044 widget_class->destroy = fm_desktop_destroy;
5045 widget_class->draw = on_draw;
5046 widget_class->get_preferred_width = on_get_preferred_width;
5047 widget_class->get_preferred_height = on_get_preferred_height;
5048 #else
5049 GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS(klass);
5050 gtk_object_class->destroy = fm_desktop_destroy;
5051
5052 widget_class->expose_event = on_expose;
5053 widget_class->size_request = on_size_request;
5054 #endif
5055 widget_class->size_allocate = on_size_allocate;
5056 widget_class->button_press_event = on_button_press;
5057 widget_class->button_release_event = on_button_release;
5058 widget_class->motion_notify_event = on_motion_notify;
5059 widget_class->leave_notify_event = on_leave_notify;
5060 widget_class->key_press_event = on_key_press;
5061 /* widget_class->style_set = on_style_set; */
5062 widget_class->direction_changed = on_direction_changed;
5063 widget_class->realize = on_realize;
5064 widget_class->focus_in_event = on_focus_in;
5065 widget_class->focus_out_event = on_focus_out;
5066 /* widget_class->scroll_event = on_scroll; */
5067 widget_class->delete_event = (DeleteEvtHandler)gtk_true;
5068 widget_class->get_accessible = fm_desktop_get_accessible;
5069
5070 widget_class->drag_motion = on_drag_motion;
5071 widget_class->drag_drop = on_drag_drop;
5072 widget_class->drag_data_received = on_drag_data_received;
5073 widget_class->drag_leave = on_drag_leave;
5074 #if FM_CHECK_VERSION(1, 2, 1)
5075 widget_class->drag_begin = on_drag_begin;
5076 #endif
5077 /* widget_class->drag_data_get = on_drag_data_get; */
5078
5079 if(XInternAtoms(gdk_x11_get_default_xdisplay(), atom_names,
5080 G_N_ELEMENTS(atom_names), False, atoms))
5081 {
5082 XA_NET_WORKAREA = atoms[0];
5083 XA_NET_NUMBER_OF_DESKTOPS = atoms[1];
5084 XA_NET_CURRENT_DESKTOP = atoms[2];
5085 XA_XROOTMAP_ID = atoms[3];
5086 XA_XROOTPMAP_ID = atoms[4];
5087 }
5088
5089 object_class->constructor = fm_desktop_constructor;
5090 object_class->set_property = fm_desktop_set_property;
5091 object_class->get_property = fm_desktop_get_property;
5092
5093 g_object_class_install_property(object_class, PROP_MONITOR,
5094 g_param_spec_int("monitor", "Monitor",
5095 "Monitor number where desktop is",
5096 -2, 127, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
5097
5098 desktop_atom = gdk_atom_intern_static_string(dnd_targets[0].target);
5099 }
5100
5101
5102 //static void on_clicked(FmFolderView* fv, FmFolderViewClickType type, FmFileInfo* file)
5103
5104 //static void on_sel_changed(FmFolderView* fv, FmFileInfoList* sels)
5105
5106 //static void on_sort_changed(FmFolderView* fv)
5107
5108 /* ---------------------------------------------------------------------
5109 FmFolderView interface implementation */
5110
_set_sel_mode(FmFolderView * fv,GtkSelectionMode mode)5111 static void _set_sel_mode(FmFolderView* fv, GtkSelectionMode mode)
5112 {
5113 /* not implemented */
5114 }
5115
_get_sel_mode(FmFolderView * fv)5116 static GtkSelectionMode _get_sel_mode(FmFolderView* fv)
5117 {
5118 return GTK_SELECTION_MULTIPLE;
5119 }
5120
5121 #if !FM_CHECK_VERSION(1, 0, 2)
_set_sort(FmFolderView * fv,GtkSortType type,FmFolderModelViewCol by)5122 static void _set_sort(FmFolderView* fv, GtkSortType type, FmFolderModelViewCol by)
5123 {
5124 FmDesktop* desktop = FM_DESKTOP(fv);
5125
5126 if(type == (GtkSortType)desktop->conf.desktop_sort_type &&
5127 by == (FmFolderModelViewCol)desktop->conf.desktop_sort_by)
5128 return;
5129 desktop->conf.desktop_sort_type = type;
5130 desktop->conf.desktop_sort_by = by;
5131 pcmanfm_save_config(FALSE);
5132 if (desktop->model)
5133 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(desktop->model),
5134 by, type);
5135 }
5136
_get_sort(FmFolderView * fv,GtkSortType * type,FmFolderModelViewCol * by)5137 static void _get_sort(FmFolderView* fv, GtkSortType* type, FmFolderModelViewCol* by)
5138 {
5139 FmDesktop* desktop = FM_DESKTOP(fv);
5140
5141 if(type)
5142 *type = desktop->conf.desktop_sort_type;
5143 if(by)
5144 *by = desktop->conf.desktop_sort_by;
5145 }
5146 #endif
5147
_set_show_hidden(FmFolderView * fv,gboolean show)5148 static void _set_show_hidden(FmFolderView* fv, gboolean show)
5149 {
5150 /* not implemented */
5151 }
5152
_get_show_hidden(FmFolderView * fv)5153 static gboolean _get_show_hidden(FmFolderView* fv)
5154 {
5155 return FALSE;
5156 }
5157
_get_folder(FmFolderView * fv)5158 static FmFolder* _get_folder(FmFolderView* fv)
5159 {
5160 FmDesktop* desktop = FM_DESKTOP(fv);
5161
5162 return desktop->model ? fm_folder_model_get_folder(desktop->model) : NULL;
5163 }
5164
_set_model(FmFolderView * fv,FmFolderModel * model)5165 static void _set_model(FmFolderView* fv, FmFolderModel* model)
5166 {
5167 /* not implemented */
5168 }
5169
_get_model(FmFolderView * fv)5170 static FmFolderModel* _get_model(FmFolderView* fv)
5171 {
5172 return FM_DESKTOP(fv)->model;
5173 }
5174
_count_selected_files(FmFolderView * fv)5175 static gint _count_selected_files(FmFolderView* fv)
5176 {
5177 FmDesktop* desktop = FM_DESKTOP(fv);
5178 GtkTreeModel* model;
5179 GtkTreeIter it;
5180 gint n = 0;
5181
5182 if (!desktop->model)
5183 return 0;
5184 model = GTK_TREE_MODEL(desktop->model);
5185 if(!gtk_tree_model_get_iter_first(model, &it))
5186 return 0;
5187 do
5188 {
5189 FmDesktopItem* item = fm_folder_model_get_item_userdata(desktop->model, &it);
5190 if(item->is_selected)
5191 n++;
5192 }
5193 while(gtk_tree_model_iter_next(model, &it));
5194 return n;
5195 }
5196
_dup_selected_files(FmFolderView * fv)5197 static FmFileInfoList* _dup_selected_files(FmFolderView* fv)
5198 {
5199 FmDesktop* desktop = FM_DESKTOP(fv);
5200 FmFileInfoList* files = NULL;
5201 GtkTreeModel* model;
5202 GtkTreeIter it;
5203
5204 if (!desktop->model)
5205 return NULL;
5206 model = GTK_TREE_MODEL(desktop->model);
5207 if(!gtk_tree_model_get_iter_first(model, &it))
5208 return NULL;
5209 do
5210 {
5211 FmDesktopItem* item = fm_folder_model_get_item_userdata(desktop->model, &it);
5212 if(item->is_selected)
5213 {
5214 if(!files)
5215 files = fm_file_info_list_new();
5216 fm_file_info_list_push_tail(files, item->fi);
5217 }
5218 }
5219 while(gtk_tree_model_iter_next(model, &it));
5220 return files;
5221 }
5222
_dup_selected_file_paths(FmFolderView * fv)5223 static FmPathList* _dup_selected_file_paths(FmFolderView* fv)
5224 {
5225 FmDesktop* desktop = FM_DESKTOP(fv);
5226 FmPathList* files = NULL;
5227 GtkTreeModel* model;
5228 GtkTreeIter it;
5229
5230 if (!desktop->model)
5231 return NULL;
5232 model = GTK_TREE_MODEL(desktop->model);
5233 if(!gtk_tree_model_get_iter_first(model, &it))
5234 return NULL;
5235 do
5236 {
5237 FmDesktopItem* item = fm_folder_model_get_item_userdata(desktop->model, &it);
5238 if(item->is_selected)
5239 {
5240 if(!files)
5241 files = fm_path_list_new();
5242 fm_path_list_push_tail(files, fm_file_info_get_path(item->fi));
5243 }
5244 }
5245 while(gtk_tree_model_iter_next(model, &it));
5246 return files;
5247 }
5248
_select_all(FmFolderView * fv)5249 static void _select_all(FmFolderView* fv)
5250 {
5251 FmDesktop* desktop = FM_DESKTOP(fv);
5252 GtkTreeIter it;
5253 GtkTreeModel* model;
5254
5255 if (!desktop->model)
5256 return;
5257 model = GTK_TREE_MODEL(desktop->model);
5258 if(!gtk_tree_model_get_iter_first(model, &it))
5259 return;
5260 do
5261 {
5262 FmDesktopItem* item = fm_folder_model_get_item_userdata(desktop->model, &it);
5263 if(!item->is_selected)
5264 {
5265 item->is_selected = TRUE;
5266 redraw_item(desktop, item);
5267 fm_desktop_item_selected_changed(desktop, item);
5268 }
5269 }
5270 while(gtk_tree_model_iter_next(model, &it));
5271 }
5272
_unselect_all(FmFolderView * fv)5273 static void _unselect_all(FmFolderView* fv)
5274 {
5275 FmDesktop* desktop = FM_DESKTOP(fv);
5276 GtkTreeIter it;
5277 GtkTreeModel* model;
5278
5279 if (!desktop->model)
5280 return;
5281 model = GTK_TREE_MODEL(desktop->model);
5282 if(!gtk_tree_model_get_iter_first(model, &it))
5283 return;
5284 do
5285 {
5286 FmDesktopItem* item = fm_folder_model_get_item_userdata(desktop->model, &it);
5287 if(item->is_selected)
5288 {
5289 item->is_selected = FALSE;
5290 redraw_item(desktop, item);
5291 fm_desktop_item_selected_changed(desktop, item);
5292 }
5293 }
5294 while(gtk_tree_model_iter_next(model, &it));
5295 }
5296
_select_invert(FmFolderView * fv)5297 static void _select_invert(FmFolderView* fv)
5298 {
5299 /* not implemented */
5300 }
5301
_select_file_path(FmFolderView * fv,FmPath * path)5302 static void _select_file_path(FmFolderView* fv, FmPath* path)
5303 {
5304 /* not implemented */
5305 }
5306
_get_custom_menu_callbacks(FmFolderView * fv,FmFolderViewUpdatePopup * popup,FmLaunchFolderFunc * launch)5307 static void _get_custom_menu_callbacks(FmFolderView* fv,
5308 FmFolderViewUpdatePopup* popup,
5309 FmLaunchFolderFunc* launch)
5310 {
5311 if(popup)
5312 *popup = fm_desktop_update_item_popup;
5313 if(launch)
5314 *launch = pcmanfm_open_folder;
5315 }
5316
5317 /* init for FmFolderView interface implementation */
fm_desktop_view_init(FmFolderViewInterface * iface)5318 static void fm_desktop_view_init(FmFolderViewInterface* iface)
5319 {
5320 iface->set_sel_mode = _set_sel_mode;
5321 iface->get_sel_mode = _get_sel_mode;
5322 #if !FM_CHECK_VERSION(1, 0, 2)
5323 iface->set_sort = _set_sort;
5324 iface->get_sort = _get_sort;
5325 #endif
5326 iface->set_show_hidden = _set_show_hidden;
5327 iface->get_show_hidden = _get_show_hidden;
5328 iface->get_folder = _get_folder;
5329 iface->set_model = _set_model;
5330 iface->get_model = _get_model;
5331 iface->count_selected_files = _count_selected_files;
5332 iface->dup_selected_files = _dup_selected_files;
5333 iface->dup_selected_file_paths = _dup_selected_file_paths;
5334 iface->select_all = _select_all;
5335 //iface->unselect_all = _unselect_all;
5336 iface->select_invert = _select_invert;
5337 iface->select_file_path = _select_file_path;
5338 iface->get_custom_menu_callbacks = _get_custom_menu_callbacks;
5339 }
5340
5341
5342 /* ---------------------------------------------------------------------
5343 Desktop preferences */
5344
5345 static GtkWindow* desktop_pref_dlg = NULL;
5346
on_response(GtkDialog * dlg,int res,GtkWindow ** pdlg)5347 static void on_response(GtkDialog* dlg, int res, GtkWindow** pdlg)
5348 {
5349 *pdlg = NULL;
5350 gtk_widget_destroy(GTK_WIDGET(dlg));
5351 }
5352
5353 /* preferences setup is tightly linked with desktop so should be here */
on_wallpaper_set(GtkFileChooserButton * btn,FmDesktop * desktop)5354 static void on_wallpaper_set(GtkFileChooserButton* btn, FmDesktop *desktop)
5355 {
5356 char* file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(btn));
5357 g_free(desktop->conf.wallpaper);
5358 desktop->conf.wallpaper = file;
5359 queue_config_save(desktop);
5360 update_background(desktop, 0);
5361 }
5362
on_update_img_preview(GtkFileChooser * chooser,GtkImage * img)5363 static void on_update_img_preview( GtkFileChooser *chooser, GtkImage* img )
5364 {
5365 char* file = gtk_file_chooser_get_preview_filename( chooser );
5366 GdkPixbuf* pix = NULL;
5367 if( file )
5368 {
5369 pix = gdk_pixbuf_new_from_file_at_scale( file, 128, 128, TRUE, NULL );
5370 g_free( file );
5371 }
5372 if( pix )
5373 {
5374 gtk_file_chooser_set_preview_widget_active(chooser, TRUE);
5375 gtk_image_set_from_pixbuf( img, pix );
5376 g_object_unref( pix );
5377 }
5378 else
5379 {
5380 gtk_image_clear( img );
5381 gtk_file_chooser_set_preview_widget_active(chooser, FALSE);
5382 }
5383 }
5384
on_wallpaper_mode_changed(GtkComboBox * combo,FmDesktop * desktop)5385 static void on_wallpaper_mode_changed(GtkComboBox* combo, FmDesktop *desktop)
5386 {
5387 int sel = gtk_combo_box_get_active(combo);
5388
5389 if(sel >= 0 && sel != (int)desktop->conf.wallpaper_mode)
5390 {
5391 desktop->conf.wallpaper_mode = sel;
5392 queue_config_save(desktop);
5393 update_background(desktop, 0);
5394 }
5395 }
5396
on_wallpaper_mode_changed2(GtkComboBox * combo,GtkWidget * wallpaper_box)5397 static void on_wallpaper_mode_changed2(GtkComboBox *combo, GtkWidget *wallpaper_box)
5398 {
5399 /* update the box */
5400 if (wallpaper_box)
5401 gtk_widget_set_sensitive(wallpaper_box,
5402 gtk_combo_box_get_active(combo) != FM_WP_COLOR);
5403 }
5404
on_bg_color_set(GtkColorButton * btn,FmDesktop * desktop)5405 static void on_bg_color_set(GtkColorButton *btn, FmDesktop *desktop)
5406 {
5407 GdkColor new_val;
5408
5409 gtk_color_button_get_color(btn, &new_val);
5410 if (!gdk_color_equal(&desktop->conf.desktop_bg, &new_val))
5411 {
5412 #if GTK_CHECK_VERSION(3, 0, 0)
5413 char *css_data = g_strdup_printf("FmDesktop {\n"
5414 "background-color: #%02x%02x%02x\n"
5415 "}", new_val.red/256, new_val.green/256,
5416 new_val.blue/256);
5417 gtk_css_provider_load_from_data(desktop->css, css_data, -1, NULL);
5418 g_free(css_data);
5419 #endif
5420 desktop->conf.desktop_bg = new_val;
5421 queue_config_save(desktop);
5422 update_background(desktop, 0);
5423 }
5424 }
5425
on_wallpaper_common_toggled(GtkToggleButton * btn,FmDesktop * desktop)5426 static void on_wallpaper_common_toggled(GtkToggleButton* btn, FmDesktop *desktop)
5427 {
5428 gboolean new_val = gtk_toggle_button_get_active(btn);
5429
5430 if(desktop->conf.wallpaper_common != new_val)
5431 {
5432 desktop->conf.wallpaper_common = new_val;
5433 queue_config_save(desktop);
5434 update_background(desktop, 0);
5435 }
5436 }
5437
on_fg_color_set(GtkColorButton * btn,FmDesktop * desktop)5438 static void on_fg_color_set(GtkColorButton *btn, FmDesktop *desktop)
5439 {
5440 GdkColor new_val;
5441
5442 gtk_color_button_get_color(btn, &new_val);
5443 if (!gdk_color_equal(&desktop->conf.desktop_fg, &new_val))
5444 {
5445 desktop->conf.desktop_fg = new_val;
5446 queue_config_save(desktop);
5447 gtk_widget_queue_draw(GTK_WIDGET(desktop));
5448 }
5449 }
5450
on_shadow_color_set(GtkColorButton * btn,FmDesktop * desktop)5451 static void on_shadow_color_set(GtkColorButton *btn, FmDesktop *desktop)
5452 {
5453 GdkColor new_val;
5454
5455 gtk_color_button_get_color(btn, &new_val);
5456 if (!gdk_color_equal(&desktop->conf.desktop_shadow, &new_val))
5457 {
5458 desktop->conf.desktop_shadow = new_val;
5459 queue_config_save(desktop);
5460 gtk_widget_queue_draw(GTK_WIDGET(desktop));
5461 }
5462 }
5463
on_wm_menu_toggled(GtkToggleButton * btn,FmDesktop * desktop)5464 static void on_wm_menu_toggled(GtkToggleButton* btn, FmDesktop *desktop)
5465 {
5466 gboolean new_val = gtk_toggle_button_get_active(btn);
5467
5468 if(desktop->conf.show_wm_menu != new_val)
5469 {
5470 desktop->conf.show_wm_menu = new_val;
5471 queue_config_save(desktop);
5472 }
5473 }
5474
on_desktop_font_set(GtkFontButton * btn,FmDesktop * desktop)5475 static void on_desktop_font_set(GtkFontButton* btn, FmDesktop *desktop)
5476 {
5477 const char* font = gtk_font_button_get_font_name(btn);
5478
5479 if(font)
5480 {
5481 PangoFontDescription *font_desc;
5482
5483 g_free(desktop->conf.desktop_font);
5484 desktop->conf.desktop_font = g_strdup(font);
5485 queue_config_save(desktop);
5486 if(desktop->monitor < 0)
5487 return;
5488 font_desc = pango_font_description_from_string(desktop->conf.desktop_font);
5489 if(font_desc)
5490 {
5491 PangoContext* pc = gtk_widget_get_pango_context((GtkWidget*)desktop);
5492
5493 pango_context_set_font_description(pc, font_desc);
5494 pango_layout_context_changed(desktop->pl);
5495 gtk_widget_queue_resize(GTK_WIDGET(desktop));
5496 pango_font_description_free(font_desc);
5497 }
5498 }
5499 }
5500
on_desktop_folder_new_win_toggled(GtkToggleButton * btn,FmDesktop * desktop)5501 static void on_desktop_folder_new_win_toggled(GtkToggleButton* btn, FmDesktop *desktop)
5502 {
5503 app_config->desktop_folder_new_win = gtk_toggle_button_get_active(btn);
5504 pcmanfm_save_config(FALSE);
5505 }
5506
5507 #if FM_CHECK_VERSION(1, 2, 0)
on_show_documents_toggled(GtkToggleButton * btn,FmDesktop * desktop)5508 static void on_show_documents_toggled(GtkToggleButton* btn, FmDesktop *desktop)
5509 {
5510 gboolean new_val = gtk_toggle_button_get_active(btn);
5511
5512 if(desktop->conf.show_documents != new_val)
5513 {
5514 desktop->conf.show_documents = new_val;
5515 queue_config_save(desktop);
5516 if (documents && documents->fi && desktop->model)
5517 {
5518 if (new_val)
5519 fm_folder_model_extra_file_add(desktop->model, documents->fi,
5520 FM_FOLDER_MODEL_ITEMPOS_PRE);
5521 else
5522 fm_folder_model_extra_file_remove(desktop->model, documents->fi);
5523 }
5524 }
5525 }
5526
on_show_trash_toggled(GtkToggleButton * btn,FmDesktop * desktop)5527 static void on_show_trash_toggled(GtkToggleButton* btn, FmDesktop *desktop)
5528 {
5529 gboolean new_val = gtk_toggle_button_get_active(btn);
5530
5531 if(desktop->conf.show_trash != new_val)
5532 {
5533 desktop->conf.show_trash = new_val;
5534 queue_config_save(desktop);
5535 if (trash_can && trash_can->fi && desktop->model)
5536 {
5537 if (new_val)
5538 fm_folder_model_extra_file_add(desktop->model, trash_can->fi,
5539 FM_FOLDER_MODEL_ITEMPOS_PRE);
5540 else
5541 fm_folder_model_extra_file_remove(desktop->model, trash_can->fi);
5542 }
5543 }
5544 }
5545
on_show_mounts_toggled(GtkToggleButton * btn,FmDesktop * desktop)5546 static void on_show_mounts_toggled(GtkToggleButton* btn, FmDesktop *desktop)
5547 {
5548 GSList *msl;
5549 gboolean new_val = gtk_toggle_button_get_active(btn);
5550
5551 if(desktop->conf.show_mounts != new_val)
5552 {
5553 desktop->conf.show_mounts = new_val;
5554 queue_config_save(desktop);
5555 if (desktop->model) for (msl = mounts; msl; msl = msl->next)
5556 {
5557 FmDesktopExtraItem *mount = msl->data;
5558 if (new_val)
5559 fm_folder_model_extra_file_add(desktop->model, mount->fi,
5560 FM_FOLDER_MODEL_ITEMPOS_POST);
5561 else
5562 fm_folder_model_extra_file_remove(desktop->model, mount->fi);
5563 }
5564 }
5565 }
5566 #endif
5567
on_desktop_folder_set_toggled(GtkToggleButton * btn,GtkFileChooserButton * chooser)5568 static void on_desktop_folder_set_toggled(GtkToggleButton *btn, GtkFileChooserButton *chooser)
5569 {
5570 gboolean active = gtk_toggle_button_get_active(btn);
5571
5572 gtk_widget_set_sensitive(GTK_WIDGET(chooser), active);
5573 if (active)
5574 g_signal_emit_by_name(chooser, "file-set");
5575 else
5576 {
5577 char *path = fm_path_to_str(fm_path_get_desktop());
5578 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(chooser), path);
5579 g_free(path);
5580 }
5581 }
5582
5583 typedef struct
5584 {
5585 FmDesktop *desktop;
5586 GtkWidget *desktop_folder_box;
5587 #if FM_CHECK_VERSION(1, 2, 0)
5588 GtkWidget *icons_page;
5589 #endif
5590 gpointer chooser;
5591 guint timeout_handler;
5592 } FolderChooserData;
5593
_chooser_timeout(gpointer user_data)5594 static gboolean _chooser_timeout(gpointer user_data)
5595 {
5596 FolderChooserData *data;
5597 GFile *gf;
5598 FmFolder *folder;
5599
5600 if(g_source_is_destroyed(g_main_current_source()))
5601 return FALSE;
5602 data = user_data;
5603 data->timeout_handler = 0;
5604 if (!gtk_widget_get_sensitive(data->desktop_folder_box))
5605 return FALSE;
5606 gf = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(data->chooser));
5607 if (gf == NULL) /* no file chosen */
5608 return FALSE;
5609 folder = fm_folder_from_gfile(gf);
5610 if (data->desktop->model == NULL ||
5611 fm_folder_model_get_folder(data->desktop->model) != folder)
5612 {
5613 disconnect_model(data->desktop);
5614 if (folder)
5615 connect_model(data->desktop, folder);
5616 else
5617 queue_layout_items(data->desktop);
5618 }
5619 g_free(data->desktop->conf.folder);
5620 if (G_UNLIKELY(data->desktop->model == NULL))
5621 data->desktop->conf.folder = g_strdup("");
5622 else if (!fm_path_equal(fm_folder_get_path(folder), fm_path_get_desktop()))
5623 data->desktop->conf.folder = g_file_get_path(gf);
5624 else
5625 data->desktop->conf.folder = NULL;
5626 queue_config_save(data->desktop);
5627 if (folder)
5628 g_object_unref(folder);
5629 g_object_unref(gf);
5630 return FALSE;
5631 }
5632
on_desktop_folder_set(GtkFileChooserButton * btn,FolderChooserData * data)5633 static void on_desktop_folder_set(GtkFileChooserButton *btn, FolderChooserData *data)
5634 {
5635 if (data->timeout_handler == 0)
5636 data->timeout_handler = gdk_threads_add_timeout(100, _chooser_timeout, data);
5637 }
5638
_data_destroy(gpointer data,GObject * dlg)5639 static void _data_destroy(gpointer data, GObject *dlg)
5640 {
5641 FolderChooserData *d = data;
5642
5643 if (d->timeout_handler)
5644 g_source_remove(d->timeout_handler);
5645 g_free(data);
5646 }
5647
on_use_desktop_folder_toggled(GtkToggleButton * btn,FolderChooserData * data)5648 static void on_use_desktop_folder_toggled(GtkToggleButton *btn, FolderChooserData *data)
5649 {
5650 gboolean active = gtk_toggle_button_get_active(btn);
5651
5652 gtk_widget_set_sensitive(data->desktop_folder_box, active);
5653 #if FM_CHECK_VERSION(1, 2, 0)
5654 gtk_widget_set_visible(data->icons_page, active);
5655 #endif
5656 if (active)
5657 g_signal_emit_by_name(data->chooser, "file-set");
5658 else
5659 {
5660 disconnect_model(data->desktop);
5661 queue_layout_items(data->desktop);
5662 g_free(data->desktop->conf.folder);
5663 data->desktop->conf.folder = g_strdup("");
5664 queue_config_save(data->desktop);
5665 /* g_debug("*** desktop folder disabled"); */
5666 }
5667 }
5668
fm_desktop_preference(GtkAction * act,FmDesktop * desktop)5669 void fm_desktop_preference(GtkAction *act, FmDesktop *desktop)
5670 {
5671 if (desktop == NULL)
5672 return;
5673
5674 if(!desktop_pref_dlg)
5675 {
5676 GtkBuilder* builder;
5677 GObject *item;
5678 GtkWidget *img_preview;
5679 FolderChooserData *data;
5680 char *path_str;
5681
5682 builder = gtk_builder_new();
5683 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/desktop-pref.ui", NULL);
5684 desktop_pref_dlg = GTK_WINDOW(gtk_builder_get_object(builder, "dlg"));
5685 item = gtk_builder_get_object(builder, "wallpaper");
5686 g_signal_connect(item, "file-set", G_CALLBACK(on_wallpaper_set), desktop);
5687 img_preview = gtk_image_new();
5688 gtk_misc_set_alignment(GTK_MISC(img_preview), 0.5, 0.0);
5689 gtk_widget_set_size_request( img_preview, 128, 128 );
5690 gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(item), img_preview);
5691 g_signal_connect( item, "update-preview", G_CALLBACK(on_update_img_preview), img_preview );
5692 if(desktop->conf.wallpaper)
5693 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(item), desktop->conf.wallpaper);
5694 item = gtk_builder_get_object(builder, "wallpaper_mode");
5695 gtk_combo_box_set_active(GTK_COMBO_BOX(item), desktop->conf.wallpaper_mode);
5696 g_signal_connect(item, "changed", G_CALLBACK(on_wallpaper_mode_changed), desktop);
5697 item = gtk_builder_get_object(builder, "wallpaper_box");
5698 if (item)
5699 {
5700 g_signal_connect(gtk_builder_get_object(builder, "wallpaper_mode"),
5701 "changed", G_CALLBACK(on_wallpaper_mode_changed2),
5702 item);
5703 gtk_widget_set_sensitive(GTK_WIDGET(item),
5704 desktop->conf.wallpaper_mode != FM_WP_COLOR);
5705 }
5706 item = gtk_builder_get_object(builder, "desktop_bg");
5707 gtk_color_button_set_color(GTK_COLOR_BUTTON(item), &desktop->conf.desktop_bg);
5708 g_signal_connect(item, "color-set", G_CALLBACK(on_bg_color_set), desktop);
5709 item = gtk_builder_get_object(builder, "wallpaper_common");
5710 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), desktop->conf.wallpaper_common);
5711 g_signal_connect(item, "toggled", G_CALLBACK(on_wallpaper_common_toggled), desktop);
5712 item = gtk_builder_get_object(builder, "desktop_fg");
5713 gtk_color_button_set_color(GTK_COLOR_BUTTON(item), &desktop->conf.desktop_fg);
5714 g_signal_connect(item, "color-set", G_CALLBACK(on_fg_color_set), desktop);
5715 item = gtk_builder_get_object(builder, "desktop_shadow");
5716 gtk_color_button_set_color(GTK_COLOR_BUTTON(item), &desktop->conf.desktop_shadow);
5717 g_signal_connect(item, "color-set", G_CALLBACK(on_shadow_color_set), desktop);
5718 item = gtk_builder_get_object(builder, "show_wm_menu");
5719 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), desktop->conf.show_wm_menu);
5720 g_signal_connect(item, "toggled", G_CALLBACK(on_wm_menu_toggled), desktop);
5721 item = gtk_builder_get_object(builder, "desktop_font");
5722 if(desktop->conf.desktop_font)
5723 gtk_font_button_set_font_name(GTK_FONT_BUTTON(item), desktop->conf.desktop_font);
5724 g_signal_connect(item, "font-set", G_CALLBACK(on_desktop_font_set), desktop);
5725
5726 data = g_new(FolderChooserData, 1);
5727 data->desktop = desktop;
5728 data->desktop_folder_box = GTK_WIDGET(gtk_builder_get_object(builder, "desktop_folder_box"));
5729 data->chooser = gtk_builder_get_object(builder, "desktop_folder");
5730 /* GtkFileChooser is very weird and sends $HOME 4 times then chosen
5731 dir before user even see the button actually, therefore the
5732 workaround is required - we add a timeout before we do anything */
5733 data->timeout_handler = gdk_threads_add_timeout(500, _chooser_timeout, data);
5734 g_object_weak_ref(data->chooser, _data_destroy, data);
5735 /* toggle button to enable icons support */
5736 item = gtk_builder_get_object(builder, "use_desktop_folder");
5737 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), desktop->model != NULL);
5738 gtk_widget_set_sensitive(data->desktop_folder_box, desktop->model != NULL);
5739 g_signal_connect(item, "toggled",
5740 G_CALLBACK(on_use_desktop_folder_toggled), data);
5741 /* radio buttons for folder choose - default or custom */
5742 item = gtk_builder_get_object(builder, "desktop_folder_set");
5743 if (desktop->model == NULL
5744 || fm_path_equal(fm_folder_model_get_folder_path(desktop->model),
5745 fm_path_get_desktop()))
5746 {
5747 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "desktop_folder_default")), TRUE);
5748 gtk_widget_set_sensitive(GTK_WIDGET(data->chooser), FALSE);
5749 }
5750 else
5751 {
5752 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), TRUE);
5753 gtk_widget_set_sensitive(GTK_WIDGET(data->chooser), TRUE);
5754 }
5755 g_signal_connect(item, "toggled",
5756 G_CALLBACK(on_desktop_folder_set_toggled), data->chooser);
5757 /* the desktop folder chooser dialog */
5758 if (desktop->model)
5759 path_str = fm_path_to_uri(fm_folder_model_get_folder_path(desktop->model));
5760 else
5761 path_str = fm_path_to_uri(fm_path_get_desktop());
5762 gtk_file_chooser_set_current_folder_uri(data->chooser, path_str);
5763 g_free(path_str);
5764 g_signal_connect(data->chooser, "file-set", G_CALLBACK(on_desktop_folder_set), data);
5765 g_signal_connect(data->chooser, "selection-changed", G_CALLBACK(on_desktop_folder_set), data);
5766 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "desktop_folder_chooser")));
5767 item = gtk_builder_get_object(builder, "desktop_folder_new_win");
5768 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), app_config->desktop_folder_new_win);
5769 g_signal_connect(item, "toggled", G_CALLBACK(on_desktop_folder_new_win_toggled), desktop);
5770 #if FM_CHECK_VERSION(1, 2, 0)
5771 item = gtk_builder_get_object(builder, "show_documents");
5772 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), desktop->conf.show_documents);
5773 gtk_widget_set_sensitive(GTK_WIDGET(item), documents != NULL);
5774 g_signal_connect(item, "toggled", G_CALLBACK(on_show_documents_toggled), desktop);
5775 item = gtk_builder_get_object(builder, "show_trash");
5776 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), desktop->conf.show_trash);
5777 gtk_widget_set_sensitive(GTK_WIDGET(item), trash_can != NULL);
5778 g_signal_connect(item, "toggled", G_CALLBACK(on_show_trash_toggled), desktop);
5779 item = gtk_builder_get_object(builder, "show_mounts");
5780 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), desktop->conf.show_mounts);
5781 g_signal_connect(item, "toggled", G_CALLBACK(on_show_mounts_toggled), desktop);
5782 data->icons_page = GTK_WIDGET(gtk_builder_get_object(builder, "icons_page"));
5783 gtk_widget_set_visible(data->icons_page, desktop->model != NULL);
5784 #endif
5785
5786 g_signal_connect(desktop_pref_dlg, "response", G_CALLBACK(on_response), &desktop_pref_dlg);
5787 g_object_unref(builder);
5788
5789 pcmanfm_ref();
5790 g_signal_connect(desktop_pref_dlg, "destroy", G_CALLBACK(pcmanfm_unref), NULL);
5791 /* make dialog be valid only before the desktop is destroyed */
5792 gtk_window_set_transient_for(desktop_pref_dlg, GTK_WINDOW(desktop));
5793 gtk_window_set_destroy_with_parent(desktop_pref_dlg, TRUE);
5794 }
5795 gtk_window_present(desktop_pref_dlg);
5796 }
5797
5798
5799 /* ---------------------------------------------------------------------
5800 Interface functions */
5801
fm_desktop_manager_init(gint on_screen)5802 void fm_desktop_manager_init(gint on_screen)
5803 {
5804 GdkDisplay * gdpy;
5805 int i, n_scr, n_mon, scr, mon;
5806 const char* desktop_path;
5807 #if FM_CHECK_VERSION(1, 2, 0)
5808 GFile *gf;
5809 #endif
5810
5811 if(! win_group)
5812 win_group = gtk_window_group_new();
5813
5814 /* create the ~/Desktop folder if it doesn't exist. */
5815 desktop_path = g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP);
5816 /* FIXME: should we use a localized folder name instead? */
5817 g_mkdir_with_parents(desktop_path, 0700); /* ensure the existance of Desktop folder. */
5818 /* FIXME: should we store the desktop folder path in the annoying ~/.config/user-dirs.dirs file? */
5819
5820 gdpy = gdk_display_get_default();
5821 n_scr = gdk_display_get_n_screens(gdpy);
5822 n_screens = 0;
5823 for(i = 0; i < n_scr; i++)
5824 n_screens += gdk_screen_get_n_monitors(gdk_display_get_screen(gdpy, i));
5825 desktops = g_new(FmDesktop*, n_screens);
5826 for(scr = 0, i = 0; scr < n_scr; scr++)
5827 {
5828 GdkScreen* screen = gdk_display_get_screen(gdpy, scr);
5829 n_mon = gdk_screen_get_n_monitors(screen);
5830 for(mon = 0; mon < n_mon; mon++)
5831 {
5832 gint mon_init = (on_screen < 0 || on_screen == (int)scr) ? (int)mon : (mon ? -2 : -1);
5833 FmDesktop *desktop = fm_desktop_new(screen, mon_init);
5834 GtkWidget *widget = GTK_WIDGET(desktop);
5835 FmFolder *desktop_folder;
5836
5837 desktops[i++] = desktop;
5838 if(mon_init < 0)
5839 continue;
5840 /* realize it: without this, setting wallpaper or font won't work */
5841 gtk_widget_realize(widget);
5842 /* realizing also loads config */
5843 if (desktop->conf.folder)
5844 {
5845 if (desktop->conf.folder[0])
5846 desktop_folder = fm_folder_from_path_name(desktop->conf.folder);
5847 else
5848 desktop_folder = NULL;
5849 }
5850 else
5851 desktop_folder = fm_folder_from_path(fm_path_get_desktop());
5852 if (desktop_folder)
5853 {
5854 connect_model(desktop, desktop_folder);
5855 g_object_unref(desktop_folder);
5856 }
5857 else
5858 /* we have to add popup here because it will be never
5859 set by connect_model() as latter wasn't called */
5860 fm_folder_view_add_popup(FM_FOLDER_VIEW(desktop),
5861 GTK_WINDOW(desktop),
5862 fm_desktop_update_popup);
5863 if (desktop->model)
5864 #if FM_CHECK_VERSION(1, 0, 2)
5865 fm_folder_model_set_sort(desktop->model,
5866 desktop->conf.desktop_sort_by,
5867 desktop->conf.desktop_sort_type);
5868 #else
5869 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(desktop->model),
5870 desktop->conf.desktop_sort_by,
5871 desktop->conf.desktop_sort_type);
5872 #endif
5873 gtk_widget_show_all(widget);
5874 gdk_window_lower(gtk_widget_get_window(widget));
5875 }
5876 }
5877
5878 icon_theme_changed = g_signal_connect(gtk_icon_theme_get_default(), "changed", G_CALLBACK(on_icon_theme_changed), NULL);
5879
5880 hand_cursor = gdk_cursor_new(GDK_HAND2);
5881
5882 #if FM_CHECK_VERSION(1, 2, 0)
5883 /* create extra items */
5884 gf = fm_file_new_for_uri("trash:///");
5885 if (g_file_query_exists(gf, NULL))
5886 trash_can = _add_extra_item("trash:///");
5887 else
5888 trash_can = NULL;
5889 if (G_LIKELY(trash_can))
5890 {
5891 trash_monitor = fm_monitor_directory(gf, NULL);
5892 g_signal_connect(trash_monitor, "changed", G_CALLBACK(on_trash_changed), trash_can);
5893 }
5894 g_object_unref(gf);
5895 documents = _add_extra_item(g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS));
5896 /* FIXME: support some other dirs */
5897 vol_mon = g_volume_monitor_get();
5898 if (G_LIKELY(vol_mon))
5899 {
5900 GList *ml = g_volume_monitor_get_mounts(vol_mon), *l;
5901
5902 /* if some mounts are already there, add them to own list */
5903 for (l = ml; l; l = l->next)
5904 {
5905 GMount *mount = G_MOUNT(l->data);
5906 on_mount_added(vol_mon, mount, NULL);
5907 g_object_unref(mount);
5908 }
5909 g_list_free(ml);
5910 g_signal_connect(vol_mon, "mount-added", G_CALLBACK(on_mount_added), NULL);
5911 g_signal_connect(vol_mon, "mount-removed", G_CALLBACK(on_mount_removed), NULL);
5912 }
5913 #endif
5914
5915 pcmanfm_ref();
5916 }
5917
fm_desktop_manager_finalize()5918 void fm_desktop_manager_finalize()
5919 {
5920 int i;
5921
5922 if (idle_config_save)
5923 {
5924 g_source_remove(idle_config_save);
5925 idle_config_save = 0;
5926 }
5927 for(i = 0; i < n_screens; i++)
5928 {
5929 gtk_widget_destroy(GTK_WIDGET(desktops[i]));
5930 }
5931 g_free(desktops);
5932 n_screens = 0;
5933 g_object_unref(win_group);
5934 win_group = NULL;
5935
5936 g_signal_handler_disconnect(gtk_icon_theme_get_default(), icon_theme_changed);
5937
5938 if(acc_grp)
5939 g_object_unref(acc_grp);
5940 acc_grp = NULL;
5941
5942 if(hand_cursor)
5943 {
5944 gdk_cursor_unref(hand_cursor);
5945 hand_cursor = NULL;
5946 }
5947
5948 #if FM_CHECK_VERSION(1, 2, 0)
5949 if (G_LIKELY(documents))
5950 {
5951 _free_extra_item(documents);
5952 documents = NULL;
5953 }
5954 if (G_LIKELY(trash_can))
5955 {
5956 g_signal_handlers_disconnect_by_func(trash_monitor, on_trash_changed, trash_can);
5957 g_object_unref(trash_monitor);
5958 _free_extra_item(trash_can);
5959 trash_can = NULL;
5960 }
5961 if (G_LIKELY(vol_mon))
5962 {
5963 g_signal_handlers_disconnect_by_func(vol_mon, on_mount_added, NULL);
5964 g_signal_handlers_disconnect_by_func(vol_mon, on_mount_removed, NULL);
5965 g_object_unref(vol_mon);
5966 vol_mon = NULL;
5967 }
5968 while (mounts)
5969 {
5970 _free_extra_item(mounts->data);
5971 mounts = g_slist_delete_link(mounts, mounts);
5972 }
5973 #endif
5974
5975 pcmanfm_unref();
5976 }
5977
fm_desktop_get(gint screen,gint monitor)5978 FmDesktop* fm_desktop_get(gint screen, gint monitor)
5979 {
5980 int i = 0, n = 0;
5981 while(i < n_screens && n <= screen)
5982 {
5983 if(n == screen && desktops[i]->monitor == monitor)
5984 return desktops[i];
5985 i++;
5986 if(i < n_screens &&
5987 (desktops[i]->monitor == 0 || desktops[i]->monitor == -1))
5988 n++;
5989 }
5990 return NULL;
5991 }
5992
fm_desktop_wallpaper_changed(FmDesktop * desktop)5993 void fm_desktop_wallpaper_changed(FmDesktop *desktop)
5994 {
5995 queue_config_save(desktop);
5996 update_background(desktop, 0);
5997 }
5998