1 // -*- C++ -*-
2
3 /*
4 * Gnome Chemisty Utils
5 * gcr/document.cc
6 *
7 * Copyright (C) 2002-2010 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 <cstring>
27 #include "document.h"
28 #include "application.h"
29 #include "view.h"
30 #include "celldlg.h"
31 #include "atomsdlg.h"
32 #include "linesdlg.h"
33 #include "sizedlg.h"
34 #include "cleavagesdlg.h"
35 #include "window.h"
36 #include <gcugtk/filechooser.h>
37 #include <gcu/cylinder.h>
38 #include <gcu/matrix.h>
39 #include <gcu/objprops.h>
40 #include <gcu/spacegroup.h>
41 #include <gcu/sphere.h>
42 #include <gcu/transform3d.h>
43 #include <gcu/vector.h>
44 #include <gcu/xml-utils.h>
45 #include <gtk/gtk.h>
46 #include <glib/gi18n-lib.h>
47 #include <libintl.h>
48 #include <cmath>
49 #include <set>
50 #include <vector>
51 #include <GL/gl.h>
52 #include <cstring>
53 #include <iostream>
54 #include <sstream>
55
56 #define __max(x,y) ((x) > (y)) ? (x) : (y)
57 #define __min(x,y) ((x) < (y)) ? (x) : (y)
58
59 using namespace std;
60
61 namespace gcr {
62
63 #define SAVE 1
64 #define LOAD 2
65 #define XML 3
66 #define FORMAT 4
67
68 gchar const *LatticeName[] = {
69 "simple cubic",
70 "body-centered cubic",
71 "face-centered cubic",
72 "hexagonal",
73 "tetragonal",
74 "body-centered tetragonal",
75 "orthorhombic",
76 "base-centered orthorhombic",
77 "body-centered orthorhombic",
78 "face-centered orthorhombic",
79 "rhombohedral",
80 "monoclinic",
81 "base-centered monoclinic",
82 "triclinic"
83 };
84
Document(gcu::Application * App)85 Document::Document (gcu::Application *App): gcu::GLDocument (App),
86 m_SpaceGroup (NULL), m_AutoSpaceGroup (false)
87 {
88 m_xmin = m_ymin = m_zmin = 0;
89 m_xmax = m_ymax = m_zmax = 1;
90 Init ();
91 }
92
93
~Document()94 Document::~Document()
95 {
96 g_free (m_filename);
97 Reinit ();
98 }
99
Reinit()100 void Document::Reinit()
101 {
102 //destruction of lists
103 while (!AtomDef.empty ()) {
104 delete AtomDef.front ();
105 AtomDef.pop_front ();
106 }
107 while (!Atoms.empty ()) {
108 delete Atoms.front ();
109 Atoms.pop_front ();
110 }
111 while (!LineDef.empty ()) {
112 delete LineDef.front ();
113 LineDef.pop_front ();
114 }
115 while (!Lines.empty ()) {
116 delete Lines.front ();
117 Lines.pop_front ();
118 }
119 while (!Cleavages.empty ()) {
120 delete Cleavages.front ();
121 Cleavages.pop_front ();
122 }
123 g_free (m_Author);
124 g_free (m_Mail);
125 g_free (m_Comment);
126 Init ();
127 }
128
Init()129 void Document::Init()
130 {
131 m_a = m_b = m_c = 100;
132 m_alpha = m_beta = m_gamma = 90;
133 m_lattice = cubic;
134 m_SpaceGroup = gcu::SpaceGroup::GetSpaceGroup (195);
135 m_AutoSpaceGroup = false;
136 m_xmin = m_ymin = m_zmin = 0;
137 m_xmax = m_ymax = m_zmax = 1;
138 m_FixedSize = false;
139 m_MaxDist = 0;
140 m_filename = NULL;
141 m_Label = NULL;
142 m_Author = NULL;
143 m_Mail = NULL;
144 m_Comment = NULL;
145 }
146
ParseXMLTree(xmlNode * xml)147 void Document::ParseXMLTree (xmlNode* xml)
148 {
149 char *txt;
150 xmlNodePtr node;
151 bool bViewLoaded = false;
152
153 Reinit ();
154 m_SpaceGroup = NULL;
155 //look for generator node
156 node = xml->children;
157 while(node) {
158 if (!strcmp ((gchar*) node->name, "text"));
159 else if (!strcmp ((gchar*) node->name, "lattice")) {
160 txt = (char*) xmlNodeGetContent (node);
161 int i = 0;
162 while (strcmp (txt, LatticeName[i]) && (i < 14))
163 i++;
164 if (i < 14)
165 m_lattice = (Lattice) i;
166 xmlFree (txt);
167 } else if (!strcmp ((gchar*) node->name, "group")) {
168 gcu::SpaceGroup *group = new gcu::SpaceGroup ();
169 txt = (char*) xmlGetProp (node, (xmlChar*) "Hall");
170 if (txt) {
171 group->SetHallName (txt);
172 xmlFree (txt);
173 } else {
174 txt = (char*) xmlGetProp (node, (xmlChar*) "HM");
175 if (txt) {
176 group->SetHMName (txt);
177 xmlFree (txt);
178 }
179 }
180 xmlNodePtr child = node->children;
181 while (child) {
182 if (!strcmp ((char const*) child->name, "transform")) {
183 txt = (char*) xmlNodeGetContent (child);
184 if (txt) {
185 group->AddTransform (txt);
186 xmlFree (txt);
187 }
188 }
189 child = child->next;
190 }
191 m_SpaceGroup = gcu::SpaceGroup::Find (group);
192 delete group;
193 } else if (!strcmp ((gchar*) node->name, "cell")) {
194 gcu::ReadFloat (node, "a", m_a, 100);
195 gcu::ReadFloat (node, "b", m_b, 100);
196 gcu::ReadFloat (node, "c", m_c, 100);
197 gcu::ReadFloat (node, "alpha", m_alpha, 90);
198 gcu::ReadFloat (node, "beta", m_beta, 90);
199 gcu::ReadFloat (node, "gamma", m_gamma, 90);
200 } else if (!strcmp ((gchar*) node->name, "size")) {
201 gcu::ReadPosition (node, "start", &m_xmin, &m_ymin, &m_zmin);
202 gcu::ReadPosition (node, "end", &m_xmax, &m_ymax, &m_zmax);
203 txt = (char*) xmlGetProp (node, (xmlChar*) "fixed");
204 if (txt) {
205 if (!strcmp (txt, "true"))
206 m_FixedSize = true;
207 xmlFree (txt);
208 }
209 } else if (!strcmp ((gchar*) node->name, "atom")) {
210 Atom *pAtom = CreateNewAtom ();
211 AddChild (pAtom);
212 if (!pAtom->Load (node)) {
213 AtomDef.remove (pAtom);
214 delete pAtom;
215 }
216 } else if (!strcmp ((gchar*) node->name, "line")) {
217 Line *pLine = CreateNewLine ();
218 if (pLine->Load (node))
219 LineDef.push_back (pLine);
220 else
221 delete pLine;
222 } else if (!strcmp ((gchar*) node->name, "cleavage")) {
223 Cleavage *pCleavage = CreateNewCleavage ();
224 if (pCleavage->Load (node))
225 Cleavages.push_back (pCleavage);
226 else
227 delete pCleavage;
228 } else if (!strcmp ((gchar*) node->name, "view")) {
229 if (!bViewLoaded &&! m_Views.empty ())
230 m_Views.front ()->Load (node);
231 else
232 LoadNewView (node);
233 bViewLoaded = true;
234 }
235 node = node->next;
236 }
237 SetDirty (false);
238 Update ();
239 }
240
LoadNewView(G_GNUC_UNUSED xmlNodePtr node)241 bool Document::LoadNewView (G_GNUC_UNUSED xmlNodePtr node)
242 {
243 Window *pWindow = static_cast < Application * > (GetApplication ())->CreateNewWindow (this);
244 View *pView = pWindow->GetView ();
245 bool result = pView->Load (node);
246 if (!result)
247 delete pWindow;
248 return result;
249 }
250
GetView()251 View *Document::GetView ()
252 {
253 if (m_Views.size() == 0) {
254 View* pView = CreateNewView ();
255 m_Views.push_back (pView);
256 }
257 return m_Views.front ();
258 }
259
Update()260 void Document::Update ()
261 {
262 m_Empty = AtomDef.empty () && LineDef.empty ();
263 Atom atom;
264 Line line;
265 gdouble alpha = m_alpha * M_PI / 180;
266 gdouble beta = m_beta * M_PI / 180;
267 gdouble gamma = m_gamma * M_PI / 180;
268 // Remove all atoms
269 while (!Atoms.empty ()) {
270 delete Atoms.front ();
271 Atoms.pop_front ();
272 }
273 // Remove all bonds
274 while (!Lines.empty ()) {
275 delete Lines.front ();
276 Lines.pop_front ();
277 }
278
279 // update space group
280 m_SpaceGroup = FindSpaceGroup ();
281
282 ////////////////////////////////////////////////////////////
283 //Establish list of atoms
284 AtomList::iterator i, iend = AtomDef.end ();
285
286 if (m_SpaceGroup) {
287 gcu::Vector v;
288 list <gcu::Vector> d;
289 for (i = AtomDef.begin(); i != iend; i++) {
290 v.SetX ((*i)->x ());
291 v.SetY ((*i)->y ());
292 v.SetZ ((*i)->z ());
293 d = m_SpaceGroup->Transform (v);
294 list <gcu::Vector>::iterator vi, viend = d.end();
295 Atom atom (**i);
296 for (vi=d.begin (); vi!= viend; vi++) {
297 atom.SetCoords ((*vi).GetX(), (*vi).GetY(), (*vi).GetZ());
298 Duplicate (atom);
299 }
300 }
301 } else for (i = AtomDef.begin(); i != iend; i++) {
302 Duplicate (**i);
303 switch (m_lattice) {
304 case body_centered_cubic:
305 case body_centered_tetragonal:
306 case body_centered_orthorhombic:
307 atom = **i;
308 atom.Move(0.5, 0.5, 0.5);
309 Duplicate (atom);
310 break;
311 case face_centered_cubic:
312 case face_centered_orthorhombic:
313 atom = **i;
314 atom.Move (0.5, 0, 0.5);
315 Duplicate (atom);
316 atom = **i;
317 atom.Move(0, 0.5, 0.5);
318 Duplicate (atom);
319 case base_centered_orthorhombic:
320 case base_centered_monoclinic:
321 atom = **i;
322 atom.Move (0.5, 0.5, 0);
323 Duplicate (atom);
324 break;
325 default:
326 break;
327 }
328 }
329
330 ////////////////////////////////////////////////////////////
331 //Establish list of lines
332 LineList::iterator j, jend = LineDef.end();
333 for (j = LineDef.begin() ; j != jend ; j++) {
334 switch ((*j)->Type()) {
335 case edges:
336 line = **j;
337 line.SetPosition (0 ,0, 0, 1, 0, 0);
338 Duplicate (line);
339 line.SetPosition (0 ,0, 0, 0, 1, 0);
340 Duplicate (line);
341 line.SetPosition (0 ,0, 0, 0, 0, 1);
342 Duplicate (line);
343 break ;
344 case diagonals:
345 line = **j;
346 line.SetPosition (0 ,0, 0, 1, 1, 1);
347 Duplicate (line);
348 line.SetPosition (1 ,0, 0, 0, 1, 1);
349 Duplicate (line);
350 line.SetPosition (0 ,1, 0, 1, 0, 1);
351 Duplicate (line);
352 line.SetPosition (1 ,1, 0, 0, 0, 1);
353 Duplicate (line);
354 break ;
355 case medians:
356 line = **j;
357 line.SetPosition (.5, .5, 0, .5, .5, 1);
358 Duplicate (line);
359 line.SetPosition (0, .5, .5, 1, .5, .5);
360 Duplicate (line);
361 line.SetPosition (.5, 0, .5, .5, 1, .5);
362 Duplicate (line);
363 break ;
364 case normal:
365 Duplicate (**j) ;
366 switch (m_lattice) {
367 case body_centered_cubic:
368 case body_centered_tetragonal:
369 case body_centered_orthorhombic:
370 line = **j;
371 line.Move (0.5, 0.5, 0.5);
372 Duplicate (line);
373 break;
374 case face_centered_cubic:
375 case face_centered_orthorhombic:
376 line = **j;
377 line.Move (0.5, 0, 0.5);
378 Duplicate (line);
379 line = **j;
380 line.Move (0, 0.5, 0.5);
381 Duplicate (line);
382 case base_centered_orthorhombic:
383 case base_centered_monoclinic:
384 line = **j;
385 line.Move (0.5, 0.5, 0);
386 Duplicate (line);
387 break;
388 default:
389 break;
390 }
391 break ;
392 case unique:
393 if (((*j)->Xmin() >= m_xmin) && ((*j)->Xmax() <= m_xmax)
394 && ((*j)->Ymin() >= m_ymin) && ((*j)->Ymax() <= m_ymax)
395 && ((*j)->Zmin() >= m_zmin) && ((*j)->Zmax() <= m_zmax))
396 Lines.push_back(new Line(**j)) ;
397 }
398 }
399
400 //Manage cleavages
401 CleavageList::iterator k;
402 for (k = Cleavages.begin(); k != Cleavages.end(); k++)
403 {
404 double x;
405 std::vector<double> ScalarProducts;
406 std::vector<double>::iterator m;
407 unsigned n;
408
409 // we might have invalid cleavages, so we need to skip them
410 if ((*k)->Planes () == 0 || ((*k)->h () == 0 && (*k)->k () == 0 && (*k)->l () == 0))
411 continue; // invalid cleavage
412 //scalar products calculus and storing
413 for (i = Atoms.begin(); i != Atoms.end(); i++)
414 {
415 x = (*i)->ScalProd((*k)->h(), (*k)->k(), (*k)->l());
416 for (m = ScalarProducts.begin(); m != (ScalarProducts.end()) && ((*m) > (x + PREC)); m++) ;
417 if ((m == ScalarProducts.end()) || (fabs(*m - x) > PREC)) ScalarProducts.insert(m, x);
418 }
419
420 //cleave atoms and bonds
421 if ((n = (*k)->Planes()) >= ScalarProducts.size())
422 {
423 GtkWidget* message = gtk_message_dialog_new(NULL, (GtkDialogFlags) 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Everything has been cleaved"));
424 g_signal_connect (G_OBJECT (message), "response", G_CALLBACK (gtk_widget_destroy), NULL);
425 gtk_widget_show(message);
426 for (i = Atoms.begin(); i != Atoms.end(); i++) (*i)->Cleave();
427 for (j = Lines.begin(); j != Lines.end(); j++) (*j)->Cleave();
428 }
429 else
430 {
431 x = ScalarProducts[n - 1];
432 for (i = Atoms.begin(); i != Atoms.end(); i++)
433 if (x < (*i)->ScalProd((*k)->h(), (*k)->k(), (*k)->l())+ PREC)
434 (*i)->Cleave() ;
435
436 //bonds cleavage
437 for (j = Lines.begin(); j != Lines.end(); j++)
438 if (x < (*j)->ScalProd((*k)->h(), (*k)->k(), (*k)->l())+ PREC)
439 (*j)->Cleave();
440 }
441
442 ScalarProducts.clear() ;
443 }
444
445 //Transform coordinates to Cartesians and find center of visible view
446 gdouble x, y, z, d,
447 xmin = G_MAXDOUBLE, ymin = G_MAXDOUBLE, zmin = G_MAXDOUBLE,
448 xmax = -G_MAXDOUBLE, ymax = -G_MAXDOUBLE, zmax = -G_MAXDOUBLE;
449 iend = Atoms.end ();
450 for (i = Atoms.begin(); i != iend; i++) {
451 (*i)->NetToCartesian(m_a, m_b, m_c, alpha, beta, gamma);
452 if ((*i)->IsCleaved ())
453 continue;
454 (*i)->GetCoords (&x, &y, &z);
455 if (x < xmin)
456 xmin = x;
457 if (y < ymin)
458 ymin = y;
459 if (z < zmin)
460 zmin = z;
461 if (x > xmax)
462 xmax = x;
463 if (y > ymax)
464 ymax = y;
465 if (z > zmax)
466 zmax = z;
467 }
468 jend = Lines.end ();
469 for (j = Lines.begin (); j != jend; j++) {
470 (*j)->NetToCartesian (m_a, m_b, m_c, alpha, beta, gamma);
471 if ((*j)->IsCleaved ())
472 continue;
473 x = (*j)->Xmin ();
474 y = (*j)->Ymin ();
475 z = (*j)->Zmin ();
476 if (x < xmin)
477 xmin = x;
478 if (y < ymin)
479 ymin = y;
480 if (z < zmin)
481 zmin = z;
482 x = (*j)->Xmax ();
483 y = (*j)->Ymax ();
484 z = (*j)->Zmax ();
485 if (x > xmax)
486 xmax = x;
487 if (y > ymax)
488 ymax = y;
489 if (z > zmax)
490 zmax = z;
491 }
492
493 //Searching the center of the crystal and find maximum distance from center
494 x = (xmin + xmax) / 2.;
495 y = (ymin + ymax) / 2.;
496 z = (zmin + zmax) / 2.;
497 m_MaxDist = 0;
498 for (i = Atoms.begin(); i != iend; i++) {
499 d = (*i)->Distance (x, y, z, m_FixedSize);
500 m_MaxDist = __max (m_MaxDist, d);
501 (*i)->Move (- x, - y, - z);
502 }
503
504 for (j = Lines.begin(); j != Lines.end(); j++) {
505 d = (*j)->Distance (x, y, z, m_FixedSize);
506 m_MaxDist = __max (m_MaxDist, d);
507 (*j)->Move (- x, - y, - z);
508 }
509 m_SpaceGroup = FindSpaceGroup ();
510 UpdateAllViews ();
511 }
512
UpdateAllViews()513 void Document::UpdateAllViews()
514 {
515 std::list < View * >::iterator i;
516 for (i = m_Views.begin (); i != m_Views.end (); i++) {
517 (*i)->Update();
518 Window *window = (*i)->GetWindow ();
519 if (window)
520 window->ClearStatus ();
521 }
522 }
523
Duplicate(Atom & atom)524 void Document::Duplicate (Atom& atom)
525 {
526 Atom AtomX, AtomY, AtomZ;
527 AtomX = atom ;
528 AtomX.Move (- floor (AtomX.x ()-m_xmin + 1e-7), - floor (AtomX.y ()-m_ymin + 1e-7), - floor (AtomX.z ()-m_zmin + 1e-7)) ;
529 while (AtomX.x () <= m_xmax + 1e-7) {
530 AtomY = AtomX ;
531 while (AtomY.y () <= m_ymax + 1e-7) {
532 AtomZ = AtomY ;
533 while (AtomZ.z () <= m_zmax + 1e-7) {
534 Atoms.push_back (new Atom (AtomZ)) ;
535 AtomZ.Move (0,0,1) ;
536 }
537 AtomY.Move (0,1,0) ;
538 }
539 AtomX.Move (1,0,0) ;
540 }
541 }
542
Duplicate(Line & line)543 void Document::Duplicate (Line& line)
544 {
545 Line LineX, LineY, LineZ ;
546 LineX = line ;
547 LineX.Move (- floor (LineX.Xmin ()-m_xmin + 1e-7), - floor (LineX.Ymin ()-m_ymin + 1e-7), - floor (LineX.Zmin ()-m_zmin + 1e-7)) ;
548 while (LineX.Xmax () <= m_xmax + 1e-7) {
549 LineY = LineX ;
550 while (LineY.Ymax () <= m_ymax + 1e-7) {
551 LineZ = LineY ;
552 while (LineZ.Zmax () <= m_zmax + 1e-7) {
553 Lines.push_back (new Line (LineZ)) ;
554 LineZ.Move (0,0,1) ;
555 }
556 LineY.Move (0,1,0) ;
557 }
558 LineX.Move (1,0,0) ;
559 }
560 }
561
Draw(gcu::Matrix const & m) const562 void Document::Draw (gcu::Matrix const &m) const
563 {
564 gcu::Vector v, v1;
565 gcu::Sphere sp (10);
566 glEnable (GL_RESCALE_NORMAL);
567 AtomList::const_iterator i, iend = Atoms.end ();
568 double red, green, blue, alpha;
569 for (i = Atoms.begin (); i != iend; i++)
570 if (!(*i)->IsCleaved ()) {
571 v.SetZ ((*i)->x ());
572 v.SetX ((*i)->y ());
573 v.SetY ((*i)->z ());
574 v = m.glmult (v);
575 (*i)->GetColor (&red, &green, &blue, &alpha);
576 glColor4d (red, green, blue, alpha) ;
577 sp.draw (v, (*i)->r () * (*i)->GetEffectiveRadiusRatio ());
578 }
579 glEnable (GL_NORMALIZE);
580 LineList::const_iterator j, jend = Lines.end ();
581 gcu::Cylinder cyl (10);
582 for (j = Lines.begin (); j != jend; j++)
583 if (!(*j)->IsCleaved ()) {
584 v.SetZ ((*j)->X1 ());
585 v.SetX ((*j)->Y1 ());
586 v.SetY ((*j)->Z1 ());
587 v = m.glmult (v);
588 v1.SetZ ((*j)->X2 ());
589 v1.SetX ((*j)->Y2 ());
590 v1.SetY ((*j)->Z2 ());
591 v1 = m.glmult (v1);
592 (*j)->GetColor (&red, &green, &blue, &alpha);
593 glColor4d (red, green, blue, alpha) ;
594 cyl.draw (v, v1, (*j)->GetRadius ());
595 }
596 }
597
CreateNewView()598 View* Document::CreateNewView()
599 {
600 return new View(this);
601 }
602
CreateNewAtom()603 Atom* Document::CreateNewAtom()
604 {
605 return new Atom();
606 }
607
CreateNewLine()608 Line* Document::CreateNewLine()
609 {
610 return new Line();
611 }
612
CreateNewCleavage()613 Cleavage* Document::CreateNewCleavage()
614 {
615 return new Cleavage();
616 }
617
GetProgramId() const618 const char* Document::GetProgramId() const
619 {
620 return NULL;
621 }
622
BuildXMLTree() const623 xmlDocPtr Document::BuildXMLTree () const
624 {
625 xmlDocPtr xml;
626 xmlNodePtr node;
627 xmlNsPtr ns;
628
629 // first check if dialogs are open and data coherent
630 gcu::Dialog *dlg;
631 if ((dlg = GetDialog ("atoms"))) {
632 const_cast <Document * > (this)->CheckAtoms ();
633 static_cast < AtomsDlg * > (dlg)->ReloadData ();
634 }
635 if ((dlg = GetDialog ("cleavages"))) {
636 const_cast <Document * > (this)->CheckCleavages ();
637 static_cast < CleavagesDlg * > (dlg)->ReloadData ();
638 }
639 if ((dlg = GetDialog ("lines"))) {
640 const_cast <Document * > (this)->CheckLines ();
641 static_cast < LinesDlg * > (dlg)->ReloadData ();
642 }
643 xml = xmlNewDoc((xmlChar*)"1.0");
644 if (xml == NULL) {throw(1);}
645
646 xmlDocSetRootElement (xml, xmlNewDocNode(xml, NULL, (xmlChar*)"crystal", NULL));
647 ns = xmlNewNs (xml->children, (xmlChar*) "http://www.nongnu.org/gcrystal", (xmlChar*) "gcry");
648 xmlSetNs (xml->children, ns);
649
650 try
651 {
652 node = xmlNewDocNode(xml, NULL, (xmlChar*)"generator", (xmlChar*)GetProgramId());
653 if (node) xmlAddChild(xml->children, node); else throw (int) 0;
654
655 node = xmlNewDocNode(xml, NULL, (xmlChar*)"lattice", (xmlChar*)LatticeName[m_lattice]);
656 if (node) xmlAddChild(xml->children, node); else throw (int) 0;
657 if (m_SpaceGroup) {
658 node = xmlNewDocNode (xml, NULL, (xmlChar*) "group", NULL);
659 if (node)
660 xmlAddChild (xml->children, node);
661 else
662 throw (int) 0;
663 string name = m_SpaceGroup->GetHallName ();
664 if (name.length () != 0)
665 xmlNewProp (node, (xmlChar*) "Hall", (xmlChar*) name.c_str ());
666 else {
667 name = m_SpaceGroup->GetHMName ();
668 if (name.length () != 0)
669 xmlNewProp (node, (xmlChar*) "HM", (xmlChar*) name.c_str ());
670 }
671 xmlNodePtr child;
672 list <gcu::Transform3d*>::const_iterator i;
673 gcu::Transform3d const *t = m_SpaceGroup->GetFirstTransform (i);
674 while (t) {
675 child = xmlNewDocNode (xml, NULL, (xmlChar*) "transform", (xmlChar const*) t->DescribeAsString ().c_str ());
676 if (child)
677 xmlAddChild (node, child);
678 else
679 throw (int) 0;
680 t = m_SpaceGroup->GetNextTransform (i);
681 }
682 }
683 node = xmlNewDocNode (xml, NULL, CC2XML ("cell"), NULL);
684 if (node)
685 xmlAddChild (xml->children, node);
686 else
687 throw (int) 0;
688 gcu::WriteFloat (node, "a", m_a);
689 gcu::WriteFloat (node, "b", m_b);
690 gcu::WriteFloat (node, "c", m_c);
691 gcu::WriteFloat (node, "alpha", m_alpha);
692 gcu::WriteFloat (node, "beta", m_beta);
693 gcu::WriteFloat (node, "gamma", m_gamma);
694
695 node = xmlNewDocNode(xml, NULL, (xmlChar*)"size", NULL);
696 if (node) xmlAddChild(xml->children, node); else throw (int) 0;
697 gcu::WritePosition(xml, node, "start", m_xmin, m_ymin, m_zmin);
698 gcu::WritePosition(xml, node, "end", m_xmax, m_ymax, m_zmax);
699 if (m_FixedSize)
700 xmlNewProp (node, (xmlChar *) "fixed", (xmlChar *) "true");
701
702 AtomList::const_iterator i;
703 for (i = AtomDef.begin(); i != AtomDef.end(); i++)
704 {
705 node = (*i)->Save(xml);
706 if (node) xmlAddChild(xml->children, node); else throw (int) 0;
707 }
708
709 LineList::const_iterator j;
710 for (j = LineDef.begin(); j != LineDef.end(); j++)
711 {
712 node = (*j)->Save(xml);
713 if (node) xmlAddChild(xml->children, node); else throw (int) 0;
714 }
715
716 CleavageList::const_iterator k;
717 for (k = Cleavages.begin(); k != Cleavages.end(); k++)
718 {
719 node = (*k)->Save(xml);
720 if (node) xmlAddChild(xml->children, node); else throw (int) 0;
721 }
722
723 list<View*>::const_iterator view;
724 for (view = m_Views.begin(); view != m_Views.end(); view++)
725 {
726 node = (*view)->Save(xml);
727 if (node) xmlAddChild(xml->children, node); else throw (int) 0;
728 }
729
730 return xml;
731 }
732 catch (int num)
733 {
734 xmlFreeDoc(xml);
735 return NULL;
736 }
737 }
738
Loaded()739 bool Document::Loaded () throw (gcu::LoaderError)
740 {
741 if (m_NameCommon.length () > 0)
742 SetTitle (m_NameCommon);
743 else if (m_NameMineral.length () > 0)
744 SetTitle (m_NameMineral);
745 else if (m_NameSystematic.length () > 0)
746 SetTitle (m_NameSystematic);
747 else if (m_NameStructure.length () > 0)
748 SetTitle (m_NameStructure);
749 LineDef.push_back (new Line (edges, 0., 0., 0., 0., 0., 0., 10., .25 , .25, .25 , 1.));
750 // set radii
751 AtomList::iterator i, iend = AtomDef.end ();
752 GcuAtomicRadius radius;
753 radius.type = GCU_VAN_DER_WAALS;
754 radius.charge = 0;
755 radius.cn = -1;
756 radius.spin = GCU_N_A_SPIN;
757 radius.scale = NULL;
758 /* use van der Waals radii for now in all cases, using ionic radii would need the
759 evaluation of the coordination numbers */
760 for (i = AtomDef.begin (); i != iend; i++) {
761 radius.Z = (*i)->GetZ ();
762 if (gcu_element_get_radius (&radius)) {
763 (*i)->SetRadius (radius);
764 (*i)->SetEffectiveRadiusRatio (.4);
765 }
766 (*i)->SetDefaultColor ();
767 }
768 if (m_lattice == triclinic) { // this is for invalid files
769 if (m_alpha == m_beta) {
770 if (m_alpha == m_gamma) {
771 if (m_alpha == 90) {
772 if (m_a != m_b)
773 m_lattice = orthorhombic;
774 else if (m_a != m_c)
775 m_lattice = tetragonal;
776 else
777 m_lattice = cubic;
778 } else
779 m_lattice = rhombohedral;
780 }
781 } else if (m_alpha == 90 && m_gamma == 90)
782 m_lattice = monoclinic;
783 else if (m_alpha == 90 && m_gamma == 120)
784 m_lattice = hexagonal;
785 }
786 switch (m_lattice) { // ensure that the parameters are coherent
787 case cubic:
788 case body_centered_cubic:
789 case face_centered_cubic:
790 m_alpha = m_beta = m_gamma = 90;
791 m_b = m_c = m_a;
792 break;
793 case hexagonal:
794 m_alpha = m_beta = 90;
795 m_gamma = 120;
796 m_b = m_a;
797 break;
798 case tetragonal:
799 case body_centered_tetragonal:
800 m_alpha = m_beta = m_gamma = 90;
801 m_b = m_a;
802 break;
803 case orthorhombic:
804 case base_centered_orthorhombic:
805 case body_centered_orthorhombic:
806 case face_centered_orthorhombic:
807 m_alpha = m_beta = m_gamma = 90;
808 break;
809 case rhombohedral:
810 if (m_alpha != 90) {
811 m_beta = m_gamma = m_alpha;
812 m_b = m_c = m_a;
813 } else {
814 // looks like the usual convention, at least in CIF files.
815 m_alpha = m_beta = 90;
816 m_gamma = 120;
817 m_b = m_a;
818 }
819 break;
820 case monoclinic:
821 case base_centered_monoclinic:
822 m_alpha = m_gamma = 90;
823 break;
824 case triclinic:
825 break;
826 }
827
828 Update ();
829 return false; // no pending reference updated
830 }
831
SetProperty(unsigned property,char const * value)832 bool Document::SetProperty (unsigned property, char const *value)
833 {
834 switch (property) {
835 case GCU_PROP_CELL_A:
836 m_a = g_ascii_strtod (value, NULL) * GetScale ();
837 break;
838 case GCU_PROP_CELL_B:
839 m_b = g_ascii_strtod (value, NULL) * GetScale ();
840 break;
841 case GCU_PROP_CELL_C:
842 m_c = g_ascii_strtod (value, NULL) * GetScale ();
843 break;
844 case GCU_PROP_CELL_ALPHA:
845 m_alpha = g_ascii_strtod (value, NULL);
846 break;
847 case GCU_PROP_CELL_BETA:
848 m_beta = g_ascii_strtod (value, NULL);
849 break;
850 case GCU_PROP_CELL_GAMMA:
851 m_gamma = g_ascii_strtod (value, NULL);
852 break;
853 case GCU_PROP_CHEMICAL_NAME_COMMON:
854 m_NameCommon = value;
855 break;
856 case GCU_PROP_CHEMICAL_NAME_SYSTEMATIC:
857 m_NameSystematic = value;
858 break;
859 case GCU_PROP_CHEMICAL_NAME_MINERAL:
860 m_NameMineral = value;
861 break;
862 case GCU_PROP_CHEMICAL_NAME_STRUCTURE:
863 m_NameStructure = value;
864 break;
865 case GCU_PROP_SPACE_GROUP: {
866 m_SpaceGroup = gcu::SpaceGroup::GetSpaceGroup (value);
867 char type = (*value == '-')? value[1]: value[0];
868 int id = m_SpaceGroup->GetId ();
869 if (id <= 2)
870 m_lattice = triclinic;
871 else if (id <= 15)
872 m_lattice = (type == 'P')? monoclinic: base_centered_monoclinic;
873 else if (id <= 74)
874 switch (type) {
875 case 'P':
876 m_lattice = orthorhombic;
877 break;
878 case 'I':
879 m_lattice = body_centered_orthorhombic;
880 break;
881 case 'F':
882 m_lattice = face_centered_orthorhombic;
883 break;
884 default:
885 m_lattice = base_centered_orthorhombic;
886 break;
887 }
888 else if (id <= 142)
889 m_lattice = (type == 'P')? tetragonal: body_centered_tetragonal;
890 else if (id <= 194) {
891 if (id == 146 || id == 148 || id == 155 || id == 160 || id == 161 || id == 166 || id == 167)
892 m_lattice = rhombohedral;
893 else
894 m_lattice = hexagonal;
895 } else {
896 switch (type) {
897 case 'P':
898 m_lattice = cubic;
899 break;
900 case 'I':
901 m_lattice = body_centered_cubic;
902 break;
903 case 'F':
904 m_lattice = face_centered_cubic;
905 break;
906 }
907 }
908 break;
909 }
910 case GCU_PROP_DOC_CREATOR:
911 g_free (m_Author);
912 m_Author = g_strdup (value);
913 break;
914 case GCU_PROP_DOC_CREATOR_EMAIL:
915 g_free (m_Mail);
916 m_Mail = g_strdup (value);
917 break;
918 default:
919 return false;
920 }
921 return true;
922 }
923
GetProperty(unsigned property) const924 std::string Document::GetProperty (unsigned property) const
925 {
926 ostringstream res;
927 switch (property) {
928 case GCU_PROP_DOC_TITLE:
929 return m_Title;
930 case GCU_PROP_CHEMICAL_NAME_COMMON:
931 return m_NameCommon;
932 case GCU_PROP_CHEMICAL_NAME_SYSTEMATIC:
933 return m_NameSystematic;
934 case GCU_PROP_CHEMICAL_NAME_MINERAL:
935 return m_NameMineral;
936 case GCU_PROP_CELL_A:
937 res << m_a / GetScale ();
938 break;
939 case GCU_PROP_CELL_B:
940 res << m_b / GetScale ();
941 break;
942 case GCU_PROP_CELL_C:
943 res << m_c / GetScale ();
944 break;
945 case GCU_PROP_CELL_ALPHA:
946 res << m_alpha;
947 break;
948 case GCU_PROP_CELL_BETA:
949 res << m_beta;
950 break;
951 case GCU_PROP_CELL_GAMMA:
952 res << m_gamma;
953 break;
954 case GCU_PROP_SPACE_GROUP:
955 return m_SpaceGroup->GetHallName ();
956 case GCU_PROP_DOC_CREATOR:
957 if (m_Author)
958 res << m_Author;
959 break;
960 case GCU_PROP_DOC_CREATOR_EMAIL:
961 if (m_Mail)
962 res << m_Mail;
963 g_free (m_Mail);
964 break;
965 default:
966 return GLDocument::GetProperty (property);
967 }
968 return res.str ();
969 }
970
AddChild(Object * object)971 void Document::AddChild (Object* object)
972 {
973 Object::AddChild (object);
974 Atom *atom = dynamic_cast <Atom *> (object);
975 if (atom) {
976 AtomDef.remove (atom); // don't add more than needed (a set might be better than a list)
977 AtomDef.push_back (atom);
978 }
979 }
980
FindSpaceGroup()981 gcu::SpaceGroup const *Document::FindSpaceGroup ()
982 {
983 if (!AtomDef.size ())
984 return NULL;
985 if (m_SpaceGroup && !m_AutoSpaceGroup)
986 return (m_SpaceGroup);
987 unsigned start, end, id;
988 switch (m_lattice) {
989 case cubic:
990 case body_centered_cubic:
991 case face_centered_cubic:
992 start = 195;
993 end = 230;
994 break;
995 case hexagonal:
996 start = 168;
997 end = 194;
998 break;
999 case tetragonal:
1000 case body_centered_tetragonal:
1001 start = 75;
1002 end = 142;
1003 break;
1004 case orthorhombic:
1005 case base_centered_orthorhombic:
1006 case body_centered_orthorhombic:
1007 case face_centered_orthorhombic:
1008 start = 16;
1009 end = 74;
1010 break;
1011 case rhombohedral:
1012 start = 143;
1013 end = 167;
1014 break;
1015 case monoclinic:
1016 case base_centered_monoclinic:
1017 start = 3;
1018 end = 15;
1019 break;
1020 case triclinic:
1021 start = 1;
1022 end = 2;
1023 break;
1024 default:
1025 start = end = 0;
1026 }
1027 //make a list of all atoms inside the cell
1028 list <Atom *> atoms;
1029 Atom *a;
1030 double x, y, z;
1031 AtomList::iterator i, i0, iend = AtomDef.end ();
1032 for (i = AtomDef.begin (); i != iend; i++) {
1033 atoms.push_back (new Atom (**i));
1034 switch (m_lattice) {
1035 case body_centered_cubic:
1036 case body_centered_tetragonal:
1037 case body_centered_orthorhombic:
1038 a = new Atom (**i);
1039 a->GetCoords (&x, &y, &z);
1040 x = (x > .5 - PREC)? x - .5: x + .5;
1041 y = (y > .5 - PREC)? y - .5: y + .5;
1042 z = (z > .5 - PREC)? z - .5: z + .5;
1043 a->SetCoords (x, y, z);
1044 atoms.push_back (a);
1045 break;
1046 case face_centered_cubic:
1047 case face_centered_orthorhombic:
1048 a = new Atom (**i);
1049 a->GetCoords (&x, &y, &z);
1050 x = (x > .5 - PREC)? x - .5: x + .5;
1051 z = (z > .5 - PREC)? z - .5: z + .5;
1052 a->SetCoords (x, y, z);
1053 atoms.push_back (a);
1054 a = new Atom (**i);
1055 a->GetCoords (&x, &y, &z);
1056 y = (y > .5 - PREC)? y - .5: y + .5;
1057 z = (z > .5 - PREC)? z - .5: z + .5;
1058 a->SetCoords (x, y, z);
1059 atoms.push_back (a);
1060 case base_centered_orthorhombic:
1061 case base_centered_monoclinic:
1062 a = new Atom (**i);
1063 a->GetCoords (&x, &y, &z);
1064 x = (x > .5 - PREC)? x - .5: x + .5;
1065 y = (y > .5 - PREC)? y - .5: y + .5;
1066 a->SetCoords (x, y, z);
1067 atoms.push_back (a);
1068 break;
1069 default:
1070 break;
1071 }
1072 }
1073 iend = atoms.end ();
1074 gcu::SpaceGroup const *res = NULL;
1075 gcu::Vector v;
1076 std::list <gcu::Vector>::iterator j, jend;
1077 for (id = end; id >= start; id--) {
1078 std::list <gcu::SpaceGroup const *> &groups = gcu::SpaceGroup::GetSpaceGroups (id);
1079 std::list <gcu::SpaceGroup const *>::iterator g, gend = groups.end ();
1080 for (g = groups.begin (); g != gend; g++) {
1081 for (i = atoms.begin (); i != iend; i++) {
1082 a = new Atom (**i);
1083 v = a->GetVector ();
1084 std::list <gcu::Vector> vv = (*g)->Transform (v);
1085 jend = vv.end ();
1086 for (j = vv.begin (); j != jend; j++) {
1087 x = (*j).GetX ();
1088 y = (*j).GetY ();
1089 z = (*j).GetZ ();
1090 while (x > 1. - PREC)
1091 x -= 1.;
1092 while (y > 1. - PREC)
1093 y -= 1.;
1094 while (z > 1. - PREC)
1095 z -= 1.;
1096 a->SetCoords (x, y, z);
1097 for (i0 = atoms.begin (); i0 != iend; i0++)
1098 if (*a == **i0)
1099 break;
1100 if (i0 == iend)
1101 goto end_loop;
1102 }
1103 delete a;
1104 if (j != jend)
1105 goto end_loop;
1106 }
1107 if (i == iend)
1108 break;
1109 end_loop:;
1110 }
1111 if (g != gend) {
1112 res = *g;
1113 break;
1114 }
1115 }
1116 // clean atoms
1117 for (i = atoms.begin (); i != iend; i++)
1118 delete *i;
1119 // now, search for duplicates in AtomDef
1120 if (!res)
1121 return NULL;
1122 set <Atom *> dups;
1123 iend = AtomDef.end ();
1124 for (i = AtomDef.begin (); i != iend; i++) {
1125 if (dups.find (*i) != dups.end ())
1126 continue;
1127 a = new Atom (**i);
1128 v = a->GetVector ();
1129 std::list <gcu::Vector> vv = res->Transform (v);
1130 jend = vv.end ();
1131 for (j = vv.begin (); j != jend; j++) {
1132 x = (*j).GetX ();
1133 y = (*j).GetY ();
1134 z = (*j).GetZ ();
1135 while (x > 1. - PREC)
1136 x -= 1.;
1137 while (y > 1. - PREC)
1138 y -= 1.;
1139 while (z > 1. - PREC)
1140 z -= 1.;
1141 a->SetCoords (x, y, z);
1142 for (i0 = i, i0++; i0 != iend; i0++) {
1143 if (dups.find (*i0) != dups.end ())
1144 continue;
1145 if (*a == **i0) {
1146 if ((*i0)->x () < (*i)->x () || (*i0)->y () < (*i)->y () || (*i0)->z () < (*i)->z ()) {
1147 dups.insert (*i);
1148 goto end_loop1;
1149 }
1150 dups.insert (*i0);
1151 }
1152 }
1153 }
1154 end_loop1:;
1155 }
1156 set <Atom *>::iterator k, kend = dups.end ();
1157 for (k = dups.begin (); k != kend; k++) {
1158 AtomDef.remove (*k);
1159 delete *k;
1160 }
1161 return res;
1162 }
1163
GetSize(double * xmin,double * xmax,double * ymin,double * ymax,double * zmin,double * zmax)1164 void Document::GetSize(double* xmin, double* xmax, double* ymin, double* ymax, double* zmin, double* zmax)
1165 {
1166 *xmin = m_xmin;
1167 *xmax = m_xmax;
1168 *ymin = m_ymin;
1169 *ymax = m_ymax;
1170 *zmin = m_zmin;
1171 *zmax = m_zmax;
1172 }
1173
SetSize(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax)1174 void Document::SetSize(double xmin, double xmax, double ymin, double ymax, double zmin, double zmax)
1175 {
1176 m_xmin = xmin;
1177 m_xmax = xmax;
1178 m_ymin = ymin;
1179 m_ymax = ymax;
1180 m_zmin = zmin;
1181 m_zmax = zmax;
1182 }
1183
GetCell(Lattice * lattice,double * a,double * b,double * c,double * alpha,double * beta,double * gamma)1184 void Document::GetCell (Lattice *lattice, double *a, double *b, double *c, double *alpha, double *beta, double *gamma)
1185 {
1186 *lattice = m_lattice;
1187 *a = m_a;
1188 *b = m_b;
1189 *c = m_c;
1190 *alpha = m_alpha;
1191 *beta = m_beta;
1192 *gamma = m_gamma;
1193 }
1194
SetCell(Lattice lattice,double a,double b,double c,double alpha,double beta,double gamma)1195 void Document::SetCell (Lattice lattice, double a, double b, double c, double alpha, double beta, double gamma)
1196 {
1197 m_lattice = lattice;
1198 m_a = a;
1199 m_b = b;
1200 m_c = c;
1201 m_alpha = alpha;
1202 m_beta = beta;
1203 m_gamma = gamma;
1204 }
1205
gcd_euler(int n1,int n2)1206 static int gcd_euler (int n1, int n2)
1207 {
1208 int buf;
1209 if (n1 < n2) {
1210 buf = n1;
1211 n1 = n2;
1212 n2 = buf;
1213 }
1214 while (1) {
1215 if (n2 == 0)
1216 return n1;
1217 buf = n1 % n2;
1218 n1 = n2;
1219 n2 = buf;
1220 }
1221 }
1222
CheckCleavages()1223 void Document::CheckCleavages ()
1224 {
1225 std::set < Cleavage * > garbage;
1226 CleavageList::iterator i, j, end = Cleavages.end ();
1227 int gcd;
1228 for (i = Cleavages.begin (); i != end; i++) {
1229 if ((*i)->Planes () == 0) {
1230 garbage.insert (*i);
1231 continue;
1232 }
1233 // now divide h, k, and l by their gcd, using Euclid's algorithm
1234 // since we probably don't need better there
1235 gcd = abs ((*i)->h ());
1236 if (!gcd) {
1237 gcd = abs ((*i)->k ());
1238 if (!gcd) {
1239 gcd = abs ((*i)->l ());
1240 if (!gcd) {
1241 garbage.insert (*i);
1242 continue;
1243 }
1244 }
1245 } else
1246 gcd = gcd_euler (gcd, abs ((*i)->k ()));
1247 gcd = gcd_euler (gcd, abs ((*i)->l ()));
1248 (*i)->h () /= gcd;
1249 (*i)->k () /= gcd;
1250 (*i)->l () /= gcd;
1251 // now we look for equivalent cleavages in the list beginning
1252 for (j = Cleavages.begin (); j != i; j++)
1253 if (*i == *j) {
1254 if ((*j)->Planes () > (*i)->Planes ()) // use the largest planes number
1255 (*i)->Planes () = (*j)->Planes ();
1256 garbage.insert (*j);
1257 break;
1258 }
1259 }
1260 std::set < Cleavage * >::iterator k,kend = garbage.end ();
1261 for (k = garbage.begin (); k != kend; k++) {
1262 Cleavages.remove (*k);
1263 delete *k;
1264 }
1265 }
1266
CheckAtoms()1267 void Document::CheckAtoms ()
1268 {
1269 std::set < Atom * > garbage;
1270 AtomList::iterator i, j, end = AtomDef.end ();
1271 for (i = AtomDef.begin (); i != end; i++) {
1272 for (j = AtomDef.begin (); j != i; j++)
1273 if (*i == *j) {
1274 garbage.insert (*j);
1275 break;
1276 }
1277 }
1278 std::set < Atom * >::iterator k,kend = garbage.end ();
1279 for (k = garbage.begin (); k != kend; k++) {
1280 AtomDef.remove (*k);
1281 delete *k;
1282 }
1283 }
1284
CheckLines()1285 void Document::CheckLines ()
1286 {
1287 std::set < Line * > garbage;
1288 LineList::iterator i, j, end = LineDef.end ();
1289 for (i = LineDef.begin (); i != end; i++) {
1290 for (j = LineDef.begin (); j != i; j++)
1291 if (*i == *j) {
1292 garbage.insert (*j);
1293 break;
1294 }
1295 }
1296 std::set < Line * >::iterator k,kend = garbage.end ();
1297 for (k = garbage.begin (); k != kend; k++) {
1298 LineDef.remove (*k);
1299 delete *k;
1300 }
1301 }
1302
Define(unsigned nPage)1303 void Document::Define (unsigned nPage)
1304 {
1305 gcu::Dialog *dialog;
1306 switch (nPage) {
1307 case 0:
1308 dialog = GetDialog ("cell");
1309 if (dialog)
1310 dialog->Present ();
1311 else
1312 new gcr::CellDlg (static_cast < Application * > (m_App), this);
1313 break;
1314 case 1:
1315 dialog = GetDialog ("atoms");
1316 if (dialog)
1317 dialog->Present ();
1318 else
1319 new gcr::AtomsDlg (static_cast <Application *> (m_App), this);
1320 break;
1321 case 2:
1322 dialog = GetDialog ("lines");
1323 if (dialog)
1324 dialog->Present ();
1325 else
1326 new gcr::LinesDlg (static_cast <Application *> (m_App), this);
1327 break;
1328 case 3:
1329 dialog = GetDialog ("size");
1330 if (dialog)
1331 dialog->Present ();
1332 else
1333 new gcr::SizeDlg (static_cast <Application *> (m_App), this);
1334 break;
1335 case 4:
1336 dialog = GetDialog ("cleavages");
1337 if (dialog)
1338 dialog->Present ();
1339 else
1340 new gcr::CleavagesDlg (static_cast < Application * > (m_App), this);
1341 break;
1342 }
1343 }
AddView(View * pView)1344 void Document::AddView(View* pView)
1345 {
1346 m_Views.push_back (pView);
1347 RenameViews ();
1348 if (!GetEmpty ())
1349 SetDirty (true);
1350 }
1351
RemoveView(View * pView)1352 bool Document::RemoveView (View* pView)
1353 {
1354 if (m_Views.size () > 1) {
1355 m_Views.remove (pView);
1356 RenameViews ();
1357 if (!m_bClosing && !GetEmpty ())
1358 SetDirty (true);
1359 return true;
1360 }
1361 if (GetDirty ()) {
1362 if (!VerifySaved ())
1363 return false;
1364 }
1365 delete this;
1366 return true;
1367 }
1368
RemoveAllViews()1369 void Document::RemoveAllViews ()
1370 {
1371 while (m_Views.size () > 1)
1372 m_Views.front ()->GetWindow ()->Destroy ();
1373 // The last one is deleted separately since this will be destroyed !
1374 m_Views.front ()->GetWindow ()->Destroy ();
1375 }
1376
VerifySaved()1377 bool Document::VerifySaved()
1378 {
1379 m_bClosing = true;
1380 if (!GetDirty ())
1381 return true;
1382 GtkWidget* mbox;
1383 int res;
1384 do
1385 {
1386 mbox = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("\"%s\" has been modified. Do you wish to save it?"), GetLabel ());
1387 gtk_dialog_add_button(GTK_DIALOG(mbox), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1388 res = gtk_dialog_run(GTK_DIALOG(mbox));
1389 gtk_widget_destroy(mbox);
1390 if (res == GTK_RESPONSE_YES) {
1391 if (m_filename == NULL) {
1392 list<string> l;
1393 l.push_front ("application/x-gcrystal");
1394 gcugtk::FileChooser (static_cast < gcugtk::Application * > (m_App), true, l, this);
1395 }
1396 if (m_filename)
1397 Save ();
1398 }
1399 }
1400 while ((res == GTK_RESPONSE_YES) && (m_filename == NULL));
1401 if (res == GTK_RESPONSE_NO)
1402 SetDirty (false);
1403 else if (res == GTK_RESPONSE_CANCEL) m_bClosing = false;
1404 return (res != GTK_RESPONSE_CANCEL);
1405 }
1406
RenameViews()1407 void Document::RenameViews ()
1408 {
1409 list <View *>::iterator i, iend = m_Views.end ();
1410 int n = 1, max = m_Views.size ();
1411 for (i = m_Views.begin (); i != iend; i++) {
1412 Window *window = (*i)->GetWindow ();
1413 GtkWindow *w = window->GetWindow ();
1414 if (!w)
1415 continue;
1416 if (max > 1) {
1417 char *t = g_strdup_printf ("%s (%i)", m_Label, n++);
1418 gtk_window_set_title (w, t);
1419 g_free (t);
1420 } else
1421 gtk_window_set_title (w, GetLabel ());
1422 window->ActivateActionWidget ("ui/MainMenu/FileMenu/Save", !m_ReadOnly);
1423 window->ActivateActionWidget ("ui/MainToolbar/Save", !m_ReadOnly);
1424 }
1425 }
1426
SetFileName(const string & filename)1427 void Document::SetFileName (const string &filename)
1428 {
1429 GFile *file = g_file_new_for_uri (filename.c_str ());
1430 GError *error = NULL;
1431 if (g_file_query_exists (file, NULL)) {
1432 GFileInfo *info = g_file_query_info (file,
1433 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
1434 G_FILE_QUERY_INFO_NONE, NULL, &error);
1435 if (error) {
1436 g_warning ("GIO error: %s", error->message);
1437 g_error_free (error);
1438 m_ReadOnly = true;
1439 } else
1440 m_ReadOnly = !g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
1441 if (info)
1442 g_object_unref (info);
1443 } else
1444 m_ReadOnly = false;
1445 g_object_unref (file);
1446 if (m_filename)
1447 g_free (m_filename);
1448 m_filename = g_strdup (filename.c_str ());
1449 char *dirname = g_path_get_dirname (m_filename);
1450 m_App->SetCurDir (dirname);
1451 g_free (dirname);
1452 int i = filename.length () - 1;
1453 while ((m_filename[i] != '/') && (i >= 0))
1454 i--;
1455 i++;
1456 int j = filename.length () - 1;
1457 while ((i < j) && (m_filename[j] != '.'))
1458 j--;
1459 char *buf = (strcmp (m_filename + j, ".gcrystal"))? g_strdup (m_filename + i): g_strndup (m_filename + i, j - i);
1460 char *unescaped = g_uri_unescape_string (buf, NULL);
1461 g_free (buf);
1462 m_DefaultLabel = unescaped;
1463 if (m_Title.length () == 0) {
1464 g_free (m_Label);
1465 m_Label = unescaped;
1466 } else
1467 g_free (unescaped);
1468 }
1469
SetTitle(const gchar * title)1470 void Document::SetTitle(const gchar* title)
1471 {
1472 m_Title = title? title: "";
1473 g_free (m_Label);
1474 m_Label = title? g_strdup (title): NULL;
1475 }
1476
SetTitle(std::string & title)1477 void Document::SetTitle (std::string& title)
1478 {
1479 m_Title = title;
1480 g_free (m_Label);
1481 m_Label = title.length ()? g_strdup (title.c_str ()): NULL;
1482 }
1483
SetAuthor(char const * author)1484 void Document::SetAuthor (char const *author)
1485 {
1486 g_free (m_Author);
1487 m_Author = g_strdup (author);
1488 }
1489
SetMail(char const * mail)1490 void Document::SetMail (char const *mail)
1491 {
1492 g_free (m_Mail);
1493 m_Mail = g_strdup (mail);
1494 }
1495
SetComment(char const * comment)1496 void Document::SetComment (char const *comment)
1497 {
1498 g_free (m_Comment);
1499 m_Comment = g_strdup (comment);
1500 }
1501
SetLabel(char const * label)1502 void Document::SetLabel (char const *label)
1503 {
1504 g_free (m_Label);
1505 m_Label = g_strdup (label);
1506 }
1507
1508 typedef struct {int n; std::list<gcr::Atom*> l;} sAtom;
1509 typedef struct {int n; std::list<gcr::Line*> l;} sLine;
1510
OnExportVRML(const string & FileName) const1511 void Document::OnExportVRML (const string &FileName) const
1512 {
1513 char tmp[128];
1514 double x0, x1, x2, x3, x4, x5;
1515 int n = 0;
1516 try {
1517 ostringstream file;
1518 GError *error = NULL;
1519 GFile *stream = g_file_new_for_uri (FileName.c_str ());
1520 GOutputStream *output = G_OUTPUT_STREAM (g_file_create (stream, G_FILE_CREATE_NONE, NULL, &error));
1521 if (error) {
1522 cerr << "gio error: " << error->message << endl;
1523 g_error_free (error);
1524 g_object_unref (stream);
1525 throw (int) 1;
1526 }
1527 std::map<std::string, sAtom>AtomsMap;
1528 std::map<std::string, sLine>LinesMap;
1529
1530 file << "#VRML V2.0 utf8" << endl;
1531
1532 //Create prototypes for atoms
1533 gcr::AtomList::const_iterator i;
1534 for (i = Atoms.begin(); i != Atoms.end(); i++)
1535 {
1536 (*i)->GetColor(&x0, &x1, &x2, &x3);
1537 snprintf(tmp, sizeof(tmp), "%g %g %g %g %g", (*i)->GetSize(), x0, x1, x2, x3);
1538 if (AtomsMap[tmp].l.empty())
1539 {
1540 AtomsMap[tmp].n = n;
1541 file << "PROTO Atom" << n++ << " [] {Shape {" << endl << "\tgeometry Sphere {radius " << (*i)->GetSize() / 100 << "}" << endl;
1542 file << "\tappearance Appearance {" << endl << "\t\tmaterial Material {" << endl << "\t\t\tdiffuseColor " << x0 << " " << x1 << " " << x2 << endl;
1543 if (x3 < 1) file << "\t\t\ttransparency " << (1 - x3) << endl;
1544 file << "\t\t\tspecularColor 1 1 1" << endl << "\t\t\tshininess 0.9" << endl << "\t\t}" << endl << "\t}\r\n}}" << endl;
1545 }
1546 AtomsMap[tmp].l.push_back(*i);
1547 }
1548
1549 //Create prototypes for bonds
1550 gcr::LineList::const_iterator j;
1551 n = 0;
1552 for (j = Lines.begin(); j != Lines.end(); j++)
1553 {
1554 (*j)->GetColor(&x0, &x1, &x2, &x3);
1555 snprintf(tmp, sizeof(tmp), "%g %g %g %g %g %g", (*j)->Long(), (*j)->GetRadius(), x0, x1, x2, x3);
1556 if (LinesMap[tmp].l.empty())
1557 {
1558 LinesMap[tmp].n = n;
1559 file << "PROTO Bond" << n++ << " [] {Shape {" << endl << "\tgeometry Cylinder {radius " << (*j)->GetRadius() / 100 << "\theight " << (*j)->Long() / 100 << "}" << endl;
1560 file << "\tappearance Appearance {" << endl << "\t\tmaterial Material {" << endl << "\t\t\tdiffuseColor " << x0 << " " << x1 << " " << x2 << endl;
1561 if (x3 < 1) file << "\t\t\ttransparency " << (1 - x3) << endl;
1562 file << "\t\t\tspecularColor 1 1 1" << endl << "\t\t\tshininess 0.9" << endl << "\t\t}" << endl << "\t}\r\n}}" << endl;
1563 }
1564 LinesMap[tmp].l.push_back(*j);
1565 }
1566
1567 //world begin
1568 m_pActiveView->GetBackgroundColor(&x0, &x1, &x2, &x3);
1569 file << "Background{skyColor " << x0 << " " << x1 << " " << x2 << "}" << endl;
1570 file << "Viewpoint {fieldOfView " << m_pActiveView->GetFoV()/90*1.570796326794897 <<"\tposition 0 0 " << m_pActiveView->GetPos() / 100 << "}" << endl;
1571 m_pActiveView->GetRotation(&x0, &x1, &x2);
1572 gcu::Matrix m(x0/90*1.570796326794897, x1/90*1.570796326794897, x2/90*1.570796326794897, gcu::euler);
1573 file << "Transform {" << endl << "\tchildren [" << endl;
1574
1575 std::map<std::string, sAtom>::iterator k;
1576 for (k = AtomsMap.begin(); k != AtomsMap.end(); k++)
1577 {
1578 for (i = (*k).second.l.begin(); i != (*k).second.l.end(); i++)
1579 {
1580 if (!(*i)->IsCleaved())
1581 {
1582 x0 = (*i)->x();
1583 x1 = (*i)->y();
1584 x2 = (*i)->z();
1585 m.Transform(x0, x1, x2);
1586 file << "\t\tTransform {translation " << x1 / 100 << " " << x2 / 100 << " " << x0 / 100\
1587 << " children [Atom" << (*k).second.n << " {}]}" << endl;
1588 }
1589 }
1590 (*k).second.l.clear();
1591 }
1592 AtomsMap.clear();
1593
1594 std::map<std::string, sLine>::iterator l;
1595 n = 0;
1596 for (l = LinesMap.begin(); l != LinesMap.end(); l++)
1597 {
1598 for (j = (*l).second.l.begin(); j != (*l).second.l.end(); j++)
1599 {
1600 if (!(*j)->IsCleaved())
1601 {
1602 x0 = (*j)->X1();
1603 x1 = (*j)->Y1();
1604 x2 = (*j)->Z1();
1605 m.Transform(x0, x1, x2);
1606 x3 = (*j)->X2();
1607 x4 = (*j)->Y2();
1608 x5 = (*j)->Z2();
1609 m.Transform(x3, x4, x5);
1610 gcr::Line line(gcr::unique, x0, x1, x2, x3, x4, x5, 0.0, 0.0, 0.0, 0.0, 0.0);
1611 line.GetRotation(x0, x1, x2, x3);
1612 file << "\t\tTransform {" << endl << "\t\t\trotation " << x1 << " " << x2 << " " << x0 << " " << x3 << endl;
1613 x0 = (line.X1() + line.X2()) / 200;
1614 x1 = (line.Y1() + line.Y2()) / 200;
1615 x2 = (line.Z1() + line.Z2()) / 200;
1616 file << "\t\t\ttranslation " << x1 << " " << x2 << " " << x0 << endl\
1617 << "\t\t\tchildren [Bond" << (*l).second.n << " {}]}" << endl;
1618 }
1619 }
1620 n++;
1621 (*l).second.l.clear();
1622 }
1623 LinesMap.clear();
1624
1625 //end of the world
1626 file << "\t]" << endl << "}" << endl;
1627
1628 g_output_stream_write (output, file.str ().c_str (), file.str ().size (), NULL, &error);
1629 if (error) {
1630 cerr << "gio error: " << error->message << endl;
1631 g_error_free (error);
1632 g_object_unref (stream);
1633 throw (int) 1;
1634 }
1635 g_output_stream_close (output, NULL, NULL);
1636 g_object_unref (stream);
1637 }
1638 catch (int n) {
1639 // TODO: implement a meaningful error handler.
1640 }
1641 }
1642
Error(int num) const1643 void Document::Error (int num) const
1644 {
1645 char const *mess = NULL;
1646 GtkWidget* message;
1647 char *unescaped = g_uri_unescape_string (m_filename, NULL);
1648 switch (num) {
1649 case SAVE:
1650 mess = _("Could not save file\n%s");
1651 break;
1652 case LOAD:
1653 mess = _("Could not load file\n%s");
1654 break;
1655 case XML:
1656 mess = _("%s: invalid xml file.\nTree is empty?");
1657 break;
1658 case FORMAT:
1659 mess = _("%s: invalid file format.");
1660 break;
1661 }
1662 message = gtk_message_dialog_new (NULL, (GtkDialogFlags) 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, mess, unescaped);
1663 g_free (unescaped);
1664 g_signal_connect_swapped (G_OBJECT (message), "response", G_CALLBACK (gtk_widget_destroy), G_OBJECT (message));
1665 gtk_widget_show (message);
1666 }
1667
Load(const std::string & filename)1668 bool Document::Load (const std::string &filename)
1669 {
1670 xmlDocPtr xml = NULL;
1671 gchar *oldfilename, *oldtitle;
1672 // close all dialogs
1673 ClearDialogs ();
1674 if (m_filename)
1675 oldfilename = g_strdup (m_filename);
1676 else oldfilename = NULL;
1677 oldtitle = g_strdup (m_Title.c_str ());
1678 try {
1679 if (SetFileName (filename), !m_filename || !m_Label)
1680 throw (int) 1;
1681 if (!(xml = xmlParseFile (filename.c_str ())))
1682 throw (int) 2;
1683 if (xml->children == NULL)
1684 throw (int) 3;
1685 if (strcmp ((char*) xml->children->name, "crystal"))
1686 throw (int) 4;
1687 if (oldfilename)
1688 g_free(oldfilename);
1689 g_free (oldtitle);
1690 ParseXMLTree (xml->children);
1691 xmlFreeDoc (xml);
1692 return true;
1693 }
1694 catch (int num) {
1695 switch (num)
1696 {
1697 case 2:
1698 case 3:
1699 Error(XML);
1700 break;
1701 case 4:
1702 Error(FORMAT);
1703 break;
1704 default:
1705 Error(LOAD);
1706 }
1707 if (num > 0) {
1708 if (oldfilename) {
1709 SetFileName (oldfilename);
1710 g_free (oldfilename);
1711 } else {
1712 g_free (m_filename);
1713 m_filename = NULL;
1714 }
1715 SetTitle (oldtitle);
1716 g_free (oldtitle);
1717 }
1718 if (num > 1)
1719 xmlFreeDoc(xml);
1720 return false;
1721 }
1722 }
1723
cb_xml_to_vfs(GOutputStream * output,const char * buf,int nb)1724 static int cb_xml_to_vfs (GOutputStream *output, const char* buf, int nb)
1725 {
1726 GError *error = NULL;
1727 int n = g_output_stream_write (output, buf, nb, NULL, &error);
1728 if (error) {
1729 g_message ("GIO error: %s", error->message);
1730 g_error_free (error);
1731 }
1732 return n;
1733 }
1734
Save() const1735 void Document::Save() const
1736 {
1737 if (!m_filename)
1738 return;
1739 xmlDocPtr xml = NULL;
1740
1741 try {
1742 xml = BuildXMLTree();
1743
1744 if (!g_date_valid (&m_CreationDate))
1745 g_date_set_time_t (&const_cast <Document *> (this)->m_CreationDate, time (NULL));
1746 g_date_set_time_t (&const_cast <Document *> (this)->m_RevisionDate, time (NULL));
1747 gcu::WriteDate (xml->children, "creation", &m_CreationDate);
1748 gcu::WriteDate (xml->children, "revision", &m_RevisionDate);
1749 xmlNodePtr node;
1750
1751 if (m_Title.length () > 0) {
1752 node = xmlNewDocNode (xml, NULL, reinterpret_cast <xmlChar const *> ("title"), reinterpret_cast <xmlChar const *> (m_Title.c_str ()));
1753 if (node)
1754 xmlAddChild (xml->children, node);
1755 else
1756 throw (int) 0;
1757 }
1758 if ((m_Author && *m_Author) || (m_Mail && *m_Mail)) {
1759 node = xmlNewDocNode (xml, NULL, (xmlChar*) "author", NULL);
1760 if (node) {
1761 if (m_Author && *m_Author)
1762 xmlNewProp (node, (xmlChar*) "name", (xmlChar*) m_Author);
1763 if (m_Mail && *m_Mail)
1764 xmlNewProp (node, (xmlChar*) "e-mail", (xmlChar*) m_Mail);
1765 xmlAddChild (xml->children, node);
1766 }
1767 else
1768 throw (int) 0;
1769 }
1770 if (m_Comment && *m_Comment) {
1771 node = xmlNewDocNode (xml, NULL, (xmlChar*) "comment", (xmlChar*) m_Comment);
1772 if (node)
1773 xmlAddChild (xml->children, node);
1774 else
1775 throw (int) 0;
1776 }
1777 xmlIndentTreeOutput = true;
1778 xmlKeepBlanksDefault (0);
1779
1780 xmlOutputBufferPtr buf = xmlAllocOutputBuffer (NULL);
1781 GFile *file = g_file_new_for_uri (m_filename);
1782 GError *error = NULL;
1783 if (g_file_query_exists (file, NULL)) {
1784 // FIXME: for now, delete it, but we might make a backup?
1785 g_file_delete (file, NULL, &error);
1786 if (error) {
1787 g_message ("GIO error: %s", error->message);
1788 g_error_free (error);
1789 g_object_unref (file);
1790 throw (int) 1;
1791 }
1792 }
1793 GOutputStream *output = G_OUTPUT_STREAM (g_file_create (file, G_FILE_CREATE_NONE, NULL, &error));
1794 if (error) {
1795 g_message ("GIO error: %s", error->message);
1796 g_error_free (error);
1797 throw (int) 1;
1798 }
1799 buf->context = output;
1800 buf->closecallback = NULL;
1801 buf->writecallback = (xmlOutputWriteCallback) cb_xml_to_vfs;
1802 int n = xmlSaveFormatFileTo (buf, xml, NULL, true);
1803 g_output_stream_close (output, NULL, NULL);
1804 g_object_unref (file);
1805 if (n < 0)
1806 throw 1;
1807
1808 xmlFreeDoc (xml);
1809 const_cast <Document *> (this)->SetDirty (false);
1810 const_cast <Document *> (this)->m_ReadOnly = false; // if saving succeded, the file is not read only...
1811 }
1812 catch (int num) {
1813 xmlFreeDoc (xml);
1814 Error (SAVE);
1815 }
1816 }
1817
SaveAsImage(const std::string & filename,char const * type,std::map<std::string,std::string> & options)1818 void Document::SaveAsImage (const std::string &filename, char const *type, std::map < std::string, std::string>& options)
1819 {
1820 m_pActiveView->SaveAsImage (filename, type, options, GetApp ()->GetImageWidth (), GetApp ()->GetImageHeight (), !m_App->GetTransparentBackground ());
1821 }
1822
1823
1824 } // namespace gcr
1825