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