~ubuntu-branches/ubuntu/precise/kalzium/precise

« back to all changes in this revision

Viewing changes to src/tools/moleculeview.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Philip Muškovac
  • Date: 2011-07-03 12:28:58 UTC
  • Revision ID: james.westby@ubuntu.com-20110703122858-q1yyxncs89e4w0hs
Tags: upstream-4.6.90+repack
Import upstream version 4.6.90+repack

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *  Copyright (C) 2006 by Carsten Niehaus <cniehaus@kde.org>
 
3
 *  Copyright (C) 2007-2008 by Marcus D. Hanwell <marcus@cryos.org>
 
4
 ***************************************************************************/
 
5
 
 
6
/***************************************************************************
 
7
 *                                                                         *
 
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.                                   *
 
12
 *                                                                         *
 
13
 ***************************************************************************/
 
14
#include "moleculeview.h"
 
15
#include <avogadro/toolgroup.h>
 
16
#include <avogadro/elementtranslator.h>
 
17
#include <avogadro/periodictableview.h>
 
18
 
 
19
#include <QGLFormat>
 
20
#include <QUndoStack>
 
21
#include <QSettings>
 
22
#include <kdebug.h>
 
23
#include <kfiledialog.h>
 
24
#include <kstandarddirs.h>
 
25
#include <kmessagebox.h>
 
26
#include <KLocale>
 
27
#include <knewstuff3/downloaddialog.h>
 
28
 
 
29
#include <openbabel2wrapper.h>
 
30
 
 
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
 
36
#endif
 
37
#include <openbabel/forcefield.h>
 
38
 
 
39
using namespace OpenBabel;
 
40
using namespace Avogadro;
 
41
 
 
42
MoleculeDialog::MoleculeDialog( QWidget * parent )
 
43
        : KDialog( parent ), m_periodicTable(0), m_addHydrogens(false)
 
44
{
 
45
        // use multi-sample (anti-aliased) OpenGL if available
 
46
        QGLFormat defFormat = QGLFormat::defaultFormat();
 
47
        defFormat.setSampleBuffers(true);
 
48
        QGLFormat::setDefaultFormat(defFormat);
 
49
 
 
50
        setCaption( i18n( "Molecular Editor" ) );
 
51
        setButtons( User3 | User2 | User1 | Close );
 
52
 
 
53
        setDefaultButton( User1 );
 
54
 
 
55
        setButtonGuiItem( User1, KGuiItem( i18n( "Load Molecule" ), "document-open", i18n( "Loading a molecule" ) ) );
 
56
 
 
57
        setButtonGuiItem( User2, KGuiItem( i18n( "Download New Molecules" ), "get-hot-new-stuff", i18n( "Download new molecule files" ) ) );
 
58
 
 
59
  setButtonGuiItem( User3, KGuiItem( i18n( "Save Molecule" ), "document-save", i18n( "Saving a molecule" ) ) );
 
60
        
 
61
        ui.setupUi(mainWidget());
 
62
 
 
63
        ui.qualityCombo->setCurrentIndex(2); //default to high quality
 
64
        
 
65
        //default to atom symbols
 
66
        ui.labelsCombo->setCurrentIndex(2);
 
67
        ui.glWidget->setLabels(2);
 
68
        
 
69
        //default to balls-and-sticks
 
70
        ui.styleCombo->setCurrentIndex(0); 
 
71
        ui.glWidget->setStyle(0);
 
72
        
 
73
        m_path = QString( "" );
 
74
 
 
75
  // Attempt to set up the UFF forcefield
 
76
  m_forceField = OBForceField::FindForceField("UFF");
 
77
  if (!m_forceField)
 
78
    ui.optimizeButton->setEnabled(false);
 
79
 
 
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);
 
84
 
 
85
  // Set up the elements combo and bond order combo
 
86
  elementCombo();
 
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);
 
95
        
 
96
 
 
97
  connect(ui.tabWidget, SIGNAL(currentChanged(int)),
 
98
      this, SLOT(setViewEdit(int)));
 
