gtkfilesystemmodel.c 54.6 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Owen Taylor's avatar
Owen Taylor committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * gtkfilesystemmodel.c: GtkTreeModel wrapping a GtkFileSystem
 * Copyright (C) 2003, Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

21
#include "config.h"
22

Owen Taylor's avatar
Owen Taylor committed
23
#include "gtkfilesystemmodel.h"
24 25 26 27

#include <stdlib.h>
#include <string.h>

28
#include "gtkfilesystem.h"
29
#include "gtkintl.h"
Federico Mena Quintero's avatar
Federico Mena Quintero committed
30
#include "gtkmarshalers.h"
31
#include "gtktreedatalist.h"
32
#include "gtktreednd.h"
33
#include "gtktreemodel.h"
34
#include "gtkalias.h"
Owen Taylor's avatar
Owen Taylor committed
35

36
/*** DEFINES ***/
37

38 39 40 41
/* priority used for all async callbacks in the main loop
 * This should be higher than redraw priorities so multiple callbacks
 * firing can be handled without intermediate redraws */
#define IO_PRIORITY G_PRIORITY_DEFAULT
42

43 44
/* random number that everyone else seems to use, too */
#define FILES_PER_QUERY 100
45

46 47
typedef struct _FileModelNode           FileModelNode;
typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass;
48

49 50 51 52
struct _FileModelNode
{
  GFile *               file;           /* file represented by this node or NULL for editable */
  GFileInfo *           info;           /* info for this file or NULL if unknown */
53

54
  guint                 index;          /* if valid, index in path - aka visible nodes before this one */
55

56 57
  guint                 visible :1;     /* if the file is currently visible */
  guint                 frozen_add :1;  /* true if the model was frozen and the entry has not been added yet */
58

59
  GValue                values[1];      /* actually n_columns values */
60 61
};

62
struct _GtkFileSystemModel
63
{
64
  GObject               parent_instance;
65

66 67 68 69
  GFile *               dir;            /* directory that's displayed */
  guint                 dir_thaw_source;/* GSource id for unfreezing the model */
  char *                attributes;     /* attributes the file info must contain */
  GFileMonitor *        dir_monitor;    /* directory that is monitored */
70

71 72
  GCancellable *        cancellable;    /* cancellable in use for all operations - cancelled on dispose */
  GArray *              files;          /* array of FileModelNode containing all our files */
73
  GSize                 node_size;	/* Size of a FileModelNode structure once its ->values field has n_columns */
74 75
  guint                 n_indexes_valid;/* count of valid indexes */
  GHashTable *          file_lookup;    /* file => array index table */
76

77 78 79 80
  guint                 n_columns;      /* number of columns */
  GType *               column_types;   /* types of each column */
  GtkFileSystemModelGetValue get_func;  /* function to call to fill in values in columns */
  gpointer              get_data;       /* data to pass to get_func */
81

82
  GtkFileFilter *       filter;         /* filter to use for deciding which nodes are visible */
83

84 85 86 87 88 89 90 91
  int                   sort_column_id; /* current sorting column */
  GtkSortType           sort_order;     /* current sorting order */
  GList *               sort_list;      /* list of sorting functions */
  GtkTreeIterCompareFunc default_sort_func; /* default sort function */
  gpointer              default_sort_data; /* data to pass to default sort func */
  GDestroyNotify        default_sort_destroy; /* function to call to destroy default_sort_data */

  guint                 frozen;         /* number of times we're frozen */
92

93 94 95 96 97 98 99
  gboolean              filter_on_thaw :1;/* set when filtering needs to happen upon thawing */
  gboolean              sort_on_thaw :1;/* set when sorting needs to happen upon thawing */

  guint                 show_hidden :1; /* whether to show hidden files */
  guint                 show_folders :1;/* whether to show folders */
  guint                 show_files :1;  /* whether to show files */
};
Owen Taylor's avatar
Owen Taylor committed
100 101 102 103 104 105 106 107

#define GTK_FILE_SYSTEM_MODEL_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))
#define GTK_IS_FILE_SYSTEM_MODEL_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_MODEL))
#define GTK_FILE_SYSTEM_MODEL_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass))

struct _GtkFileSystemModelClass
{
  GObjectClass parent_class;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
108 109 110

  /* Signals */

111
  void (*finished_loading) (GtkFileSystemModel *model, GError *error);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
112 113
};

114 115 116 117 118 119 120 121 122 123 124 125 126 127
/* iter setup:
 * @user_data: the model
 * @user_data2: GUINT_TO_POINTER of array index of current entry
 *
 * All other fields are unused. Note that the array index does not corrspond
 * 1:1 with the path index as entries might not be visible.
 */
#define ITER_INDEX(iter) GPOINTER_TO_UINT((iter)->user_data2)
#define ITER_IS_VALID(model, iter) ((model) == (iter)->user_data)
#define ITER_INIT_FROM_INDEX(model, _iter, _index) G_STMT_START {\
  g_assert (_index < (model)->files->len); \
  (_iter)->user_data = (model); \
  (_iter)->user_data2 = GUINT_TO_POINTER (_index); \
}G_STMT_END
Federico Mena Quintero's avatar
Federico Mena Quintero committed
128

129
/*** FileModelNode ***/
Federico Mena Quintero's avatar
Federico Mena Quintero committed
130

131 132
#define get_node(_model, _index) ((FileModelNode *) ((_model)->files->data + (_index) * (_model)->node_size))
#define node_index(_model, _node) (((gchar *) (_node) - (_model)->files->data) / (_model)->node_size)
Owen Taylor's avatar
Owen Taylor committed
133 134

