gtkfilesystemmodel.c 54.8 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
  GFile *               dir;            /* directory that's displayed */
  guint                 dir_thaw_source;/* GSource id for unfreezing the model */
68 69
  char *                attributes;     /* attributes the file info must contain, or NULL for all attributes */
  GFileMonitor *        dir_monitor;    /* directory that is monitored, or NULL if monitoring was not supported */
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
static void add_file (GtkFileSystemModel *model,
		      GFile              *file,
		      GFileInfo          *info);
117 118 119 120 121 122 123 124 125 126 127 128 129 130
/* 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
131

132
/*** FileModelNode ***/
Federico Mena Quintero's avatar
Federico Mena Quintero committed
133

134 135
#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
136 137

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

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
  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
159 160
}

161 162
static guint
node_get_index (GtkFileSystemModel *model, guint id)
Owen Taylor's avatar
Owen Taylor committed
163
{
164 165 166 167
  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
168 169
}

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

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

181 182 183 184
  g_assert (i < model->files->len);

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

186 187 188 189 190 191
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
192

193 194 195
  if (node->visible == visible ||
      node->frozen_add)
    return;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
196

197
  if (visible)
Owen Taylor's avatar
Owen Taylor committed
198
    {
199 200 201 202 203 204 205 206 207 208 209 210 211 212
      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
213 214 215
    }
}

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

227 228
  if (node->info == NULL)
    return FALSE;
229

230 231 232
  if (!model->show_hidden &&
      (g_file_info_get_is_hidden (node->info) || g_file_info_get_is_backup (node->info)))
    return FALSE;
233

234 235 236 237 238 239
  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;
240

241 242
  if (is_folder)
    return TRUE;
243

244 245
  if (model->filter == NULL)
    return TRUE;
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 297 298 299
  /* 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;
300 301
}

302
/*** GtkTreeModel ***/
Owen Taylor's avatar
Owen Taylor committed
303 304 305 306

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

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

static GType
gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
321
				       gint          i)
Owen Taylor's avatar
Owen Taylor committed
322
{
323 324 325 326 327
  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
328 329
}

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

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

338 339 340 341 342 343 344 345 346
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
347

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

350
  if (parent != NULL)
Owen Taylor's avatar
Owen Taylor committed
351 352
    return FALSE;

353 354 355 356
  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
357
    {
358 359 360 361
      /* fast path */
      node = bsearch (GUINT_TO_POINTER (find), 
                      model->files->data,
                      model->n_indexes_valid,
362
                      model->node_size,
363 364 365 366 367 368 369 370 371 372 373 374 375 376
                      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
377 378
    }

379
  ITER_INIT_FROM_INDEX (model, iter, id);
Owen Taylor's avatar
Owen Taylor committed
380 381 382
  return TRUE;
}

383 384 385 386 387 388 389 390 391 392 393 394 395
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
396 397 398 399 400 401
static GtkTreePath *
gtk_file_system_model_get_path (GtkTreeModel *tree_model,
				GtkTreeIter  *iter)
{
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
      
402
  g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL);
Owen Taylor's avatar
Owen Taylor committed
403

404
  return gtk_tree_path_new_from_node (model, ITER_INDEX (iter));
Owen Taylor's avatar
Owen Taylor committed
405 406 407 408 409 410 411 412 413
}

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);
414
  const GValue *original;
Owen Taylor's avatar
Owen Taylor committed
415
  
416 417
  g_return_if_fail ((guint) column < model->n_columns);
  g_return_if_fail (ITER_IS_VALID (model, iter));
418

419 420 421 422 423
  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
424
    }
425 426
  else
    g_value_init (value, model->column_types[column]);
Owen Taylor's avatar
Owen Taylor committed
427 428 429 430 431 432
}

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

436 437 438 439 440
  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
441

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

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

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

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

  if (iter)
474
    return 0;
Owen Taylor's avatar
Owen Taylor committed
475

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

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

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

static void
gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
				  GtkTreeIter  *iter)
{
498
  /* FIXME: implement */
Owen Taylor's avatar
Owen Taylor committed
499 500
}

501 502
static void
gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
503
{
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
  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;
}
519

520
/*** GtkTreeSortable ***/
521

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

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

536
  if (model->files->len <= 2)
537 538
    return FALSE;

539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
  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;
    }
557

558 559
  data->order = model->sort_order == GTK_SORT_DESCENDING ? -1 : 1;
  data->model = model;
560 561 562
  return TRUE;
}

563 564
static int
compare_array_element (gconstpointer a, gconstpointer b, gpointer user_data)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
565
{
566 567 568 569 570 571
  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
572 573
}

574
static void
575
gtk_file_system_model_sort (GtkFileSystemModel *model)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
576
{
577
  SortData data;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
578

579 580 581 582 583
  if (model->frozen)
    {
      model->sort_on_thaw = TRUE;
      return;
    }
584

585 586 587 588 589 590 591 592 593 594 595
  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,
596
                         model->node_size,
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 626 627 628
                         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);
        }
    }
