1 /* Dia -- a diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *
4  * Property system for dia objects/shapes.
5  * Copyright (C) 2000 James Henstridge
6  * Copyright (C) 2001 Cyrille Chepelov
7  * Major restructuration done in August 2001 by C. Chepelov
8  *
9  * propdesc.c: This module handles operations on property descriptors and
10  * property descriptor lists.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #include <glib.h>
32 
33 #include "properties.h"
34 #include "propinternals.h"
35 
36 void
prop_desc_list_calculate_quarks(PropDescription * plist)37 prop_desc_list_calculate_quarks(PropDescription *plist)
38 {
39   guint i;
40 
41   for (i = 0; plist[i].name != NULL; i++) {
42     if (plist[i].quark == 0)
43       plist[i].quark = g_quark_from_static_string(plist[i].name);
44     if (plist[i].type_quark == 0)
45       plist[i].type_quark = g_quark_from_static_string(plist[i].type);
46     if (!plist[i].ops)
47       plist[i].ops = prop_type_get_ops(plist[i].type);
48   }
49 }
50 
51 const PropDescription *
prop_desc_list_find_prop(const PropDescription * plist,const gchar * name)52 prop_desc_list_find_prop(const PropDescription *plist, const gchar *name)
53 {
54   gint i = 0;
55   GQuark name_quark = g_quark_from_string(name);
56 
57   while (plist[i].name != NULL) {
58     if (plist[i].quark == name_quark)
59       return &plist[i];
60     i++;
61   }
62   return NULL;
63 }
64 
65 /* finds the real handler in case there are several levels of indirection */
66 PropEventHandler
prop_desc_find_real_handler(const PropDescription * pdesc)67 prop_desc_find_real_handler(const PropDescription *pdesc)
68 {
69   PropEventHandler ret = pdesc->event_handler;
70   const PropEventHandlerChain *chain = &pdesc->chain_handler;
71   if (!chain->handler) return ret;
72   while (chain) {
73     if (chain->handler) ret = chain->handler;
74     chain = chain->chain;
75   }
76   return ret;
77 }
78 
79 /* free a handler indirection list */
80 void
prop_desc_free_handler_chain(PropDescription * pdesc)81 prop_desc_free_handler_chain(PropDescription *pdesc)
82 {
83   if (pdesc) {
84     PropEventHandlerChain *chain = pdesc->chain_handler.chain;
85     while (chain) {
86       PropEventHandlerChain *next = chain->chain;
87       g_free(chain);
88       chain = next;
89     }
90     pdesc->chain_handler.chain = NULL;
91     pdesc->chain_handler.handler = NULL;
92   }
93 }
94 
95 /* free a handler indirection list in a list of descriptors */
96 void
prop_desc_list_free_handler_chain(PropDescription * pdesc)97 prop_desc_list_free_handler_chain(PropDescription *pdesc)
98 {
99   if (!pdesc) return;
100   while (pdesc->name) {
101     prop_desc_free_handler_chain(pdesc);
102     pdesc++;
103   }
104 }
105 
106 /* insert an event handler */
107 void
prop_desc_insert_handler(PropDescription * pdesc,PropEventHandler handler)108 prop_desc_insert_handler(PropDescription *pdesc,
109                          PropEventHandler handler)
110 {
111   if ((pdesc->chain_handler.handler) || (pdesc->chain_handler.chain)) {
112     /* not the first level. Push things forward. */
113     PropEventHandlerChain *pushed = g_new0(PropEventHandlerChain,1);
114     *pushed = pdesc->chain_handler;
115     pdesc->chain_handler.chain = pushed;
116   }
117   pdesc->chain_handler.handler = pdesc->event_handler;
118   pdesc->event_handler = handler;
119 }
120 
121 static const PropDescription null_prop_desc = { NULL };
122 
123 PropDescription *
prop_desc_lists_union(GList * plists)124 prop_desc_lists_union(GList *plists)
125 {
126   GArray *arr = g_array_new(TRUE, TRUE, sizeof(PropDescription));
127   PropDescription *ret;
128   GList *tmp;
129 
130   /* make sure the array is allocated */
131   /* FIXME: this doesn't seem to do anything useful if an error occurs. */
132   g_array_append_val(arr, null_prop_desc);
133   g_array_remove_index(arr, 0);
134 
135   /* Each element in the list is a GArray of PropDescription,
136      terminated by a NULL element. */
137   for (tmp = plists; tmp; tmp = tmp->next) {
138     PropDescription *plist = tmp->data;
139     int i;
140 
141     for (i = 0; plist[i].name != NULL; i++) {
142       int j;
143 
144       if (plist[i].flags & PROP_FLAG_DONT_MERGE)
145         continue; /* exclude anything that can't be merged */
146 
147       /* Check to see if this PropDescription is already included in
148 	 the union. */
149       for (j = 0; j < arr->len; j++)
150 	if (g_array_index(arr, PropDescription, j).quark == plist[i].quark)
151 	  break;
152 
153       /* Add to the union if it isn't already present. */
154       if (j == arr->len)
155 	g_array_append_val(arr, plist[i]);
156     }
157   }
158 
159   /* Get the actually array and free the GArray wrapper. */
160   ret = (PropDescription *)arr->data;
161   g_array_free(arr, FALSE);
162   return ret;
163 }
164 
165 gboolean
propdescs_can_be_merged(const PropDescription * p1,const PropDescription * p2)166 propdescs_can_be_merged(const PropDescription *p1,
167                         const PropDescription *p2) {
168   PropEventHandler peh1 = prop_desc_find_real_handler(p1);
169   PropEventHandler peh2 = prop_desc_find_real_handler(p2);
170 
171   if (p1->ops != p2->ops) return FALSE;
172   if ((p1->flags|p2->flags) & PROP_FLAG_DONT_MERGE) return FALSE;
173   if (peh1 != peh2) return FALSE;
174   if ((p1->ops->can_merge && !(p1->ops->can_merge(p1,p2))) ||
175       (p2->ops->can_merge && !(p2->ops->can_merge(p2,p1)))) return FALSE;
176 
177   return TRUE;
178 }
179 
180 PropDescription *
prop_desc_lists_intersection(GList * plists)181 prop_desc_lists_intersection(GList *plists)
182 {
183   GArray *arr = g_array_new(TRUE, TRUE, sizeof(PropDescription));
184   PropDescription *ret;
185   GList *tmp;
186   gint i;
187 
188   /* make sure the array is allocated */
189   g_array_append_val(arr, null_prop_desc);
190   g_array_remove_index(arr, 0);
191 
192   if (plists) {
193 
194     /* initialise the intersection with the first list. */
195     ret = plists->data;
196     for (i = 0; ret[i].name != NULL; i++)
197       g_array_append_val(arr, ret[i]);
198 
199     /* check each PropDescription list for intersection */
200     for (tmp = plists->next; tmp; tmp = tmp->next) {
201       ret = tmp->data;
202 
203       /* go through array in reverse so that removals don't stuff things up */
204       for (i = arr->len - 1; i >= 0; i--) {
205 	gint j;
206 	/* FIXME: we can avoid a copy here by making cand a pointer to
207 	   the data in the GArray. */
208         PropDescription cand = g_array_index(arr,PropDescription,i);
209         gboolean remove = TRUE;
210 	for (j = 0; ret[j].name != NULL; j++) {
211 	  if (cand.quark == ret[j].quark) {
212             remove = !(propdescs_can_be_merged(&ret[j],&cand));
213             break;
214           }
215         }
216 	if (remove) g_array_remove_index(arr, i);
217       }
218     }
219   }
220   ret = (PropDescription *)arr->data;
221   g_array_free(arr, FALSE);
222   return ret;
223 }
224 
225