99
 
 
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 ) ) );
 
109
 
 
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()));
 
121
      
 
122
  connect(ui.glWidget->molecule(), SIGNAL(updated()),
 
123
          this, SLOT(slotUpdateStatistics()));
 
124
 
 
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() ) );
 
131
 
 
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();
 
135
  QString error;
 
136
  if(!nEngines && !nTools)
 
137
    error = i18n("No tools or engines loaded - it is likely that the Avogadro plugins could not be located.");
 
138
  else if(!nEngines)
 
139
    error = i18n("No engines loaded - it is likely that the Avogadro plugins could not be located.");
 
140
  else if(!nTools)
 
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"));
 
144
}
 
145
 
 
146
void MoleculeDialog::slotLoadMolecule()
 
147
{
 
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();
 
151
  QString error;
 
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.");
 
154
  else if(!nEngines)
 
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.");
 
156
  else if(!nTools)
 
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);
 
160
 
 
161
  m_path = KGlobal::dirs()->findResourceDir( "appdata", "data/molecules/" ) +
 
162
    "data/molecules/";
 
163
 
 
164
  QString commonMoleculeFormats = i18n( "Common molecule formats" );
 
165
  QString allFiles = i18n( "All files" );
 
166
 
 
167
  QString filename = KFileDialog::getOpenFileName( 
 
168
          m_path,
 
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"
 
173
          "* *.*|"+allFiles,
 
174
          this,
 
175
          i18n( "Choose a file to open" ) );
 
176
 
 
177
  if( filename.isEmpty() ) return;
 
178
 
 
179
  kDebug() << "Filename to load: " << filename;
 
180
 
 
181
  Molecule* molecule = OpenBabel2Wrapper::readMolecule( filename );
 
182
 
 
183
  // Check that a valid molecule object was returned
 
184
  if (!molecule)
 
185
    return;
 
186
 
 
187
  if (molecule->numAtoms() != 0)
 
188
  {
 
189
    disconnect(ui.glWidget->molecule(), 0, this, 0);
 
190
               molecule->center();
 
191
               ui.glWidget->setMolecule( molecule );
 
192
               ui.glWidget->update();
 
193
               slotUpdateStatistics();
 
194
    connect(molecule, SIGNAL(updated()), this, SLOT(slotUpdateStatistics()));
 
195
  }
 
196
  ui.glWidget->invalidateDLs();
 
197
}
 
198
 
 
199
void MoleculeDialog::slotSaveMolecule()
 
200
{
 
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"
 
209
      "* *.*|"+allFiles,
 
210
    this,
 
211
    i18n( "Choose a file to save to" ) );
 
212
 
 
213
  if(!filename.contains('.')) filename.append(".cml");
 
214
 
 
215
  OpenBabel2Wrapper::writeMolecule( filename, ui.glWidget->molecule() );
 
216
}
 
217
 
 
218
void MoleculeDialog::setViewEdit(int mode)
 
219
{
 
220
    if (mode == 0)
 
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);
 
226
    }
 
227
    else if (mode == 2)
 
228
      ui.glWidget->toolGroup()->setActiveTool("Measure");
 
229
}
 
230
 
 
231
MoleculeDialog::~MoleculeDialog( )
 
232
{
 
233
   delete m_drawSettings;
 
234
}
 
235
 
 
236
void MoleculeDialog::slotUpdateStatistics()
 
237
{
 
238
        Avogadro::Molecule* mol = ui.glWidget->molecule();
 
239
        if ( !mol ) return;
 
240
 
 
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();
 
245
}
 
246
 
 
247
void MoleculeDialog::slotDownloadNewStuff()
 
248
{
 
249
    kDebug() << "Kalzium new stuff";
 
250
 
 
251
    KNS3::DownloadDialog dialog(this);
 
252
    dialog.exec();
 
253
 
 
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();
 
259
        }
 
260
    }
 
261
}
 