static void
135
node_validate_indexes (GtkFileSystemModel *model, guint min_index, guint min_visible)
Owen Taylor's avatar
Owen Taylor committed
136
{
137
  guint validate, current;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
138

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
  if (model->files->len == 0)
    return;
  min_index = MIN (min_index, model->files->len - 1);
  validate = model->n_indexes_valid;
  if (validate)
    current = get_node (model, validate - 1)->index;
  else
    current = 0;
  while (validate <= min_index && current <= min_visible)
    {
      FileModelNode *node = get_node (model, validate);
      if (node->visible)
        current++;
      node->index = current;
      validate++;
    }
  model->n_indexes_valid = validate;
Owen Taylor's avatar
Owen Taylor committed
156 157
}

158 159
static guint
node_get_index (GtkFileSystemModel *model, guint id)
Owen Taylor's avatar
Owen Taylor committed
160
{
161 162 163 164
  if (model->n_indexes_valid <= id)
    node_validate_indexes (model, id, G_MAXUINT);

  return get_node (model, id)->index - 1;
Owen Taylor's avatar
Owen Taylor committed
165 166
}

167 168
static void 
node_invalidate_index (GtkFileSystemModel *model, guint id)
Owen Taylor's avatar
Owen Taylor committed
169
{
170
  model->n_indexes_valid = MIN (model->n_indexes_valid, id);
Owen Taylor's avatar
Owen Taylor committed
171 172
}

173 174
static GtkTreePath *
gtk_tree_path_new_from_node (GtkFileSystemModel *model, guint id)
Owen Taylor's avatar
Owen Taylor committed
175
{
176
  guint i = node_get_index (model, id);
Owen Taylor's avatar
Owen Taylor committed
177

178 179 180 181
  g_assert (i < model->files->len);

  return gtk_tree_path_new_from_indices (i, -1);
}
Owen Taylor's avatar
Owen Taylor committed
182

183 184 185 186 187 188
static void
node_set_visible (GtkFileSystemModel *model, guint id, gboolean visible)
{
  FileModelNode *node = get_node (model, id);
  GtkTreePath *path;
  GtkTreeIter iter;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
189

190 191 192
  if (node->visible == visible ||
      node->frozen_add)
    return;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
193

194
  if (visible)
Owen Taylor's avatar
Owen Taylor committed
195
    {
196 197 198 199 200 201 202 203 204 205 206 207 208 209
      node->visible = TRUE;
      node_invalidate_index (model, id);
      path = gtk_tree_path_new_from_node (model, id);
      ITER_INIT_FROM_INDEX (model, &iter, id);
      gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
      gtk_tree_path_free (path);
    }
  else
    {
      path = gtk_tree_path_new_from_node (model, id);
      node->visible = FALSE;
      node_invalidate_index (model, id);
      gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
      gtk_tree_path_free (path);
Owen Taylor's avatar
Owen Taylor committed
210 211 212
    }
}

213 214
static gboolean
node_should_be_visible (GtkFileSystemModel *model, guint id)
215
{
216
  FileModelNode *node = get_node (model, id);
217 218 219 220 221 222
  GtkFileFilterInfo filter_info = { 0, };
  GtkFileFilterFlags required;
  gboolean is_folder, result;
  char *mime_type = NULL;
  char *filename = NULL;
  char *uri = NULL;
223

224 225
  if (node->info == NULL)
    return FALSE;
226

227 228 229
  if (!model->show_hidden &&
      (g_file_info_get_is_hidden (node->info) || g_file_info_get_is_backup (node->info)))
    return FALSE;
230

231 232 233 234 235 236
  is_folder = _gtk_file_info_consider_as_directory (node->info);
  
  /* wtf? */
  if (model->show_folders != model->show_files &&
      model->show_folders != is_folder)
    return FALSE;
237

238 239
  if (is_folder)
    return TRUE;
240

241 242
  if (model->filter == NULL)
    return TRUE;
243

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
  /* fill info */
  required = gtk_file_filter_get_needed (model->filter);

  filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME;
  filter_info.display_name = g_file_info_get_display_name (node->info);

  if (required & GTK_FILE_FILTER_MIME_TYPE)
    {
      filter_info.mime_type = g_file_info_get_attribute_string (node->info, "filechooser::mime-type");
      if (filter_info.mime_type != NULL)
        filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE;
      else
        {
          const char *s = g_file_info_get_content_type (node->info);
          if (s)
            {
              mime_type = g_content_type_get_mime_type (s);
              if (mime_type)
                {
                  filter_info.mime_type = mime_type;
                  filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE;
                }
            }
        }
    }

  if (required & GTK_FILE_FILTER_FILENAME)
    {
      filename = g_file_get_path (node->file);
      if (filter_info.filename)
        {
          filter_info.filename = filename;
	  filter_info.contains |= GTK_FILE_FILTER_FILENAME;
        }
    }

  if (required & GTK_FILE_FILTER_URI)
    {
      uri = g_file_get_uri (node->file);
      if (uri)
        {
          filter_info.uri = uri;
	  filter_info.contains |= GTK_FILE_FILTER_URI;
        }
    }

  result = gtk_file_filter_filter (model->filter, &filter_info);

  g_free (mime_type);
  g_free (filename);
  g_free (uri);

  return result;
297 298
}

299
/*** GtkTreeModel ***/
Owen Taylor's avatar
Owen Taylor committed
300 301 302 303

