]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilesystemmodel.c
Revert the patch to #137520, as 2.4.1 is for conservative bug fixes only.
[~andy/gtk] / gtk / gtkfilesystemmodel.c
index 184f9d43780a84d08110762bd68227f607fb89e6..79e08674ccc3f599043b0bcb67a8aff53b383832 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
+#include <config.h>
 #include <string.h>
 
 #include "gtkfilesystemmodel.h"
 #include "gtkfilesystem.h"
 #include "gtkintl.h"
+#include "gtktreednd.h"
 #include "gtktreemodel.h"
 
 typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
@@ -45,6 +47,7 @@ struct _GtkFileSystemModel
   GtkFileInfoType types;
   FileModelNode  *roots;
   GtkFileFolder  *root_folder;
+  GtkFilePath    *root_path;
 
   GtkFileSystemModelFilter filter_func;
   gpointer filter_data;
@@ -58,6 +61,7 @@ struct _GtkFileSystemModel
   guint show_folders : 1;
   guint show_files : 1;
   guint folders_only : 1;
+  guint has_editable : 1;
 };
 
 struct _FileModelNode
@@ -89,6 +93,8 @@ static void gtk_file_system_model_iface_init   (GtkTreeModelIface       *iface);
 static void gtk_file_system_model_init         (GtkFileSystemModel      *model);
 static void gtk_file_system_model_finalize     (GObject                 *object);
 
+static void drag_source_iface_init (GtkTreeDragSourceIface *iface);
+
 static GtkTreeModelFlags gtk_file_system_model_get_flags       (GtkTreeModel *tree_model);
 static gint              gtk_file_system_model_get_n_columns   (GtkTreeModel *tree_model);
 static GType             gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
