Merge lp:~noskcaj/ubuntu/vivid/thunar/1.6.4 into lp:ubuntu/vivid/thunar

Proposed by Jackson Doak
Status: Needs review
Proposed branch: lp:~noskcaj/ubuntu/vivid/thunar/1.6.4
Merge into: lp:ubuntu/vivid/thunar
Diff against target: 212897 lines (+58226/-66350)
175 files modified
.pc/02_fix-default-application-selection.patch/thunar/thunar-file.c (+0/-4064)
.pc/applied-patches (+0/-5)
.pc/git-force-toolbr-icons.patch/thunar/thunar-window.c (+0/-3846)
.pc/git-save-keyboard-shortcuts.patch/thunar/thunar-application.c (+0/-2067)
.pc/git-xfdesktop-4.11.patch/plugins/thunar-wallpaper/twp-provider.c (+0/-307)
.pc/gtk3-bookmarks.patch/thunar/thunar-gio-extensions.c (+0/-587)
.pc/gtk3-bookmarks.patch/thunar/thunar-shortcuts-model.c (+0/-2214)
.pc/gtk3-bookmarks.patch/thunar/thunar-util.c (+0/-485)
.pc/menu-icon-tweaks.patch/thunar/thunar-standard-view.c (+9/-2)
.pc/menu-icon-tweaks.patch/thunar/thunar-tree-view.c (+1/-0)
.pc/menu-icon-tweaks.patch/thunar/thunar-window.c (+1/-0)
ChangeLog (+2473/-0)
INSTALL (+3/-3)
Makefile.am (+26/-3)
Makefile.in (+147/-41)
NEWS (+29/-0)
aclocal.m4 (+289/-154)
config.guess (+99/-214)
config.h.in (+0/-3)
config.sub (+33/-24)
configure (+431/-285)
configure.ac (+4/-4)
debian/changelog (+10/-0)
debian/patches/0001-Don-t-copy-templates-but-create-them-bug-8312.patch (+0/-256)
debian/patches/02_fix-default-application-selection.patch (+0/-39)
debian/patches/git-force-toolbr-icons.patch (+0/-18)
debian/patches/git-save-keyboard-shortcuts.patch (+0/-138)
debian/patches/git-xfdesktop-4.11.patch (+0/-188)
debian/patches/gtk3-bookmarks.patch (+0/-80)
debian/patches/series (+0/-5)
debian/thunar.install (+2/-0)
depcomp (+2/-1)
docs/Makefile.am (+1/-2)
docs/Makefile.in (+53/-26)
docs/README.thunarrc (+0/-253)
docs/Thunar.1 (+2/-2)
docs/design/Makefile.in (+46/-17)
docs/papers/Makefile.in (+46/-17)
docs/reference/Makefile.in (+52/-24)
docs/reference/thunarx/Makefile.am (+2/-1)
docs/reference/thunarx/Makefile.in (+134/-75)
docs/reference/thunarx/html/ThunarxFileInfo.html (+798/-571)
docs/reference/thunarx/html/ThunarxMenuProvider.html (+311/-243)
docs/reference/thunarx/html/ThunarxPreferencesProvider.html (+131/-97)
docs/reference/thunarx/html/ThunarxPropertyPage.html (+329/-227)
docs/reference/thunarx/html/ThunarxPropertyPageProvider.html (+148/-115)
docs/reference/thunarx/html/ThunarxProviderFactory.html (+125/-102)
docs/reference/thunarx/html/ThunarxProviderPlugin.html (+654/-490)
docs/reference/thunarx/html/ThunarxRenamer.html (+635/-475)
docs/reference/thunarx/html/ThunarxRenamerProvider.html (+122/-95)
docs/reference/thunarx/html/index.html (+4/-4)
docs/reference/thunarx/html/index.sgml (+70/-40)
docs/reference/thunarx/html/ix01.html (+109/-109)
docs/reference/thunarx/html/style.css (+317/-107)
docs/reference/thunarx/html/thunarx-Variables-and-functions-to-check-the-library-version.html (+208/-170)
docs/reference/thunarx/html/thunarx-abstraction-layer.html (+9/-9)
docs/reference/thunarx/html/thunarx-fundamentals.html (+9/-9)
docs/reference/thunarx/html/thunarx-overview.html (+9/-9)
docs/reference/thunarx/html/thunarx-providers.html (+9/-9)
docs/reference/thunarx/html/thunarx-using-extensions.html (+9/-9)
docs/reference/thunarx/html/thunarx-writing-extensions-advanced-topics.html (+11/-11)
docs/reference/thunarx/html/thunarx-writing-extensions-getting-started.html (+10/-10)
docs/reference/thunarx/html/thunarx-writing-extensions.html (+13/-13)
docs/reference/thunarx/html/thunarx.devhelp2 (+58/-58)
docs/reference/thunarx/version.xml (+1/-1)
examples/Makefile.in (+52/-24)
examples/tex-open-terminal/Makefile.in (+49/-19)
gtk-doc.make (+77/-42)
icons/128x128/Makefile.in (+46/-17)
icons/16x16/Makefile.in (+46/-17)
icons/24x24/Makefile.in (+46/-17)
icons/48x48/Makefile.in (+46/-17)
icons/64x64/Makefile.in (+46/-17)
icons/Makefile.in (+52/-24)
icons/scalable/Makefile.in (+46/-17)
ltmain.sh (+19/-13)
missing (+2/-2)
org.xfce.thunar.policy.in.in (+37/-0)
pixmaps/Makefile.in (+46/-17)
plugins/Makefile.in (+52/-24)
plugins/thunar-apr/Makefile.in (+49/-19)
plugins/thunar-sbr/Makefile.in (+49/-19)
plugins/thunar-sendto-email/Makefile.in (+49/-19)
plugins/thunar-tpa/Makefile.in (+49/-19)
plugins/thunar-tpa/thunar-tpa.c (+9/-11)
plugins/thunar-uca/Makefile.in (+49/-19)
plugins/thunar-wallpaper/Makefile.in (+49/-19)
po/POTFILES.in (+2/-0)
po/ar.po (+511/-807)
po/ast.po (+938/-1327)
po/be.po (+768/-1215)
po/bg.po (+590/-885)
po/bn.po (+602/-854)
po/ca.po (+322/-741)
po/cs.po (+632/-1066)
po/da.po (+520/-901)
po/de.po (+683/-1106)
po/el.po (+717/-984)
po/en_AU.po (+3597/-0)
po/en_GB.po (+544/-927)
po/eo.po (+666/-1129)
po/es.po (+478/-670)
po/et.po (+984/-1409)
po/eu.po (+533/-918)
po/fi.po (+618/-879)
po/fr.po (+723/-1132)
po/gl.po (+617/-1057)
po/he.po (+394/-724)
po/hr.po (+552/-658)
po/hu.po (+417/-570)
po/id.po (+299/-685)
po/is.po (+939/-1426)
po/it.po (+598/-1088)
po/ja.po (+469/-585)
po/kk.po (+436/-791)
po/ko.po (+486/-523)
po/lt.po (+613/-875)
po/lv.po (+621/-1034)
po/ms.po (+3563/-0)
po/nb.po (+632/-1032)
po/nl.po (+359/-784)
po/nn.po (+364/-772)
po/oc.po (+3591/-0)
po/pa.po (+621/-898)
po/pl.po (+557/-967)
po/pt.po (+541/-758)
po/pt_BR.po (+607/-1009)
po/ro.po (+539/-771)
po/ru.po (+328/-504)
po/sk.po (+642/-1058)
po/sq.po (+646/-1072)
po/sr.po (+577/-765)
po/sv.po (+717/-1142)
po/te.po (+720/-834)
po/th.po (+3564/-0)
po/tr.po (+749/-1141)
po/ug.po (+577/-816)
po/uk.po (+542/-903)
po/ur.po (+645/-1036)
po/ur_PK.po (+645/-1036)
po/vi.po (+1027/-1225)
po/zh_CN.po (+543/-675)
po/zh_HK.po (+3551/-0)
po/zh_TW.po (+599/-896)
test-driver (+16/-4)
thunar.appdata.xml.in (+24/-0)
thunar/Makefile.in (+49/-19)
thunar/main.c (+4/-4)
thunar/thunar-create-dialog.c (+1/-1)
thunar/thunar-dialogs.c (+6/-4)
thunar/thunar-dnd.c (+1/-1)
thunar/thunar-file.c (+135/-50)
thunar/thunar-file.h (+141/-139)
thunar/thunar-folder.c (+7/-2)
thunar/thunar-gio-extensions.c (+14/-7)
thunar/thunar-history.c (+2/-0)
thunar/thunar-icon-renderer.c (+43/-2)
thunar/thunar-io-jobs-util.c (+39/-39)
thunar/thunar-list-model.c (+85/-6)
thunar/thunar-preferences-dialog.c (+7/-1)
thunar/thunar-preferences.c (+13/-0)
thunar/thunar-properties-dialog.c (+2/-4)
thunar/thunar-renamer-model.c (+3/-4)
thunar/thunar-session-client.c (+1/-0)
thunar/thunar-size-label.c (+22/-9)
thunar/thunar-standard-view.c (+9/-2)
thunar/thunar-text-renderer.c (+2/-1)
thunar/thunar-thumbnail-cache.c (+128/-128)
thunar/thunar-thumbnailer.c (+2/-0)
thunar/thunar-transfer-job.c (+30/-17)
thunar/thunar-tree-view.c (+1/-0)
thunar/thunar-util.c (+112/-0)
thunar/thunar-util.h (+2/-0)
thunarx/Makefile.in (+50/-20)
thunarx/thunarx-config.h (+1/-1)
To merge this branch: bzr merge lp:~noskcaj/ubuntu/vivid/thunar/1.6.4
Reviewer Review Type Date Requested Status
Daniel Holbach (community) Needs Fixing
Review via email: mp+245533@code.launchpad.net

Description of the change

New upstream release. Xubuntu 15.04

To post a comment you must log in.
100. By Jackson Doak

Install appdata and polkit files in thunar deb

Revision history for this message
Daniel Holbach (dholbach) wrote :

It'd be really nice if you could explain in d/changelog why the patches are dropped.

review: Needs Fixing
101. By Jackson Doak

  02_fix-default-application-selection.patch, fixed upstream

Unmerged revisions

101. By Jackson Doak

  02_fix-default-application-selection.patch, fixed upstream

100. By Jackson Doak

Install appdata and polkit files in thunar deb

99. By Jackson Doak

