Updated Spanish translation
[anjuta.git] / plugins / mk-project / mk-project.c
blobae0bc9e3d5351e25911730ef81f79bb22b9013fe
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
2 /* mk-project.c
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., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include "mk-project.h"
28 #include "mk-rule.h"
30 #include "mk-project-private.h"
32 #include <libanjuta/interfaces/ianjuta-project.h>
33 #include <libanjuta/anjuta-debug.h>
34 #include <libanjuta/anjuta-utils.h>
35 #include <libanjuta/anjuta-pkg-config.h>
37 #include <string.h>
38 #include <memory.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <ctype.h>
43 #include <sys/types.h>
44 #include <signal.h>
45 #include <glib/gi18n.h>
46 #include <gio/gio.h>
47 #include <glib.h>
48 #include "mk-scanner.h"
51 #define UNIMPLEMENTED G_STMT_START { g_warning (G_STRLOC": unimplemented"); } G_STMT_END
53 /* Constant strings for parsing perl script error output */
54 #define ERROR_PREFIX "ERROR("
55 #define WARNING_PREFIX "WARNING("
56 #define MESSAGE_DELIMITER ": "
58 static const gchar *valid_makefiles[] = {"GNUmakefile", "makefile", "Makefile", NULL};
61 /* Target types
62 *---------------------------------------------------------------------------*/
64 static MkpNodeInfo MkpNodeInformation[] = {
65 {{ANJUTA_PROJECT_GROUP,
66 N_("Group"),
67 "",
68 NULL}},
70 {{ANJUTA_PROJECT_SOURCE,
71 N_("Source"),
72 "",
73 NULL}},
75 {{ANJUTA_PROJECT_TARGET,
76 N_("Unknown"),
77 "text/plain",
78 NULL}},
80 {{ANJUTA_PROJECT_UNKNOWN,
81 NULL,
82 NULL,
83 NULL}}
87 /* ----- Standard GObject types and variables ----- */
89 enum {
90 PROP_0,
91 PROP_PROJECT_DIR
94 static GObject *parent_class;
96 /* Helper functions
97 *---------------------------------------------------------------------------*/
99 #if 0
100 static void
101 error_set (GError **error, gint code, const gchar *message)
103 if (error != NULL) {
104 if (*error != NULL) {
105 gchar *tmp;
107 /* error already created, just change the code
108 * and prepend the string */
109 (*error)->code = code;
110 tmp = (*error)->message;
111 (*error)->message = g_strconcat (message, "\n\n", tmp, NULL);
112 g_free (tmp);
114 } else {
115 *error = g_error_new_literal (IANJUTA_PROJECT_ERROR,
116 code,
117 message);
121 #endif
123 /* Work even if file is not a descendant of parent */
124 static gchar*
125 get_relative_path (GFile *parent, GFile *file)
127 gchar *relative;
129 relative = g_file_get_relative_path (parent, file);
130 if (relative == NULL)
132 if (g_file_equal (parent, file))
134 relative = g_strdup ("");
136 else
138 GFile *grand_parent = g_file_get_parent (parent);
139 gint level;
140 gchar *grand_relative;
141 gchar *ptr;
142 gsize len;
145 for (level = 1; !g_file_has_prefix (file, grand_parent); level++)
147 GFile *next = g_file_get_parent (grand_parent);
149 g_object_unref (grand_parent);
150 grand_parent = next;
153 grand_relative = g_file_get_relative_path (grand_parent, file);
154 g_object_unref (grand_parent);
156 len = strlen (grand_relative);
157 relative = g_new (gchar, len + level * 3 + 1);
158 ptr = relative;
159 for (; level; level--)
161 memcpy(ptr, ".." G_DIR_SEPARATOR_S, 3);
162 ptr += 3;
164 memcpy (ptr, grand_relative, len + 1);
165 g_free (grand_relative);
169 return relative;
172 static GFileType
173 file_type (GFile *file, const gchar *filename)
175 GFile *child;
176 GFileInfo *info;
177 GFileType type;
179 child = filename != NULL ? g_file_get_child (file, filename) : g_object_ref (file);
181 //g_message ("check file %s", g_file_get_path (child));
183 info = g_file_query_info (child,
184 G_FILE_ATTRIBUTE_STANDARD_TYPE,
185 G_FILE_QUERY_INFO_NONE,
186 NULL,
187 NULL);
188 if (info != NULL)
190 type = g_file_info_get_file_type (info);
191 g_object_unref (info);
193 else
195 type = G_FILE_TYPE_UNKNOWN;
198 g_object_unref (child);
200 return type;
203 /* Group node
204 *---------------------------------------------------------------------------*/
206 static AnjutaProjectNode*
207 mkp_group_new (GFile *file)
209 MkpGroup *group = g_object_new (MKP_TYPE_GROUP, NULL);;
210 group->base.file = g_object_ref (file);
212 group->base.type = ANJUTA_PROJECT_GROUP;
213 group->base.properties = NULL;
214 group->base.properties_info = NULL;
215 group->base.name = NULL;
216 group->base.state = 0;
219 return ANJUTA_PROJECT_NODE(group);
222 static void
223 mkp_group_class_init (MkpGroupClass *klass)
228 static void
229 mkp_group_init (MkpGroup *obj)
234 G_DEFINE_TYPE (MkpGroup, mkp_group, ANJUTA_TYPE_PROJECT_NODE);
236 /* Target node
237 *---------------------------------------------------------------------------*/
239 void
240 mkp_target_add_token (MkpTarget *target, AnjutaToken *token)
242 target->tokens = g_list_prepend (target->tokens, token);
246 static GList *
247 mkp_target_get_token (MkpTarget *target)
249 return target->tokens;
252 AnjutaProjectNode*
253 mkp_target_new (const gchar *name, AnjutaProjectNodeType type)
255 MkpTarget *target = NULL;
257 target = g_object_new (MKP_TYPE_TARGET, NULL);
258 target->base.name = g_strdup (name);
259 target->base.type = ANJUTA_PROJECT_TARGET;
260 target->base.state = 0;
262 return ANJUTA_PROJECT_NODE(target);
265 static void
266 mkp_target_class_init (MkpTargetClass *klass)
271 static void
272 mkp_target_init (MkpTarget *obj)
277 G_DEFINE_TYPE (MkpTarget, mkp_target, ANJUTA_TYPE_PROJECT_NODE);
279 /* Object node
280 *---------------------------------------------------------------------------*/
282 AnjutaProjectNode*
283 mkp_object_new (const gchar *name)
285 MkpObject *node = NULL;
287 node = g_object_new (MKP_TYPE_OBJECT, NULL);
288 node->base.name = g_strdup (name);
289 node->base.type = ANJUTA_PROJECT_OBJECT;
290 node->base.state = 0;
292 return ANJUTA_PROJECT_NODE(node);
295 static void
296 mkp_object_class_init (MkpObjectClass *klass)
301 static void
302 mkp_object_init (MkpObject *obj)
307 G_DEFINE_TYPE (MkpObject, mkp_object, ANJUTA_TYPE_PROJECT_NODE);
310 /* Source node
311 *---------------------------------------------------------------------------*/
313 AnjutaProjectNode*
314 mkp_source_new (GFile *file)
316 MkpSource *source = NULL;
318 source = g_object_new (MKP_TYPE_SOURCE, NULL);
319 source->base.file = g_object_ref (file);
320 source->base.type = ANJUTA_PROJECT_SOURCE;
321 source->base.properties = NULL;
322 source->base.properties_info = NULL;
323 source->base.name = NULL;
324 source->base.state = 0;
326 return ANJUTA_PROJECT_NODE (source);
329 static void
330 mkp_source_class_init (MkpSourceClass *klass)
335 static void
336 mkp_source_init (MkpSource *obj)
341 G_DEFINE_TYPE (MkpSource, mkp_source, ANJUTA_TYPE_PROJECT_NODE);
344 * File monitoring support --------------------------------
345 * FIXME: review these
347 static void
348 monitor_cb (GFileMonitor *monitor,
349 GFile *file,
350 GFile *other_file,
351 GFileMonitorEvent event_type,
352 gpointer data)
354 MkpProject *project = data;
356 g_return_if_fail (project != NULL && MKP_IS_PROJECT (project));
358 switch (event_type) {
359 case G_FILE_MONITOR_EVENT_CHANGED:
360 case G_FILE_MONITOR_EVENT_CREATED:
361 g_signal_emit_by_name (G_OBJECT (project), "file-changed", data);
362 break;
363 default:
364 break;
369 static void
370 monitor_add (MkpProject *project, GFile *file)
372 GFileMonitor *monitor = NULL;
374 g_return_if_fail (project != NULL);
375 g_return_if_fail (project->monitors != NULL);
377 if (file == NULL)
378 return;
380 monitor = g_hash_table_lookup (project->monitors, file);
381 if (!monitor) {
382 gboolean exists;
384 /* FIXME clarify if uri is uri, path or both */
385 exists = g_file_query_exists (file, NULL);
387 if (exists) {
388 monitor = g_file_monitor_file (file,
389 G_FILE_MONITOR_NONE,
390 NULL,
391 NULL);
392 if (monitor != NULL)
394 g_signal_connect (G_OBJECT (monitor),
395 "changed",
396 G_CALLBACK (monitor_cb),
397 project);
398 g_hash_table_insert (project->monitors,
399 g_object_ref (file),
400 monitor);
406 static void
407 monitors_remove (MkpProject *project)
409 g_return_if_fail (project != NULL);
411 if (project->monitors)
412 g_hash_table_destroy (project->monitors);
413 project->monitors = NULL;
416 static void
417 files_hash_foreach_monitor (gpointer key,
418 gpointer value,
419 gpointer user_data)
421 GFile *makefile = (GFile *)key;
422 MkpProject *project = user_data;
424 monitor_add (project, makefile);
427 static void
428 monitors_setup (MkpProject *project)
430 g_return_if_fail (project != NULL);
432 monitors_remove (project);
434 /* setup monitors hash */
435 project->monitors = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
436 (GDestroyNotify) g_file_monitor_cancel);
438 if (project->files)
439 g_hash_table_foreach (project->files, files_hash_foreach_monitor, project);
444 * ---------------- Data structures managment
447 static AnjutaProjectNode *
448 project_node_new (MkpProject *project, AnjutaProjectNode *parent, AnjutaProjectNodeType type, GFile *file, const gchar *name, GError **error)
450 AnjutaProjectNode *node = NULL;
452 switch (type & ANJUTA_PROJECT_TYPE_MASK) {
453 case ANJUTA_PROJECT_ROOT:
454 case ANJUTA_PROJECT_GROUP:
455 node = ANJUTA_PROJECT_NODE (mkp_group_new (file));
456 break;
457 case ANJUTA_PROJECT_TARGET:
458 node = ANJUTA_PROJECT_NODE (mkp_target_new (name, 0));
459 break;
460 case ANJUTA_PROJECT_SOURCE:
461 node = ANJUTA_PROJECT_NODE (mkp_source_new (file));
462 break;
463 default:
464 g_assert_not_reached ();
465 break;
467 if (node != NULL) node->type = type;
469 return node;
472 static AnjutaProjectNode*
473 project_load_makefile (MkpProject *project, GFile *file, MkpGroup *parent, GError **error)
475 MkpScanner *scanner;
476 AnjutaToken *arg;
477 AnjutaTokenFile *tfile;
478 AnjutaToken *parse;
479 gboolean ok;
480 GError *err = NULL;
482 /* Parse makefile */
483 DEBUG_PRINT ("Parse: %s", g_file_get_uri (file));
484 tfile = anjuta_token_file_new (file);
485 g_hash_table_insert (project->files, g_object_ref (file), g_object_ref (tfile));
486 arg = anjuta_token_file_load (tfile, NULL);
487 scanner = mkp_scanner_new (project);
488 parse = mkp_scanner_parse_token (scanner, arg, &err);
489 ok = parse != NULL;
490 mkp_scanner_free (scanner);
491 if (!ok)
493 if (err != NULL)
495 g_set_error_literal (error, IANJUTA_PROJECT_ERROR,
496 IANJUTA_PROJECT_ERROR_PROJECT_MALFORMED,
497 err->message);
498 g_error_free (err);
500 else
502 g_set_error (error, IANJUTA_PROJECT_ERROR,
503 IANJUTA_PROJECT_ERROR_PROJECT_MALFORMED,
504 "%s",
505 _("Unable to parse make file"));
508 return NULL;
511 /* Load target */
512 mkp_project_enumerate_targets (project, ANJUTA_PROJECT_NODE(parent));
514 return ANJUTA_PROJECT_NODE(parent);
517 /* Project access functions
518 *---------------------------------------------------------------------------*/
520 MkpProject *
521 mkp_project_get_root (MkpProject *project)
523 return MKP_PROJECT(project);
526 static const GList *
527 mkp_project_get_node_info (MkpProject *project, GError **error)
529 static GList *info_list = NULL;
531 if (info_list == NULL)
533 MkpNodeInfo *node;
535 for (node = MkpNodeInformation; node->base.type != 0; node++)
537 info_list = g_list_prepend (info_list, node);
540 info_list = g_list_reverse (info_list);
543 return info_list;
546 gboolean
547 mkp_project_get_token_location (MkpProject *project, AnjutaTokenFileLocation *location, AnjutaToken *token)
549 GHashTableIter iter;
550 gpointer key;
551 gpointer value;
553 g_hash_table_iter_init (&iter, project->files);
554 while (g_hash_table_iter_next (&iter, &key, &value))
556 if (anjuta_token_file_get_token_location ((AnjutaTokenFile *)value, location, token))
558 return TRUE;
562 return FALSE;
565 /* Variable access functions
566 *---------------------------------------------------------------------------*/
568 const gchar *
569 mkp_variable_get_name (MkpVariable *variable)
571 return variable->name;
574 gchar *
575 mkp_variable_evaluate (MkpVariable *variable, MkpProject *project)
577 return anjuta_token_evaluate (variable->value);
580 static MkpVariable*
581 mkp_variable_new (gchar *name, AnjutaTokenType assign, AnjutaToken *value)
583 MkpVariable *variable = NULL;
585 g_return_val_if_fail (name != NULL, NULL);
587 variable = g_slice_new0(MkpVariable);
588 variable->name = g_strdup (name);
589 variable->assign = assign;
590 variable->value = value;
592 return variable;
595 static void
596 mkp_variable_free (MkpVariable *variable)
598 g_free (variable->name);
600 g_slice_free (MkpVariable, variable);
603 /* Public functions
604 *---------------------------------------------------------------------------*/
606 void
607 mkp_project_update_variable (MkpProject *project, AnjutaToken *variable)
609 AnjutaToken *arg;
610 char *name = NULL;
611 MakeTokenType assign = 0;
612 AnjutaToken *value = NULL;
614 //fprintf(stdout, "update variable");
615 //anjuta_token_dump (variable);
617 arg = anjuta_token_first_item (variable);
618 name = g_strstrip (anjuta_token_evaluate (arg));
619 arg = anjuta_token_next_item (arg);
621 //g_message ("new variable %s", name);
622 switch (anjuta_token_get_type (arg))
624 case MK_TOKEN_EQUAL:
625 case MK_TOKEN_IMMEDIATE_EQUAL:
626 case MK_TOKEN_CONDITIONAL_EQUAL:
627 case MK_TOKEN_APPEND:
628 assign = anjuta_token_get_type (arg);
629 break;
630 default:
631 break;
634 value = anjuta_token_next_item (arg);
636 if (assign != 0)
638 MkpVariable *var;
640 //g_message ("assign %d name %s value %s\n", assign, name, anjuta_token_evaluate (value));
641 var = (MkpVariable *)g_hash_table_lookup (project->variables, name);
642 if (var != NULL)
644 var->assign = assign;
645 var->value = value;
647 else
649 var = mkp_variable_new (name, assign, value);
650 g_hash_table_insert (project->variables, var->name, var);
655 //g_message ("update variable %s", name);
657 if (name) g_free (name);
660 AnjutaToken*
661 mkp_project_get_variable_token (MkpProject *project, AnjutaToken *variable)
663 guint length;
664 const gchar *string;
665 gchar *name;
666 MkpVariable *var;
668 length = anjuta_token_get_length (variable);
669 string = anjuta_token_get_string (variable);
670 if ((length == 0) || (string == NULL)) return NULL;
672 if (string[1] == '(')
674 name = g_strndup (string + 2, length - 3);
676 else
678 name = g_strndup (string + 1, 1);
680 var = g_hash_table_lookup (project->variables, name);
681 g_free (name);
683 return var != NULL ? var->value : NULL;
686 static AnjutaProjectNode *
687 mkp_project_load_root (MkpProject *project, AnjutaProjectNode *node, GError **error)
689 GFile *root_file;
690 GFile *make_file = NULL;
691 const gchar **makefile;
692 MkpGroup *group;
694 /* Unload current project */
695 root_file = g_object_ref (anjuta_project_node_get_file (node));
696 mkp_project_unload (project);
697 project->root_file = root_file;
698 DEBUG_PRINT ("reload project %p root file %p", project, project->root_file);
700 /* shortcut hash tables */
701 project->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
702 project->files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, g_object_unref);
703 project->variables = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)mkp_variable_free);
705 /* Initialize rules data */
706 mkp_project_init_rules (project);
708 /* Initialize list styles */
709 project->space_list = anjuta_token_style_new (NULL, " ", "\n", NULL, 0);
710 project->arg_list = anjuta_token_style_new (NULL, ", ", ",\n ", ")", 0);
712 /* Find make file */
713 for (makefile = valid_makefiles; *makefile != NULL; makefile++)
715 if (file_type (root_file, *makefile) == G_FILE_TYPE_REGULAR)
717 make_file = g_file_get_child (root_file, *makefile);
718 break;
721 if (make_file == NULL)
723 g_set_error (error, IANJUTA_PROJECT_ERROR,
724 IANJUTA_PROJECT_ERROR_DOESNT_EXIST,
725 _("Project doesn't exist or invalid path"));
727 return NULL;
730 /* Create group */
731 group = MKP_GROUP(mkp_group_new (root_file));
732 anjuta_project_node_append (node, ANJUTA_PROJECT_NODE(group));
733 g_hash_table_insert (project->groups, g_file_get_uri (root_file), group);
736 /* Parse make file */
737 project_load_makefile (project, make_file, group, error);
738 g_object_unref (make_file);
740 monitors_setup (project);
742 return node;
745 AnjutaProjectNode*
746 mkp_project_load_node (MkpProject *project, AnjutaProjectNode *node, GError **error)
748 switch (anjuta_project_node_get_node_type (node))
750 case ANJUTA_PROJECT_ROOT:
751 project->loading++;
752 DEBUG_PRINT("*** Loading project: %p root file: %p\n", project, project->root_file);
753 return mkp_project_load_root (project, node, error);
754 case ANJUTA_PROJECT_GROUP:
755 project->loading++;
756 return project_load_makefile (project, node->file, MKP_GROUP(node), error);
757 default:
758 return NULL;
762 gboolean
763 mkp_project_reload (MkpProject *project, GError **error)
765 GFile *root_file;
766 GFile *make_file;
767 const gchar **makefile;
768 MkpGroup *group;
769 gboolean ok = TRUE;
771 /* Unload current project */
772 root_file = g_object_ref (project->root_file);
773 mkp_project_unload (project);
774 project->root_file = root_file;
775 DEBUG_PRINT ("reload project %p root file %p", project, project->root_file);
777 /* shortcut hash tables */
778 project->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
779 project->files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, g_object_unref);
780 project->variables = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)mkp_variable_free);
782 /* Initialize rules data */
783 mkp_project_init_rules (project);
785 /* Initialize list styles */
786 project->space_list = anjuta_token_style_new (NULL, " ", "\n", NULL, 0);
787 project->arg_list = anjuta_token_style_new (NULL, ", ", ",\n ", ")", 0);
789 /* Find make file */
790 for (makefile = valid_makefiles; *makefile != NULL; makefile++)
792 if (file_type (root_file, *makefile) == G_FILE_TYPE_REGULAR)
794 make_file = g_file_get_child (root_file, *makefile);
795 break;
798 if (make_file == NULL)
800 g_set_error (error, IANJUTA_PROJECT_ERROR,
801 IANJUTA_PROJECT_ERROR_DOESNT_EXIST,
802 _("Project doesn't exist or invalid path"));
804 return FALSE;
807 /* Create group */
808 group = MKP_GROUP(mkp_group_new (root_file));
809 anjuta_project_node_append (ANJUTA_PROJECT_NODE(project), ANJUTA_PROJECT_NODE(group));
810 g_hash_table_insert (project->groups, g_file_get_uri (root_file), group);
812 /* Parse make file */
813 project_load_makefile (project, make_file, group, error);
814 g_object_unref (make_file);
816 monitors_setup (project);
819 return ok;
822 gboolean
823 mkp_project_load (MkpProject *project,
824 GFile *directory,
825 GError **error)
827 g_return_val_if_fail (directory != NULL, FALSE);
829 return mkp_project_load_root (project, ANJUTA_PROJECT_NODE(project), error) != NULL;
832 void
833 mkp_project_unload (MkpProject *project)
835 AnjutaProjectNode *node;
837 monitors_remove (project);
839 /* project data */
840 if (project->root_file) g_object_unref (project->root_file);
841 project->root_file = NULL;
843 /* Remove all children */
844 while ((node = anjuta_project_node_first_child (ANJUTA_PROJECT_NODE (project))) != NULL)
846 g_object_unref (node);
849 /* shortcut hash tables */
850 if (project->groups) g_hash_table_destroy (project->groups);
851 project->groups = NULL;
852 if (project->files) g_hash_table_destroy (project->files);
853 project->files = NULL;
854 if (project->variables) g_hash_table_destroy (project->variables);
855 project->variables = NULL;
857 mkp_project_free_rules (project);
859 /* List styles */
860 if (project->space_list) anjuta_token_style_free (project->space_list);
861 if (project->arg_list) anjuta_token_style_free (project->arg_list);
864 gint
865 mkp_project_probe (GFile *directory,
866 GError **error)
868 gboolean probe;
869 gboolean dir;
871 dir = (file_type (directory, NULL) == G_FILE_TYPE_DIRECTORY);
872 if (!dir)
874 g_set_error (error, IANJUTA_PROJECT_ERROR,
875 IANJUTA_PROJECT_ERROR_DOESNT_EXIST,
876 _("Project doesn't exist or invalid path"));
879 probe = dir;
880 if (probe)
882 const gchar **makefile;
884 /* Look for makefiles */
885 probe = FALSE;
886 for (makefile = valid_makefiles; *makefile != NULL; makefile++)
888 if (file_type (directory, *makefile) == G_FILE_TYPE_REGULAR)
890 probe = TRUE;
891 break;
896 return probe ? IANJUTA_PROJECT_PROBE_MAKE_FILES : 0;
899 gboolean
900 mkp_project_save (MkpProject *project, GError **error)
902 gpointer key;
903 gpointer value;
904 GHashTableIter iter;
906 g_return_val_if_fail (project != NULL, FALSE);
908 g_hash_table_iter_init (&iter, project->files);
909 while (g_hash_table_iter_next (&iter, &key, &value))
911 GError *error = NULL;
912 AnjutaTokenFile *tfile = (AnjutaTokenFile *)value;
913 anjuta_token_file_save (tfile, &error);
916 return TRUE;
919 gboolean
920 mkp_project_move (MkpProject *project, const gchar *path)
922 GFile *old_root_file;
923 GFile *new_file;
924 gchar *relative;
925 GHashTableIter iter;
926 gpointer key;
927 gpointer value;
928 AnjutaTokenFile *tfile;
929 GHashTable* old_hash;
931 /* Change project root directory */
932 old_root_file = project->root_file;
933 project->root_file = g_file_new_for_path (path);
935 /* Change project root directory in groups */
936 old_hash = project->groups;
937 project->groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
938 g_hash_table_iter_init (&iter, old_hash);
939 while (g_hash_table_iter_next (&iter, &key, &value))
941 MkpGroup *group = (MkpGroup *)value;
943 relative = get_relative_path (old_root_file, group->base.file);
944 new_file = g_file_resolve_relative_path (project->root_file, relative);
945 g_free (relative);
946 g_object_unref (group->base.file);
947 group->base.file = new_file;
949 g_hash_table_insert (project->groups, g_file_get_uri (new_file), group);
951 g_hash_table_destroy (old_hash);
953 /* Change all files */
954 old_hash = project->files;
955 project->files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, g_object_unref, g_object_unref);
956 g_hash_table_iter_init (&iter, old_hash);
957 while (g_hash_table_iter_next (&iter, &key, (gpointer *)&tfile))
959 relative = get_relative_path (old_root_file, anjuta_token_file_get_file (tfile));
960 new_file = g_file_resolve_relative_path (project->root_file, relative);
961 g_free (relative);
962 anjuta_token_file_move (tfile, new_file);
964 g_hash_table_insert (project->files, new_file, tfile);
965 g_object_unref (key);
967 g_hash_table_steal_all (old_hash);
968 g_hash_table_destroy (old_hash);
970 g_object_unref (old_root_file);
972 return TRUE;
975 MkpProject *
976 mkp_project_new (GFile *file, GError **error)
978 MkpProject *project;
980 project = MKP_PROJECT (g_object_new (MKP_TYPE_PROJECT, NULL));
981 project->parent.file = (file != NULL) ? g_file_dup (file) : NULL;
982 project->parent.type = ANJUTA_PROJECT_ROOT;
984 return project;
987 static gboolean
988 mkp_project_is_loaded (MkpProject *project)
990 return project->loading == 0;
994 /* Implement IAnjutaProject
995 *---------------------------------------------------------------------------*/
997 static gboolean
998 iproject_load_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **err)
1000 if (node == NULL) node = ANJUTA_PROJECT_NODE (obj);
1003 if (mkp_project_load_node (MKP_PROJECT (obj), node, err) != NULL) {
1004 (MKP_PROJECT(obj))->loading--;
1005 g_signal_emit_by_name (MKP_PROJECT(obj), "node-loaded", node, err);
1007 return TRUE;
1010 return FALSE;
1013 static gboolean
1014 iproject_save_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **err)
1016 return mkp_project_save (MKP_PROJECT(node), err);
1019 static AnjutaProjectProperty *
1020 iproject_set_property (IAnjutaProject *obj, AnjutaProjectNode *node, const gchar *id, const gchar *name, const gchar *value, GError **error)
1022 g_set_error (error, IANJUTA_PROJECT_ERROR,
1023 IANJUTA_PROJECT_ERROR_NOT_SUPPORTED,
1024 _("Project doesn't allow to set properties"));
1026 return NULL;
1029 static gboolean
1030 iproject_remove_property (IAnjutaProject *obj, AnjutaProjectNode *node, const gchar *id, const gchar *name, GError **error)
1032 g_set_error (error, IANJUTA_PROJECT_ERROR,
1033 IANJUTA_PROJECT_ERROR_NOT_SUPPORTED,
1034 _("Project doesn't allow to set properties"));
1036 return FALSE;
1039 static const GList*
1040 iproject_get_node_info (IAnjutaProject *obj, GError **err)
1042 return mkp_project_get_node_info (MKP_PROJECT (obj), err);
1045 static AnjutaProjectNode *
1046 iproject_get_root (IAnjutaProject *obj, GError **err)
1048 return ANJUTA_PROJECT_NODE(mkp_project_get_root (MKP_PROJECT(obj)));
1051 static gboolean
1052 iproject_is_loaded (IAnjutaProject *obj, GError **err)
1054 return mkp_project_is_loaded (MKP_PROJECT (obj));
1057 static gboolean
1058 iproject_remove_node (IAnjutaProject *obj, AnjutaProjectNode *node, GError **err)
1060 anjuta_project_node_set_state (node, ANJUTA_PROJECT_REMOVED);
1061 g_signal_emit_by_name (obj, "node-modified", node, NULL);
1063 return TRUE;
1066 static AnjutaProjectNode *
1067 iproject_add_node_before (IAnjutaProject *obj, AnjutaProjectNode *parent, AnjutaProjectNode *sibling, AnjutaProjectNodeType type, GFile *file, const gchar *name, GError **error)
1069 AnjutaProjectNode *node;
1071 node = project_node_new (MKP_PROJECT (obj), parent, type, file, name, error);
1072 anjuta_project_node_set_state (node, ANJUTA_PROJECT_MODIFIED);
1073 anjuta_project_node_insert_before (parent, sibling, node);
1075 g_signal_emit_by_name (obj, "node-changed", node, NULL);
1077 return node;
1080 static AnjutaProjectNode *
1081 iproject_add_node_after (IAnjutaProject *obj, AnjutaProjectNode *parent, AnjutaProjectNode *sibling, AnjutaProjectNodeType type, GFile *file, const gchar *name, GError **error)
1083 AnjutaProjectNode *node;
1085 node = project_node_new (MKP_PROJECT (obj), parent, type, file, name, error);
1086 anjuta_project_node_set_state (node, ANJUTA_PROJECT_MODIFIED);
1087 anjuta_project_node_insert_after (parent, sibling, node);
1089 g_signal_emit_by_name (obj, "node-modified", node, NULL);
1091 return node;
1094 static void
1095 iproject_iface_init(IAnjutaProjectIface* iface)
1097 iface->load_node = iproject_load_node;
1098 iface->save_node = iproject_save_node;
1099 iface->set_property = iproject_set_property;
1100 iface->remove_property = iproject_remove_property;
1101 iface->get_node_info = iproject_get_node_info;
1102 iface->get_root = iproject_get_root;
1104 iface->is_loaded = iproject_is_loaded;
1106 iface->remove_node = iproject_remove_node;
1108 iface->add_node_before = iproject_add_node_before;
1109 iface->add_node_after = iproject_add_node_after;
1113 /* GObject implementation
1114 *---------------------------------------------------------------------------*/
1116 static void
1117 mkp_project_dispose (GObject *object)
1119 g_return_if_fail (MKP_IS_PROJECT (object));
1121 mkp_project_unload (MKP_PROJECT (object));
1123 G_OBJECT_CLASS (parent_class)->dispose (object);
1126 static void
1127 mkp_project_instance_init (MkpProject *project)
1129 g_return_if_fail (project != NULL);
1130 g_return_if_fail (MKP_IS_PROJECT (project));
1132 /* project data */
1133 project->root_file = NULL;
1134 project->suffix = NULL;
1135 project->rules = NULL;
1137 project->space_list = NULL;
1138 project->arg_list = NULL;
1141 static void
1142 mkp_project_class_init (MkpProjectClass *klass)
1144 GObjectClass *object_class;
1146 parent_class = g_type_class_peek_parent (klass);
1148 object_class = G_OBJECT_CLASS (klass);
1149 object_class->dispose = mkp_project_dispose;
1153 ANJUTA_TYPE_BEGIN(MkpProject, mkp_project, ANJUTA_TYPE_PROJECT_NODE);
1154 ANJUTA_TYPE_ADD_INTERFACE(iproject, IANJUTA_TYPE_PROJECT);
1155 ANJUTA_TYPE_END;