// -*- C++ -*-
/*
* GChemPaint library
* molecule.cc
*
* Copyright (C) 2001-2007 Jean Bréfort <jean.brefort@normalesup.org>
*
* 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 <glib/gi18n-lib.h>
#include <unistd.h>
#include <openbabel/obconversion.h>
#include <cmath>
#include <clocale>
#include <cstring>
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<gcpBond*>::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<gcpAtom*>::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<gcpAtom*, MergedAtom*> AtomMap;
map<gcpAtom*, MergedAtom*>::iterator j, endj;
map<gcpBond*, gcpBond*> BondMap;
map<gcpBond*, gcpBond*>::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<gcpAtom*>::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<gcpBond*>::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<gcpAtom*>::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<string, Object*>::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<gcpAtom*>::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<gcpBond*>::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<gcpAtom*>::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<gcpFragment*>::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, "<ui><popup><menu action='Molecule'><menuitem action='ghemical'/></menu></popup></ui>", -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, "<ui><popup><menu action='Molecule'><menuitem action='inchi'/></menu></popup></ui>", -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, "<ui><popup><menu action='Molecule'><menuitem action='webbook'/></menu></popup></ui>", -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, "<ui><popup><menu action='Molecule'><menuitem action='pubchem'/></menu></popup></ui>", -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, "<ui><popup><menu action='Molecule'><menuitem action='smiles'/></menu></popup></ui>", -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, "<ui><popup><menu action='Molecule'><menuitem action='calc'/></menu></popup></ui>", -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, "<ui><popup><menu action='Molecule'><menuitem action='select-align'/></menu></popup></ui>", -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<string, unsigned> AtomTable;
list<gcpAtom*>::iterator ia, enda = m_Atoms.end ();
list<gcpBond*> 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<Atom*, Bond*>::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<gcpBond*>::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<string, unsigned> AtomTable;
list<gcpAtom*>::iterator ia, enda = m_Atoms.end ();
list<gcpBond*> 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<Atom*, Bond*>::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<gcpBond*>::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<gcpDocument *>(GetDocument ()), str, gcpStringDlg::SMILES);
}
void gcpMolecule::ShowInChI ()
{
if (m_Changed)
BuildInChI ();
new gcpStringDlg (reinterpret_cast<gcpDocument *>(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<gcpAtom*>::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<gcpDocument*> (GetDocument ())->GetView ();
list<gcpBond*>::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!
*/
}
syntax highlighted by Code2HTML, v. 0.9.1