static GtkTreeModelFlags
gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
{
304 305
  /* GTK_TREE_MODEL_ITERS_PERSIST doesn't work with arrays :( */
  return GTK_TREE_MODEL_LIST_ONLY;
Owen Taylor's avatar
Owen Taylor committed
306 307 308 309 310
}

static gint
gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model)
{
311 312 313
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
  
  return model->n_columns;
Owen Taylor's avatar
Owen Taylor committed
314 315 316 317
}

static GType
gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
318
				       gint          i)
Owen Taylor's avatar
Owen Taylor committed
319
{
320 321 322 323 324
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
  
  g_return_val_if_fail (i >= 0 && (guint) i < model->n_columns, G_TYPE_NONE);

  return model->column_types[i];
Owen Taylor's avatar
Owen Taylor committed
325 326
}

327 328
static int
compare_indices (gconstpointer key, gconstpointer _node)
Owen Taylor's avatar
Owen Taylor committed
329
{
330 331 332 333
  const FileModelNode *node = _node;

  return GPOINTER_TO_UINT (key) - node->index;
}
Owen Taylor's avatar
Owen Taylor committed
334

335 336 337 338 339 340 341 342 343
static gboolean
gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
				      GtkTreeIter  *iter,
				      GtkTreeIter  *parent,
				      gint          n)
{
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
  char *node;
  guint id, find;
Owen Taylor's avatar
Owen Taylor committed
344

345
  g_return_val_if_fail (n >= 0, FALSE);
Owen Taylor's avatar
Owen Taylor committed
346

347
  if (parent != NULL)
Owen Taylor's avatar
Owen Taylor committed
348 349
    return FALSE;

350 351 352 353
  find = n + 1;

  if (model->n_indexes_valid > 0 &&
      get_node (model, model->n_indexes_valid - 1)->index >= find)
Owen Taylor's avatar
Owen Taylor committed
354
    {
355 356 357 358
      /* fast path */
      node = bsearch (GUINT_TO_POINTER (find), 
                      model->files->data,
                      model->n_indexes_valid,
359
                      model->node_size,
360 361 362 363 364 365 366 367 368 369 370 371 372 373
                      compare_indices);
      if (node == NULL)
        return FALSE;

      id = node_index (model, node);
      while (!get_node (model, id)->visible)
        id--;
    }
  else
    {
      node_validate_indexes (model, G_MAXUINT, n);
      id = model->n_indexes_valid - 1;
      if (model->n_indexes_valid == 0 || get_node (model, id)->index != find)
        return FALSE;
Owen Taylor's avatar
Owen Taylor committed
374 375
    }

376
  ITER_INIT_FROM_INDEX (model, iter, id);
Owen Taylor's avatar
Owen Taylor committed
377 378 379
  return TRUE;
}

380 381 382 383 384 385 386 387 388 389 390 391 392
static gboolean
gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
				GtkTreeIter  *iter,
				GtkTreePath  *path)
{
  g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);

  return gtk_file_system_model_iter_nth_child (tree_model, 
                                               iter,
                                               NULL, 
                                               gtk_tree_path_get_indices (path)[0]);
}

Owen Taylor's avatar
Owen Taylor committed
393 394 395 396 397 398
static GtkTreePath *
gtk_file_system_model_get_path (GtkTreeModel *tree_model,
				GtkTreeIter  *iter)
{
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
      
399
  g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL);
Owen Taylor's avatar
Owen Taylor committed
400

401
  return gtk_tree_path_new_from_node (model, ITER_INDEX (iter));
Owen Taylor's avatar
Owen Taylor committed
402 403 404 405 406 407 408 409 410
}

static void
gtk_file_system_model_get_value (GtkTreeModel *tree_model,
				 GtkTreeIter  *iter,
				 gint          column,
				 GValue       *value)
{
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
411
  const GValue *original;
Owen Taylor's avatar
Owen Taylor committed
412
  
413 414
  g_return_if_fail ((guint) column < model->n_columns);
  g_return_if_fail (ITER_IS_VALID (model, iter));
415

416 417 418 419 420
  original = _gtk_file_system_model_get_value (model, iter, column);
  if (original)
    {
      g_value_init (value, G_VALUE_TYPE (original));
      g_value_copy (original, value);
Owen Taylor's avatar
Owen Taylor committed
421
    }
422 423
  else
    g_value_init (value, model->column_types[column]);
Owen Taylor's avatar
Owen Taylor committed
424 425 426 427 428 429
}

static gboolean
gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
				 GtkTreeIter  *iter)
{
430 431
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
  guint i;
Owen Taylor's avatar
Owen Taylor committed
432

433 434 435 436 437
  g_return_val_if_fail (ITER_IS_VALID (model, iter), FALSE);

  for (i = ITER_INDEX (iter) + 1; i < model->files->len; i++) 
    {
      FileModelNode *node = get_node (model, i);
Owen Taylor's avatar
Owen Taylor committed
438

439 440 441 442 443 444 445 446
      if (node->visible)
        {
          ITER_INIT_FROM_INDEX (model, iter, i);
          return TRUE;
        }
    }
      
  return FALSE;
Owen Taylor's avatar
Owen Taylor committed
447 448 449 450 451 452 453
}

static gboolean
gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
				     GtkTreeIter  *iter,
				     GtkTreeIter  *parent)
{
454
  return FALSE;
Owen Taylor's avatar
Owen Taylor committed
455 456 457 458 459 460
}

static gboolean
gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
				      GtkTreeIter  *iter)
{
461
  return FALSE;
Owen Taylor's avatar
Owen Taylor committed
462 463 464 465 466 467 468 469 470
}

