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