@@ -123,6 +129,12 @@ static void              gtk_file_system_model_ref_node        (GtkTreeModel *tr
 static void              gtk_file_system_model_unref_node      (GtkTreeModel *tree_model,
                                                                GtkTreeIter  *iter);
 
+static gboolean drag_source_row_draggable (GtkTreeDragSource   *drag_source,
+                                          GtkTreePath         *path);
+static gboolean drag_source_drag_data_get (GtkTreeDragSource   *drag_source,
+                                          GtkTreePath         *path,
+                                          GtkSelectionData    *selection_data);
+
 static FileModelNode *file_model_node_new        (GtkFileSystemModel *model,
                                                  const GtkFilePath  *path);
 static void           file_model_node_free       (FileModelNode      *node);
@@ -143,8 +155,10 @@ static void               file_model_node_clear        (GtkFileSystemModel *mode
 static FileModelNode *    file_model_node_get_children (GtkFileSystemModel *model,
                                                        FileModelNode      *node);
 
+#if 0
 static void roots_changed_callback (GtkFileSystem      *file_system,
                                    GtkFileSystemModel *model);
+#endif
                                    
 static void deleted_callback       (GtkFileFolder *folder,
                                    FileModelNode *node);
@@ -170,6 +184,8 @@ static void root_files_removed_callback (GtkFileFolder      *folder,
                                         GSList             *paths,
                                         GtkFileSystemModel *model);
 
+static GObjectClass *parent_class = NULL;
+
 GType
 _gtk_file_system_model_get_type (void)
 {
@@ -193,8 +209,15 @@ _gtk_file_system_model_get_type (void)
       static const GInterfaceInfo file_system_info =
       {
        (GInterfaceInitFunc) gtk_file_system_model_iface_init, /* interface_init */
-       NULL,                                                 /* interface_finalize */
-       NULL                                                  /* interface_data */
+       NULL,                                                  /* interface_finalize */
+       NULL                                                   /* interface_data */
+      };
+
+      static const GInterfaceInfo drag_source_info =
+      {
+       (GInterfaceInitFunc) drag_source_iface_init,           /* interface_init */
+       NULL,                                                  /* interface_finalize */
+       NULL                                                   /* interface_data */
       };
 
       file_system_model_type = g_type_register_static (G_TYPE_OBJECT,
@@ -203,6 +226,9 @@ _gtk_file_system_model_get_type (void)
       g_type_add_interface_static (file_system_model_type,
                                   GTK_TYPE_TREE_MODEL,
                                   &file_system_info);
+      g_type_add_interface_static (file_system_model_type,
+                                  GTK_TYPE_TREE_DRAG_SOURCE,
+                                  &drag_source_info);
     }
 
   return file_system_model_type;
@@ -212,7 +238,9 @@ static void
 gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
-  
+
+  parent_class = g_type_class_peek_parent (class);
+
   gobject_class->finalize = gtk_file_system_model_finalize;
 }
 
@@ -252,6 +280,12 @@ gtk_file_system_model_finalize (GObject *object)
   if (model->root_folder)
     g_object_unref (model->root_folder);
 
+  if (model->root_path)
+    gtk_file_path_free (model->root_path);
+
+  if (model->file_system)
+    g_object_unref (model->file_system);
+
   children = model->roots;
   while (children)
     {
@@ -259,6 +293,16 @@ gtk_file_system_model_finalize (GObject *object)
       file_model_node_free (children);
       children = next;
     }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+drag_source_iface_init (GtkTreeDragSourceIface *iface)
+{
+  iface->row_draggable = drag_source_row_draggable;
+  iface->drag_data_get = drag_source_drag_data_get;
+  iface->drag_data_delete = NULL;
 }
 
 /*
@@ -271,7 +315,7 @@ gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
   GtkTreeModelFlags flags = GTK_TREE_MODEL_ITERS_PERSIST;
 
-  if (model->max_depth == 1)
+  if (model->max_depth == 0)
     flags |= GTK_TREE_MODEL_LIST_ONLY;
 
   return flags;
@@ -369,18 +413,31 @@ gtk_file_system_model_get_value (GtkTreeModel *tree_model,
 {
   GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
   FileModelNode *node = iter->user_data;
+  const GtkFileInfo *info;
   
   switch (column)
     {
     case GTK_FILE_SYSTEM_MODEL_INFO:
+      if (model->has_editable && node == model->roots)
+       info = NULL;
+      else
+       info = file_model_node_get_info (model, node);
+
       g_value_init (value, GTK_TYPE_FILE_INFO);
-      g_value_set_boxed (value, file_model_node_get_info (model, node));
+      g_value_set_boxed (value, info);
       break;
     case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME:
       {
-       const GtkFileInfo *info = file_model_node_get_info (model, node);
        g_value_init (value, G_TYPE_STRING);
-       g_value_set_string (value, gtk_file_info_get_display_name (info));
+
+       if (model->has_editable && node == model->roots)
+         g_value_set_string (value, "");
+       else
+         {
+           const GtkFileInfo *info = file_model_node_get_info (model, node);
+
+           g_value_set_string (value, gtk_file_info_get_display_name (info));
+         }
       }
       break;
     default:
@@ -536,12 +593,64 @@ gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
                         iter->user_data);
 }
 
+static gboolean
+drag_source_row_draggable (GtkTreeDragSource *drag_source,
+                          GtkTreePath       *path)
+{
+  GtkFileSystemModel *model;
+  GtkTreeIter iter;
+  FileModelNode *node;
+
+  model = GTK_FILE_SYSTEM_MODEL (drag_source);
+
+  if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
+    return FALSE;
+
+  if (!model->has_editable)
+    return TRUE;
+
+  node = iter.user_data;
+  return (node != model->roots);
+}
+
+static gboolean
+drag_source_drag_data_get (GtkTreeDragSource *drag_source,
+                          GtkTreePath       *path,
+                          GtkSelectionData  *selection_data)
+{
+  GtkFileSystemModel *model;
+  GtkTreeIter iter;
+  const GtkFilePath *file_path;
+  char *uri;
+  char *uris;
+
+  model = GTK_FILE_SYSTEM_MODEL (drag_source);
+
+  if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
+    return FALSE;
+
+  file_path = _gtk_file_system_model_get_path (model, &iter);
+  g_assert (file_path != NULL);
+
+  uri = gtk_file_system_path_to_uri (model->file_system, file_path);
+  uris = g_strconcat (uri, "\r\n", NULL);
+
+  gtk_selection_data_set (selection_data,
+                         gdk_atom_intern ("text/uri-list", FALSE),
+                         8,
+                         uris,
+                         strlen (uris) + 1);
+
+  g_free (uri);
+  g_free (uris);
+
+  return TRUE;
+}
+
 /**
  * _gtk_file_system_model_new:
  * @file_system: an object implementing #GtkFileSystem
- * @root_path: the path of root of the file system to display,
- *            or %NULL to display starting from the
- *            root or roots of the fielsystem.
+ * @root_path: the path of root of the file system to display
  * @max_depth: the maximum depth from the children of @root_path
  *             or the roots of the file system to display in
  *             the file selector). A depth of 0 displays
@@ -571,6 +680,7 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
   GSList *tmp_list;
 
   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
+  g_return_val_if_fail (root_path != NULL, NULL);
 
   model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
   model->file_system = g_object_ref (file_system);
@@ -583,7 +693,8 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
   if (root_path)
     {
       GSList *child_paths;
-      
+
+      model->root_path = gtk_file_path_copy (root_path);
       model->root_folder = gtk_file_system_get_folder (file_system, root_path,
                                                       model->types,
                                                       NULL);   /* NULL-GError */
@@ -605,12 +716,14 @@ _gtk_file_system_model_new (GtkFileSystem     *file_system,
                                   G_CALLBACK (root_files_removed_callback), model, 0);
        }
     }
