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