// -*- C++ -*- /* * GChemPaint library * molecule.cc * * Copyright (C) 2001-2007 Jean Bréfort * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #include "gchempaint-config.h" #include "molecule.h" #include "document.h" #include "view.h" #include "application.h" #include "tool.h" #include "stringdlg.h" #include #include #include #include #include #include static void do_export_to_ghemical (gcpMolecule* pMol) { pMol->ExportToGhemical (); } static void do_select_alignment (GObject *action, gcpMolecule* pMol) { Object *object = (Object*) g_object_get_data (action, "item"); pMol->SelectAlignmentItem (object); } static void do_build_inchi (gcpMolecule* pMol) { pMol->ShowInChI (); } static void do_build_smiles (gcpMolecule* pMol) { pMol->BuildSmiles (); } static void do_show_webbook (gcpMolecule* pMol) { pMol->ShowWebBase ("http://webbook.nist.gov/cgi/cbook.cgi?Name=", "&Units=SI"); } static void do_show_pubchem (gcpMolecule* pMol) { pMol->ShowWebBase ("http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?CMD=search&DB=pccompound&term=\"", "\""); } static void do_open_in_calc (gcpMolecule* pMol) { pMol->OpenCalc (); } gcpMolecule::gcpMolecule(TypeId Type): Object(Type) { m_Alignment = NULL; m_Changed = true; } gcpMolecule::gcpMolecule(gcpAtom* pAtom): Object(MoleculeType) { AddAtom(pAtom); gcpChain* pChain = new gcpChain(this, pAtom); //will find the cycles delete pChain; m_Alignment = NULL; m_Changed = true; } gcpMolecule::~gcpMolecule() { std::list::iterator n; for (n = m_Bonds.begin(); n != m_Bonds.end(); n++) (*n)->RemoveAllCycles(); while (!m_Cycles.empty()) { delete m_Cycles.front(); m_Cycles.pop_front(); } while (!m_Chains.empty()) { delete m_Chains.front(); m_Chains.pop_front(); } } void gcpMolecule::AddAtom (gcpAtom* pAtom) { m_Atoms.remove (pAtom); // avoid duplicates m_Atoms.push_back (pAtom); AddChild (pAtom); } void gcpMolecule::AddFragment(gcpFragment* pFragment) { m_Fragments.push_back(pFragment); AddChild(pFragment); } void gcpMolecule::AddBond(gcpBond* pBond) { if (pBond->GetAtom (0) && pBond->GetAtom (1)) CheckCrossings (pBond); m_Bonds.push_back(pBond); AddChild(pBond); EmitSignal (OnChangedSignal); } void gcpMolecule::Remove(Object* pObject) { if (pObject == m_Alignment) m_Alignment = NULL; switch (pObject->GetType()) { case AtomType: m_Atoms.remove((gcpAtom*)pObject); break; case BondType: m_Bonds.remove((gcpBond*)pObject); break; case FragmentType: m_Fragments.remove((gcpFragment*)pObject); break; } pObject->SetParent(GetParent()); } void gcpMolecule::UpdateCycles(gcpBond* pBond) //FIXME: function not totally implemented { gcpChain* pChain = new gcpChain(this, pBond); //will find the cycles delete pChain; } typedef struct { gcpAtom *pAtom; int sb; // number of shared bonds int so0, so1; // total bond order of shared bonds for each atom } MergedAtom; bool gcpMolecule::Merge(gcpMolecule* pMolecule, bool RemoveDuplicates) { gcpAtom* pAtom; gcpFragment* pFragment; gcpBond* pBond; gcpChain* pChain; gcpCycle* pCycle; if (RemoveDuplicates) { std::list::iterator i = m_Atoms.begin (), end = m_Atoms.end (); double x, y, x0, y0, x1, y1; MergedAtom *ma; gcpBond *b0, *b1; bool DoMerge; int n; map AtomMap; map::iterator j, endj; map BondMap; map::iterator b, endb; for (; i != end; i++) { (*i)->GetCoords (&x, &y); pAtom = (gcpAtom*) pMolecule->GetAtomAt (x, y); if (pAtom) { if ((*i)->GetZ()!= pAtom->GetZ()) { // Cannot merge atoms which are not of the same element. endj = AtomMap.end (); for (j = AtomMap.begin (); j != endj; j++) delete (*j).second; return false; } ma = new MergedAtom; ma->sb = ma->so0 = ma->so1 = 0; ma->pAtom = pAtom; endj = AtomMap.end (); for (j = AtomMap.begin (); j != endj; j++) { if ((b1 = (gcpBond*) pAtom->GetBond ((*j).second->pAtom))) { b0 = (gcpBond*) (*i)->GetBond ((*j).first); if (b0) { ma->sb++; ma->so0 += b0->GetOrder (); ma->so1 += b1->GetOrder (); (*j).second->sb++; (*j).second->so0 += b0->GetOrder (); (*j).second->so1 += b1->GetOrder (); BondMap[b0] = b1; } } } AtomMap[*i] = ma; } } // Now check if merging is possible for each shared atom. DoMerge = AtomMap.size() != 0; if (DoMerge){ x = y = 0.; endj = AtomMap.end (); for (j = AtomMap.begin (); j != endj; j++) { ma = (*j).second; n = ma->pAtom->GetTotalBondsNumber () - ma->so0 - ma->so1 + ma->sb; if (!(*j).first->AcceptNewBonds (n)) { DoMerge = false; break; } n = (*j).first->GetTotalBondsNumber () - ma->so0 - ma->so1 + ma->sb; if (!ma->pAtom->AcceptNewBonds (n)) { DoMerge = false; break; } (*j).first->GetCoords (&x0, &y0); ma->pAtom->GetCoords (&x1, &y1); x += x1 - x0; y += y1 - y0; } } if (DoMerge) { //First align molecules x /= 2.* AtomMap.size(); y /= 2.* AtomMap.size(); Move (x, y); pMolecule->Move (-x, -y); //Then align each atom individually endj = AtomMap.end (); for (j = AtomMap.begin (); j != endj; j++) { (*j).first->GetCoords (&x0, &y0); (*j).second->pAtom->GetCoords (&x1, &y1); (*j).first->Move ((x1 - x0) / 2.,(y1 - y0) / 2.); } gcpView *pView = ((gcpDocument*) GetDocument ())->GetView (); /* Treat shared bonds (set order to 1, store max order in b1 and remove the bond * from pMolecule. */ endb = BondMap.end (); for (b = BondMap.begin (); b != endb; b++) { b1 = (*b).second; n = (*b).first->GetOrder (); pView->Remove (b1); pMolecule->Remove (b1); (*b).first->SetOrder (1); pAtom = (gcpAtom*) b1->GetAtom (0); pAtom->RemoveBond(b1); b1->ReplaceAtom (pAtom, NULL); pAtom = (gcpAtom*) b1->GetAtom (1); pAtom->RemoveBond(b1); b1->ReplaceAtom (pAtom, NULL); if (n > b1->GetOrder ()) b1->SetOrder (n); } // Treat shared atoms and delete from pMolecule map< Atom *, Bond * >::iterator ai; endj = AtomMap.end (); for (j = AtomMap.begin (); j != endj; j++) { b0 = (gcpBond*) (*j).second->pAtom->GetFirstBond (ai); while (b0) { b0->ReplaceAtom ((*j).second->pAtom, (*j).first); (*j).first->AddBond (b0); b0 = (gcpBond*) (*j).second->pAtom->GetNextBond (ai); } pMolecule->Remove ((*j).second->pAtom); pView->Remove ((*j).second->pAtom); delete (*j).second->pAtom; } // Try to restore max bond order for shared bonds and destroy old bonds endb = BondMap.end (); for (b = BondMap.begin (); b != endb; b++) { n = (*b).second->GetOrder () - 1; b0 = (*b).first; while ((n > 0) && (!((gcpAtom*) b0->GetAtom(0))->AcceptNewBonds (n) || !((gcpAtom*) b0->GetAtom(1))->AcceptNewBonds (n))) n--; if (n > 0) b0->SetOrder (n + 1); delete (*b).second; } } // Clean memory endj = AtomMap.end (); for (j = AtomMap.begin (); j != endj; j++) delete (*j).second; //return if merging is not possible if (!DoMerge) return false; } while (!pMolecule->m_Atoms.empty()) { pAtom = pMolecule->m_Atoms.front(); AddAtom(pAtom); pMolecule->m_Atoms.pop_front(); } while (!pMolecule->m_Fragments.empty()) { pFragment = pMolecule->m_Fragments.front(); AddFragment(pFragment); pMolecule->m_Fragments.pop_front(); } while (!pMolecule->m_Bonds.empty()) { pBond = pMolecule->m_Bonds.front(); AddBond(pBond); pMolecule->m_Bonds.pop_front(); } while (!pMolecule->m_Chains.empty()) //FIXME: Chains should change { pChain = pMolecule->m_Chains.front(); m_Chains.push_back(pChain); pMolecule->m_Chains.pop_front(); } while (!pMolecule->m_Cycles.empty()) { pCycle = pMolecule->m_Cycles.front(); m_Cycles.push_back(pCycle); pMolecule->m_Cycles.pop_front(); } Object* pObj = pMolecule->GetParent (); delete pMolecule; pObj->EmitSignal (OnChangedSignal); if (RemoveDuplicates) UpdateCycles (); EmitSignal (OnChangedSignal); return true; } bool gcpMolecule::Load (xmlNodePtr node) { char* buf; xmlNodePtr child; Object* pObject; gcpDocument* pDoc = (gcpDocument*) GetDocument (); buf = (char*) xmlGetProp (node, (xmlChar*) "id"); if (buf) { SetId (buf); xmlFree (buf); } child = GetNodeByName (node, (char*) "atom"); while (child) { pObject = new gcpAtom (); if (pDoc) AddChild (pObject); if (!pObject->Load (child)) { delete pObject; return false; } if (pDoc) pDoc->AddAtom ((gcpAtom*) pObject); AddAtom ((gcpAtom*) pObject); child = GetNextNodeByName (child->next, (char*) "atom"); } child = GetNodeByName (node, (char*) "fragment"); while (child) { pObject = new gcpFragment (); AddChild (pObject); if (!pObject->Load (child)) { delete pObject; return false; } if (pDoc) pDoc->AddFragment ((gcpFragment*) pObject); AddFragment ((gcpFragment*) pObject); child = GetNextNodeByName (child->next, (char*) "fragment"); } child = GetNodeByName (node, (char*) "bond"); while (child) { pObject = new gcpBond (); AddBond ((gcpBond*) pObject); if (!pObject->Load (child)) { delete pObject; m_Bonds.remove ((gcpBond*) pObject); return false; } if (pDoc) pDoc->AddBond ((gcpBond*) pObject); child = GetNextNodeByName (child->next, (char*) "bond"); CheckCrossings ((gcpBond*) pObject); } if (!m_Atoms.empty ()) { gcpAtom* pAtom = m_Atoms.front (); std::list::iterator i = m_Atoms.begin (); i++; for (; i != m_Atoms.end (); i++) (*i)->SetParent (NULL); gcpChain* pChain = new gcpChain (this, pAtom); //will find the cycles delete pChain; } buf = (char*) xmlGetProp (node, (const xmlChar*) "valign"); if (buf) { m_Alignment = GetDescendant (buf); xmlFree (buf); if (!m_Alignment) return false; } m_Changed = true; return true; } void gcpMolecule::UpdateCycles() { Lock (true); std::list::iterator n; for (n = m_Bonds.begin(); n != m_Bonds.end(); n++) (*n)->RemoveAllCycles(); while (!m_Cycles.empty()) { delete m_Cycles.front(); m_Cycles.pop_front(); } if (!m_Atoms.empty()) { std::list::iterator i = m_Atoms.begin(); i++; for (; i != m_Atoms.end(); i++) (*i)->SetParent(NULL); gcpChain* pChain = new gcpChain(this, *(m_Atoms.begin())); //will find the cycles delete pChain; } Lock (false); } void gcpMolecule::Clear() { m_Bonds.clear(); m_Atoms.clear(); m_Fragments.clear(); } void gcpMolecule::SetSelected (GtkWidget* w, int state) { map::iterator i; Object *child = GetFirstChild (i); while (child) { child->SetSelected (w, state); child = GetNextChild (i); } } void gcpMolecule::Transform2D (Matrix2D& m, double x, double y) { Object::Transform2D (m, x, y); std::list::iterator i = m_Atoms.begin (); for (; i != m_Atoms.end (); i++) if (((*i)->GetZ () != 6) && (*i)->GetAttachedHydrogens () && (*i)->GetBondsNumber ()) (*i)->Update (); } Object* gcpMolecule::GetAtomAt(double x, double y, double z) { // Make use of gcpBond::GetAtomAt std::list::iterator n, end = m_Bonds.end(); Object* pObj = NULL; for (n = m_Bonds.begin(); n != end; n++) if ((pObj = (*n)->GetAtomAt (x, y))) break; return pObj; } double gcpMolecule::GetYAlign () { if (m_Alignment) return m_Alignment->GetYAlign (); double y, maxy = - DBL_MAX, miny = DBL_MAX; std::list::iterator i = m_Atoms.begin(), end = m_Atoms.end(); for (; i != end; i++) { y = (*i)->GetYAlign (); if (y < miny) miny = y; if (y > maxy) maxy = y; } std::list::iterator ig = m_Fragments.begin(), endg = m_Fragments.end(); for (; ig != endg; ig++) { y = (*ig)->GetYAlign (); if (y < miny) miny = y; if (y > maxy) maxy = y; } return (miny + maxy) / 2.0; } bool gcpMolecule::BuildContextualMenu (GtkUIManager *UIManager, Object *object, double x, double y) { bool result = false; GtkActionGroup *group = gtk_action_group_new ("molecule"); GtkAction *action; action = gtk_action_new ("Molecule", _("Molecule"), NULL, NULL); gtk_action_group_add_action (group, action); if (!m_Fragments.size ()) { if (((gcpDocument*) GetDocument ())->GetApplication ()->HaveGhemical ()) { action = gtk_action_new ("ghemical", _("Export molecule to Ghemical"), NULL, NULL); g_signal_connect_swapped (action, "activate", G_CALLBACK (do_export_to_ghemical), this); gtk_action_group_add_action (group, action); g_object_unref (action); gtk_ui_manager_add_ui_from_string (UIManager, "", -1, NULL); } if (((gcpDocument*) GetDocument ())->GetApplication ()->HaveInChI ()) { action = gtk_action_new ("inchi", _("Generate InChI"), NULL, NULL); g_signal_connect_swapped (action, "activate", G_CALLBACK (do_build_inchi), this); gtk_action_group_add_action (group, action); g_object_unref (action); gtk_ui_manager_add_ui_from_string (UIManager, "", -1, NULL); action = gtk_action_new ("webbook", _("NIST WebBook page for this molecule"), NULL, NULL); g_signal_connect_swapped (action, "activate", G_CALLBACK (do_show_webbook), this); gtk_action_group_add_action (group, action); g_object_unref (action); gtk_ui_manager_add_ui_from_string (UIManager, "", -1, NULL); action = gtk_action_new ("pubchem", _("PubChem page for this molecule"), NULL, NULL); g_signal_connect_swapped (action, "activate", G_CALLBACK (do_show_pubchem), this); gtk_action_group_add_action (group, action); g_object_unref (action); gtk_ui_manager_add_ui_from_string (UIManager, "", -1, NULL); } action = gtk_action_new ("smiles", _("Generate Smiles"), NULL, NULL); g_signal_connect_swapped (action, "activate", G_CALLBACK (do_build_smiles), this); gtk_action_group_add_action (group, action); g_object_unref (action); gtk_ui_manager_add_ui_from_string (UIManager, "", -1, NULL); action = gtk_action_new ("calc", _("Open in Calculator"), NULL, NULL); g_signal_connect_swapped (action, "activate", G_CALLBACK (do_open_in_calc), this); gtk_action_group_add_action (group, action); g_object_unref (action); gtk_ui_manager_add_ui_from_string (UIManager, "", -1, NULL); result = true; } if (m_Bonds.size ()) { action = gtk_action_new ("select-align", _("Select alignment item"), NULL, NULL); g_signal_connect (action, "activate", G_CALLBACK (do_select_alignment), this); g_object_set_data (G_OBJECT (action), "item", object); gtk_action_group_add_action (group, action); g_object_unref (action); gtk_ui_manager_add_ui_from_string (UIManager, "", -1, NULL); result = true; } if (result) gtk_ui_manager_insert_action_group (UIManager, group, 0); g_object_unref (group); return result | GetParent ()->BuildContextualMenu (UIManager, object, x, y); } void gcpMolecule::ExportToGhemical () { OBMol Mol; OBConversion Conv; OBFormat* pOutFormat = Conv.FindFormat ("gpr"); Conv.SetInAndOutFormats (pOutFormat, pOutFormat); BuildOBMol (Mol); char *tmpname = g_strdup ("/tmp/gcp2gprXXXXXX"); int f = g_mkstemp (tmpname); gchar *old_num_locale; close (f); ofstream ofs; ofs.open(tmpname); if (!ofs) throw (int) 1; old_num_locale = g_strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); Conv.Write (&Mol, &ofs); setlocale(LC_NUMERIC, old_num_locale); g_free(old_num_locale); ofs.close(); char *command_line = g_strconcat ("ghemical -f ", tmpname, NULL); g_free (tmpname); g_spawn_command_line_async (command_line, NULL); g_free (command_line); } void gcpMolecule::SelectAlignmentItem (Object *child) { m_Alignment = (m_Alignment != child)? child: NULL; EmitSignal (OnChangedSignal); } xmlNodePtr gcpMolecule::Save (xmlDocPtr xml) { xmlNodePtr node = Object::Save (xml); if (!node) return NULL; if (m_Alignment) xmlNewProp (node, (const xmlChar*) "valign", (const xmlChar*) m_Alignment->GetId ()); return node; } void gcpMolecule::BuildOBMol (OBMol &Mol) { double xav = 0., yav = 0., zf; unsigned n = m_Atoms.size(); map AtomTable; list::iterator ia, enda = m_Atoms.end (); list BondList; double x, y, z; for (ia = m_Atoms.begin(); ia != enda; ia++) { (*ia)->GetCoords(&x, &y, &z); xav += x; yav += y; } xav /= n; yav /= n; gcpAtom* pgAtom; OBAtom obAtom; unsigned index = 1; map::iterator i; gcpBond *pBond; Mol.BeginModify(); Mol.ReserveAtoms(n); for (ia = m_Atoms.begin(); ia != enda; ia++) { pgAtom = *ia; AtomTable [pgAtom->GetId ()] = index; obAtom.SetIdx(index++); obAtom.SetAtomicNum(pgAtom->GetZ()); pgAtom->GetCoords(&x, &y, &z); // Scans the atom bonds and change z to try conservation of stereochemistry pBond = (gcpBond*) pgAtom->GetFirstBond (i); while (pBond) { zf = (pBond->GetAtom (0) == pgAtom)? 1.: -1.; switch (pBond->GetType ()) { case UpBondType: z += zf * 50.; break; case DownBondType: z -= zf * 50.; break; default: break; } pBond = (gcpBond*) pgAtom->GetNextBond (i); } obAtom.SetVector((xav - x )/ 100, (yav - y) / 100, z / 100); Mol.AddAtom(obAtom); obAtom.Clear(); } list::iterator j, endb = m_Bonds.end (); int start, end, order; for (j = m_Bonds.begin(); j != endb; j++) { order = (*j)->GetOrder(); start = AtomTable[(*j)->GetAtom(0)->GetId()]; end = AtomTable[(*j)->GetAtom(1)->GetId()]; Mol.AddBond(start, end, order, 0); } Mol.EndModify(); } void gcpMolecule::BuildOBMol2D (OBMol &Mol) { double xav = 0., yav = 0.; unsigned n = m_Atoms.size(); map AtomTable; list::iterator ia, enda = m_Atoms.end (); list BondList; double x, y, z; for (ia = m_Atoms.begin(); ia != enda; ia++) { (*ia)->GetCoords(&x, &y, &z); xav += x; yav += y; } xav /= n; yav /= n; gcpAtom* pgAtom; OBAtom obAtom; unsigned index = 1; map::iterator i; Mol.BeginModify(); Mol.ReserveAtoms(n); Mol.SetDimension (2); for (ia = m_Atoms.begin(); ia != enda; ia++) { pgAtom = *ia; AtomTable [pgAtom->GetId ()] = index; obAtom.SetIdx(index++); obAtom.SetAtomicNum(pgAtom->GetZ()); pgAtom->GetCoords(&x, &y, &z); // Scans the atom bonds and change z to try conservation of stereochemistry obAtom.SetVector((xav - x )/ 100, (yav - y) / 100, 0.); Mol.AddAtom(obAtom); obAtom.Clear(); } list::iterator j, endb = m_Bonds.end (); int start, end, order, flag; for (j = m_Bonds.begin(); j != endb; j++) { order = (*j)->GetOrder(); start = AtomTable[(*j)->GetAtom(0)->GetId()]; end = AtomTable[(*j)->GetAtom(1)->GetId()]; switch((*j)->GetType()) { case UpBondType: flag = OB_WEDGE_BOND; break; case DownBondType: flag = OB_HASH_BOND; break; default: flag = 0; } Mol.AddBond(start, end, order, flag); } Mol.EndModify(); } void gcpMolecule::BuildInChI () { OBMol Mol; OBConversion Conv; BuildOBMol2D (Mol); OBFormat *pInChIFormat = Conv.FindFormat ("inchi"), *pMolFormat = Conv.FindFormat ("mol"); if (pInChIFormat) { Conv.SetInAndOutFormats (pMolFormat, pInChIFormat); Conv.SetOptions ("xt", OpenBabel::OBConversion::OUTOPTIONS); ostringstream ofs; char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL)); setlocale (LC_NUMERIC, "C"); Conv.Write (&Mol, &ofs); setlocale (LC_NUMERIC, old_num_locale); g_free (old_num_locale); // remove "INCHI=" and the new line char at the end m_InChI = ofs.str ().substr (0, ofs.str ().length () - 2); } else { Conv.SetInAndOutFormats (pMolFormat, pMolFormat); char *tmpname = g_strdup ("/tmp/inchiXXXXXX"); int f = g_mkstemp (tmpname); close (f); ofstream ofs; ofs.open (tmpname); char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL)); setlocale (LC_NUMERIC, "C"); Conv.Write (&Mol, &ofs); setlocale (LC_NUMERIC, old_num_locale); ofs.close (); // calling main_inchi -STDIO -AuxNone -NoLabels char *command = g_strdup_printf ("main_inchi %s -STDIO -AuxNone -NoLabels", tmpname); char *output, *errors; g_spawn_command_line_sync (command, &output, &errors, NULL, NULL); if (output) { // remove the new line char at the end output[strlen (output) - 1] = 0; m_InChI = output + 6; g_free (output); } if (errors) g_free (errors); g_free (command); g_free (old_num_locale); remove (tmpname); g_free (tmpname); } m_Changed = false; } void gcpMolecule::BuildSmiles () { OBMol Mol; OBConversion Conv; OBFormat* pOutFormat = Conv.FindFormat ("smi"); Conv.SetInAndOutFormats (pOutFormat, pOutFormat); BuildOBMol2D (Mol); ostringstream ofs; char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL)); setlocale (LC_NUMERIC, "C"); Conv.Write (&Mol, &ofs); setlocale (LC_NUMERIC, old_num_locale); //TODO: do something with the string g_free (old_num_locale); string str = ofs.str ().substr (0, ofs.str ().length () - 2); new gcpStringDlg (reinterpret_cast(GetDocument ()), str, gcpStringDlg::SMILES); } void gcpMolecule::ShowInChI () { if (m_Changed) BuildInChI (); new gcpStringDlg (reinterpret_cast(GetDocument ()), m_InChI, gcpStringDlg::INCHI); } void gcpMolecule::ShowWebBase (char const* uri_start, char const *uri_end) { if (m_Changed) BuildInChI (); if (m_InChI.length () == 0) return; //should emit at least a warning string::size_type t; while ((t = m_InChI.find ('+')) != string::npos) m_InChI.replace (t, 1, "%2b"); string uri = string (uri_start) + m_InChI + uri_end; ((gcpDocument*) GetDocument ())->GetApplication ()->ShowURI (uri); } bool gcpMolecule::OnSignal (SignalId Signal, Object *Child) { m_Changed = true; return true; } void gcpMolecule::OpenCalc () { list::iterator ia, enda = m_Atoms.end (); ostringstream ofs; int nH; ofs << "gchemcalc "; for (ia = m_Atoms.begin(); ia != enda; ia++) { ofs << (*ia)->GetSymbol(); nH = (*ia)->GetAttachedHydrogens (); if (nH > 0) { ofs << "H" << nH; } } g_spawn_command_line_async (ofs.str ().c_str (), NULL); } void gcpMolecule::CheckCrossings (gcpBond *pBond) { gcpView *pView = reinterpret_cast (GetDocument ())->GetView (); list::iterator i, iend = m_Bonds.end (); for (i = m_Bonds.begin (); i != iend; i++) if (((*i) != pBond) && (*i)->IsCrossing (pBond)) { pView->Update (pBond); pView->Update (*i); } } void gcpMolecule::Add (GtkWidget* w) { /* Now that Object::Add adds children, we need to override this method because a molecule children (atoms, bonds, and fragments) are added to the widget by the document. FIXME! FIXME! FIXME! CHANGE THIS OLD WEIRD CODE! */ }