1 // -*- C++ -*-
2 
3 /*
4  * GChemPaint library
5  * molecule.cc
6  *
7  * Copyright (C) 2001-2011 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 "application.h"
27 #include "atom.h"
28 #include "bond.h"
29 #include "document.h"
30 #include "molecule.h"
31 #include "tool.h"
32 #include "view.h"
33 #include <gcugtk/stringdlg.h>
34 #include <gcugtk/ui-manager.h>
35 #include <gcu/chain.h>
36 #include <gsf/gsf-input-memory.h>
37 #include <gsf/gsf-input-stdio.h>
38 #include <gsf/gsf-output-memory.h>
39 #include <glib/gi18n-lib.h>
40 #include <unistd.h>
41 #include <cmath>
42 #include <cstring>
43 #include <map>
44 #include <set>
45 #include <sstream>
46 
47 using namespace gcu;
48 using namespace std;
49 
50 namespace gcp {
51 
52 class MoleculePrivate
53 {
54 public:
55 	static void ShowInChIKey (Molecule *mol);
56 	static void ShowInChI (Molecule *mol);
57 	static void ShowSMILES (Molecule *mol);
58 	static void ExportToGhemical (Molecule *mol);
59 	static void ExportTo3D (Molecule *mol);
60 	static void ExportToAvogadro (Molecule *mol);
61 	static char *Build3D (Molecule *mol);
62 };
63 
ShowInChIKey(Molecule * mol)64 void MoleculePrivate::ShowInChIKey (Molecule *mol)
65 {
66 
67 	new gcugtk::StringDlg (reinterpret_cast<Document *>(mol->GetDocument ()), mol->GetInChIKey (), gcugtk::StringDlg::INCHIKEY);
68 }
69 
ShowInChI(Molecule * mol)70 void MoleculePrivate::ShowInChI (Molecule *mol)
71 {
72 
73 	new gcugtk::StringDlg (reinterpret_cast<Document *>(mol->GetDocument ()), mol->GetInChI (), gcugtk::StringDlg::INCHI);
74 }
75 
ShowSMILES(Molecule * mol)76 void MoleculePrivate::ShowSMILES (Molecule *mol)
77 {
78 
79 	new gcugtk::StringDlg (reinterpret_cast<Document *>(mol->GetDocument ()), mol->GetSMILES (), gcugtk::StringDlg::SMILES);
80 }
81 
Build3D(Molecule * mol)82 char *MoleculePrivate::Build3D (Molecule *mol)
83 {
84 	std::string const &InChI = mol->GetInChI ();
85 	GsfInput *in = gsf_input_memory_new (reinterpret_cast <guint8 const *> (InChI.c_str ()), InChI.length (), false);
86 	char *cml = mol->GetDocument ()->GetApp ()->ConvertToCML (in, "inchi", "-hc --gen3D");
87 	g_object_unref (in);
88 	return cml;
89 }
90 
ExportToGhemical(Molecule * mol)91 void MoleculePrivate::ExportToGhemical (Molecule *mol)
92 {
93 	char *cml = Build3D (mol);
94 	if (!cml) // how does this happen?
95 		return;
96 	char *tmpname = g_strdup ("/tmp/gprXXXXXX.gpr");
97 	int f = g_mkstemp (tmpname);
98 	close (f);
99 	std::string uri = "file://";
100 	uri += tmpname;
101 	mol->GetDocument ()->GetApp ()->ConvertFromCML (cml, uri, "gpr");
102 	g_free (cml);
103 	char *command_line = g_strconcat ("ghemical -f ", tmpname, NULL);
104 	g_free (tmpname);
105 	g_spawn_command_line_async (command_line, NULL);
106 	g_free (command_line);
107 }
108 
ExportTo3D(Molecule * mol)109 void MoleculePrivate::ExportTo3D (Molecule *mol)
110 {
111 	char *cml = Build3D (mol);
112 	if (!cml) // how does this happen?
113 		return;
114 	char *tmpname = g_strdup ("/tmp/cmlXXXXXX.cml");
115 	int f = g_mkstemp (tmpname);
116 	write (f, cml, strlen (cml));
117 	close (f);
118 	g_free (cml);
119 	char *command_line = g_strconcat ("gchem3d-" GCU_API_VER " ", tmpname, NULL);
120 	g_free (tmpname);
121 	g_spawn_command_line_async (command_line, NULL);
122 	g_free (command_line);
123 }
124 
ExportToAvogadro(Molecule * mol)125 void MoleculePrivate::ExportToAvogadro (Molecule *mol)
126 {
127 	char *cml = Build3D (mol);
128 	if (!cml) // how does this happen?
129 		return;
130 	char *tmpname = g_strdup ("/tmp/cmlXXXXXX.cml");
131 	int f = g_mkstemp (tmpname);
132 	write (f, cml, strlen (cml));
133 	close (f);
134 	g_free (cml);
135 	char *command_line = g_strconcat ("avogadro ", tmpname, NULL);
136 	g_free (tmpname);
137 	g_spawn_command_line_async (command_line, NULL);
138 	g_free (command_line);
139 }
140 
do_select_alignment(GObject * action,Molecule * pMol)141 static void do_select_alignment (GObject *action, Molecule* pMol)
142 {
143 	Object *object = (Object*) g_object_get_data (action, "item");
144 	pMol->SelectAlignmentItem (object);
145 }
146 
do_open_in_calc(Molecule * pMol)147 static void do_open_in_calc (Molecule* pMol)
148 {
149 	pMol->OpenCalc ();
150 }
151 
Molecule(TypeId Type)152 Molecule::Molecule (TypeId Type): gcugtk::Molecule (Type, gcu::ContentType2D)
153 {
154 	m_Alignment = NULL;
155 	m_IsResidue = false;
156 }
157 
Molecule(Atom * pAtom)158 Molecule::Molecule (Atom* pAtom): gcugtk::Molecule (pAtom, gcu::ContentType2D)
159 {
160 	m_Alignment = NULL;
161 	m_IsResidue = false;
162 }
163 
~Molecule()164 Molecule::~Molecule ()
165 {
166 }
167 
AddChild(Object * object)168 void Molecule::AddChild (Object* object)
169 {
170 	switch (object->GetType ()) {
171 	case gcu::AtomType:
172 			if (object->GetParent () && object->GetParent ()->GetType () == FragmentType)
173 				object = object->GetParent ();
174 			else {
175 				gcu::Molecule::AddChild (object);
176 				break;
177 			}
178 	case FragmentType: {
179 		Fragment *fragment = reinterpret_cast<Fragment *> (object);
180 		m_Fragments.remove (fragment);
181 		AddFragment (fragment);
182 		break;
183 	}
184 	default:
185 		gcu::Molecule::AddChild (object);
186 		break;
187 	}
188 }
189 
AddAtom(gcu::Atom * pAtom)190 void Molecule::AddAtom (gcu::Atom* pAtom)
191 {
192 	gcu::Molecule::AddAtom (pAtom);
193 	if (!pAtom->GetZ ())
194 		m_IsResidue = true;
195 }
196 
AddFragment(Fragment * pFragment)197 void Molecule::AddFragment (Fragment* pFragment)
198 {
199 	m_Fragments.remove (pFragment); // just in case
200 	m_Fragments.push_back (pFragment);
201 	Object::AddChild (pFragment);
202 }
203 
AddBond(gcu::Bond * pBond)204 void Molecule::AddBond (gcu::Bond* pBond)
205 {
206 	if (pBond->GetAtom (0) && pBond->GetAtom (1))
207 		CheckCrossings (reinterpret_cast <Bond *> (pBond));
208 	gcu::Molecule::AddBond (pBond);
209 	EmitSignal (OnChangedSignal);
210 }
211 
Remove(Object * pObject)212 void Molecule::Remove (Object* pObject)
213 {
214 	if (pObject == m_Alignment)
215 		m_Alignment = NULL;
216 	switch (pObject->GetType ()) {
217 	case FragmentType:
218 		m_Fragments.remove ((Fragment*) pObject);
219 		break;
220 	default:
221 		gcu::Molecule::Remove (pObject);
222 		break;
223 	}
224 	pObject->SetParent (GetParent ());
225 }
226 
227 typedef struct {
228 	Atom *pAtom;
229 	int sb; // number of shared bonds
230 	int so0, so1; // total bond order of shared bonds for each atom
231 } MergedAtom;
232 
Merge(Molecule * pMolecule,bool RemoveDuplicates)233 bool Molecule::Merge (Molecule* pMolecule, bool RemoveDuplicates)
234 {
235 	Atom* pAtom;
236 	Fragment* pFragment;
237 	Bond* pBond;
238 	Chain* pChain;
239 	Cycle* pCycle;
240 	if (RemoveDuplicates) {
241 		std::list<gcu::Atom*>::iterator i = m_Atoms.begin (), end = m_Atoms.end ();
242 		double x, y, x0, y0, x1, y1;
243 		MergedAtom *ma;
244 		Bond *b0, *b1;
245 		bool DoMerge;
246 		int n;
247 		map<Atom*, MergedAtom*> AtomMap;
248 		map<Atom*, MergedAtom*>::iterator j, endj;
249 		map<Bond*, Bond*> BondMap;
250 		map<Bond*, Bond*>::iterator b, endb;
251 		for (; i != end; i++) {
252 			(*i)->GetCoords (&x, &y);
253 			pAtom = (Atom*) pMolecule->GetAtomAt (x, y);
254 			if (pAtom) {
255 				if ((*i)->GetZ()!= pAtom->GetZ())
256 				{
257 					// Cannot merge atoms which are not of the same element.
258 					endj = AtomMap.end ();
259 					for (j = AtomMap.begin (); j != endj; j++)
260 						delete (*j).second;
261 					return false;
262 				}
263 				ma = new MergedAtom;
264 				ma->sb = ma->so0 = ma->so1 = 0;
265 				ma->pAtom = pAtom;
266 				endj = AtomMap.end ();
267 				for (j = AtomMap.begin (); j != endj; j++) {
268 					if ((b1 = (Bond*) pAtom->GetBond ((*j).second->pAtom))) {
269 						b0 = (Bond*) (*i)->GetBond ((*j).first);
270 						if (b0) {
271 							ma->sb++;
272 							ma->so0 += b0->GetOrder ();
273 							ma->so1 += b1->GetOrder ();
274 							(*j).second->sb++;
275 							(*j).second->so0 += b0->GetOrder ();
276 							(*j).second->so1 += b1->GetOrder ();
277 							BondMap[b0] = b1;
278 						}
279 					}
280 				}
281 				AtomMap[reinterpret_cast <Atom *> (*i)] =  ma;
282 			}
283 		}
284 		// Now check if merging is possible for each shared atom.
285 		DoMerge = AtomMap.size () != 0;
286 		if (DoMerge) {
287 			x = y = 0.;
288 			endj = AtomMap.end ();
289 			for (j = AtomMap.begin (); j != endj; j++) {
290 				ma = (*j).second;
291 				n = ma->pAtom->GetTotalBondsNumber () - ma->so0 - ma->so1 + ma->sb;
292 				if (!(*j).first->AcceptNewBonds (n)) {
293 					DoMerge = false;
294 					break;
295 				}
296 				n = (*j).first->GetTotalBondsNumber () - ma->so0 - ma->so1 + ma->sb;
297 				if (!ma->pAtom->AcceptNewBonds (n)) {
298 					DoMerge = false;
299 					break;
300 				}
301 				(*j).first->GetCoords (&x0, &y0);
302 				ma->pAtom->GetCoords (&x1, &y1);
303 				x += x1 - x0;
304 				y += y1 - y0;
305 			}
306 		}
307 		if (DoMerge) {
308 			//First align molecules
309 			x /= 2.* AtomMap.size ();
310 			y /= 2.* AtomMap.size ();
311 			Move (x, y);
312 			pMolecule->Move (-x, -y);
313 
314 			//Then align each atom individually
315 			endj = AtomMap.end ();
316 			for (j = AtomMap.begin (); j != endj; j++) {
317 				(*j).first->GetCoords (&x0, &y0);
318 				(*j).second->pAtom->GetCoords (&x1, &y1);
319 				(*j).first->Move ((x1 - x0) / 2.,(y1 - y0) / 2.);
320 			}
321 			View *pView = ((Document*) GetDocument ())->GetView ();
322 
323 			/* Treat shared bonds (set order to 1, store max order in b1 and remove the bond
324 			 * from pMolecule. */
325 			endb = BondMap.end ();
326 			for (b = BondMap.begin (); b != endb; b++) {
327 				b1 = (*b).second;
328 				n = (*b).first->GetOrder ();
329 				pView->Remove (b1);
330 				pMolecule->Remove (b1);
331 				(*b).first->SetOrder (1);
332 				pAtom = (Atom*) b1->GetAtom (0);
333 				pAtom->RemoveBond (b1);
334 				b1->ReplaceAtom (pAtom, NULL);
335 				pAtom = (Atom*) b1->GetAtom (1);
336 				pAtom->RemoveBond (b1);
337 				b1->ReplaceAtom (pAtom, NULL);
338 				if (n > b1->GetOrder ())
339 					b1->SetOrder (n);
340 			}
341 
342 			// Treat shared atoms and delete from pMolecule
343 			map< gcu::Atom *, gcu::Bond * >::iterator ai;
344 			endj = AtomMap.end ();
345 			for (j = AtomMap.begin (); j != endj; j++) {
346 				b0 = (Bond*) (*j).second->pAtom->GetFirstBond (ai);
347 				while (b0) {
348 					b0->ReplaceAtom ((*j).second->pAtom, (*j).first);
349 					(*j).first->AddBond (b0);
350 					b0 = (Bond*) (*j).second->pAtom->GetNextBond (ai);
351 				}
352 				pMolecule->Remove ((*j).second->pAtom);
353 				pView->Remove ((*j).second->pAtom);
354 				delete (*j).second->pAtom;
355 			}
356 
357 			// Try to restore max bond order for shared bonds and destroy old bonds
358 			endb = BondMap.end ();
359 			for (b = BondMap.begin (); b != endb; b++) {
360 				n = (*b).second->GetOrder () - 1;
361 				b0 = (*b).first;
362 				while ((n > 0) &&
363 						(!((Atom*) b0->GetAtom (0))->AcceptNewBonds (n) ||
364 						!((Atom*) b0->GetAtom(1))->AcceptNewBonds (n)))
365 					n--;
366 				if (n > 0)
367 					b0->SetOrder (n + 1);
368 				delete (*b).second;
369 			}
370 		}
371 
372 		// Clean memory
373 		endj = AtomMap.end ();
374 		for (j = AtomMap.begin (); j != endj; j++)
375 			delete (*j).second;
376 		//return if merging is not possible
377 		if (!DoMerge) return false;
378 	}
379 	while (!pMolecule->m_Atoms.empty ()) {
380 		pAtom =  reinterpret_cast <Atom *> (pMolecule->m_Atoms.front ());
381 		AddAtom (pAtom);
382 		pMolecule->m_Atoms.pop_front ();
383 	}
384 	while (!pMolecule->m_Fragments.empty ()) {
385 		pFragment = pMolecule->m_Fragments.front ();
386 		AddFragment (pFragment);
387 		pMolecule->m_Fragments.pop_front ();
388 	}
389 	while (!pMolecule->m_Bonds.empty ()) {
390 		pBond =  reinterpret_cast <Bond *> (pMolecule->m_Bonds.front ());
391 		AddBond (pBond);
392 		pMolecule->m_Bonds.pop_front ();
393 	}
394 	while (!pMolecule->m_Chains.empty ()) {
395 		//FIXME: Chains should change
396 		pChain = pMolecule->m_Chains.front ();
397 		m_Chains.push_back (pChain);
398 		pMolecule->m_Chains.pop_front ();
399 	}
400 	while (!pMolecule->m_Cycles.empty()) {
401 		pCycle = pMolecule->m_Cycles.front ();
402 		m_Cycles.push_back (pCycle);
403 		pMolecule->m_Cycles.pop_front ();
404 	}
405 	Object* pObj = pMolecule->GetParent ();
406 	delete pMolecule;
407 	pObj->EmitSignal (OnChangedSignal);
408 	if (RemoveDuplicates)
409 		UpdateCycles ();
410 	EmitSignal (OnChangedSignal);
411 	return true;
412 }
413 
Load(xmlNodePtr node)414 bool Molecule::Load (xmlNodePtr node)
415 {
416 	char* buf;
417 	xmlNodePtr child;
418 	Object* pObject;
419 	Document* pDoc = (Document*) GetDocument ();
420 
421 	buf = (char*) xmlGetProp (node, (xmlChar*) "id");
422 	if (buf) {
423 		SetId (buf);
424 		xmlFree (buf);
425 	}
426 	child = GetNodeByName (node, "atom");
427 	while (child) {
428 		pObject = new Atom ();
429 		if (pDoc)
430 			AddChild (pObject);
431 		if (!pObject->Load (child)) {
432 			delete pObject;
433 			return false;
434 		}
435 		if (pDoc)
436 			pDoc->AddAtom ((Atom*) pObject);
437 		AddAtom ((Atom*) pObject);
438 		child = GetNextNodeByName (child->next, "atom");
439 	}
440 	// FIXME, the following looks like a kludge
441 	child = GetNodeByName (node, "pseudo-atom");
442 	while (child) {
443 		pObject = (GetApplication ())? CreateObject ("pseudo-atom", pDoc): Application::GetApplication ("GChemPaint")->CreateObject ("pseudo-atom", pDoc);
444 		if (pDoc)
445 			AddChild (pObject);
446 		if (!pObject->Load (child)) {
447 			delete pObject;
448 			return false;
449 		}
450 		if (pDoc)
451 			pDoc->AddAtom ((Atom*) pObject);
452 		AddAtom ((Atom*) pObject);
453 		child = GetNextNodeByName (child->next, "pseudo-atom");
454 	}
455 
456 	child = GetNodeByName (node, "fragment");
457 	while (child) {
458 		pObject = new Fragment ();
459 		if (pDoc)
460 			AddChild (pObject);
461 		if (!pObject->Load (child))  {
462 			delete pObject;
463 			return false;
464 		}
465 		if (pDoc)
466 			pDoc->AddFragment ((Fragment*) pObject);
467 		child = GetNextNodeByName (child->next, "fragment");
468 	}
469 
470 	child = GetNodeByName (node, "bond");
471 	while (child) {
472 		pObject = new Bond ();
473 		AddBond ((Bond*) pObject);
474 		if (!pObject->Load (child)) {
475 			m_Bonds.remove ((Bond*) pObject);
476 			delete pObject;
477 			return false;
478 		}
479 		if (pDoc)
480 			pDoc->AddBond ((Bond*) pObject);
481 		child = GetNextNodeByName (child->next, "bond");
482 		CheckCrossings ((Bond*) pObject);
483 	}
484 /*	if (!m_Atoms.empty ()) {
485 		Atom* pAtom =  reinterpret_cast <Atom *> (m_Atoms.front ());
486 		list<gcu::Atom*>::iterator i = m_Atoms.begin ();
487 		i++;
488 		for (; i != m_Atoms.end (); i++)
489 			(*i)->SetParent (NULL);
490 		// erase cycles
491 		list<gcu::Bond*>::iterator j, jend = m_Bonds.end ();
492 		for (j = m_Bonds.begin (); j != jend; j++)
493 			(*j)->RemoveAllCycles ();
494 		Chain* pChain = new Chain (this, pAtom); //will find the cycles
495 		delete pChain;
496 	}*/
497 
498 	child = GetNodeByName (node, "brackets");
499 	while (child) {
500 		pObject = CreateObject ((const char*) child->name, this);
501 		if (pDoc)
502 			AddChild (pObject);
503 		if (!pObject->Load (child))  {
504 			delete pObject;
505 			return false;
506 		}
507 		child = GetNextNodeByName (child->next, "brackets");
508 	}
509 	buf = (char*) xmlGetProp (node, (const xmlChar*) "valign");
510 	if (buf) {
511 		pDoc->SetTarget (buf, reinterpret_cast <Object **> (&m_Alignment), this, this, ActionDelete);
512 		xmlFree (buf);
513 	}
514 	pDoc->ObjectLoaded (this);
515 	return true;
516 }
517 
Clear()518 void Molecule::Clear ()
519 {
520 	m_Bonds.clear ();
521 	m_Atoms.clear ();
522 	m_Fragments.clear ();
523 }
524 
Transform2D(Matrix2D & m,double x,double y)525 void Molecule::Transform2D (Matrix2D& m, double x, double y)
526 {
527 	Object::Transform2D (m, x, y);
528 	std::list<gcu::Atom*>::iterator i = m_Atoms.begin ();
529 	for (; i != m_Atoms.end (); i++)
530 	if (((*i)->GetZ () != 6) &&  reinterpret_cast <Atom *> (*i)->GetAttachedHydrogens () &&
531 		(*i)->GetBondsNumber ())  reinterpret_cast <Atom *> (*i)->Update ();
532 }
533 
GetAtomAt(double x,double y,G_GNUC_UNUSED double z)534 Object* Molecule::GetAtomAt (double x, double y, G_GNUC_UNUSED double z)
535 {
536 	// Make use of Bond::GetAtomAt
537 	std::list<gcu::Bond*>::iterator n, end = m_Bonds.end ();
538 	Object* pObj = NULL;
539 	for (n = m_Bonds.begin(); n != end; n++)
540 		if ((pObj = (*n)->GetAtomAt (x, y)))
541 			break;
542 	return pObj;
543 }
544 
GetYAlign()545 double Molecule::GetYAlign ()
546 {
547 	if (m_Alignment)
548 		return m_Alignment->GetYAlign ();
549 	double y, maxy = - DBL_MAX, miny = DBL_MAX;
550 	std::list<gcu::Atom*>::iterator i = m_Atoms.begin (), end = m_Atoms.end ();
551 	for (; i != end; i++) {
552 		y =  reinterpret_cast <Atom *> (*i)->GetYAlign ();
553 		if (y < miny)
554 			miny = y;
555 		if (y > maxy)
556 			maxy = y;
557 	}
558 	std::list<Fragment*>::iterator ig = m_Fragments.begin (), endg = m_Fragments.end ();
559 	for (; ig != endg; ig++) {
560 		y = (*ig)->GetYAlign ();
561 		if (y < miny)
562 			miny = y;
563 		if (y > maxy)
564 			maxy = y;
565 	}
566 	return (miny + maxy) / 2.0;
567 }
568 
BuildContextualMenu(gcu::UIManager * UIManager,Object * object,double x,double y)569 bool Molecule::BuildContextualMenu (gcu::UIManager *UIManager, Object *object, double x, double y)
570 {
571 	if (m_IsResidue)
572 		return false;
573 	GtkUIManager *uim = static_cast < gcugtk::UIManager * > (UIManager)->GetUIManager ();
574 	GtkActionGroup *group = gtk_action_group_new ("molecule");
575 	GtkAction *action;
576 	action = gtk_action_new ("Molecule", _("Molecule"), NULL, NULL);
577 	gtk_action_group_add_action (group, action);
578 	g_object_unref (action);
579 	bool result = false;
580 	if (!m_Fragments.size ()) {
581 		Application *app = static_cast <Document *> (GetDocument ())->GetApplication ();
582 		if (app->Have3DSupport ()) {
583 			action = gtk_action_new ("open3d", _("Open 3D model in"), NULL, NULL);
584 			gtk_action_group_add_action (group, action);
585 			g_object_unref (action);
586 			if (app->GetHaveGhemical ()) {
587 				action = gtk_action_new ("ghemical", _("Ghemical"), NULL, NULL);
588 				g_signal_connect_swapped (action, "activate", G_CALLBACK (MoleculePrivate::ExportToGhemical), this);
589 				gtk_action_group_add_action (group, action);
590 				g_object_unref (action);
591 				gtk_ui_manager_add_ui_from_string (uim, "<ui><popup><menu action='Molecule'><menu action='open3d'><menuitem action='ghemical'/></menu></menu></popup></ui>", -1, NULL);
592 			}
593 			if (app->GetHaveGChem3D ()) {
594 				action = gtk_action_new ("gchem3d", _("GChem3D"), NULL, NULL);
595 				g_signal_connect_swapped (action, "activate", G_CALLBACK (MoleculePrivate::ExportTo3D), this);
596 				gtk_action_group_add_action (group, action);
597 				g_object_unref (action);
598 				gtk_ui_manager_add_ui_from_string (uim, "<ui><popup><menu action='Molecule'><menu action='open3d'><menuitem action='gchem3d'/></menu></menu></popup></ui>", -1, NULL);
599 			}
600 			if (app->GetHaveAvogadro ()) {
601 				action = gtk_action_new ("avogadro", _("Avogadro"), NULL, NULL);
602 				g_signal_connect_swapped (action, "activate", G_CALLBACK (MoleculePrivate::ExportToAvogadro), this);
603 				gtk_action_group_add_action (group, action);
604 				g_object_unref (action);
605 				gtk_ui_manager_add_ui_from_string (uim, "<ui><popup><menu action='Molecule'><menu action='open3d'><menuitem action='avogadro'/></menu></menu></popup></ui>", -1, NULL);
606 			}
607 		}
608 		BuildDatabasesMenu (uim, "<ui><popup><menu action='Molecule'>", "</menu></popup></ui>");
609 		action = gtk_action_new ("inchi", _("Generate InChI"), NULL, NULL);
610 		g_signal_connect_swapped (action, "activate", G_CALLBACK (MoleculePrivate::ShowInChI), this);
611 		gtk_action_group_add_action (group, action);
612 		g_object_unref (action);
613 		gtk_ui_manager_add_ui_from_string (uim, "<ui><popup><menu action='Molecule'><menuitem action='inchi'/></menu></popup></ui>", -1, NULL);
614 		action = gtk_action_new ("inchikey", _("Generate InChIKey"), NULL, NULL);
615 		g_signal_connect_swapped (action, "activate", G_CALLBACK (MoleculePrivate::ShowInChIKey), this);
616 		gtk_action_group_add_action (group, action);
617 		g_object_unref (action);
618 		gtk_ui_manager_add_ui_from_string (uim, "<ui><popup><menu action='Molecule'><menuitem action='inchikey'/></menu></popup></ui>", -1, NULL);
619 		action = gtk_action_new ("smiles", _("Generate SMILES"), NULL, NULL);
620 		g_signal_connect_swapped (action, "activate", G_CALLBACK (MoleculePrivate::ShowSMILES), this);
621 		gtk_action_group_add_action (group, action);
622 		g_object_unref (action);
623 		gtk_ui_manager_add_ui_from_string (uim, "<ui><popup><menu action='Molecule'><menuitem action='smiles'/></menu></popup></ui>", -1, NULL);
624 		action = gtk_action_new ("calc", _("Open in Calculator"), NULL, NULL);
625 		g_signal_connect_swapped (action, "activate", G_CALLBACK (do_open_in_calc), this);
626 		gtk_action_group_add_action (group, action);
627 		g_object_unref (action);
628 		gtk_ui_manager_add_ui_from_string (uim, "<ui><popup><menu action='Molecule'><menuitem action='calc'/></menu></popup></ui>", -1, NULL);
629 		result = true;
630 	}
631 	if (m_Bonds.size ()) {
632 		action = gtk_action_new ("select-align", _("Select alignment item"), NULL, NULL);
633 		g_signal_connect (action, "activate", G_CALLBACK (do_select_alignment), this);
634 		g_object_set_data (G_OBJECT (action), "item", object);
635 		gtk_action_group_add_action (group, action);
636 		g_object_unref (action);
637 		gtk_ui_manager_add_ui_from_string (uim, "<ui><popup><menu action='Molecule'><menuitem action='select-align'/></menu></popup></ui>", -1, NULL);
638 		result = true;
639 	}
640 	gtk_ui_manager_insert_action_group (uim, group, 0);
641 	g_object_unref (group);
642 	return result | Object::BuildContextualMenu (UIManager, object, x, y);
643 }
644 
SelectAlignmentItem(Object * child)645 void Molecule::SelectAlignmentItem (Object *child)
646 {
647 	m_Alignment = (m_Alignment != child)? child: NULL;
648 	EmitSignal (OnChangedSignal);
649 }
650 
Save(xmlDocPtr xml) const651 xmlNodePtr Molecule::Save (xmlDocPtr xml) const
652 {
653 	xmlNodePtr node = Object::Save (xml);
654 	if (!node)
655 		return NULL;
656 	if (m_Alignment)
657 		xmlNewProp (node, (const xmlChar*) "valign", (const xmlChar*) m_Alignment->GetId ());
658 	return node;
659 }
660 
OnSignal(G_GNUC_UNUSED SignalId Signal,G_GNUC_UNUSED Object * Child)661 bool Molecule::OnSignal (G_GNUC_UNUSED SignalId Signal, G_GNUC_UNUSED Object *Child)
662 {
663 	View *view = static_cast < Document * > (GetDocument ())->GetView ();
664 	gcu::Object *obj;
665 	std::set < gcu::Object * >::iterator j;
666 	for (obj = GetFirstLink (j); obj; obj = GetNextLink (j))
667 		view->Update (obj);
668 	ResetIndentifiers ();
669 	return true;
670 }
671 
OpenCalc()672 void Molecule::OpenCalc ()
673 {
674 	list<gcu::Atom*>::iterator ia, enda = m_Atoms.end ();
675 	ostringstream ofs;
676 	int nH;
677 	ofs << "gchemcalc-" API_VERSION " ";
678 	for (ia = m_Atoms.begin(); ia != enda; ia++) {
679 		ofs << (*ia)->GetSymbol();
680 		nH = reinterpret_cast <Atom *> (*ia)->GetAttachedHydrogens ();
681 		if (nH > 0) {
682 			ofs << "H" << nH;
683 		}
684 	}
685 	g_spawn_command_line_async (ofs.str ().c_str (), NULL);
686 }
687 
CheckCrossings(Bond * pBond)688 void Molecule::CheckCrossings (Bond *pBond)
689 {
690 	View *pView = reinterpret_cast<Document*> (GetDocument ())->GetView ();
691 	list<gcu::Bond*>::iterator i, iend = m_Bonds.end ();
692 	for (i = m_Bonds.begin (); i != iend; i++)
693 		if (((*i) != pBond) && reinterpret_cast <Bond *> (*i)->IsCrossing (pBond)) {
694 			pView->Update (pBond);
695 			pView->Update (*i);
696 		}
697 }
698 
GetRawFormula() const699 std::string Molecule::GetRawFormula () const
700 {
701 	ostringstream ofs;
702 
703 	if (!m_Fragments.size ()) {
704 		// we do not support fragments at the moment
705 		map<string, int> elts;
706 		list<gcu::Atom*>::const_iterator ia, enda = m_Atoms.end ();
707 		for (ia = m_Atoms.begin(); ia != enda; ia++) {
708 			if ((*ia)->GetZ () == 0)
709 				continue;
710 			elts[(*ia)->GetSymbol ()]++;
711 			elts["H"] += reinterpret_cast <Atom *> (*ia)->GetAttachedHydrogens ();
712 		}
713 		if (elts["C"] > 0) {
714 			ofs << "C" << elts["C"];
715 			elts.erase ("C");
716 		}
717 		if (elts["H"] > 0) {
718 			ofs << "H" << elts["H"];
719 			elts.erase ("H");
720 		}
721 		map<string, int>::iterator is, isend = elts.end ();
722 		for (is = elts.begin (); is != isend; is++)
723 			ofs << (*is).first << (*is).second;
724 	}
725 
726 	return ofs.str ();
727 }
728 
BuildConnectivity(gcu::Atom * atom,set<gcu::Atom * > & ConnectedAtoms)729 static void BuildConnectivity (gcu::Atom *atom, set < gcu::Atom * > &ConnectedAtoms)
730 {
731 	std::map < gcu::Atom *, gcu::Bond * >::iterator i;
732 	gcu::Bond *bond;
733 	ConnectedAtoms.insert (atom);
734 	for (bond = atom->GetFirstBond (i); bond; bond = atom->GetNextBond (i)) {
735 		gcu::Atom *end = bond->GetAtom (atom);
736 		if (ConnectedAtoms.find (end) == ConnectedAtoms.end ())
737 			BuildConnectivity (end, ConnectedAtoms);
738 	}
739 }
740 
OnLoaded()741 void Molecule::OnLoaded ()
742 {
743 	// First update fragments;
744 	std::list < Fragment * >::iterator f, fend = m_Fragments.end ();
745 	for (f = m_Fragments.begin (); f != fend; f++)
746 		(*f)->Update ();
747 	// Then check if we have only one molecule or split, this might happen with
748 	// cml (root node being molecule) or malformed files.
749 	if (m_Atoms.size () + m_Fragments.size () > 1) {
750 		/* FIXME: what should be done with fragments? */
751 		std::set < gcu::Atom * > ConnectedAtoms;
752 		std::list < gcu::Atom * >:: iterator i;
753 		while (1) {
754 			if (m_Atoms.size () > 0)
755 				BuildConnectivity (GetFirstAtom (i), ConnectedAtoms);
756 			else if (m_Fragments.size () > 0) {
757 				BuildConnectivity ((*m_Fragments.begin())->GetAtom (), ConnectedAtoms);
758 			} else
759 				break;
760 			if (m_Atoms.size () + m_Fragments.size () == ConnectedAtoms.size ())
761 				break;
762 			// now split the molecule
763 			Molecule *new_mol;
764 			Atom *atom = (m_Atoms.size ())?
765 							static_cast < Atom * > (GetFirstAtom (i)):
766 							(*m_Fragments.begin())->GetAtom ();
767 			new_mol = new Molecule ();
768 			GetParent ()->AddChild (new_mol);
769 			new_mol->AddChild (atom);
770 			gcu::Chain *chain = new gcu::Chain (new_mol, atom);
771 			delete chain;
772 			// move chiral atoms to new mol if needed
773 			std::set < gcu::Atom * >::iterator i, iend = ConnectedAtoms.end ();
774 			for (i = ConnectedAtoms.begin (); i != iend; i++) {
775 				Atom *atom = static_cast < Atom * > (*i);
776 				if (m_ChiralAtoms.find (atom) != m_ChiralAtoms.end ()) {
777 					m_ChiralAtoms.erase (atom);
778 					new_mol->m_ChiralAtoms.insert (atom);
779 				}
780 			}
781 			// remove new_mol children from there
782 			std::list < gcu::Atom * >::iterator a, aend = new_mol->m_Atoms.end ();
783 			std::list < gcu::Bond * >::iterator b, bend = new_mol->m_Bonds.end ();
784 			fend = new_mol->m_Fragments.end ();
785 			// update fragments if needed
786 			for (a = new_mol->m_Atoms.begin (); a != aend; a++)
787 				m_Atoms.remove (*a);
788 			for (b = new_mol->m_Bonds.begin (); b != bend; b++)
789 				m_Bonds.remove (*b);
790 			for (f = new_mol->m_Fragments.begin (); f != fend; f++)
791 				m_Fragments.remove (*f);
792 			// clean connected atoms for the next round
793 			ConnectedAtoms.clear ();
794 		}
795 	}
796 	UpdateCycles ();
797 	// now we need to consider atoms with parity (essentially on import)
798 	std::set < Atom * >::iterator it, end = m_ChiralAtoms.end ();
799 	std::set < Atom * > done;
800 	for (it = m_ChiralAtoms.begin (); it != end; it++)
801 		if ((*it)->UpdateStereoBonds ())
802 			done.insert (*it);
803 	end = done.end ();
804 	// for now, we don't keep them
805 	for (it = done.begin (); it != end; it++)
806 		m_ChiralAtoms.erase (*it);
807 	// check for crossing bonds
808 	std::list < gcu::Bond * >::const_iterator i;
809 	Bond const *bond = static_cast < Bond const * > (GetFirstBond (i));
810 	while (bond) {
811 		CheckCrossings (const_cast <Bond *> (bond));
812 		bond = static_cast < Bond const * > (GetNextBond (i));
813 	}
814 	GetParent ()->OnLoaded (); // FIXME: shouldn't needed.
815 }
816 
GetAtomsNumber() const817 unsigned Molecule::GetAtomsNumber () const
818 {
819 	return m_Atoms.size () + m_Fragments.size ();
820 }
821 
GetMeanBondLength() const822 double Molecule::GetMeanBondLength () const
823 {
824 	unsigned n = 0;
825 	double l = 0;
826 	std::list < gcu::Bond * >::const_iterator i, end = m_Bonds.end ();
827 	for (i = m_Bonds.begin (); i != end; i++) {
828 		n++;
829 		l += (*i)->Get2DLength ();
830 	}
831 	return l / n;
832 }
833 
AtomIsChiral(Atom * atom) const834 bool Molecule::AtomIsChiral (Atom *atom) const {
835 	std::set < Atom * >::const_iterator it = m_ChiralAtoms.find (atom);
836 	if (it == m_ChiralAtoms.end ())
837 		return false;
838 	return atom->HasStereoBond ();
839 }
840 
841 }	//	namespace gcp
842