1
/***************************************************************************
2
* Copyright (C) 2006 by Carsten Niehaus <cniehaus@kde.org>
3
* Copyright (C) 2007-2008 by Marcus D. Hanwell <marcus@cryos.org>
4
***************************************************************************/
6
/***************************************************************************
8
* This program is free software; you can redistribute it and/or modify *
9
* it under the terms of the GNU General Public License as published by *
10
* the Free Software Foundation; either version 2 of the License, or *
11
* (at your option) any later version. *
13
***************************************************************************/
14
#include "moleculeview.h"
15
#include <avogadro/toolgroup.h>
16
#include <avogadro/elementtranslator.h>
17
#include <avogadro/periodictableview.h>
23
#include <kfiledialog.h>
24
#include <kstandarddirs.h>
25
#include <kmessagebox.h>
27
#include <knewstuff3/downloaddialog.h>
29
#include <openbabel2wrapper.h>
31
#include <openbabel/mol.h>
32
#include <openbabel/obiter.h>
33
// This is needed to ensure that the forcefields are set up right with GCC vis
34
#ifdef __KDE_HAVE_GCC_VISIBILITY
35
#define HAVE_GCC_VISIBILITY
37
#include <openbabel/forcefield.h>
39
using namespace OpenBabel;
40
using namespace Avogadro;
42
MoleculeDialog::MoleculeDialog( QWidget * parent )
43
: KDialog( parent ), m_periodicTable(0), m_addHydrogens(false)
45
// use multi-sample (anti-aliased) OpenGL if available
46
QGLFormat defFormat = QGLFormat::defaultFormat();
47
defFormat.setSampleBuffers(true);
48
QGLFormat::setDefaultFormat(defFormat);
50
setCaption( i18n( "Molecular Editor" ) );
51
setButtons( User3 | User2 | User1 | Close );
53
setDefaultButton( User1 );
55
setButtonGuiItem( User1, KGuiItem( i18n( "Load Molecule" ), "document-open", i18n( "Loading a molecule" ) ) );
57
setButtonGuiItem( User2, KGuiItem( i18n( "Download New Molecules" ), "get-hot-new-stuff", i18n( "Download new molecule files" ) ) );
59
setButtonGuiItem( User3, KGuiItem( i18n( "Save Molecule" ), "document-save", i18n( "Saving a molecule" ) ) );
61
ui.setupUi(mainWidget());
63
ui.qualityCombo->setCurrentIndex(2); //default to high quality
65
//default to atom symbols
66
ui.labelsCombo->setCurrentIndex(2);
67
ui.glWidget->setLabels(2);
69
//default to balls-and-sticks
70
ui.styleCombo->setCurrentIndex(0);
71
ui.glWidget->setStyle(0);
73
m_path = QString( "" );
75
// Attempt to set up the UFF forcefield
76
m_forceField = OBForceField::FindForceField("UFF");
78
ui.optimizeButton->setEnabled(false);
80
// Need the undo stack even though we aren't using it just yet - atom deletion
81
// doesn't work without it...
82
QUndoStack* tmp = new QUndoStack(this);
83
ui.glWidget->setUndoStack(tmp);
85
// Set up the elements combo and bond order combo
87
ui.bondCombo->addItem(i18n("Single"));
88
ui.bondCombo->addItem(i18n("Double"));
89
ui.bondCombo->addItem(i18n("Triple"));
90
// Initialise the draw settings
91
m_drawSettings = new QSettings;
92
m_drawSettings->setValue("currentElement", 6);
93
m_drawSettings->setValue("bondOrder", 1);
94
m_drawSettings->setValue("addHydrogens", 0);
97
connect(ui.tabWidget, SIGNAL(currentChanged(int)),
98
this, SLOT(setViewEdit(int)));
100
// Visualization parameters
101
connect(ui.qualityCombo, SIGNAL(activated( int )),
102
ui.glWidget , SLOT( setQuality( int ) ) );
103
connect(ui.styleCombo, SIGNAL(activated( int )),
104
ui.glWidget , SLOT( setStyle( int ) ) );
105
connect(ui.style2Combo, SIGNAL(activated( int )),
106
ui.glWidget , SLOT( setStyle2( int ) ) );
107
connect(ui.labelsCombo, SIGNAL(activated( int )),
108
ui.glWidget , SLOT( setLabels( int ) ) );
110
// Editing parameters
111
connect(ui.elementCombo, SIGNAL(currentIndexChanged(int)),
112
this, SLOT(slotElementChanged(int)));
113
connect(ui.bondCombo, SIGNAL(currentIndexChanged(int)),
114
this, SLOT(slotBondOrderChanged(int)));
115
connect(ui.hydrogenBox, SIGNAL(stateChanged(int)),
116
this, SLOT(slotAddHydrogensChanged(int)));
117
connect(ui.hydrogensButton, SIGNAL(clicked()),
118
this, SLOT(slotAdjustHydrogens()));
119
connect(ui.optimizeButton, SIGNAL(clicked()),
120
this, SLOT(slotGeometryOptimize()));
122
connect(ui.glWidget->molecule(), SIGNAL(updated()),
123
this, SLOT(slotUpdateStatistics()));
125
connect(this, SIGNAL( user1Clicked() ),
126
this, SLOT( slotLoadMolecule() ) );
127
connect(this, SIGNAL( user2Clicked() ),
128
this, SLOT( slotDownloadNewStuff() ) );
129
connect(this, SIGNAL( user3Clicked() ),
130
this, SLOT( slotSaveMolecule() ) );
132
// Check that we have managed to load up some tools and engines
133
int nEngines = ui.glWidget->engines().size() - 1;
134
int nTools = ui.glWidget->toolGroup()->tools().size();
136
if(!nEngines && !nTools)
137
error = i18n("No tools or engines loaded - it is likely that the Avogadro plugins could not be located.");
139
error = i18n("No engines loaded - it is likely that the Avogadro plugins could not be located.");
141
error = i18n("No tools loaded - it is likely that the Avogadro plugins could not be located.");
142
if(!nEngines || !nTools)
143
KMessageBox::error(this, error, i18n("Kalzium"));
146
void MoleculeDialog::slotLoadMolecule()
148
// Check that we have managed to load up some tools and engines
149
int nEngines = ui.glWidget->engines().size() - 1;
150
int nTools = ui.glWidget->toolGroup()->tools().size();
152
if(!nEngines && !nTools)
153
error = i18n("No tools or engines loaded - it is likely that the Avogadro plugins could not be located. No molecules can be viewed until this issue is resolved.");
155
error = i18n("No engines loaded - it is likely that the Avogadro plugins could not be located. No molecules can be viewed until this issue is resolved.");
157
error = i18n("No tools loaded - it is likely that the Avogadro plugins could not be located. No molecules can be viewed until this issue is resolved.");
158
if(!nEngines || !nTools)
159
KMessageBox::information(this, error);
161
m_path = KGlobal::dirs()->findResourceDir( "appdata", "data/molecules/" ) +
164
QString commonMoleculeFormats = i18n( "Common molecule formats" );
165
QString allFiles = i18n( "All files" );
167
QString filename = KFileDialog::getOpenFileName(
169
"*.cml *.xyz *.ent *.pdb *.alc *.chm *.cdx *.cdxml *.c3d1 *.c3d2"
170
" *.gpr *.mdl *.mol *.sdf *.sd *.crk3d *.cht *.dmol *.bgf"
171
" *.gam *.inp *.gamin *.gamout *.tmol *.fract"
172
" *.mpd *.mol2|"+commonMoleculeFormats+"\n"
175
i18n( "Choose a file to open" ) );
177
if( filename.isEmpty() ) return;
179
kDebug() << "Filename to load: " << filename;
181
Molecule* molecule = OpenBabel2Wrapper::readMolecule( filename );
183
// Check that a valid molecule object was returned
187
if (molecule->numAtoms() != 0)
189
disconnect(ui.glWidget->molecule(), 0, this, 0);
191
ui.glWidget->setMolecule( molecule );
192
ui.glWidget->update();
193
slotUpdateStatistics();
194
connect(molecule, SIGNAL(updated()), this, SLOT(slotUpdateStatistics()));
196
ui.glWidget->invalidateDLs();
199
void MoleculeDialog::slotSaveMolecule()
201
KUrl url("kfiledialog:///kalzium");
202
QString commonMoleculeFormats = i18n( "Common molecule formats" );
203
QString allFiles = i18n( "All files" );
204
QString filename = KFileDialog::getSaveFileName(url,
205
"*.cml *.xyz *.ent *.pdb *.alc *.chm *.cdx *.cdxml *.c3d1 *.c3d2"
206
" *.gpr *.mdl *.mol *.sdf *.sd *.crk3d *.cht *.dmol *.bgf"
207
" *.gam *.inp *.gamin *.gamout *.tmol *.fract"
208
" *.mpd *.mol2|"+commonMoleculeFormats+"\n"
211
i18n( "Choose a file to save to" ) );
213
if(!filename.contains('.')) filename.append(".cml");
215
OpenBabel2Wrapper::writeMolecule( filename, ui.glWidget->molecule() );
218
void MoleculeDialog::setViewEdit(int mode)
221
ui.glWidget->toolGroup()->setActiveTool("Navigate");
222
else if (mode == 1) {
223
ui.glWidget->toolGroup()->setActiveTool("Draw");
224
if (ui.glWidget->toolGroup()->activeTool())
225
ui.glWidget->toolGroup()->activeTool()->readSettings(*m_drawSettings);
228
ui.glWidget->toolGroup()->setActiveTool("Measure");
231
MoleculeDialog::~MoleculeDialog( )
233
delete m_drawSettings;
236
void MoleculeDialog::slotUpdateStatistics()
238
Avogadro::Molecule* mol = ui.glWidget->molecule();
241
ui.nameLabel->setText( mol->OBMol().GetTitle() );
242
ui.weightLabel->setText( i18nc( "This 'u' stands for the chemical unit (u for 'units'). Most likely this does not need to be translated at all!", "%1 u", mol->OBMol().GetMolWt() ) );
243
ui.formulaLabel->setText( OpenBabel2Wrapper::getPrettyFormula( mol ) );
244
// ui.glWidget->update();
247
void MoleculeDialog::slotDownloadNewStuff()
249
kDebug() << "Kalzium new stuff";
251
KNS3::DownloadDialog dialog(this);
254
// list of changed entries
255
foreach (const KNS3::Entry& entry, dialog.changedEntries()) {
256
// care only about installed ones
257
if (entry.status() == KNS3::Entry::Installed) {
258
kDebug() << "Changed Entry: " << entry.installedFiles();
263
void MoleculeDialog::elementCombo()
265
ui.elementCombo->addItem(ElementTranslator::name(1) + " (1)");
266
m_elementsIndex.append(1);
267
ui.elementCombo->addItem(ElementTranslator::name(5) + " (5)");
268
m_elementsIndex.append(5);
269
ui.elementCombo->addItem(ElementTranslator::name(6) + " (6)");
270
m_elementsIndex.append(6);
271
ui.elementCombo->addItem(ElementTranslator::name(7) + " (7)");
272
m_elementsIndex.append(7);
273
ui.elementCombo->addItem(ElementTranslator::name(8) + " (8)");
274
m_elementsIndex.append(8);
275
ui.elementCombo->addItem(ElementTranslator::name(9) + " (9)");
276
m_elementsIndex.append(9);
277
ui.elementCombo->addItem(ElementTranslator::name(15) + " (15)");
278
m_elementsIndex.append(15);
279
ui.elementCombo->addItem(ElementTranslator::name(16) + " (16)");
280
m_elementsIndex.append(16);
281
ui.elementCombo->addItem(ElementTranslator::name(17) + " (17)");
282
m_elementsIndex.append(17);
283
ui.elementCombo->addItem(ElementTranslator::name(35) + " (35)");
284
m_elementsIndex.append(35);
285
ui.elementCombo->addItem(tr("Other..."));
286
m_elementsIndex.append(0);
287
ui.elementCombo->setCurrentIndex(2);
290
void MoleculeDialog::slotElementChanged(int element)
292
// Check if other was chosen
293
if (m_elementsIndex[element] == 0)
295
if (!m_periodicTable)
297
m_periodicTable = new PeriodicTableView(this);
298
connect(m_periodicTable, SIGNAL(elementChanged(int)),
299
this, SLOT(slotCustomElementChanged(int)));
301
m_periodicTable->show();
305
m_drawSettings->setValue("currentElement", m_elementsIndex[element]);
306
ui.glWidget->toolGroup()->activeTool()->readSettings(*m_drawSettings);
309
void MoleculeDialog::slotCustomElementChanged(int element)
311
// Set the element so we can draw with it
312
m_drawSettings->setValue("currentElement", element);
313
if (ui.glWidget->toolGroup()->activeTool())
314
ui.glWidget->toolGroup()->activeTool()->readSettings(*m_drawSettings);
316
// Check to see if we already have this in the comboBox list
317
// If not, we get back -1 and need to create a new item
318
int comboItem = m_elementsIndex.indexOf(element);
321
ui.elementCombo->setCurrentIndex(comboItem);
322
return; // we found it in the list, so we're done
325
// Find where we should put the new entry
326
// (i.e., in order by atomic number)
328
foreach(int entry, m_elementsIndex)
330
// Two cases: entry > index -- insert the new element before this one
331
// Or... we hit the "Other" menu choice -- also insert here
332
if (entry > element || entry == 0)
338
// And now we set up a new entry into the combo list
339
QString entryName(ElementTranslator::name(element)); // (e.g., "Hydrogen")
340
entryName += " (" + QString::number(element) + ')';
342
m_elementsIndex.insert(position, element);
343
ui.elementCombo->insertItem(position, entryName);
344
ui.elementCombo->setCurrentIndex(position);
347
void MoleculeDialog::slotBondOrderChanged(int bond)
349
m_drawSettings->setValue("bondOrder", bond+1);
350
if (ui.glWidget->toolGroup()->activeTool())
351
ui.glWidget->toolGroup()->activeTool()->readSettings(*m_drawSettings);
354
void MoleculeDialog::slotAddHydrogensChanged(int hydrogens)
356
m_drawSettings->setValue("addHydrogens", hydrogens);
357
if (ui.glWidget->toolGroup()->activeTool())
358
ui.glWidget->toolGroup()->activeTool()->readSettings(*m_drawSettings);
361
void MoleculeDialog::slotAdjustHydrogens()
363
// Add/remove hydrogens from the molecule
364
if (!m_addHydrogens) {
365
ui.hydrogensButton->setText(i18n("Remove hydrogens"));
366
ui.glWidget->molecule()->addHydrogens();
367
m_addHydrogens = true;
370
ui.hydrogensButton->setText(i18n("Add hydrogens"));
371
ui.glWidget->molecule()->removeHydrogens();
372
m_addHydrogens = false;
374
ui.glWidget->molecule()->update();
377
void MoleculeDialog::slotGeometryOptimize()
379
// Perform a geometry optimization
383
Molecule* molecule = ui.glWidget->molecule();
384
OpenBabel::OBMol obmol(molecule->OBMol());
386
// Warn the user if the force field cannot be set up for the molecule
387
if (!m_forceField->Setup(obmol))
389
KMessageBox::error(this, i18n("Kalzium"),
390
i18n("Could not set up force field for this molecule"));
394
// Reasonable default values for most users
395
m_forceField->SteepestDescentInitialize(500, 1.0e-5);
396
// Provide some feedback as the optimization runs
397
while (m_forceField->SteepestDescentTakeNSteps(5))
399
m_forceField->UpdateCoordinates(obmol);
400
molecule->setOBMol(&obmol);
405
#include "moleculeview.moc"