629

630
  model->sort_on_thaw = FALSE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
631 632
}

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

640 641 642 643 644 645
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);
646

647 648 649 650
  if (sort_column_id)
    *sort_column_id = model->sort_column_id;
  if (order)
    *order = model->sort_order;
Owen Taylor's avatar
Owen Taylor committed
651

652 653 654
  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;
655

656
  return TRUE;
Owen Taylor's avatar
Owen Taylor committed
657 658 659
}

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

666 667
  if ((model->sort_column_id == sort_column_id) &&
      (model->sort_order == order))
Owen Taylor's avatar
Owen Taylor committed
668 669
    return;

670
  if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
Owen Taylor's avatar
Owen Taylor committed
671
    {
672
      if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
Owen Taylor's avatar
Owen Taylor committed
673
	{
674
	  GtkTreeDataSortHeader *header = NULL;
Owen Taylor's avatar
Owen Taylor committed
675

676 677
	  header = _gtk_tree_data_list_get_header (model->sort_list, 
						   sort_column_id);
Owen Taylor's avatar
Owen Taylor committed
678

679 680 681
	  /* 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
682 683 684
	}
      else
	{
685
	  g_return_if_fail (model->default_sort_func != NULL);
Owen Taylor's avatar
Owen Taylor committed
686 687 688
	}
    }

689 690 691 692 693 694 695

  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
696 697
}

Owen Taylor's avatar
Owen Taylor committed
698
static void
699 700 701 702 703
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
704
{
705 706 707 708 709
  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
710

711 712
  if (model->sort_column_id == sort_column_id)
    gtk_file_system_model_sort (model);
Owen Taylor's avatar
Owen Taylor committed
713 714
}

715 716 717 718 719
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
720
{
721
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
Owen Taylor's avatar
Owen Taylor committed
722

723
  if (model->default_sort_destroy)
Owen Taylor's avatar
Owen Taylor committed
724
    {
725 726 727 728
      GDestroyNotify d = model->default_sort_destroy;

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

731 732 733
  model->default_sort_func = func;
  model->default_sort_data = data;
  model->default_sort_destroy = destroy;
Owen Taylor's avatar
Owen Taylor committed
734

735 736
  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
737 738
}

739 740
static gboolean
gtk_file_system_model_has_default_sort_func (GtkTreeSortable *sortable)
Owen Taylor's avatar
Owen Taylor committed
741
{
742
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
Owen Taylor's avatar
Owen Taylor committed
743

744
  return (model->default_sort_func != NULL);
Owen Taylor's avatar
Owen Taylor committed
745 746
}

747 748
static void
gtk_file_system_model_sortable_init (GtkTreeSortableIface *iface)
Owen Taylor's avatar
Owen Taylor committed
749
{
750 751 752 753 754
  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
755 756
}

757 758 759 760 761
/*** GtkTreeDragSource ***/

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

766 767
  if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
    return FALSE;
768

769
  return ITER_INDEX (&iter) != 0;
Owen Taylor's avatar
Owen Taylor committed
770 771
}

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

782 783
  if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
    return FALSE;
Owen Taylor's avatar
Owen Taylor committed
784

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

789 790 791 792
  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
793

794
  return TRUE;
Owen Taylor's avatar
Owen Taylor committed
795
}
Owen Taylor's avatar
Owen Taylor committed
796

797 798
static void
drag_source_iface_init (GtkTreeDragSourceIface *iface)
Owen Taylor's avatar
Owen Taylor committed
799
{
800 801 802
  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
803 804
}

805
/*** GtkFileSystemModel ***/
806

807 808 809 810
/* Signal IDs */
enum {
  FINISHED_LOADING,
  LAST_SIGNAL
811 812
};

813 814 815 816 817 818 819 820 821 822 823 824
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))

825
static void
826
gtk_file_system_model_dispose (GObject *object)
827
{
828 829 830 831 832
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);

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

834 835
  G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object);
}
836 837


838 839 840 841 842
static void
gtk_file_system_model_finalize (GObject *object)
{
  GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object);
  guint i;
843

844
  for (i = 0; i < model->files->len; i++)
845
    {
846 847 848 849 850 851 852
      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);
853

854 855 856 857 858 859 860
  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);
861 862
  if (model->filter)
    g_object_unref (model->filter);
863

864 865 866
  _gtk_tree_data_list_header_free (model->sort_list);
  if (model->default_sort_destroy)
    model->default_sort_destroy (model->default_sort_data);
867

868 869
  G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->finalize (object);
}
870

871 872 873 874
static void
_gtk_file_system_model_class_init (GtkFileSystemModelClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
875

876 877
  gobject_class->finalize = gtk_file_system_model_finalize;
  gobject_class->dispose = gtk_file_system_model_dispose;
878

879 880 881 882 883 884 885 886 887
  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);
}
888

