1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) Johannes Schmid 2007 <jhs@gnome.org>
6 * anjuta is free software.
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
13 * anjuta is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with anjuta. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
25 #include "file-model.h"
26 #include <glib/gi18n.h>
28 #include <libanjuta/anjuta-debug.h>
29 #include <libanjuta/anjuta-plugin.h>
30 #include <libanjuta/anjuta-async-notify.h>
34 const gchar
* BINARY_SUFFIX
[] =
54 PROP_FILTER_UNVERSIONED
59 SIGNAL_DIRECTORY_EXPANDED
,
63 guint signals
[SIGNAL_LAST
];
65 typedef struct _FileModelPrivate FileModelPrivate
;
66 typedef struct _FileModelAsyncData FileModelAsyncData
;
68 struct _FileModelPrivate
71 gboolean filter_binary
;
72 gboolean filter_hidden
;
73 gboolean filter_backup
;
74 gboolean filter_unversioned
;
81 struct _FileModelAsyncData
84 GtkTreeRowReference
* reference
;
87 #define FILE_MODEL_GET_PRIVATE(o) \
88 (G_TYPE_INSTANCE_GET_PRIVATE((o), FILE_TYPE_MODEL, FileModelPrivate))
90 G_DEFINE_TYPE (FileModel
, file_model
, GTK_TYPE_TREE_STORE
)
93 file_model_filter_file (FileModel
* model
,
96 FileModelPrivate
* priv
= FILE_MODEL_GET_PRIVATE (model
);
98 if (priv
->filter_hidden
&& g_file_info_get_is_hidden(file_info
))
100 else if (priv
->filter_backup
&& g_file_info_get_is_backup(file_info
))
102 else if (priv
->filter_binary
&&
103 g_file_info_get_file_type (file_info
) != G_FILE_TYPE_DIRECTORY
)
106 const gchar
* name
= g_file_info_get_name (file_info
);
107 for (i
= 0; BINARY_SUFFIX
[i
] != NULL
; i
++)
109 if (g_str_has_suffix (name
, BINARY_SUFFIX
[i
]))
120 file_model_add_dummy (FileModel
* model
,
123 GtkTreeStore
* store
= GTK_TREE_STORE (model
);
126 gtk_tree_store_append (store
, &dummy
, iter
);
127 gtk_tree_store_set (store
, &dummy
,
128 COLUMN_FILENAME
, _("Loading..."),
137 GtkTreeRowReference
* ref
;
141 file_model_vcs_status_callback(GFile
*file
,
142 AnjutaVcsStatus status
,
145 VcsData
* data
= user_data
;
146 FileModelPrivate
*priv
= FILE_MODEL_GET_PRIVATE (data
->model
);
147 gchar
* path
= g_file_get_path (file
);
149 GtkTreePath
* tree_path
= gtk_tree_row_reference_get_path (data
->ref
);
156 GtkTreeModel
* model
= gtk_tree_row_reference_get_model (data
->ref
);
158 gtk_tree_model_get_iter (model
,
161 gtk_tree_model_get (model
, &iter
,
162 COLUMN_FILE
, &dir
, -1);
164 parent
= g_file_get_parent (file
);
166 if (gtk_tree_model_iter_children (model
, &child
, &iter
) &&
167 g_file_equal (dir
, parent
))
173 gtk_tree_model_get (model
, &child
,
180 if (file
&& child_file
&& g_file_equal (file
, child_file
))
182 if (priv
->filter_unversioned
&&
183 (status
== ANJUTA_VCS_STATUS_UNVERSIONED
||
184 status
== ANJUTA_VCS_STATUS_IGNORED
))
186 gtk_tree_store_remove (GTK_TREE_STORE (model
), &child
);
190 gtk_tree_store_set (GTK_TREE_STORE (model
),
196 g_object_unref (child_file
);
200 while (gtk_tree_model_iter_next (model
, &child
));
202 gtk_tree_path_free (tree_path
);
203 g_object_unref (dir
);
204 g_object_unref (parent
);
210 file_model_free_vcs_data (VcsData
*data
)
212 gtk_tree_row_reference_free (data
->ref
);
217 file_model_get_vcs_status (FileModel
* model
,
221 GtkTreePath
* path
= gtk_tree_model_get_path (GTK_TREE_MODEL(model
),
223 FileModelPrivate
* priv
= FILE_MODEL_GET_PRIVATE(model
);
227 VcsData
* data
= g_new0(VcsData
, 1);
228 AnjutaAsyncNotify
* notify
= anjuta_async_notify_new();
229 data
->ref
= gtk_tree_row_reference_new (GTK_TREE_MODEL (model
),
233 g_signal_connect_swapped (G_OBJECT (notify
), "finished",
234 G_CALLBACK (file_model_free_vcs_data
), data
);
236 ianjuta_vcs_query_status(priv
->ivcs
,
238 file_model_vcs_status_callback
,
244 gtk_tree_path_free (path
);
248 file_model_update_file (FileModel
* model
,
251 GFileInfo
* file_info
,
254 GtkTreeStore
* store
= GTK_TREE_STORE(model
);
255 gboolean is_dir
= FALSE
;
257 GtkIconInfo
* icon_info
;
259 GdkPixbuf
* pixbuf
= NULL
;
262 icon
= g_file_info_get_icon(file_info
);
265 g_object_get (icon
, "names", &icon_names
, NULL
);
267 if ((icon_info
= gtk_icon_theme_choose_icon (gtk_icon_theme_get_default(),
268 (const gchar
**)icon_names
,
270 GTK_ICON_LOOKUP_FORCE_SIZE
)))
272 pixbuf
= gtk_icon_info_load_icon (icon_info
, NULL
);
273 gtk_icon_info_free(icon_info
);
275 g_strfreev (icon_names
);
278 if (g_file_info_get_file_type(file_info
) == G_FILE_TYPE_DIRECTORY
)
281 display_name
= g_markup_printf_escaped("%s",
282 g_file_info_get_display_name(file_info
));
284 gtk_tree_store_set (store
, iter
,
285 COLUMN_DISPLAY
, display_name
,
286 COLUMN_FILENAME
, display_name
,
288 COLUMN_PIXBUF
, pixbuf
,
289 COLUMN_STATUS
, ANJUTA_VCS_STATUS_NONE
,
290 COLUMN_IS_DIR
, is_dir
,
291 COLUMN_SORT
, g_file_info_get_sort_order(file_info
),
297 file_model_add_dummy(model
, iter
);
303 gtk_tree_model_iter_parent (GTK_TREE_MODEL (model
), &parent
, iter
);
304 gtk_tree_model_get (GTK_TREE_MODEL (model
), &parent
,
305 COLUMN_FILE
, &dir
, -1);
306 file_model_get_vcs_status (model
, &parent
, dir
);
307 g_object_unref (dir
);
311 g_object_unref (pixbuf
);
312 g_free(display_name
);
316 file_model_update_file_foreach_func (GtkTreeModel
* model
,
324 gtk_tree_model_get (model
, iter
,
325 COLUMN_FILE
, &file
, -1);
330 info
= g_file_query_info (file
,
332 G_FILE_QUERY_INFO_NONE
,
337 g_object_unref (file
);
341 file_model_update_file (FILE_MODEL (model
),
346 g_object_unref (info
);
347 g_object_unref (file
);
349 /* Continue iterating */
354 file_model_add_file (FileModel
* model
,
357 GFileInfo
* file_info
)
360 GtkTreeStore
* store
= GTK_TREE_STORE(model
);
362 if (file_model_filter_file (model
, file_info
))
364 gtk_tree_store_append (store
, &iter
, parent
);
365 file_model_update_file (model
, &iter
, file
, file_info
, TRUE
);
369 void file_model_update_vcs_status (FileModel
* model
)
371 gtk_tree_model_foreach (GTK_TREE_MODEL(model
),
372 file_model_update_file_foreach_func
, NULL
);
376 on_file_model_changed (GFileMonitor
* monitor
,
379 GFileMonitorEvent event_type
,
382 GtkTreeRowReference
* reference
= (GtkTreeRowReference
*)data
;
386 GtkTreeIter file_iter
;
387 gboolean found
= FALSE
;
389 /* reference could be invalid if the file has already been destroyed */
390 if (!gtk_tree_row_reference_valid(reference
))
393 model
= FILE_MODEL(gtk_tree_row_reference_get_model (reference
));
394 path
= gtk_tree_row_reference_get_path (reference
);
396 gtk_tree_model_get_iter (GTK_TREE_MODEL(model
), &iter
, path
);
397 gtk_tree_path_free (path
);
399 if (gtk_tree_model_iter_children (GTK_TREE_MODEL(model
), &file_iter
, &iter
))
404 gtk_tree_model_get (GTK_TREE_MODEL(model
), &file_iter
,
405 COLUMN_FILE
, &model_file
, -1);
406 if (model_file
&& file
&& g_file_equal (model_file
, file
))
408 g_object_unref (model_file
);
412 g_clear_object (&model_file
);
414 while (gtk_tree_model_iter_next (GTK_TREE_MODEL(model
), &file_iter
));
416 if (event_type
== G_FILE_MONITOR_EVENT_CHANGED
||
417 event_type
== G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED
||
418 event_type
== G_FILE_MONITOR_EVENT_DELETED
)
426 case G_FILE_MONITOR_EVENT_CHANGED
:
427 case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED
:
428 case G_FILE_MONITOR_EVENT_CREATED
:
430 GFileInfo
* file_info
;
431 file_info
= g_file_query_info (file
,
433 G_FILE_QUERY_INFO_NONE
,
438 file_model_add_file (model
, &iter
, file
, file_info
);
440 file_model_update_file (model
, &file_iter
, file
, file_info
, FALSE
);
441 g_object_unref (file_info
);
445 case G_FILE_MONITOR_EVENT_DELETED
:
447 gtk_tree_store_remove (GTK_TREE_STORE (model
), &file_iter
);
457 file_model_add_watch (FileModel
* model
, GtkTreePath
* path
)
460 GtkTreeRowReference
* reference
;
462 GFileMonitor
* monitor
;
464 gtk_tree_model_get_iter (GTK_TREE_MODEL (model
),
467 gtk_tree_model_get (GTK_TREE_MODEL(model
), &iter
,
468 COLUMN_FILE
, &file
, -1);
470 reference
= gtk_tree_row_reference_new (GTK_TREE_MODEL(model
), path
);
472 monitor
= g_file_monitor_directory (file
, G_FILE_MONITOR_NONE
,
474 g_signal_connect (monitor
, "changed", G_CALLBACK(on_file_model_changed
),
477 g_object_set_data_full (G_OBJECT(file
), "file-monitor", monitor
, (GDestroyNotify
)g_object_unref
);
478 /* Reference is used by monitor, should be kept it until the monitor is destroyed */
479 g_object_set_data_full (G_OBJECT(monitor
), "reference", reference
, (GDestroyNotify
)gtk_tree_row_reference_free
);
480 g_object_unref (file
);
484 on_row_expanded_async (GObject
* source_object
,
485 GAsyncResult
* result
,
488 FileModelAsyncData
* data
= user_data
;
489 GFile
* dir
= G_FILE (source_object
);
490 GFileEnumerator
* files
;
492 GtkTreeIter real_iter
;
494 GtkTreeRowReference
* ref
= data
->reference
;
496 FileModel
* model
= data
->model
;
497 GFileInfo
* file_info
;
499 files
= g_file_enumerate_children_finish (dir
, result
, &err
);
500 path
= gtk_tree_row_reference_get_path (ref
);
504 gtk_tree_row_reference_free (ref
);
505 g_object_unref (files
);
511 DEBUG_PRINT ("GIO-Error: %s", err
->message
);
513 // TODO: Collapse row
517 gtk_tree_model_get_iter (GTK_TREE_MODEL(data
->model
), &real_iter
, path
);
519 while (files
&& (file_info
= g_file_enumerator_next_file (files
, NULL
, NULL
)))
521 GFile
* file
= g_file_get_child (dir
, g_file_info_get_name(file_info
));
522 file_model_add_file (data
->model
, &real_iter
, file
, file_info
);
523 g_object_unref (file
);
524 g_object_unref (file_info
);
526 /* Remove dummy node */
527 gtk_tree_model_iter_children (GTK_TREE_MODEL(model
), &dummy
, &real_iter
);
528 gtk_tree_store_remove (GTK_TREE_STORE(model
), &dummy
);
530 file_model_add_watch (model
, path
);
531 file_model_get_vcs_status (model
, &real_iter
, dir
);
533 g_signal_emit (model
, signals
[SIGNAL_DIRECTORY_EXPANDED
], 0, &real_iter
, path
);
535 gtk_tree_path_free (path
);
536 gtk_tree_row_reference_free (ref
);
537 g_object_unref(files
);
541 file_model_row_expanded (GtkTreeView
* tree_view
, GtkTreeIter
* iter
,
542 GtkTreePath
* path
, gpointer user_data
)
544 GtkTreeModel
* sort_model
= gtk_tree_view_get_model(tree_view
);
545 FileModel
* model
= FILE_MODEL(user_data
);
547 GtkTreeIter real_iter
;
548 GCancellable
* cancel
= g_cancellable_new ();
549 GtkTreePath
* real_path
;
551 DEBUG_PRINT ("%s", "row_expanded");
553 gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(sort_model
),
556 gtk_tree_model_get(GTK_TREE_MODEL(model
), &real_iter
,
557 COLUMN_FILE
, &dir
, -1);
559 FileModelAsyncData
* data
= g_new0 (FileModelAsyncData
, 1);
561 real_path
= gtk_tree_model_get_path (GTK_TREE_MODEL (model
),
563 data
->reference
= gtk_tree_row_reference_new (GTK_TREE_MODEL (model
),
565 gtk_tree_path_free (real_path
);
567 g_object_set_data (G_OBJECT(dir
), "_cancel", cancel
);
569 g_file_enumerate_children_async (dir
,
571 G_FILE_QUERY_INFO_NONE
,
574 on_row_expanded_async
,
576 g_object_unref (dir
);
580 file_model_row_collapsed (GtkTreeView
* tree_view
, GtkTreeIter
* iter
,
581 GtkTreePath
* path
, gpointer data
)
583 GtkTreeModel
* sort_model
= gtk_tree_view_get_model(tree_view
);
584 FileModel
* model
= FILE_MODEL(data
);
586 GtkTreeIter sort_iter
;
587 GtkTreeIter real_iter
;
589 GCancellable
* cancel
;
591 /* Iter might be invalid in some conditions */
592 gtk_tree_model_get_iter (sort_model
, &sort_iter
, path
);
594 gtk_tree_model_sort_convert_iter_to_child_iter(GTK_TREE_MODEL_SORT(sort_model
),
595 &real_iter
, &sort_iter
);
597 gtk_tree_model_get (GTK_TREE_MODEL (model
), &real_iter
,
598 COLUMN_FILE
, &dir
, -1);
600 cancel
= g_object_get_data (G_OBJECT(dir
), "_cancel");
601 g_cancellable_cancel (cancel
);
602 g_object_unref (cancel
);
603 g_object_unref (dir
);
605 while (gtk_tree_model_iter_children (GTK_TREE_MODEL(model
), &child
, &real_iter
))
607 gtk_tree_store_remove (GTK_TREE_STORE (model
), &child
);
610 file_model_add_dummy (model
, &real_iter
);
614 file_model_init (FileModel
*object
)
620 file_model_finalize (GObject
*object
)
622 FileModel
*model
= FILE_MODEL(object
);
623 FileModelPrivate
* priv
= FILE_MODEL_GET_PRIVATE(model
);
625 g_clear_object (&priv
->base_path
);
627 g_object_remove_weak_pointer (G_OBJECT (priv
->ivcs
), (void**)&priv
->ivcs
);
629 G_OBJECT_CLASS (file_model_parent_class
)->finalize (object
);
633 file_model_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
635 g_return_if_fail (FILE_IS_MODEL (object
));
636 FileModel
* model
= FILE_MODEL(object
);
637 FileModelPrivate
* priv
= FILE_MODEL_GET_PRIVATE(model
);
642 g_clear_object (&priv
->base_path
);
643 priv
->base_path
= g_value_dup_object (value
);
644 if (!priv
->base_path
)
645 priv
->base_path
= g_file_new_for_uri ("file:///");
647 case PROP_FILTER_BINARY
:
648 priv
->filter_binary
= g_value_get_boolean (value
);
650 case PROP_FILTER_HIDDEN
:
651 priv
->filter_hidden
= g_value_get_boolean (value
);
653 case PROP_FILTER_BACKUP
:
654 priv
->filter_backup
= g_value_get_boolean (value
);
656 case PROP_FILTER_UNVERSIONED
:
657 priv
->filter_unversioned
= g_value_get_boolean (value
);
661 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
667 file_model_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
669 g_return_if_fail (FILE_IS_MODEL (object
));
670 FileModel
* model
= FILE_MODEL(object
);
671 FileModelPrivate
* priv
= FILE_MODEL_GET_PRIVATE(model
);
676 g_value_set_object (value
, priv
->base_path
);
678 case PROP_FILTER_BINARY
:
679 g_value_set_boolean (value
, priv
->filter_binary
);
680 case PROP_FILTER_HIDDEN
:
681 g_value_set_boolean (value
, priv
->filter_hidden
);
682 case PROP_FILTER_BACKUP
:
683 g_value_set_boolean (value
, priv
->filter_backup
);
684 case PROP_FILTER_UNVERSIONED
:
685 g_value_set_boolean (value
, priv
->filter_unversioned
);
688 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
695 file_model_class_init (FileModelClass
*klass
)
697 GObjectClass
* object_class
= G_OBJECT_CLASS (klass
);
699 object_class
->finalize
= file_model_finalize
;
700 object_class
->set_property
= file_model_set_property
;
701 object_class
->get_property
= file_model_get_property
;
703 g_type_class_add_private (object_class
, sizeof(FileModelPrivate
));
705 g_object_class_install_property (object_class
,
707 g_param_spec_object ("base-path",
709 _("GFile representing the top-most path displayed"),
711 G_PARAM_READABLE
| G_PARAM_WRITABLE
| G_PARAM_CONSTRUCT
));
712 g_object_class_install_property (object_class
,
714 g_param_spec_boolean ("filter_binary",
715 "Filter binary files",
718 G_PARAM_READABLE
| G_PARAM_WRITABLE
| G_PARAM_CONSTRUCT
));
720 g_object_class_install_property (object_class
,
722 g_param_spec_boolean ("filter_hidden",
723 "Filter hidden files",
726 G_PARAM_READABLE
| G_PARAM_WRITABLE
| G_PARAM_CONSTRUCT
));
728 g_object_class_install_property (object_class
,
730 g_param_spec_boolean ("filter_backup",
731 "Filter backup files",
734 G_PARAM_READABLE
| G_PARAM_WRITABLE
| G_PARAM_CONSTRUCT
));
736 g_object_class_install_property (object_class
,
737 PROP_FILTER_UNVERSIONED
,
738 g_param_spec_boolean ("filter_unversioned",
739 "Filter unversioned files",
742 G_PARAM_READABLE
| G_PARAM_WRITABLE
| G_PARAM_CONSTRUCT
));
744 signals
[SIGNAL_DIRECTORY_EXPANDED
] =
745 g_signal_new ("directory-expanded",
746 G_TYPE_FROM_CLASS (object_class
),
749 G_TYPE_NONE
, 2, GTK_TYPE_TREE_ITER
, GTK_TYPE_TREE_PATH
);
754 file_model_new (GtkTreeView
* tree_view
, GFile
* base_path
)
757 g_object_new (FILE_TYPE_MODEL
, "base-path", base_path
, NULL
);
758 GType types
[N_COLUMNS
] = {GDK_TYPE_PIXBUF
, G_TYPE_STRING
,
759 G_TYPE_STRING
, G_TYPE_UINT
, G_TYPE_OBJECT
,
760 G_TYPE_BOOLEAN
, G_TYPE_INT
, G_TYPE_BOOLEAN
};
761 FileModelPrivate
* priv
= FILE_MODEL_GET_PRIVATE(model
);
763 g_signal_connect (G_OBJECT (tree_view
), "row-collapsed",
764 G_CALLBACK (file_model_row_collapsed
), model
);
765 g_signal_connect (G_OBJECT (tree_view
), "row-expanded",
766 G_CALLBACK (file_model_row_expanded
), model
);
768 gtk_tree_store_set_column_types (GTK_TREE_STORE (model
), N_COLUMNS
,
771 priv
->view
= tree_view
;
773 return FILE_MODEL(model
);
777 file_model_refresh (FileModel
* model
)
779 GtkTreeStore
* store
= GTK_TREE_STORE (model
);
780 FileModelPrivate
* priv
= FILE_MODEL_GET_PRIVATE(model
);
781 GFileInfo
* base_info
;
783 gtk_tree_store_clear (store
);
785 base_info
= g_file_query_info (priv
->base_path
, "standard::*",
786 G_FILE_QUERY_INFO_NONE
, NULL
, NULL
);
791 file_model_add_file (model
, NULL
, priv
->base_path
, base_info
);
792 g_object_unref (base_info
);
796 file_model_get_file (FileModel
* model
, GtkTreeIter
* iter
)
800 gtk_tree_model_get (GTK_TREE_MODEL (model
), iter
, COLUMN_FILE
, &file
, -1);
806 file_model_get_filename (FileModel
* model
, GtkTreeIter
* iter
)
809 gtk_tree_model_get (GTK_TREE_MODEL (model
), iter
, COLUMN_FILENAME
, &filename
, -1);
815 file_model_set_ivcs (FileModel
* model
, IAnjutaVcs
*ivcs
)
817 FileModelPrivate
*priv
= FILE_MODEL_GET_PRIVATE (model
);
820 g_object_remove_weak_pointer (G_OBJECT (priv
->ivcs
), (void**)&priv
->ivcs
);
824 g_object_add_weak_pointer (G_OBJECT (priv
->ivcs
), (void**)&priv
->ivcs
);