262
 
 
263
void MoleculeDialog::elementCombo()
 
264
{
 
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);
 
288
}
 
289
 
 
290
void MoleculeDialog::slotElementChanged(int element)
 
291
{
 
292
  // Check if other was chosen
 
293
  if (m_elementsIndex[element] == 0)
 
294
  {
 
295
    if (!m_periodicTable)
 
296
    {
 
297
      m_periodicTable = new PeriodicTableView(this);
 
298
      connect(m_periodicTable, SIGNAL(elementChanged(int)),
 
299
              this, SLOT(slotCustomElementChanged(int)));
 
300
    }
 
301
    m_periodicTable->show();
 
302
    return;
 
303
  }
 
304
 
 
305
  m_drawSettings->setValue("currentElement", m_elementsIndex[element]);
 
306
  ui.glWidget->toolGroup()->activeTool()->readSettings(*m_drawSettings);
 
307
}
 
308
 
 
309
void MoleculeDialog::slotCustomElementChanged(int element)
 
310
{
 
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);
 
315
 
 
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);
 
319
  if (comboItem != -1)
 
320
  {
 
321
    ui.elementCombo->setCurrentIndex(comboItem);
 
322
    return; // we found it in the list, so we're done
 
323
  }
 
324
 
 
325
  // Find where we should put the new entry
 
326
  // (i.e., in order by atomic number)
 
327
  int position = 0;
 
328
  foreach(int entry, m_elementsIndex)
 
329
  {
 
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)
 
333
      break;
 
334
 
 
335
    ++position;
 
336
  }
 
337
 
 
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) + ')';
 
341
 
 
342
  m_elementsIndex.insert(position, element);
 
343
  ui.elementCombo->insertItem(position, entryName);
 
344
  ui.elementCombo->setCurrentIndex(position);
 
345
}
 
346
 
 
347
void MoleculeDialog::slotBondOrderChanged(int bond)
 
348
{
 
349
  m_drawSettings->setValue("bondOrder", bond+1);
 
350
  if (ui.glWidget->toolGroup()->activeTool())
 
351
    ui.glWidget->toolGroup()->activeTool()->readSettings(*m_drawSettings);
 
352
}
 
353
 
 
354
void MoleculeDialog::slotAddHydrogensChanged(int hydrogens)
 
355
{
 
356
  m_drawSettings->setValue("addHydrogens", hydrogens);
 
357
  if (ui.glWidget->toolGroup()->activeTool())
 
358
    ui.glWidget->toolGroup()->activeTool()->readSettings(*m_drawSettings);
 
359
}
 
360
 
 
361
void MoleculeDialog::slotAdjustHydrogens()
 
362
{
 
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;
 
368
  }
 
369
  else {
 
370
    ui.hydrogensButton->setText(i18n("Add hydrogens"));
 
371
    ui.glWidget->molecule()->removeHydrogens();
 
372
    m_addHydrogens = false;
 
373
  }
 
374
  ui.glWidget->molecule()->update();
 
375
}
 
376
 
 
377
void MoleculeDialog::slotGeometryOptimize()
 
378
{
 
379
  // Perform a geometry optimization
 
380
  if (!m_forceField)
 
381
    return;
 
382
 
 
383
  Molecule* molecule = ui.glWidget->molecule();
 
384
  OpenBabel::OBMol obmol(molecule->OBMol());
 
385
 
 
386
  // Warn the user if the force field cannot be set up for the molecule
 
387
  if (!m_forceField->Setup(obmol))
 
388
  {
 
389
    KMessageBox::error(this, i18n("Kalzium"),
 
390
      i18n("Could not set up force field for this molecule"));
 
391
    return;
 
392
  }
 
393
 
 
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))
 
398
  {
 
399
    m_forceField->UpdateCoordinates(obmol);
 
400
    molecule->setOBMol(&obmol);
 
401
    molecule->update();
 
402
  }
 
403
}
 
404
 
 
405
#include "moleculeview.moc"