static gint
gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
				       GtkTreeIter  *iter)
{
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);

  if (iter)
471
    return 0;
Owen Taylor's avatar
Owen Taylor committed
472

473
  return node_get_index (model, model->files->len - 1) + 1;
Owen Taylor's avatar
Owen Taylor committed
474 475 476 477 478 479 480
}

static gboolean
gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
				   GtkTreeIter  *iter,
				   GtkTreeIter  *child)
{
481
  return FALSE;
Owen Taylor's avatar
Owen Taylor committed
482 483 484 485 486 487
}

static void
gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
				GtkTreeIter  *iter)
{
488
  /* FIXME: implement */
Owen Taylor's avatar
Owen Taylor committed
489 490 491 492 493 494
}

static void
gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
				  GtkTreeIter  *iter)
{
495
  /* FIXME: implement */
Owen Taylor's avatar
Owen Taylor committed
496 497
}

498 499
static void
gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
500
{
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
  iface->get_flags =       gtk_file_system_model_get_flags;
  iface->get_n_columns =   gtk_file_system_model_get_n_columns;
  iface->get_column_type = gtk_file_system_model_get_column_type;
  iface->get_iter =        gtk_file_system_model_get_iter;
  iface->get_path =        gtk_file_system_model_get_path;
  iface->get_value =       gtk_file_system_model_get_value;
  iface->iter_next =       gtk_file_system_model_iter_next;
  iface->iter_children =   gtk_file_system_model_iter_children;
  iface->iter_has_child =  gtk_file_system_model_iter_has_child;
  iface->iter_n_children = gtk_file_system_model_iter_n_children;
  iface->iter_nth_child =  gtk_file_system_model_iter_nth_child;
  iface->iter_parent =     gtk_file_system_model_iter_parent;
  iface->ref_node =        gtk_file_system_model_ref_node;
  iface->unref_node =      gtk_file_system_model_unref_node;
}
516

517
/*** GtkTreeSortable ***/
518

519 520 521 522 523 524 525
typedef struct _SortData SortData;
struct _SortData {
  GtkFileSystemModel *    model;
  GtkTreeIterCompareFunc  func;
  gpointer                data;
  int                     order;        /* -1 to invert sort order or 1 to keep it */
};
526

527 528 529 530 531
/* returns FALSE if no sort necessary */
static gboolean
sort_data_init (SortData *data, GtkFileSystemModel *model)
{
  GtkTreeDataSortHeader *header;
532

533
  if (model->files->len <= 2)
534 535
    return FALSE;

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
  switch (model->sort_column_id)
    {
    case GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID:
      if (!model->default_sort_func)
        return FALSE;
      data->func = model->default_sort_func;
      data->data = model->default_sort_data;
      break;
    case GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID:
      return FALSE;
    default:
      header = _gtk_tree_data_list_get_header (model->sort_list, model->sort_column_id);
      if (header == NULL)
        return FALSE;
      data->func = header->func;
      data->data = header->data;
      break;
    }
554

555 556
  data->order = model->sort_order == GTK_SORT_DESCENDING ? -1 : 1;
  data->model = model;
557 558 559
  return TRUE;
}

560 561
static int
compare_array_element (gconstpointer a, gconstpointer b, gpointer user_data)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
562
{
563 564 565 566 567 568
  SortData *data = user_data;
  GtkTreeIter itera, iterb;

  ITER_INIT_FROM_INDEX (data->model, &itera, node_index (data->model, a));
  ITER_INIT_FROM_INDEX (data->model, &iterb, node_index (data->model, b));
  return data->func (GTK_TREE_MODEL (data->model), &itera, &iterb, data->data) * data->order;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
569 570
}

571
static void
572
gtk_file_system_model_sort (GtkFileSystemModel *model)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
573
{
574
  SortData data;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
575

576 577 578 579 580
  if (model->frozen)
    {
      model->sort_on_thaw = TRUE;
      return;
    }
581

582 583 584 585 586 587 588 589 590 591 592
  if (sort_data_init (&data, model))
    {
      GtkTreePath *path;
      guint i, j, n_elements;

      node_validate_indexes (model, G_MAXUINT, G_MAXUINT);
      n_elements = node_get_index (model, model->files->len - 1) + 1;
      model->n_indexes_valid = 0;
      g_hash_table_remove_all (model->file_lookup);
      g_qsort_with_data (get_node (model, 1),
                         model->files->len - 1,
593
                         model->node_size,
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
                         compare_array_element,
                         &data);
      g_assert (model->n_indexes_valid == 0);
      g_assert (g_hash_table_size (model->file_lookup) == 0);
      if (n_elements)
        {
          int *new_order = g_new (int, n_elements);
        
          j = 0;
          for (i = 0; i < model->files->len; i++)
            {
              FileModelNode *node = get_node (model, i);
              if (!node->visible)
                {
                  node->index = j;
                  continue;
                }

              new_order[j] = node->index;
              j++;
              node->index = j;
            }
          g_assert (j == n_elements);
          path = gtk_tree_path_new ();
          gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
                                         path,
                                         NULL,
                                         new_order);
          gtk_tree_path_free (path);
          g_free (new_order);
        }
    }
626

627
  model->sort_on_thaw = FALSE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
628 629
}

630 631
static void
gtk_file_system_model_sort_node (GtkFileSystemModel *model, guint node)
Owen Taylor's avatar
Owen Taylor committed
632
{
633 634 635
  /* FIXME: improve */
  gtk_file_system_model_sort (model);
}
636

