1 /* Dia -- a diagram creation/manipulation program -*- c -*-
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 * Basic Property types definition.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <string.h>
31
32 #include <gtk/gtk.h>
33 #define WIDGET GtkWidget
34 #include "properties.h"
35 #include "prop_basic.h"
36 #include "propinternals.h"
37
38 typedef struct {
39 const gchar *name;
40 const gchar *type;
41 } PropMoniker;
42
43 static guint
desc_hash_hash(const PropMoniker * desc)44 desc_hash_hash(const PropMoniker *desc)
45 {
46 guint name_hash = g_str_hash(desc->name);
47 guint type_hash = g_str_hash(desc->type);
48
49 return (name_hash ^ ((type_hash & 0xFFFF0000) >> 16) ^
50 ((type_hash & 0x0000FFFF) << 16) ^ (type_hash));
51 }
52
53 static guint
desc_hash_compare(const PropMoniker * a,const PropMoniker * b)54 desc_hash_compare(const PropMoniker *a, const PropMoniker *b)
55 {
56 return ((0 == strcmp(a->name,b->name)) && (0 == strcmp(a->type,b->type)));
57 }
58
59 Property *
make_new_prop(const char * name,PropertyType type,guint flags)60 make_new_prop(const char *name, PropertyType type, guint flags)
61 {
62 static GHashTable *hash = NULL;
63 PropMoniker *moniker = g_new0(PropMoniker, 1);
64 PropDescription *descr;
65
66 moniker->name = name;
67 moniker->type = type;
68
69 if (!hash) hash = g_hash_table_new((GHashFunc)desc_hash_hash,
70 (GCompareFunc)desc_hash_compare);
71
72 descr = g_hash_table_lookup(hash,moniker);
73 if (descr) {
74 g_free(moniker);
75 } else {
76 descr = g_new0(PropDescription,1);
77 descr->name = name;
78 descr->type = type;
79 descr->flags = flags;
80 descr->quark = g_quark_from_static_string(descr->name);
81 descr->type_quark = g_quark_from_static_string(descr->type);
82 descr->ops = prop_type_get_ops(type);
83 g_hash_table_insert(hash,moniker,descr);
84 /* we don't ever free anything allocated here. */
85 }
86 return descr->ops->new_prop(descr,pdtpp_synthetic);
87 }
88
89
90 /**************************************************************************/
91 /* The COMMON property type. prop->ops will always point to these ops, */
92 /* whose role is to update the prop->experience flag field and then call */
93 /* the original property routines. Exception is commonprop_new which does */
94 /* not exist. */
95 /**************************************************************************/
96
97 static Property *
commonprop_new(const PropDescription * pdesc,PropDescToPropPredicate reason)98 commonprop_new(const PropDescription *pdesc, PropDescToPropPredicate reason)
99 {
100 /* Will be called mostly when doing copies and empty copies.
101 Will NOT be called upon the creation of an original property. */
102 return pdesc->ops->new_prop(pdesc,reason);
103 }
104
105 static void
commonprop_free(Property * prop)106 commonprop_free(Property *prop)
107 {
108 prop->real_ops->free(prop);
109 }
110
111 static Property *
commonprop_copy(Property * src)112 commonprop_copy(Property *src)
113 {
114 Property *prop = src->real_ops->copy(src);
115 src->experience |= PXP_COPIED;
116 prop->experience |= PXP_COPY;
117 return prop;
118 }
119
120 static WIDGET *
commonprop_get_widget(Property * prop,PropDialog * dialog)121 commonprop_get_widget(Property *prop, PropDialog *dialog)
122 {
123 WIDGET *wid = prop->real_ops->get_widget(prop,dialog);
124 prop->experience |= PXP_GET_WIDGET;
125 return wid;
126 }
127
128 static void
commonprop_reset_widget(Property * prop,WIDGET * widget)129 commonprop_reset_widget(Property *prop, WIDGET *widget)
130 {
131 prop->real_ops->reset_widget(prop,widget);
132 prop->experience |= PXP_RESET_WIDGET;
133 }
134
135 static void
commonprop_set_from_widget(Property * prop,WIDGET * widget)136 commonprop_set_from_widget(Property *prop, WIDGET *widget)
137 {
138 prop->real_ops->set_from_widget(prop,widget);
139 prop->experience |= PXP_SET_FROM_WIDGET;
140 }
141
142 static void
commonprop_load(Property * prop,AttributeNode attr,DataNode data)143 commonprop_load(Property *prop, AttributeNode attr, DataNode data)
144 {
145 prop->real_ops->load(prop,attr,data);
146 prop->experience |= PXP_LOADED;
147 }
148
149 static void
commonprop_save(Property * prop,AttributeNode attr)150 commonprop_save(Property *prop, AttributeNode attr)
151 {
152 prop->real_ops->save(prop,attr);
153 prop->experience |= PXP_SAVED;
154 }
155
156 static gboolean
commonprop_can_merge(const PropDescription * pd1,const PropDescription * pd2)157 commonprop_can_merge(const PropDescription *pd1, const PropDescription *pd2)
158 {
159 return pd1->ops->can_merge(pd1,pd2);
160 }
161
162 static void
commonprop_get_from_offset(const Property * prop,void * base,guint offset,guint offset2)163 commonprop_get_from_offset(const Property *prop,
164 void *base, guint offset, guint offset2)
165 {
166 prop->real_ops->get_from_offset(prop,base,offset,offset2);
167 ((Property *)prop)->experience |= PXP_GFO;
168 }
169
170 static void
commonprop_set_from_offset(Property * prop,void * base,guint offset,guint offset2)171 commonprop_set_from_offset(Property *prop,
172 void *base, guint offset, guint offset2)
173 {
174 prop->real_ops->set_from_offset(prop,base,offset,offset2);
175 prop->experience |= PXP_SFO;
176 }
177
178 static const PropertyOps commonprop_ops = {
179 (PropertyType_New) commonprop_new,
180 (PropertyType_Free) commonprop_free,
181 (PropertyType_Copy) commonprop_copy,
182 (PropertyType_Load) commonprop_load,
183 (PropertyType_Save) commonprop_save,
184 (PropertyType_GetWidget) commonprop_get_widget,
185 (PropertyType_ResetWidget) commonprop_reset_widget,
186 (PropertyType_SetFromWidget) commonprop_set_from_widget,
187
188 (PropertyType_CanMerge) commonprop_can_merge,
189 (PropertyType_GetFromOffset) commonprop_get_from_offset,
190 (PropertyType_SetFromOffset) commonprop_set_from_offset
191 };
192
193
194
initialize_property(Property * prop,const PropDescription * pdesc,PropDescToPropPredicate reason)195 void initialize_property(Property *prop, const PropDescription *pdesc,
196 PropDescToPropPredicate reason)
197 {
198 prop->reason = reason;
199 prop->name = pdesc->name;
200 prop->name_quark = pdesc->quark;
201 if (!prop->name_quark) {
202 prop->name_quark = g_quark_from_string(prop->name);
203 g_error("%s: late quark construction for property %s",
204 G_STRFUNC,prop->name);
205 }
206 prop->type = pdesc->type;
207 prop->type_quark = pdesc->type_quark;
208 prop->ops = &commonprop_ops;
209 prop->real_ops = pdesc->ops;
210 /* if late quark construction, we'll have NULL here */
211 prop->descr = pdesc;
212 prop->reason = reason;
213 prop->extra_data = pdesc->extra_data;
214 /* prop->self remains 0 until we get a widget from this. */
215 prop->event_handler = pdesc->event_handler;
216
217 prop->experience = 0;
218 }
219
copy_init_property(Property * dest,const Property * src)220 void copy_init_property(Property *dest, const Property *src)
221 { /* XXX: inline this ? */
222 memcpy(dest,src,sizeof(*dest));
223 dest->experience = 0;
224 }
225
226
227 /**************************************************************************/
228 /* The NOOP property type. It is mostly useful to serve as both a library */
229 /* and a cut-n-paste template library; this is the reason why its member */
230 /* functions are not declared static (in the general case, they should). */
231 /**************************************************************************/
232
233 NoopProperty *
noopprop_new(const PropDescription * pdesc,PropDescToPropPredicate reason)234 noopprop_new(const PropDescription *pdesc, PropDescToPropPredicate reason)
235 {
236 NoopProperty *prop = g_new(NoopProperty,1);
237 initialize_property(&prop->common, pdesc, reason);
238 return prop;
239 }
240
241 void
noopprop_free(NoopProperty * prop)242 noopprop_free(NoopProperty *prop)
243 {
244 g_free(prop);
245 }
246
247 NoopProperty *
noopprop_copy(NoopProperty * src)248 noopprop_copy(NoopProperty *src)
249 {
250 NoopProperty *prop =
251 (NoopProperty *)src->common.ops->new_prop(src->common.descr,
252 src->common.reason);
253 copy_init_property(&prop->common,&src->common);
254 return prop; /* Yes, this one could be just be _new().
255 It's actually a placeholder. */
256 }
257
258 WIDGET *
noopprop_get_widget(NoopProperty * prop,PropDialog * dialog)259 noopprop_get_widget(NoopProperty *prop, PropDialog *dialog)
260 {
261 return NULL;
262 }
263
264 void
noopprop_reset_widget(NoopProperty * prop,WIDGET * widget)265 noopprop_reset_widget(NoopProperty *prop, WIDGET *widget)
266 {
267 }
268
269 void
noopprop_set_from_widget(NoopProperty * prop,WIDGET * widget)270 noopprop_set_from_widget(NoopProperty *prop, WIDGET *widget)
271 {
272 }
273
274 void
noopprop_load(NoopProperty * prop,AttributeNode attr,DataNode data)275 noopprop_load(NoopProperty *prop, AttributeNode attr, DataNode data)
276 {
277 }
278
279 void
noopprop_save(NoopProperty * prop,AttributeNode attr)280 noopprop_save(NoopProperty *prop, AttributeNode attr)
281 {
282 }
283
284 gboolean
noopprop_can_merge(const PropDescription * pd1,const PropDescription * pd2)285 noopprop_can_merge(const PropDescription *pd1, const PropDescription *pd2)
286 {
287 return TRUE;
288 }
289
290 gboolean
noopprop_cannot_merge(const PropDescription * pd1,const PropDescription * pd2)291 noopprop_cannot_merge(const PropDescription *pd1, const PropDescription *pd2)
292 {
293 return TRUE;
294 }
295
296 void
noopprop_get_from_offset(const NoopProperty * prop,void * base,guint offset,guint offset2)297 noopprop_get_from_offset(const NoopProperty *prop,
298 void *base, guint offset, guint offset2)
299 {
300 }
301
302 void
noopprop_set_from_offset(NoopProperty * prop,void * base,guint offset,guint offset2)303 noopprop_set_from_offset(NoopProperty *prop,
304 void *base, guint offset, guint offset2)
305 {
306 }
307
308 static const PropertyOps noopprop_ops = {
309 (PropertyType_New) noopprop_new,
310 (PropertyType_Free) noopprop_free,
311 (PropertyType_Copy) noopprop_copy,
312 (PropertyType_Load) noopprop_load,
313 (PropertyType_Save) noopprop_save,
314 (PropertyType_GetWidget) noopprop_get_widget,
315 (PropertyType_ResetWidget) noopprop_reset_widget,
316 (PropertyType_SetFromWidget) noopprop_set_from_widget,
317
318 (PropertyType_CanMerge) noopprop_can_merge, /* but we have noopprop_cannot_merge */
319 (PropertyType_GetFromOffset) noopprop_get_from_offset,
320 (PropertyType_SetFromOffset) noopprop_set_from_offset
321 };
322
323 /***************************************************************************/
324 /* The INVALID property type. It is mostly useful to serve a library. */
325 /* Each time its code is executed, a g_assert_not_reached() will be run... */
326 /***************************************************************************/
327
328 InvalidProperty *
invalidprop_new(const PropDescription * pdesc,PropDescToPropPredicate reason)329 invalidprop_new(const PropDescription *pdesc,
330 PropDescToPropPredicate reason)
331 {
332 g_assert_not_reached();
333 return NULL;
334 }
335
336 void
invalidprop_free(InvalidProperty * prop)337 invalidprop_free(InvalidProperty *prop)
338 {
339 g_assert_not_reached();
340 }
341
342 InvalidProperty *
invalidprop_copy(InvalidProperty * src)343 invalidprop_copy(InvalidProperty *src)
344 {
345 g_assert_not_reached();
346 return NULL;
347 }
348
349 WIDGET *
invalidprop_get_widget(InvalidProperty * prop,PropDialog * dialog)350 invalidprop_get_widget(InvalidProperty *prop, PropDialog *dialog)
351 {
352 g_assert_not_reached();
353 return NULL;
354 }
355
356 void
invalidprop_reset_widget(InvalidProperty * prop,WIDGET * widget)357 invalidprop_reset_widget(InvalidProperty *prop, WIDGET *widget)
358 {
359 g_assert_not_reached();
360 }
361
362 void
invalidprop_set_from_widget(InvalidProperty * prop,WIDGET * widget)363 invalidprop_set_from_widget(InvalidProperty *prop, WIDGET *widget)
364 {
365 g_assert_not_reached();
366 }
367
368 void
invalidprop_load(InvalidProperty * prop,AttributeNode attr,DataNode data)369 invalidprop_load(InvalidProperty *prop, AttributeNode attr, DataNode data)
370 {
371 g_assert_not_reached();
372 }
373
374 void
invalidprop_save(InvalidProperty * prop,AttributeNode attr)375 invalidprop_save(InvalidProperty *prop, AttributeNode attr)
376 {
377 g_assert_not_reached();
378 }
379
380 gboolean
invalidprop_can_merge(const PropDescription * pd1,const PropDescription * pd2)381 invalidprop_can_merge(const PropDescription *pd1, const PropDescription *pd2)
382 {
383 g_assert_not_reached();
384 return TRUE;
385 }
386
387 void
invalidprop_get_from_offset(const InvalidProperty * prop,void * base,guint offset,guint offset2)388 invalidprop_get_from_offset(const InvalidProperty *prop,
389 void *base, guint offset, guint offset2)
390 {
391 g_assert_not_reached();
392 }
393
394 void
invalidprop_set_from_offset(InvalidProperty * prop,void * base,guint offset,guint offset2)395 invalidprop_set_from_offset(InvalidProperty *prop,
396 void *base, guint offset, guint offset2)
397 {
398 g_assert_not_reached();
399 }
400
401 static const PropertyOps invalidprop_ops = {
402 (PropertyType_New) invalidprop_new,
403 (PropertyType_Free) invalidprop_free,
404 (PropertyType_Copy) invalidprop_copy,
405 (PropertyType_Load) invalidprop_load,
406 (PropertyType_Save) invalidprop_save,
407 (PropertyType_GetWidget) invalidprop_get_widget,
408 (PropertyType_ResetWidget) invalidprop_reset_widget,
409 (PropertyType_SetFromWidget) invalidprop_set_from_widget,
410
411 (PropertyType_CanMerge) invalidprop_can_merge,
412 (PropertyType_GetFromOffset) invalidprop_get_from_offset,
413 (PropertyType_SetFromOffset) invalidprop_set_from_offset
414 };
415
416 /***************************************************************************/
417 /* The UNIMPLEMENTED property type. It is mostly useful to serve a library */
418 /* for unfinished property types. */
419 /***************************************************************************/
420
421 UnimplementedProperty *
unimplementedprop_new(const PropDescription * pdesc,PropDescToPropPredicate reason)422 unimplementedprop_new(const PropDescription *pdesc,
423 PropDescToPropPredicate reason)
424 {
425 g_warning("%s: for property %s",G_STRFUNC,pdesc->name);
426 return NULL;
427 }
428
429 void
unimplementedprop_free(UnimplementedProperty * prop)430 unimplementedprop_free(UnimplementedProperty *prop)
431 {
432 g_warning("%s: for property %s",G_STRFUNC,prop->common.descr->name);
433 }
434
435 UnimplementedProperty *
unimplementedprop_copy(UnimplementedProperty * src)436 unimplementedprop_copy(UnimplementedProperty *src)
437 {
438 g_warning("%s: for property %s",G_STRFUNC,src->common.descr->name);
439 return NULL;
440 }
441
442 WIDGET *
unimplementedprop_get_widget(UnimplementedProperty * prop,PropDialog * dialog)443 unimplementedprop_get_widget(UnimplementedProperty *prop, PropDialog *dialog)
444 {
445 g_warning("%s: for property %s",G_STRFUNC,prop->common.descr->name);
446 return NULL;
447 }
448
449 void
unimplementedprop_reset_widget(UnimplementedProperty * prop,WIDGET * widget)450 unimplementedprop_reset_widget(UnimplementedProperty *prop, WIDGET *widget)
451 {
452 g_warning("%s: for property %s",G_STRFUNC,prop->common.descr->name);
453 }
454
455 void
unimplementedprop_set_from_widget(UnimplementedProperty * prop,WIDGET * widget)456 unimplementedprop_set_from_widget(UnimplementedProperty *prop, WIDGET *widget)
457 {
458 g_warning("%s: for property %s",G_STRFUNC,prop->common.descr->name);
459 }
460
461 void
unimplementedprop_load(UnimplementedProperty * prop,AttributeNode attr,DataNode data)462 unimplementedprop_load(UnimplementedProperty *prop,
463 AttributeNode attr, DataNode data)
464 {
465 g_warning("%s: for property %s",G_STRFUNC,prop->common.descr->name);
466 }
467
468 void
unimplementedprop_save(UnimplementedProperty * prop,AttributeNode attr)469 unimplementedprop_save(UnimplementedProperty *prop, AttributeNode attr)
470 {
471 g_warning("%s: for property %s",G_STRFUNC,prop->common.descr->name);
472 }
473
474 gboolean
unimplementedprop_can_merge(const PropDescription * pd1,const PropDescription * pd2)475 unimplementedprop_can_merge(const PropDescription *pd1, const PropDescription *pd2)
476 {
477 g_warning("%s: for property %s/%s",G_STRFUNC,pd1->name,pd2->name);
478 return FALSE;
479 }
480
481 void
unimplementedprop_get_from_offset(const UnimplementedProperty * prop,void * base,guint offset,guint offset2)482 unimplementedprop_get_from_offset(const UnimplementedProperty *prop,
483 void *base, guint offset, guint offset2)
484 {
485 g_warning("%s: for property %s",G_STRFUNC,prop->common.descr->name);
486 }
487
488 void
unimplementedprop_set_from_offset(UnimplementedProperty * prop,void * base,guint offset,guint offset2)489 unimplementedprop_set_from_offset(UnimplementedProperty *prop,
490 void *base, guint offset, guint offset2)
491 {
492 g_warning("%s: for property %s",G_STRFUNC,prop->common.descr->name);
493 }
494
495 static const PropertyOps unimplementedprop_ops = {
496 (PropertyType_New) unimplementedprop_new,
497 (PropertyType_Free) unimplementedprop_free,
498 (PropertyType_Copy) unimplementedprop_copy,
499 (PropertyType_Load) unimplementedprop_load,
500 (PropertyType_Save) unimplementedprop_save,
501 (PropertyType_GetWidget) unimplementedprop_get_widget,
502 (PropertyType_ResetWidget) unimplementedprop_reset_widget,
503 (PropertyType_SetFromWidget) unimplementedprop_set_from_widget,
504
505 (PropertyType_CanMerge) unimplementedprop_can_merge,
506 (PropertyType_GetFromOffset) unimplementedprop_get_from_offset,
507 (PropertyType_SetFromOffset) unimplementedprop_set_from_offset
508 };
509
510 /* ************************************************************** */
511
512 void
prop_basic_register(void)513 prop_basic_register(void)
514 {
515 prop_type_register(PROP_TYPE_NOOP,&noopprop_ops);
516 prop_type_register(PROP_TYPE_INVALID,&invalidprop_ops);
517 prop_type_register(PROP_TYPE_UNIMPLEMENTED,&unimplementedprop_ops);
518 }
519