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