637 638 639 640 641 642
static gboolean
gtk_file_system_model_get_sort_column_id (GtkTreeSortable  *sortable,
                                          gint             *sort_column_id,
                                          GtkSortType      *order)
{
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
643

644 645 646 647
  if (sort_column_id)
    *sort_column_id = model->sort_column_id;
  if (order)
    *order = model->sort_order;
Owen Taylor's avatar
Owen Taylor committed
648

649 650 651
  if (model->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID ||
      model->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
    return FALSE;
652

653
  return TRUE;
Owen Taylor's avatar
Owen Taylor committed
654 655 656
}

static void
657 658 659
gtk_file_system_model_set_sort_column_id (GtkTreeSortable  *sortable,
                                          gint              sort_column_id,
                                          GtkSortType       order)
Owen Taylor's avatar
Owen Taylor committed
660
{
661
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
Owen Taylor's avatar
Owen Taylor committed
662

663 664
  if ((model->sort_column_id == sort_column_id) &&
      (model->sort_order == order))
Owen Taylor's avatar
Owen Taylor committed
665 666
    return;

667
  if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
Owen Taylor's avatar
Owen Taylor committed
668
    {
669
      if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
Owen Taylor's avatar
Owen Taylor committed
670
	{
671
	  GtkTreeDataSortHeader *header = NULL;
Owen Taylor's avatar
Owen Taylor committed
672

673 674
	  header = _gtk_tree_data_list_get_header (model->sort_list, 
						   sort_column_id);
Owen Taylor's avatar
Owen Taylor committed
675

676 677 678
	  /* We want to make sure that we have a function */
	  g_return_if_fail (header != NULL);
	  g_return_if_fail (header->func != NULL);
Owen Taylor's avatar
Owen Taylor committed
679 680 681
	}
      else
	{
682
	  g_return_if_fail (model->default_sort_func != NULL);
Owen Taylor's avatar
Owen Taylor committed
683 684 685
	}
    }

686 687 688 689 690 691 692

  model->sort_column_id = sort_column_id;
  model->sort_order = order;

  gtk_tree_sortable_sort_column_changed (sortable);

  gtk_file_system_model_sort (model);
Owen Taylor's avatar
Owen Taylor committed
693 694
}

Owen Taylor's avatar
Owen Taylor committed
695
static void
696 697 698 699 700
gtk_file_system_model_set_sort_func (GtkTreeSortable        *sortable,
                                     gint                    sort_column_id,
                                     GtkTreeIterCompareFunc  func,
                                     gpointer                data,
                                     GDestroyNotify          destroy)
Owen Taylor's avatar
Owen Taylor committed
701
{
702 703 704 705 706
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);

  model->sort_list = _gtk_tree_data_list_set_header (model->sort_list, 
                                                     sort_column_id, 
                                                     func, data, destroy);
Owen Taylor's avatar
Owen Taylor committed
707

708 709
  if (model->sort_column_id == sort_column_id)
    gtk_file_system_model_sort (model);
Owen Taylor's avatar
Owen Taylor committed
710 711
}

712 713 714 715 716
static void
gtk_file_system_model_set_default_sort_func (GtkTreeSortable        *sortable,
                                             GtkTreeIterCompareFunc  func,
                                             gpointer                data,
                                             GDestroyNotify          destroy)
