1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta-project.c
4 * Copyright (C) Sébastien Granjoux 2009 <seb.sfo@free.fr>
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "anjuta-project.h"
21
22 #include "anjuta-debug.h"
23 #include "anjuta-marshal.h"
24 #include "anjuta-enum-types.h"
25
26 #include <string.h>
27
28 /**
29 * SECTION:anjuta-project
30 * @title: Anjuta project
31 * @short_description: Anjuta project
32 * @see_also:
33 * @stability: Unstable
34 * @include: libanjuta/anjuta-project.h
35 *
36 * A project in Anjuta is represented by a tree. There are six kinds of node.
37 *
38 * The root node is the parent of all other nodes, it can implement
39 * IAnjutaProject interface and represent the project itself but it is not
40 * mandatory.
41 *
42 * A module node represents a module in autotools project, it is a group of
43 * packages.
44 *
45 * A package node represents a package in autotools project, it is library.
46 *
47 * A group node is used to group several target or source, it can represent
48 * a directory by example.
49 *
50 * A target node represents an object file defined explicitely.
51 * There are different kinds of target: program, library...
52 * A target have as children all source needed to build it.
53 *
54 * A source node represents a source file. These are lead of the tree, a source
55 * node cannot have children.
56 *
57 * All these nodes are base objects. They have derived in each project backend
58 * to provide more specific information.
59 */
60
61 /* Node properties
62 *---------------------------------------------------------------------------*/
63
64 /* Implement Boxed type
65 *---------------------------------------------------------------------------*/
66
67 /**
68 * anjuta_project_property_new:
69 * @value: (transfer none): Value
70 * @name: (allow-none) (transfer none): Optional name used by map properties
71 * @user_data: (allow-none) (transfer full): Optional user data
72 *
73 * Returns: (transfer full):
74 */
75 AnjutaProjectProperty *
anjuta_project_property_new(const gchar * value,const gchar * name,gpointer user_data)76 anjuta_project_property_new (const gchar *value,
77 const gchar *name,
78 gpointer user_data)
79 {
80 AnjutaProjectProperty *prop = g_slice_new0(AnjutaProjectProperty);
81
82 prop->value = g_strdup (value);
83 prop->name = name != NULL ? g_strdup (name) : NULL;
84 prop->user_data = user_data;
85 prop->info = NULL;
86
87 return prop;
88 }
89
90 AnjutaProjectProperty *
anjuta_project_property_copy(AnjutaProjectProperty * prop)91 anjuta_project_property_copy (AnjutaProjectProperty *prop)
92 {
93 return anjuta_project_property_new (prop->value, prop->name, prop->user_data);
94 }
95
96 void
anjuta_project_property_free(AnjutaProjectProperty * prop)97 anjuta_project_property_free (AnjutaProjectProperty *prop)
98 {
99 g_free (prop->value);
100 g_free (prop->name);
101 g_slice_free (AnjutaProjectProperty, prop);
102 }
103
104 GType
anjuta_project_property_get_type(void)105 anjuta_project_property_get_type (void)
106 {
107 static GType type_id = 0;
108
109 if (!type_id)
110 type_id = g_boxed_type_register_static ("AnjutaProjectProperty",
111 (GBoxedCopyFunc) anjuta_project_property_copy,
112 (GBoxedFreeFunc) anjuta_project_property_free);
113
114 return type_id;
115 }
116
117 /* Node properties information
118 *---------------------------------------------------------------------------*/
119
120 /* Implement Boxed type
121 *---------------------------------------------------------------------------*/
122
123 /**
124 * anjuta_project_property_info_new:
125 * @id: (transfer none): Property identifier
126 * @name: (transfer none): Translatable property name
127 * @type: Property value type
128 * @flags: Property flags
129 * @description: (transfer none): Property description
130 * @default_value: (transfer full): Default property value
131 * @user_data: (allow-none) (transfer full): Optional user data
132 *
133 * Returns: (transfer full):
134 */
135 AnjutaProjectPropertyInfo *
anjuta_project_property_info_new(const gchar * id,const gchar * name,AnjutaProjectValueType type,AnjutaProjectPropertyFlags flags,const gchar * description,AnjutaProjectProperty * default_value,gpointer user_data)136 anjuta_project_property_info_new (const gchar *id,
137 const gchar *name,
138 AnjutaProjectValueType type,
139 AnjutaProjectPropertyFlags flags,
140 const gchar *description,
141 AnjutaProjectProperty *default_value,
142 gpointer user_data)
143 {
144 AnjutaProjectPropertyInfo *info = g_slice_new0(AnjutaProjectPropertyInfo);
145
146 info->id = g_strdup (id);
147 info->name = g_strdup (name);
148 info->type = type;
149 info->flags = flags;
150 info->description = g_strdup (description);
151 info->default_value = default_value;
152 info->user_data = user_data;
153
154 info->default_value->info = info;
155
156 return info;
157 }
158
159 AnjutaProjectPropertyInfo *
anjuta_project_property_info_copy(AnjutaProjectPropertyInfo * info)160 anjuta_project_property_info_copy (AnjutaProjectPropertyInfo *info)
161 {
162 return anjuta_project_property_info_new (info->id, info->name, info->type,
163 info->flags, info->description,
164 info->default_value, info->user_data);
165 }
166
167 void
anjuta_project_property_info_free(AnjutaProjectPropertyInfo * info)168 anjuta_project_property_info_free (AnjutaProjectPropertyInfo *info)
169 {
170 g_free (info->id);
171 g_free (info->name);
172 g_free (info->description);
173 anjuta_project_property_free (info->default_value);
174 g_slice_free (AnjutaProjectPropertyInfo, info);
175 }
176
177 GType
anjuta_project_property_info_get_type(void)178 anjuta_project_property_info_get_type (void)
179 {
180 static GType type_id = 0;
181
182 if (!type_id)
183 type_id = g_boxed_type_register_static ("AnjutaProjectPropertyInfo",
184 (GBoxedCopyFunc) anjuta_project_property_info_copy,
185 (GBoxedFreeFunc) anjuta_project_property_info_free);
186
187 return type_id;
188 }
189
190 /* Node
191 *---------------------------------------------------------------------------*/
192
193
194 /* Moving in tree functions
195 *---------------------------------------------------------------------------*/
196
197 /**
198 * anjuta_project_node_parent:
199 *
200 * Returns: (transfer none):
201 */
202 AnjutaProjectNode *
anjuta_project_node_parent(AnjutaProjectNode * node)203 anjuta_project_node_parent(AnjutaProjectNode *node)
204 {
205 g_return_val_if_fail (node != NULL, NULL);
206
207 return node->parent;
208 }
209
210 /**
211 * anjuta_project_node_root:
212 *
213 * Returns: (transfer none):
214 */
215 AnjutaProjectNode *
anjuta_project_node_root(AnjutaProjectNode * node)216 anjuta_project_node_root (AnjutaProjectNode *node)
217 {
218 g_return_val_if_fail (node != NULL, NULL);
219
220 while (node->parent != NULL)
221 {
222 node = node->parent;
223 }
224
225 return node;
226 }
227
228 /**
229 * anjuta_project_node_first_child:
230 *
231 * Returns: (transfer none):
232 */
233
234 AnjutaProjectNode *
anjuta_project_node_first_child(AnjutaProjectNode * node)235 anjuta_project_node_first_child(AnjutaProjectNode *node)
236 {
237 g_return_val_if_fail (node != NULL, NULL);
238
239 return node->children;
240 }
241
242 /**
243 * anjuta_project_node_last_child:
244 *
245 * Returns: (transfer none):
246 */
247
248 AnjutaProjectNode *
anjuta_project_node_last_child(AnjutaProjectNode * node)249 anjuta_project_node_last_child(AnjutaProjectNode *node)
250 {
251 g_return_val_if_fail (node != NULL, NULL);
252
253 node = node->children;
254 if (node)
255 while (node->next)
256 node = node->next;
257
258 return node;
259 }
260
261 /**
262 * anjuta_project_node_next_sibling:
263 *
264 * Returns: (transfer none):
265 */
266 AnjutaProjectNode *
anjuta_project_node_next_sibling(AnjutaProjectNode * node)267 anjuta_project_node_next_sibling (AnjutaProjectNode *node)
268 {
269 g_return_val_if_fail (node != NULL, NULL);
270
271 return node->next;
272 }
273
274 /**
275 * anjuta_project_node_prev_sibling:
276 *
277 * Returns: (transfer none):
278 */
279 AnjutaProjectNode *
anjuta_project_node_prev_sibling(AnjutaProjectNode * node)280 anjuta_project_node_prev_sibling (AnjutaProjectNode *node)
281 {
282 g_return_val_if_fail (node != NULL, NULL);
283
284 return node->prev;
285 }
286
287 /**
288 * anjuta_project_node_nth_child:
289 *
290 * Returns: (transfer none):
291 */
anjuta_project_node_nth_child(AnjutaProjectNode * node,guint n)292 AnjutaProjectNode *anjuta_project_node_nth_child (AnjutaProjectNode *node, guint n)
293 {
294 g_return_val_if_fail (node != NULL, NULL);
295
296 node = node->children;
297 if (node)
298 while ((n-- > 0) && node)
299 node = node->next;
300
301 return node;
302 }
303
304 static AnjutaProjectNode *
anjuta_project_node_post_order_traverse(AnjutaProjectNode * node,AnjutaProjectNodeTraverseFunc func,gpointer data)305 anjuta_project_node_post_order_traverse (AnjutaProjectNode *node, AnjutaProjectNodeTraverseFunc func, gpointer data)
306 {
307 AnjutaProjectNode *child;
308
309 child = node->children;
310 while (child != NULL)
311 {
312 AnjutaProjectNode *current;
313
314 current = child;
315 child = current->next;
316 current = anjuta_project_node_post_order_traverse (current, func, data);
317 if (current != NULL)
318 {
319 return current;
320 }
321 }
322
323 return func (node, data) ? node : NULL;
324 }
325
326 static AnjutaProjectNode *
anjuta_project_node_pre_order_traverse(AnjutaProjectNode * node,AnjutaProjectNodeTraverseFunc func,gpointer data)327 anjuta_project_node_pre_order_traverse (AnjutaProjectNode *node, AnjutaProjectNodeTraverseFunc func, gpointer data)
328 {
329 AnjutaProjectNode *child;
330
331 if (func (node, data))
332 {
333 return node;
334 }
335
336 child = node->children;
337 while (child != NULL)
338 {
339 AnjutaProjectNode *current;
340
341 current = child;
342 child = current->next;
343 current = anjuta_project_node_pre_order_traverse (current, func, data);
344 if (current != NULL)
345 {
346 return current;
347 }
348 }
349
350 return NULL;
351 }
352
353
354 /**
355 * anjuta_project_node_traverse:
356 * @func: (scope call):
357 *
358 * Returns: (transfer none):
359 */
360 AnjutaProjectNode *
anjuta_project_node_traverse(AnjutaProjectNode * node,GTraverseType order,AnjutaProjectNodeTraverseFunc func,gpointer data)361 anjuta_project_node_traverse (AnjutaProjectNode *node, GTraverseType order, AnjutaProjectNodeTraverseFunc func, gpointer data)
362 {
363 g_return_val_if_fail (node != NULL, NULL);
364 g_return_val_if_fail (func != NULL, NULL);
365 g_return_val_if_fail ((order != G_PRE_ORDER) || (order != G_POST_ORDER), NULL);
366
367 switch (order)
368 {
369 case G_PRE_ORDER:
370 return anjuta_project_node_pre_order_traverse (node, func, data);
371 case G_POST_ORDER:
372 return anjuta_project_node_post_order_traverse (node, func, data);
373 default:
374 return NULL;
375 }
376 }
377
378 /**
379 * anjuta_project_node_children_traverse:
380 * @func: (scope call):
381 *
382 * Returns: (transfer none):
383 */
384 AnjutaProjectNode *
anjuta_project_node_children_traverse(AnjutaProjectNode * node,AnjutaProjectNodeTraverseFunc func,gpointer data)385 anjuta_project_node_children_traverse (AnjutaProjectNode *node, AnjutaProjectNodeTraverseFunc func, gpointer data)
386 {
387 AnjutaProjectNode *child;
388
389 g_return_val_if_fail (node != NULL, NULL);
390
391 child = node->children;
392 while (child != NULL)
393 {
394 AnjutaProjectNode *current;
395
396 current = child;
397 child = current->next;
398 if (func (current, data))
399 {
400 return current;
401 }
402 }
403
404 return NULL;
405 }
406
407 static void
anjuta_project_node_post_order_foreach(AnjutaProjectNode * node,AnjutaProjectNodeForeachFunc func,gpointer data)408 anjuta_project_node_post_order_foreach (AnjutaProjectNode *node, AnjutaProjectNodeForeachFunc func, gpointer data)
409 {
410 AnjutaProjectNode *child;
411
412 child = node->children;
413 while (child != NULL)
414 {
415 AnjutaProjectNode *current;
416
417 current = child;
418 child = current->next;
419 anjuta_project_node_post_order_foreach (current, func, data);
420 }
421
422 func (node, data);
423 }
424
425 static void
anjuta_project_node_pre_order_foreach(AnjutaProjectNode * node,AnjutaProjectNodeForeachFunc func,gpointer data)426 anjuta_project_node_pre_order_foreach (AnjutaProjectNode *node, AnjutaProjectNodeForeachFunc func, gpointer data)
427 {
428 AnjutaProjectNode *child;
429
430 func (node, data);
431
432 child = node->children;
433 while (child != NULL)
434 {
435 AnjutaProjectNode *current;
436
437 current = child;
438 child = current->next;
439 anjuta_project_node_pre_order_foreach (current, func, data);
440 }
441 }
442
443 /**
444 * anjuta_project_node_foreach:
445 * @func: (scope call):
446 */
447 void
anjuta_project_node_foreach(AnjutaProjectNode * node,GTraverseType order,AnjutaProjectNodeForeachFunc func,gpointer data)448 anjuta_project_node_foreach (AnjutaProjectNode *node, GTraverseType order, AnjutaProjectNodeForeachFunc func, gpointer data)
449 {
450 g_return_if_fail (node != NULL);
451 g_return_if_fail (func != NULL);
452 g_return_if_fail ((order != G_PRE_ORDER) || (order != G_POST_ORDER));
453
454 switch (order)
455 {
456 case G_PRE_ORDER:
457 anjuta_project_node_pre_order_foreach (node, func, data);
458 break;
459 case G_POST_ORDER:
460 anjuta_project_node_post_order_foreach (node, func, data);
461 break;
462 default:
463 break;
464 }
465 }
466
467 /**
468 * anjuta_project_node_children_foreach:
469 * @func: (scope call):
470 */
471 void
anjuta_project_node_children_foreach(AnjutaProjectNode * node,AnjutaProjectNodeForeachFunc func,gpointer data)472 anjuta_project_node_children_foreach (AnjutaProjectNode *node, AnjutaProjectNodeForeachFunc func, gpointer data)
473 {
474 AnjutaProjectNode *child;
475
476 g_return_if_fail (node != NULL);
477
478 child = node->children;
479 while (child != NULL)
480 {
481 AnjutaProjectNode *current;
482
483 current = child;
484 child = current->next;
485 func (current, data);
486 }
487 }
488
489 /**
490 * anjuta_project_node_parent_type:
491 *
492 * Returns: (transfer none):
493 */
494 AnjutaProjectNode *
anjuta_project_node_parent_type(AnjutaProjectNode * node,AnjutaProjectNodeType type)495 anjuta_project_node_parent_type(AnjutaProjectNode *node, AnjutaProjectNodeType type)
496 {
497 do
498 {
499 node = anjuta_project_node_parent (node);
500 if (node == NULL) break;
501 }
502 while (anjuta_project_node_get_node_type (node) != type);
503
504 return node;
505 }
506
507
508
509 /* Debugging functions
510 *---------------------------------------------------------------------------*/
511
check_node(AnjutaProjectNode * node,gpointer data)512 static gboolean check_node (AnjutaProjectNode *node, gpointer data)
513 {
514 if (!ANJUTA_IS_PROJECT_NODE (node)) g_critical (" Node %p of %p is not a AnjutaProjectNode", node, data);
515 if (node->prev == NULL)
516 {
517 if ((node->parent != NULL) && (node->parent->children != node)) g_critical (" Node %p of %p has the wrong parent", node, data);
518 }
519 else
520 {
521 if (node->prev->next != node) g_critical (" Node %p of %p has the wrong predecessor", node, data);
522 if (node->prev->parent != node->parent) g_critical (" Node %p of %p has the wrong parent", node, data);
523 }
524 if (node->next != NULL)
525 {
526 if (node->next->prev != node) g_critical (" Node %p of %p has the wrong successor", node, data);
527 }
528 if (node->children != NULL)
529 {
530 if (node->children->parent != node) g_critical (" Node %p of %p has the wrong children", node, data);
531 }
532
533 return FALSE;
534 }
535
536 void
anjuta_project_node_check(AnjutaProjectNode * parent)537 anjuta_project_node_check (AnjutaProjectNode *parent)
538 {
539 AnjutaProjectNode *node;
540
541 g_message ("Check node %p", parent);
542 node = anjuta_project_node_traverse (parent, G_POST_ORDER, check_node, parent);
543 if (node == NULL) g_message (" Node %p is valid", parent);
544 }
545
546
547 static void
anjuta_project_node_show(AnjutaProjectNode * node,gint indent)548 anjuta_project_node_show (AnjutaProjectNode *node, gint indent)
549 {
550 g_message("%*s %p: %s", indent, "", node, node != NULL ? anjuta_project_node_get_name (node) : NULL);
551 }
552
553 static void
anjuta_project_node_dump_child(AnjutaProjectNode * parent,gint indent)554 anjuta_project_node_dump_child (AnjutaProjectNode *parent, gint indent)
555 {
556 AnjutaProjectNode *child;
557
558 anjuta_project_node_show (parent, indent);
559 indent += 4;
560
561 for (child = anjuta_project_node_first_child (parent); child != NULL; child = anjuta_project_node_next_sibling (child))
562 {
563 anjuta_project_node_dump_child (child, indent);
564 }
565 }
566
567 void
anjuta_project_node_dump(AnjutaProjectNode * parent)568 anjuta_project_node_dump (AnjutaProjectNode *parent)
569 {
570 anjuta_project_node_dump_child (parent, 0);
571 }
572
573
574
575 /* Adding node functions
576 *---------------------------------------------------------------------------*/
577
578 /**
579 * anjuta_project_node_insert_before:
580 * @parent:
581 * @sibling: (allow-none) (transfer none):
582 * @node: (transfer none):
583 *
584 * Returns: (transfer none):
585 */
586 AnjutaProjectNode *
anjuta_project_node_insert_before(AnjutaProjectNode * parent,AnjutaProjectNode * sibling,AnjutaProjectNode * node)587 anjuta_project_node_insert_before (AnjutaProjectNode *parent, AnjutaProjectNode *sibling, AnjutaProjectNode *node)
588 {
589 g_return_val_if_fail (node != NULL, NULL);
590 g_return_val_if_fail (parent != NULL, node);
591
592 /* FIXME: Try to avoid filling parent member to allow these checks
593 g_return_val_if_fail (node->parent == NULL)
594 if (sibling)
595 g_return_val_if_fail (sibling->parent == parent, node);*/
596
597 g_object_ref_sink (node);
598
599 node->parent = parent;
600 if (sibling)
601 {
602 if (sibling->prev)
603 {
604 node->prev = sibling->prev;
605 node->prev->next = node;
606 node->next = sibling;
607 sibling->prev = node;
608 }
609 else
610 {
611 node->parent->children = node;
612 node->next = sibling;
613 sibling->prev = node;
614 }
615 }
616 else
617 {
618 if (parent->children)
619 {
620 sibling = parent->children;
621 while (sibling->next)
622 sibling = sibling->next;
623 node->prev = sibling;
624 sibling->next = node;
625 }
626 else
627 {
628 node->parent->children = node;
629 }
630 }
631
632 return node;
633 }
634
635 /**
636 * anjuta_project_node_insert_after:
637 * @parent:
638 * @sibling: (allow-none) (transfer none):
639 * @node: (transfer none):
640 *
641 * Returns: (transfer none):
642 */
643 AnjutaProjectNode *
anjuta_project_node_insert_after(AnjutaProjectNode * parent,AnjutaProjectNode * sibling,AnjutaProjectNode * node)644 anjuta_project_node_insert_after (AnjutaProjectNode *parent, AnjutaProjectNode *sibling, AnjutaProjectNode *node)
645 {
646 g_return_val_if_fail (node != NULL, NULL);
647 g_return_val_if_fail (parent != NULL, node);
648
649 /* FIXME: Try to avoid filling parent member to allow these checks
650 g_return_val_if_fail (node->parent == NULL)
651 if (sibling)
652 g_return_val_if_fail (sibling->parent == parent, node);*/
653
654 g_object_ref_sink (node);
655
656 node->parent = parent;
657 if (sibling)
658 {
659 if (sibling->next)
660 {
661 sibling->next->prev = node;
662 }
663 node->next = sibling->next;
664 node->prev = sibling;
665 sibling->next = node;
666 }
667 else
668 {
669 if (parent->children)
670 {
671 node->next = parent->children;
672 parent->children->prev = node;
673 }
674 parent->children = node;
675 }
676
677 return node;
678 }
679
680 /**
681 * anjuta_project_node_remove:
682 * @node: (transfer none):
683 *
684 * Returns: (transfer full):
685 */
686 AnjutaProjectNode *
anjuta_project_node_remove(AnjutaProjectNode * node)687 anjuta_project_node_remove (AnjutaProjectNode *node)
688 {
689 g_return_val_if_fail (node != NULL, NULL);
690
691 if (node->prev)
692 node->prev->next = node->next;
693 else if (node->parent)
694 node->parent->children = node->next;
695 node->parent = NULL;
696 if (node->next)
697 {
698 node->next->prev = node->prev;
699 node->next = NULL;
700 }
701 node->prev = NULL;
702
703 return node;
704 }
705
706 /**
707 * anjuta_project_node_prepend:
708 *
709 * Returns: (transfer none):
710 */
711 AnjutaProjectNode *
anjuta_project_node_prepend(AnjutaProjectNode * parent,AnjutaProjectNode * node)712 anjuta_project_node_prepend (AnjutaProjectNode *parent, AnjutaProjectNode *node)
713 {
714 return anjuta_project_node_insert_before (parent, parent->children, node);
715 }
716
717 /**
718 * anjuta_project_node_append:
719 *
720 * Returns: (transfer none):
721 */
722 AnjutaProjectNode *
anjuta_project_node_append(AnjutaProjectNode * parent,AnjutaProjectNode * node)723 anjuta_project_node_append (AnjutaProjectNode *parent, AnjutaProjectNode *node)
724 {
725 return anjuta_project_node_insert_before (parent, NULL, node);
726 }
727
728 /* Access functions
729 *---------------------------------------------------------------------------*/
730
731 AnjutaProjectNodeType
anjuta_project_node_get_node_type(const AnjutaProjectNode * node)732 anjuta_project_node_get_node_type (const AnjutaProjectNode *node)
733 {
734 return node == NULL ? ANJUTA_PROJECT_UNKNOWN : (node->type & ANJUTA_PROJECT_TYPE_MASK);
735 }
736
737 AnjutaProjectNodeType
anjuta_project_node_get_full_type(const AnjutaProjectNode * node)738 anjuta_project_node_get_full_type (const AnjutaProjectNode *node)
739 {
740 return node == NULL ? ANJUTA_PROJECT_UNKNOWN : node->type;
741 }
742
743
744 AnjutaProjectNodeState
anjuta_project_node_get_state(const AnjutaProjectNode * node)745 anjuta_project_node_get_state (const AnjutaProjectNode *node)
746 {
747 return node == NULL ? ANJUTA_PROJECT_OK : (node->state);
748 }
749
750 const gchar *
anjuta_project_node_get_name(const AnjutaProjectNode * node)751 anjuta_project_node_get_name (const AnjutaProjectNode *node)
752 {
753 if ((node->name == NULL) && (node->file != NULL))
754 {
755 ((AnjutaProjectNode *)node)->name = g_file_get_basename (node->file);
756 }
757
758 return node->name;
759 }
760
761 /**
762 * anjuta_project_node_get_file:
763 *
764 * Returns: (transfer none):
765 */
766 GFile*
anjuta_project_node_get_file(const AnjutaProjectNode * node)767 anjuta_project_node_get_file (const AnjutaProjectNode *node)
768 {
769 switch (node->type & ANJUTA_PROJECT_TYPE_MASK)
770 {
771 case ANJUTA_PROJECT_OBJECT:
772 case ANJUTA_PROJECT_TARGET:
773 if ((node->name) && (node->parent != NULL) && (node->parent->file != NULL))
774 {
775 GFile *file = g_file_get_child (node->parent->file, node->name);
776
777 if ((node->file != NULL) && g_file_equal (node->file, file))
778 {
779 /* Keep the same file */
780 g_object_unref (file);
781 }
782 else
783 {
784 /* Parent has been updated, update file */
785 if (node->file != NULL) g_object_unref (node->file);
786 ((AnjutaProjectNode *)node)->file = file;
787 }
788 }
789 break;
790 default:
791 break;
792 }
793
794 return node->file;
795 }
796
797 /**
798 * anjuta_project_node_get_properties_info:
799 *
800 * Returns: (transfer none) (element-type Anjuta.ProjectPropertyInfo):
801 */
802 GList *
anjuta_project_node_get_properties_info(AnjutaProjectNode * node)803 anjuta_project_node_get_properties_info (AnjutaProjectNode *node)
804 {
805 return node->properties_info;
806 }
807
808 /**
809 * anjuta_project_node_get_properties:
810 *
811 * Returns: (transfer none) (element-type Anjuta.ProjectProperty):
812 */
813 GList *
anjuta_project_node_get_properties(AnjutaProjectNode * node)814 anjuta_project_node_get_properties (AnjutaProjectNode *node)
815 {
816 return node->properties;
817 }
818
819 static gint
find_property_info(gconstpointer item,gconstpointer data)820 find_property_info (gconstpointer item, gconstpointer data)
821 {
822 AnjutaProjectPropertyInfo *info = (AnjutaProjectPropertyInfo *)item;
823 const gchar *id = (const gchar *)data;
824
825 return strcmp (info->id, id);
826 }
827
828 /**
829 * anjuta_project_node_get_property_info:
830 * @node: (transfer none):
831 * @id: (transfer none): Property identifier
832 *
833 * Returns: (transfer none):
834 */
835 AnjutaProjectPropertyInfo *
anjuta_project_node_get_property_info(AnjutaProjectNode * node,const gchar * id)836 anjuta_project_node_get_property_info (AnjutaProjectNode *node,
837 const gchar *id)
838 {
839 GList *found;
840
841 /* Find property info */
842 found = g_list_find_custom (node->properties_info, id, find_property_info);
843
844 return found != NULL ? (AnjutaProjectPropertyInfo *)found->data : NULL;
845 }
846
847 static gint
find_property(gconstpointer item,gconstpointer data)848 find_property (gconstpointer item, gconstpointer data)
849 {
850 AnjutaProjectProperty *prop = (AnjutaProjectProperty *)item;
851 AnjutaProjectPropertyInfo *info = (AnjutaProjectPropertyInfo *)data;
852
853 return prop->info != info;
854 }
855
856 /**
857 * anjuta_project_node_get_property:
858 * @node: (transfer none):
859 * @id: (transfer none): Property identifier
860 *
861 * Returns: (transfer none):
862 */
863 AnjutaProjectProperty *
anjuta_project_node_get_property(AnjutaProjectNode * node,const gchar * id)864 anjuta_project_node_get_property (AnjutaProjectNode *node, const gchar *id)
865 {
866 AnjutaProjectPropertyInfo *info;
867 AnjutaProjectProperty *prop = NULL;
868
869 /* Find property info */
870 info = anjuta_project_node_get_property_info (node, id);
871 if (info != NULL)
872 {
873 GList *found;
874
875 /* Get default property */
876 prop = info->default_value;
877
878 /* Find custom property */
879 found = g_list_find_custom (node->properties, info, find_property);
880 if (found != NULL)
881 {
882 prop = (AnjutaProjectProperty *)found->data;
883 }
884 }
885
886 return prop;
887 }
888
889 /* If name is specified, look for a property with the same name, useful for
890 * map properties */
891 AnjutaProjectProperty *
anjuta_project_node_get_map_property(AnjutaProjectNode * node,const gchar * id,const gchar * name)892 anjuta_project_node_get_map_property (AnjutaProjectNode *node, const gchar *id, const gchar *name)
893 {
894 AnjutaProjectPropertyInfo *info;
895 AnjutaProjectProperty *prop = NULL;
896
897 /* Find property info */
898 info = anjuta_project_node_get_property_info (node, id);
899 if (info != NULL)
900 {
901 GList *found;
902
903 /* Get default property */
904 prop = info->default_value;
905
906 /* Find property */
907 found = node->properties;
908 do
909 {
910 found = g_list_find_custom (found, info, find_property);
911 if (found != NULL)
912 {
913 prop = (AnjutaProjectProperty *)found->data;
914 if ((info->type != ANJUTA_PROJECT_PROPERTY_MAP) || (g_strcmp0 (prop->name, name) == 0)) break;
915 prop = NULL;
916 found = g_list_next (found);
917 }
918 }
919 while (found != NULL);
920 }
921
922 return prop;
923 }
924
925 /* Set functions
926 *---------------------------------------------------------------------------*/
927
928 gboolean
anjuta_project_node_set_state(AnjutaProjectNode * node,AnjutaProjectNodeState state)929 anjuta_project_node_set_state (AnjutaProjectNode *node, AnjutaProjectNodeState state)
930 {
931 if (node == NULL) return FALSE;
932 node->state |= state;
933 return TRUE;
934 }
935
936 gboolean
anjuta_project_node_clear_state(AnjutaProjectNode * node,AnjutaProjectNodeState state)937 anjuta_project_node_clear_state (AnjutaProjectNode *node, AnjutaProjectNodeState state)
938 {
939 if (node == NULL) return FALSE;
940 node->state &= ~state;
941 return TRUE;
942 }
943
944 /**
945 * anjuta_project_node_insert_property_info:
946 * @node: (transfer none):
947 * @info: (transfer none):
948 *
949 * Returns: (transfer none):
950 */
951
952 AnjutaProjectPropertyInfo *
anjuta_project_node_insert_property_info(AnjutaProjectNode * node,AnjutaProjectPropertyInfo * info)953 anjuta_project_node_insert_property_info (AnjutaProjectNode *node,
954 AnjutaProjectPropertyInfo *info)
955 {
956 node->properties_info = g_list_append (node->properties_info, info);
957
958 return info;
959 }
960
961 /**
962 * anjuta_project_node_insert_property:
963 * @node: (transfer none):
964 * @info: (transfer none):
965 * @property: (transfer full):
966 *
967 * Returns: (transfer none):
968 */
969
970 AnjutaProjectProperty *
anjuta_project_node_insert_property(AnjutaProjectNode * node,AnjutaProjectPropertyInfo * info,AnjutaProjectProperty * property)971 anjuta_project_node_insert_property (AnjutaProjectNode *node, AnjutaProjectPropertyInfo *info, AnjutaProjectProperty *property)
972 {
973 /* Make sure the property is native */
974 property->info = info;
975
976 /* Add in properties list */
977 node->properties = g_list_append (node->properties, property);
978
979 return property;
980 }
981
982 AnjutaProjectProperty *
anjuta_project_node_remove_property(AnjutaProjectNode * node,AnjutaProjectProperty * prop)983 anjuta_project_node_remove_property (AnjutaProjectNode *node, AnjutaProjectProperty *prop)
984 {
985 /* Search the exact property, useful for list property */
986 if (prop != prop->info->default_value)
987 {
988 node->properties = g_list_remove (node->properties, prop);
989 }
990
991 return prop;
992 }
993
994
995 /* Get node from file functions
996 *---------------------------------------------------------------------------*/
997
998 static gboolean
anjuta_project_group_compare(AnjutaProjectNode * node,gpointer data)999 anjuta_project_group_compare (AnjutaProjectNode *node, gpointer data)
1000 {
1001 GFile *file = (GFile *)data;
1002
1003 if (((node->type & ANJUTA_PROJECT_TYPE_MASK) == ANJUTA_PROJECT_GROUP) && g_file_equal (node->file, file))
1004 {
1005 return TRUE;
1006 }
1007 else
1008 {
1009 return FALSE;
1010 }
1011 }
1012
1013 /**
1014 * anjuta_project_node_get_group_from_file:
1015 * @root: the root node
1016 * @directory: Directory to search in
1017 *
1018 * Returns: (transfer none):
1019 */
1020 AnjutaProjectNode *
anjuta_project_node_get_group_from_file(const AnjutaProjectNode * root,GFile * directory)1021 anjuta_project_node_get_group_from_file (const AnjutaProjectNode *root, GFile *directory)
1022 {
1023 AnjutaProjectNode *node;
1024
1025 node = anjuta_project_node_traverse ((AnjutaProjectNode *)root, G_PRE_ORDER, anjuta_project_group_compare, directory);
1026
1027 return node;
1028 }
1029
1030 static gboolean
anjuta_project_target_compare(AnjutaProjectNode * node,gpointer data)1031 anjuta_project_target_compare (AnjutaProjectNode *node, gpointer data)
1032 {
1033 const gchar *name = (gchar *)data;
1034
1035 if (((node->type & ANJUTA_PROJECT_TYPE_MASK) == ANJUTA_PROJECT_TARGET) && (strcmp (node->name, name) == 0))
1036 {
1037 return TRUE;
1038 }
1039 else
1040 {
1041 return FALSE;
1042 }
1043 }
1044
1045 AnjutaProjectNode *
anjuta_project_target_get_node_from_name(const AnjutaProjectNode * parent,const gchar * name)1046 anjuta_project_target_get_node_from_name (const AnjutaProjectNode *parent, const gchar *name)
1047 {
1048 AnjutaProjectNode *node;
1049
1050 node = anjuta_project_node_traverse ((AnjutaProjectNode *)parent, G_PRE_ORDER, anjuta_project_target_compare, (gpointer)name);
1051
1052 return node;
1053 }
1054
1055 static gboolean
anjuta_project_source_compare(AnjutaProjectNode * node,gpointer data)1056 anjuta_project_source_compare (AnjutaProjectNode *node, gpointer data)
1057 {
1058 GFile *file = (GFile *)data;
1059
1060 if (((node->type & ANJUTA_PROJECT_TYPE_MASK) == ANJUTA_PROJECT_SOURCE) && g_file_equal (node->file, file))
1061 {
1062 return TRUE;
1063 }
1064 else
1065 {
1066 return FALSE;
1067 }
1068 }
1069
1070 /**
1071 * anjuta_project_node_get_source_from_file:
1072 * @parent: the parent node
1073 * @file: The file to query the source for
1074 *
1075 * Returns: (transfer none):
1076 */
1077 AnjutaProjectNode *
anjuta_project_node_get_source_from_file(const AnjutaProjectNode * parent,GFile * file)1078 anjuta_project_node_get_source_from_file (const AnjutaProjectNode *parent, GFile *file)
1079 {
1080 AnjutaProjectNode *node;
1081
1082
1083 node = anjuta_project_node_traverse ((AnjutaProjectNode *)parent, G_PRE_ORDER, anjuta_project_source_compare, (gpointer)file);
1084
1085 return node;
1086 }
1087
1088
1089 /* Implement GObject
1090 *---------------------------------------------------------------------------*/
1091
1092 enum
1093 {
1094 UPDATED,
1095 LOADED,
1096 LAST_SIGNAL
1097 };
1098
1099 enum {
1100 PROP_NONE,
1101 PROP_NAME,
1102 PROP_FILE,
1103 PROP_STATE,
1104 PROP_TYPE
1105 };
1106
1107
1108 static unsigned int anjuta_project_node_signals[LAST_SIGNAL] = { 0 };
1109
1110 G_DEFINE_TYPE (AnjutaProjectNode, anjuta_project_node, G_TYPE_INITIALLY_UNOWNED);
1111
1112 static void
anjuta_project_node_init(AnjutaProjectNode * node)1113 anjuta_project_node_init (AnjutaProjectNode *node)
1114 {
1115 node->next = NULL;
1116 node->prev = NULL;
1117 node->parent = NULL;
1118 node->children = NULL;
1119
1120 node->type = 0;
1121 node->state = 0;
1122 node->properties = NULL;
1123 node->properties_info = NULL;
1124 node->file = NULL;
1125 node->name = NULL;
1126 }
1127
1128 static void
anjuta_project_node_dispose(GObject * object)1129 anjuta_project_node_dispose (GObject *object)
1130 {
1131 AnjutaProjectNode *node = ANJUTA_PROJECT_NODE(object);
1132
1133 anjuta_project_node_remove (node);
1134
1135 if (node->file != NULL) g_object_unref (node->file);
1136 node->file = NULL;
1137
1138 while (node->children != NULL)
1139 {
1140 AnjutaProjectNode *child;
1141
1142 child = anjuta_project_node_remove (node->children);
1143 g_object_unref (child);
1144 }
1145
1146 G_OBJECT_CLASS (anjuta_project_node_parent_class)->dispose (object);
1147 }
1148
1149 static void
anjuta_project_node_finalize(GObject * object)1150 anjuta_project_node_finalize (GObject *object)
1151 {
1152 AnjutaProjectNode *node = ANJUTA_PROJECT_NODE(object);
1153
1154 if (node->name != NULL) g_free (node->name);
1155
1156 g_list_free_full (node->properties, (GDestroyNotify) anjuta_project_property_free);
1157 node->properties = NULL;
1158
1159 g_list_free (node->properties_info);
1160 node->properties_info = NULL;
1161
1162 G_OBJECT_CLASS (anjuta_project_node_parent_class)->finalize (object);
1163 }
1164
1165 static void
anjuta_project_node_get_gobject_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1166 anjuta_project_node_get_gobject_property (GObject *object,
1167 guint prop_id,
1168 GValue *value,
1169 GParamSpec *pspec)
1170 {
1171 AnjutaProjectNode *node = ANJUTA_PROJECT_NODE(object);
1172 switch (prop_id) {
1173 case PROP_NAME:
1174 g_value_set_string (value, anjuta_project_node_get_name (node));
1175 break;
1176 case PROP_FILE:
1177 g_value_set_object (value, node->file);
1178 break;
1179 case PROP_STATE:
1180 g_value_set_flags (value, anjuta_project_node_get_state (node));
1181 break;
1182 case PROP_TYPE:
1183 g_value_set_flags (value, anjuta_project_node_get_node_type (node));
1184 break;
1185 default:
1186 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1187 }
1188 }
1189
1190 static void
anjuta_project_node_set_gobject_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1191 anjuta_project_node_set_gobject_property (GObject *object,
1192 guint prop_id,
1193 const GValue *value,
1194 GParamSpec *pspec)
1195 {
1196 AnjutaProjectNode *node = ANJUTA_PROJECT_NODE(object);
1197 switch (prop_id) {
1198 case PROP_NAME:
1199 if (node->name != NULL)
1200 g_free (node->name);
1201 node->name = g_value_dup_string (value);
1202 break;
1203 case PROP_FILE:
1204 if (node->file != NULL)
1205 g_object_unref (node->file);
1206 node->file = g_value_dup_object (value);
1207 break;
1208 case PROP_STATE:
1209 anjuta_project_node_set_state (node, g_value_get_flags (value));
1210 break;
1211 case PROP_TYPE:
1212 node->type = g_value_get_flags (value);
1213 break;
1214 default:
1215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1216 }
1217 }
1218
1219 static void
anjuta_project_node_class_init(AnjutaProjectNodeClass * klass)1220 anjuta_project_node_class_init (AnjutaProjectNodeClass *klass)
1221 {
1222 GObjectClass* object_class = G_OBJECT_CLASS (klass);
1223 GParamSpec* pspec;
1224
1225 object_class->finalize = anjuta_project_node_finalize;
1226 object_class->dispose = anjuta_project_node_dispose;
1227 object_class->get_property = anjuta_project_node_get_gobject_property;
1228 object_class->set_property = anjuta_project_node_set_gobject_property;
1229
1230 /*Change both signal to use marshal_VOID__POINTER_BOXED
1231 adding a AnjutaProjectNode pointer corresponding to the
1232 loaded node => done
1233 Such marshal doesn't exist as glib marshal, so look in the
1234 symbol db plugin how to add new marshal => done
1235 ToDo :
1236 This new argument can be used in the plugin object in
1237 order to add corresponding shortcut when the project
1238 is loaded and a new node is loaded.
1239 The plugin should probably get the GFile from the
1240 AnjutaProjectNode object and then use a function
1241 in project-view.c to create the corresponding shortcut*/
1242
1243 anjuta_project_node_signals[UPDATED] = g_signal_new ("updated",
1244 G_OBJECT_CLASS_TYPE (object_class),
1245 G_SIGNAL_RUN_LAST,
1246 G_STRUCT_OFFSET (AnjutaProjectNodeClass, updated),
1247 NULL, NULL,
1248 anjuta_cclosure_marshal_VOID__STRING_BOXED,
1249 G_TYPE_NONE,
1250 2,
1251 G_TYPE_POINTER,
1252 G_TYPE_ERROR);
1253
1254 anjuta_project_node_signals[LOADED] = g_signal_new ("loaded",
1255 G_OBJECT_CLASS_TYPE (object_class),
1256 G_SIGNAL_RUN_LAST,
1257 G_STRUCT_OFFSET (AnjutaProjectNodeClass, loaded),
1258 NULL, NULL,
1259 anjuta_cclosure_marshal_VOID__STRING_BOXED,
1260 G_TYPE_NONE,
1261 2,
1262 G_TYPE_POINTER,
1263 G_TYPE_ERROR);
1264
1265 pspec = g_param_spec_flags ("type",
1266 "Type",
1267 "Node type",
1268 ANJUTA_TYPE_PROJECT_NODE_TYPE,
1269 0,
1270 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1271 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TYPE,
1272 pspec);
1273
1274 pspec = g_param_spec_flags ("state",
1275 "State",
1276 "Node state",
1277 ANJUTA_TYPE_PROJECT_NODE_STATE,
1278 0,
1279 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1280 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_STATE,
1281 pspec);
1282
1283 pspec = g_param_spec_string ("name",
1284 "Name",
1285 "Node name",
1286 "",
1287 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1288 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_NAME,
1289 pspec);
1290
1291 pspec = g_param_spec_object ("file",
1292 "File",
1293 "The GFile for the node",
1294 G_TYPE_FILE,
1295 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1296 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILE,
1297 pspec);
1298
1299 }
1300
1301
1302
1303 /* Node information
1304 *---------------------------------------------------------------------------*/
1305
1306 /* Public functions
1307 *---------------------------------------------------------------------------*/
1308
1309 const gchar *
anjuta_project_node_info_name(const AnjutaProjectNodeInfo * info)1310 anjuta_project_node_info_name (const AnjutaProjectNodeInfo *info)
1311 {
1312 return info->name;
1313 }
1314
1315 const gchar *
anjuta_project_node_info_mime(const AnjutaProjectNodeInfo * info)1316 anjuta_project_node_info_mime (const AnjutaProjectNodeInfo *info)
1317 {
1318 return info->mime_type;
1319 }
1320
1321 AnjutaProjectNodeType
anjuta_project_node_info_type(const AnjutaProjectNodeInfo * info)1322 anjuta_project_node_info_type (const AnjutaProjectNodeInfo *info)
1323 {
1324 return info->type;
1325 }
1326
1327 const gchar *
anjuta_project_node_info_property_help_id(const AnjutaProjectNodeInfo * info)1328 anjuta_project_node_info_property_help_id (const AnjutaProjectNodeInfo *info)
1329 {
1330 return info->property_help_id;
1331 }
1332
1333 /**
1334 * anjuta_project_node_info_new:
1335 * @name: (transfer none):
1336 * @mime_type: (transfer none):
1337 *
1338 * Returns: (transfer full):
1339 */
1340 AnjutaProjectNodeInfo *
anjuta_project_node_info_new(AnjutaProjectNodeType type,const gchar * name,const gchar * mime_type)1341 anjuta_project_node_info_new (AnjutaProjectNodeType type,
1342 const gchar *name,
1343 const gchar *mime_type)
1344 {
1345 AnjutaProjectNodeInfo *info = g_slice_new0 (AnjutaProjectNodeInfo);
1346 info->type = type;
1347 info->name = g_strdup (name);
1348 info->mime_type = g_strdup (mime_type);
1349
1350 return info;
1351 }
1352
1353 AnjutaProjectNodeInfo *
anjuta_project_node_info_copy(AnjutaProjectNodeInfo * info)1354 anjuta_project_node_info_copy (AnjutaProjectNodeInfo *info)
1355 {
1356 return anjuta_project_node_info_new (info->type, info->name, info->mime_type);
1357 }
1358
anjuta_project_node_info_free(AnjutaProjectNodeInfo * info)1359 void anjuta_project_node_info_free (AnjutaProjectNodeInfo *info)
1360 {
1361 g_slice_free (AnjutaProjectNodeInfo, info);
1362 }
1363
1364 /* Implement Boxed type
1365 *---------------------------------------------------------------------------*/
1366
1367 GType
anjuta_project_node_info_get_type()1368 anjuta_project_node_info_get_type ()
1369 {
1370 static GType type_id = 0;
1371
1372 if (!type_id)
1373 type_id = g_boxed_type_register_static ("AnjutaProjectNodeInfo",
1374 (GBoxedCopyFunc) anjuta_project_node_info_copy,
1375 (GBoxedFreeFunc) anjuta_project_node_info_free);
1376
1377 return type_id;
1378 }
1379