SHARE
TWEET

Untitled

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