Owen Taylor's avatar
Owen Taylor committed
717
{
718
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
Owen Taylor's avatar
Owen Taylor committed
719

720
  if (model->default_sort_destroy)
Owen Taylor's avatar
Owen Taylor committed
721
    {
722 723 724 725
      GDestroyNotify d = model->default_sort_destroy;

      model->default_sort_destroy = NULL;
      d (model->default_sort_data);
Owen Taylor's avatar
Owen Taylor committed
726 727
    }

728 729 730
  model->default_sort_func = func;
  model->default_sort_data = data;
  model->default_sort_destroy = destroy;
Owen Taylor's avatar
Owen Taylor committed
731

732 733
  if (model->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
    gtk_file_system_model_sort (model);
Owen Taylor's avatar
Owen Taylor committed
734 735
}

736 737
static gboolean
gtk_file_system_model_has_default_sort_func (GtkTreeSortable *sortable)
Owen Taylor's avatar
Owen Taylor committed
738
{
739
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
Owen Taylor's avatar
Owen Taylor committed
740

741
  return (model->default_sort_func != NULL);
Owen Taylor's avatar
Owen Taylor committed
742 743
}

744 745
static void
gtk_file_system_model_sortable_init (GtkTreeSortableIface *iface)
Owen Taylor's avatar
Owen Taylor committed
746
{
747 748 749 750 751
  iface->get_sort_column_id = gtk_file_system_model_get_sort_column_id;
  iface->set_sort_column_id = gtk_file_system_model_set_sort_column_id;
  iface->set_sort_func = gtk_file_system_model_set_sort_func;
  iface->set_default_sort_func = gtk_file_system_model_set_default_sort_func;
  iface->has_default_sort_func = gtk_file_system_model_has_default_sort_func;
Owen Taylor's avatar
Owen Taylor committed
752 753
}

754 755 756 757 758
/*** GtkTreeDragSource ***/

static gboolean
drag_source_row_draggable (GtkTreeDragSource *drag_source,
			   GtkTreePath       *path)
Owen Taylor's avatar
Owen Taylor committed
759
{
760 761
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (drag_source);
  GtkTreeIter iter;
Owen Taylor's avatar
Owen Taylor committed
762

763 764
  if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
    return FALSE;
765

766
  return ITER_INDEX (&iter) != 0;
Owen Taylor's avatar
Owen Taylor committed
767 768
}

769 770 771 772
static gboolean
drag_source_drag_data_get (GtkTreeDragSource *drag_source,
			   GtkTreePath       *path,
			   GtkSelectionData  *selection_data)
Owen Taylor's avatar
Owen Taylor committed
773
{
774 775 776 777
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (drag_source);
  FileModelNode *node;
  GtkTreeIter iter;
  char *uris[2]; 
Owen Taylor's avatar
Owen Taylor committed
778

779 780
  if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
    return FALSE;
Owen Taylor's avatar
Owen Taylor committed
781

782 783 784
  node = get_node (model, ITER_INDEX (&iter));
  if (node->file == NULL)
    return FALSE;
Owen Taylor's avatar
Owen Taylor committed
785

786 787 788 789
  uris[0] = g_file_get_uri (node->file);
  uris[1] = NULL;
  gtk_selection_data_set_uris (selection_data, uris);
  g_free (uris[0]);
Owen Taylor's avatar
Owen Taylor committed
790

791
  return TRUE;
Owen Taylor's avatar
Owen Taylor committed
792
}
Owen Taylor's avatar
Owen Taylor committed
793

794 795
static void
drag_source_iface_init (GtkTreeDragSourceIface *iface)
Owen Taylor's avatar
Owen Taylor committed
796
{
797 798 799
  iface->row_draggable = drag_source_row_draggable;
  iface->drag_data_get = drag_source_drag_data_get;
  iface->drag_data_delete = NULL;
Owen Taylor's avatar
Owen Taylor committed
800 801
}

802
/*** GtkFileSystemModel ***/
803

804 805 806 807
/* Signal IDs */
enum {
  FINISHED_LOADING,
  LAST_SIGNAL
808 809
};

810 811 812 813 814 815 816 817 818 819 820 821
static guint file_system_model_signals[LAST_SIGNAL] = { 0 };



G_DEFINE_TYPE_WITH_CODE (GtkFileSystemModel, _gtk_file_system_model, G_TYPE_OBJECT,
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
						gtk_file_system_model_iface_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
						gtk_file_system_model_sortable_init)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
						drag_source_iface_init))

822
static void
823
gtk_file_system_model_dispose (GObject *object)
824
{
825 826 827 828 829
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);

  g_cancellable_cancel (model->cancellable);
  if (model->dir_monitor)
    g_file_monitor_cancel (model->dir_monitor);
830

831 832
  G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object);
}
833 834


835 836 837 838 839
static void
gtk_file_system_model_finalize (GObject *object)
{
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
  guint i;
840

841
  for (i = 0; i < model->files->len; i++)
842
    {
843 844 845 846 847 848 849
      FileModelNode *node = get_node (model, i);
      if (node->file)
        g_object_unref (node->file);
      if (node->info)
        g_object_unref (node->info);
    }
  g_array_free (model->files, TRUE);
850

851 852 853 854 855 856 857
  g_object_unref (model->cancellable);
  g_free (model->attributes);
  if (model->dir)
    g_object_unref (model->dir);
  if (model->dir_monitor)
    g_object_unref (model->dir_monitor);
  g_hash_table_destroy (model->file_lookup);
858 859
  if (model->filter)
    g_object_unref (model->filter);
860

861 862 863
  _gtk_tree_data_list_header_free (model->sort_list);
  if (model->default_sort_destroy)
    model->default_sort_destroy (model->default_sort_data);
864

865 866
  G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->finalize (object);
}
867

868 869 870 871
static void
_gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
872

873 874
  gobject_class->finalize = gtk_file_system_model_finalize;
  gobject_class->dispose = gtk_file_system_model_dispose;
875

876 877 878 879 880 881 882 883 884
  file_system_model_signals[FINISHED_LOADING] =
    g_signal_new (I_("finished-loading"),
		  G_OBJECT_CLASS_TYPE (gobject_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkFileSystemModelClass, finished_loading),
		  NULL, NULL,
		  _gtk_marshal_VOID__POINTER,
		  G_TYPE_NONE, 1, G_TYPE_POINTER);
}
885

886 887 888 889 890 891
static void
_gtk_file_system_model_init (GtkFileSystemModel *model)
{
  model->show_files = TRUE;
  model->show_folders = TRUE;
  model->show_hidden = FALSE;
892

893
  model->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
894

895 896 897
  model->file_lookup = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal);
  model->cancellable = g_cancellable_new ();
}
898

899
/*** API ***/
900

901 902 903 904 905
static void
gtk_file_system_model_closed_enumerator (GObject *object, GAsyncResult *res, gpointer data)
{
  g_file_enumerator_close_finish (G_FILE_ENUMERATOR (object), res, NULL);
}
906

907 908 909 910
static gboolean
thaw_func (gpointer data)
{
  GtkFileSystemModel *model = data;
911

912 913
  _gtk_file_system_model_thaw_updates (model);
  model->dir_thaw_source = 0;
914

915
  return FALSE;
916 917
}