889 890 891 892 893 894
static void
_gtk_file_system_model_init (GtkFileSystemModel *model)
{
  model->show_files = TRUE;
  model->show_folders = TRUE;
  model->show_hidden = FALSE;
895

896
  model->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
897

898 899 900
  model->file_lookup = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal);
  model->cancellable = g_cancellable_new ();
}
901

902
/*** API ***/
903

904 905 906 907 908
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);
}
909

910 911 912 913
static gboolean
thaw_func (gpointer data)
{
  GtkFileSystemModel *model = data;
914

915 916
  _gtk_file_system_model_thaw_updates (model);
  model->dir_thaw_source = 0;
917

918
  return FALSE;
919 920
}

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

929
  gdk_threads_enter ();
930

931
  files = g_file_enumerator_next_files_finish (enumerator, res, &error);
932

933
  if (files)
934
    {
935
      if (model->dir_thaw_source == 0)
936
        {
937 938 939 940 941 942 943
          _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
944

945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
      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);
960
          add_file (model, file, info);
961 962 963 964
          g_object_unref (file);
          g_object_unref (info);
        }
      g_list_free (files);
Owen Taylor's avatar
Owen Taylor committed
965
    }
966

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

981 982 983 984 985
      if (error)
        g_error_free (error);
      else
        g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, NULL);

986
      g_object_unref (model);
987
    }
988 989 990 991 992 993 994
  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
995

996
  gdk_threads_leave ();
Owen Taylor's avatar
Owen Taylor committed
997 998 999
}

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

1008 1009 1010
  info = g_file_query_info_finish (file, res, NULL);
  if (info == NULL)
    return;
Owen Taylor's avatar
Owen Taylor committed
1011

1012
  _gtk_file_system_model_update_file (model, file, info, TRUE);
Owen Taylor's avatar
Owen Taylor committed
1013 1014
}

1015 1016 1017 1018 1019 1020
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
1021
{
1022
  switch (type)
Owen Taylor's avatar
Owen Taylor committed
1023
    {
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
      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
1046 1047 1048
    }
}

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

1056
  gdk_threads_enter ();
Owen Taylor's avatar
Owen Taylor committed
1057

1058 1059
  enumerator = g_file_enumerate_children_finish (G_FILE (dir), res, &error);
  if (enumerator == NULL)
Owen Taylor's avatar
Owen Taylor committed
1060
    {
1061 1062 1063
      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
1064
    }
1065
  else
1066
    {
1067 1068 1069 1070 1071 1072 1073 1074
      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,
1075
                                                     G_FILE_MONITOR_NONE,
1076
                                                     model->cancellable,
1077
                                                     NULL); /* we don't mind if directory monitoring isn't supported, so the GError is NULL here */
1078 1079 1080 1081 1082
      if (model->dir_monitor)
        g_signal_connect (model->dir_monitor,
                          "changed",
                          G_CALLBACK (gtk_file_system_model_monitor_change),
                          model);
1083
    }
1084 1085

  gdk_threads_leave ();
Owen Taylor's avatar
Owen Taylor committed
1086 1087 1088
}

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

1095
  g_assert (model->files == NULL);
1096
  g_assert (n_columns > 0);
1097

1098
  model->n_columns = n_columns;
1099 1100 1101
  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 */
1102

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

1112
      model->column_types[i] = type;
1113 1114
    }

1115
  model->sort_list = _gtk_tree_data_list_header_new (n_columns, model->column_types);
1116

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

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

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

  g_object_ref (model);
  g_file_enumerate_children_async (model->dir,
                                   attributes,
1136
                                   G_FILE_QUERY_INFO_NONE,
1137 1138 1139 1140
                                   IO_PRIORITY,
                                   model->cancellable,
                                   gtk_file_system_model_got_enumerator,
                                   model);
1141

Owen Taylor's avatar
Owen Taylor committed
1142 1143
}

1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
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;
}

1161 1162
/**
 * _gtk_file_system_model_new:
1163 1164 1165 1166 1167 1168
 * @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
1169 1170
 * to the list using _gtk_file_system_model_add_and_query_file()
 * or _gtk_file_system_model_update_file().
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
 *
 * 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:
1195 1196
 * @directory: the directory to show.
 * @attributes: attributes to immediately load or %NULL for all
1197 1198 1199 1200
 * @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
1201 1202 1203
 *
 * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel
 * object wraps the given @directory as a #GtkTreeModel.
1204 1205 1206 1207
 * 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.
1208
 * 
1209
 * Return value: the newly created #GtkFileSystemModel
1210 1211
 **/
GtkFileSystemModel *
1212 1213 1214 1215 1216 1217
_gtk_file_system_model_new_for_directory (GFile *                    dir,
                                          const gchar *              attributes,
                                          GtkFileSystemModelGetValue get_func,
                                          gpointer                   get_data,
                                          guint                      n_columns,
                                          ...)
1218 1219
{
  GtkFileSystemModel *model;
1220
  va_list args;
1221

1222
  g_return_val_if_fail (G_IS_FILE (dir), NULL);
1223 1224
  g_return_val_if_fail (get_func != NULL