1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
4 * Copyright (C) 2009 Sébastien Granjoux
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
27 #include "dir-project.h"
31 #include <libanjuta/interfaces/ianjuta-project.h>
32 #include <libanjuta/anjuta-debug.h>
33 #include <libanjuta/anjuta-utils.h>
41 #include <sys/types.h>
43 #include <glib/gi18n.h>
47 #define SOURCES_FILE PACKAGE_DATA_DIR "/sources.list"
50 AnjutaDirRootNode parent
;
52 /* shortcut hash tables, mapping id -> GNode from the tree above */
55 /* project files monitors */
58 /* List of source files pattern */
62 struct _DirProjectClass
{
63 AnjutaDirRootNodeClass parent_class
;
66 /* A file or directory name part of a path */
67 typedef struct _DirMatchString DirMatchString
;
69 struct _DirMatchString
79 /* A pattern used to match a part of a path */
80 typedef struct _DirPattern DirPattern
;
90 /* A list of pattern found in one file */
91 typedef struct _DirPatternList DirPatternList
;
93 struct _DirPatternList
98 GHashTable
*extensions
;
101 /* ----- Standard GObject types and variables ----- */
108 static GObject
*parent_class
;
111 *---------------------------------------------------------------------------*/
113 static AnjutaProjectNode
*
114 project_node_new (DirProject
*project
, AnjutaProjectNode
*parent
, AnjutaProjectNodeType type
, GFile
*file
, const gchar
*name
, GError
**error
)
116 AnjutaProjectNode
*node
= NULL
;
118 switch (type
& ANJUTA_PROJECT_TYPE_MASK
) {
119 case ANJUTA_PROJECT_GROUP
:
124 g_set_error (error
, IANJUTA_PROJECT_ERROR
,
125 IANJUTA_PROJECT_ERROR_VALIDATION_FAILED
,
132 group_file
= g_file_get_child (anjuta_project_node_get_file (parent
), name
);
133 node
= dir_group_node_new (group_file
, G_OBJECT (project
));
134 g_object_unref (group_file
);
139 node
= dir_group_node_new (file
, G_OBJECT (project
));
142 case ANJUTA_PROJECT_OBJECT
:
147 g_set_error (error
, IANJUTA_PROJECT_ERROR
,
148 IANJUTA_PROJECT_ERROR_VALIDATION_FAILED
,
155 object_file
= g_file_get_child (anjuta_project_node_get_file (parent
), name
);
156 node
= dir_object_node_new (object_file
);
157 g_object_unref (object_file
);
162 node
= dir_object_node_new (file
);
165 case ANJUTA_PROJECT_SOURCE
:
170 g_set_error (error
, IANJUTA_PROJECT_ERROR
,
171 IANJUTA_PROJECT_ERROR_VALIDATION_FAILED
,
178 source_file
= g_file_get_child (anjuta_project_node_get_file (parent
), name
);
179 node
= dir_source_node_new (source_file
);
180 g_object_unref (source_file
);
185 node
= dir_source_node_new (file
);
189 g_assert_not_reached ();
195 node
->parent
= parent
;
203 *---------------------------------------------------------------------------*/
206 dir_pattern_free (DirPattern
*pat
)
208 g_free (pat
->object
);
209 if (pat
->regex
!= NULL
) g_regex_unref (pat
->regex
);
211 g_slice_free (DirPattern
, pat
);
214 /* Create a new pattern matching a directory of a file name in a path */
217 dir_pattern_new (const gchar
*pattern
, gboolean reverse
)
219 DirPattern
*pat
= NULL
;
220 GString
*regex
= g_string_new (NULL
);
221 const char *ptr
= pattern
;
223 pat
= g_slice_new0(DirPattern
);
224 /* Check if it is a reverse pattern */
227 pat
->match
= reverse
? TRUE
: FALSE
;
232 pat
->match
= reverse
? FALSE
: TRUE
;
235 /* Check if the pattern is local */
238 g_string_append_c (regex
, '^');
243 g_string_append (regex
, "(?:^|\\" G_DIR_SEPARATOR_S
")");
251 next
= ptr
+ strcspn (ptr
, "\\:.^$[|()?*+{");
253 g_string_append_len (regex
, ptr
, next
- ptr
);
257 /* Remaining data are for object */
260 else if (*ptr
== '*')
262 /* Replace with corresponding regular expression */
263 g_string_append (regex
, "(.+)");
266 else if (*ptr
== '?')
268 /* Replace with corresponding regular expression */
272 } while (*ptr
== '?');
273 g_string_append_printf (regex
, "(.{%ld})", (long int) (ptr
- next
));
275 else if (*ptr
== '\\')
277 /* Add next character without a special signification */
278 g_string_append_c (regex
, *ptr
++);
279 if (*ptr
!= '\0') g_string_append_c (regex
, *ptr
++);
281 else if (isspace (*ptr
))
286 else if (*ptr
!= '\0')
288 /* Automatically escape character if not done */
289 if ((ptr
== pattern
) || (*(ptr
- 1) != '\\'))
291 g_string_append_c (regex
, '\\');
293 g_string_append_c (regex
, *ptr
++);
296 if ((regex
->len
> 1) && (regex
->str
[regex
->len
- 1] == '/'))
298 /* Match directory only */
299 pat
->directory
= TRUE
;
300 g_string_truncate (regex
, regex
->len
- 1);
302 g_string_append_c (regex
, '$');
303 pat
->regex
= g_regex_new (regex
->str
, G_REGEX_OPTIMIZE
, 0, NULL
);
304 if (pat
->regex
== NULL
)
306 dir_pattern_free (pat
);
310 if ((pat
!= NULL
) && (*ptr
== ':'))
312 g_string_truncate (regex
, 0);
315 while (isspace (*ptr
)) ptr
++;
322 next
= ptr
+ strcspn (ptr
, "\\?*");
324 g_string_append_len (regex
, ptr
, next
- ptr
);
328 /* Replace with corresponding replacement */
329 g_string_append_printf (regex
, "\\%d", replace
++);
332 else if (*ptr
== '?')
337 } while (*ptr
== '?');
338 g_string_append_printf (regex
, "\\%d", replace
++);
340 else if (*ptr
== '\\')
342 /* Add next character without a special signification */
343 g_string_append_c (regex
, *ptr
++);
344 if (*ptr
!= '\0') g_string_append_c (regex
, *ptr
++);
347 pat
->object
= g_string_free (regex
, FALSE
);
351 g_string_free (regex
, TRUE
);
357 /* Replace regular expression by a lookup in a hash table if we look only for
358 * a file with a particular extension as it's much faster.
359 * Return TRUE if it is possible */
362 dir_pattern_optimize (DirPattern
*pat
, DirPattern
*last
, GHashTable
*extensions
)
364 const gchar
*pattern
= g_regex_get_pattern (pat
->regex
);
367 ext
= strrchr (pattern
, '.');
369 (strncmp (pattern
, "(?:^|\\/)(.+)\\", ext
- pattern
) == 0))
373 for (ptr
= ext
+ 1; isalnum(*ptr
) || (*ptr
== '_') || ((ptr
[0] == '\\') && (ptr
[1] == '+')); *ptr
== '\\' ? ptr
+= 2 : ptr
++);
374 if ((ptr
[0] == '$') && (ptr
[1] == '\0'))
376 gchar
*key
= g_strndup (ext
+ 1, strlen(ext
) - 2);
377 if (g_hash_table_lookup (extensions
, key
) == NULL
)
379 g_hash_table_insert (extensions
, key
, last
== NULL
? pat
: last
);
389 /* Read a file containing pattern, the syntax is similar to .gitignore file.
391 * It is not a regular expression, only * and ? are used as joker.
392 * If the name end with / it will match only a directory.
393 * If the name starts with / it must be relative to the project directory, so
394 * by example /.git/ will match only a directory named .git in the project
395 * directory, while CVS/ will match a directory named CVS anywhere in the
397 * If the name starts with ! the meaning is reversed. In a file containing
398 * matching file, if a pattern starting ! matches, it means that the file has
399 * to be removed from the matching list.
400 * All pattern are read in order, so it is possible to match a group of files
401 * and add pattern afterward to remove some of these files.
402 * A name starting with # is a comment.
403 * All spaces at the beginning of a name are ignored.
404 * If a name is followed by : a following name is taken as the object file name
407 dir_push_pattern_list (GList
*stack
, GFile
*dir
, GFile
*file
, gboolean ignore
, GError
**error
)
411 DirPatternList
*list
= NULL
;
413 DirPattern
*last
= NULL
;
415 if (!g_file_load_contents (file
, NULL
, &content
, NULL
, NULL
, error
))
420 list
= g_slice_new0(DirPatternList
);
421 list
->sources
= NULL
;
422 list
->objects
= NULL
;
423 list
->directory
= dir
;
424 list
->extensions
= g_hash_table_new_full (g_str_hash
, g_str_equal
, (GDestroyNotify
)g_free
, NULL
);
427 for (ptr
= content
; *ptr
!= '\0';)
431 next
= strchr (ptr
, '\n');
432 if (next
!= NULL
) *next
= '\0';
435 /* Discard space at the beginning */
436 while (isspace (*ptr
)) ptr
++;
438 if ((*ptr
!= '#') && (ptr
!= next
))
441 DirPattern
*pat
= NULL
;
442 gboolean used
= FALSE
;
444 if (next
!= NULL
) *next
= '\0';
445 pat
= dir_pattern_new (ptr
, ignore
);
448 if ((last
!= NULL
) && (last
->match
!= pat
->match
)) last
= NULL
;
449 if (dir_pattern_optimize (pat
, last
, list
->extensions
))
454 g_regex_unref (pat
->regex
);
456 list
->sources
= g_list_prepend (list
->sources
, pat
);
462 list
->sources
= g_list_prepend (list
->sources
, pat
);
467 if (pat
->object
!= NULL
)
469 if (used
) pat
= dir_pattern_new (ptr
, ignore
);
470 list
->objects
= g_list_prepend (list
->objects
, pat
);
474 dir_pattern_free (pat
);
479 gchar
*filename
= g_file_get_path (file
);
480 g_warning("Invalid pattern in %s line %d", filename
, line
);
485 if (next
== NULL
) break;
490 list
->sources
= g_list_reverse (list
->sources
);
491 list
->objects
= g_list_reverse (list
->objects
);
493 return g_list_prepend (stack
, list
);
497 dir_pop_pattern_list (GList
*stack
)
499 DirPatternList
*top
= (DirPatternList
*)stack
->data
;
501 stack
= g_list_remove_link (stack
, stack
);
503 g_list_foreach (top
->sources
, (GFunc
)dir_pattern_free
, NULL
);
504 g_list_free (top
->sources
);
505 g_list_foreach (top
->objects
, (GFunc
)dir_pattern_free
, NULL
);
506 g_list_free (top
->objects
);
507 g_object_unref (top
->directory
);
508 g_hash_table_destroy (top
->extensions
);
509 g_slice_free (DirPatternList
, top
);
515 dir_pattern_stack_is_match (GFile
*root
, GList
*stack
, GFile
*file
)
522 /* Create name from file */
523 filename
= g_file_get_relative_path (root
, file
);
525 directory
= g_file_query_file_type (file
, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
, NULL
) == G_FILE_TYPE_DIRECTORY
;
526 /* Include directories by default */
529 /* Check all valid patterns */
530 for (list
= g_list_last (stack
); list
!= NULL
; list
= g_list_previous (list
))
532 DirPatternList
*pat_list
= (DirPatternList
*)list
->data
;
534 DirPattern
*pat_ext
= NULL
;
537 /* Check only the extension to be faster on the common case */
538 ext
= strrchr (filename
, '.');
541 pat_ext
= g_hash_table_lookup (pat_list
->extensions
, ext
+ 1);
544 for (node
= g_list_first (pat_list
->sources
); node
!= NULL
; node
= g_list_next (node
))
546 DirPattern
*pat
= (DirPattern
*)node
->data
;
548 if ((pat
->directory
&& !directory
) || (!pat
->directory
&& directory
))
551 if ((pat
== pat_ext
) || ((pat
->regex
!= NULL
) && g_regex_match (pat
->regex
, filename
, 0, NULL
)))
563 dir_pattern_find_file_object (GFile
*root
, GList
*stack
, GFile
*file
)
565 GFile
*object
= NULL
;
567 if (g_file_query_file_type (file
, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
, NULL
) != G_FILE_TYPE_DIRECTORY
)
572 /* Create name from file */
573 filename
= g_file_get_relative_path (root
, file
);
575 /* Check all valid patterns */
576 for (list
= g_list_last (stack
); list
!= NULL
; list
= g_list_previous (list
))
578 DirPatternList
*pat_list
= (DirPatternList
*)list
->data
;
581 for (node
= g_list_first (pat_list
->objects
); node
!= NULL
; node
= g_list_next (node
))
583 DirPattern
*pat
= (DirPattern
*)node
->data
;
585 if (pat
->directory
|| !pat
->match
|| (pat
->object
== NULL
) )
588 if (g_regex_match (pat
->regex
, filename
, 0, NULL
))
592 objname
= g_regex_replace (pat
->regex
, filename
, -1, 0, pat
->object
, 0, NULL
);
593 object
= g_file_get_child (root
, objname
);
607 AnjutaProjectNode
*parent
;
610 /* the number of files to enumerate each time */
614 dir_project_load_directory_callback (GObject
*source_object
,
618 GFileEnumerator
*enumerator
= G_FILE_ENUMERATOR(source_object
);
621 DirData
*data
= (DirData
*) user_data
;
624 infos
= g_file_enumerator_next_files_finish (enumerator
, res
, &err
);
626 GList
*removed
= NULL
;
628 /* either we are finished or an error occured */
629 anjuta_project_node_clear_state (data
->parent
, ANJUTA_PROJECT_LOADING
);
631 g_signal_emit_by_name (data
->proj
, "node-loaded", data
->parent
, err
);
634 AnjutaProjectNode
*node
, *next
;
635 for (node
= anjuta_project_node_first_child (data
->parent
);
639 int state
= anjuta_project_node_get_state (node
);
641 next
= anjuta_project_node_next_sibling (node
);
642 if (state
& ANJUTA_PROJECT_LOADING
)
644 /* these nodes are no longer necessary */
645 gchar
*uri
= g_file_get_uri (node
->file
);
646 g_hash_table_remove (data
->proj
->groups
, uri
);
649 anjuta_project_node_remove (node
);
650 removed
= g_list_prepend (removed
, node
);
653 g_signal_emit_by_name (data
->proj
, "node-loaded", data
->parent
, NULL
);
655 g_list_foreach (removed
, (GFunc
)g_object_unref
, NULL
);
656 g_list_free (removed
);
657 g_object_unref (data
->parent
);
658 g_slice_free (DirData
, data
);
659 g_object_unref (enumerator
);
664 root
= anjuta_project_node_get_file (ANJUTA_PROJECT_NODE (data
->proj
));
665 for (l
= infos
; l
!= NULL
; l
= g_list_next(l
))
671 info
= G_FILE_INFO(l
->data
);
673 name
= g_file_info_get_name (info
);
674 file
= g_file_get_child (data
->parent
->file
, name
);
675 g_object_unref (info
);
677 /* Check if file is a source */
678 if (!dir_pattern_stack_is_match (root
, data
->proj
->sources
, file
)) continue;
680 if (g_file_query_file_type (file
, G_FILE_QUERY_INFO_NONE
, NULL
) == G_FILE_TYPE_DIRECTORY
)
682 AnjutaProjectNode
*group
;
685 uri
= g_file_get_uri (file
);
686 group
= g_hash_table_lookup (data
->proj
->groups
, uri
);
689 anjuta_project_node_clear_state (group
, ANJUTA_PROJECT_LOADING
);
694 /* Create a group for directory */
695 group
= project_node_new (data
->proj
, NULL
, ANJUTA_PROJECT_GROUP
, file
, NULL
, NULL
);
696 g_hash_table_insert (data
->proj
->groups
, uri
, group
);
697 anjuta_project_node_append (data
->parent
, group
);
698 anjuta_project_node_set_state (group
, ANJUTA_PROJECT_INCOMPLETE
);
703 AnjutaProjectNode
*source
= NULL
;
705 AnjutaProjectNode
*node
;
706 for (node
= anjuta_project_node_first_child (data
->parent
);
708 node
= anjuta_project_node_next_sibling (node
))
710 source
= (anjuta_project_node_get_node_type (node
) == ANJUTA_PROJECT_OBJECT
) ?
711 anjuta_project_node_first_child (node
) :
713 if (g_file_equal (file
, anjuta_project_node_get_file (source
)))
715 anjuta_project_node_clear_state (node
, ANJUTA_PROJECT_LOADING
);
724 AnjutaProjectNode
*parent
;
726 /* Create object if possible */
727 object
= dir_pattern_find_file_object (root
, data
->proj
->sources
, file
);
730 parent
= project_node_new (data
->proj
, NULL
, ANJUTA_PROJECT_OBJECT
| ANJUTA_PROJECT_PROJECT
, object
, NULL
, NULL
);
731 g_object_unref (object
);
732 anjuta_project_node_append (data
->parent
, parent
);
736 parent
= data
->parent
;
739 /* Create a source for files */
740 source
= project_node_new (data
->proj
, NULL
, ANJUTA_PROJECT_SOURCE
| ANJUTA_PROJECT_PROJECT
, file
, NULL
, NULL
);
741 anjuta_project_node_append (parent
, source
);
747 g_file_enumerator_next_files_async (enumerator
, NUM_FILES
, G_PRIORITY_DEFAULT_IDLE
, NULL
,
748 dir_project_load_directory_callback
, data
);
752 *---------------------------------------------------------------------------*/
754 static AnjutaProjectNode
*
755 dir_project_load_directory (DirProject
*project
, AnjutaProjectNode
*parent
, GError
**error
)
757 GFileEnumerator
*enumerator
;
759 enumerator
= g_file_enumerate_children (parent
->file
,
760 G_FILE_ATTRIBUTE_STANDARD_NAME
,
761 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS
,
765 if (enumerator
== NULL
)
768 /* mark all children as loading so we can remove them if no longer relevant */
769 AnjutaProjectNode
*node
;
770 for (node
= anjuta_project_node_first_child (parent
);
772 node
= anjuta_project_node_next_sibling (node
))
774 anjuta_project_node_set_state (node
, ANJUTA_PROJECT_LOADING
);
777 DirData
*data
= g_slice_new (DirData
);
778 data
->proj
= project
;
779 data
->parent
= g_object_ref (parent
);
781 g_file_enumerator_next_files_async (enumerator
, NUM_FILES
, G_PRIORITY_DEFAULT_IDLE
, NULL
,
782 dir_project_load_directory_callback
, data
);
784 anjuta_project_node_set_state (parent
, ANJUTA_PROJECT_LOADING
);
788 static AnjutaProjectNode
*
789 dir_project_load_root (DirProject
*project
, GError
**error
)
794 root_file
= anjuta_project_node_get_file (ANJUTA_PROJECT_NODE (project
));
795 DEBUG_PRINT ("reload project %p root file %p", project
, root_file
);
797 if (g_file_query_file_type (root_file
, G_FILE_QUERY_INFO_NONE
, NULL
) != G_FILE_TYPE_DIRECTORY
)
799 g_set_error (error
, IANJUTA_PROJECT_ERROR
,
800 IANJUTA_PROJECT_ERROR_DOESNT_EXIST
,
801 _("Project doesn't exist or invalid path"));
806 /* shortcut hash tables */
807 if (project
->groups
== NULL
)
809 project
->groups
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
812 /* Load source pattern */
813 source_file
= g_file_new_for_path (SOURCES_FILE
);
814 project
->sources
= dir_push_pattern_list (NULL
, g_object_ref (root_file
), source_file
, FALSE
, NULL
);
815 g_object_unref (source_file
);
817 dir_group_node_set_file (ANJUTA_DIR_GROUP_NODE (project
), root_file
, G_OBJECT (project
));
818 dir_project_load_directory (project
, ANJUTA_PROJECT_NODE (project
), NULL
);
820 return ANJUTA_PROJECT_NODE (project
);
824 dir_project_load_node (DirProject
*project
, AnjutaProjectNode
*node
, GError
**error
)
826 if (node
== NULL
) node
= ANJUTA_PROJECT_NODE (project
);
827 switch (anjuta_project_node_get_node_type (node
))
829 case ANJUTA_PROJECT_ROOT
:
830 return dir_project_load_root (project
, error
);
831 case ANJUTA_PROJECT_GROUP
:
832 return dir_project_load_directory (project
, node
, error
);
839 foreach_node_save (AnjutaProjectNode
*node
,
842 gint state
= anjuta_project_node_get_state (node
);
843 AnjutaProjectNode
*parent
;
847 if (state
& ANJUTA_PROJECT_MODIFIED
)
849 switch (anjuta_project_node_get_node_type (node
))
851 case ANJUTA_PROJECT_GROUP
:
852 g_file_make_directory_with_parents (anjuta_project_node_get_file (node
), NULL
, NULL
);
854 case ANJUTA_PROJECT_SOURCE
:
856 (parent
!= NULL
) && (anjuta_project_node_get_node_type (parent
) != ANJUTA_PROJECT_GROUP
) && (anjuta_project_node_get_node_type (parent
) != ANJUTA_PROJECT_ROOT
);
857 parent
= anjuta_project_node_parent (parent
));
860 GFile
*file
= anjuta_project_node_get_file (node
);
861 gchar
*basename
= g_file_get_basename (file
);
862 GFile
*dest
= g_file_get_child (anjuta_project_node_get_file (parent
), basename
);
865 if (!g_file_equal (dest
, file
))
867 g_file_copy_async (file
, dest
, G_FILE_COPY_BACKUP
, G_PRIORITY_DEFAULT
, NULL
, NULL
, NULL
, NULL
, NULL
);
872 g_object_unref (dest
);
879 else if (state
& ANJUTA_PROJECT_REMOVED
)
881 switch (anjuta_project_node_get_node_type (node
))
883 case ANJUTA_PROJECT_GROUP
:
884 case ANJUTA_PROJECT_SOURCE
:
885 g_file_trash (anjuta_project_node_get_file (node
), NULL
, &err
);
888 g_warning ("trashing file failed with %s", err
->message
);
897 anjuta_project_node_clear_state (node
, ANJUTA_PROJECT_REMOVED
| ANJUTA_PROJECT_MODIFIED
);
901 dir_project_save_node (DirProject
*project
, AnjutaProjectNode
*node
, GError
**error
)
904 anjuta_project_node_foreach (node
, G_POST_ORDER
, foreach_node_save
, project
);
910 dir_project_unload (DirProject
*project
)
912 /* shortcut hash tables */
913 if (project
->groups
) g_hash_table_destroy (project
->groups
);
914 project
->groups
= NULL
;
916 /* sources patterns */
917 while (project
->sources
)
919 project
->sources
= dir_pop_pattern_list (project
->sources
);
924 dir_project_probe (GFile
*file
,
929 probe
= g_file_query_file_type (file
, G_FILE_QUERY_INFO_NONE
, NULL
) == G_FILE_TYPE_DIRECTORY
;
932 g_set_error (error
, IANJUTA_PROJECT_ERROR
,
933 IANJUTA_PROJECT_ERROR_DOESNT_EXIST
,
934 _("Project doesn't exist or invalid path"));
937 return probe
? IANJUTA_PROJECT_PROBE_FILES
: 0;
941 dir_project_get_node_info (DirProject
*project
, GError
**error
)
943 static AnjutaProjectNodeInfo node_info
[] = {
944 {ANJUTA_PROJECT_GROUP
,
948 {ANJUTA_PROJECT_SOURCE
,
952 {ANJUTA_PROJECT_UNKNOWN
,
956 static GList
*info_list
= NULL
;
958 if (info_list
== NULL
)
960 AnjutaProjectNodeInfo
*node
;
962 for (node
= node_info
; node
->type
!= 0; node
++)
964 info_list
= g_list_prepend (info_list
, node
);
967 info_list
= g_list_reverse (info_list
);
974 find_not_loaded_node (gpointer key
, gpointer value
, gpointer user_data
)
976 AnjutaProjectNode
*node
= (AnjutaProjectNode
*)value
;
979 found
= anjuta_project_node_get_state (node
) & (ANJUTA_PROJECT_LOADING
| ANJUTA_PROJECT_INCOMPLETE
);
985 dir_project_is_loaded (DirProject
*project
)
987 if (project
->groups
== NULL
)
990 return g_hash_table_find (project
->groups
, find_not_loaded_node
, NULL
) == NULL
;
994 *---------------------------------------------------------------------------*/
997 dir_project_new (GFile
*directory
, GError
**error
)
1001 project
= DIR_PROJECT (g_object_new (DIR_TYPE_PROJECT
, NULL
));
1002 project
->parent
.base
.file
= g_object_ref (directory
);
1008 /* Implement IAnjutaProject
1009 *---------------------------------------------------------------------------*/
1012 iproject_load_node (IAnjutaProject
*obj
, AnjutaProjectNode
*node
, GError
**err
)
1014 node
= dir_project_load_node (DIR_PROJECT (obj
), node
, err
);
1016 return node
!= NULL
;
1020 iproject_save_node (IAnjutaProject
*obj
, AnjutaProjectNode
*node
, GError
**err
)
1022 node
= dir_project_save_node (DIR_PROJECT (obj
), node
, err
);
1023 g_signal_emit_by_name (obj
, "node-saved", node
, err
);
1025 return node
!= NULL
;
1028 static AnjutaProjectNode
*
1029 iproject_add_node_before (IAnjutaProject
*obj
, AnjutaProjectNode
*parent
, AnjutaProjectNode
*sibling
, AnjutaProjectNodeType type
, GFile
*file
, const gchar
*name
, GError
**error
)
1031 AnjutaProjectNode
*node
;
1033 node
= project_node_new (DIR_PROJECT (obj
), parent
, type
, file
, name
, error
);
1034 anjuta_project_node_set_state (node
, ANJUTA_PROJECT_MODIFIED
);
1035 anjuta_project_node_insert_before (parent
, sibling
, node
);
1037 g_signal_emit_by_name (obj
, "node-changed", node
, NULL
);
1042 static AnjutaProjectNode
*
1043 iproject_add_node_after (IAnjutaProject
*obj
, AnjutaProjectNode
*parent
, AnjutaProjectNode
*sibling
, AnjutaProjectNodeType type
, GFile
*file
, const gchar
*name
, GError
**error
)
1045 AnjutaProjectNode
*node
;
1047 node
= project_node_new (DIR_PROJECT (obj
), parent
, type
, file
, name
, error
);
1048 anjuta_project_node_set_state (node
, ANJUTA_PROJECT_MODIFIED
);
1049 anjuta_project_node_insert_after (parent
, sibling
, node
);
1051 g_signal_emit_by_name (obj
, "node-changed", node
, NULL
);
1057 iproject_remove_node (IAnjutaProject
*obj
, AnjutaProjectNode
*node
, GError
**err
)
1059 anjuta_project_node_set_state (node
, ANJUTA_PROJECT_REMOVED
);
1060 g_signal_emit_by_name (obj
, "node-changed", node
, NULL
);
1065 static AnjutaProjectProperty
*
1066 iproject_set_property (IAnjutaProject
*obj
, AnjutaProjectNode
*node
, const gchar
*id
, const gchar
*name
, const gchar
*value
, GError
**error
)
1068 g_set_error (error
, IANJUTA_PROJECT_ERROR
,
1069 IANJUTA_PROJECT_ERROR_NOT_SUPPORTED
,
1070 _("Project doesn't allow to set properties"));
1076 iproject_remove_property (IAnjutaProject
*obj
, AnjutaProjectNode
*node
, const gchar
*id
, const gchar
*name
, GError
**error
)
1078 g_set_error (error
, IANJUTA_PROJECT_ERROR
,
1079 IANJUTA_PROJECT_ERROR_NOT_SUPPORTED
,
1080 _("Project doesn't allow to set properties"));
1085 static AnjutaProjectNode
*
1086 iproject_get_root (IAnjutaProject
*obj
, GError
**error
)
1088 return ANJUTA_PROJECT_NODE (obj
);
1092 iproject_get_node_info (IAnjutaProject
*obj
, GError
**err
)
1094 return dir_project_get_node_info (DIR_PROJECT (obj
), err
);
1098 iproject_is_loaded (IAnjutaProject
*obj
, GError
**err
)
1100 return dir_project_is_loaded (DIR_PROJECT (obj
));
1104 iproject_iface_init(IAnjutaProjectIface
* iface
)
1106 iface
->load_node
= iproject_load_node
;
1107 iface
->save_node
= iproject_save_node
;
1108 iface
->add_node_before
= iproject_add_node_before
;
1109 iface
->add_node_after
= iproject_add_node_after
;
1110 iface
->remove_node
= iproject_remove_node
;
1111 iface
->set_property
= iproject_set_property
;
1112 iface
->remove_property
= iproject_remove_property
;
1113 iface
->get_root
= iproject_get_root
;
1114 iface
->get_node_info
= iproject_get_node_info
;
1115 iface
->is_loaded
= iproject_is_loaded
;
1118 /* GbfProject implementation
1119 *---------------------------------------------------------------------------*/
1122 dir_project_dispose (GObject
*object
)
1124 g_return_if_fail (DIR_IS_PROJECT (object
));
1126 dir_project_unload (DIR_PROJECT (object
));
1128 G_OBJECT_CLASS (parent_class
)->dispose (object
);
1132 dir_project_instance_init (DirProject
*project
)
1134 g_return_if_fail (project
!= NULL
);
1135 g_return_if_fail (DIR_IS_PROJECT (project
));
1137 project
->monitors
= NULL
;
1138 project
->groups
= NULL
;
1140 project
->sources
= NULL
;
1144 dir_project_class_init (DirProjectClass
*klass
)
1146 GObjectClass
*object_class
;
1148 parent_class
= g_type_class_peek_parent (klass
);
1150 object_class
= G_OBJECT_CLASS (klass
);
1151 object_class
->dispose
= dir_project_dispose
;
1154 ANJUTA_TYPE_BEGIN(DirProject
, dir_project
, ANJUTA_TYPE_DIR_ROOT_NODE
);
1155 ANJUTA_TYPE_ADD_INTERFACE(iproject
, IANJUTA_TYPE_PROJECT
);