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