* New upstream release.
* Dropped patches: git-force-toolbr-icons.patch, gtk3-bookmarks.patch,
  git-save-keyboard-shortcuts.patch, git-xfdesktop-4.11.patch,
  02_fix-default-application-selection.patch

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== removed directory '.pc/02_fix-default-application-selection.patch'
=== removed directory '.pc/02_fix-default-application-selection.patch/thunar'
=== removed file '.pc/02_fix-default-application-selection.patch/thunar/thunar-file.c'
--- .pc/02_fix-default-application-selection.patch/thunar/thunar-file.c 2014-10-30 11:00:02 +0000
+++ .pc/02_fix-default-application-selection.patch/thunar/thunar-file.c 1970-01-01 00:00:00 +0000
@@ -1,4064 +0,0 @@
1/* vi:set et ai sw=2 sts=2 ts=2: */
2/*-
3 * Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org>
4 * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#ifdef HAVE_SYS_TYPES_H
27#include <sys/types.h>
28#endif
29
30#ifdef HAVE_SYS_STAT_H
31#include <sys/stat.h>
32#endif
33
34#ifdef HAVE_ERRNO_H
35#include <errno.h>
36#endif
37#ifdef HAVE_MEMORY_H
38#include <memory.h>
39#endif
40#ifdef HAVE_STDLIB_H
41#include <stdlib.h>
42#endif
43#ifdef HAVE_STRING_H
44#include <string.h>
45#endif
46#ifdef HAVE_TIME_H
47#include <time.h>
48#endif
49#ifdef HAVE_UNISTD_H
50#include <unistd.h>
51#endif
52
53#include <gio/gio.h>
54#include <libxfce4ui/libxfce4ui.h>
55
56#include <thunarx/thunarx.h>
57
58#include <thunar/thunar-application.h>
59#include <thunar/thunar-chooser-dialog.h>
60#include <thunar/thunar-exec.h>
61#include <thunar/thunar-file.h>
62#include <thunar/thunar-file-monitor.h>
63#include <thunar/thunar-gio-extensions.h>
64#include <thunar/thunar-gobject-extensions.h>
65#include <thunar/thunar-private.h>
66#include <thunar/thunar-user.h>
67#include <thunar/thunar-util.h>
68#include <thunar/thunar-dialogs.h>
69#include <thunar/thunar-icon-factory.h>
70
71
72
73/* Dump the file cache every X second, set to 0 to disable */
74#define DUMP_FILE_CACHE 0
75
76
77
78/* Signal identifiers */
79enum
80{
81 DESTROY,
82 LAST_SIGNAL,
83};
84
85
86
87static void thunar_file_info_init (ThunarxFileInfoIface *iface);
88static void thunar_file_dispose (GObject *object);
89static void thunar_file_finalize (GObject *object);
90static gchar *thunar_file_info_get_name (ThunarxFileInfo *file_info);
91static gchar *thunar_file_info_get_uri (ThunarxFileInfo *file_info);
92static gchar *thunar_file_info_get_parent_uri (ThunarxFileInfo *file_info);
93static gchar *thunar_file_info_get_uri_scheme (ThunarxFileInfo *file_info);
94static gchar *thunar_file_info_get_mime_type (ThunarxFileInfo *file_info);
95static gboolean thunar_file_info_has_mime_type (ThunarxFileInfo *file_info,
96 const gchar *mime_type);
97static gboolean thunar_file_info_is_directory (ThunarxFileInfo *file_info);
98static GFileInfo *thunar_file_info_get_file_info (ThunarxFileInfo *file_info);
99static GFileInfo *thunar_file_info_get_filesystem_info (ThunarxFileInfo *file_info);
100static GFile *thunar_file_info_get_location (ThunarxFileInfo *file_info);
101static void thunar_file_info_changed (ThunarxFileInfo *file_info);
102static gboolean thunar_file_denies_access_permission (const ThunarFile *file,
103 ThunarFileMode usr_permissions,
104 ThunarFileMode grp_permissions,
105 ThunarFileMode oth_permissions);
106static void thunar_file_monitor (GFileMonitor *monitor,
107 GFile *path,
108 GFile *other_path,
109 GFileMonitorEvent event_type,
110 gpointer user_data);
111static gboolean thunar_file_load (ThunarFile *file,
112 GCancellable *cancellable,
113 GError **error);
114static gboolean thunar_file_is_readable (const ThunarFile *file);
115static gboolean thunar_file_same_filesystem (const ThunarFile *file_a,
116 const ThunarFile *file_b);
117
118
119
120G_LOCK_DEFINE_STATIC (file_cache_mutex);
121G_LOCK_DEFINE_STATIC (file_content_type_mutex);
122
123
124
125static ThunarUserManager *user_manager;
126static GHashTable *file_cache;
127static guint32 effective_user_id;
128static GQuark thunar_file_watch_quark;
129static guint file_signals[LAST_SIGNAL];
130
131
132
133#define FLAG_SET_THUMB_STATE(file,new_state) G_STMT_START{ (file)->flags = ((file)->flags & ~THUNAR_FILE_FLAG_THUMB_MASK) | (new_state); }G_STMT_END
134#define FLAG_GET_THUMB_STATE(file) ((file)->flags & THUNAR_FILE_FLAG_THUMB_MASK)
135#define FLAG_SET(file,flag) G_STMT_START{ ((file)->flags |= (flag)); }G_STMT_END
136#define FLAG_UNSET(file,flag) G_STMT_START{ ((file)->flags &= ~(flag)); }G_STMT_END
137#define FLAG_IS_SET(file,flag) (((file)->flags & (flag)) != 0)
138
139#define DEFAULT_CONTENT_TYPE "application/octet-stream"
140
141
142
143typedef enum
144{
145 THUNAR_FILE_FLAG_THUMB_MASK = 0x03, /* storage for ThunarFileThumbState */
146 THUNAR_FILE_FLAG_IN_DESTRUCTION = 1 << 2, /* for avoiding recursion during destroy */
147 THUNAR_FILE_FLAG_IS_MOUNTED = 1 << 3, /* whether this file is mounted */
148}
149ThunarFileFlags;
150
151struct _ThunarFileClass
152{
153 GObjectClass __parent__;
154
155 /* signals */
156 void (*destroy) (ThunarFile *file);
157};
158
159struct _ThunarFile
160{
161 GObject __parent__;
162
163 /* storage for the file information */
164 GFileInfo *info;
165 GFileType kind;
166 GFile *gfile;
167 gchar *content_type;
168 gchar *icon_name;
169
170 gchar *custom_icon_name;
171 gchar *display_name;
172 gchar *basename;
173 gchar *thumbnail_path;
174
175 /* sorting */
176 gchar *collate_key;
177 gchar *collate_key_nocase;
178
179 /* flags for thumbnail state etc */
180 ThunarFileFlags flags;
181};
182
183typedef struct
184{
185 GFileMonitor *monitor;
186 guint watch_count;
187}
188ThunarFileWatch;
189
190typedef struct
191{
192 ThunarFileGetFunc func;
193 gpointer user_data;
194 GCancellable *cancellable;
195}
196ThunarFileGetData;
197
198static struct
199{
200 GUserDirectory type;
201 const gchar *icon_name;
202}
203thunar_file_dirs[] =
204{
205 { G_USER_DIRECTORY_DESKTOP, "user-desktop" },
206 { G_USER_DIRECTORY_DOCUMENTS, "folder-documents" },
207 { G_USER_DIRECTORY_DOWNLOAD, "folder-download" },
208 { G_USER_DIRECTORY_MUSIC, "folder-music" },
209 { G_USER_DIRECTORY_PICTURES, "folder-pictures" },
210 { G_USER_DIRECTORY_PUBLIC_SHARE, "folder-publicshare" },
211 { G_USER_DIRECTORY_TEMPLATES, "folder-templates" },
212 { G_USER_DIRECTORY_VIDEOS, "folder-videos" }
213};
214
215
216
217G_DEFINE_TYPE_WITH_CODE (ThunarFile, thunar_file, G_TYPE_OBJECT,
218 G_IMPLEMENT_INTERFACE (THUNARX_TYPE_FILE_INFO, thunar_file_info_init))
219
220
221
222#ifdef G_ENABLE_DEBUG
223#ifdef HAVE_ATEXIT
224static gboolean thunar_file_atexit_registered = FALSE;
225
226
227
228static void
229thunar_file_atexit_foreach (gpointer key,
230 gpointer value,
231 gpointer user_data)
232{
233 gchar *uri;
234
235 uri = g_file_get_uri (key);
236 g_print ("--> %s (%u)\n", uri, G_OBJECT (value)->ref_count);
237 if (G_OBJECT (key)->ref_count > 2)
238 g_print (" GFile (%u)\n", G_OBJECT (key)->ref_count - 2);
239 g_free (uri);
240}
241
242
243
244static void
245thunar_file_atexit (void)
246{
247 G_LOCK (file_cache_mutex);
248
249 if (file_cache == NULL || g_hash_table_size (file_cache) == 0)
250 {
251 G_UNLOCK (file_cache_mutex);
252 return;
253 }
254
255 g_print ("--- Leaked a total of %u ThunarFile objects:\n",
256 g_hash_table_size (file_cache));
257
258 g_hash_table_foreach (file_cache, thunar_file_atexit_foreach, NULL);
259
260 g_print ("\n");
261
262 G_UNLOCK (file_cache_mutex);
263}
264#endif
265#endif
266
267
268
269#if DUMP_FILE_CACHE
270static void
271thunar_file_cache_dump_foreach (gpointer gfile,
272 gpointer value,
273 gpointer user_data)
274{
275 gchar *name;
276
277 name = g_file_get_parse_name (G_FILE (gfile));
278 g_print (" %s (%u)\n", name, G_OBJECT (value)->ref_count);
279 g_free (name);
280}
281
282
283
284static gboolean
285thunar_file_cache_dump (gpointer user_data)
286{
287 G_LOCK (file_cache_mutex);
288
289 if (file_cache != NULL)
290 {
291 g_print ("--- %d ThunarFile objects in cache:\n",
292 g_hash_table_size (file_cache));
293
294 g_hash_table_foreach (file_cache, thunar_file_cache_dump_foreach, NULL);
295
296 g_print ("\n");
297 }
298
299 G_UNLOCK (file_cache_mutex);
300
301 return TRUE;
302}
303#endif
304
305
306
307static void
308thunar_file_class_init (ThunarFileClass *klass)
309{
310 GObjectClass *gobject_class;
311
312#ifdef G_ENABLE_DEBUG
313#ifdef HAVE_ATEXIT
314 if (G_UNLIKELY (!thunar_file_atexit_registered))
315 {
316 atexit ((void (*)(void)) thunar_file_atexit);
317 thunar_file_atexit_registered = TRUE;
318 }
319#endif
320#endif
321
322#if DUMP_FILE_CACHE
323 g_timeout_add_seconds (DUMP_FILE_CACHE, thunar_file_cache_dump, NULL);
324#endif
325
326 /* pre-allocate the required quarks */
327 thunar_file_watch_quark = g_quark_from_static_string ("thunar-file-watch");
328
329 /* grab a reference on the user manager */
330 user_manager = thunar_user_manager_get_default ();
331
332 /* determine the effective user id of the process */
333 effective_user_id = geteuid ();
334
335 gobject_class = G_OBJECT_CLASS (klass);
336 gobject_class->dispose = thunar_file_dispose;
337 gobject_class->finalize = thunar_file_finalize;
338
339 /**
340 * ThunarFile::destroy:
341 * @file : the #ThunarFile instance.
342 *
343 * Emitted when the system notices that the @file
344 * was destroyed.
345 **/
346 file_signals[DESTROY] =
347 g_signal_new (I_("destroy"),
348 G_TYPE_FROM_CLASS (klass),
349 G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
350 G_STRUCT_OFFSET (ThunarFileClass, destroy),
351 NULL, NULL,
352 g_cclosure_marshal_VOID__VOID,
353 G_TYPE_NONE, 0);
354}
355
356
357
358static void
359thunar_file_init (ThunarFile *file)
360{
361}
362
363
364
365static void
366thunar_file_info_init (ThunarxFileInfoIface *iface)
367{
368 iface->get_name = thunar_file_info_get_name;
369 iface->get_uri = thunar_file_info_get_uri;
370 iface->get_parent_uri = thunar_file_info_get_parent_uri;
371 iface->get_uri_scheme = thunar_file_info_get_uri_scheme;
372 iface->get_mime_type = thunar_file_info_get_mime_type;
373 iface->has_mime_type = thunar_file_info_has_mime_type;
374 iface->is_directory = thunar_file_info_is_directory;
375 iface->get_file_info = thunar_file_info_get_file_info;
376 iface->get_filesystem_info = thunar_file_info_get_filesystem_info;
377 iface->get_location = thunar_file_info_get_location;
378 iface->changed = thunar_file_info_changed;
379}
380
381
382
383static void
384thunar_file_dispose (GObject *object)
385{
386 ThunarFile *file = THUNAR_FILE (object);
387
388 /* check that we don't recurse here */
389 if (!FLAG_IS_SET (file, THUNAR_FILE_FLAG_IN_DESTRUCTION))
390 {
391 /* emit the "destroy" signal */
392 FLAG_SET (file, THUNAR_FILE_FLAG_IN_DESTRUCTION);
393 g_signal_emit (object, file_signals[DESTROY], 0);
394 FLAG_UNSET (file, THUNAR_FILE_FLAG_IN_DESTRUCTION);
395 }
396
397 (*G_OBJECT_CLASS (thunar_file_parent_class)->dispose) (object);
398}
399
400
401
402static void
403thunar_file_finalize (GObject *object)
404{
405 ThunarFile *file = THUNAR_FILE (object);
406
407 /* verify that nobody's watching the file anymore */
408#ifdef G_ENABLE_DEBUG
409 ThunarFileWatch *file_watch = g_object_get_qdata (G_OBJECT (file), thunar_file_watch_quark);
410 if (file_watch != NULL)
411 {
412 g_error ("Attempt to finalize a ThunarFile, which has an active "
413 "watch count of %d", file_watch->watch_count);
414 }
415#endif
416
417 /* drop the entry from the cache */
418 G_LOCK (file_cache_mutex);
419 g_hash_table_remove (file_cache, file->gfile);
420 G_UNLOCK (file_cache_mutex);
421
422 /* release file info */
423 if (file->info != NULL)
424 g_object_unref (file->info);
425
426 /* free the custom icon name */
427 g_free (file->custom_icon_name);
428
429 /* content type info */
430 g_free (file->content_type);
431 g_free (file->icon_name);
432
433 /* free display name and basename */
434 g_free (file->display_name);
435 g_free (file->basename);
436
437 /* free collate keys */
438 if (file->collate_key_nocase != file->collate_key)
439 g_free (file->collate_key_nocase);
440 g_free (file->collate_key);
441
442 /* free the thumbnail path */
443 g_free (file->thumbnail_path);
444
445 /* release file */
446 g_object_unref (file->gfile);
447
448 (*G_OBJECT_CLASS (thunar_file_parent_class)->finalize) (object);
449}
450
451
452
453static gchar *
454thunar_file_info_get_name (ThunarxFileInfo *file_info)
455{
456 return g_strdup (thunar_file_get_basename (THUNAR_FILE (file_info)));
457}
458
459
460
461static gchar*
462thunar_file_info_get_uri (ThunarxFileInfo *file_info)
463{
464 return thunar_file_dup_uri (THUNAR_FILE (file_info));
465}
466
467
468
469static gchar*
470thunar_file_info_get_parent_uri (ThunarxFileInfo *file_info)
471{
472 GFile *parent;
473 gchar *uri = NULL;
474
475 parent = g_file_get_parent (THUNAR_FILE (file_info)->gfile);
476 if (G_LIKELY (parent != NULL))
477 {
478 uri = g_file_get_uri (parent);
479 g_object_unref (parent);
480 }
481
482 return uri;
483}
484
485
486
487static gchar*
488thunar_file_info_get_uri_scheme (ThunarxFileInfo *file_info)
489{
490 return g_file_get_uri_scheme (THUNAR_FILE (file_info)->gfile);
491}
492
493
494
495static gchar*
496thunar_file_info_get_mime_type (ThunarxFileInfo *file_info)
497{
498 return g_strdup (thunar_file_get_content_type (THUNAR_FILE (file_info)));
499}
500
501
502
503static gboolean
504thunar_file_info_has_mime_type (ThunarxFileInfo *file_info,
505 const gchar *mime_type)
506{
507 if (THUNAR_FILE (file_info)->info == NULL)
508 return FALSE;
509
510 return g_content_type_is_a (thunar_file_get_content_type (THUNAR_FILE (file_info)), mime_type);
511}
512
513
514
515static gboolean
516thunar_file_info_is_directory (ThunarxFileInfo *file_info)
517{
518 return thunar_file_is_directory (THUNAR_FILE (file_info));
519}
520
521
522
523static GFileInfo *
524thunar_file_info_get_file_info (ThunarxFileInfo *file_info)
525{
526 _thunar_return_val_if_fail (THUNAR_IS_FILE (file_info), NULL);
527
528 if (THUNAR_FILE (file_info)->info != NULL)
529 return g_object_ref (THUNAR_FILE (file_info)->info);
530 else
531 return NULL;
532}
533
534
535
536static GFileInfo *
537thunar_file_info_get_filesystem_info (ThunarxFileInfo *file_info)
538{
539 _thunar_return_val_if_fail (THUNAR_IS_FILE (file_info), NULL);
540
541 return g_file_query_filesystem_info (THUNAR_FILE (file_info)->gfile,
542 THUNARX_FILESYSTEM_INFO_NAMESPACE,
543 NULL, NULL);
544}
545
546
547
548static GFile *
549thunar_file_info_get_location (ThunarxFileInfo *file_info)
550{
551 _thunar_return_val_if_fail (THUNAR_IS_FILE (file_info), NULL);
552 return g_object_ref (THUNAR_FILE (file_info)->gfile);
553}
554
555
556
557static void
558thunar_file_info_changed (ThunarxFileInfo *file_info)
559{
560 ThunarFile *file = THUNAR_FILE (file_info);
561
562 _thunar_return_if_fail (THUNAR_IS_FILE (file_info));
563
564 /* set the new thumbnail state manually, so we only emit file
565 * changed once */
566 FLAG_SET_THUMB_STATE (file, THUNAR_FILE_THUMB_STATE_UNKNOWN);
567
568 /* tell the file monitor that this file changed */
569 thunar_file_monitor_file_changed (file);
570}
571
572
573
574static gboolean
575thunar_file_denies_access_permission (const ThunarFile *file,
576 ThunarFileMode usr_permissions,
577 ThunarFileMode grp_permissions,
578 ThunarFileMode oth_permissions)
579{
580 ThunarFileMode mode;
581 ThunarGroup *group;
582 ThunarUser *user;
583 gboolean result;
584 GList *groups;
585 GList *lp;
586
587 /* query the file mode */
588 mode = thunar_file_get_mode (file);
589
590 /* query the owner of the file, if we cannot determine
591 * the owner, we can't tell if we're denied to access
592 * the file, so we simply return FALSE then.
593 */
594 user = thunar_file_get_user (file);
595 if (G_UNLIKELY (user == NULL))
596 return FALSE;
597
598 /* root is allowed to do everything */
599 if (G_UNLIKELY (effective_user_id == 0))
600 return FALSE;
601
602 if (thunar_user_is_me (user))
603 {
604 /* we're the owner, so the usr permissions must be granted */
605 result = ((mode & usr_permissions) == 0);
606
607 /* release the user */
608 g_object_unref (G_OBJECT (user));
609 }
610 else
611 {
612 group = thunar_file_get_group (file);
613 if (G_LIKELY (group != NULL))
614 {
615 /* release the file owner */
616 g_object_unref (G_OBJECT (user));
617
618 /* determine the effective user */
619 user = thunar_user_manager_get_user_by_id (user_manager, effective_user_id);
620 if (G_LIKELY (user != NULL))
621 {
622 /* check the group permissions */
623 groups = thunar_user_get_groups (user);
624 for (lp = groups; lp != NULL; lp = lp->next)
625 if (THUNAR_GROUP (lp->data) == group)
626 {
627 g_object_unref (G_OBJECT (user));
628 g_object_unref (G_OBJECT (group));
629 return ((mode & grp_permissions) == 0);
630 }
631
632 /* release the effective user */
633 g_object_unref (G_OBJECT (user));
634 }
635
636 /* release the file group */
637 g_object_unref (G_OBJECT (group));
638 }
639
640 /* check other permissions */
641 result = ((mode & oth_permissions) == 0);
642 }
643
644 return result;
645}
646
647
648
649static void
650thunar_file_monitor_update (GFile *path,
651 GFileMonitorEvent event_type)
652{
653 ThunarFile *file;
654
655 _thunar_return_if_fail (G_IS_FILE (path));
656 file = thunar_file_cache_lookup (path);
657 if (G_LIKELY (file != NULL))
658 {
659 switch (event_type)
660 {
661 case G_FILE_MONITOR_EVENT_CREATED:
662 case G_FILE_MONITOR_EVENT_CHANGED:
663 case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
664 thunar_file_reload (file);
665 break;
666
667 case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
668 case G_FILE_MONITOR_EVENT_DELETED:
669 thunar_file_reload (file);
670 break;
671
672 default:
673 break;
674 }
675 }
676}
677
678
679
680static void
681thunar_file_monitor (GFileMonitor *monitor,
682 GFile *path,
683 GFile *other_path,
684 GFileMonitorEvent event_type,
685 gpointer user_data)
686{
687 ThunarFile *file = THUNAR_FILE (user_data);
688
689 _thunar_return_if_fail (G_IS_FILE_MONITOR (monitor));
690 _thunar_return_if_fail (THUNAR_IS_FILE (file));
691
692 if (G_UNLIKELY (!G_IS_FILE (path) || !g_file_equal (path, file->gfile)))
693 return;
694
695 if (G_LIKELY (G_IS_FILE (path)))
696 thunar_file_monitor_update (path, event_type);
697
698 if (G_UNLIKELY (G_IS_FILE (other_path)))
699 thunar_file_monitor_update (other_path, event_type);
700}
701
702
703
704static void
705thunar_file_watch_destroyed (gpointer data)
706{
707 ThunarFileWatch *file_watch = data;
708
709 if (G_LIKELY (file_watch->monitor != NULL))
710 {
711 g_file_monitor_cancel (file_watch->monitor);
712 g_object_unref (file_watch->monitor);
713 }
714
715 g_slice_free (ThunarFileWatch, file_watch);
716}
717
718
719
720static void
721thunar_file_watch_reconnect (ThunarFile *file)
722{
723 ThunarFileWatch *file_watch;
724
725 /* recreate the monitor without changing the watch_count for file renames */
726 file_watch = g_object_get_qdata (G_OBJECT (file), thunar_file_watch_quark);
727 if (file_watch != NULL)
728 {
729 /* reset the old monitor */
730 if (G_LIKELY (file_watch->monitor != NULL))
731 {
732 g_file_monitor_cancel (file_watch->monitor);
733 g_object_unref (file_watch->monitor);
734 }
735
736 /* create a file or directory monitor */
737 file_watch->monitor = g_file_monitor (file->gfile, G_FILE_MONITOR_WATCH_MOUNTS, NULL, NULL);
738 if (G_LIKELY (file_watch->monitor != NULL))
739 {
740 /* watch monitor for file changes */
741 g_signal_connect (file_watch->monitor, "changed", G_CALLBACK (thunar_file_monitor), file);
742 }
743 }
744}
745
746
747
748static void
749thunar_file_set_emblem_names_ready (GObject *source_object,
750 GAsyncResult *result,
751 gpointer user_data)
752{
753 ThunarFile *file = THUNAR_FILE (user_data);
754 GError *error = NULL;
755
756 if (!g_file_set_attributes_finish (G_FILE (source_object), result, NULL, &error))
757 {
758 g_warning ("Failed to set metadata: %s", error->message);
759 g_error_free (error);
760
761 g_file_info_remove_attribute (file->info, "metadata::emblems");
762 }
763
764 thunar_file_changed (file);
765}
766
767
768
769static void
770thunar_file_info_clear (ThunarFile *file)
771{
772 _thunar_return_if_fail (THUNAR_IS_FILE (file));
773
774 /* release the current file info */
775 if (file->info != NULL)
776 {
777 g_object_unref (file->info);
778 file->info = NULL;
779 }
780
781 /* unset */
782 file->kind = G_FILE_TYPE_UNKNOWN;
783
784 /* free the custom icon name */
785 g_free (file->custom_icon_name);
786 file->custom_icon_name = NULL;
787
788 /* free display name and basename */
789 g_free (file->display_name);
790 file->display_name = NULL;
791
792 g_free (file->basename);
793 file->basename = NULL;
794
795 /* content type */
796 g_free (file->content_type);
797 file->content_type = NULL;
798 g_free (file->icon_name);
799 file->icon_name = NULL;
800
801 /* free collate keys */
802 if (file->collate_key_nocase != file->collate_key)
803 g_free (file->collate_key_nocase);
804 file->collate_key_nocase = NULL;
805
806 g_free (file->collate_key);
807 file->collate_key = NULL;
808
809 /* free thumbnail path */
810 g_free (file->thumbnail_path);
811 file->thumbnail_path = NULL;
812
813 /* assume the file is mounted by default */
814 FLAG_SET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
815
816 /* set thumb state to unknown */
817 FLAG_SET_THUMB_STATE (file, THUNAR_FILE_THUMB_STATE_UNKNOWN);
818}
819
820
821
822static void
823thunar_file_info_reload (ThunarFile *file,
824 GCancellable *cancellable)
825{
826 const gchar *target_uri;
827 GKeyFile *key_file;
828 gchar *p;
829 const gchar *display_name;
830 gboolean is_secure = FALSE;
831 gchar *casefold;
832 gchar *path;
833
834 _thunar_return_if_fail (THUNAR_IS_FILE (file));
835 _thunar_return_if_fail (file->info == NULL || G_IS_FILE_INFO (file->info));
836
837 if (G_LIKELY (file->info != NULL))
838 {
839 /* this is requesed so often, cache it */
840 file->kind = g_file_info_get_file_type (file->info);
841
842 if (file->kind == G_FILE_TYPE_MOUNTABLE)
843 {
844 target_uri = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
845 if (target_uri != NULL
846 && !g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT))
847 FLAG_SET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
848 else
849 FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
850 }
851 }
852
853 /* determine the basename */
854 file->basename = g_file_get_basename (file->gfile);
855 _thunar_assert (file->basename != NULL);
856
857 /* problematic files with content type reading */
858 if (strcmp (file->basename, "kmsg") == 0
859 && g_file_is_native (file->gfile))
860 {
861 path = g_file_get_path (file->gfile);
862 if (g_strcmp0 (path, "/proc/kmsg") == 0)
863 file->content_type = g_strdup (DEFAULT_CONTENT_TYPE);
864 g_free (path);
865 }
866
867 /* check if this file is a desktop entry */
868 if (thunar_file_is_desktop_file (file, &is_secure) && is_secure)
869 {
870 /* determine the custom icon and display name for .desktop files */
871
872 /* query a key file for the .desktop file */
873 key_file = thunar_g_file_query_key_file (file->gfile, cancellable, NULL);
874 if (key_file != NULL)
875 {
876 /* read the icon name from the .desktop file */
877 file->custom_icon_name = g_key_file_get_string (key_file,
878 G_KEY_FILE_DESKTOP_GROUP,
879 G_KEY_FILE_DESKTOP_KEY_ICON,
880 NULL);
881
882 if (G_UNLIKELY (exo_str_is_empty (file->custom_icon_name)))
883 {
884 /* make sure we set null if the string is empty else the assertion in
885 * thunar_icon_factory_lookup_icon() will fail */
886 g_free (file->custom_icon_name);
887 file->custom_icon_name = NULL;
888 }
889 else
890 {
891 /* drop any suffix (e.g. '.png') from themed icons */
892 if (!g_path_is_absolute (file->custom_icon_name))
893 {
894 p = strrchr (file->custom_icon_name, '.');
895 if (p != NULL)
896 *p = '\0';
897 }
898 }
899
900 /* read the display name from the .desktop file (will be overwritten later
901 * if it's undefined here) */
902 file->display_name = g_key_file_get_locale_string (key_file,
903 G_KEY_FILE_DESKTOP_GROUP,
904 G_KEY_FILE_DESKTOP_KEY_NAME,
905 NULL, NULL);
906
907 /* drop the name if it's empty or has invalid encoding */
908 if (exo_str_is_empty (file->display_name)
909 || !g_utf8_validate (file->display_name, -1, NULL))
910 {
911 g_free (file->display_name);
912 file->display_name = NULL;
913 }
914
915 /* free the key file */
916 g_key_file_free (key_file);
917 }
918 }
919
920 /* determine the display name */
921 if (file->display_name == NULL)
922 {
923 if (G_LIKELY (file->info != NULL))
924 {
925 display_name = g_file_info_get_display_name (file->info);
926 if (G_LIKELY (display_name != NULL))
927 {
928 if (strcmp (display_name, "/") == 0)
929 file->display_name = g_strdup (_("File System"));
930 else
931 file->display_name = g_strdup (display_name);
932 }
933 }
934
935 /* faccl back to a name for the gfile */
936 if (file->display_name == NULL)
937 file->display_name = thunar_g_file_get_display_name (file->gfile);
938 }
939
940 /* create case sensitive collation key */
941 file->collate_key = g_utf8_collate_key_for_filename (file->display_name, -1);
942
943 /* lowercase the display name */
944 casefold = g_utf8_casefold (file->display_name, -1);
945
946 /* if the lowercase name is equal, only peek the already hash key */
947 if (casefold != NULL && strcmp (casefold, file->display_name) != 0)
948 file->collate_key_nocase = g_utf8_collate_key_for_filename (casefold, -1);
949 else
950 file->collate_key_nocase = file->collate_key;
951
952 /* cleanup */
953 g_free (casefold);
954}
955
956
957
958static void
959thunar_file_get_async_finish (GObject *object,
960 GAsyncResult *result,
961 gpointer user_data)
962{
963 ThunarFileGetData *data = user_data;
964 ThunarFile *file;
965 GFileInfo *file_info;
966 GError *error = NULL;
967 GFile *location = G_FILE (object);
968
969 _thunar_return_if_fail (G_IS_FILE (location));
970 _thunar_return_if_fail (G_IS_ASYNC_RESULT (result));
971
972 /* finish querying the file information */
973 file_info = g_file_query_info_finish (location, result, &error);
974
975 /* allocate a new file object */
976 file = g_object_new (THUNAR_TYPE_FILE, NULL);
977 file->gfile = g_object_ref (location);
978
979 /* reset the file */
980 thunar_file_info_clear (file);
981
982 /* set the file information */
983 file->info = file_info;
984
985 /* update the file from the information */
986 thunar_file_info_reload (file, data->cancellable);
987
988 /* update the mounted info */
989 if (error != NULL
990 && error->domain == G_IO_ERROR
991 && error->code == G_IO_ERROR_NOT_MOUNTED)
992 {
993 FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
994 g_clear_error (&error);
995 }
996
997 /* insert the file into the cache */
998 G_LOCK (file_cache_mutex);
999#ifdef G_ENABLE_DEBUG
1000 /* check if there is no instance created in the meantime */
1001 _thunar_assert (g_hash_table_lookup (file_cache, file->gfile) == NULL);
1002#endif
1003 g_hash_table_insert (file_cache, g_object_ref (file->gfile), file);
1004 G_UNLOCK (file_cache_mutex);
1005
1006 /* pass the loaded file and possible errors to the return function */
1007 (data->func) (location, file, error, data->user_data);
1008
1009 /* release the file, see description in ThunarFileGetFunc */
1010 g_object_unref (file);
1011
1012 /* free the error, if there is any */
1013 if (error != NULL)
1014 g_error_free (error);
1015
1016 /* release the get data */
1017 if (data->cancellable != NULL)
1018 g_object_unref (data->cancellable);
1019 g_slice_free (ThunarFileGetData, data);
1020}
1021
1022
1023
1024/**
1025 * thunar_file_load:
1026 * @file : a #ThunarFile.
1027 * @cancellable : a #GCancellable.
1028 * @error : return location for errors or %NULL.
1029 *
1030 * Loads all information about the file. As this is a possibly
1031 * blocking call, it can be cancelled using @cancellable.
1032 *
1033 * If loading the file fails or the operation is cancelled,
1034 * @error will be set.
1035 *
1036 * Return value: %TRUE on success, %FALSE on error or interruption.
1037 **/
1038static gboolean
1039thunar_file_load (ThunarFile *file,
1040 GCancellable *cancellable,
1041 GError **error)
1042{
1043 GError *err = NULL;
1044
1045 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
1046 _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1047 _thunar_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1048 _thunar_return_val_if_fail (G_IS_FILE (file->gfile), FALSE);
1049
1050 /* reset the file */
1051 thunar_file_info_clear (file);
1052
1053 /* query a new file info */
1054 file->info = g_file_query_info (file->gfile,
1055 THUNARX_FILE_INFO_NAMESPACE,
1056 G_FILE_QUERY_INFO_NONE,
1057 cancellable, &err);
1058
1059 /* update the file from the information */
1060 thunar_file_info_reload (file, cancellable);
1061
1062 /* update the mounted info */
1063 if (err != NULL
1064 && err->domain == G_IO_ERROR
1065 && err->code == G_IO_ERROR_NOT_MOUNTED)
1066 {
1067 FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
1068 g_clear_error (&err);
1069 }
1070
1071 if (err != NULL)
1072 {
1073 g_propagate_error (error, err);
1074 return FALSE;
1075 }
1076 else
1077 {
1078 return TRUE;
1079 }
1080}
1081
1082
1083
1084/**
1085 * thunar_file_get:
1086 * @file : a #GFile.
1087 * @error : return location for errors.
1088 *
1089 * Looks up the #ThunarFile referred to by @file. This function may return a
1090 * ThunarFile even though the file doesn't actually exist. This is the case
1091 * with remote URIs (like SFTP) for instance, if they are not mounted.
1092 *
1093 * The caller is responsible to call g_object_unref()
1094 * when done with the returned object.
1095 *
1096 * Return value: the #ThunarFile for @file or %NULL on errors.
1097 **/
1098ThunarFile*
1099thunar_file_get (GFile *gfile,
1100 GError **error)
1101{
1102 ThunarFile *file;
1103
1104 _thunar_return_val_if_fail (G_IS_FILE (gfile), NULL);
1105
1106 /* check if we already have a cached version of that file */
1107 file = thunar_file_cache_lookup (gfile);
1108 if (G_UNLIKELY (file != NULL))
1109 {
1110 /* take a reference for the caller */
1111 g_object_ref (file);
1112 }
1113 else
1114 {
1115 /* allocate a new object */
1116 file = g_object_new (THUNAR_TYPE_FILE, NULL);
1117 file->gfile = g_object_ref (gfile);
1118
1119 if (thunar_file_load (file, NULL, error))
1120 {
1121 /* setup lock until the file is inserted */
1122 G_LOCK (file_cache_mutex);
1123
1124 /* insert the file into the cache */
1125 g_hash_table_insert (file_cache, g_object_ref (file->gfile), file);
1126
1127 /* done inserting in the cache */
1128 G_UNLOCK (file_cache_mutex);
1129 }
1130 else
1131 {
1132 /* failed loading, destroy the file */
1133 g_object_unref (file);
1134
1135 /* make sure we return NULL */
1136 file = NULL;
1137 }
1138 }
1139
1140 return file;
1141}
1142
1143
1144/**
1145 * thunar_file_get_with_info:
1146 * @uri : an URI or an absolute filename.
1147 * @info : #GFileInfo to use when loading the info.
1148 * @not_mounted : if the file is mounted.
1149 *
1150 * Looks up the #ThunarFile referred to by @file. This function may return a
1151 * ThunarFile even though the file doesn't actually exist. This is the case
1152 * with remote URIs (like SFTP) for instance, if they are not mounted.
1153 *
1154 * This function does not use g_file_query_info() to get the info,
1155 * but takes a reference on the @info,
1156 *
1157 * The caller is responsible to call g_object_unref()
1158 * when done with the returned object.
1159 *
1160 * Return value: the #ThunarFile for @file or %NULL on errors.
1161 **/
1162ThunarFile *
1163thunar_file_get_with_info (GFile *gfile,
1164 GFileInfo *info,
1165 gboolean not_mounted)
1166{
1167 ThunarFile *file;
1168
1169 _thunar_return_val_if_fail (G_IS_FILE (gfile), NULL);
1170 _thunar_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
1171
1172 /* check if we already have a cached version of that file */
1173 file = thunar_file_cache_lookup (gfile);
1174 if (G_UNLIKELY (file != NULL))
1175 {
1176 /* take a reference for the caller */
1177 g_object_ref (file);
1178 }
1179 else
1180 {
1181 /* allocate a new object */
1182 file = g_object_new (THUNAR_TYPE_FILE, NULL);
1183 file->gfile = g_object_ref (gfile);
1184
1185 /* reset the file */
1186 thunar_file_info_clear (file);
1187
1188 /* set the passed info */
1189 file->info = g_object_ref (info);
1190
1191 /* update the file from the information */
1192 thunar_file_info_reload (file, NULL);
1193
1194 /* update the mounted info */
1195 if (not_mounted)
1196 FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
1197
1198 /* setup lock until the file is inserted */
1199 G_LOCK (file_cache_mutex);
1200
1201 /* insert the file into the cache */
1202 g_hash_table_insert (file_cache, g_object_ref (file->gfile), file);
1203
1204 /* done inserting in the cache */
1205 G_UNLOCK (file_cache_mutex);
1206 }
1207
1208 return file;
1209}
1210
1211
1212
1213
1214
1215/**
1216 * thunar_file_get_for_uri:
1217 * @uri : an URI or an absolute filename.
1218 * @error : return location for errors or %NULL.
1219 *
1220 * Convenience wrapper function for thunar_file_get_for_path(), as its
1221 * often required to determine a #ThunarFile for a given @uri.
1222 *
1223 * The caller is responsible to free the returned object using
1224 * g_object_unref() when no longer needed.
1225 *
1226 * Return value: the #ThunarFile for the given @uri or %NULL if
1227 * unable to determine.
1228 **/
1229ThunarFile*
1230thunar_file_get_for_uri (const gchar *uri,
1231 GError **error)
1232{
1233 ThunarFile *file;
1234 GFile *path;
1235
1236 _thunar_return_val_if_fail (uri != NULL, NULL);
1237 _thunar_return_val_if_fail (error == NULL || *error == NULL, NULL);
1238
1239 path = g_file_new_for_commandline_arg (uri);
1240 file = thunar_file_get (path, error);
1241 g_object_unref (path);
1242
1243 return file;
1244}
1245
1246
1247
1248/**
1249 * thunar_file_get_async:
1250 **/
1251void
1252thunar_file_get_async (GFile *location,
1253 GCancellable *cancellable,
1254 ThunarFileGetFunc func,
1255 gpointer user_data)
1256{
1257 ThunarFile *file;
1258 ThunarFileGetData *data;
1259
1260 _thunar_return_if_fail (G_IS_FILE (location));
1261 _thunar_return_if_fail (func != NULL);
1262
1263 /* check if we already have a cached version of that file */
1264 file = thunar_file_cache_lookup (location);
1265 if (G_UNLIKELY (file != NULL))
1266 {
1267 /* call the return function with the file from the cache */
1268 (func) (location, file, NULL, user_data);
1269 }
1270 else
1271 {
1272 /* allocate get data */
1273 data = g_slice_new0 (ThunarFileGetData);
1274 data->user_data = user_data;
1275 data->func = func;
1276 if (cancellable != NULL)
1277 data->cancellable = g_object_ref (cancellable);
1278
1279 /* load the file information asynchronously */
1280 g_file_query_info_async (location,
1281 THUNARX_FILE_INFO_NAMESPACE,
1282 G_FILE_QUERY_INFO_NONE,
1283 G_PRIORITY_DEFAULT,
1284 cancellable,
1285 thunar_file_get_async_finish,
1286 data);
1287 }
1288}
1289
1290
1291
1292/**
1293 * thunar_file_get_file:
1294 * @file : a #ThunarFile instance.
1295 *
1296 * Returns the #GFile that refers to the location of @file.
1297 *
1298 * The returned #GFile is owned by @file and must not be released
1299 * with g_object_unref().
1300 *
1301 * Return value: the #GFile corresponding to @file.
1302 **/
1303GFile *
1304thunar_file_get_file (const ThunarFile *file)
1305{
1306 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
1307 _thunar_return_val_if_fail (G_IS_FILE (file->gfile), NULL);
1308 return file->gfile;
1309}
1310
1311
1312
1313/**
1314 * thunar_file_get_info:
1315 * @file : a #ThunarFile instance.
1316 *
1317 * Returns the #GFileInfo for @file.
1318 *
1319 * Note, that there's no reference taken for the caller on the
1320 * returned #GFileInfo, so if you need the object for a longer
1321 * perioud, you'll need to take a reference yourself using the
1322 * g_object_ref() method.
1323 *
1324 * Return value: the #GFileInfo for @file or %NULL.
1325 **/
1326GFileInfo *
1327thunar_file_get_info (const ThunarFile *file)
1328{
1329 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
1330 _thunar_return_val_if_fail (file->info == NULL || G_IS_FILE_INFO (file->info), NULL);
1331 return file->info;
1332}
1333
1334
1335
1336/**
1337 * thunar_file_get_parent:
1338 * @file : a #ThunarFile instance.
1339 * @error : return location for errors.
1340 *
1341 * Determines the parent #ThunarFile for @file. If @file has no parent or
1342 * the user is not allowed to open the parent folder of @file, %NULL will
1343 * be returned and @error will be set to point to a #GError that
1344 * describes the cause. Else, the #ThunarFile will be returned, and
1345 * the caller must call g_object_unref() on it.
1346 *
1347 * You may want to call thunar_file_has_parent() first to
1348 * determine whether @file has a parent.
1349 *
1350 * Return value: the parent #ThunarFile or %NULL.
1351 **/
1352ThunarFile*
1353thunar_file_get_parent (const ThunarFile *file,
1354 GError **error)
1355{
1356 ThunarFile *parent = NULL;
1357 GFile *parent_file;
1358
1359 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
1360 _thunar_return_val_if_fail (error == NULL || *error == NULL, NULL);
1361
1362 parent_file = g_file_get_parent (file->gfile);
1363
1364 if (parent_file == NULL)
1365 {
1366 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT, _("The root folder has no parent"));
1367 return NULL;
1368 }
1369
1370 parent = thunar_file_get (parent_file, error);
1371 g_object_unref (parent_file);
1372
1373 return parent;
1374}
1375
1376
1377
1378/**
1379 * thunar_file_check_loaded:
1380 * @file : a #ThunarFile instance.
1381 *
1382 * Check if @file has its information loaded, if not, try this once else
1383 * return %FALSE.
1384 *
1385 * Return value: %TRUE on success, else %FALSE.
1386 **/
1387gboolean
1388thunar_file_check_loaded (ThunarFile *file)
1389{
1390 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
1391
1392 if (G_UNLIKELY (file->info == NULL))
1393 thunar_file_load (file, NULL, NULL);
1394
1395 return (file->info != NULL);
1396}
1397
1398
1399
1400/**
1401 * thunar_file_execute:
1402 * @file : a #ThunarFile instance.
1403 * @working_directory : the working directory used to resolve relative filenames
1404 * in @file_list.
1405 * @parent : %NULL, a #GdkScreen or #GtkWidget.
1406 * @file_list : the list of #GFile<!---->s to supply to @file on execution.
1407 * @error : return location for errors or %NULL.
1408 *
1409 * Tries to execute @file on the specified @screen. If @file is executable
1410 * and could have been spawned successfully, %TRUE is returned, else %FALSE
1411 * will be returned and @error will be set to point to the error location.
1412 *
1413 * Return value: %TRUE on success, else %FALSE.
1414 **/
1415gboolean
1416thunar_file_execute (ThunarFile *file,
1417 GFile *working_directory,
1418 gpointer parent,
1419 GList *file_list,
1420 GError **error)
1421{
1422 gboolean snotify = FALSE;
1423 gboolean terminal;
1424 gboolean result = FALSE;
1425 GKeyFile *key_file;
1426 GError *err = NULL;
1427 GFile *file_parent;
1428 gchar *icon_name = NULL;
1429 gchar *name;
1430 gchar *type;
1431 gchar *url;
1432 gchar *location;
1433 gchar *escaped_location;
1434 gchar **argv = NULL;
1435 gchar *exec;
1436 gchar *directory = NULL;
1437 gboolean is_secure = FALSE;
1438
1439 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
1440 _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1441
1442 location = thunar_g_file_get_location (file->gfile);
1443
1444 if (thunar_file_is_desktop_file (file, &is_secure))
1445 {
1446 /* parse file first, even if it is insecure */
1447 key_file = thunar_g_file_query_key_file (file->gfile, NULL, &err);
1448 if (key_file == NULL)
1449 {
1450 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
1451 _("Failed to parse the desktop file: %s"), err->message);
1452 g_error_free (err);
1453 return FALSE;
1454 }
1455
1456 type = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TYPE, NULL);
1457 if (G_LIKELY (exo_str_is_equal (type, "Application")))
1458 {
1459 exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
1460 if (G_LIKELY (exec != NULL))
1461 {
1462 /* if the .desktop file is not secure, ask user what to do */
1463 if (is_secure || thunar_dialogs_show_insecure_program (parent, _("Untrusted application launcher"), file, exec))
1464 {
1465 /* parse other fields */
1466 name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
1467 icon_name = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
1468 directory = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
1469 terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL);
1470 snotify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL);
1471
1472 result = thunar_exec_parse (exec, file_list, icon_name, name, location, terminal, NULL, &argv, error);
1473
1474 g_free (name);
1475 }
1476 else
1477 {
1478 /* fall-through to free value and leave without execution */
1479 result = TRUE;
1480 }
1481
1482 g_free (exec);
1483 }
1484 else
1485 {
1486 /* TRANSLATORS: `Exec' is a field name in a .desktop file. Don't translate it. */
1487 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
1488 _("No Exec field specified"));
1489 }
1490 }
1491 else if (exo_str_is_equal (type, "Link"))
1492 {
1493 url = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, NULL);
1494 if (G_LIKELY (url != NULL))
1495 {
1496 /* if the .desktop file is not secure, ask user what to do */
1497 if (is_secure || thunar_dialogs_show_insecure_program (parent, _("Untrusted link launcher"), file, url))
1498 {
1499 /* pass the URL to the webbrowser, this could be a bit strange,
1500 * but then at least we are on the secure side */
1501 argv = g_new (gchar *, 3);
1502 argv[0] = g_strdup ("exo-open");
1503 argv[1] = url;
1504 argv[2] = NULL;
1505 }
1506
1507 result = TRUE;
1508 }
1509 else
1510 {
1511 /* TRANSLATORS: `URL' is a field name in a .desktop file. Don't translate it. */
1512 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
1513 _("No URL field specified"));
1514 }
1515 }
1516 else
1517 {
1518 g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, _("Invalid desktop file"));
1519 }
1520
1521 g_free (type);
1522 g_key_file_free (key_file);
1523 }
1524 else
1525 {
1526 /* fake the Exec line */
1527 escaped_location = g_shell_quote (location);
1528 exec = g_strconcat (escaped_location, " %F", NULL);
1529 result = thunar_exec_parse (exec, file_list, NULL, NULL, NULL, FALSE, NULL, &argv, error);
1530 g_free (escaped_location);
1531 g_free (exec);
1532 }
1533
1534 if (G_LIKELY (result && argv != NULL))
1535 {
1536 /* use other directory if the Path from the desktop file was not set */
1537 if (G_LIKELY (directory == NULL))
1538 {
1539 /* determine the working directory */
1540 if (G_LIKELY (working_directory != NULL))
1541 {
1542 /* copy the working directory provided to this method */
1543 directory = g_file_get_path (working_directory);
1544 }
1545 else if (file_list != NULL)
1546 {
1547 /* use the directory of the first list item */
1548 file_parent = g_file_get_parent (file_list->data);
1549 directory = (file_parent != NULL) ? thunar_g_file_get_location (file_parent) : NULL;
1550 g_object_unref (file_parent);
1551 }
1552 else
1553 {
1554 /* use the directory of the executable file */
1555 parent = g_file_get_parent (file->gfile);
1556 directory = (parent != NULL) ? thunar_g_file_get_location (parent) : NULL;
1557 g_object_unref (parent);
1558 }
1559 }
1560
1561 /* execute the command */
1562 result = xfce_spawn_on_screen (thunar_util_parse_parent (parent, NULL),
1563 directory, argv, NULL, G_SPAWN_SEARCH_PATH,
1564 snotify, gtk_get_current_event_time (), icon_name, error);
1565 }
1566
1567 /* clean up */
1568 g_strfreev (argv);
1569 g_free (location);
1570 g_free (directory);
1571 g_free (icon_name);
1572
1573 return result;
1574}
1575
1576
1577
1578/**
1579 * thunar_file_launch:
1580 * @file : a #ThunarFile instance.
1581 * @parent : a #GtkWidget or a #GdkScreen on which to launch the @file.
1582 * May also be %NULL in which case the default #GdkScreen will
1583 * be used.
1584 * @startup_id : startup id for the new window (send over for dbus) or %NULL.
1585 * @error : return location for errors or %NULL.
1586 *
1587 * If @file is an executable file, tries to execute it. Else if @file is
1588 * a directory, opens a new #ThunarWindow to display the directory. Else,
1589 * the default handler for @file is determined and run.
1590 *
1591 * The @parent can be either a #GtkWidget or a #GdkScreen, on which to
1592 * launch the @file. If @parent is a #GtkWidget, the chooser dialog (if
1593 * no default application is available for @file) will be transient for
1594 * @parent. Else if @parent is a #GdkScreen it specifies the screen on
1595 * which to launch @file.
1596 *
1597 * Return value: %TRUE on success, else %FALSE.
1598 **/
1599gboolean
1600thunar_file_launch (ThunarFile *file,
1601 gpointer parent,
1602 const gchar *startup_id,
1603 GError **error)
1604{
1605 GdkAppLaunchContext *context;
1606 ThunarApplication *application;
1607 GAppInfo *app_info;
1608 gboolean succeed;
1609 GList path_list;
1610 GdkScreen *screen;
1611
1612 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
1613 _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1614 _thunar_return_val_if_fail (parent == NULL || GDK_IS_SCREEN (parent) || GTK_IS_WIDGET (parent), FALSE);
1615
1616 screen = thunar_util_parse_parent (parent, NULL);
1617
1618 /* check if we have a folder here */
1619 if (thunar_file_is_directory (file))
1620 {
1621 application = thunar_application_get ();
1622 thunar_application_open_window (application, file, screen, startup_id);
1623 g_object_unref (G_OBJECT (application));
1624 return TRUE;
1625 }
1626
1627 /* check if we should execute the file */
1628 if (thunar_file_is_executable (file))
1629 return thunar_file_execute (file, NULL, parent, NULL, error);
1630
1631 /* determine the default application to open the file */
1632 /* TODO We should probably add a cancellable argument to thunar_file_launch() */
1633 app_info = thunar_file_get_default_handler (THUNAR_FILE (file));
1634
1635 /* display the application chooser if no application is defined for this file
1636 * type yet */
1637 if (G_UNLIKELY (app_info == NULL))
1638 {
1639 thunar_show_chooser_dialog (parent, file, TRUE);
1640 return TRUE;
1641 }
1642
1643 /* HACK: check if we're not trying to launch another file manager again, possibly
1644 * ourselfs which will end in a loop */
1645 if (g_strcmp0 (g_app_info_get_id (app_info), "exo-file-manager.desktop") == 0
1646 || g_strcmp0 (g_app_info_get_id (app_info), "Thunar.desktop") == 0
1647 || g_strcmp0 (g_app_info_get_name (app_info), "exo-file-manager") == 0)
1648 {
1649 g_object_unref (G_OBJECT (app_info));
1650 thunar_show_chooser_dialog (parent, file, TRUE);
1651 return TRUE;
1652 }
1653
1654 /* fake a path list */
1655 path_list.data = file->gfile;
1656 path_list.next = path_list.prev = NULL;
1657
1658 /* create a launch context */
1659 context = gdk_app_launch_context_new ();
1660 gdk_app_launch_context_set_screen (context, screen);
1661 gdk_app_launch_context_set_timestamp (context, gtk_get_current_event_time ());
1662
1663 /* otherwise try to execute the application */
1664 succeed = g_app_info_launch (app_info, &path_list, G_APP_LAUNCH_CONTEXT (context), error);
1665
1666 /* destroy the launch context */
1667 g_object_unref (context);
1668
1669 /* release the handler reference */
1670 g_object_unref (G_OBJECT (app_info));
1671
1672 return succeed;
1673}
1674
1675
1676
1677/**
1678 * thunar_file_rename:
1679 * @file : a #ThunarFile instance.
1680 * @name : the new file name in UTF-8 encoding.
1681 * @error : return location for errors or %NULL.
1682 *
1683 * Tries to rename @file to the new @name. If @file cannot be renamed,
1684 * %FALSE will be returned and @error will be set accordingly. Else, if
1685 * the operation succeeds, %TRUE will be returned, and @file will have
1686 * a new URI and a new display name.
1687 *
1688 * When offering a rename action in the user interface, the implementation
1689 * should first check whether the file is available, using the
1690 * thunar_file_is_renameable() method.
1691 *
1692 * Return value: %TRUE on success, else %FALSE.
1693 **/
1694gboolean
1695thunar_file_rename (ThunarFile *file,
1696 const gchar *name,
1697 GCancellable *cancellable,
1698 gboolean called_from_job,
1699 GError **error)
1700{
1701 ThunarApplication *application;
1702 ThunarThumbnailCache *thumbnail_cache;
1703 GKeyFile *key_file;
1704 GError *err = NULL;
1705 GFile *previous_file;
1706 GFile *renamed_file;
1707 gboolean is_secure;
1708 const gchar * const *languages;
1709 guint i;
1710 gboolean name_set = FALSE;
1711
1712 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
1713 _thunar_return_val_if_fail (g_utf8_validate (name, -1, NULL), FALSE);
1714 _thunar_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
1715 _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1716
1717 /* check if this file is a desktop entry */
1718 if (thunar_file_is_desktop_file (file, &is_secure)
1719 && is_secure)
1720 {
1721 /* try to load the desktop entry into a key file */
1722 key_file = thunar_g_file_query_key_file (file->gfile, cancellable, &err);
1723 if (key_file == NULL)
1724 {
1725 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
1726 _("Failed to parse the desktop file: %s"), err->message);
1727 g_error_free (err);
1728 return FALSE;
1729 }
1730
1731 /* check if we can set the language name */
1732 languages = g_get_language_names ();
1733 if (languages != NULL)
1734 {
1735 for (i = 0; !name_set && languages[i] != NULL; i++)
1736 {
1737 /* skip C language */
1738 if (g_ascii_strcasecmp (languages[i], "C") == 0)
1739 continue;
1740
1741 /* change the translated Name field of the desktop entry */
1742 g_key_file_set_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1743 G_KEY_FILE_DESKTOP_KEY_NAME,
1744 languages[i], name);
1745
1746 /* done */
1747 name_set = TRUE;
1748 }
1749 }
1750
1751 if (!name_set)
1752 {
1753 /* change the Name field of the desktop entry */
1754 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1755 G_KEY_FILE_DESKTOP_KEY_NAME, name);
1756 }
1757
1758 /* write the changes back to the file */
1759 if (thunar_g_file_write_key_file (file->gfile, key_file, cancellable, &err))
1760 {
1761 /* reload file information */
1762 thunar_file_load (file, NULL, NULL);
1763
1764 if (!called_from_job)
1765 {
1766 /* tell the associated folder that the file was renamed */
1767 thunarx_file_info_renamed (THUNARX_FILE_INFO (file));
1768
1769 /* notify everybody that the file has changed */
1770 thunar_file_changed (file);
1771 }
1772
1773 /* release the key file and return with success */
1774 g_key_file_free (key_file);
1775 return TRUE;
1776 }
1777 else
1778 {
1779 /* propagate the error message and return with failure */
1780 g_propagate_error (error, err);
1781 g_key_file_free (key_file);
1782 return FALSE;
1783 }
1784 }
1785 else
1786 {
1787 /* remember the previous file */
1788 previous_file = g_object_ref (file->gfile);
1789
1790 /* try to rename the file */
1791 renamed_file = g_file_set_display_name (file->gfile, name, cancellable, error);
1792
1793 /* notify the thumbnail cache that we can now also move the thumbnail */
1794 application = thunar_application_get ();
1795 thumbnail_cache = thunar_application_get_thumbnail_cache (application);
1796 thunar_thumbnail_cache_move_file (thumbnail_cache, previous_file, renamed_file);
1797 g_object_unref (thumbnail_cache);
1798 g_object_unref (application);
1799
1800 /* check if we succeeded */
1801 if (renamed_file != NULL)
1802 {
1803 /* set the new file */
1804 file->gfile = renamed_file;
1805
1806 /* reload file information */
1807 thunar_file_load (file, NULL, NULL);
1808
1809 /* need to re-register the monitor handle for the new uri */
1810 thunar_file_watch_reconnect (file);
1811
1812 G_LOCK (file_cache_mutex);
1813
1814 /* drop the previous entry from the cache */
1815 g_hash_table_remove (file_cache, previous_file);
1816
1817 /* drop the reference on the previous file */
1818 g_object_unref (previous_file);
1819
1820 /* insert the new entry */
1821 g_hash_table_insert (file_cache, g_object_ref (file->gfile), file);
1822
1823 G_UNLOCK (file_cache_mutex);
1824
1825 if (!called_from_job)
1826 {
1827 /* tell the associated folder that the file was renamed */
1828 thunarx_file_info_renamed (THUNARX_FILE_INFO (file));
1829
1830 /* emit the file changed signal */
1831 thunar_file_changed (file);
1832 }
1833
1834 return TRUE;
1835 }
1836 else
1837 {
1838 g_object_unref (previous_file);
1839
1840 return FALSE;
1841 }
1842 }
1843}
1844
1845
1846
1847/**
1848 * thunar_file_accepts_drop:
1849 * @file : a #ThunarFile instance.
1850 * @file_list : the list of #GFile<!---->s that will be droppped.
1851 * @context : the current #GdkDragContext, which is used for the drop.
1852 * @suggested_action_return : return location for the suggested #GdkDragAction or %NULL.
1853 *
1854 * Checks whether @file can accept @path_list for the given @context and
1855 * returns the #GdkDragAction<!---->s that can be used or 0 if no actions
1856 * apply.
1857 *
1858 * If any #GdkDragAction<!---->s apply and @suggested_action_return is not
1859 * %NULL, the suggested #GdkDragAction for this drop will be stored to the
1860 * location pointed to by @suggested_action_return.
1861 *
1862 * Return value: the #GdkDragAction<!---->s supported for the drop or
1863 * 0 if no drop is possible.
1864 **/
1865GdkDragAction
1866thunar_file_accepts_drop (ThunarFile *file,
1867 GList *file_list,
1868 GdkDragContext *context,
1869 GdkDragAction *suggested_action_return)
1870{
1871 GdkDragAction suggested_action;
1872 GdkDragAction actions;
1873 ThunarFile *ofile;
1874 GFile *parent_file;
1875 GList *lp;
1876 guint n;
1877
1878 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0);
1879 _thunar_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), 0);
1880
1881 /* we can never drop an empty list */
1882 if (G_UNLIKELY (file_list == NULL))
1883 return 0;
1884
1885 /* default to whatever GTK+ thinks for the suggested action */
1886 suggested_action = context->suggested_action;
1887
1888 /* check if we have a writable directory here or an executable file */
1889 if (thunar_file_is_directory (file) && thunar_file_is_writable (file))
1890 {
1891 /* determine the possible actions */
1892 actions = context->actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
1893
1894 /* cannot create symbolic links in the trash or copy to the trash */
1895 if (thunar_file_is_trashed (file))
1896 actions &= ~(GDK_ACTION_COPY | GDK_ACTION_LINK);
1897
1898 /* check up to 100 of the paths (just in case somebody tries to
1899 * drag around his music collection with 5000 files).
1900 */
1901 for (lp = file_list, n = 0; lp != NULL && n < 100; lp = lp->next, ++n)
1902 {
1903 /* we cannot drop a file on itself */
1904 if (G_UNLIKELY (g_file_equal (file->gfile, lp->data)))
1905 return 0;
1906
1907 /* check whether source and destination are the same */
1908 parent_file = g_file_get_parent (lp->data);
1909 if (G_LIKELY (parent_file != NULL))
1910 {
1911 if (g_file_equal (file->gfile, parent_file))
1912 {
1913 g_object_unref (parent_file);
1914 return 0;
1915 }
1916 else
1917 g_object_unref (parent_file);
1918 }
1919
1920 /* copy/move/link within the trash not possible */
1921 if (G_UNLIKELY (thunar_g_file_is_trashed (lp->data) && thunar_file_is_trashed (file)))
1922 return 0;
1923 }
1924
1925 /* if the source offers both copy and move and the GTK+ suggested action is copy, try to be smart telling whether
1926 * we should copy or move by default by checking whether the source and target are on the same disk.
1927 */
1928 if ((actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE)) != 0
1929 && (suggested_action == GDK_ACTION_COPY))
1930 {
1931 /* default to move as suggested action */
1932 suggested_action = GDK_ACTION_MOVE;
1933
1934 /* check for up to 100 files, for the reason state above */
1935 for (lp = file_list, n = 0; lp != NULL && n < 100; lp = lp->next, ++n)
1936 {
1937 /* dropping from the trash always suggests move */
1938 if (G_UNLIKELY (thunar_g_file_is_trashed (lp->data)))
1939 break;
1940
1941 /* determine the cached version of the source file */
1942 ofile = thunar_file_cache_lookup (lp->data);
1943
1944 /* we have only move if we know the source and both the source and the target
1945 * are on the same disk, and the source file is owned by the current user.
1946 */
1947 if (ofile == NULL
1948 || !thunar_file_same_filesystem (file, ofile)
1949 || (ofile->info != NULL
1950 && g_file_info_get_attribute_uint32 (ofile->info,
1951 G_FILE_ATTRIBUTE_UNIX_UID) != effective_user_id))
1952 {
1953 /* default to copy and get outa here */
1954 suggested_action = GDK_ACTION_COPY;
1955 break;
1956 }
1957 }
1958 }
1959 }
1960 else if (thunar_file_is_executable (file))
1961 {
1962 /* determine the possible actions */
1963 actions = context->actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
1964 }
1965 else
1966 return 0;
1967
1968 /* determine the preferred action based on the context */
1969 if (G_LIKELY (suggested_action_return != NULL))
1970 {
1971 /* determine a working action */
1972 if (G_LIKELY ((suggested_action & actions) != 0))
1973 *suggested_action_return = suggested_action;
1974 else if ((actions & GDK_ACTION_ASK) != 0)
1975 *suggested_action_return = GDK_ACTION_ASK;
1976 else if ((actions & GDK_ACTION_COPY) != 0)
1977 *suggested_action_return = GDK_ACTION_COPY;
1978 else if ((actions & GDK_ACTION_LINK) != 0)
1979 *suggested_action_return = GDK_ACTION_LINK;
1980 else if ((actions & GDK_ACTION_MOVE) != 0)
1981 *suggested_action_return = GDK_ACTION_MOVE;
1982 else
1983 *suggested_action_return = GDK_ACTION_PRIVATE;
1984 }
1985
1986 /* yeppa, we can drop here */
1987 return actions;
1988}
1989
1990
1991
1992/**
1993 * thunar_file_get_date:
1994 * @file : a #ThunarFile instance.
1995 * @date_type : the kind of date you are interested in.
1996 *
1997 * Queries the given @date_type from @file and returns the result.
1998 *
1999 * Return value: the time for @file of the given @date_type.
2000 **/
2001guint64
2002thunar_file_get_date (const ThunarFile *file,
2003 ThunarFileDateType date_type)
2004{
2005 const gchar *attribute;
2006
2007 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0);
2008
2009 if (file->info == NULL)
2010 return 0;
2011
2012 switch (date_type)
2013 {
2014 case THUNAR_FILE_DATE_ACCESSED:
2015 attribute = G_FILE_ATTRIBUTE_TIME_ACCESS;
2016 break;
2017 case THUNAR_FILE_DATE_CHANGED:
2018 attribute = G_FILE_ATTRIBUTE_TIME_CHANGED;
2019 break;
2020 case THUNAR_FILE_DATE_MODIFIED:
2021 attribute = G_FILE_ATTRIBUTE_TIME_MODIFIED;
2022 break;
2023 default:
2024 _thunar_assert_not_reached ();
2025 }
2026
2027 return g_file_info_get_attribute_uint64 (file->info, attribute);
2028}
2029
2030
2031
2032/**
2033 * thunar_file_get_date_string:
2034 * @file : a #ThunarFile instance.
2035 * @date_type : the kind of date you are interested to know about @file.
2036 * @date_style : the style used to format the date.
2037 *
2038 * Tries to determine the @date_type of @file, and if @file supports the
2039 * given @date_type, it'll be formatted as string and returned. The
2040 * caller is responsible for freeing the string using the g_free()
2041 * function.
2042 *
2043 * Return value: the @date_type of @file formatted as string.
2044 **/
2045gchar*
2046thunar_file_get_date_string (const ThunarFile *file,
2047 ThunarFileDateType date_type,
2048 ThunarDateStyle date_style)
2049{
2050 return thunar_util_humanize_file_time (thunar_file_get_date (file, date_type), date_style);
2051}
2052
2053
2054
2055/**
2056 * thunar_file_get_mode_string:
2057 * @file : a #ThunarFile instance.
2058 *
2059 * Returns the mode of @file as text. You'll need to free
2060 * the result using g_free() when you're done with it.
2061 *
2062 * Return value: the mode of @file as string.
2063 **/
2064gchar*
2065thunar_file_get_mode_string (const ThunarFile *file)
2066{
2067 ThunarFileMode mode;
2068 GFileType kind;
2069 gchar *text;
2070
2071 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2072
2073 kind = thunar_file_get_kind (file);
2074 mode = thunar_file_get_mode (file);
2075 text = g_new (gchar, 11);
2076
2077 /* file type */
2078 /* TODO earlier versions of Thunar had 'P' for ports and
2079 * 'D' for doors. Do we still need those? */
2080 switch (kind)
2081 {
2082 case G_FILE_TYPE_SYMBOLIC_LINK: text[0] = 'l'; break;
2083 case G_FILE_TYPE_REGULAR: text[0] = '-'; break;
2084 case G_FILE_TYPE_DIRECTORY: text[0] = 'd'; break;
2085 case G_FILE_TYPE_SPECIAL:
2086 case G_FILE_TYPE_UNKNOWN:
2087 default:
2088 if (S_ISCHR (mode))
2089 text[0] = 'c';
2090 else if (S_ISSOCK (mode))
2091 text[0] = 's';
2092 else if (S_ISFIFO (mode))
2093 text[0] = 'f';
2094 else if (S_ISBLK (mode))
2095 text[0] = 'b';
2096 else
2097 text[0] = ' ';
2098 }
2099
2100 /* permission flags */
2101 text[1] = (mode & THUNAR_FILE_MODE_USR_READ) ? 'r' : '-';
2102 text[2] = (mode & THUNAR_FILE_MODE_USR_WRITE) ? 'w' : '-';
2103 text[3] = (mode & THUNAR_FILE_MODE_USR_EXEC) ? 'x' : '-';
2104 text[4] = (mode & THUNAR_FILE_MODE_GRP_READ) ? 'r' : '-';
2105 text[5] = (mode & THUNAR_FILE_MODE_GRP_WRITE) ? 'w' : '-';
2106 text[6] = (mode & THUNAR_FILE_MODE_GRP_EXEC) ? 'x' : '-';
2107 text[7] = (mode & THUNAR_FILE_MODE_OTH_READ) ? 'r' : '-';
2108 text[8] = (mode & THUNAR_FILE_MODE_OTH_WRITE) ? 'w' : '-';
2109 text[9] = (mode & THUNAR_FILE_MODE_OTH_EXEC) ? 'x' : '-';
2110
2111 /* special flags */
2112 if (G_UNLIKELY (mode & THUNAR_FILE_MODE_SUID))
2113 text[3] = 's';
2114 if (G_UNLIKELY (mode & THUNAR_FILE_MODE_SGID))
2115 text[6] = 's';
2116 if (G_UNLIKELY (mode & THUNAR_FILE_MODE_STICKY))
2117 text[9] = 't';
2118
2119 text[10] = '\0';
2120
2121 return text;
2122}
2123
2124
2125
2126/**
2127 * thunar_file_get_size_string:
2128 * @file : a #ThunarFile instance.
2129 *
2130 * Returns the size of the file as text in a human readable
2131 * format. You'll need to free the result using g_free()
2132 * if you're done with it.
2133 *
2134 * Return value: the size of @file in a human readable
2135 * format.
2136 **/
2137gchar *
2138thunar_file_get_size_string (const ThunarFile *file)
2139{
2140 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2141 return g_format_size (thunar_file_get_size (file));
2142}
2143
2144
2145
2146/**
2147 * thunar_file_get_volume:
2148 * @file : a #ThunarFile instance.
2149 *
2150 * Attempts to determine the #GVolume on which @file is located. If @file cannot
2151 * determine it's volume, then %NULL will be returned. Else a #GVolume instance
2152 * is returned which has to be released by the caller using g_object_unref().
2153 *
2154 * Return value: the #GVolume for @file or %NULL.
2155 **/
2156GVolume*
2157thunar_file_get_volume (const ThunarFile *file)
2158{
2159 GVolume *volume = NULL;
2160 GMount *mount;
2161
2162 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2163
2164 /* TODO make this function call asynchronous */
2165 mount = g_file_find_enclosing_mount (file->gfile, NULL, NULL);
2166 if (mount != NULL)
2167 {
2168 volume = g_mount_get_volume (mount);
2169 g_object_unref (mount);
2170 }
2171
2172 return volume;
2173}
2174
2175
2176
2177/**
2178 * thunar_file_get_group:
2179 * @file : a #ThunarFile instance.
2180 *
2181 * Determines the #ThunarGroup for @file. If there's no
2182 * group associated with @file or if the system is unable to
2183 * determine the group, %NULL will be returned.
2184 *
2185 * The caller is responsible for freeing the returned object
2186 * using g_object_unref().
2187 *
2188 * Return value: the #ThunarGroup for @file or %NULL.
2189 **/
2190ThunarGroup *
2191thunar_file_get_group (const ThunarFile *file)
2192{
2193 guint32 gid;
2194
2195 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2196
2197 /* TODO what are we going to do on non-UNIX systems? */
2198 gid = g_file_info_get_attribute_uint32 (file->info,
2199 G_FILE_ATTRIBUTE_UNIX_GID);
2200
2201 return thunar_user_manager_get_group_by_id (user_manager, gid);
2202}
2203
2204
2205
2206/**
2207 * thunar_file_get_user:
2208 * @file : a #ThunarFile instance.
2209 *
2210 * Determines the #ThunarUser for @file. If there's no
2211 * user associated with @file or if the system is unable
2212 * to determine the user, %NULL will be returned.
2213 *
2214 * The caller is responsible for freeing the returned object
2215 * using g_object_unref().
2216 *
2217 * Return value: the #ThunarUser for @file or %NULL.
2218 **/
2219ThunarUser*
2220thunar_file_get_user (const ThunarFile *file)
2221{
2222 guint32 uid;
2223
2224 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2225
2226 /* TODO what are we going to do on non-UNIX systems? */
2227 uid = g_file_info_get_attribute_uint32 (file->info,
2228 G_FILE_ATTRIBUTE_UNIX_UID);
2229
2230 return thunar_user_manager_get_user_by_id (user_manager, uid);
2231}
2232
2233
2234
2235/**
2236 * thunar_file_get_content_type:
2237 * @file : a #ThunarFile.
2238 *
2239 * Returns the content type of @file.
2240 *
2241 * Return value: content type of @file.
2242 **/
2243const gchar *
2244thunar_file_get_content_type (ThunarFile *file)
2245{
2246 GFileInfo *info;
2247 GError *err = NULL;
2248 const gchar *content_type = NULL;
2249
2250 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2251
2252 if (G_UNLIKELY (file->content_type == NULL))
2253 {
2254 G_LOCK (file_content_type_mutex);
2255
2256 /* make sure we weren't waiting for a lock */
2257 if (G_UNLIKELY (file->content_type != NULL))
2258 goto bailout;
2259
2260 /* make sure this is not loaded in the general info */
2261 _thunar_assert (file->info == NULL
2262 || !g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE));
2263
2264 if (G_UNLIKELY (file->kind == G_FILE_TYPE_DIRECTORY))
2265 {
2266 /* this we known for sure */
2267 file->content_type = g_strdup ("inode/directory");
2268 }
2269 else
2270 {
2271 /* async load the content-type */
2272 info = g_file_query_info (file->gfile,
2273 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
2274 G_FILE_QUERY_INFO_NONE,
2275 NULL, &err);
2276
2277 if (G_LIKELY (info != NULL))
2278 {
2279 /* store the new content type */
2280 content_type = g_file_info_get_content_type (info);
2281 if (G_UNLIKELY (content_type != NULL))
2282 file->content_type = g_strdup (content_type);
2283 g_object_unref (G_OBJECT (info));
2284 }
2285 else
2286 {
2287 g_warning ("Content type loading failed for %s: %s",
2288 thunar_file_get_display_name (file),
2289 err->message);
2290 g_error_free (err);
2291 }
2292
2293 /* always provide a fallback */
2294 if (file->content_type == NULL)
2295 file->content_type = g_strdup (DEFAULT_CONTENT_TYPE);
2296 }
2297
2298 bailout:
2299
2300 G_UNLOCK (file_content_type_mutex);
2301 }
2302
2303 return file->content_type;
2304}
2305
2306
2307
2308gboolean
2309thunar_file_load_content_type (ThunarFile *file)
2310{
2311 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), TRUE);
2312
2313 if (file->content_type != NULL)
2314 return FALSE;
2315
2316 thunar_file_get_content_type (file);
2317
2318 return TRUE;
2319}
2320
2321
2322
2323/**
2324 * thunar_file_get_symlink_target:
2325 * @file : a #ThunarFile.
2326 *
2327 * Returns the path of the symlink target or %NULL if the @file
2328 * is not a symlink.
2329 *
2330 * Return value: path of the symlink target or %NULL.
2331 **/
2332const gchar *
2333thunar_file_get_symlink_target (const ThunarFile *file)
2334{
2335 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2336
2337 if (file->info == NULL)
2338 return NULL;
2339
2340 return g_file_info_get_symlink_target (file->info);
2341}
2342
2343
2344
2345/**
2346 * thunar_file_get_basename:
2347 * @file : a #ThunarFile.
2348 *
2349 * Returns the basename of the @file in UTF-8 encoding.
2350 *
2351 * Return value: UTF-8 encoded basename of the @file.
2352 **/
2353const gchar *
2354thunar_file_get_basename (const ThunarFile *file)
2355{
2356 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2357 return file->basename;
2358}
2359
2360
2361
2362/**
2363 * thunar_file_is_symlink:
2364 * @file : a #ThunarFile.
2365 *
2366 * Returns %TRUE if @file is a symbolic link.
2367 *
2368 * Return value: %TRUE if @file is a symbolic link.
2369 **/
2370gboolean
2371thunar_file_is_symlink (const ThunarFile *file)
2372{
2373 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2374
2375 if (file->info == NULL)
2376 return FALSE;
2377
2378 return g_file_info_get_is_symlink (file->info);
2379}
2380
2381
2382
2383/**
2384 * thunar_file_get_size:
2385 * @file : a #ThunarFile instance.
2386 *
2387 * Tries to determine the size of @file in bytes and
2388 * returns the size.
2389 *
2390 * Return value: the size of @file in bytes.
2391 **/
2392guint64
2393thunar_file_get_size (const ThunarFile *file)
2394{
2395 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0);
2396
2397 if (file->info == NULL)
2398 return 0;
2399
2400 return g_file_info_get_size (file->info);
2401}
2402
2403
2404
2405/**
2406 * thunar_file_get_default_handler:
2407 * @file : a #ThunarFile instance.
2408 *
2409 * Returns the default #GAppInfo for @file or %NULL if there is none.
2410 *
2411 * The caller is responsible to free the returned #GAppInfo using
2412 * g_object_unref().
2413 *
2414 * Return value: Default #GAppInfo for @file or %NULL if there is none.
2415 **/
2416GAppInfo *
2417thunar_file_get_default_handler (const ThunarFile *file)
2418{
2419 const gchar *content_type;
2420 GAppInfo *app_info = NULL;
2421 gboolean must_support_uris = FALSE;
2422 gchar *path;
2423
2424 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2425
2426 content_type = thunar_file_get_content_type (THUNAR_FILE (file));
2427 if (content_type != NULL)
2428 {
2429 path = g_file_get_path (file->gfile);
2430 must_support_uris = (path == NULL);
2431 g_free (path);
2432
2433 app_info = g_app_info_get_default_for_type (content_type, must_support_uris);
2434 }
2435
2436 if (app_info == NULL)
2437 app_info = g_file_query_default_handler (file->gfile, NULL, NULL);
2438
2439 return app_info;
2440}
2441
2442
2443
2444/**
2445 * thunar_file_get_kind:
2446 * @file : a #ThunarFile instance.
2447 *
2448 * Returns the kind of @file.
2449 *
2450 * Return value: the kind of @file.
2451 **/
2452GFileType
2453thunar_file_get_kind (const ThunarFile *file)
2454{
2455 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), G_FILE_TYPE_UNKNOWN);
2456 return file->kind;
2457}
2458
2459
2460
2461GFile *
2462thunar_file_get_target_location (const ThunarFile *file)
2463{
2464 const gchar *uri;
2465
2466 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2467
2468 if (file->info == NULL)
2469 return g_object_ref (file->gfile);
2470
2471 uri = g_file_info_get_attribute_string (file->info,
2472 G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
2473
2474 return (uri != NULL) ? g_file_new_for_uri (uri) : NULL;
2475}
2476
2477
2478
2479/**
2480 * thunar_file_get_mode:
2481 * @file : a #ThunarFile instance.
2482 *
2483 * Returns the permission bits of @file.
2484 *
2485 * Return value: the permission bits of @file.
2486 **/
2487ThunarFileMode
2488thunar_file_get_mode (const ThunarFile *file)
2489{
2490 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0);
2491
2492 if (file->info == NULL)
2493 return 0;
2494
2495 if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_UNIX_MODE))
2496 return g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_MODE);
2497 else
2498 return thunar_file_is_directory (file) ? 0777 : 0666;
2499}
2500
2501
2502
2503gboolean
2504thunar_file_is_mounted (const ThunarFile *file)
2505{
2506 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2507 return FLAG_IS_SET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
2508}
2509
2510
2511
2512gboolean
2513thunar_file_exists (const ThunarFile *file)
2514{
2515 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2516 return g_file_query_exists (file->gfile, NULL);
2517}
2518
2519
2520
2521/**
2522 * thunar_file_is_directory:
2523 * @file : a #ThunarFile instance.
2524 *
2525 * Checks whether @file refers to a directory.
2526 *
2527 * Return value: %TRUE if @file is a directory.
2528 **/
2529gboolean
2530thunar_file_is_directory (const ThunarFile *file)
2531{
2532 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2533 return file->kind == G_FILE_TYPE_DIRECTORY;
2534}
2535
2536
2537
2538/**
2539 * thunar_file_is_shortcut:
2540 * @file : a #ThunarFile instance.
2541 *
2542 * Checks whether @file refers to a shortcut to something else.
2543 *
2544 * Return value: %TRUE if @file is a shortcut.
2545 **/
2546gboolean
2547thunar_file_is_shortcut (const ThunarFile *file)
2548{
2549 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2550 return file->kind == G_FILE_TYPE_SHORTCUT;
2551}
2552
2553
2554
2555/**
2556 * thunar_file_is_mountable:
2557 * @file : a #ThunarFile instance.
2558 *
2559 * Checks whether @file refers to a mountable file/directory.
2560 *
2561 * Return value: %TRUE if @file is a mountable file/directory.
2562 **/
2563gboolean
2564thunar_file_is_mountable (const ThunarFile *file)
2565{
2566 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2567 return file->kind == G_FILE_TYPE_MOUNTABLE;
2568}
2569
2570
2571
2572/**
2573 * thunar_file_is_local:
2574 * @file : a #ThunarFile instance.
2575 *
2576 * Returns %TRUE if @file is a local file with the
2577 * file:// URI scheme.
2578 *
2579 * Return value: %TRUE if @file is local.
2580 **/
2581gboolean
2582thunar_file_is_local (const ThunarFile *file)
2583{
2584 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2585 return g_file_has_uri_scheme (file->gfile, "file");
2586}
2587
2588
2589
2590/**
2591 * thunar_file_is_parent:
2592 * @file : a #ThunarFile instance.
2593 * @child : another #ThunarFile instance.
2594 *
2595 * Determines whether @file is the parent directory of @child.
2596 *
2597 * Return value: %TRUE if @file is the parent of @child.
2598 **/
2599gboolean
2600thunar_file_is_parent (const ThunarFile *file,
2601 const ThunarFile *child)
2602{
2603 gboolean is_parent = FALSE;
2604 GFile *parent;
2605
2606 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2607 _thunar_return_val_if_fail (THUNAR_IS_FILE (child), FALSE);
2608
2609 parent = g_file_get_parent (child->gfile);
2610 if (parent != NULL)
2611 {
2612 is_parent = g_file_equal (file->gfile, parent);
2613 g_object_unref (parent);
2614 }
2615
2616 return is_parent;
2617}
2618
2619
2620
2621/**
2622 * thunar_file_is_ancestor:
2623 * @file : a #ThunarFile instance.
2624 * @ancestor : another #GFile instance.
2625 *
2626 * Determines whether @file is somewhere inside @ancestor,
2627 * possibly with intermediate folders.
2628 *
2629 * Return value: %TRUE if @ancestor contains @file as a
2630 * child, grandchild, great grandchild, etc.
2631 **/
2632gboolean
2633thunar_file_is_gfile_ancestor (const ThunarFile *file,
2634 GFile *ancestor)
2635{
2636 gboolean is_ancestor = FALSE;
2637 GFile *current = NULL;
2638 GFile *tmp;
2639
2640 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2641 _thunar_return_val_if_fail (G_IS_FILE (ancestor), FALSE);
2642
2643 for (current = g_object_ref (file->gfile);
2644 is_ancestor == FALSE && current != NULL;
2645 tmp = g_file_get_parent (current), g_object_unref (current), current = tmp)
2646 {
2647 if (G_UNLIKELY (g_file_equal (current, ancestor)))
2648 is_ancestor = TRUE;
2649 }
2650
2651 if (current != NULL)
2652 g_object_unref (current);
2653
2654 return is_ancestor;
2655}
2656
2657
2658
2659/**
2660 * thunar_file_is_ancestor:
2661 * @file : a #ThunarFile instance.
2662 * @ancestor : another #ThunarFile instance.
2663 *
2664 * Determines whether @file is somewhere inside @ancestor,
2665 * possibly with intermediate folders.
2666 *
2667 * Return value: %TRUE if @ancestor contains @file as a
2668 * child, grandchild, great grandchild, etc.
2669 **/
2670gboolean
2671thunar_file_is_ancestor (const ThunarFile *file,
2672 const ThunarFile *ancestor)
2673{
2674 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2675 _thunar_return_val_if_fail (THUNAR_IS_FILE (ancestor), FALSE);
2676
2677 return thunar_file_is_gfile_ancestor (file, ancestor->gfile);
2678}
2679
2680
2681
2682/**
2683 * thunar_file_is_executable:
2684 * @file : a #ThunarFile instance.
2685 *
2686 * Determines whether the owner of the current process is allowed
2687 * to execute the @file (or enter the directory refered to by
2688 * @file). On UNIX it also returns %TRUE if @file refers to a
2689 * desktop entry.
2690 *
2691 * Return value: %TRUE if @file can be executed.
2692 **/
2693gboolean
2694thunar_file_is_executable (const ThunarFile *file)
2695{
2696 gboolean can_execute = FALSE;
2697 const gchar *content_type;
2698
2699 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2700
2701 if (file->info == NULL)
2702 return FALSE;
2703
2704 if (g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
2705 {
2706 /* get the content type of the file */
2707 content_type = thunar_file_get_content_type (THUNAR_FILE (file));
2708 if (G_LIKELY (content_type != NULL))
2709 {
2710#ifdef G_OS_WIN32
2711 /* check for .exe, .bar or .com */
2712 can_execute = g_content_type_can_be_executable (content_type);
2713#else
2714 /* check if the content type is save to execute, we don't use
2715 * g_content_type_can_be_executable() for unix because it also returns
2716 * true for "text/plain" and we don't want that */
2717 if (g_content_type_is_a (content_type, "application/x-executable")
2718 || g_content_type_is_a (content_type, "application/x-shellscript"))
2719 {
2720 can_execute = TRUE;
2721 }
2722#endif
2723 }
2724 }
2725
2726 return can_execute || thunar_file_is_desktop_file (file, NULL);
2727}
2728
2729
2730
2731/**
2732 * thunar_file_is_readable:
2733 * @file : a #ThunarFile instance.
2734 *
2735 * Determines whether the owner of the current process is allowed
2736 * to read the @file.
2737 *
2738 * Return value: %TRUE if @file can be read.
2739 **/
2740static gboolean
2741thunar_file_is_readable (const ThunarFile *file)
2742{
2743 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2744
2745 if (file->info == NULL)
2746 return FALSE;
2747
2748 if (!g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
2749 return TRUE;
2750
2751 return g_file_info_get_attribute_boolean (file->info,
2752 G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
2753}
2754
2755
2756
2757/**
2758 * thunar_file_is_writable:
2759 * @file : a #ThunarFile instance.
2760 *
2761 * Determines whether the owner of the current process is allowed
2762 * to write the @file.
2763 *
2764 * Return value: %TRUE if @file can be read.
2765 **/
2766gboolean
2767thunar_file_is_writable (const ThunarFile *file)
2768{
2769 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2770
2771 if (file->info == NULL)
2772 return FALSE;
2773
2774 if (!g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
2775 return TRUE;
2776
2777 return g_file_info_get_attribute_boolean (file->info,
2778 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
2779}
2780
2781
2782
2783/**
2784 * thunar_file_is_hidden:
2785 * @file : a #ThunarFile instance.
2786 *
2787 * Checks whether @file can be considered a hidden file.
2788 *
2789 * Return value: %TRUE if @file is a hidden file, else %FALSE.
2790 **/
2791gboolean
2792thunar_file_is_hidden (const ThunarFile *file)
2793{
2794 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2795
2796 if (file->info == NULL)
2797 return FALSE;
2798
2799 return g_file_info_get_is_hidden (file->info)
2800 || g_file_info_get_is_backup (file->info);
2801}
2802
2803
2804
2805/**
2806 * thunar_file_is_home:
2807 * @file : a #ThunarFile.
2808 *
2809 * Checks whether @file refers to the users home directory.
2810 *
2811 * Return value: %TRUE if @file is the users home directory.
2812 **/
2813gboolean
2814thunar_file_is_home (const ThunarFile *file)
2815{
2816 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2817 return thunar_g_file_is_home (file->gfile);
2818}
2819
2820
2821
2822/**
2823 * thunar_file_is_regular:
2824 * @file : a #ThunarFile.
2825 *
2826 * Checks whether @file refers to a regular file.
2827 *
2828 * Return value: %TRUE if @file is a regular file.
2829 **/
2830gboolean
2831thunar_file_is_regular (const ThunarFile *file)
2832{
2833 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2834 return file->kind == G_FILE_TYPE_REGULAR;
2835}
2836
2837
2838
2839/**
2840 * thunar_file_is_trashed:
2841 * @file : a #ThunarFile instance.
2842 *
2843 * Returns %TRUE if @file is a local file that resides in
2844 * the trash bin.
2845 *
2846 * Return value: %TRUE if @file is in the trash, or
2847 * the trash folder itself.
2848 **/
2849gboolean
2850thunar_file_is_trashed (const ThunarFile *file)
2851{
2852 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2853 return thunar_g_file_is_trashed (file->gfile);
2854}
2855
2856
2857
2858/**
2859 * thunar_file_is_desktop_file:
2860 * @file : a #ThunarFile.
2861 * @is_secure : if %NULL do a simple check, else it will set this boolean
2862 * to indicate if the desktop file is safe see bug #5012
2863 * for more info.
2864 *
2865 * Returns %TRUE if @file is a .desktop file. The @is_secure return value
2866 * will tell if the .desktop file is also secure.
2867 *
2868 * Return value: %TRUE if @file is a .desktop file.
2869 **/
2870gboolean
2871thunar_file_is_desktop_file (const ThunarFile *file,
2872 gboolean *is_secure)
2873{
2874 const gchar * const *data_dirs;
2875 guint n;
2876 gchar *path;
2877
2878 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2879
2880 if (file->info == NULL)
2881 return FALSE;
2882
2883 /* only allow regular files with a .desktop extension */
2884 if (!g_str_has_suffix (file->basename, ".desktop")
2885 || file->kind != G_FILE_TYPE_REGULAR)
2886 return FALSE;
2887
2888 /* don't check more if not needed */
2889 if (is_secure == NULL)
2890 return TRUE;
2891
2892 /* desktop files outside xdg directories need to be executable for security reasons */
2893 if (g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
2894 {
2895 /* has +x */
2896 *is_secure = TRUE;
2897 }
2898 else
2899 {
2900 /* assume the file is not safe */
2901 *is_secure = FALSE;
2902
2903 /* deskopt files in xdg directories are also fine... */
2904 if (g_file_is_native (thunar_file_get_file (file)))
2905 {
2906 data_dirs = g_get_system_data_dirs ();
2907 if (G_LIKELY (data_dirs != NULL))
2908 {
2909 path = g_file_get_path (thunar_file_get_file (file));
2910 for (n = 0; data_dirs[n] != NULL; n++)
2911 {
2912 if (g_str_has_prefix (path, data_dirs[n]))
2913 {
2914 /* has known prefix, can launch without problems */
2915 *is_secure = TRUE;
2916 break;
2917 }
2918 }
2919 g_free (path);
2920 }
2921 }
2922 }
2923
2924 return TRUE;
2925}
2926
2927
2928
2929/**
2930 * thunar_file_get_display_name:
2931 * @file : a #ThunarFile instance.
2932 *
2933 * Returns the @file name in the UTF-8 encoding, which is
2934 * suitable for displaying the file name in the GUI.
2935 *
2936 * Return value: the @file name suitable for display.
2937 **/
2938const gchar *
2939thunar_file_get_display_name (const ThunarFile *file)
2940{
2941 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
2942 return file->display_name;
2943}
2944
2945
2946
2947/**
2948 * thunar_file_get_deletion_date:
2949 * @file : a #ThunarFile instance.
2950 * @date_style : the style used to format the date.
2951 *
2952 * Returns the deletion date of the @file if the @file
2953 * is located in the trash. Otherwise %NULL will be
2954 * returned.
2955 *
2956 * The caller is responsible to free the returned string
2957 * using g_free() when no longer needed.
2958 *
2959 * Return value: the deletion date of @file if @file is
2960 * in the trash, %NULL otherwise.
2961 **/
2962gchar*
2963thunar_file_get_deletion_date (const ThunarFile *file,
2964 ThunarDateStyle date_style)
2965{
2966 const gchar *date;
2967 time_t deletion_time;
2968
2969 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
2970 _thunar_return_val_if_fail (G_IS_FILE_INFO (file->info), NULL);
2971
2972 date = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE);
2973 if (G_UNLIKELY (date == NULL))
2974 return NULL;
2975
2976 /* try to parse the DeletionDate (RFC 3339 string) */
2977 deletion_time = thunar_util_time_from_rfc3339 (date);
2978
2979 /* humanize the time value */
2980 return thunar_util_humanize_file_time (deletion_time, date_style);
2981}
2982
2983
2984
2985/**
2986 * thunar_file_get_original_path:
2987 * @file : a #ThunarFile instance.
2988 *
2989 * Returns the original path of the @file if the @file
2990 * is located in the trash. Otherwise %NULL will be
2991 * returned.
2992 *
2993 * Return value: the original path of @file if @file is
2994 * in the trash, %NULL otherwise.
2995 **/
2996const gchar *
2997thunar_file_get_original_path (const ThunarFile *file)
2998{
2999 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
3000
3001 if (file->info == NULL)
3002 return NULL;
3003
3004 return g_file_info_get_attribute_byte_string (file->info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
3005}
3006
3007
3008
3009/**
3010 * thunar_file_get_item_count:
3011 * @file : a #ThunarFile instance.
3012 *
3013 * Returns the number of items in the trash, if @file refers to the
3014 * trash root directory. Otherwise returns 0.
3015 *
3016 * Return value: number of files in the trash if @file is the trash
3017 * root dir, 0 otherwise.
3018 **/
3019guint32
3020thunar_file_get_item_count (const ThunarFile *file)
3021{
3022 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), 0);
3023
3024 if (file->info == NULL)
3025 return 0;
3026
3027 return g_file_info_get_attribute_uint32 (file->info,
3028 G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
3029}
3030
3031
3032
3033/**
3034 * thunar_file_is_chmodable:
3035 * @file : a #ThunarFile instance.
3036 *
3037 * Determines whether the owner of the current process is allowed
3038 * to changed the file mode of @file.
3039 *
3040 * Return value: %TRUE if the mode of @file can be changed.
3041 **/
3042gboolean
3043thunar_file_is_chmodable (const ThunarFile *file)
3044{
3045 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
3046
3047 /* we can only change the mode if we the euid is
3048 * a) equal to the file owner id
3049 * or
3050 * b) the super-user id
3051 * and the file is not in the trash.
3052 */
3053 if (file->info == NULL)
3054 {
3055 return (effective_user_id == 0 && !thunar_file_is_trashed (file));
3056 }
3057 else
3058 {
3059 return ((effective_user_id == 0
3060 || effective_user_id == g_file_info_get_attribute_uint32 (file->info,
3061 G_FILE_ATTRIBUTE_UNIX_UID))
3062 && !thunar_file_is_trashed (file));
3063 }
3064}
3065
3066
3067
3068/**
3069 * thunar_file_is_renameable:
3070 * @file : a #ThunarFile instance.
3071 *
3072 * Determines whether @file can be renamed using
3073 * #thunar_file_rename(). Note that the return
3074 * value is just a guess and #thunar_file_rename()
3075 * may fail even if this method returns %TRUE.
3076 *
3077 * Return value: %TRUE if @file can be renamed.
3078 **/
3079gboolean
3080thunar_file_is_renameable (const ThunarFile *file)
3081{
3082 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
3083
3084 if (file->info == NULL)
3085 return FALSE;
3086
3087 return g_file_info_get_attribute_boolean (file->info,
3088 G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME);
3089}
3090
3091
3092
3093gboolean
3094thunar_file_can_be_trashed (const ThunarFile *file)
3095{
3096 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
3097
3098 if (file->info == NULL)
3099 return FALSE;
3100
3101 return g_file_info_get_attribute_boolean (file->info,
3102 G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH);
3103}
3104
3105
3106
3107/**
3108 * thunar_file_get_emblem_names:
3109 * @file : a #ThunarFile instance.
3110 *
3111 * Determines the names of the emblems that should be displayed for
3112 * @file. The returned list is owned by the caller, but the list
3113 * items - the name strings - are owned by @file. So the caller
3114 * must call g_list_free(), but don't g_free() the list items.
3115 *
3116 * Note that the strings contained in the returned list are
3117 * not garantied to exist over the next iteration of the main
3118 * loop. So in case you need the list of emblem names for
3119 * a longer time, you'll need to take a copy of the strings.
3120 *
3121 * Return value: the names of the emblems for @file.
3122 **/
3123GList*
3124thunar_file_get_emblem_names (ThunarFile *file)
3125{
3126 guint32 uid;
3127 gchar **emblem_names;
3128 GList *emblems = NULL;
3129
3130 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
3131
3132 /* leave if there is no info */
3133 if (file->info == NULL)
3134 return NULL;
3135
3136 /* determine the custom emblems */
3137 emblem_names = g_file_info_get_attribute_stringv (file->info, "metadata::emblems");
3138 if (G_UNLIKELY (emblem_names != NULL))
3139 {
3140 for (; *emblem_names != NULL; ++emblem_names)
3141 emblems = g_list_append (emblems, *emblem_names);
3142 }
3143
3144 if (thunar_file_is_symlink (file))
3145 emblems = g_list_prepend (emblems, THUNAR_FILE_EMBLEM_NAME_SYMBOLIC_LINK);
3146
3147 /* determine the user ID of the file owner */
3148 /* TODO what are we going to do here on non-UNIX systems? */
3149 uid = file->info != NULL
3150 ? g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_UID)
3151 : 0;
3152
3153 /* we add "cant-read" if either (a) the file is not readable or (b) a directory, that lacks the
3154 * x-bit, see http://bugzilla.xfce.org/show_bug.cgi?id=1408 for the details about this change.
3155 */
3156 if (!thunar_file_is_readable (file)
3157 || (thunar_file_is_directory (file)
3158 && thunar_file_denies_access_permission (file, THUNAR_FILE_MODE_USR_EXEC,
3159 THUNAR_FILE_MODE_GRP_EXEC,
3160 THUNAR_FILE_MODE_OTH_EXEC)))
3161 {
3162 emblems = g_list_prepend (emblems, THUNAR_FILE_EMBLEM_NAME_CANT_READ);
3163 }
3164 else if (G_UNLIKELY (uid == effective_user_id && !thunar_file_is_writable (file)))
3165 {
3166 /* we own the file, but we cannot write to it, that's why we mark it as "cant-write", so
3167 * users won't be surprised when opening the file in a text editor, but are unable to save.
3168 */
3169 emblems = g_list_prepend (emblems, THUNAR_FILE_EMBLEM_NAME_CANT_WRITE);
3170 }
3171
3172 return emblems;
3173}
3174
3175
3176
3177/**
3178 * thunar_file_set_emblem_names:
3179 * @file : a #ThunarFile instance.
3180 * @emblem_names : a #GList of emblem names.
3181 *
3182 * Sets the custom emblem name list of @file to @emblem_names
3183 * and stores them in the @file<!---->s metadata.
3184 **/
3185void
3186thunar_file_set_emblem_names (ThunarFile *file,
3187 GList *emblem_names)
3188{
3189 GList *lp;
3190 gchar **emblems = NULL;
3191 gint n;
3192 GFileInfo *info;
3193
3194 _thunar_return_if_fail (THUNAR_IS_FILE (file));
3195 _thunar_return_if_fail (G_IS_FILE_INFO (file->info));
3196
3197 /* allocate a zero-terminated array for the emblem names */
3198 emblems = g_new0 (gchar *, g_list_length (emblem_names) + 1);
3199
3200 /* turn the emblem_names list into a zero terminated array */
3201 for (lp = emblem_names, n = 0; lp != NULL; lp = lp->next)
3202 {
3203 /* skip special emblems */
3204 if (strcmp (lp->data, THUNAR_FILE_EMBLEM_NAME_SYMBOLIC_LINK) == 0
3205 || strcmp (lp->data, THUNAR_FILE_EMBLEM_NAME_CANT_READ) == 0
3206 || strcmp (lp->data, THUNAR_FILE_EMBLEM_NAME_CANT_WRITE) == 0
3207 || strcmp (lp->data, THUNAR_FILE_EMBLEM_NAME_DESKTOP) == 0)
3208 continue;
3209
3210 /* add the emblem to our list */
3211 emblems[n++] = g_strdup (lp->data);
3212 }
3213
3214 /* set the value in the current info */
3215 if (n == 0)
3216 g_file_info_remove_attribute (file->info, "metadata::emblems");
3217 else
3218 g_file_info_set_attribute_stringv (file->info, "metadata::emblems", emblems);
3219
3220 /* set meta data to the daemon */
3221 info = g_file_info_new ();
3222 g_file_info_set_attribute_stringv (info, "metadata::emblems", emblems);
3223 g_file_set_attributes_async (file->gfile, info,
3224 G_FILE_QUERY_INFO_NONE,
3225 G_PRIORITY_DEFAULT,
3226 NULL,
3227 thunar_file_set_emblem_names_ready,
3228 file);
3229 g_object_unref (G_OBJECT (info));
3230
3231 g_strfreev (emblems);
3232}
3233
3234
3235
3236/**
3237 * thunar_file_set_custom_icon:
3238 * @file : a #ThunarFile instance.
3239 * @custom_icon : the new custom icon for the @file.
3240 * @error : return location for errors or %NULL.
3241 *
3242 * Tries to change the custom icon of the .desktop file referred
3243 * to by @file. If that fails, %FALSE is returned and the
3244 * @error is set accordingly.
3245 *
3246 * Return value: %TRUE if the icon of @file was changed, %FALSE otherwise.
3247 **/
3248gboolean
3249thunar_file_set_custom_icon (ThunarFile *file,
3250 const gchar *custom_icon,
3251 GError **error)
3252{
3253 GKeyFile *key_file;
3254
3255 _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3256 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
3257 _thunar_return_val_if_fail (custom_icon != NULL, FALSE);
3258
3259 key_file = thunar_g_file_query_key_file (file->gfile, NULL, error);
3260
3261 if (key_file == NULL)
3262 return FALSE;
3263
3264 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3265 G_KEY_FILE_DESKTOP_KEY_ICON, custom_icon);
3266
3267 if (thunar_g_file_write_key_file (file->gfile, key_file, NULL, error))
3268 {
3269 /* tell everybody that we have changed */
3270 thunar_file_changed (file);
3271
3272 g_key_file_free (key_file);
3273 return TRUE;
3274 }
3275 else
3276 {
3277 g_key_file_free (key_file);
3278 return FALSE;
3279 }
3280}
3281
3282
3283/**
3284 * thunar_file_is_desktop:
3285 * @file : a #ThunarFile.
3286 *
3287 * Checks whether @file refers to the users desktop directory.
3288 *
3289 * Return value: %TRUE if @file is the users desktop directory.
3290 **/
3291gboolean
3292thunar_file_is_desktop (const ThunarFile *file)
3293{
3294 GFile *desktop;
3295 gboolean is_desktop = FALSE;
3296
3297 desktop = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
3298 is_desktop = g_file_equal (file->gfile, desktop);
3299 g_object_unref (desktop);
3300
3301 return is_desktop;
3302}
3303
3304
3305
3306const gchar *
3307thunar_file_get_thumbnail_path (ThunarFile *file)
3308{
3309 GChecksum *checksum;
3310 gchar *basename;
3311 gchar *uri;
3312
3313 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
3314
3315 /* if the thumbstate is known to be not there, return null */
3316 if (thunar_file_get_thumb_state (file) == THUNAR_FILE_THUMB_STATE_NONE)
3317 return NULL;
3318
3319 if (G_UNLIKELY (file->thumbnail_path == NULL))
3320 {
3321 checksum = g_checksum_new (G_CHECKSUM_MD5);
3322 if (G_LIKELY (checksum != NULL))
3323 {
3324 uri = thunar_file_dup_uri (file);
3325 g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
3326 g_free (uri);
3327
3328 basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
3329 g_checksum_free (checksum);
3330
3331 file->thumbnail_path = g_build_filename (xfce_get_homedir (), ".thumbnails",
3332 "normal", basename, NULL);
3333
3334 g_free (basename);
3335 }
3336 }
3337
3338 return file->thumbnail_path;
3339}
3340
3341
3342
3343/**
3344 * thunar_file_get_thumb_state:
3345 * @file : a #ThunarFile.
3346 *
3347 * Returns the current #ThunarFileThumbState for @file. This
3348 * method is intended to be used by #ThunarIconFactory only.
3349 *
3350 * Return value: the #ThunarFileThumbState for @file.
3351 **/
3352ThunarFileThumbState
3353thunar_file_get_thumb_state (const ThunarFile *file)
3354{
3355 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), THUNAR_FILE_THUMB_STATE_UNKNOWN);
3356 return FLAG_GET_THUMB_STATE (file);
3357}
3358
3359
3360
3361/**
3362 * thunar_file_set_thumb_state:
3363 * @file : a #ThunarFile.
3364 * @thumb_state : the new #ThunarFileThumbState.
3365 *
3366 * Sets the #ThunarFileThumbState for @file to @thumb_state.
3367 * This will cause a "file-changed" signal to be emitted from
3368 * #ThunarFileMonitor.
3369 **/
3370void
3371thunar_file_set_thumb_state (ThunarFile *file,
3372 ThunarFileThumbState state)
3373{
3374 _thunar_return_if_fail (THUNAR_IS_FILE (file));
3375
3376 /* check if the state changes */
3377 if (thunar_file_get_thumb_state (file) == state)
3378 return;
3379
3380 /* set the new thumbnail state */
3381 FLAG_SET_THUMB_STATE (file, state);
3382
3383 /* remove path if the type is not supported */
3384 if (state == THUNAR_FILE_THUMB_STATE_NONE
3385 && file->thumbnail_path != NULL)
3386 {
3387 g_free (file->thumbnail_path);
3388 file->thumbnail_path = NULL;
3389 }
3390
3391 /* if the file has a thumbnail, reload it */
3392 if (state == THUNAR_FILE_THUMB_STATE_READY)
3393 thunar_file_monitor_file_changed (file);
3394}
3395
3396
3397
3398/**
3399 * thunar_file_get_custom_icon:
3400 * @file : a #ThunarFile instance.
3401 *
3402 * Queries the custom icon from @file if any, else %NULL is returned.
3403 * The custom icon can be either a themed icon name or an absolute path
3404 * to an icon file in the local file system.
3405 *
3406 * Return value: the custom icon for @file or %NULL.
3407 **/
3408const gchar *
3409thunar_file_get_custom_icon (const ThunarFile *file)
3410{
3411 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
3412 return file->custom_icon_name;
3413}
3414
3415
3416
3417/**
3418 * thunar_file_get_preview_icon:
3419 * @file : a #ThunarFile instance.
3420 *
3421 * Returns the preview icon for @file if any, else %NULL is returned.
3422 *
3423 * Return value: the custom icon for @file or %NULL, the GIcon is owner
3424 * by the file, so do not unref it.
3425 **/
3426GIcon *
3427thunar_file_get_preview_icon (const ThunarFile *file)
3428{
3429 GObject *icon;
3430
3431 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
3432 _thunar_return_val_if_fail (G_IS_FILE_INFO (file->info), NULL);
3433
3434 icon = g_file_info_get_attribute_object (file->info, G_FILE_ATTRIBUTE_PREVIEW_ICON);
3435 if (G_LIKELY (icon != NULL))
3436 return G_ICON (icon);
3437
3438 return NULL;
3439}
3440
3441
3442
3443GFilesystemPreviewType
3444thunar_file_get_preview_type (const ThunarFile *file)
3445{
3446 GFilesystemPreviewType preview;
3447 GFileInfo *info;
3448
3449 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), G_FILESYSTEM_PREVIEW_TYPE_NEVER);
3450 _thunar_return_val_if_fail (G_IS_FILE (file->gfile), G_FILESYSTEM_PREVIEW_TYPE_NEVER);
3451
3452 info = g_file_query_filesystem_info (file->gfile, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, NULL, NULL);
3453 if (G_LIKELY (info != NULL))
3454 {
3455 preview = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW);
3456 g_object_unref (G_OBJECT (info));
3457 }
3458 else
3459 {
3460 /* assume we don't know */
3461 preview = G_FILESYSTEM_PREVIEW_TYPE_NEVER;
3462 }
3463
3464 return preview;
3465}
3466
3467
3468
3469static const gchar *
3470thunar_file_get_icon_name_for_state (const gchar *icon_name,
3471 ThunarFileIconState icon_state)
3472{
3473 if (exo_str_is_empty (icon_name))
3474 return NULL;
3475
3476 /* check if we have an accept icon for the icon we found */
3477 if (icon_state != THUNAR_FILE_ICON_STATE_DEFAULT
3478 && (strcmp (icon_name, "inode-directory") == 0
3479 || strcmp (icon_name, "folder") == 0))
3480 {
3481 if (icon_state == THUNAR_FILE_ICON_STATE_DROP)
3482 return "folder-drag-accept";
3483 else if (icon_state == THUNAR_FILE_ICON_STATE_OPEN)
3484 return "folder-open";
3485 }
3486
3487 return icon_name;
3488}
3489
3490
3491
3492/**
3493 * thunar_file_get_icon_name:
3494 * @file : a #ThunarFile instance.
3495 * @icon_state : the state of the @file<!---->s icon we are interested in.
3496 * @icon_theme : the #GtkIconTheme on which to lookup up the icon name.
3497 *
3498 * Returns the name of the icon that can be used to present @file, based
3499 * on the given @icon_state and @icon_theme.
3500 *
3501 * Return value: the icon name for @file in @icon_theme.
3502 **/
3503const gchar *
3504thunar_file_get_icon_name (ThunarFile *file,
3505 ThunarFileIconState icon_state,
3506 GtkIconTheme *icon_theme)
3507{
3508 GFile *icon_file;
3509 GIcon *icon = NULL;
3510 const gchar * const *names;
3511 gchar *icon_name = NULL;
3512 gchar *path;
3513 const gchar *special_names[] = { NULL, "folder", NULL };
3514 guint i;
3515 const gchar *special_dir;
3516 GFileInfo *fileinfo;
3517
3518 _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
3519 _thunar_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
3520
3521 /* return cached name */
3522 if (G_LIKELY (file->icon_name != NULL))
3523 return thunar_file_get_icon_name_for_state (file->icon_name, icon_state);
3524
3525 /* the system root folder has a special icon */
3526 if (thunar_file_is_directory (file))
3527 {
3528 if (G_LIKELY (thunar_file_is_local (file)))
3529 {
3530 path = g_file_get_path (file->gfile);
3531 if (G_LIKELY (path != NULL))
3532 {
3533 if (strcmp (path, G_DIR_SEPARATOR_S) == 0)
3534 *special_names = "drive-harddisk";
3535 else if (strcmp (path, xfce_get_homedir ()) == 0)
3536 *special_names = "user-home";
3537 else
3538 {
3539 for (i = 0; i < G_N_ELEMENTS (thunar_file_dirs); i++)
3540 {
3541 special_dir = g_get_user_special_dir (thunar_file_dirs[i].type);
3542 if (special_dir != NULL
3543 && strcmp (path, special_dir) == 0)
3544 {
3545 *special_names = thunar_file_dirs[i].icon_name;
3546 break;
3547 }
3548 }
3549 }
3550
3551 g_free (path);
3552 }
3553 }
3554 else if (!thunar_file_has_parent (file))
3555 {
3556 if (g_file_has_uri_scheme (file->gfile, "trash"))
3557 {
3558 special_names[0] = thunar_file_get_item_count (file) > 0 ? "user-trash-full" : "user-trash";
3559 special_names[1] = "user-trash";
3560 }
3561 else if (g_file_has_uri_scheme (file->gfile, "recent"))
3562 {
3563 special_names[0] = "document-open-recent";
3564 }
3565 else if (g_file_has_uri_scheme (file->gfile, "computer"))
3566 {
3567 special_names[0] = "computer";
3568 }
3569 }
3570
3571 if (*special_names != NULL)
3572 {
3573 names = special_names;
3574 goto check_names;
3575 }
3576 }
3577 else if (thunar_file_is_mountable (file))
3578 {
3579 /* query the icon (computer:// backend) */
3580 fileinfo = g_file_query_info (file->gfile,
3581 G_FILE_ATTRIBUTE_STANDARD_ICON,
3582 G_FILE_QUERY_INFO_NONE, NULL, NULL);
3583 if (G_LIKELY (fileinfo != NULL))
3584 {
3585 /* take the icon from the info */
3586 icon = g_file_info_get_icon (fileinfo);
3587 if (G_LIKELY (icon != NULL))
3588 g_object_ref (icon);
3589
3590 /* release */
3591 g_object_unref (G_OBJECT (fileinfo));
3592
3593 if (G_LIKELY (icon != NULL))
3594 goto check_icon;
3595 }
3596 }
3597
3598 /* try again later */
3599 if (file->info == NULL)
3600 return NULL;
3601
3602 /* lookup for content type, just like gio does for local files */
3603 icon = g_content_type_get_icon (thunar_file_get_content_type (file));
3604 if (G_LIKELY (icon != NULL))
3605 {
3606 check_icon:
3607
3608 if (G_IS_THEMED_ICON (icon))
3609 {
3610 names = g_themed_icon_get_names (G_THEMED_ICON (icon));
3611
3612 check_names:
3613
3614 if (G_LIKELY (names != NULL))
3615 {
3616 for (i = 0; names[i] != NULL; ++i)
3617 if (*names[i] != '(' /* see gnome bug 688042 */
3618 && gtk_icon_theme_has_icon (icon_theme, names[i]))
3619 {
3620 icon_name = g_strdup (names[i]);
3621 break;
3622 }
3623 }
3624 }
3625 else if (G_IS_FILE_ICON (icon))
3626 {
3627 icon_file = g_file_icon_get_file (G_FILE_ICON (icon));
3628 if (icon_file != NULL)
3629 icon_name = g_file_get_path (icon_file);
3630 }
3631
3632 if (G_LIKELY (icon != NULL))
3633 g_object_unref (icon);
3634 }
3635
3636 /* store new name, or empty string to avoid recursion */
3637 if (G_LIKELY (icon_name != NULL))
3638 file->icon_name = icon_name;
3639 else
3640 file->icon_name = g_strdup ("");
3641
3642 return thunar_file_get_icon_name_for_state (file->icon_name, icon_state);
3643}
3644
3645
3646
3647/**
3648 * thunar_file_watch:
3649 * @file : a #ThunarFile instance.
3650 *
3651 * Tells @file to watch itself for changes. Not all #ThunarFile
3652 * implementations must support this, but if a #ThunarFile
3653 * implementation implements the thunar_file_watch() method,
3654 * it must also implement the thunar_file_unwatch() method.
3655 *
3656 * The #ThunarFile base class implements automatic "ref
3657 * counting" for watches, that says, you can call thunar_file_watch()
3658 * multiple times, but the virtual method will be invoked only
3659 * once. This also means that you MUST call thunar_file_unwatch()
3660 * for every thunar_file_watch() invokation, else the application
3661 * will abort.
3662 **/
3663void
3664thunar_file_watch (ThunarFile *file)
3665{
3666 ThunarFileWatch *file_watch;
3667
3668 _thunar_return_if_fail (THUNAR_IS_FILE (file));
3669
3670 file_watch = g_object_get_qdata (G_OBJECT (file), thunar_file_watch_quark);
3671 if (file_watch == NULL)
3672 {
3673 file_watch = g_slice_new (ThunarFileWatch);
3674 file_watch->watch_count = 1;
3675
3676 /* create a file or directory monitor */
3677 file_watch->monitor = g_file_monitor (file->gfile, G_FILE_MONITOR_WATCH_MOUNTS, NULL, NULL);
3678 if (G_LIKELY (file_watch->monitor != NULL))
3679 {
3680 /* watch monitor for file changes */
3681 g_signal_connect (file_watch->monitor, "changed", G_CALLBACK (thunar_file_monitor), file);
3682 }
3683
3684 /* attach to file */
3685 g_object_set_qdata_full (G_OBJECT (file), thunar_file_watch_quark, file_watch, thunar_file_watch_destroyed);
3686 }
3687 else
3688 {
3689 /* increase watch count */
3690 _thunar_return_if_fail (G_IS_FILE_MONITOR (file_watch->monitor));
3691 file_watch->watch_count++;
3692 }
3693}
3694
3695
3696
3697/**
3698 * thunar_file_unwatch:
3699 * @file : a #ThunarFile instance.
3700 *
3701 * See thunar_file_watch() for a description of how watching
3702 * #ThunarFile<!---->s works.
3703 **/
3704void
3705thunar_file_unwatch (ThunarFile *file)
3706{
3707 ThunarFileWatch *file_watch;
3708
3709 _thunar_return_if_fail (THUNAR_IS_FILE (file));
3710
3711 file_watch = g_object_get_qdata (G_OBJECT (file), thunar_file_watch_quark);
3712 if (file_watch != NULL)
3713 {
3714 /* remove if this was the last ref */
3715 if (--file_watch->watch_count == 0)
3716 g_object_set_qdata (G_OBJECT (file), thunar_file_watch_quark, NULL);
3717 }
3718 else
3719 {
3720 _thunar_assert_not_reached ();
3721 }
3722}
3723
3724
3725
3726/**
3727 * thunar_file_reload:
3728 * @file : a #ThunarFile instance.
3729 *
3730 * Tells @file to reload its internal state, e.g. by reacquiring
3731 * the file info from the underlying media.
3732 *
3733 * You must be able to handle the case that @file is
3734 * destroyed during the reload call.
3735 **/
3736void
3737thunar_file_reload (ThunarFile *file)
3738{
3739 _thunar_return_if_fail (THUNAR_IS_FILE (file));
3740
3741 /* clear file pxmap cache */
3742 thunar_icon_factory_clear_pixmap_cache (file);
3743
3744 if (!thunar_file_load (file, NULL, NULL))
3745 {
3746 /* destroy the file if we cannot query any file information */
3747 thunar_file_destroy (file);
3748 return;
3749 }
3750
3751 /* ... and tell others */
3752 thunar_file_changed (file);
3753
3754}
3755
3756
3757
3758/**
3759 * thunar_file_destroy:
3760 * @file : a #ThunarFile instance.
3761 *
3762 * Emits the ::destroy signal notifying all reference holders
3763 * that they should release their references to the @file.
3764 *
3765 * This method is very similar to what gtk_object_destroy()
3766 * does for #GtkObject<!---->s.
3767 **/
3768void
3769thunar_file_destroy (ThunarFile *file)
3770{
3771 _thunar_return_if_fail (THUNAR_IS_FILE (file));
3772
3773 if (!FLAG_IS_SET (file, THUNAR_FILE_FLAG_IN_DESTRUCTION))
3774 {
3775 /* take an additional reference on the file, as the file-destroyed
3776 * invocation may already release the last reference.
3777 */
3778 g_object_ref (G_OBJECT (file));
3779
3780 /* tell the file monitor that this file was destroyed */
3781 thunar_file_monitor_file_destroyed (file);
3782
3783 /* run the dispose handler */
3784 g_object_run_dispose (G_OBJECT (file));
3785
3786 /* release our reference */
3787 g_object_unref (G_OBJECT (file));
3788 }
3789}
3790
3791
3792
3793/**
3794 * thunar_file_compare_by_name:
3795 * @file_a : the first #ThunarFile.
3796 * @file_b : the second #ThunarFile.
3797 * @case_sensitive : whether the comparison should be case-sensitive.
3798 *
3799 * Compares @file_a and @file_b by their display names. If @case_sensitive
3800 * is %TRUE the comparison will be case-sensitive.
3801 *
3802 * Return value: -1 if @file_a should be sorted before @file_b, 1 if
3803 * @file_b should be sorted before @file_a, 0 if equal.
3804 **/
3805gint
3806thunar_file_compare_by_name (const ThunarFile *file_a,
3807 const ThunarFile *file_b,
3808 gboolean case_sensitive)
3809{
3810 gint result = 0;
3811
3812#ifdef G_ENABLE_DEBUG
3813 /* probably too expensive to do the instance check every time
3814 * this function is called, so only for debugging builds.
3815 */
3816 _thunar_return_val_if_fail (THUNAR_IS_FILE (file_a), 0);
3817 _thunar_return_val_if_fail (THUNAR_IS_FILE (file_b), 0);
3818#endif
3819
3820 /* case insensitive checking */
3821 if (G_LIKELY (!case_sensitive))
3822 result = strcmp (file_a->collate_key_nocase, file_b->collate_key_nocase);
3823
3824 /* fall-back to case sensitive */
3825 if (result == 0)
3826 result = strcmp (file_a->collate_key, file_b->collate_key);
3827
3828 /* this happens in the trash */
3829 if (result == 0)
3830 {
3831 result = g_strcmp0 (thunar_file_get_original_path (file_a),
3832 thunar_file_get_original_path (file_b));
3833 }
3834
3835 return result;
3836}
3837
3838
3839
3840static gboolean
3841thunar_file_same_filesystem (const ThunarFile *file_a,
3842 const ThunarFile *file_b)
3843{
3844 const gchar *filesystem_id_a;
3845 const gchar *filesystem_id_b;
3846
3847 _thunar_return_val_if_fail (THUNAR_IS_FILE (file_a), FALSE);
3848 _thunar_return_val_if_fail (THUNAR_IS_FILE (file_b), FALSE);
3849
3850 /* return false if we have no information about one of the files */
3851 if (file_a->info == NULL || file_b->info == NULL)
3852 return FALSE;
3853
3854 /* determine the filesystem IDs */
3855 filesystem_id_a = g_file_info_get_attribute_string (file_a->info,
3856 G_FILE_ATTRIBUTE_ID_FILESYSTEM);
3857
3858 filesystem_id_b = g_file_info_get_attribute_string (file_b->info,
3859 G_FILE_ATTRIBUTE_ID_FILESYSTEM);
3860
3861 /* compare the filesystem IDs */
3862 return exo_str_is_equal (filesystem_id_a, filesystem_id_b);
3863}
3864
3865
3866
3867/**
3868 * thunar_file_cache_lookup:
3869 * @file : a #GFile.
3870 *
3871 * Looks up the #ThunarFile for @file in the internal file
3872 * cache and returns the file present for @file in the
3873 * cache or %NULL if no #ThunarFile is cached for @file.
3874 *
3875 * Note that no reference is taken for the caller.
3876 *
3877 * This method should not be used but in very rare cases.
3878 * Consider using thunar_file_get() instead.
3879 *
3880 * Return value: the #ThunarFile for @file in the internal
3881 * cache, or %NULL.
3882 **/
3883ThunarFile *
3884thunar_file_cache_lookup (const GFile *file)
3885{
3886 ThunarFile *cached_file;
3887
3888 _thunar_return_val_if_fail (G_IS_FILE (file), NULL);
3889
3890 G_LOCK (file_cache_mutex);
3891
3892 /* allocate the ThunarFile cache on-demand */
3893 if (G_UNLIKELY (file_cache == NULL))
3894 {
3895 file_cache = g_hash_table_new_full (g_file_hash,
3896 (GEqualFunc) g_file_equal,
3897 (GDestroyNotify) g_object_unref,
3898 NULL);
3899 }
3900
3901 cached_file = g_hash_table_lookup (file_cache, file);
3902
3903 G_UNLOCK (file_cache_mutex);
3904
3905 return cached_file;
3906}
3907
3908
3909
3910gchar *
3911thunar_file_cached_display_name (const GFile *file)
3912{
3913 ThunarFile *cached_file;
3914 gchar *display_name;
3915
3916 /* check if we have a ThunarFile for it in the cache (usually is the case) */
3917 cached_file = thunar_file_cache_lookup (file);
3918 if (cached_file != NULL)
3919 {
3920 /* determine the display name of the file */
3921 display_name = g_strdup (thunar_file_get_display_name (cached_file));
3922 }
3923 else
3924 {
3925 /* determine something a hopefully good approximation of the display name */
3926 display_name = thunar_g_file_get_display_name (G_FILE (file));
3927 }
3928
3929 return display_name;
3930}
3931
3932
3933
3934static gint
3935compare_app_infos (gconstpointer a,
3936 gconstpointer b)
3937{
3938 return g_app_info_equal (G_APP_INFO (a), G_APP_INFO (b)) ? 0 : 1;
3939}
3940
3941
3942
3943/**
3944 * thunar_file_list_get_applications:
3945 * @file_list : a #GList of #ThunarFile<!---->s.
3946 *
3947 * Returns the #GList of #GAppInfo<!---->s that can be used to open
3948 * all #ThunarFile<!---->s in the given @file_list.
3949 *
3950 * The caller is responsible to free the returned list using something like:
3951 * <informalexample><programlisting>
3952 * g_list_free_full (list, g_object_unref);
3953 * </programlisting></informalexample>
3954 *
3955 * Return value: the list of #GAppInfo<!---->s that can be used to open all
3956 * items in the @file_list.
3957 **/
3958GList*
3959thunar_file_list_get_applications (GList *file_list)
3960{
3961 GList *applications = NULL;
3962 GList *list;
3963 GList *next;
3964 GList *ap;
3965 GList *lp;
3966 const gchar *previous_type = NULL;
3967 const gchar *current_type;
3968
3969 /* determine the set of applications that can open all files */
3970 for (lp = file_list; lp != NULL; lp = lp->next)
3971 {
3972 current_type = thunar_file_get_content_type (lp->data);
3973
3974 /* no need to check anything if this file has the same mimetype as the previous file */
3975 if (current_type != NULL && previous_type != NULL)
3976 if (G_LIKELY (g_content_type_equals (previous_type, current_type)))
3977 continue;
3978
3979 /* store the previous type */
3980 previous_type = current_type;
3981
3982 /* determine the list of applications that can open this file */
3983 if (G_UNLIKELY (current_type != NULL))
3984 list = g_app_info_get_all_for_type (current_type);
3985 else
3986 list = NULL;
3987
3988 if (G_UNLIKELY (applications == NULL))
3989 {
3990 /* first file, so just use the applications list */
3991 applications = list;
3992 }
3993 else
3994 {
3995 /* keep only the applications that are also present in list */
3996 for (ap = applications; ap != NULL; ap = next)
3997 {
3998 /* grab a pointer on the next application */
3999 next = ap->next;
4000
4001 /* check if the application is present in list */
4002 if (g_list_find_custom (list, ap->data, compare_app_infos) == NULL)
4003 {
4004 /* drop our reference on the application */
4005 g_object_unref (G_OBJECT (ap->data));
4006
4007 /* drop this application from the list */
4008 applications = g_list_delete_link (applications, ap);
4009 }
4010 }
4011
4012 /* release the list of applications for this file */
4013 g_list_free_full (list, g_object_unref);
4014 }
4015
4016 /* check if the set is still not empty */
4017 if (G_LIKELY (applications == NULL))
4018 break;
4019 }
4020
4021 /* remove hidden applications */
4022 for (ap = applications; ap != NULL; ap = next)
4023 {
4024 /* grab a pointer on the next application */
4025 next = ap->next;
4026
4027 if (!thunar_g_app_info_should_show (ap->data))
4028 {
4029 /* drop our reference on the application */
4030 g_object_unref (G_OBJECT (ap->data));
4031
4032 /* drop this application from the list */
4033 applications = g_list_delete_link (applications, ap);
4034 }
4035 }
4036
4037 return applications;
4038}
4039
4040
4041
4042/**
4043 * thunar_file_list_to_thunar_g_file_list:
4044 * @file_list : a #GList of #ThunarFile<!---->s.
4045 *
4046 * Transforms the @file_list to a #GList of #GFile<!---->s for
4047 * the #ThunarFile<!---->s contained within @file_list.
4048 *
4049 * The caller is responsible to free the returned list using
4050 * thunar_g_file_list_free() when no longer needed.
4051 *
4052 * Return value: the list of #GFile<!---->s for @file_list.
4053 **/
4054GList*
4055thunar_file_list_to_thunar_g_file_list (GList *file_list)
4056{
4057 GList *list = NULL;
4058 GList *lp;
4059
4060 for (lp = g_list_last (file_list); lp != NULL; lp = lp->prev)
4061 list = g_list_prepend (list, g_object_ref (THUNAR_FILE (lp->data)->gfile));
4062
4063 return list;
4064}
40650
=== modified file '.pc/applied-patches'
--- .pc/applied-patches 2014-10-31 07:27:27 +0000
+++ .pc/applied-patches 2015-01-06 18:59:01 +0000
@@ -1,7 +1,2 @@
101_support-non-multiarch-modules.patch101_support-non-multiarch-modules.patch
202_fix-default-application-selection.patch
3git-xfdesktop-4.11.patch
4gtk3-bookmarks.patch
5git-save-keyboard-shortcuts.patch
6menu-icon-tweaks.patch2menu-icon-tweaks.patch
7git-force-toolbr-icons.patch
83
=== removed directory '.pc/git-force-toolbr-icons.patch'
=== removed directory '.pc/git-force-toolbr-icons.patch/thunar'
=== removed file '.pc/git-force-toolbr-icons.patch/thunar/thunar-window.c'
--- .pc/git-force-toolbr-icons.patch/thunar/thunar-window.c 2014-04-05 08:22:40 +0000
+++ .pc/git-force-toolbr-icons.patch/thunar/thunar-window.c 1970-01-01 00:00:00 +0000
@@ -1,3846 +0,0 @@
1/* vi:set et ai sw=2 sts=2 ts=2: */
2/*-
3 * Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org>
4 * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#ifdef HAVE_UNISTD_H
27#include <unistd.h>
28#endif
29#ifdef HAVE_LOCALE_H
30#include <locale.h>
31#endif
32
33#include <gdk/gdkkeysyms.h>
34#include <libxfce4ui/libxfce4ui.h>
35
36#include <thunar/thunar-application.h>
37#include <thunar/thunar-browser.h>
38#include <thunar/thunar-clipboard-manager.h>
39#include <thunar/thunar-compact-view.h>
40#include <thunar/thunar-details-view.h>
41#include <thunar/thunar-dialogs.h>
42#include <thunar/thunar-shortcuts-pane.h>
43#include <thunar/thunar-gio-extensions.h>
44#include <thunar/thunar-gobject-extensions.h>
45#include <thunar/thunar-gtk-extensions.h>
46#include <thunar/thunar-icon-view.h>
47#include <thunar/thunar-launcher.h>
48#include <thunar/thunar-location-buttons.h>
49#include <thunar/thunar-location-dialog.h>
50#include <thunar/thunar-location-entry.h>
51#include <thunar/thunar-marshal.h>
52#include <thunar/thunar-pango-extensions.h>
53#include <thunar/thunar-preferences-dialog.h>
54#include <thunar/thunar-preferences.h>
55#include <thunar/thunar-private.h>
56#include <thunar/thunar-util.h>
57#include <thunar/thunar-statusbar.h>
58#include <thunar/thunar-stock.h>
59#include <thunar/thunar-trash-action.h>
60#include <thunar/thunar-tree-pane.h>
61#include <thunar/thunar-window.h>
62#include <thunar/thunar-window-ui.h>
63#include <thunar/thunar-device-monitor.h>
64
65#include <glib.h>
66
67
68
69/* Property identifiers */
70enum
71{
72 PROP_0,
73 PROP_CURRENT_DIRECTORY,
74 PROP_SHOW_HIDDEN,
75 PROP_UI_MANAGER,
76 PROP_ZOOM_LEVEL,
77};
78
79/* Signal identifiers */
80enum
81{
82 BACK,
83 RELOAD,
84 TOGGLE_SIDEPANE,
85 TOGGLE_MENUBAR,
86 ZOOM_IN,
87 ZOOM_OUT,
88 ZOOM_RESET,
89 TAB_CHANGE,
90 LAST_SIGNAL,
91};
92
93
94
95static void thunar_window_dispose (GObject *object);
96static void thunar_window_finalize (GObject *object);
97static void thunar_window_get_property (GObject *object,
98 guint prop_id,
99 GValue *value,
100 GParamSpec *pspec);
101static void thunar_window_set_property (GObject *object,
102 guint prop_id,
103 const GValue *value,
104 GParamSpec *pspec);
105static gboolean thunar_window_back (ThunarWindow *window);
106static gboolean thunar_window_reload (ThunarWindow *window);
107static gboolean thunar_window_toggle_sidepane (ThunarWindow *window);
108static gboolean thunar_window_toggle_menubar (ThunarWindow *window);
109static void thunar_window_toggle_menubar_deactivate (GtkWidget *menubar,
110 ThunarWindow *window);
111static gboolean thunar_window_zoom_in (ThunarWindow *window);
112static gboolean thunar_window_zoom_out (ThunarWindow *window);
113static gboolean thunar_window_zoom_reset (ThunarWindow *window);
114static gboolean thunar_window_tab_change (ThunarWindow *window,
115 gint nth);
116static void thunar_window_realize (GtkWidget *widget);
117static void thunar_window_unrealize (GtkWidget *widget);
118static gboolean thunar_window_configure_event (GtkWidget *widget,
119 GdkEventConfigure *event);
120static void thunar_window_notebook_switch_page (GtkWidget *notebook,
121 GtkWidget *page,
122 guint page_num,
123 ThunarWindow *window);
124static void thunar_window_notebook_page_added (GtkWidget *notebook,
125 GtkWidget *page,
126 guint page_num,
127 ThunarWindow *window);
128static void thunar_window_notebook_page_removed (GtkWidget *notebook,
129 GtkWidget *page,
130 guint page_num,
131 ThunarWindow *window);
132static gboolean thunar_window_notebook_button_press_event (GtkWidget *notebook,
133 GdkEventButton *event,
134 ThunarWindow *window);
135static gboolean thunar_window_notebook_popup_menu (GtkWidget *notebook,
136 ThunarWindow *window);
137static gpointer thunar_window_notebook_create_window (GtkWidget *notebook,
138 GtkWidget *page,
139 gint x,
140 gint y,
141 ThunarWindow *window);
142static void thunar_window_notebook_insert (ThunarWindow *window,
143 ThunarFile *directory);
144static void thunar_window_merge_custom_preferences (ThunarWindow *window);
145static gboolean thunar_window_bookmark_merge (gpointer user_data);
146static void thunar_window_merge_go_actions (ThunarWindow *window);
147static void thunar_window_install_location_bar (ThunarWindow *window,
148 GType type);
149static void thunar_window_install_sidepane (ThunarWindow *window,
150 GType type);
151static void thunar_window_start_open_location (ThunarWindow *window,
152 const gchar *initial_text);
153static void thunar_window_action_open_new_tab (GtkAction *action,
154 ThunarWindow *window);
155static void thunar_window_action_open_new_window (GtkAction *action,
156 ThunarWindow *window);
157static void thunar_window_action_empty_trash (GtkAction *action,
158 ThunarWindow *window);
159static void thunar_window_action_detach_tab (GtkAction *action,
160 ThunarWindow *window);
161static void thunar_window_action_close_all_windows (GtkAction *action,
162 ThunarWindow *window);
163static void thunar_window_action_close_tab (GtkAction *action,
164 ThunarWindow *window);
165static void thunar_window_action_close_window (GtkAction *action,
166 ThunarWindow *window);
167static void thunar_window_action_preferences (GtkAction *action,
168 ThunarWindow *window);
169static void thunar_window_action_reload (GtkAction *action,
170 ThunarWindow *window);
171static void thunar_window_action_pathbar_changed (GtkToggleAction *action,
172 ThunarWindow *window);
173static void thunar_window_action_toolbar_changed (GtkToggleAction *action,
174 ThunarWindow *window);
175static void thunar_window_action_shortcuts_changed (GtkToggleAction *action,
176 ThunarWindow *window);
177static void thunar_window_action_tree_changed (GtkToggleAction *action,
178 ThunarWindow *window);
179static void thunar_window_action_statusbar_changed (GtkToggleAction *action,
180 ThunarWindow *window);
181static void thunar_window_action_menubar_changed (GtkToggleAction *action,
182 ThunarWindow *window);
183static void thunar_window_action_zoom_in (GtkAction *action,
184 ThunarWindow *window);
185static void thunar_window_action_zoom_out (GtkAction *action,
186 ThunarWindow *window);
187static void thunar_window_action_zoom_reset (GtkAction *action,
188 ThunarWindow *window);
189static void thunar_window_action_view_changed (GtkRadioAction *action,
190 GtkRadioAction *current,
191 ThunarWindow *window);
192static void thunar_window_action_go_up (GtkAction *action,
193 ThunarWindow *window);
194static void thunar_window_action_open_home (GtkAction *action,
195 ThunarWindow *window);
196static void thunar_window_action_open_desktop (GtkAction *action,
197 ThunarWindow *window);
198static void thunar_window_action_open_templates (GtkAction *action,
199 ThunarWindow *window);
200static void thunar_window_action_open_file_system (GtkAction *action,
201 ThunarWindow *window);
202static void thunar_window_action_open_trash (GtkAction *action,
203 ThunarWindow *window);
204static void thunar_window_action_open_network (GtkAction *action,
205 ThunarWindow *window);
206static void thunar_window_action_open_bookmark (GtkAction *action,
207 ThunarWindow *window);
208static void thunar_window_action_open_location (GtkAction *action,
209 ThunarWindow *window);
210static void thunar_window_action_contents (GtkAction *action,
211 ThunarWindow *window);
212static void thunar_window_action_about (GtkAction *action,
213 ThunarWindow *window);
214static void thunar_window_action_show_hidden (GtkToggleAction *action,
215 ThunarWindow *window);
216static void thunar_window_current_directory_changed (ThunarFile *current_directory,
217 ThunarWindow *window);
218static void thunar_window_connect_proxy (GtkUIManager *manager,
219 GtkAction *action,
220 GtkWidget *proxy,
221 ThunarWindow *window);
222static void thunar_window_disconnect_proxy (GtkUIManager *manager,
223 GtkAction *action,
224 GtkWidget *proxy,
225 ThunarWindow *window);
226static void thunar_window_menu_item_selected (GtkWidget *menu_item,
227 ThunarWindow *window);
228static void thunar_window_menu_item_deselected (GtkWidget *menu_item,
229 ThunarWindow *window);
230static void thunar_window_update_custom_actions (ThunarView *view,
231 GParamSpec *pspec,
232 ThunarWindow *window);
233static void thunar_window_notify_loading (ThunarView *view,
234 GParamSpec *pspec,
235 ThunarWindow *window);
236static void thunar_window_device_pre_unmount (ThunarDeviceMonitor *device_monitor,
237 ThunarDevice *device,
238 GFile *root_file,
239 ThunarWindow *window);
240static void thunar_window_device_changed (ThunarDeviceMonitor *device_monitor,
241 ThunarDevice *device,
242 ThunarWindow *window);
243static gboolean thunar_window_merge_idle (gpointer user_data);
244static void thunar_window_merge_idle_destroy (gpointer user_data);
245static gboolean thunar_window_save_paned (ThunarWindow *window);
246static gboolean thunar_window_save_geometry_timer (gpointer user_data);
247static void thunar_window_save_geometry_timer_destroy (gpointer user_data);
248static void thunar_window_set_zoom_level (ThunarWindow *window,
249 ThunarZoomLevel zoom_level);
250
251
252
253struct _ThunarWindowClass
254{
255 GtkWindowClass __parent__;
256
257 /* internal action signals */
258 gboolean (*back) (ThunarWindow *window);
259 gboolean (*reload) (ThunarWindow *window);
260 gboolean (*toggle_sidepane) (ThunarWindow *window);
261 gboolean (*toggle_menubar) (ThunarWindow *window);
262 gboolean (*zoom_in) (ThunarWindow *window);
263 gboolean (*zoom_out) (ThunarWindow *window);
264 gboolean (*zoom_reset) (ThunarWindow *window);
265 gboolean (*tab_change) (ThunarWindow *window,
266 gint idx);
267};
268
269struct _ThunarWindow
270{
271 GtkWindow __parent__;
272
273 /* support for custom preferences actions */
274 ThunarxProviderFactory *provider_factory;
275 guint custom_preferences_merge_id;
276
277 /* UI manager merge ID for go menu actions */
278 guint go_items_actions_merge_id;
279
280 /* UI manager merge ID for the bookmark actions */
281 guint bookmark_items_actions_merge_id;
282 GtkActionGroup *bookmark_action_group;
283 GFile *bookmark_file;
284 GFileMonitor *bookmark_monitor;
285 guint bookmark_reload_idle_id;
286
287 ThunarClipboardManager *clipboard;
288
289 ThunarPreferences *preferences;
290
291 ThunarIconFactory *icon_factory;
292
293 GtkActionGroup *action_group;
294 GtkUIManager *ui_manager;
295
296 /* to be able to change folder on "device-pre-unmount" if required */
297 ThunarDeviceMonitor *device_monitor;
298
299 /* closures for the menu_item_selected()/menu_item_deselected() callbacks */
300 GClosure *menu_item_selected_closure;
301 GClosure *menu_item_deselected_closure;
302
303 /* custom menu actions for the file menu */
304 GtkActionGroup *custom_actions;
305 guint custom_merge_id;
306
307 GtkWidget *table;
308 GtkWidget *menubar;
309 GtkWidget *spinner;
310 GtkWidget *paned;
311 GtkWidget *sidepane;
312 GtkWidget *view_box;
313 GtkWidget *notebook;
314 GtkWidget *view;
315 GtkWidget *statusbar;
316
317 GType view_type;
318 GSList *view_bindings;
319
320 /* support for two different styles of location bars */
321 GtkWidget *location_bar;
322 GtkWidget *location_toolbar;
323
324 ThunarLauncher *launcher;
325
326 ThunarFile *current_directory;
327
328 /* zoom-level support */
329 ThunarZoomLevel zoom_level;
330
331 /* menu merge idle source */
332 guint merge_idle_id;
333
334 /* support to remember window geometry */
335 guint save_geometry_timer_id;
336
337 /* support to toggle side pane using F9,
338 * see the toggle_sidepane() function.
339 */
340 GType toggle_sidepane_type;
341};
342
343
344
345static GtkActionEntry action_entries[] =
346{
347 { "file-menu", NULL, N_ ("_File"), NULL, },
348 { "new-tab", "tab-new", N_ ("New _Tab"), "<control>T", N_ ("Open a new tab for the displayed location"), G_CALLBACK (thunar_window_action_open_new_tab), },
349 { "new-window", "window-new", N_ ("New _Window"), "<control>N", N_ ("Open a new Thunar window for the displayed location"), G_CALLBACK (thunar_window_action_open_new_window), },
350 { "sendto-menu", NULL, N_ ("_Send To"), NULL, },
351 { "empty-trash", NULL, N_ ("_Empty Trash"), NULL, N_ ("Delete all files and folders in the Trash"), G_CALLBACK (thunar_window_action_empty_trash), },
352 { "detach-tab", NULL, N_ ("Detac_h Tab"), NULL, N_ ("Open current folder in a new window"), G_CALLBACK (thunar_window_action_detach_tab), },
353 { "close-all-windows", NULL, N_ ("Close _All Windows"), "<control><shift>W", N_ ("Close all Thunar windows"), G_CALLBACK (thunar_window_action_close_all_windows), },
354 { "close-tab", GTK_STOCK_CLOSE, N_ ("C_lose Tab"), "<control>W", N_ ("Close this folder"), G_CALLBACK (thunar_window_action_close_tab), },
355 { "close-window", GTK_STOCK_QUIT, N_ ("_Close Window"), "<control>Q", N_ ("Close this window"), G_CALLBACK (thunar_window_action_close_window), },
356 { "edit-menu", NULL, N_ ("_Edit"), NULL, },
357 { "preferences", GTK_STOCK_PREFERENCES, N_ ("Pr_eferences..."), NULL, N_ ("Edit Thunars Preferences"), G_CALLBACK (thunar_window_action_preferences), },
358 { "view-menu", NULL, N_ ("_View"), NULL, },
359 { "reload", GTK_STOCK_REFRESH, N_ ("_Reload"), "<control>R", N_ ("Reload the current folder"), G_CALLBACK (thunar_window_action_reload), },
360 { "view-location-selector-menu", NULL, N_ ("_Location Selector"), NULL, },
361 { "view-side-pane-menu", NULL, N_ ("_Side Pane"), NULL, },
362 { "zoom-in", GTK_STOCK_ZOOM_IN, N_ ("Zoom I_n"), "<control>plus", N_ ("Show the contents in more detail"), G_CALLBACK (thunar_window_action_zoom_in), },
363 { "zoom-out", GTK_STOCK_ZOOM_OUT, N_ ("Zoom _Out"), "<control>minus", N_ ("Show the contents in less detail"), G_CALLBACK (thunar_window_action_zoom_out), },
364 { "zoom-reset", GTK_STOCK_ZOOM_100, N_ ("Normal Si_ze"), "<control>0", N_ ("Show the contents at the normal size"), G_CALLBACK (thunar_window_action_zoom_reset), },
365 { "go-menu", NULL, N_ ("_Go"), NULL, },
366 { "open-parent", GTK_STOCK_GO_UP, N_ ("Open _Parent"), "<alt>Up", N_ ("Open the parent folder"), G_CALLBACK (thunar_window_action_go_up), },
367 { "open-home", GTK_STOCK_HOME, N_ ("_Home"), "<alt>Home", N_ ("Go to the home folder"), G_CALLBACK (thunar_window_action_open_home), },
368 { "open-desktop", THUNAR_STOCK_DESKTOP, N_ ("Desktop"), NULL, N_ ("Go to the desktop folder"), G_CALLBACK (thunar_window_action_open_desktop), },
369 { "open-file-system", GTK_STOCK_HARDDISK, N_ ("File System"), NULL, N_ ("Browse the file system"), G_CALLBACK (thunar_window_action_open_file_system), },
370 { "open-network", GTK_STOCK_NETWORK, N_("B_rowse Network"), NULL, N_ ("Browse local network connections"), G_CALLBACK (thunar_window_action_open_network), },
371 { "open-templates", THUNAR_STOCK_TEMPLATES, N_("T_emplates"), NULL, N_ ("Go to the templates folder"), G_CALLBACK (thunar_window_action_open_templates), },
372 { "open-location", NULL, N_ ("_Open Location..."), "<control>L", N_ ("Specify a location to open"), G_CALLBACK (thunar_window_action_open_location), },
373 { "help-menu", NULL, N_ ("_Help"), NULL, },
374 { "contents", GTK_STOCK_HELP, N_ ("_Contents"), "F1", N_ ("Display Thunar user manual"), G_CALLBACK (thunar_window_action_contents), },
375 { "about", GTK_STOCK_ABOUT, N_ ("_About"), NULL, N_ ("Display information about Thunar"), G_CALLBACK (thunar_window_action_about), },
376};
377
378static const GtkToggleActionEntry toggle_action_entries[] =
379{
380 { "show-hidden", NULL, N_ ("Show _Hidden Files"), "<control>H", N_ ("Toggles the display of hidden files in the current window"), G_CALLBACK (thunar_window_action_show_hidden), FALSE, },
381 { "view-location-selector-pathbar", NULL, N_ ("_Pathbar Style"), NULL, N_ ("Modern approach with buttons that correspond to folders"), G_CALLBACK (thunar_window_action_pathbar_changed), FALSE, },
382 { "view-location-selector-toolbar", NULL, N_ ("_Toolbar Style"), NULL, N_ ("Traditional approach with location bar and navigation buttons"), G_CALLBACK (thunar_window_action_toolbar_changed), FALSE, },
383 { "view-side-pane-shortcuts", NULL, N_ ("_Shortcuts"), "<control>B", N_ ("Toggles the visibility of the shortcuts pane"), G_CALLBACK (thunar_window_action_shortcuts_changed), FALSE, },
384 { "view-side-pane-tree", NULL, N_ ("_Tree"), "<control>E", N_ ("Toggles the visibility of the tree pane"), G_CALLBACK (thunar_window_action_tree_changed), FALSE, },
385 { "view-statusbar", NULL, N_ ("St_atusbar"), NULL, N_ ("Change the visibility of this window's statusbar"), G_CALLBACK (thunar_window_action_statusbar_changed), FALSE, },
386 { "view-menubar", NULL, N_ ("_Menubar"), "<control>M", N_ ("Change the visibility of this window's menubar"), G_CALLBACK (thunar_window_action_menubar_changed), TRUE, },
387};
388
389
390
391static guint window_signals[LAST_SIGNAL];
392
393
394
395G_DEFINE_TYPE_WITH_CODE (ThunarWindow, thunar_window, GTK_TYPE_WINDOW,
396 G_IMPLEMENT_INTERFACE (THUNAR_TYPE_BROWSER, NULL))
397
398
399
400static void
401thunar_window_class_init (ThunarWindowClass *klass)
402{
403 GtkWidgetClass *gtkwidget_class;
404 GtkBindingSet *binding_set;
405 GObjectClass *gobject_class;
406 guint i;
407
408 gobject_class = G_OBJECT_CLASS (klass);
409 gobject_class->dispose = thunar_window_dispose;
410 gobject_class->finalize = thunar_window_finalize;
411 gobject_class->get_property = thunar_window_get_property;
412 gobject_class->set_property = thunar_window_set_property;
413
414 gtkwidget_class = GTK_WIDGET_CLASS (klass);
415 gtkwidget_class->realize = thunar_window_realize;
416 gtkwidget_class->unrealize = thunar_window_unrealize;
417 gtkwidget_class->configure_event = thunar_window_configure_event;
418
419 klass->back = thunar_window_back;
420 klass->reload = thunar_window_reload;
421 klass->toggle_sidepane = thunar_window_toggle_sidepane;
422 klass->toggle_menubar = thunar_window_toggle_menubar;
423 klass->zoom_in = thunar_window_zoom_in;
424 klass->zoom_out = thunar_window_zoom_out;
425 klass->zoom_reset = thunar_window_zoom_reset;
426 klass->tab_change = thunar_window_tab_change;
427
428 /**
429 * ThunarWindow:current-directory:
430 *
431 * The directory currently displayed within this #ThunarWindow
432 * or %NULL.
433 **/
434 g_object_class_install_property (gobject_class,
435 PROP_CURRENT_DIRECTORY,
436 g_param_spec_object ("current-directory",
437 "current-directory",
438 "current-directory",
439 THUNAR_TYPE_FILE,
440 EXO_PARAM_READWRITE));
441
442 /**
443 * ThunarWindow:show-hidden:
444 *
445 * Whether to show hidden files in the current window.
446 **/
447 g_object_class_install_property (gobject_class,
448 PROP_SHOW_HIDDEN,
449 g_param_spec_boolean ("show-hidden",
450 "show-hidden",
451 "show-hidden",
452 FALSE,
453 EXO_PARAM_READABLE));
454
455 /**
456 * ThunarWindow:ui-manager:
457 *
458 * The #GtkUIManager used for this #ThunarWindow. This property
459 * can only be read and is garantied to always contain a valid
460 * #GtkUIManager instance (thus it's never %NULL).
461 **/
462 g_object_class_install_property (gobject_class,
463 PROP_UI_MANAGER,
464 g_param_spec_object ("ui-manager",
465 "ui-manager",
466 "ui-manager",
467 GTK_TYPE_UI_MANAGER,
468 EXO_PARAM_READABLE));
469
470 /**
471 * ThunarWindow:zoom-level:
472 *
473 * The #ThunarZoomLevel applied to the #ThunarView currently
474 * shown within this window.
475 **/
476 g_object_class_install_property (gobject_class,
477 PROP_ZOOM_LEVEL,
478 g_param_spec_enum ("zoom-level",
479 "zoom-level",
480 "zoom-level",
481 THUNAR_TYPE_ZOOM_LEVEL,
482 THUNAR_ZOOM_LEVEL_NORMAL,
483 EXO_PARAM_READWRITE));
484
485 /**
486 * ThunarWindow::back:
487 * @window : a #ThunarWindow instance.
488 *
489 * Emitted whenever the user requests to go to the
490 * previous visited folder. This is an internal
491 * signal used to bind the action to keys.
492 **/
493 window_signals[BACK] =
494 g_signal_new (I_("back"),
495 G_TYPE_FROM_CLASS (klass),
496 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
497 G_STRUCT_OFFSET (ThunarWindowClass, back),
498 g_signal_accumulator_true_handled, NULL,
499 _thunar_marshal_BOOLEAN__VOID,
500 G_TYPE_BOOLEAN, 0);
501
502 /**
503 * ThunarWindow::reload:
504 * @window : a #ThunarWindow instance.
505 *
506 * Emitted whenever the user requests to reload the contents
507 * of the currently displayed folder. This is an internal
508 * signal used to bind the action to keys.
509 **/
510 window_signals[RELOAD] =
511 g_signal_new (I_("reload"),
512 G_TYPE_FROM_CLASS (klass),
513 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
514 G_STRUCT_OFFSET (ThunarWindowClass, reload),
515 g_signal_accumulator_true_handled, NULL,
516 _thunar_marshal_BOOLEAN__VOID,
517 G_TYPE_BOOLEAN, 0);
518
519 /**
520 * ThunarWindow::toggle-sidepane:
521 * @window : a #ThunarWindow instance.
522 *
523 * Emitted whenever the user toggles the visibility of the
524 * sidepane. This is an internal signal used to bind the
525 * action to keys.
526 **/
527 window_signals[TOGGLE_SIDEPANE] =
528 g_signal_new (I_("toggle-sidepane"),
529 G_TYPE_FROM_CLASS (klass),
530 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
531 G_STRUCT_OFFSET (ThunarWindowClass, toggle_sidepane),
532 g_signal_accumulator_true_handled, NULL,
533 _thunar_marshal_BOOLEAN__VOID,
534 G_TYPE_BOOLEAN, 0);
535
536 /**
537 * ThunarWindow::toggle-sidepane:
538 * @window : a #ThunarWindow instance.
539 *
540 * Emitted whenever the user toggles the visibility of the
541 * sidepane. This is an internal signal used to bind the
542 * action to keys.
543 **/
544 window_signals[TOGGLE_MENUBAR] =
545 g_signal_new (I_("toggle-menubar"),
546 G_TYPE_FROM_CLASS (klass),
547 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
548 G_STRUCT_OFFSET (ThunarWindowClass, toggle_menubar),
549 g_signal_accumulator_true_handled, NULL,
550 _thunar_marshal_BOOLEAN__VOID,
551 G_TYPE_BOOLEAN, 0);
552
553 /**
554 * ThunarWindow::zoom-in:
555 * @window : a #ThunarWindow instance.
556 *
557 * Emitted whenever the user requests to zoom in. This
558 * is an internal signal used to bind the action to keys.
559 **/
560 window_signals[ZOOM_IN] =
561 g_signal_new (I_("zoom-in"),
562 G_TYPE_FROM_CLASS (klass),
563 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
564 G_STRUCT_OFFSET (ThunarWindowClass, zoom_in),
565 g_signal_accumulator_true_handled, NULL,
566 _thunar_marshal_BOOLEAN__VOID,
567 G_TYPE_BOOLEAN, 0);
568
569 /**
570 * ThunarWindow::zoom-out:
571 * @window : a #ThunarWindow instance.
572 *
573 * Emitted whenever the user requests to zoom out. This
574 * is an internal signal used to bind the action to keys.
575 **/
576 window_signals[ZOOM_OUT] =
577 g_signal_new (I_("zoom-out"),
578 G_TYPE_FROM_CLASS (klass),
579 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
580 G_STRUCT_OFFSET (ThunarWindowClass, zoom_out),
581 g_signal_accumulator_true_handled, NULL,
582 _thunar_marshal_BOOLEAN__VOID,
583 G_TYPE_BOOLEAN, 0);
584
585 /**
586 * ThunarWindow::zoom-reset:
587 * @window : a #ThunarWindow instance.
588 *
589 * Emitted whenever the user requests reset the zoom level.
590 * This is an internal signal used to bind the action to keys.
591 **/
592 window_signals[ZOOM_RESET] =
593 g_signal_new (I_("zoom-reset"),
594 G_TYPE_FROM_CLASS (klass),
595 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
596 G_STRUCT_OFFSET (ThunarWindowClass, zoom_reset),
597 g_signal_accumulator_true_handled, NULL,
598 _thunar_marshal_BOOLEAN__VOID,
599 G_TYPE_BOOLEAN, 0);
600
601 /**
602 * ThunarWindow::tab-chage:
603 * @window : a #ThunarWindow instance.
604 * @idx : tab index,
605 *
606 * Emitted whenever the user uses a Alt+N combination to
607 * switch tabs.
608 **/
609 window_signals[TAB_CHANGE] =
610 g_signal_new (I_("tab-change"),
611 G_TYPE_FROM_CLASS (klass),
612 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
613 G_STRUCT_OFFSET (ThunarWindowClass, tab_change),
614 g_signal_accumulator_true_handled, NULL,
615 _thunar_marshal_BOOLEAN__INT,
616 G_TYPE_BOOLEAN, 1,
617 G_TYPE_INT);
618
619 /* setup the key bindings for the windows */
620 binding_set = gtk_binding_set_by_class (klass);
621 gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0, "back", 0);
622 gtk_binding_entry_add_signal (binding_set, GDK_F5, 0, "reload", 0);
623 gtk_binding_entry_add_signal (binding_set, GDK_F9, 0, "toggle-sidepane", 0);
624 gtk_binding_entry_add_signal (binding_set, GDK_F10, 0, "toggle-menubar", 0);
625 gtk_binding_entry_add_signal (binding_set, GDK_KP_Add, GDK_CONTROL_MASK, "zoom-in", 0);
626 gtk_binding_entry_add_signal (binding_set, GDK_KP_Subtract, GDK_CONTROL_MASK, "zoom-out", 0);
627 gtk_binding_entry_add_signal (binding_set, GDK_KP_0, GDK_CONTROL_MASK, "zoom-reset", 0);
628 gtk_binding_entry_add_signal (binding_set, GDK_KP_Insert, GDK_CONTROL_MASK, "zoom-reset", 0);
629
630 /* setup the key bindings for Alt+N */
631 for (i = 0; i < 10; i++)
632 {
633 gtk_binding_entry_add_signal (binding_set, GDK_0 + i, GDK_MOD1_MASK,
634 "tab-change", 1, G_TYPE_UINT, i - 1);
635 }
636}
637
638
639
640static inline gint
641view_type2index (GType type)
642{
643 /* this necessary for platforms where sizeof(GType) != sizeof(gint),
644 * see http://bugzilla.xfce.org/show_bug.cgi?id=2726 for details.
645 */
646 if (sizeof (GType) == sizeof (gint))
647 {
648 /* no need to map anything */
649 return (gint) type;
650 }
651 else
652 {
653 /* map from types to unique indices */
654 if (G_LIKELY (type == THUNAR_TYPE_COMPACT_VIEW))
655 return 0;
656 else if (type == THUNAR_TYPE_DETAILS_VIEW)
657 return 1;
658 else
659 return 2;
660 }
661}
662
663
664
665static inline GType
666view_index2type (gint idx)
667{
668 /* this necessary for platforms where sizeof(GType) != sizeof(gint),
669 * see http://bugzilla.xfce.org/show_bug.cgi?id=2726 for details.
670 */
671 if (sizeof (GType) == sizeof (gint))
672 {
673 /* no need to map anything */
674 return (GType) idx;
675 }
676 else
677 {
678 /* map from indices to unique types */
679 switch (idx)
680 {
681 case 0: return THUNAR_TYPE_COMPACT_VIEW;
682 case 1: return THUNAR_TYPE_DETAILS_VIEW;
683 default: return THUNAR_TYPE_ICON_VIEW;
684 }
685 }
686}
687
688
689
690static void
691thunar_window_init (ThunarWindow *window)
692{
693 GtkRadioAction *radio_action;
694 GtkAccelGroup *accel_group;
695 GtkWidget *label;
696 GtkWidget *infobar;
697 GtkWidget *item;
698 GtkAction *action;
699 gboolean last_show_hidden;
700 gboolean last_menubar_visible;
701 GSList *group;
702 gchar *last_location_bar;
703 gchar *last_side_pane;
704 GType type;
705 gint last_separator_position;
706 gint last_window_width;
707 gint last_window_height;
708 gboolean last_window_maximized;
709 gboolean last_statusbar_visible;
710 GtkRcStyle *style;
711
712 /* unset the view type */
713 window->view_type = G_TYPE_NONE;
714
715 /* grab a reference on the provider factory */
716 window->provider_factory = thunarx_provider_factory_get_default ();
717
718 /* grab a reference on the preferences */
719 window->preferences = thunar_preferences_get ();
720
721 /* get all properties for init */
722 g_object_get (G_OBJECT (window->preferences),
723 "last-show-hidden", &last_show_hidden,
724 "last-window-width", &last_window_width,
725 "last-window-height", &last_window_height,
726 "last-window-maximized", &last_window_maximized,
727 "last-menubar-visible", &last_menubar_visible,
728 "last-separator-position", &last_separator_position,
729 "last-location-bar", &last_location_bar,
730 "last-side-pane", &last_side_pane,
731 "last-statusbar-visible", &last_statusbar_visible,
732 NULL);
733
734 /* connect to the volume monitor */
735 window->device_monitor = thunar_device_monitor_get ();
736 g_signal_connect (window->device_monitor, "device-pre-unmount", G_CALLBACK (thunar_window_device_pre_unmount), window);
737 g_signal_connect (window->device_monitor, "device-removed", G_CALLBACK (thunar_window_device_changed), window);
738 g_signal_connect (window->device_monitor, "device-changed", G_CALLBACK (thunar_window_device_changed), window);
739
740 /* allocate a closure for the menu_item_selected() callback */
741 window->menu_item_selected_closure = g_cclosure_new_object (G_CALLBACK (thunar_window_menu_item_selected), G_OBJECT (window));
742 g_closure_ref (window->menu_item_selected_closure);
743 g_closure_sink (window->menu_item_selected_closure);
744
745 /* allocate a closure for the menu_item_deselected() callback */
746 window->menu_item_deselected_closure = g_cclosure_new_object (G_CALLBACK (thunar_window_menu_item_deselected), G_OBJECT (window));
747 g_closure_ref (window->menu_item_deselected_closure);
748 g_closure_sink (window->menu_item_deselected_closure);
749 window->icon_factory = thunar_icon_factory_get_default ();
750
751 /* setup the action group for this window */
752 window->action_group = gtk_action_group_new ("ThunarWindow");
753 gtk_action_group_set_translation_domain (window->action_group, GETTEXT_PACKAGE);
754 gtk_action_group_add_actions (window->action_group, action_entries, G_N_ELEMENTS (action_entries), GTK_WIDGET (window));
755 gtk_action_group_add_toggle_actions (window->action_group, toggle_action_entries, G_N_ELEMENTS (toggle_action_entries), GTK_WIDGET (window));
756
757 /* initialize the "show-hidden" action using the last value from the preferences */
758 action = gtk_action_group_get_action (window->action_group, "show-hidden");
759 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), last_show_hidden);
760
761 /*
762 * add view options
763 */
764 radio_action = gtk_radio_action_new ("view-as-icons", _("View as _Icons"), _("Display folder content in an icon view"),
765 NULL, view_type2index (THUNAR_TYPE_ICON_VIEW));
766 gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (radio_action), "<control>1");
767 gtk_radio_action_set_group (radio_action, NULL);
768 group = gtk_radio_action_get_group (radio_action);
769 g_object_unref (G_OBJECT (radio_action));
770
771 radio_action = gtk_radio_action_new ("view-as-detailed-list", _("View as _Detailed List"), _("Display folder content in a detailed list view"),
772 NULL, view_type2index (THUNAR_TYPE_DETAILS_VIEW));
773 gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (radio_action), "<control>2");
774 gtk_radio_action_set_group (radio_action, group);
775 group = gtk_radio_action_get_group (radio_action);
776 g_object_unref (G_OBJECT (radio_action));
777
778 radio_action = gtk_radio_action_new ("view-as-compact-list", _("View as _Compact List"), _("Display folder content in a compact list view"),
779 NULL, view_type2index (THUNAR_TYPE_COMPACT_VIEW));
780 gtk_action_group_add_action_with_accel (window->action_group, GTK_ACTION (radio_action), "<control>3");
781 gtk_radio_action_set_group (radio_action, group);
782 group = gtk_radio_action_get_group (radio_action);
783 g_object_unref (G_OBJECT (radio_action));
784
785 window->ui_manager = gtk_ui_manager_new ();
786 g_signal_connect (G_OBJECT (window->ui_manager), "connect-proxy", G_CALLBACK (thunar_window_connect_proxy), window);
787 g_signal_connect (G_OBJECT (window->ui_manager), "disconnect-proxy", G_CALLBACK (thunar_window_disconnect_proxy), window);
788 gtk_ui_manager_insert_action_group (window->ui_manager, window->action_group, 0);
789 gtk_ui_manager_add_ui_from_string (window->ui_manager, thunar_window_ui, thunar_window_ui_length, NULL);
790
791 accel_group = gtk_ui_manager_get_accel_group (window->ui_manager);
792 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
793
794 /* setup the launcher support */
795 window->launcher = thunar_launcher_new ();
796 thunar_launcher_set_widget (window->launcher, GTK_WIDGET (window));
797 thunar_component_set_ui_manager (THUNAR_COMPONENT (window->launcher), window->ui_manager);
798 exo_binding_new (G_OBJECT (window), "current-directory", G_OBJECT (window->launcher), "current-directory");
799 g_signal_connect_swapped (G_OBJECT (window->launcher), "change-directory", G_CALLBACK (thunar_window_set_current_directory), window);
800 g_signal_connect_swapped (G_OBJECT (window->launcher), "open-new-tab", G_CALLBACK (thunar_window_notebook_insert), window);
801
802 /* determine the default window size from the preferences */
803 gtk_window_set_default_size (GTK_WINDOW (window), last_window_width, last_window_height);
804
805 /* restore the maxized state of the window */
806 if (G_UNLIKELY (last_window_maximized))
807 gtk_window_maximize (GTK_WINDOW (window));
808
809 window->table = gtk_table_new (6, 1, FALSE);
810 gtk_container_add (GTK_CONTAINER (window), window->table);
811 gtk_widget_show (window->table);
812
813 window->menubar = gtk_ui_manager_get_widget (window->ui_manager, "/main-menu");
814 gtk_table_attach (GTK_TABLE (window->table), window->menubar, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
815
816 /* update menubar visibiliy */
817 action = gtk_action_group_get_action (window->action_group, "view-menubar");
818 g_signal_connect (G_OBJECT (window->menubar), "deactivate", G_CALLBACK (thunar_window_toggle_menubar_deactivate), window);
819 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), last_menubar_visible);
820
821 /* append the menu item for the spinner */
822 item = gtk_menu_item_new ();
823 gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
824 gtk_menu_item_set_right_justified (GTK_MENU_ITEM (item), TRUE);
825 gtk_menu_shell_append (GTK_MENU_SHELL (window->menubar), item);
826 gtk_widget_show (item);
827
828 /* place the spinner into the menu item */
829 window->spinner = gtk_spinner_new ();
830 gtk_container_add (GTK_CONTAINER (item), window->spinner);
831 exo_binding_new (G_OBJECT (window->spinner), "active",
832 G_OBJECT (window->spinner), "visible");
833
834 /* check if we need to add the root warning */
835 if (G_UNLIKELY (geteuid () == 0))
836 {
837 /* add the bar for the root warning */
838 infobar = gtk_info_bar_new ();
839 gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_WARNING);
840 gtk_table_attach (GTK_TABLE (window->table), infobar, 0, 1, 2, 3, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
841 gtk_widget_show (infobar);
842
843 /* add the label with the root warning */
844 label = gtk_label_new (_("Warning, you are using the root account, you may harm your system."));
845 gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))), label);
846 gtk_widget_show (label);
847 }
848
849 window->paned = gtk_hpaned_new ();
850 gtk_container_set_border_width (GTK_CONTAINER (window->paned), 0);
851 gtk_table_attach (GTK_TABLE (window->table), window->paned, 0, 1, 4, 5, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
852 gtk_widget_show (window->paned);
853
854 /* determine the last separator position and apply it to the paned view */
855 gtk_paned_set_position (GTK_PANED (window->paned), last_separator_position);
856 g_signal_connect_swapped (window->paned, "accept-position", G_CALLBACK (thunar_window_save_paned), window);
857 g_signal_connect_swapped (window->paned, "button-release-event", G_CALLBACK (thunar_window_save_paned), window);
858
859 window->view_box = gtk_table_new (3, 1, FALSE);
860 gtk_paned_pack2 (GTK_PANED (window->paned), window->view_box, TRUE, FALSE);
861 gtk_widget_show (window->view_box);
862
863 /* tabs */
864 window->notebook = gtk_notebook_new ();
865 gtk_table_attach (GTK_TABLE (window->view_box), window->notebook, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
866 g_signal_connect (G_OBJECT (window->notebook), "switch-page", G_CALLBACK (thunar_window_notebook_switch_page), window);
867 g_signal_connect (G_OBJECT (window->notebook), "page-added", G_CALLBACK (thunar_window_notebook_page_added), window);
868 g_signal_connect (G_OBJECT (window->notebook), "page-removed", G_CALLBACK (thunar_window_notebook_page_removed), window);
869 g_signal_connect_after (G_OBJECT (window->notebook), "button-press-event", G_CALLBACK (thunar_window_notebook_button_press_event), window);
870 g_signal_connect (G_OBJECT (window->notebook), "popup-menu", G_CALLBACK (thunar_window_notebook_popup_menu), window);
871 g_signal_connect (G_OBJECT (window->notebook), "create-window", G_CALLBACK (thunar_window_notebook_create_window), window);
872 gtk_notebook_set_show_border (GTK_NOTEBOOK (window->notebook), FALSE);
873 gtk_notebook_set_homogeneous_tabs (GTK_NOTEBOOK (window->notebook), TRUE);
874 gtk_notebook_set_scrollable (GTK_NOTEBOOK (window->notebook), TRUE);
875 gtk_container_set_border_width (GTK_CONTAINER (window->notebook), 0);
876 gtk_notebook_set_group_name (GTK_NOTEBOOK (window->notebook), "thunar-tabs");
877 gtk_widget_show (window->notebook);
878
879 /* drop the notebook borders */
880 style = gtk_rc_style_new ();
881 style->xthickness = style->ythickness = 0;
882 gtk_widget_modify_style (window->notebook, style);
883 g_object_unref (G_OBJECT (style));
884
885 /* determine the selected location selector */
886 if (exo_str_is_equal (last_location_bar, g_type_name (THUNAR_TYPE_LOCATION_BUTTONS)))
887 type = THUNAR_TYPE_LOCATION_BUTTONS;
888 else if (exo_str_is_equal (last_location_bar, g_type_name (THUNAR_TYPE_LOCATION_ENTRY)))
889 type = THUNAR_TYPE_LOCATION_ENTRY;
890 else
891 type = G_TYPE_NONE;
892 g_free (last_location_bar);
893
894 /* activate the selected location selector */
895 action = gtk_action_group_get_action (window->action_group, "view-location-selector-pathbar");
896 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), (type == THUNAR_TYPE_LOCATION_BUTTONS));
897 action = gtk_action_group_get_action (window->action_group, "view-location-selector-toolbar");
898 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), (type == THUNAR_TYPE_LOCATION_ENTRY));
899
900 /* determine the selected side pane (FIXME: Should probably be last-shortcuts-visible and last-tree-visible preferences) */
901 if (exo_str_is_equal (last_side_pane, g_type_name (THUNAR_TYPE_SHORTCUTS_PANE)))
902 type = THUNAR_TYPE_SHORTCUTS_PANE;
903 else if (exo_str_is_equal (last_side_pane, g_type_name (THUNAR_TYPE_TREE_PANE)))
904 type = THUNAR_TYPE_TREE_PANE;
905 else
906 type = G_TYPE_NONE;
907 g_free (last_side_pane);
908
909 /* activate the selected side pane */
910 action = gtk_action_group_get_action (window->action_group, "view-side-pane-shortcuts");
911 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), (type == THUNAR_TYPE_SHORTCUTS_PANE));
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: