1 // -*- C++ -*-
2
3 /*
4 * CDXML files loader plugin
5 * cdxml.cc
6 *
7 * Copyright (C) 2007-2016 Jean Bréfort <jean.brefort@normalesup.org>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 * USA
23 */
24
25 #include "config.h"
26 #include <gcp/document.h>
27 #include <gcp/theme.h>
28 #include <gcp/view.h>
29 #include <gcp/widgetdata.h>
30 #include <gcu/application.h>
31 #include <gcu/atom.h>
32 #include <gcu/bond.h>
33 #include <gcu/element.h>
34 #include <gcu/formula.h>
35 #include <gcu/loader.h>
36 #include <gcu/molecule.h>
37 #include <gcu/objprops.h>
38 #include <gcu/xml-utils.h>
39
40 #include <goffice/app/module-plugin-defs.h>
41 #include <gsf/gsf-libxml.h>
42 #include <glib/gi18n-lib.h>
43 #include <libintl.h>
44 #include <cstdio>
45 #include <list>
46 #include <map>
47 #include <stack>
48 #include <string>
49 #include <vector>
50 #include <cstring>
51
52 #include <iostream>
53 #include <sstream>
54
55 using namespace std;
56 using namespace gcu;
57
58 static map<string, unsigned> KnownProps;
59
60 typedef struct {
61 unsigned index;
62 string encoding;
63 string name;
64 } CDXMLFont;
65
66 typedef struct {
67 xmlDocPtr xml;
68 xmlNodePtr node;
69 xmlNodePtr parent;
70 GOIOContext *s;
71 bool italic;
72 bool bold;
73 bool underline;
74 unsigned font;
75 double size;
76 int position;
77 unsigned color;
78 } WriteTextState;
79
80 class CDXMLLoader: public gcu::Loader
81 {
82 public:
83 CDXMLLoader ();
84 virtual ~CDXMLLoader ();
85
86 ContentType Read (Document *doc, GsfInput *in, char const *mime_type, GOIOContext *io);
87 bool Write (Object const *obj, GsfOutput *out, char const *mime_type, GOIOContext *io, ContentType type);
88
89 private:
90 bool WriteObject (xmlDocPtr xml, xmlNodePtr node, Object const *object, GOIOContext *io);
91 static void AddIntProperty (xmlNodePtr node, char const *id, int value);
92 static void AddFloatProperty (xmlNodePtr node, char const *id, double value);
93 static void AddStringProperty (xmlNodePtr node, char const *id, string const &value);
94 static bool WriteArrow (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
95 static bool WriteAtom (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
96 static bool WriteFragment (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
97 static bool WriteBond (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
98 static bool WriteMesomery(CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
99 static bool WriteMolecule (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
100 static bool WriteReaction(CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
101 static bool WriteReactionStep(CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
102 static bool WriteRetrosynthesis(CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
103 static bool WriteText(CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s);
104 bool WriteNode (xmlNodePtr node, WriteTextState *state);
105 bool WriteScheme (xmlDocPtr xml, xmlNodePtr parent, Object const *obj, std::string const &arrow_type, GOIOContext *s);
106
107 private:
108 map <string, bool (*) (CDXMLLoader *, xmlDocPtr, xmlNodePtr, Object const *, GOIOContext *)> m_WriteCallbacks;
109 map <unsigned, GOColor> m_Colors;
110 map <unsigned, CDXMLFont> m_Fonts;
111 map <string, unsigned> m_SavedIds;
112 int m_MaxId;
113 unsigned m_Z;
114 int m_LabelFont, m_Font;
115 unsigned m_LabelFontFace, m_LabelFontColor;
116 double m_FontSize, m_LabelFontSize, m_Scale, m_Zoom, m_CHeight;
117 bool m_WriteScheme;
118 };
119
CDXMLLoader()120 CDXMLLoader::CDXMLLoader ()
121 {
122 AddMimeType ("chemical/x-cdxml");
123 KnownProps["Comment"] = GCU_PROP_DOC_COMMENT;
124 KnownProps["CreationDate"] = GCU_PROP_DOC_CREATION_TIME;
125 KnownProps["CreationUserName"] = GCU_PROP_DOC_CREATOR;
126 KnownProps["ModificationDate"] = GCU_PROP_DOC_MODIFICATION_TIME;
127 KnownProps["Name"] = GCU_PROP_DOC_TITLE;
128 KnownProps["p"] = GCU_PROP_POS2D;
129 KnownProps["Element"] = GCU_PROP_ATOM_Z;
130 KnownProps["Charge"] = GCU_PROP_ATOM_CHARGE;
131 KnownProps["id"] = GCU_PROP_ID;
132 KnownProps["B"] = GCU_PROP_BOND_BEGIN;
133 KnownProps["Display"] = GCU_PROP_BOND_TYPE;
134 KnownProps["E"] = GCU_PROP_BOND_END;
135 KnownProps["Order"] = GCU_PROP_BOND_ORDER;
136 KnownProps["DoublePosition"] = GCU_PROP_BOND_DOUBLE_POSITION;
137 KnownProps["LabelJustification"] =GCU_PROP_TEXT_JUSTIFICATION;
138 KnownProps["CaptionJustification"] =GCU_PROP_TEXT_ALIGNMENT;
139 KnownProps["LabelAlignment"] = GCU_PROP_TEXT_ALIGNMENT;
140 KnownProps["Justification"] =GCU_PROP_TEXT_JUSTIFICATION;
141 // Add write callbacks
142 m_WriteCallbacks["atom"] = WriteAtom;
143 m_WriteCallbacks["fragment"] = WriteFragment;
144 m_WriteCallbacks["bond"] = WriteBond;
145 m_WriteCallbacks["molecule"] = WriteMolecule;
146 m_WriteCallbacks["text"] = WriteText;
147 m_WriteCallbacks["reaction"] = WriteReaction;
148 m_WriteCallbacks["reaction-arrow"] = WriteArrow;
149 m_WriteCallbacks["mesomery"] = WriteMesomery;
150 m_WriteCallbacks["mesomery-arrow"] = WriteArrow;
151 m_WriteCallbacks["retrosynthesis-arrow"] = WriteArrow;
152 m_WriteCallbacks["retrosynthesis"] = WriteRetrosynthesis;
153 m_WriteScheme = true;
154 }
155
~CDXMLLoader()156 CDXMLLoader::~CDXMLLoader ()
157 {
158 RemoveMimeType ("chemical/x-cdxml");
159 }
160
161 typedef struct {
162 Object *obj;
163 unsigned property;
164 string value;
165 } CDXMLProps;
166
167 typedef struct {
168 std::list < unsigned > Arrows, Reagents, Products, ObjectsAbove, ObjectsBelow;
169 } StepData;
170
171 typedef struct {
172 unsigned Id;
173 std::list < StepData > Steps;
174 } SchemeData;
175
176 typedef struct {
177 Document *doc;
178 Application *app;
179 gcp::Theme *theme;
180 ostringstream themedesc;
181 GOIOContext *context;
182 stack<Object*> cur;
183 list<CDXMLProps> failed;
184 map<unsigned, CDXMLFont> fonts;
185 std::map <unsigned, std::string> loaded_ids;
186 vector<string> colors;
187 string markup;
188 unsigned attributes;
189 unsigned font;
190 unsigned color;
191 string size;
192 int line_height;
193 unsigned captionFont, labelFont, textAlign;
194 double CHeight, padding;
195 SchemeData scheme;
196 std::list < SchemeData > schemes;
197 } CDXMLReadState;
198
199 static void
cdxml_simple_end(GsfXMLIn * xin,G_GNUC_UNUSED GsfXMLBlob * blob)200 cdxml_simple_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
201 {
202 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
203 state->cur.top ()->Lock (false);
204 state->cur.top ()->OnLoaded ();
205 state->cur.pop ();
206 }
207
208 static void
cdxml_doc(GsfXMLIn * xin,xmlChar const ** attrs)209 cdxml_doc (GsfXMLIn *xin, xmlChar const **attrs)
210 {
211 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
212 map<string, unsigned>::iterator it;
213 double bond_dist_ratio = -1., bond_length = 66.24, x;
214 state->themedesc << "<?xml version=\"1.0\"?>" << std::endl << "<theme name=\"ChemDraw\"";
215 state->doc->SetProperty (GCU_PROP_DOC_CREATOR, ""); // Chemdraw does not use it for now.
216 if (attrs)
217 while (*attrs) {
218 std::string key = reinterpret_cast < char const * > (*attrs);
219 if ((it = KnownProps.find (key)) != KnownProps.end ())
220 state->doc->SetProperty ((*it).second, reinterpret_cast < char const * > (*(attrs + 1)));
221 else if (key == "BondLength") {
222 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
223 input >> bond_length;
224 bond_length *= 8.;
225 state->themedesc << " bond-length=\"" << bond_length << "\" zoom-factor=\"6\"";
226 state->doc->SetScale (8.);
227 } else if (key == "BondSpacing") {
228 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
229 input >> bond_dist_ratio;
230 bond_dist_ratio /= 100.;
231 } else if (key == "LineWidth") {
232 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
233 input >> x;
234 x *= 4. / 3.;
235 state->themedesc << " bond-width=\"" << x << "\" arrow-width=\"" << x << "\" hash-width=\"" << x << "\"";
236 } else if (key == "BoldWidth") {
237 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
238 input >> x;
239 state->themedesc << " stereo-bond-width=\"" << x * 4. / 3. << "\"";
240 } else if (key == "HashSpacing") {
241 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
242 input >> x;
243 state->themedesc << " hash-dist=\"" << x * 4. / 3. << "\"";
244 } else if (key == "ChainAngle")
245 state->themedesc << " bond-angle=\"" << reinterpret_cast < char const * > (*(attrs + 1)) << "\"";
246 else if (key == "MarginWidth") {
247 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
248 input >> x;
249 x *= 4. / 3.;
250 state->padding = x * 2.;
251 state->themedesc << " padding=\"" << x << "\" arrow-padding=\"" << x << "\" object-padding=\"" << x << "\" sign-padding=\"" << x << "\"";
252 } else if (key == "CaptionFont") {
253 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
254 input >> state->captionFont;
255 } else if (key == "CaptionSize") {
256 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
257 input >> x;
258 int size = x * PANGO_SCALE;
259 state->themedesc << " text-font-size=\"" << size << "\"";
260 } else if (key == "CaptionFace") {
261 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
262 int face;
263 input >> face;
264 switch (face & 3) { // we do not support anything else
265 default:
266 case 0:
267 state->themedesc << " text-font-style=\"normal\" text-font-weight=\"normal\"";
268 break;
269 case 1:
270 state->themedesc << " text-font-style=\"normal\" text-font-weight=\"bold\"";
271 break;
272 case 2:
273 state->themedesc << " text-font-style=\"italic\" text-font-weight=\"normal\"";
274 break;
275 case 3:
276 state->themedesc << " text-font-style=\"italic\" text-font-weight=\"bold\"";
277 break;
278 }
279 } else if (key == "LabelFont") {
280 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
281 input >> state->labelFont;
282 } else if (key == "LabelSize") {
283 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
284 input >> x;
285 int size = x * PANGO_SCALE;
286 state->themedesc << " font-size=\"" << size << "\"";
287 } else if (key == "LabelFace") {
288 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
289 int face;
290 input >> face;
291 switch (face & 3) { // we do not support anything else
292 default:
293 case 0:
294 state->themedesc << " font-style=\"normal\" font-weight=\"normal\"";
295 break;
296 case 1:
297 state->themedesc << " font-style=\"normal\" font-weight=\"bold\"";
298 break;
299 case 2:
300 state->themedesc << " font-style=\"italic\" font-weight=\"normal\"";
301 break;
302 case 3:
303 state->themedesc << " font-style=\"italic\" font-weight=\"bold\"";
304 break;
305 }
306 } else if (key == "CaptionJustification") {
307 std::istringstream input (reinterpret_cast < char const * > (*(attrs + 1)));
308 input >> state->textAlign;
309 }
310 attrs += 2;
311 }
312 if (bond_dist_ratio > 0.)
313 state->themedesc << " bond-dist=\"" << bond_length * bond_dist_ratio / 6. << "\"";
314 state->cur.push (state->doc);
315 }
316
317 static void
cdxml_page_start(GsfXMLIn * xin,xmlChar const **)318 cdxml_page_start (GsfXMLIn *xin, xmlChar const **)
319 {
320 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
321 // we need to set the theme when starting the first page
322 if (state->theme != NULL)
323 return;
324 state->themedesc << "/>";
325 gcp::Document *cpDoc = dynamic_cast <gcp::Document *> (state->doc);
326 if (cpDoc != NULL) {
327 xmlDocPtr xml = xmlParseMemory (state->themedesc.str().c_str(), state->themedesc.str().length());
328 state->theme = new gcp::Theme (NULL);
329 state->theme->Load (xml->children);
330 xmlFreeDoc (xml);
331 gcp::Theme *LocalTheme = gcp::TheThemeManager.GetTheme (state->theme->GetName ().c_str ());
332 if (LocalTheme && *LocalTheme == *(state->theme)) {
333 cpDoc->SetTheme (LocalTheme);
334 delete state->theme;
335 state->theme = LocalTheme; // don't point to an invalid object
336 } else {
337 gcp::TheThemeManager.AddFileTheme (state->theme, state->doc->GetTitle ().c_str ());
338 cpDoc->SetTheme (state->theme);
339 }
340 state->CHeight = cpDoc->GetView ()->GetCHeight (); // FIXME, we probably miss a factor, may be 6.
341 }
342 }
343
344 static void
cdxml_fragment_start(GsfXMLIn * xin,G_GNUC_UNUSED xmlChar const ** attrs)345 cdxml_fragment_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
346 {
347 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
348 Object *obj = state->app->CreateObject ("molecule", state->cur.top ());
349 state->cur.push (obj);
350 state->doc->ObjectLoaded (obj);
351 if (attrs)
352 while (*attrs) {
353 if (!strcmp (reinterpret_cast < char const * > (*attrs), "id")) {
354 unsigned id = atoi (reinterpret_cast < char const * > (attrs[1]));
355 state->loaded_ids[id] = obj->GetId ();
356 }
357 attrs += 2;
358 }
359 }
360
361 static void
cdxml_fragment_end(GsfXMLIn * xin,G_GNUC_UNUSED GsfXMLBlob * blob)362 cdxml_fragment_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
363 {
364 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
365 static_cast <Molecule*> (state->cur.top ())->UpdateCycles ();
366 state->cur.top ()->Lock (false);
367 state->cur.top ()->OnLoaded ();
368 state->cur.pop ();
369 }
370
371 static map<string, int>BondTypes;
372 static void
cdxml_bond_start(GsfXMLIn * xin,xmlChar const ** attrs)373 cdxml_bond_start (GsfXMLIn *xin, xmlChar const **attrs)
374 {
375 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
376 Object *obj = state->app->CreateObject ("bond", state->cur.top ());
377 obj->SetProperty (GCU_PROP_BOND_ORDER, "1");
378 map<string, unsigned>::iterator it;
379 if (attrs)
380 while (*attrs) {
381 if ((it = KnownProps.find ((char const *) *attrs++)) != KnownProps.end ()) {
382 if ((*it).second == GCU_PROP_BOND_TYPE) {
383 if (BondTypes.empty ()) {
384 BondTypes["Solid"] = 0;
385 BondTypes["Dash"] = 1;
386 BondTypes["Hash"] = 2;
387 BondTypes["WedgedHashBegin"] = 3;
388 BondTypes["WedgedHashEnd"] = 4;
389 BondTypes["Bold"] = 5;
390 BondTypes["WedgeBegin"] = 6;
391 BondTypes["WedgeEnd"] = 7;
392 BondTypes["Wavy"] = 8;
393 BondTypes["HollowWedgeBegin"] = 9;
394 BondTypes["HollowWedgeEnd"] = 10;
395 BondTypes["WavyWedgeBegin"] = 11;
396 BondTypes["WavyWedgeEnd"] = 12;
397 BondTypes["Dot"] = 13;
398 BondTypes["DashDot"] = 14;
399 }
400 switch (BondTypes[(char const *) *attrs]) {
401 case 1:
402 case 2:
403 case 3:
404 obj->SetProperty (GCU_PROP_BOND_TYPE, "hash");
405 break;
406 case 4:
407 obj->SetProperty (GCU_PROP_BOND_TYPE, "hash-invert");
408 break;
409 case 5:
410 obj->SetProperty (GCU_PROP_BOND_TYPE, "large");
411 break;
412 case 6:
413 obj->SetProperty (GCU_PROP_BOND_TYPE, "wedge");
414 break;
415 case 7:
416 obj->SetProperty (GCU_PROP_BOND_TYPE, "wedge-invert");
417 break;
418 case 8:
419 obj->SetProperty (GCU_PROP_BOND_TYPE, "squiggle");
420 break;
421 default:
422 obj->SetProperty (GCU_PROP_BOND_TYPE, "normal");
423 }
424 } else if ((*it).second == GCU_PROP_BOND_ORDER) {
425 unsigned order = atoi ((char const *) *attrs);
426 switch (order) {
427 case 2:
428 obj->SetProperty (GCU_PROP_BOND_ORDER, "2");
429 break;
430 case 4:
431 obj->SetProperty (GCU_PROP_BOND_ORDER, "3");
432 break;
433 default:
434 obj->SetProperty (GCU_PROP_BOND_ORDER, "1");
435 break;
436 }
437 } else if ((*it).second == GCU_PROP_BOND_DOUBLE_POSITION) {
438 if (!strcmp (reinterpret_cast < char const * > (*attrs), "Center"))
439 obj->SetProperty (GCU_PROP_BOND_DOUBLE_POSITION, "center");
440 else if (!strcmp (reinterpret_cast < char const * > (*attrs), "Right"))
441 obj->SetProperty (GCU_PROP_BOND_DOUBLE_POSITION, "right");
442 else if (!strcmp (reinterpret_cast < char const * > (*attrs), "Left"))
443 obj->SetProperty (GCU_PROP_BOND_DOUBLE_POSITION, "left");
444 } else if (!obj->SetProperty ((*it).second, (char const *) *attrs)) {
445 CDXMLProps p;
446 p.obj = obj;
447 p.property = (*it).second;
448 p.value = (char const *) *attrs;
449 state->failed.push_back (p);
450 }
451 }
452 attrs++;
453 }
454 state->cur.push (obj);
455 state->doc->ObjectLoaded (obj);
456 }
457
458 static void
cdxml_text_start(GsfXMLIn * xin,xmlChar const ** attrs)459 cdxml_text_start (GsfXMLIn *xin, xmlChar const **attrs)
460 {
461 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
462 gcu::Object *obj = state->app->CreateObject ("text", /*(state->cur.top ()->GetType () == gcu::DocumentType)? */state->cur.top ()/*: NULL*/);
463 state->cur.push (obj);
464 state->doc->ObjectLoaded (obj);
465 char *lowered;
466 state->line_height = 1;
467 map<string, unsigned>::iterator it;
468 if (attrs)
469 while (*attrs) {
470 if (!strcmp (reinterpret_cast < char const * > (*attrs), "id")) {
471 unsigned id = atoi (reinterpret_cast < char const * > (attrs[1]));
472 state->loaded_ids[id] = obj->GetId ();
473 attrs += 2;
474 } else if (!strcmp (reinterpret_cast < char const * > (*attrs), "p")) {
475 std::istringstream in (reinterpret_cast < char const * > (attrs[1]));
476 double x, y;
477 in >> x >> y;
478 y -= state->CHeight;
479 std::ostringstream out;
480 out << x << " " << y;
481 obj->SetProperty (GCU_PROP_POS2D, out.str ().c_str ());
482 attrs += 2;
483 } else if (!strcmp (reinterpret_cast < char const * > (*attrs), "LineHeight") ||
484 !strcmp (reinterpret_cast < char const * > (*attrs), "CaptionLineHeight")) {
485 std::string val (reinterpret_cast < char const * > (attrs[1]));
486 if (val == "auto")
487 obj->SetProperty (GCU_PROP_TEXT_VARIABLE_LINE_HEIGHT, "false");
488 else if (val == "variable")
489 obj->SetProperty (GCU_PROP_TEXT_VARIABLE_LINE_HEIGHT, "true");
490 else {
491 std::istringstream in (val);
492 in >> state->line_height;
493 }
494
495 attrs += 2;
496 } else if ((it = KnownProps.find ((char const *) *attrs++)) != KnownProps.end ()) {
497 lowered = g_ascii_strdown ((char const *) *attrs++, -1);
498 obj->SetProperty ((*it).second, lowered);
499 g_free (lowered);
500 }
501 }
502 state->markup = "<text>";
503 }
504
505 static void
cdxml_text_end(GsfXMLIn * xin,G_GNUC_UNUSED GsfXMLBlob * blob)506 cdxml_text_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
507 {
508 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
509 if (state->cur.top ()->GetParent () == NULL || (gcu::Object::GetTypeName (state->cur.top ()->GetParent ()->GetType ()) == "atom"))
510 delete state->cur.top ();
511 else {
512 state->markup += "</text>";
513 state->cur.top ()->SetProperty (GCU_PROP_TEXT_MARKUP, state->markup.c_str ());
514 if (state->line_height > 1) {
515 state->cur.top ()->SetProperty (GCU_PROP_TEXT_VARIABLE_LINE_HEIGHT, "false");
516 std::istringstream in (state->cur.top ()->GetProperty (GCU_PROP_TEXT_MAX_LINE_HEIGHT));
517 double lh;
518 in >> lh;
519 std::ostringstream out;
520 out << state->line_height - lh;
521 state->cur.top ()->SetProperty (GCU_PROP_TEXT_INTERLINE, out.str ().c_str ());
522 }
523 }
524 state->markup.clear ();
525 state->cur.pop ();
526 }
527
528 static void
cdxml_string_start(GsfXMLIn * xin,xmlChar const ** attrs)529 cdxml_string_start (GsfXMLIn *xin, xmlChar const **attrs)
530 {
531 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
532 state->attributes = 0;
533 if (attrs)
534 while (*attrs) {
535 if (!strcmp ((char const *) *attrs, "font")) {
536 attrs++;
537 state->font = atoi ((char const *) *attrs);
538 state->markup += "<font name=\"";
539 state->markup += state->fonts[state->font].name;
540 state->markup += ",";
541 } else if (!strcmp ((char const *) *attrs, "face")) {
542 attrs++;
543 state->attributes |= atoi ((char const *) *attrs);
544 } else if (!strcmp ((char const *) *attrs, "size")) {
545 attrs++;
546 state->size = (char const *) *attrs;
547 } else if (!strcmp ((char const *) *attrs, "color")) {
548 attrs++;
549 state->attributes |= 0x100;
550 state->color = atoi ((char const *) *attrs);
551 } else
552 attrs ++;
553 attrs++;
554 }
555 state->markup += state->size + "\">";
556 if (state->attributes & 0x100)
557 state->markup += string ("<fore ") + state->colors[state->color] + ">";
558 if (state->attributes & 1)
559 state->markup += "<b>";
560 if (state->attributes & 2)
561 state->markup += "<i>";
562 if (state->attributes & 4)
563 state->markup += "<u>";
564 if ((state->attributes & 0x60) != 0x60) {
565 if (state->attributes & 0x20)
566 state->markup += "<sub>";
567 else if (state->attributes & 0x40)
568 state->markup += "<sup>";
569 }
570
571 // TODO: parse attributes
572 }
573
574 static void
cdxml_string_end(GsfXMLIn * xin,G_GNUC_UNUSED GsfXMLBlob * blob)575 cdxml_string_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
576 {
577 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
578 bool opened = true;
579 if ((state->attributes & 0x60) == 0x60) {
580 // for now put all numbers as subscripts
581 // FIXME: fix this kludgy code
582 int cur = 0, size = strlen (xin->content->str);
583 char new_size[G_ASCII_DTOSTR_BUF_SIZE], height[G_ASCII_DTOSTR_BUF_SIZE];
584 g_ascii_dtostr (new_size, G_ASCII_DTOSTR_BUF_SIZE, g_ascii_strtod (state->size.c_str (), NULL) / 1.5);
585 g_ascii_dtostr (height, G_ASCII_DTOSTR_BUF_SIZE, g_ascii_strtod (state->size.c_str (), NULL) / 3.);
586 while (cur < size) {
587 while (cur < size && (xin->content->str[cur] < '0' || xin->content->str[cur] > '9'))
588 state->markup += xin->content->str[cur++];
589 if (cur < size) {
590 if (state->attributes & 4)
591 state->markup += "</u>";
592 if (state->attributes & 2)
593 state->markup += "</i>";
594 if (state->attributes & 1)
595 state->markup += "</b>";
596 if (state->attributes & 0x100)
597 state->markup += "</fore>";
598 state->markup += string ("</font><font name=\"") + state->fonts[state->font].name + " " + new_size + "\">";
599 if (state->attributes & 0x100)
600 state->markup += string ("<fore ") + state->colors[state->color] + ">";
601 state->markup += string ("<sub height=\"") + height + "\">";
602 while (xin->content->str[cur] >= '0' && xin->content->str[cur] <= '9')
603 state->markup += xin->content->str[cur++];
604 state->markup += "</sub>";
605 if (state->attributes & 0x100)
606 state->markup += "</fore>";
607 state->markup += string ("</font>");
608 if (cur < size) {
609 state->markup += string ("<font name=\"") + state->fonts[state->font].name + " " + state->size + "\">";
610 if (state->attributes & 0x100)
611 state->markup += string ("<fore ") + state->colors[state->color] + ">";
612 if (state->attributes & 1)
613 state->markup += "<b>";
614 if (state->attributes & 2)
615 state->markup += "<i>";
616 if (state->attributes & 4)
617 state->markup += "<u>";
618 } else
619 opened = false;
620 }
621 }
622 } else {
623 state->markup += xin->content->str;
624 if (state->attributes & 0x20)
625 state->markup += "</sub>";
626 else if (state->attributes & 0x40)
627 state->markup += "</sup>";
628 }
629 if (opened) {
630 if (state->attributes & 4)
631 state->markup += "</u>";
632 if (state->attributes & 2)
633 state->markup += "</i>";
634 if (state->attributes & 1)
635 state->markup += "</b>";
636 if (state->attributes & 0x100)
637 state->markup += "</fore>";
638 state->markup += "</font>";
639 }
640 state->attributes = 0;
641 }
642
643 static void
fragment_done(G_GNUC_UNUSED GsfXMLIn * xin,CDXMLReadState * state)644 fragment_done (G_GNUC_UNUSED GsfXMLIn *xin, CDXMLReadState *state)
645 {
646 Object *atom = state->cur.top (), *child;
647 state->cur.pop ();
648 map <string, Object *>::iterator i;
649 Molecule *mol = NULL, *mol1 = NULL;
650 string buf;
651 //TODO: retreive text and molecule and compare
652 while ((child = atom->GetFirstChild (i))) {
653 child->SetParent (NULL);
654 if (child->GetType () == MoleculeType)
655 mol = dynamic_cast <Molecule *> (child);
656 else {
657 buf = child->GetProperty (GCU_PROP_TEXT_TEXT);
658 delete child;
659 }
660 }
661 if (mol) {
662 if (buf.length () > 0) {
663 try {
664 Formula form (buf, GCU_FORMULA_PARSE_RESIDUE);
665 mol1 = Molecule::MoleculeFromFormula (state->doc, form);
666 bool have_pseudo = false;
667 Object *obj = mol->GetFirstChild (i);
668 gcu::Atom *a = NULL;
669 while (obj) {
670 a = dynamic_cast <gcu::Atom *> (obj);
671 if (a && ! a->GetZ ()) {
672 have_pseudo = true;
673 break;
674 }
675 obj = mol->GetNextChild (i);
676 }
677 if (!mol1 || !(*mol == *mol1)) {
678 if (have_pseudo) {
679 // try adding a new residue
680 // first examine the first atom
681 map <gcu::Atom*, gcu::Bond*>::iterator i;
682 gcu::Bond *b = a->GetFirstBond (i);
683 int residue_offset = 0;
684 if (!b)
685 goto fragment_error;
686 gcu::Atom *a2 = b->GetAtom (a);
687 if (!a2)
688 goto fragment_error;
689 list<FormulaElt *> const &elts = form.GetElements ();
690 list<FormulaElt *>::const_iterator j = elts.begin ();
691 FormulaAtom *fatom = dynamic_cast <FormulaAtom *> (*j);
692 int valence;
693 if (!fatom || fatom->elt != a2->GetZ ())
694 goto fragment_add;
695 valence = Element::GetElement (fatom->elt)->GetDefaultValence ();
696 switch (valence) {
697 case 2: {
698 /* remove the first atom and replace it by a pseudo-atom, then add the residue
699 this helps with things begining with an oxygen or a sulfur, but might be
700 not enough n other cases */
701 double x, y;
702 a2->GetCoords (&x, &y);
703 a->SetCoords (x, y);
704 a->RemoveBond (b);
705 a2->RemoveBond (b);
706 mol->Remove (b);
707 delete b;
708 if (a2->GetBondsNumber () > 1)
709 goto fragment_error;
710 b = a2->GetFirstBond (i);
711 if (b->GetOrder () != 1)
712 goto fragment_error;
713 b->ReplaceAtom (a2, a);
714 a->AddBond (b);
715 mol->Remove (a2);
716 delete a2;
717 // now remove the atom from the new residue symbol
718 residue_offset += fatom->end;
719 break;
720 }
721 case 3:
722 // we do not support that at the moment
723 goto fragment_error;
724 break;
725 default:
726 // we do not support that at the moment
727 goto fragment_error;
728 }
729 fragment_add:
730 // Try create a new document, using the symbol as name
731 // reparent the molecule to avoid a crash
732 state->doc->AddChild (mol);
733 state->doc->CreateResidue (buf.c_str () + residue_offset, buf.c_str () + residue_offset, mol);
734 mol = NULL;
735 goto fragment_success;
736 }
737 fragment_error:
738 g_warning (_("failed for %s\n"),buf.c_str ());
739 }
740 }
741 catch (parse_error &error) {
742 int start, length;
743 puts (error.what (start, length));
744 }
745 fragment_success:
746 string pos = atom->GetProperty (GCU_PROP_POS2D);
747 string id = atom->GetId ();
748 mol = reinterpret_cast <Molecule *> (state->cur.top ());
749 mol->Remove (atom);
750 delete atom;
751 atom = state->app->CreateObject ("fragment", mol);
752 atom->SetProperty (GCU_PROP_TEXT_TEXT, buf.c_str ());
753 atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_ID, id.c_str ());
754 atom->SetProperty (GCU_PROP_FRAGMENT_ATOM_START, "0");
755 atom->SetProperty (GCU_PROP_POS2D, pos.c_str ());
756 if (mol1) {
757 mol1->SetParent (NULL);
758 delete mol1;
759 }
760 mol = NULL;
761 }
762 if (mol)
763 delete mol;
764 } else goto fragment_success;
765 }
766
767 static void
cdxml_node_start(GsfXMLIn * xin,xmlChar const ** attrs)768 cdxml_node_start (GsfXMLIn *xin, xmlChar const **attrs)
769 {
770 static GsfXMLInNode const atom_dtd[] = {
771 GSF_XML_IN_NODE (ATOM, ATOM, -1, "n", GSF_XML_CONTENT, NULL, NULL),
772 GSF_XML_IN_NODE (ATOM, T, -1, "t", GSF_XML_CONTENT, cdxml_text_start, cdxml_text_end),
773 GSF_XML_IN_NODE (T, S, -1, "s", GSF_XML_CONTENT, cdxml_string_start, cdxml_string_end),
774 GSF_XML_IN_NODE (ATOM, FRAGMENT, -1, "fragment", GSF_XML_CONTENT, cdxml_fragment_start, cdxml_fragment_end),
775 GSF_XML_IN_NODE (FRAGMENT, NODE, -1, "n", GSF_XML_CONTENT, cdxml_node_start, cdxml_simple_end),
776 GSF_XML_IN_NODE (FRAGMENT, BOND, -1, "b", GSF_XML_CONTENT, cdxml_bond_start, cdxml_simple_end),
777 GSF_XML_IN_NODE (FRAGMENT, T1, -1, "t", GSF_XML_CONTENT, NULL, NULL),
778 GSF_XML_IN_NODE (T1, S1, -1, "s", GSF_XML_CONTENT, cdxml_string_start, cdxml_string_end),
779 GSF_XML_IN_NODE_END
780 };
781 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
782 Object *obj = state->app->CreateObject ("atom", state->cur.top ());
783 obj->SetProperty (GCU_PROP_ATOM_Z, "6");
784 state->doc->ObjectLoaded (obj);
785 map<string, unsigned>::iterator it;
786 bool fragment = false;
787 if (attrs)
788 while (*attrs) {
789 if ((it = KnownProps.find ((char const *) *attrs)) != KnownProps.end ()) {
790 attrs++;
791 obj->SetProperty ((*it).second, (char const *) *attrs);
792 } else if (!strcmp ((char const *) *attrs++, "NodeType")) {
793 if (!strcmp ((char const *) *attrs, "Fragment") ||
794 !strcmp ((char const *) *attrs, "Nickname") ||
795 !strcmp ((char const *) *attrs, "Unspecified") ||
796 !strcmp ((char const *) *attrs, "GenericNickname"))
797 fragment = true;
798 else if (!strcmp ((char const *) *attrs, "ExternalConnectionPoint")) {
799 // convert the atom to a pseudo atom.
800 string pos = obj->GetProperty (GCU_PROP_POS2D);
801 string id = obj->GetProperty (GCU_PROP_ID);
802 Molecule *mol = dynamic_cast <Molecule *> (state->cur.top ());
803 if (mol)
804 mol->Remove (obj);
805 delete obj;
806 obj = state->app->CreateObject ("pseudo-atom", state->cur.top ());
807 if (id.length ())
808 obj->SetProperty (GCU_PROP_ID, id.c_str ());
809 obj->SetProperty (GCU_PROP_POS2D, pos.c_str ());
810 }
811 attrs++;
812 }
813 attrs++;
814 }
815 state->cur.push (obj);
816 if (fragment) {
817 static GsfXMLInDoc *doc = NULL;
818 if (NULL == doc)
819 doc = gsf_xml_in_doc_new (atom_dtd, NULL);
820 state->cur.push (obj); // push it twice in that case
821 state->doc->ObjectLoaded (obj);
822 gsf_xml_in_push_state (xin, doc, state, (GsfXMLInExtDtor) fragment_done, attrs);
823 }
824 }
825
826 static void
cdxml_font_start(GsfXMLIn * xin,xmlChar const ** attrs)827 cdxml_font_start (GsfXMLIn *xin, xmlChar const **attrs)
828 {
829 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
830 CDXMLFont font;
831 font.index = 0;
832 if (attrs)
833 while (*attrs) {
834 if (!strcmp ((char const *) *attrs, "id"))
835 font.index = atoi ((char const *) *(attrs + 1));
836 else if (!strcmp ((char const *) *attrs, "charset"))
837 font.encoding = (char const *) *(attrs + 1);
838 else if (!strcmp ((char const *) *attrs, "name"))
839 font.name = (char const *) *(attrs + 1);
840 attrs += 2;
841 }
842 if (state->labelFont == font.index)
843 state->themedesc << " font-family=\"" << font.name << "\"";
844 if (state->captionFont == font.index)
845 state->themedesc << " text-font-family=\"" << font.name << "\"";
846 state->fonts[font.index] = font;
847 }
848
849 static void
cdxml_color(GsfXMLIn * xin,xmlChar const ** attrs)850 cdxml_color (GsfXMLIn *xin, xmlChar const **attrs)
851 {
852 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
853 string red, green, blue;
854 if (attrs)
855 while (*attrs) {
856 if (!strcmp ((char const *) *attrs, "r"))
857 red = (char const *) attrs[1];
858 else if (!strcmp ((char const *) *attrs, "g"))
859 green = (char const *) attrs[1];
860 else if (!strcmp ((char const *) *attrs, "b"))
861 blue = (char const *) attrs[1];
862 attrs += 2;
863 }
864 state->colors.push_back (string ("red=\"") + red + "\" green=\"" + green + "\" blue=\"" + blue + "\"");
865 }
866
867 static void
cdxml_group_start(GsfXMLIn * xin,G_GNUC_UNUSED xmlChar const ** attrs)868 cdxml_group_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
869 {
870 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
871 Object *obj = state->app->CreateObject ("group", state->cur.top ());
872 obj->Lock ();
873 state->cur.push (obj);
874 state->doc->ObjectLoaded (obj);
875 }
876
877 static void
cdxml_graphic_start(GsfXMLIn * xin,xmlChar const ** attrs)878 cdxml_graphic_start (GsfXMLIn *xin, xmlChar const **attrs)
879 {
880 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
881 guint32 Id = 0;
882 guint16 type = 0xffff, arrow_type = 0xffff;
883 double x0, y0, x1, y1;
884 if (attrs)
885 while (*attrs) {
886 if (!strcmp ((char const *) *attrs, "id"))
887 Id = atoi ((char const *) attrs[1]);
888 else if (!strcmp ((char const *) *attrs, "BoundingBox")) {
889 istringstream str (reinterpret_cast <char const *> (attrs[1]));
890 str >> x1 >> y1 >> x0 >> y0;
891 } else if (!strcmp ((char const *) *attrs, "GraphicType")) {
892 if (!strcmp ((char const *) attrs[1], "Line"))
893 type = 1;
894 } else if (!strcmp ((char const *) *attrs, "ArrowType")) {
895 if (!strcmp ((char const *) attrs[1], "FullHead") || !strcmp ((char const *) attrs[1], "HalfHead"))
896 arrow_type = 2;
897 else if (!strcmp ((char const *) attrs[1], "Resonance"))
898 arrow_type = 4;
899 else if (!strcmp ((char const *) attrs[1], "Equilibrium"))
900 arrow_type = 8;
901 else if (!strcmp ((char const *) attrs[1], "Hollow"))
902 arrow_type = 16;
903 else if (!strcmp ((char const *) attrs[1], "RetroSynthetic"))
904 arrow_type = 32;
905 }
906 attrs+=2;
907 }
908 if (type == 1) {
909 Object *obj = NULL;
910 ostringstream str;
911 switch (arrow_type) {
912 case 1:
913 case 2:
914 obj = state->app->CreateObject ("reaction-arrow", state->cur.top ());
915 str << "ra" << Id;
916 break;
917 case 4:
918 obj = state->app->CreateObject ("mesomery-arrow", state->cur.top ());
919 str << "ma" << Id;
920 break;
921 case 8:
922 obj = state->app->CreateObject ("reaction-arrow", state->cur.top ());
923 str << "ra" << Id;
924 obj->SetProperty (GCU_PROP_REACTION_ARROW_TYPE, "double");
925 break;
926 case 32:
927 obj = state->app->CreateObject ("retrosynthesis-arrow", state->cur.top ());
928 str << "rsa" << Id;
929 break;
930 default:
931 break;
932 }
933 if (obj) {
934 obj->SetId (str.str ().c_str ());
935 state->loaded_ids[Id] = str.str ();
936 ostringstream str_;
937 str_ << x0 << " " << y0 << " " << x1 << " " << y1;
938 obj->SetProperty (GCU_PROP_ARROW_COORDS, str_.str ().c_str ());
939 state->doc->ObjectLoaded (obj);
940 }
941 }
942 }
943
944 static void
cdxml_step_start(GsfXMLIn * xin,G_GNUC_UNUSED xmlChar const ** attrs)945 cdxml_step_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
946 {
947 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
948 StepData data;
949 if (attrs)
950 while (*attrs) {
951 std::string key = reinterpret_cast < char const * > (*attrs);
952 std::istringstream values (reinterpret_cast < char const * > (attrs[1]));
953 attrs +=2;
954 std::list < unsigned > *target;
955 if (key == "ReactionStepReactants")
956 target = &data.Reagents;
957 else if (key == "ReactionStepProducts")
958 target = &data.Products;
959 else if (key == "ReactionStepArrows")
960 target = &data.Arrows;
961 else if (key == "ReactionStepObjectsAboveArrow")
962 target = &data.ObjectsAbove;
963 else if (key == "ReactionStepObjectsBelowArrow")
964 target = &data.ObjectsBelow;
965 else
966 continue;
967 while (!values.eof ()) {
968 unsigned id;
969 values >> id;
970 target->push_back (id);
971 }
972 }
973 state->scheme.Steps.push_back (data);
974 }
975
976 static void
cdxml_scheme_end(GsfXMLIn * xin,G_GNUC_UNUSED GsfXMLBlob * blob)977 cdxml_scheme_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
978 {
979 CDXMLReadState *state = (CDXMLReadState *) xin->user_state;
980 state->schemes.push_back (state->scheme);
981 state->scheme.Steps.clear ();
982 }
983
build_scheme(CDXMLReadState & state,SchemeData & scheme)984 static void build_scheme (CDXMLReadState &state, SchemeData &scheme)
985 {
986 gcu::Document *doc = state.doc;
987 std::list < StepData >::iterator i, iend = scheme.Steps.end ();
988 std::list < unsigned >::iterator j, jend;
989 int IsReaction = 0, IsMesomery = 0, IsRetrosynthesis = 0;
990 bool HasMesomeryArrows = false;
991 gcu::Object *parent, *arrow, *obj, *step, *reactant;
992 for (i = scheme.Steps.begin (); i != iend; i++) {
993 if ((*i).Arrows.size () != 1)
994 return; // unsupported feature, don't load the scheme
995 obj = doc->GetChild ((state.loaded_ids[*((*i).Arrows.begin())]).c_str ());
996 if (obj == NULL)
997 return;
998 std::string klass = gcu::Object::GetTypeName (obj->GetType ());
999 if (klass == "retrosynthesis-arrow") {
1000 if (IsRetrosynthesis == -1)
1001 return;
1002 IsRetrosynthesis = 1;
1003 IsReaction = IsMesomery = -1;
1004 } else if (klass == "mesomery-arrow") {
1005 if (IsMesomery == -1)
1006 return;
1007 IsRetrosynthesis = -1;
1008 if (IsMesomery == 0 && IsReaction == 0)
1009 IsMesomery = 1;
1010 HasMesomeryArrows = true;
1011 } else if (klass == "reaction-arrow") {
1012 if (IsReaction == -1 || IsMesomery == -1)
1013 return;
1014 IsReaction = 1;
1015 IsRetrosynthesis = -1;
1016 IsMesomery = 0;
1017 } else
1018 return;
1019 }
1020 if (IsRetrosynthesis == 1) {
1021 gcu::Object *retrosynthesis = doc->CreateObject ("retrosynthesis", doc);
1022 std::set < std::string > targets;
1023 std::set < std::string >::iterator target;
1024 ostringstream str;
1025 str << "rsy" << scheme.Id;
1026 retrosynthesis->SetId (str.str ().c_str ());
1027 state.loaded_ids[scheme.Id] = retrosynthesis->GetId ();
1028 // now, add the objects to the retrosynthesis
1029 for (i = scheme.Steps.begin (); i != iend; i++) {
1030 if ((*i).Reagents.size () != 1 || (*i).Products.size () != 1) {
1031 delete retrosynthesis;
1032 return;
1033 }
1034 // first the arrow
1035 arrow = doc->GetChild ((state.loaded_ids[*((*i).Arrows.begin())]).c_str ());
1036 obj = doc->GetDescendant (state.loaded_ids[*(*i).Reagents.begin ()].c_str ());
1037 parent = obj->GetParent ();
1038 if (parent == doc)
1039 parent = doc->CreateObject ("retrosynthesis-step", retrosynthesis);
1040 else if (parent->GetParent () != retrosynthesis) {
1041 delete retrosynthesis;
1042 return;
1043 }
1044 parent->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
1045 arrow->SetProperty (GCU_PROP_ARROW_START_ID, parent->GetId ());
1046 targets.insert (parent->GetId ());
1047 obj = doc->GetDescendant (state.loaded_ids[*(*i).Products.begin ()].c_str ());
1048 parent = obj->GetParent ();
1049 if (parent == doc)
1050 parent = doc->CreateObject ("retrosynthesis-step", retrosynthesis);
1051 else if (parent->GetParent () != retrosynthesis) {
1052 delete retrosynthesis;
1053 return;
1054 }
1055 parent->AddChild (obj);
1056 arrow->SetProperty (GCU_PROP_ARROW_END_ID, parent->GetId ());
1057 target = targets.find (parent->GetId ());
1058 if (target != targets.end ())
1059 targets.erase (target);
1060 retrosynthesis->AddChild (arrow);
1061 }
1062 if (targets.size () != 1) {
1063 delete retrosynthesis;
1064 return;
1065 }
1066 // using GCU_PROP_MOLECULE even if not ideal (the target is a step, not the molecule inside)
1067 retrosynthesis->SetProperty (GCU_PROP_MOLECULE, (*targets.begin()).c_str ());
1068 // Ignore objects over and under the arrows for now
1069 } else if (IsMesomery == 1) {
1070 gcu::Object *mesomery = doc->CreateObject ("mesomery", doc);
1071 ostringstream str;
1072 str << "msy" << scheme.Id;
1073 mesomery->SetId (str.str ().c_str ());
1074 state.loaded_ids[scheme.Id] = mesomery->GetId ();
1075 // now, add the objects to the mesomery
1076 for (i = scheme.Steps.begin (); i != iend; i++) {
1077 if ((*i).Reagents.size () != 1 || (*i).Products.size () != 1) {
1078 delete mesomery;
1079 return;
1080 }
1081 // first the arrow
1082 arrow = doc->GetChild ((state.loaded_ids[*((*i).Arrows.begin())]).c_str ());
1083 obj = doc->GetDescendant (state.loaded_ids[*(*i).Reagents.begin ()].c_str ());
1084 parent = obj->GetParent ();
1085 if (parent == doc)
1086 parent = doc->CreateObject ("mesomer", mesomery);
1087 else if (parent->GetParent () != mesomery) {
1088 delete mesomery;
1089 return;
1090 }
1091 parent->SetProperty (GCU_PROP_MESOMER, obj->GetId ());
1092 arrow->SetProperty (GCU_PROP_ARROW_START_ID, parent->GetId ());
1093 obj = doc->GetDescendant (state.loaded_ids[*(*i).Products.begin ()].c_str ());
1094 parent = obj->GetParent ();
1095 if (parent == doc)
1096 parent = doc->CreateObject ("mesomer", mesomery);
1097 else if (parent->GetParent () != mesomery) {
1098 delete mesomery;
1099 return;
1100 }
1101 parent->AddChild (obj);
1102 arrow->SetProperty (GCU_PROP_ARROW_END_ID, parent->GetId ());
1103 mesomery->AddChild (arrow);
1104 }
1105 // Ignore objects over and under the arrows for now
1106 } else if (IsReaction ==1) {
1107 if (HasMesomeryArrows) {
1108 // build mesomeries inside reactions,
1109 // FIXME: nots supported for now
1110 return;
1111 }
1112 gcu::Object *reaction = doc->CreateObject ("reaction", doc);
1113 ostringstream str;
1114 str << "r" << scheme.Id;
1115 reaction->SetId (str.str ().c_str ());
1116 state.loaded_ids[scheme.Id] = reaction->GetId ();
1117 // now, add the objects to the reaction
1118 for (i = scheme.Steps.begin (); i != iend; i++) {
1119 // first the arrow
1120 arrow = doc->GetChild ((state.loaded_ids[*((*i).Arrows.begin())]).c_str ());
1121 reaction->AddChild (arrow);
1122 // then reagents
1123 jend = (*i).Reagents.end ();
1124 parent = NULL;
1125 gcu::Object *rs = NULL; // make g++ happy
1126 for (j = (*i).Reagents.begin (); j != jend; j++) {
1127 obj = doc->GetDescendant (state.loaded_ids[*j].c_str ());
1128 if (obj == NULL) {
1129 delete reaction;
1130 return;
1131 }
1132 parent = obj->GetParent ();
1133 if (rs == NULL) {
1134 if (parent == doc) {
1135 rs = reaction->CreateObject ("reaction-step", reaction);
1136 arrow->SetProperty (GCU_PROP_ARROW_START_ID, rs->GetId ());
1137 reactant = rs->CreateObject ("reactant", rs);
1138 reactant->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
1139 } else {
1140 rs = parent->GetParent ();
1141 if (rs->GetParent () != reaction) {
1142 delete reaction;
1143 return;
1144 }
1145 }
1146 } else {
1147 if (parent == doc) {
1148 reactant = rs->CreateObject ("reactant", rs);
1149 reactant->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
1150 } else if (rs != parent->GetParent ()) {
1151 delete reaction;
1152 return;
1153 }
1154 }
1155 // search for potential stoichiometry coefficients
1156 arrow->SetProperty (GCU_PROP_ARROW_START_ID, rs->GetId ());
1157 rs->OnLoaded ();
1158 }
1159 // same treatment for products
1160 jend = (*i).Products.end ();
1161 rs = NULL;
1162 for (j = (*i).Products.begin (); j != jend; j++) {
1163 obj = doc->GetDescendant (state.loaded_ids[*j].c_str ());
1164 if (obj == NULL) {
1165 delete reaction;
1166 return;
1167 }
1168 parent = obj->GetParent ();
1169 if (rs == NULL) {
1170 if (parent == doc) {
1171 rs = reaction->CreateObject ("reaction-step", reaction);
1172 arrow->SetProperty (GCU_PROP_ARROW_END_ID, rs->GetId ());
1173 reactant = rs->CreateObject ("reactant", rs);
1174 reactant->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
1175 } else {
1176 rs = parent->GetParent ();
1177 if (rs->GetParent () != reaction) {
1178 delete reaction;
1179 return;
1180 }
1181 }
1182 } else {
1183 if (parent == doc) {
1184 reactant = rs->CreateObject ("reactant", rs);
1185 reactant->SetProperty (GCU_PROP_MOLECULE, obj->GetId ());
1186 } else if (rs != parent->GetParent ()) {
1187 delete reaction;
1188 return;
1189 }
1190 }
1191 // search for potential stoichiometry coefficients
1192 arrow->SetProperty (GCU_PROP_ARROW_END_ID, rs->GetId ());
1193 rs->OnLoaded ();
1194 }
1195 // last, the objects attached above and below the arrow
1196 if (!(*i).ObjectsAbove.empty () || !(*i).ObjectsBelow.empty ()) {
1197 jend = (*i).ObjectsAbove.end ();
1198 for (j = (*i).ObjectsAbove.begin (); j != jend; j++) {
1199 obj = doc->GetDescendant (state.loaded_ids[*j].c_str ());
1200 if (obj == NULL) // we should emit at least a warning
1201 continue;
1202 parent = arrow->CreateObject ("reaction-prop", arrow);
1203 parent->SetProperty (GCU_PROP_ARROW_OBJECT, obj->GetId ());
1204 }
1205 jend = (*i).ObjectsBelow.end ();
1206 for (j = (*i).ObjectsBelow.begin (); j != jend; j++) {
1207 obj = doc->GetDescendant (state.loaded_ids[*j].c_str ());
1208 if (obj == NULL) // we should emit at least a warning
1209 continue;
1210 parent = arrow->CreateObject ("reaction-prop", arrow);
1211 parent->SetProperty (GCU_PROP_ARROW_OBJECT, obj->GetId ());
1212 }
1213 }
1214 }
1215 // now search for stoichiometry coefficients if any
1216 gcp::WidgetData *data = static_cast <gcp::Document * > (doc)->GetView ()->GetData ();
1217 gccv::Rect rect;
1218 double x0, y0, x1;
1219 std::map < std::string, Object * >::iterator k, l, r;
1220 std::pair <gcu::Object *, gcu::Object * > couple;
1221 std::list < std::pair <gcu::Object *, gcu::Object * > > couples;
1222 obj = doc->GetFirstChild (k);
1223 while (obj) {
1224 // assuming that only text object can be stoichiometric coefs
1225 if (obj->GetType () == gcu::TextType) {
1226 data->GetObjectBounds (obj, rect);
1227 x0 = rect.x0;
1228 y0 = (rect.y0 + rect.y1) / 2.;
1229 x1 = rect.x1 + state.padding;
1230 for (step = reaction->GetFirstChild (l); step; step = reaction->GetNextChild (l)) {
1231 if (gcu::Object::GetTypeName (step->GetType ()) != "reaction-step")
1232 continue;
1233 data->GetObjectBounds (step, rect);
1234 if (x0 > rect.x1 || x1 < rect.x0 || y0 > rect.y1 || y0 < rect.y0)
1235 continue;
1236 for (reactant = step->GetFirstChild (r); reactant; reactant = step->GetNextChild (r)) {
1237 if (reactant->GetType () != gcu::ReactantType)
1238 continue;
1239 data->GetObjectBounds (reactant, rect);
1240 if (x0 > rect.x0 || x1 < rect.x0 || y0 > rect.y1 || y0 < rect.y0)
1241 continue;
1242 // if we get there, we got it
1243 // we must not set it now to avoid an invalid iterator at this point, so store in couples.
1244 couple.first = reactant;
1245 couple.second = obj;
1246 couples.push_back (couple);
1247 goto next_text;
1248 }
1249 }
1250 }
1251 next_text:
1252 obj = doc->GetNextChild (k);
1253 }
1254 std::list < std::pair <gcu::Object *, gcu::Object * > >::iterator c, cend = couples.end ();
1255 for (c = couples.begin (); c != cend; c++) {
1256 (*c).first->SetProperty (GCU_PROP_STOICHIOMETRY, (*c).second->GetId ());
1257 }
1258 }
1259 }
1260
1261 ////////////////////////////////////////////////////////////////////////////////
1262 // Reading code
1263 static GsfXMLInNode const cdxml_dtd[] = {
1264 GSF_XML_IN_NODE (CDXML, CDXML, -1, "CDXML", GSF_XML_CONTENT, &cdxml_doc, NULL),
1265 GSF_XML_IN_NODE (CDXML, COLORTABLE, -1, "colortable", GSF_XML_CONTENT, NULL, NULL),
1266 GSF_XML_IN_NODE (COLORTABLE, COLOR, -1, "color", GSF_XML_CONTENT, &cdxml_color, NULL),
1267 GSF_XML_IN_NODE (CDXML, FONTTABLE, -1, "fonttable", GSF_XML_CONTENT, NULL, NULL),
1268 GSF_XML_IN_NODE (FONTTABLE, FONT, -1, "font", GSF_XML_CONTENT, cdxml_font_start, NULL),
1269 GSF_XML_IN_NODE (CDXML, PAGE, -1, "page", GSF_XML_CONTENT, cdxml_page_start, NULL),
1270 GSF_XML_IN_NODE (PAGE, T, -1, "t", GSF_XML_NO_CONTENT, cdxml_text_start, cdxml_text_end),
1271 GSF_XML_IN_NODE (T, S, -1, "s", GSF_XML_CONTENT, cdxml_string_start, cdxml_string_end),
1272 GSF_XML_IN_NODE (PAGE, FRAGMENT, -1, "fragment", GSF_XML_CONTENT, &cdxml_fragment_start, &cdxml_fragment_end),
1273 GSF_XML_IN_NODE (FRAGMENT, NODE, -1, "n", GSF_XML_CONTENT, cdxml_node_start, cdxml_simple_end),
1274 GSF_XML_IN_NODE (NODE, T, -1, "t", GSF_XML_2ND, NULL, NULL),
1275 GSF_XML_IN_NODE (FRAGMENT, BOND, -1, "b", GSF_XML_CONTENT, cdxml_bond_start, cdxml_simple_end),
1276 GSF_XML_IN_NODE (PAGE, GROUP, -1, "group", GSF_XML_CONTENT, cdxml_group_start, cdxml_simple_end),
1277 GSF_XML_IN_NODE (GROUP, FRAGMENT, -1, "fragment", GSF_XML_2ND, NULL, NULL),
1278 GSF_XML_IN_NODE (PAGE, GRAPHIC, -1, "graphic", GSF_XML_CONTENT, cdxml_graphic_start, NULL),
1279 GSF_XML_IN_NODE (PAGE, ALTGROUP, -1, "altgroup", GSF_XML_CONTENT, NULL, NULL),
1280 GSF_XML_IN_NODE (PAGE, CURVE, -1, "curve", GSF_XML_CONTENT, NULL, NULL),
1281 GSF_XML_IN_NODE (PAGE, STEP, -1, "step", GSF_XML_NO_CONTENT, NULL, NULL),
1282 GSF_XML_IN_NODE (PAGE, SCHEME, -1, "scheme", GSF_XML_NO_CONTENT, NULL, cdxml_scheme_end),
1283 GSF_XML_IN_NODE (SCHEME, REACTIONSTEP, -1, "step", GSF_XML_CONTENT, cdxml_step_start, NULL),
1284 GSF_XML_IN_NODE (PAGE, SPECTRUM, -1, "spectrum", GSF_XML_CONTENT, NULL, NULL),
1285 GSF_XML_IN_NODE (PAGE, EMBEDDEDOBJECT, -1, "embeddedobject", GSF_XML_CONTENT, NULL, NULL),
1286 GSF_XML_IN_NODE (PAGE, SEQUENCE, -1, "sequence", GSF_XML_CONTENT, NULL, NULL),
1287 GSF_XML_IN_NODE (PAGE, CROSSREFERENCE, -1, "crossreference", GSF_XML_CONTENT, NULL, NULL),
1288 GSF_XML_IN_NODE (PAGE, SPLITTER, -1, "splitter", GSF_XML_CONTENT, NULL, NULL),
1289 GSF_XML_IN_NODE (PAGE, TABLE, -1, "table", GSF_XML_CONTENT, NULL, NULL),
1290 GSF_XML_IN_NODE (PAGE, BRACKETEDGROUP, -1, "bracketedgroup", GSF_XML_CONTENT, NULL, NULL),
1291 GSF_XML_IN_NODE (PAGE, BORDER, -1, "border", GSF_XML_CONTENT, NULL, NULL),
1292 GSF_XML_IN_NODE (PAGE, GEOMETRY, -1, "geometry", GSF_XML_CONTENT, NULL, NULL),
1293 GSF_XML_IN_NODE (PAGE, CONSTRAINT, -1, "constraint", GSF_XML_CONTENT, NULL, NULL),
1294 GSF_XML_IN_NODE (PAGE, TLCPLATE, -1, "tlcplate", GSF_XML_CONTENT, NULL, NULL),
1295 GSF_XML_IN_NODE (PAGE, CHEMICALPROPERTY, -1, "chemicalproperty", GSF_XML_CONTENT, NULL, NULL),
1296 GSF_XML_IN_NODE (PAGE, ARROW, -1, "arrow", GSF_XML_CONTENT, NULL, NULL),
1297 GSF_XML_IN_NODE (PAGE, BIOSHAPE, -1, "bioshape", GSF_XML_CONTENT, NULL, NULL),
1298 GSF_XML_IN_NODE (PAGE, STOICHIOMETRY, -1, "stoichiometrygrid", GSF_XML_CONTENT, NULL, NULL),
1299 GSF_XML_IN_NODE (PAGE, PLASMIDMAP, -1, "plasmidmap", GSF_XML_CONTENT, NULL, NULL),
1300 GSF_XML_IN_NODE (PAGE, OBJECTTAG, -1, "objecttag", GSF_XML_CONTENT, NULL, NULL),
1301 GSF_XML_IN_NODE (PAGE, ANNOTATION, -1, "annotation", GSF_XML_CONTENT, NULL, NULL),
1302 GSF_XML_IN_NODE (PAGE, RLOGIC, -1, "rlogic", GSF_XML_CONTENT, NULL, NULL),
1303 GSF_XML_IN_NODE (CDXML, TEMPLATEGRID, -1, "templategrid", GSF_XML_CONTENT, NULL, NULL),
1304 GSF_XML_IN_NODE_END
1305 };
1306
Read(Document * doc,GsfInput * in,G_GNUC_UNUSED char const * mime_type,GOIOContext * io)1307 ContentType CDXMLLoader::Read (Document *doc, GsfInput *in, G_GNUC_UNUSED char const *mime_type, GOIOContext *io)
1308 {
1309 CDXMLReadState state;
1310
1311 state.doc = doc;
1312 state.app = doc->GetApplication ();
1313 state.context = io;
1314 ContentType success = ContentTypeUnknown;
1315 state.colors.push_back ("red=\"0\" green=\"0\" blue=\"0\""); // black
1316 state.colors.push_back ("red=\"1\" green=\"1\" blue=\"1\""); // white
1317 state.font = 0;
1318 state.color = 0;
1319 state.theme = NULL;
1320 state.labelFont = -1;
1321 state.captionFont = -1;
1322
1323 if (NULL != in) {
1324 GsfXMLInDoc *xml = gsf_xml_in_doc_new (cdxml_dtd, NULL);
1325 if (gsf_xml_in_doc_parse (xml, in, &state))
1326 success = ContentType2D;
1327
1328 if (success == ContentTypeUnknown)
1329 go_io_warning (state.context,
1330 _("'%s' is corrupt!"),
1331 gsf_input_name (in));
1332 else if (!state.failed.empty ()) {
1333 CDXMLProps p;
1334 Object *parent = NULL;
1335 while (!state.failed.empty ()) {
1336 p = state.failed.front ();
1337 if (parent != p.obj->GetParent ()) {
1338 if (parent)
1339 parent->OnLoaded ();
1340 parent = p.obj->GetParent ();
1341 }
1342 if (!p.obj->SetProperty (p.property, p.value.c_str ())) {
1343 success = ContentTypeUnknown;
1344 go_io_warning (state.context,
1345 _("'%s' is corrupt!"),
1346 gsf_input_name (in));
1347 }
1348 state.failed.pop_front ();
1349 }
1350 if (parent)
1351 parent->OnLoaded ();
1352 }
1353 gsf_xml_in_doc_free (xml);
1354 }
1355 // Update the view so that the positions are correct
1356 static_cast <gcp::Document *> (doc)->GetView ()->Update (doc);
1357 // now build schemes
1358 std::list < SchemeData >::iterator i, iend = state.schemes.end ();
1359 for (i = state.schemes.begin (); i != iend; i++) {
1360 if ((*i).Steps.empty ())
1361 continue;
1362 build_scheme (state, *i);
1363 }
1364 return success;
1365 }
1366
1367 ////////////////////////////////////////////////////////////////////////////////
1368 // Writing code
1369
1370 static bool start = true;
cb_xml_to_vfs(GsfOutput * output,const guint8 * buf,int nb)1371 static int cb_xml_to_vfs (GsfOutput *output, const guint8* buf, int nb)
1372 {
1373 if (start) {
1374 char const *end = strchr (reinterpret_cast <char const *> (buf), '\n');
1375 gsf_output_write (output, 40, reinterpret_cast <guint8 const *> ("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"));
1376 gsf_output_write (output, 70, reinterpret_cast <guint8 const *> ("<!DOCTYPE CDXML SYSTEM \"http://www.cambridgesoft.com/xml/cdxml.dtd\" >\n"));
1377 start = false;
1378 return gsf_output_write (output, strlen (end) - 1, reinterpret_cast <guint8 const *> (end + 1))? nb: 0;
1379 } else
1380 return gsf_output_write (output, nb, buf)? nb: 0;
1381 }
1382
WriteArrow(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,GOIOContext * s)1383 bool CDXMLLoader::WriteArrow (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s)
1384 {
1385 std::map <std::string, Object *>::const_iterator i;
1386 gcu::Object const *child = obj->GetFirstChild (i);
1387 while (child) {
1388 if (!loader->WriteObject (xml, parent, child, s))
1389 return false;
1390 child = obj->GetNextChild (i);
1391 }
1392 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const * > ("graphic"), NULL);
1393 xmlAddChild (parent, node);
1394 loader->m_SavedIds[obj->GetId ()] = loader->m_MaxId;
1395 AddIntProperty (node, "id", loader->m_MaxId++);
1396 std::istringstream in (obj->GetProperty (GCU_PROP_ARROW_COORDS));
1397 double x0, y0, x1, y1;
1398 in >> x0 >> y0 >> x1 >> y1;
1399 std::ostringstream out;
1400 out << x1 << " " << y1 << " " << x0 << " " << y0;
1401 AddStringProperty (node, "BoundingBox", out.str ());
1402 AddIntProperty (node, "Z", loader->m_Z++);
1403 AddStringProperty (node, "GraphicType", "Line");
1404 std::string type = gcu::Object::GetTypeName (obj->GetType ());
1405 if (type == "reaction-arrow")
1406 AddStringProperty (node, "ArrowType", (obj->GetProperty (GCU_PROP_REACTION_ARROW_TYPE) == "double")? "Equilibrium": "FullHead");
1407 else if (type == "mesomery-arrow")
1408 AddStringProperty (node, "ArrowType", "Resonance");
1409 else if (type == "retrosynthesis-arrow")
1410 AddStringProperty (node, "ArrowType", "RetroSynthetic");
1411 return true;
1412 }
1413
WriteScheme(xmlDocPtr xml,xmlNodePtr parent,Object const * obj,std::string const & arrow_type,GOIOContext * s)1414 bool CDXMLLoader::WriteScheme (xmlDocPtr xml, xmlNodePtr parent, Object const *obj, std::string const &arrow_type, GOIOContext *s)
1415 {
1416 std::map <std::string, Object *>::const_iterator i;
1417 gcu::Object const *child = obj->GetFirstChild (i);
1418 std::list < gcu::Object const * > arrows;
1419 std::list < gcu::Object const * >::const_iterator it, itend;
1420 while (child) {
1421 std::string name = Object::GetTypeName (child->GetType ());
1422 if (name == arrow_type)
1423 arrows.push_back (child);
1424 else if (!WriteObject (xml, parent, child, s))
1425 return false;
1426 child = obj->GetNextChild (i);
1427 }
1428 itend = arrows.end ();
1429 for (it = arrows.begin (); it != itend; it++)
1430 // save the graphic object.
1431 if (!WriteArrow (this, xml, parent, *it, s))
1432 return false;
1433 if (!m_WriteScheme)
1434 return true;
1435 // Now, save the scheme
1436 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const * > ("scheme"), NULL);
1437 xmlAddChild (parent, node);
1438 AddIntProperty (node, "id", m_MaxId++);
1439 for (it = arrows.begin (); it != itend; it++) {
1440 // save the associated step
1441 xmlNodePtr step = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const * > ("step"), NULL);
1442 xmlAddChild (node, step);
1443 AddIntProperty (step, "id", m_MaxId++);
1444 // reactants
1445 gcu::Object const *arrow = *it;
1446 gcu::Object const *cur = obj->GetDescendant (arrow->GetProperty (GCU_PROP_ARROW_START_ID).c_str ());
1447 if (cur) {
1448 child = cur->GetFirstChild (i);
1449 if (child) {
1450 std::ostringstream out;
1451 out << m_SavedIds[child->GetId ()];
1452 AddStringProperty (step, "ReactionStepReactants", out.str ());
1453 }
1454 }
1455 // products
1456 cur = obj->GetDescendant (arrow->GetProperty (GCU_PROP_ARROW_END_ID).c_str ());
1457 if (cur) {
1458 child = cur->GetFirstChild (i);
1459 if (child) {
1460 std::ostringstream out;
1461 out << m_SavedIds[child->GetId ()];
1462 AddStringProperty (step, "ReactionStepProducts", out.str ());
1463 }
1464 }
1465 // arrow
1466 AddIntProperty (step, "ReactionStepArrows", m_SavedIds[arrow->GetId ()]);
1467 }
1468 return true;
1469 }
1470
WriteMesomery(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,GOIOContext * s)1471 bool CDXMLLoader::WriteMesomery (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s)
1472 {
1473 return loader->WriteScheme (xml, parent, obj, "mesomery-arrow", s);
1474 }
1475
WriteReactionStep(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,GOIOContext * s)1476 bool CDXMLLoader::WriteReactionStep(CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s)
1477 {
1478 std::map <std::string, Object *>::const_iterator i;
1479 gcu::Object const *child = obj->GetFirstChild (i);
1480 while (child) {
1481 std::string name = Object::GetTypeName (child->GetType ());
1482 if (name == "reaction-operator") {
1483 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const * > ("graphic"), NULL);
1484 xmlAddChild (parent, node);
1485 loader->m_SavedIds[obj->GetId ()] = loader->m_MaxId;
1486 AddIntProperty (node, "id", loader->m_MaxId++);
1487 // Write the bounding box
1488 std::istringstream str(child->GetProperty (GCU_PROP_POS2D));
1489 double x, y;
1490 str >> x >> y;
1491 x -= loader->m_FontSize / 3;
1492 y += loader->m_CHeight + loader->m_FontSize / 2;
1493 std::ostringstream out;
1494 out << x << " " << y - loader->m_FontSize << " " << x << " " << y;
1495 AddStringProperty (node, "BoundingBox", out.str ());
1496 AddIntProperty (node, "Z", loader->m_Z++);
1497 AddStringProperty (node, "GraphicType", "Symbol");
1498 AddStringProperty (node, "SymbolType", "Plus");
1499 } else {
1500 // search for a mesomery inside the reaction
1501 std::string id = child->GetProperty (GCU_PROP_MOLECULE);
1502 gcu::Object *mol = child->GetChild (id.c_str ());
1503 if (gcu::Object::GetTypeName (mol->GetType ()) == "mesomery")
1504 loader->m_WriteScheme = false;
1505 if (!loader->WriteObject (xml, parent, child, s))
1506 return false;
1507 }
1508 child = obj->GetNextChild (i);
1509 }
1510 return true;
1511 }
1512
WriteReaction(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,GOIOContext * s)1513 bool CDXMLLoader::WriteReaction (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s)
1514 {
1515 std::map <std::string, Object *>::const_iterator i;
1516 gcu::Object const *child = obj->GetFirstChild (i);
1517 std::list < gcu::Object const * > arrows;
1518 std::list < gcu::Object const * >::const_iterator it, itend;
1519 bool ok = true;
1520 while (child) {
1521 std::string name = Object::GetTypeName (child->GetType ());
1522 if (name == "reaction-step") {
1523 ok &= loader->WriteReactionStep (loader, xml, parent, child, s);
1524 } else if (name == "reaction-arrow")
1525 arrows.push_back (child);
1526 child = obj->GetNextChild (i);
1527 }
1528 if (!ok)
1529 return false;
1530 itend = arrows.end ();
1531 for (it = arrows.begin (); it != itend; it++)
1532 // save the graphic object.
1533 if (!WriteArrow (loader, xml, parent, *it, s))
1534 return false;
1535 // Now, save the reaction
1536 if (loader->m_WriteScheme) {
1537 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const * > ("scheme"), NULL);
1538 xmlAddChild (parent, node);
1539 AddIntProperty (node, "id", loader->m_MaxId++);
1540 for (it = arrows.begin (); it != itend; it++) {
1541 // save the associated step
1542 xmlNodePtr step = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const * > ("step"), NULL);
1543 xmlAddChild (node, step);
1544 AddIntProperty (step, "id", loader->m_MaxId++);
1545 std::list < unsigned > Ids, Ids_;
1546 // FIXME what happens if the reaction step contains a mesomery?
1547 // reactants
1548 gcu::Object const *arrow = *it;
1549 gcu::Object const *cur = obj->GetDescendant (arrow->GetProperty (GCU_PROP_ARROW_START_ID).c_str ());
1550 if (cur) {
1551 child = cur->GetFirstChild (i);
1552 while (child) {
1553 if (child->GetType () == gcu::ReactantType)
1554 Ids.push_back (loader->m_SavedIds[child->GetProperty (GCU_PROP_MOLECULE)]);
1555 child = cur->GetNextChild (i);
1556 }
1557 if (!Ids.empty ()) {
1558 std::ostringstream out;
1559 out << Ids.front ();
1560 Ids.pop_front ();
1561 while (!Ids.empty ()) {
1562 out << " " << Ids.front ();
1563 Ids.pop_front ();
1564 }
1565 AddStringProperty (step, "ReactionStepReactants", out.str ());
1566 }
1567 }
1568 // products
1569 cur = obj->GetDescendant (arrow->GetProperty (GCU_PROP_ARROW_END_ID).c_str ());
1570 if (cur) {
1571 child = cur->GetFirstChild (i);
1572 while (child) {
1573 if (child->GetType () == gcu::ReactantType)
1574 Ids.push_back (loader->m_SavedIds[child->GetProperty (GCU_PROP_MOLECULE)]);
1575 child = cur->GetNextChild (i);
1576 }
1577 if (!Ids.empty ()) {
1578 std::ostringstream out;
1579 out << Ids.front ();
1580 Ids.pop_front ();
1581 while (!Ids.empty ()) {
1582 out << " " << Ids.front ();
1583 Ids.pop_front ();
1584 }
1585 AddStringProperty (step, "ReactionStepProducts", out.str ());
1586 }
1587 }
1588 // arrow
1589 AddIntProperty (step, "ReactionStepArrows", loader->m_SavedIds[arrow->GetId ()]);
1590 // attached objects
1591 child = arrow->GetFirstChild (i);
1592 double y = const_cast < gcu::Object * > (arrow)->GetYAlign ();
1593 while (child) {
1594 if (y > const_cast < gcu::Object * > (child)->GetYAlign ())
1595 Ids_.push_back (loader->m_SavedIds[child->GetProperty (GCU_PROP_ARROW_OBJECT)]);
1596 else
1597 Ids.push_back (loader->m_SavedIds[child->GetProperty (GCU_PROP_ARROW_OBJECT)]);
1598 child = arrow->GetNextChild (i);
1599 }
1600 // objects above the arrow
1601 if (!Ids.empty ()) {
1602 std::ostringstream out;
1603 out << Ids.front ();
1604 Ids.pop_front ();
1605 while (!Ids.empty ()) {
1606 out << " " << Ids.front ();
1607 Ids.pop_front ();
1608 }
1609 AddStringProperty (step, "ReactionStepObjectsAboveArrow", out.str ());
1610 }
1611 // objects below the arrow
1612 if (!Ids_.empty ()) {
1613 std::ostringstream out;
1614 out << Ids_.front ();
1615 Ids_.pop_front ();
1616 while (!Ids_.empty ()) {
1617 out << " " << Ids_.front ();
1618 Ids_.pop_front ();
1619 }
1620 AddStringProperty (step, "ReactionStepObjectsBelowArrow", out.str ());
1621 }
1622 }
1623 }
1624 loader->m_WriteScheme = true;
1625 return true;
1626 }
1627
WriteRetrosynthesis(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,GOIOContext * s)1628 bool CDXMLLoader::WriteRetrosynthesis (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s)
1629 {
1630 return loader->WriteScheme (xml, parent, obj, "retrosynthesis-arrow", s);
1631 }
1632
WriteAtom(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,G_GNUC_UNUSED GOIOContext * s)1633 bool CDXMLLoader::WriteAtom (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, G_GNUC_UNUSED GOIOContext *s)
1634 {
1635 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("n"), NULL);
1636 xmlAddChild (parent, node);
1637 loader->m_SavedIds[obj->GetId ()] = loader->m_MaxId;
1638 AddIntProperty (node, "id", loader->m_MaxId++);
1639 string prop = obj->GetProperty (GCU_PROP_POS2D);
1640 AddStringProperty (node, "p", prop);
1641 AddIntProperty (node, "Z", loader->m_Z++);
1642 prop = obj->GetProperty (GCU_PROP_ATOM_Z);
1643 if (prop != "6")
1644 AddStringProperty (node, "Element", prop);
1645 prop = obj->GetProperty (GCU_PROP_TEXT_TEXT);
1646 if (prop.length () > 0) {
1647 xmlNodePtr text = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("t"), NULL);
1648 xmlAddChild (node, text);
1649 string prop2 = obj->GetProperty (GCU_PROP_TEXT_POSITION);
1650 AddStringProperty (text, "p", prop2);
1651 AddStringProperty (text, "LabelJustification", "Left");
1652 AddStringProperty (text, "LabelAlignment", "Left");
1653 xmlNodePtr sub = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("s"), NULL);
1654 xmlAddChild (text, sub);
1655 AddIntProperty (sub, "font", loader->m_LabelFont);
1656 AddIntProperty (sub, "face", loader->m_LabelFontFace);
1657 AddIntProperty (sub, "size", loader->m_LabelFontSize);
1658 AddIntProperty (sub, "color", loader->m_LabelFontColor);
1659 xmlNodeAddContent (sub, reinterpret_cast <xmlChar const *> (prop.c_str ()));
1660 }
1661 return true;
1662 }
1663
WriteFragment(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,G_GNUC_UNUSED GOIOContext * s)1664 bool CDXMLLoader::WriteFragment (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, G_GNUC_UNUSED GOIOContext *s)
1665 {
1666 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("n"), NULL);
1667 xmlAddChild (parent, node);
1668 loader->m_SavedIds[obj->GetId ()] = loader->m_MaxId;
1669 std::string prop = obj->GetProperty (GCU_PROP_FRAGMENT_ATOM_ID);
1670 gcu::Object *atom = obj->GetChild (prop.c_str ());
1671 loader->m_SavedIds[atom->GetId ()] = loader->m_MaxId;
1672 AddIntProperty (node, "id", loader->m_MaxId++);
1673 prop = obj->GetProperty (GCU_PROP_POS2D);
1674 AddStringProperty (node, "p", prop);
1675 AddIntProperty (node, "Z", loader->m_Z++);
1676 AddStringProperty (node, "NodeType", "Fragment");
1677 prop = obj->GetProperty (GCU_PROP_TEXT_TEXT);
1678 std::string pos = obj->GetProperty (GCU_PROP_FRAGMENT_ATOM_START);
1679 unsigned as = atoi (pos.c_str ());
1680 if (as > 0) {
1681 char const *symbol = static_cast < gcu::Atom * > (atom)->GetSymbol ();
1682 unsigned ae = as + strlen (symbol);
1683 if (ae < prop.length () - 1) {
1684 // attachment point is in the middle of the string, we need to bring it to start,
1685 // and add put the left part inside brackets
1686 std::string left = prop.substr (0, as), right = prop.substr (ae);
1687 prop = symbol;
1688 prop += "(";
1689 gcu::Formula *formula = new gcu::Formula (left, GCU_FORMULA_PARSE_RESIDUE);
1690 std::list < FormulaElt * > const &elts = formula->GetElements ();
1691 std::list< FormulaElt * >::const_reverse_iterator i, end = elts.rend ();
1692 for (i = elts.rbegin (); i!= end; i++)
1693 prop += (*i)->Text ();
1694 prop += ")";
1695 prop += right;
1696 delete formula;
1697 } else {
1698 // atom is at end, we need to revert the formula
1699 gcu::Formula *formula = new gcu::Formula (prop, GCU_FORMULA_PARSE_RESIDUE);
1700 std::list < FormulaElt * > const &elts = formula->GetElements ();
1701 prop.clear ();
1702 std::list< FormulaElt * >::const_reverse_iterator i, end = elts.rend ();
1703 for (i = elts.rbegin (); i!= end; i++)
1704 prop += (*i)->Text ();
1705 delete formula;
1706 }
1707 }
1708 if (prop.length () > 0) {
1709 xmlNodePtr text = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("t"), NULL);
1710 xmlAddChild (node, text);
1711 string prop2 = obj->GetProperty (GCU_PROP_TEXT_POSITION);
1712 AddStringProperty (text, "p", prop2);
1713 AddStringProperty (text, "LabelJustification", "Left");
1714 AddStringProperty (text, "LabelAlignment", "Left");
1715 xmlNodePtr sub = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("s"), NULL);
1716 xmlAddChild (text, sub);
1717 AddIntProperty (sub, "font", loader->m_LabelFont);
1718 AddIntProperty (sub, "face", loader->m_LabelFontFace);
1719 AddIntProperty (sub, "size", loader->m_LabelFontSize);
1720 AddIntProperty (sub, "color", loader->m_LabelFontColor);
1721 xmlNodeAddContent (sub, reinterpret_cast <xmlChar const *> (prop.c_str ()));
1722 }
1723 return true;
1724 }
1725
WriteBond(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,G_GNUC_UNUSED GOIOContext * s)1726 bool CDXMLLoader::WriteBond (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, G_GNUC_UNUSED GOIOContext *s)
1727 {
1728 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("b"), NULL);
1729 xmlAddChild (parent, node);
1730 loader->m_SavedIds[obj->GetId ()] = loader->m_MaxId;
1731 AddIntProperty (node, "id", loader->m_MaxId++);
1732 AddIntProperty (node, "Z", loader->m_Z++);
1733 string prop = obj->GetProperty (GCU_PROP_BOND_BEGIN);
1734 AddIntProperty (node, "B", loader->m_SavedIds[prop]);
1735 prop = obj->GetProperty (GCU_PROP_BOND_END);
1736 AddIntProperty (node, "E", loader->m_SavedIds[prop]);
1737 prop = obj->GetProperty (GCU_PROP_BOND_ORDER);
1738 if (prop == "3")
1739 prop = "4";
1740 else if (prop != "2")
1741 prop.clear ();
1742 if (prop.length ())
1743 AddStringProperty (node, "Order", prop);
1744 prop = obj->GetProperty (GCU_PROP_BOND_TYPE);
1745 if (prop == "wedge")
1746 prop = "WedgeBegin";
1747 else if (prop == "hash")
1748 prop = "WedgedHashBegin";
1749 else if (prop == "squiggle")
1750 prop = "Wavy";
1751 else if (prop == "large")
1752 prop = "Bold";
1753 else
1754 prop.clear ();
1755 if (prop.length ())
1756 AddStringProperty (node, "Display", prop);
1757 prop = obj->GetProperty (GCU_PROP_BOND_DOUBLE_POSITION);
1758 if (prop == "center")
1759 AddStringProperty (node, "DoublePosition", "Center");
1760 else if (prop == "right")
1761 AddStringProperty (node, "DoublePosition", "Right");
1762 else if (prop == "left")
1763 AddStringProperty (node, "DoublePosition", "Left");
1764 return true;
1765 }
1766
WriteMolecule(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,GOIOContext * s)1767 bool CDXMLLoader::WriteMolecule (CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s)
1768 {
1769 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("fragment"), NULL);
1770 xmlAddChild (parent, node);
1771 loader->m_SavedIds[obj->GetId ()] = loader->m_MaxId;
1772 AddIntProperty (node, "id", loader->m_MaxId++);
1773 // save atoms
1774 std::map <std::string, Object *>::const_iterator i;
1775 Object const *child = obj->GetFirstChild (i);
1776 while (child) {
1777 if (child->GetType () == AtomType && !loader->WriteObject (xml, node, child, s))
1778 return false;
1779 child = obj->GetNextChild (i);
1780 }
1781 // save fragments
1782 child = obj->GetFirstChild (i);
1783 while (child) {
1784 if (child->GetType () == FragmentType && !loader->WriteObject (xml, node, child, s))
1785 return false;
1786 child = obj->GetNextChild (i);
1787 }
1788 // save bonds
1789 child = obj->GetFirstChild (i);
1790 while (child) {
1791 if (child->GetType () == BondType && !loader->WriteObject (xml, node, child, s))
1792 return false;
1793 child = obj->GetNextChild (i);
1794 }
1795 return true;
1796 }
1797
WriteNode(xmlNodePtr node,WriteTextState * state)1798 bool CDXMLLoader::WriteNode (xmlNodePtr node, WriteTextState *state)
1799 {
1800 std::string name (reinterpret_cast < char const * > (node->name));
1801 if (name == "br" || name == "text") {
1802 if (state->node == NULL) {
1803 state->node = xmlNewDocNode (state->xml, NULL, reinterpret_cast < xmlChar const * > ("s"),
1804 (name == "br")? reinterpret_cast < xmlChar const * > ("s\n"): xmlNodeGetContent (node));
1805 xmlAddChild (state->parent, state->node);
1806 AddIntProperty (state->node, "font", state->font);
1807 AddIntProperty (state->node, "size", state->size);
1808 AddIntProperty (state->node, "color", state->color);
1809 unsigned font_type = 0;
1810 if (state->bold)
1811 font_type |= 1;
1812 if (state->italic)
1813 font_type |= 2;
1814 if (state->underline)
1815 font_type |= 4;
1816 switch (state->position) {
1817 case -1:
1818 font_type |= 0x20;
1819 break;
1820 case 1:
1821 font_type |= 0x40;
1822 break;
1823 default:
1824 break;
1825 }
1826 if (font_type != 0)
1827 AddIntProperty (state->node, "face", font_type);
1828 } else
1829 xmlNodeAddContent (state->node, (name == "br")? reinterpret_cast < xmlChar const * > ("\n"): xmlNodeGetContent (node));
1830 return true;
1831 } else
1832 state->node = NULL;
1833 WriteTextState child_state = *state;
1834 if (name == "i")
1835 child_state.italic = true;
1836 else if (name == "b")
1837 child_state.bold = true;
1838 else if (name == "u")
1839 child_state.underline = true;
1840 else if (name == "font") {
1841 xmlChar *buf = xmlGetProp (node, reinterpret_cast < xmlChar const * > ("name"));
1842 PangoFontDescription *desc = pango_font_description_from_string (reinterpret_cast < char * > (buf));
1843 xmlFree (buf);
1844 child_state.size = static_cast < double > (pango_font_description_get_size (desc)) / PANGO_SCALE;
1845 std::string family = pango_font_description_get_family (desc);
1846 if (family == "Arial")
1847 child_state.font = 3;
1848 else if (family == "Times New Roman")
1849 child_state.font = 4;
1850 else {
1851 guint16 i = 5;
1852 std::map < unsigned, CDXMLFont >::iterator it, itend = m_Fonts.end ();
1853 for (it = m_Fonts.find (i); it != itend; it++, i++)
1854 if (family == (*it).second.name)
1855 break;
1856 if (it == itend)
1857 m_Fonts[i] = (CDXMLFont) {i, "iso-10646", family};
1858 child_state.font = i;
1859 }
1860 } else if (name == "sub")
1861 child_state.position = -1;
1862 else if (name == "sup")
1863 child_state.position = 1;
1864 else if (name == "fore") {
1865 GOColor color = ReadColor (node);
1866 guint i = 2;
1867 std::map < unsigned, GOColor >::iterator it, itend = m_Colors.end ();
1868 for (it = m_Colors.find (i); it != itend; it++, i++)
1869 if (color == (*it).second)
1870 break;
1871 if (it == itend)
1872 m_Colors[i] = color;
1873 child_state.color = i;
1874
1875 }
1876 xmlNodePtr child = node->children;
1877 while (child) {
1878 WriteNode (child, &child_state);
1879 child = child->next;
1880 }
1881 return true;
1882 }
1883
WriteText(CDXMLLoader * loader,xmlDocPtr xml,xmlNodePtr parent,Object const * obj,GOIOContext * s)1884 bool CDXMLLoader::WriteText(CDXMLLoader *loader, xmlDocPtr xml, xmlNodePtr parent, Object const *obj, GOIOContext *s)
1885 {
1886 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("t"), NULL);
1887 xmlAddChild (parent, node);
1888 loader->m_SavedIds[obj->GetId ()] = loader->m_MaxId;
1889 AddIntProperty (node, "id", loader->m_MaxId++);
1890 std::string prop = obj->GetProperty (GCU_PROP_POS2D);
1891 if (prop.length ()) {
1892 istringstream str (prop);
1893 double x, y;
1894 str >> x >> y;
1895 y += loader->m_CHeight;
1896 std::ostringstream out;
1897 out << x << " " << y;
1898 AddStringProperty (node, "p", out.str ());
1899 }
1900 AddIntProperty (node, "Z", loader->m_Z++);
1901 // no need to export left justification or alignment.
1902 prop = obj->GetProperty (GCU_PROP_TEXT_ALIGNMENT);
1903 if (prop == "right")
1904 AddStringProperty (node, "Justification", "Right");
1905 else if (prop == "center")
1906 AddStringProperty (node, "Justification", "Center");
1907 prop = obj->GetProperty (GCU_PROP_TEXT_JUSTIFICATION);
1908 if (prop == "right")
1909 AddStringProperty (node, "CaptionJustification", "Right");
1910 else if (prop == "center")
1911 AddStringProperty (node, "CaptionJustification", "Center");
1912 else if (prop == "justify")
1913 AddStringProperty (node, "CaptionJustification", "Full");
1914 double inl;
1915 std::istringstream in (obj->GetProperty (GCU_PROP_TEXT_INTERLINE));
1916 in >> inl;
1917 if (inl <= 0.) {
1918 prop = obj->GetProperty (GCU_PROP_TEXT_VARIABLE_LINE_HEIGHT);
1919 AddStringProperty (node, "CaptionLineHeight", (prop =="true")? "variable": "auto");
1920 } else {
1921 std::istringstream in (obj->GetProperty (GCU_PROP_TEXT_MAX_LINE_HEIGHT));
1922 double lh;
1923 in >> lh;
1924 AddIntProperty (node, "CaptionLineHeight", inl + lh);
1925 }
1926 prop = obj->GetProperty (GCU_PROP_TEXT_MARKUP);
1927 xmlDocPtr doc = xmlParseMemory (prop.c_str(), prop.length ());
1928 xmlNodePtr text_node = doc->children->children;
1929 WriteTextState state;
1930 state.xml = xml;
1931 state.node = NULL;
1932 state.parent = node;
1933 state.s = s;
1934 state.italic = false;
1935 state.bold = false;
1936 state.underline = false;
1937 state.font = 3;
1938 state.size = 10;
1939 state.position = 0;
1940 state.color = 3;
1941 while (text_node) {
1942 if (strcmp (reinterpret_cast < char const *>(text_node->name), "position"))
1943 loader->WriteNode (text_node, &state);
1944 text_node = text_node->next;
1945 }
1946 xmlFreeDoc (doc);
1947 return true;
1948 }
1949
WriteObject(xmlDocPtr xml,xmlNodePtr node,Object const * object,GOIOContext * io)1950 bool CDXMLLoader::WriteObject (xmlDocPtr xml, xmlNodePtr node, Object const *object, GOIOContext *io)
1951 {
1952 string name = Object::GetTypeName (object->GetType ());
1953 map <string, bool (*) (CDXMLLoader *, xmlDocPtr, xmlNodePtr, Object const *, GOIOContext *)>::iterator i = m_WriteCallbacks.find (name);
1954 if (i != m_WriteCallbacks.end ())
1955 return (*i).second (this, xml, node, object, io);
1956 // if we don't save the object iself, try to save its children
1957 std::map <std::string, Object *>::const_iterator j;
1958 Object const *child = object->GetFirstChild (j);
1959 while (child) {
1960 if (!WriteObject (xml, node, child, io))
1961 return false;
1962 child = object->GetNextChild (j);
1963 }
1964 return true; /* loosing data is not considered an error, it is just a missing feature
1965 either in this code or in the cml schema */
1966 }
1967
AddIntProperty(xmlNodePtr node,char const * id,int value)1968 void CDXMLLoader::AddIntProperty (xmlNodePtr node, char const *id, int value)
1969 {
1970 char *buf = g_strdup_printf ("%d", value);
1971 xmlNewProp (node, reinterpret_cast <xmlChar const *> (id), reinterpret_cast <xmlChar *> (buf));
1972 g_free (buf);
1973 }
AddFloatProperty(xmlNodePtr node,char const * id,double value)1974 void CDXMLLoader::AddFloatProperty (xmlNodePtr node, char const *id, double value)
1975 {
1976 std::ostringstream s;
1977 s << value;
1978 xmlNewProp (node, reinterpret_cast <xmlChar const *> (id), reinterpret_cast <xmlChar const *> (s.str ().c_str ()));
1979 }
1980
AddStringProperty(xmlNodePtr node,char const * id,string const & value)1981 void CDXMLLoader::AddStringProperty (xmlNodePtr node, char const *id, string const &value)
1982 {
1983 xmlNewProp (node, reinterpret_cast <xmlChar const *> (id), reinterpret_cast <xmlChar const *> (value.c_str ()));
1984 }
1985
Write(Object const * obj,GsfOutput * out,G_GNUC_UNUSED char const * mime_type,G_GNUC_UNUSED GOIOContext * io,G_GNUC_UNUSED ContentType type)1986 bool CDXMLLoader::Write (Object const *obj, GsfOutput *out, G_GNUC_UNUSED char const *mime_type, G_GNUC_UNUSED GOIOContext *io, G_GNUC_UNUSED ContentType type)
1987 {
1988 map<string, CDXMLFont> fonts;
1989 // FIXME: should be able to export a molecule or a reaction
1990 Document const *doc = dynamic_cast <Document const *> (obj);
1991 xmlNodePtr colors, fonttable, page;
1992 if (!doc || !out)
1993 return false;
1994
1995 m_MaxId = m_Z = 1;
1996
1997 // Init default colors
1998 m_Colors[2] = GO_COLOR_WHITE;
1999 m_Colors[3] = GO_COLOR_BLACK;
2000 m_Colors[4] = GO_COLOR_RED;
2001 m_Colors[5] = GO_COLOR_YELLOW;
2002 m_Colors[6] = GO_COLOR_GREEN;
2003 m_Colors[7] = GO_COLOR_CYAN;
2004 m_Colors[8] = GO_COLOR_BLUE;
2005 m_Colors[9] = GO_COLOR_VIOLET;
2006
2007 // Init fonts, we always use Unknown as the charset, hoping it is not an issue
2008 m_Fonts[3] = (CDXMLFont) {3, string ("iso-8859-1"), string ("Arial")};
2009 m_Fonts[4] = (CDXMLFont) {4, string ("iso-8859-1"), string ("Times New Roman")};
2010 m_LabelFont = 3;
2011 m_LabelFontSize = 10.;
2012
2013 /* we can't use sax, because we need colors and fonts */
2014 xmlDocPtr xml = xmlNewDoc (reinterpret_cast <xmlChar const *> ("1.0"));
2015 xmlDocSetRootElement (xml, xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("CDXML"), NULL));
2016 std::string str = doc->GetApp ()->GetName () + " " VERSION;
2017 xmlNewProp (xml->children, reinterpret_cast <xmlChar const *> ("CreationProgram"),
2018 reinterpret_cast <xmlChar const *> (str.c_str ()));
2019 // Get the theme (we need a gcp::Document there)
2020 gcp::Document const *cpDoc = dynamic_cast < gcp::Document const * > (doc);
2021 gcp::Theme const *theme = cpDoc->GetTheme ();
2022 m_Zoom = 1. / theme->GetZoomFactor();
2023 m_Scale = .75 / m_Zoom;
2024 m_CHeight = const_cast < gcp::Document * > (cpDoc)->GetView ()->GetCHeight ();
2025 // save title authors and the like
2026 str = doc->GetProperty (GCU_PROP_DOC_CREATOR);
2027 if (str.length () > 0)
2028 AddStringProperty (xml->children, "CreationUserName", str);
2029 /* Don't write dates for now since the needed format is unknown
2030 str = doc->GetProperty (GCU_PROP_DOC_CREATION_TIME);
2031 unsigned d, m, Y;
2032 if (sscanf (str.c_str (), "%u-%u-%u", &Y,&m, &d) == 3) {
2033 GDateTime *date = g_date_time_new_utc (Y, m, d, 0, 0, 0.);
2034 AddDateProperty (out, "CreationDate", date);
2035 g_date_time_unref (date);
2036 }
2037 GDateTime *dt = g_date_time_new_now_utc ();
2038 AddDateProperty (out, "ModificationDate", dt);
2039 g_date_time_unref (dt);
2040 */
2041 str = doc->GetProperty (GCU_PROP_DOC_TITLE);
2042 if (str.length () > 0)
2043 AddStringProperty (xml->children, "Name", str);
2044 str = doc->GetProperty (GCU_PROP_DOC_COMMENT);
2045 if (str.length () > 0)
2046 AddStringProperty (xml->children, "Comment", str);
2047 // determine the bond length and scale the document appropriately
2048 const_cast <Document *> (doc)->SetScale (1. / m_Scale);
2049 double l = theme->GetBondLength () * m_Scale;
2050 AddFloatProperty (xml->children, "BondLength", l);
2051 int n = theme->GetBondDist () * 100. * m_Zoom / theme->GetBondLength ();
2052 AddIntProperty (xml->children, "BondSpacing", n);
2053 l = theme->GetBondWidth () * .75;
2054 AddFloatProperty (xml->children, "LineWidth", l);
2055 l = theme->GetStereoBondWidth () * .75;
2056 AddFloatProperty (xml->children, "BoldWidth", l);
2057 l = theme->GetHashDist () * .75;
2058 AddFloatProperty (xml->children, "HashSpacing", l);
2059 AddFloatProperty (xml->children, "ChainAngle", theme->GetBondAngle ());
2060 l = theme->GetPadding () * .75;
2061 AddFloatProperty (xml->children, "MarginWidth", l);
2062 str = theme->GetTextFontFamily ();
2063 if (str == "Arial")
2064 n = 3;
2065 else if (str == "Times New Roman")
2066 n = 4;
2067 else {
2068 n = 5;
2069 std::map < unsigned, CDXMLFont >::iterator it, itend = m_Fonts.end ();
2070 for (it = m_Fonts.find (n); it != itend; it++, n++)
2071 if (str == (*it).second.name)
2072 break;
2073 if (it == itend)
2074 m_Fonts[n] = (CDXMLFont) {static_cast < guint16 > (n), "iso-10646", str};
2075 }
2076 AddIntProperty (xml->children, "CaptionFont", n);
2077 n = theme->GetTextFontSize () / PANGO_SCALE;
2078 m_FontSize = n * .75;
2079 AddIntProperty (xml->children, "CaptionSize", n);
2080 n = 0;
2081 if (theme->GetTextFontWeight () > PANGO_WEIGHT_NORMAL)
2082 n |= 1;
2083 if (theme->GetTextFontStyle () != PANGO_STYLE_NORMAL)
2084 n |= 2;
2085 AddIntProperty (xml->children, "CaptionFace", n);
2086 str = theme->GetFontFamily ();
2087 if (str == "Arial")
2088 m_LabelFont = 3;
2089 else if (str == "Times New Roman")
2090 m_LabelFont = 4;
2091 else {
2092 m_LabelFont = 5;
2093 std::map < unsigned, CDXMLFont >::iterator it, itend = m_Fonts.end ();
2094 for (it = m_Fonts.find (m_LabelFont); it != itend; it++, m_LabelFont++)
2095 if (str == (*it).second.name)
2096 break;
2097 if (it == itend)
2098 m_Fonts[m_LabelFont] = (CDXMLFont) {static_cast < guint16 > (m_LabelFont), "iso-10646", str};
2099 }
2100 AddIntProperty (xml->children, "LabelFont", m_LabelFont);
2101 m_LabelFontSize = theme->GetFontSize () / PANGO_SCALE;
2102 AddIntProperty (xml->children, "LabelSize", m_LabelFontSize);
2103 m_LabelFontFace = 0x60;
2104 if (theme->GetFontWeight () > PANGO_WEIGHT_NORMAL)
2105 m_LabelFontFace |= 1;
2106 if (theme->GetFontStyle () != PANGO_STYLE_NORMAL)
2107 m_LabelFontFace |= 2;
2108 AddIntProperty (xml->children, "LabelFace", m_LabelFontFace);
2109 m_LabelFontColor = 0;
2110 AddIntProperty (xml->children, "CaptionJustification", 0);
2111 // add color table
2112 colors = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("colortable"), NULL);
2113 xmlAddChild (xml->children, colors);
2114 // add font table
2115 fonttable = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("fonttable"), NULL);
2116 xmlAddChild (xml->children, fonttable);
2117 // start page
2118 page = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("page"), NULL);
2119 xmlAddChild (xml->children, page);
2120 // build tree from children
2121 std::map <std::string, Object *>::const_iterator i;
2122 Object const *child = doc->GetFirstChild (i);
2123 while (child) {
2124 if (!WriteObject (xml, page, child, io)) {
2125 xmlFreeDoc (xml);
2126 m_Colors.clear ();
2127 m_Fonts.clear ();
2128 m_SavedIds.clear ();
2129 return false;
2130 }
2131 child = doc->GetNextChild (i);
2132 }
2133 // add colors to color table
2134 map <unsigned, GOColor>::iterator color, end_color = m_Colors.end ();
2135 for (color = m_Colors.begin (); color != end_color; color++) {
2136 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("color"), NULL);
2137 xmlAddChild (colors, node);
2138 WriteFloat (node, "r", GO_COLOR_DOUBLE_R ((*color).second));
2139 WriteFloat (node, "g", GO_COLOR_DOUBLE_G ((*color).second));
2140 WriteFloat (node, "b", GO_COLOR_DOUBLE_B ((*color).second));
2141 }
2142 // write fonts
2143 map <unsigned, CDXMLFont>::iterator font, end_font = m_Fonts.end ();
2144 for (font = m_Fonts.begin (); font != end_font; font++) {
2145 xmlNodePtr node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("font"), NULL);
2146 xmlAddChild (fonttable, node);
2147 WriteInt (node, "id", (*font).second.index);
2148 AddStringProperty (node, "charset", (*font).second.encoding);
2149 AddStringProperty (node, "name", (*font).second.name);
2150 }
2151 xmlIndentTreeOutput = true;
2152 xmlKeepBlanksDefault (0);
2153 xmlOutputBufferPtr buf = xmlAllocOutputBuffer (NULL);
2154 buf->context = out;
2155 buf->closecallback = NULL;
2156 buf->writecallback = (xmlOutputWriteCallback) cb_xml_to_vfs;
2157 start = true;
2158 xmlSaveFormatFileTo (buf, xml, NULL, true);
2159 xmlFreeDoc (xml);
2160 m_Colors.clear ();
2161 m_Fonts.clear ();
2162 m_SavedIds.clear ();
2163 return true;
2164 }
2165
2166 ////////////////////////////////////////////////////////////////////////////////
2167 // Initialization
2168
2169 static CDXMLLoader loader;
2170
2171 extern "C" {
2172
2173 extern GOPluginModuleDepend const go_plugin_depends [] = {
2174 { "goffice", GOFFICE_API_VERSION }
2175 };
2176 extern GOPluginModuleHeader const go_plugin_header =
2177 { GOFFICE_MODULE_PLUGIN_MAGIC_NUMBER, G_N_ELEMENTS (go_plugin_depends) };
2178
2179 G_MODULE_EXPORT void
go_plugin_init(G_GNUC_UNUSED GOPlugin * plugin,G_GNUC_UNUSED GOCmdContext * cc)2180 go_plugin_init (G_GNUC_UNUSED GOPlugin *plugin, G_GNUC_UNUSED GOCmdContext *cc)
2181 {
2182 bindtextdomain (GETTEXT_PACKAGE, DATADIR"/locale");
2183 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2184 }
2185
2186 G_MODULE_EXPORT void
go_plugin_shutdown(G_GNUC_UNUSED GOPlugin * plugin,G_GNUC_UNUSED GOCmdContext * cc)2187 go_plugin_shutdown (G_GNUC_UNUSED GOPlugin *plugin, G_GNUC_UNUSED GOCmdContext *cc)
2188 {
2189 }
2190
2191 }
2192