918 919
static void
gtk_file_system_model_got_files (GObject *object, GAsyncResult *res, gpointer data)
Owen Taylor's avatar
Owen Taylor committed
920
{
921 922 923 924
  GFileEnumerator *enumerator = G_FILE_ENUMERATOR (object);
  GtkFileSystemModel *model = data;
  GList *walk, *files;
  GError *error = NULL;
Owen Taylor's avatar
Owen Taylor committed
925

926
  gdk_threads_enter ();
927

928
  files = g_file_enumerator_next_files_finish (enumerator, res, &error);
929

930
  if (files)
931
    {
932
      if (model->dir_thaw_source == 0)
933
        {
934 935 936 937 938 939 940
          _gtk_file_system_model_freeze_updates (model);
          model->dir_thaw_source = gdk_threads_add_timeout_full (IO_PRIORITY + 1,
                                                                 50,
                                                                 thaw_func,
                                                                 model,
                                                                 NULL);
        }
Owen Taylor's avatar
Owen Taylor committed
941

942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
      for (walk = files; walk; walk = walk->next)
        {
          const char *name;
          GFileInfo *info;
          GFile *file;
          
          info = walk->data;
          name = g_file_info_get_name (info);
          if (name == NULL)
            {
              /* Shouldn't happen, but the APIs allow it */
              g_object_unref (info);
              continue;
            }
          file = g_file_get_child (model->dir, name);
          _gtk_file_system_model_add_file (model, file, info);
          g_object_unref (file);
          g_object_unref (info);
        }
      g_list_free (files);
Owen Taylor's avatar
Owen Taylor committed
962
    }
963

964
  if (files == NULL)
965
    {
966 967 968 969 970 971
      g_file_enumerator_close_async (enumerator, 
                                     IO_PRIORITY,
                                     model->cancellable,
                                     gtk_file_system_model_closed_enumerator,
                                     NULL);
      if (model->dir_thaw_source != 0)
972
        {
973 974 975
          g_source_remove (model->dir_thaw_source);
          model->dir_thaw_source = 0;
          _gtk_file_system_model_thaw_updates (model);
976 977
        }

978 979 980 981 982
      if (error)
        g_error_free (error);
      else
        g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, NULL);

983
      g_object_unref (model);
984
    }
985 986 987 988 989 990 991
  else
    g_file_enumerator_next_files_async (enumerator,
                                        g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
                                        IO_PRIORITY,
                                        model->cancellable,
                                        gtk_file_system_model_got_files,
                                        model);
Owen Taylor's avatar
Owen Taylor committed
992

993
  gdk_threads_leave ();
Owen Taylor's avatar
Owen Taylor committed
994 995 996
}

static void
997 998 999
gtk_file_system_model_query_done (GObject *     object,
                                  GAsyncResult *res,
                                  gpointer      data)
Owen Taylor's avatar
Owen Taylor committed
1000
{
1001 1002 1003
  GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */
  GFile *file = G_FILE (object);
  GFileInfo *info;
Owen Taylor's avatar
Owen Taylor committed
1004

1005 1006 1007
  info = g_file_query_info_finish (file, res, NULL);
  if (info == NULL)
    return;
Owen Taylor's avatar
Owen Taylor committed
1008

1009
  _gtk_file_system_model_update_file (model, file, info, TRUE);
Owen Taylor's avatar
Owen Taylor committed
1010 1011
}

1012 1013 1014 1015 1016 1017
static void
gtk_file_system_model_monitor_change (GFileMonitor *      monitor,
                                      GFile *             file,
                                      GFile *             other_file,
                                      GFileMonitorEvent   type,
                                      GtkFileSystemModel *model)
Owen Taylor's avatar
Owen Taylor committed
1018
{
1019
  switch (type)
Owen Taylor's avatar
Owen Taylor committed
1020
    {
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
      case G_FILE_MONITOR_EVENT_CREATED:
      case G_FILE_MONITOR_EVENT_CHANGED:
      case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
        /* We can treat all of these the same way */
        g_file_query_info_async (file,
                                 model->attributes,
                                 0,
                                 IO_PRIORITY,
                                 model->cancellable,
                                 gtk_file_system_model_query_done,
                                 model);
        break;
      case G_FILE_MONITOR_EVENT_DELETED:
        _gtk_file_system_model_remove_file (model, file);
        break;
      case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
        /* FIXME: use freeze/thaw with this somehow? */
      case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
      case G_FILE_MONITOR_EVENT_UNMOUNTED:
      default:
        /* ignore these */
        break;
Owen Taylor's avatar
Owen Taylor committed
1043 1044 1045
    }
}

1046 1047
static void
gtk_file_system_model_got_enumerator (GObject *dir, GAsyncResult *res, gpointer data)
Owen Taylor's avatar
Owen Taylor committed
1048
{
1049 1050 1051
  GtkFileSystemModel *model = data;
  GFileEnumerator *enumerator;
  GError *error = NULL;
1052

1053
  gdk_threads_enter ();
Owen Taylor's avatar
Owen Taylor committed
1054

1055 1056
  enumerator = g_file_enumerate_children_finish (G_FILE (dir), res, &error);
  if (enumerator == NULL)
Owen Taylor's avatar
Owen Taylor committed
1057
    {
1058 1059 1060
      g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error);
      g_object_unref (model);
      g_error_free (error);
Owen Taylor's avatar
Owen Taylor committed
1061
    }
1062
  else
1063
    {
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
      g_file_enumerator_next_files_async (enumerator,
                                          g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY,
                                          IO_PRIORITY,
                                          model->cancellable,
                                          gtk_file_system_model_got_files,
                                          model);
      g_object_unref (enumerator);
      model->dir_monitor = g_file_monitor_directory (model->dir,
                                                     0,
                                                     model->cancellable,
                                                     NULL);
      if (model->dir_monitor)
        g_signal_connect (model->dir_monitor,
                          "changed",
                          G_CALLBACK (gtk_file_system_model_monitor_change),
                          model);
1080
    }