+#if 0
   else
     {
       roots = gtk_file_system_list_roots (file_system);
       g_signal_connect_object (file_system, "roots-changed",
                               G_CALLBACK (roots_changed_callback), model, 0);
     }
+#endif
 
   roots = gtk_file_paths_sort (roots);
   
@@ -669,9 +782,8 @@ model_refilter_recurse (GtkFileSystemModel *model,
          GtkTreeIter iter;
 
          iter.user_data = nodes;
-         gtk_tree_model_row_inserted (tree_model, path, &iter);
-
          nodes->is_visible = TRUE;
+         gtk_tree_model_row_inserted (tree_model, path, &iter);
        }
       else
        model_refilter_recurse (model, nodes, path);
@@ -781,13 +893,22 @@ _gtk_file_system_model_set_show_files (GtkFileSystemModel *model,
  *   is owned by @model and must not be modified or freed.
  *   If you want to save the information for later use,
  *   you must make a copy, since the structure may be
- *   freed on later changes to the file system.
+ *   freed on later changes to the file system.  If you have
+ *   called _gtk_file_system_model_add_editable() and the @iter
+ *   corresponds to the row that this function returned, the
+ *   return value will be NULL.
  **/
 const GtkFileInfo *
 _gtk_file_system_model_get_info (GtkFileSystemModel *model,
                                 GtkTreeIter        *iter)
 {
-  return file_model_node_get_info (model, iter->user_data);
+  FileModelNode *node;
+
+  node = iter->user_data;
+  if (model->has_editable && node == model->roots)
+    return NULL;
+  else
+    return file_model_node_get_info (model, node);
 }
 
 /**
@@ -808,6 +929,9 @@ _gtk_file_system_model_get_path (GtkFileSystemModel *model,
 {
   FileModelNode *node = iter->user_data;
 
+  if (model->has_editable && node == model->roots)
+    return NULL;
+
   if (node->is_dummy)
     return node->parent->path;
   else
@@ -859,16 +983,14 @@ find_and_ref_path (GtkFileSystemModel  *model,
   FileModelNode *child_node;
   GtkFileFolder *folder;
 
-  if (!gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
+  if (gtk_file_path_compare (path, model->root_path) == 0
+      || !gtk_file_system_get_parent (model->file_system, path, &parent_path, NULL))
     return NULL;
 
   if (parent_path)
     {
       parent_node = find_and_ref_path (model, parent_path, cleanups);
       gtk_file_path_free (parent_path);
-
-      if (!parent_node)
-       return NULL;
     }
   else
     parent_node = NULL;
@@ -980,6 +1102,71 @@ _gtk_file_system_model_path_do (GtkFileSystemModel       *model,
   return node != NULL;
 }
 
+/**
+ * _gtk_file_system_model_add_editable:
+ * @model: a #GtkFileSystemModel
+ * @iter: Location to return the iter corresponding to the editable row
+ * 
+ * Adds an "empty" row at the beginning of the model.  This does not refer to
+ * any file, but is a temporary placeholder for a file name that the user will
+ * type when a corresponding cell is made editable.  When your code is done
+ * using this temporary row, call _gtk_file_system_model_remove_editable().
+ **/
+void
+_gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter)
+{
+  FileModelNode *node;
+  GtkTreePath *path;
+
+  g_return_if_fail (!model->has_editable);
+
+  model->has_editable = TRUE;
+
+  node = file_model_node_new (model, NULL);
+  node->is_visible = TRUE;
+
+  node->next = model->roots;
+  model->roots = node;
+
+  file_model_node_ref (node);
+
+  path = gtk_tree_path_new ();
+  gtk_tree_path_append_index (path, 0);
+  iter->user_data = node;
+
+  gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter);
+
+  gtk_tree_path_free (path);
+}
+
+/**
+ * _gtk_file_system_model_remove_editable:
+ * @model: a #GtkFileSystemModel
+ * 
+ * Removes the "empty" row at the beginning of the model that was
+ * created with _gtk_file_system_model_add_editable().  You should call
+ * this function when your code is finished editing this temporary row.
+ **/
+void
+_gtk_file_system_model_remove_editable (GtkFileSystemModel *model)
+{
+  GtkTreePath *path;
+
+  g_return_if_fail (model->has_editable);
+
+  model->has_editable = FALSE;
+  file_model_node_unref (model, model->roots);
+
+  model->roots = model->roots->next;
+
+  path = gtk_tree_path_new ();
+  gtk_tree_path_append_index (path, 0);
+
+  gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+
+  gtk_tree_path_free (path);
+}
+
 static FileModelNode *
 file_model_node_new (GtkFileSystemModel *model,
                     const GtkFilePath  *path)
@@ -1023,6 +1210,7 @@ file_model_node_get_info (GtkFileSystemModel *model,
                                                 node->path,
                                                 NULL); /* NULL-GError */
        }
+#if 0
       else
        {
          node->info = gtk_file_system_get_root_info (model->file_system,
@@ -1030,6 +1218,7 @@ file_model_node_get_info (GtkFileSystemModel *model,
                                                      model->types,
                                                      NULL);  /* NULL-GError */
        }
+#endif
     }
 
   return node->info;
@@ -1039,24 +1228,32 @@ static gboolean
 file_model_node_is_visible (GtkFileSystemModel *model,
                            FileModelNode      *node)
 {
-  if (model->show_hidden && model->show_folders && model->show_files)
-    return TRUE;
-  else
+  if (model->show_folders != model->show_files ||
+      !model->show_hidden ||
+      model->filter_func)
     {
       const GtkFileInfo *info = file_model_node_get_info (model, node);
-      gboolean is_folder = gtk_file_info_get_is_folder (info);
 
-      if (!model->show_folders && is_folder)
-       return FALSE;
-      if (!model->show_files && !is_folder)
+      if (!info)
+       {
+         /* File probably disappeared underneath us or resides in a
+            directory where we have only partial access rights.  */
+         return FALSE;
+       }
+
+      if (model->show_folders != model->show_files &&
+         model->show_folders != gtk_file_info_get_is_folder (info))
        return FALSE;
+
       if (!model->show_hidden && gtk_file_info_get_is_hidden (info))
        return FALSE;
-      if (model->filter_func && !model->filter_func (model, node->path, info, model->filter_data))
-       return FALSE;
 
-      return TRUE;
+      if (model->filter_func &&
+         !model->filter_func (model, node->path, info, model->filter_data))
+       return FALSE;
     }
+
+  return TRUE;
 }
 
 static void
@@ -1557,6 +1754,7 @@ do_files_removed (GtkFileSystemModel *model,
   g_slist_free (sorted_paths);
 }
 
+#if 0
 static void
 roots_changed_callback (GtkFileSystem      *file_system,
                        GtkFileSystemModel *model)
@@ -1653,6 +1851,7 @@ roots_changed_callback (GtkFileSystem      *file_system,
   g_slist_free (new_roots);
   gtk_tree_path_free (path);
 }
+#endif
 
 static void
 deleted_callback (GtkFileFolder      *folder,