1081 1082

  gdk_threads_leave ();
Owen Taylor's avatar
Owen Taylor committed
1083 1084 1085
}

static void
1086 1087 1088
gtk_file_system_model_set_n_columns (GtkFileSystemModel *model,
                                     gint                n_columns,
                                     va_list             args)
Owen Taylor's avatar
Owen Taylor committed
1089
{
1090
  guint i;
1091

1092
  g_assert (model->files == NULL);
1093
  g_assert (n_columns > 0);
1094

1095
  model->n_columns = n_columns;
1096 1097 1098
  model->column_types = g_slice_alloc (sizeof (GType) * n_columns);

  model->node_size = sizeof (FileModelNode) + sizeof (GValue) * (n_columns - 1); /* minus 1 because FileModelNode.values[] has a default size of 1 */
1099

1100
  for (i = 0; i < (guint) n_columns; i++)
1101
    {
1102 1103
      GType type = va_arg (args, GType);
      if (! _gtk_tree_data_list_check_type (type))
1104
	{
1105 1106
	  g_error ("%s: type %s cannot be a column type for GtkFileSystemModel\n", G_STRLOC, g_type_name (type));
          return; /* not reached */
1107 1108
	}

1109
      model->column_types[i] = type;
1110 1111
    }

1112
  model->sort_list = _gtk_tree_data_list_header_new (n_columns, model->column_types);
1113

1114
  model->files = g_array_sized_new (FALSE, FALSE, model->node_size, FILES_PER_QUERY);
1115 1116
  /* add editable node at start */
  g_array_set_size (model->files, 1);
1117
  memset (get_node (model, 0), 0, model->node_size);
Owen Taylor's avatar
Owen Taylor committed
1118 1119 1120
}

static void
1121 1122 1123
gtk_file_system_model_set_directory (GtkFileSystemModel *model,
                                     GFile *             dir,
			             const gchar *       attributes)
Owen Taylor's avatar
Owen Taylor committed
1124
{
1125
  g_assert (G_IS_FILE (dir));
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137

  model->dir = g_object_ref (dir);
  model->attributes = g_strdup (attributes);

  g_object_ref (model);
  g_file_enumerate_children_async (model->dir,
                                   attributes,
                                   0,
                                   IO_PRIORITY,
                                   model->cancellable,
                                   gtk_file_system_model_got_enumerator,
                                   model);
1138

Owen Taylor's avatar
Owen Taylor committed
1139 1140
}

1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
static GtkFileSystemModel *
_gtk_file_system_model_new_valist (GtkFileSystemModelGetValue get_func,
                                   gpointer            get_data,
                                   guint               n_columns,
                                   va_list             args)
{
  GtkFileSystemModel *model;

  model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
  model->get_func = get_func;
  model->get_data = get_data;

  gtk_file_system_model_set_n_columns (model, n_columns, args);

  return model;
}

1158 1159
/**
 * _gtk_file_system_model_new:
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
 * @get_func: function to call for getting a value
 * @get_data: user data argument passed to @get_func
 * @n_columns: number of columns
 * @...: @n_columns #GType types for the columns
 *
 * Creates a new #GtkFileSystemModel object. You need to add files
 * to the list using _gtk_file_system_model_add_file().
 *
 * Return value: the newly created #GtkFileSystemModel
 **/
GtkFileSystemModel *
_gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
                            gpointer            get_data,
                            guint               n_columns,
                            ...)
{
  GtkFileSystemModel *model;
  va_list args;

  g_return_val_if_fail (get_func != NULL, NULL);
  g_return_val_if_fail (n_columns > 0, NULL);

  va_start (args, n_columns);
  model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
  va_end (args);

  return model;
}

/**
 * _gtk_file_system_model_new_for_directory:
1191 1192
 * @directory: the directory to show.
 * @attributes: attributes to immediately load or %NULL for all
1193 1194 1195 1196
 * @get_func: function that the model should call to query data about a file
 * @get_data: user data to pass to the @get_func
 * @n_columns: number of columns
 * @...: @n_columns #GType types for the columns
1197 1198 1199
 *
 * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
 * object wraps the given @directory as a #GtkTreeModel.
1200 1201 1202 1203
 * The model will query the given directory with the given @attributes
 * and add all files inside the directory automatically. If supported,
 * it will also monitor the drectory and update the model's
 * contents to reflect changes, if the @directory supports monitoring.
1204
 * 
1205
 * Return value: the newly created #GtkFileSystemModel
1206 1207
 **/
GtkFileSystemModel *
1208 1209 1210 1211 1212 1213
_gtk_file_system_model_new_for_directory (GFile *                    dir,
                                          const gchar *              attributes,
                                          GtkFileSystemModelGetValue get_func,
                                          gpointer                   get_data,
                                          guint                      n_columns,
                                          ...)
1214 1215
{
  GtkFileSystemModel *model;
1216
  va_list args;
1217

1218
  g_return_val_if_fail (G_IS_FILE (dir), NULL);
1219 1220
  g_return_val_if_fail (get_func != NULL, NULL);
  g_return_val_if_fail (n_columns > 0, NULL);
1221

1222
  va_start (args, n_columns);
1223
  model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
1224
  va_end (args);
1225

1226
  gtk_file_system_model_set_directory (model, dir, attributes);
1227

1228
  return model;
1229 1230
}

1231 1232
static void
gtk_file_system_model_refilter_all (GtkFileSystemModel *model)