This might be the "right way" to fix the issue - mark our exported classes as
[kdeedu-porting.git] / kalzium / libavogadro-kalzium / src / glwidget.cpp
blobe42bd139750332044d4ba2e4499a94ee706b842e
1 /**********************************************************************
2 GLWidget - general OpenGL display
4 Copyright (C) 2006,2007 Geoffrey R. Hutchison
5 Copyright (C) 2006,2007 Donald Ephraim Curtis
6 Copyright (C) 2007 Benoit Jacob
7 Copyright (C) 2007,2008 Marcus D. Hanwell
9 This file is part of the Avogadro molecular editor project.
10 For more information, see <http://avogadro.sourceforge.net/>
12 Avogadro is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 Avogadro is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 02110-1301, USA.
26 **********************************************************************/
28 #include <config.h>
30 #include <avogadro/glwidget.h>
31 #include <avogadro/glpainter.h>
32 #include <avogadro/painterdevice.h>
33 #include <avogadro/toolgroup.h>
35 #include "elementcolor.h"
37 #include <QUndoStack>
38 #include <QDir>
39 #include <QPluginLoader>
40 #include <QTime>
42 #ifdef ENABLE_THREADED_GL
43 #include <QWaitCondition>
44 #include <QMutex>
45 #endif
47 #include <QDebug>
49 #include <stdio.h>
50 #include <vector>
52 using namespace OpenBabel;
53 using namespace Eigen;
55 namespace Avogadro {
57 bool engineLessThan( const Engine* lhs, const Engine* rhs )
59 Engine::EngineFlags lhsFlags = lhs->flags();
60 Engine::EngineFlags rhsFlags = rhs->flags();
62 if ( !( lhsFlags & Engine::Overlay ) && rhsFlags & Engine::Overlay ) {
63 return true;
64 } else if (( lhsFlags & Engine::Overlay ) && ( rhsFlags & Engine::Overlay ) ) {
65 return lhs->transparencyDepth() < rhs->transparencyDepth();
66 } else if (( lhsFlags & Engine::Overlay ) && !( rhsFlags & Engine::Overlay ) ) {
67 return false;
68 } else if ( !( lhsFlags & Engine::Molecules ) && rhsFlags & Engine::Molecules ) {
69 return true;
70 } else if (( lhsFlags & Engine::Molecules ) && ( rhsFlags & Engine::Molecules ) ) {
71 return lhs->transparencyDepth() < rhs->transparencyDepth();
72 } else if (( lhsFlags & Engine::Molecules ) && !( rhsFlags & Engine::Molecules ) ) {
73 return false;
74 } else if ( !( lhsFlags & Engine::Atoms ) && rhsFlags & Engine::Atoms ) {
75 return true;
76 } else if (( lhsFlags & Engine::Atoms ) && ( rhsFlags & Engine::Atoms ) ) {
77 return lhs->transparencyDepth() < rhs->transparencyDepth();
78 } else if (( lhsFlags & Engine::Atoms ) && !( rhsFlags & Engine::Atoms ) ) {
79 return false;
80 } else if ( !( lhsFlags & Engine::Bonds ) && rhsFlags & Engine::Bonds ) {
81 return true;
82 } else if (( lhsFlags & Engine::Bonds ) && ( rhsFlags & Engine::Bonds ) ) {
83 return lhs->transparencyDepth() < rhs->transparencyDepth();
84 } else if (( lhsFlags & Engine::Bonds ) && !( rhsFlags & Engine::Bonds ) ) {
85 return false;
87 return false;
90 class GLHitPrivate
92 public:
93 GLHitPrivate() {};
95 GLuint type;
96 GLuint name;
97 GLuint minZ;
98 GLuint maxZ;
101 GLHit::GLHit() : d( new GLHitPrivate ) {}
103 GLHit::GLHit( const GLHit &other ) : d( new GLHitPrivate )
105 GLHitPrivate *e = other.d;
106 d->type = e->type;
107 d->name = e->name;
108 d->minZ = e->minZ;
109 d->maxZ = e->maxZ;
112 GLHit::GLHit( GLuint type, GLuint name, GLuint minZ, GLuint maxZ ) : d( new GLHitPrivate )
114 d->name = name;
115 d->type = type;
116 d->minZ = minZ;
117 d->maxZ = maxZ;
120 GLHit &GLHit::operator=( const GLHit &other )
122 GLHitPrivate *e = other.d;
123 d->type = e->type;
124 d->name = e->name;
125 d->minZ = e->minZ;
126 d->maxZ = e->maxZ;
127 return *this;
130 GLHit::~GLHit()
132 delete d;
135 bool GLHit::operator<( const GLHit &other ) const
137 GLHitPrivate *e = other.d;
138 return d->minZ < e->minZ;
141 bool GLHit::operator==( const GLHit &other ) const
143 GLHitPrivate *e = other.d;
144 return (( d->type == e->type ) && ( d->name == e->name ) );
147 GLuint GLHit::name() const { return d->name; }
148 GLuint GLHit::type() const { return d->type; }
149 GLuint GLHit::minZ() const { return d->minZ; }
150 GLuint GLHit::maxZ() const { return d->maxZ; }
152 void GLHit::setName( GLuint name ) { d->name = name; }
153 void GLHit::setType( GLuint type ) { d->type = type; }
154 void GLHit::setMinZ( GLuint minZ ) { d->minZ = minZ; }
155 void GLHit::setMaxZ( GLuint maxZ ) { d->maxZ = maxZ; }
157 class GLPainterDevice : public PainterDevice
159 public:
160 GLPainterDevice(GLWidget *gl) { widget = gl; }
161 ~GLPainterDevice() {}
163 Painter *painter() const { return widget->painter(); }
164 Camera *camera() const { return widget->camera(); }
165 bool isSelected( const Primitive *p ) const { return widget->isSelected(p); }
166 double radius( const Primitive *p ) const { return widget->radius(p); }
167 const Molecule *molecule() const { return widget->molecule(); }
168 Color *colorMap() const { return widget->colorMap(); }
170 int width() { return widget->width(); }
171 int height() { return widget->height(); }
173 private:
174 GLWidget *widget;
177 class GLWidgetPrivate
179 public:
180 GLWidgetPrivate() : background( Qt::black ),
181 aCells( 1 ), bCells( 1 ), cCells( 1 ),
182 uc (0),
183 molecule( 0 ),
184 camera( new Camera ),
185 tool( 0 ),
186 toolGroup( 0 ),
187 selectBuf( 0 ),
188 selectBufSize( -1 ),
189 #ifdef ENABLE_THREADED_GL
190 thread( 0 ),
191 #else
192 initialized( false ),
193 #endif
194 painter( 0 ),
195 map( 0),
196 defaultMap( new ElementColor ),
197 updateCache(true),
198 quickRender(false),
199 renderAxes(false),
200 renderDebug(true),
201 dlistQuick(0), dlistOpaque(0), dlistTransparent(0),
202 pd(0)
204 loadEngineFactories();
207 ~GLWidgetPrivate()
209 if ( selectBuf ) delete[] selectBuf;
210 delete camera;
211 delete defaultMap;
213 // free the display lists
214 if (dlistQuick)
215 glDeleteLists(dlistQuick, 1);
216 if (dlistOpaque)
217 glDeleteLists(dlistOpaque, 1);
218 if (dlistTransparent)
219 glDeleteLists(dlistTransparent, 1);
222 void updateListQuick();
223 static void loadEngineFactories();
224 static QList<EngineFactory *> engineFactories;
225 static QHash<QString, EngineFactory *> engineClassFactory;
227 QList<Engine *> engines;
229 QColor background;
231 Vector3d normalVector;
232 Vector3d center;
233 double radius;
234 const Atom *farthestAtom;
236 //! number of unit cells in a, b, and c crystal directions
237 unsigned char aCells;
238 unsigned char bCells;
239 unsigned char cCells;
241 OBUnitCell *uc;
243 Molecule *molecule;
245 Camera *camera;
247 Tool *tool;
248 ToolGroup *toolGroup;
250 GLuint *selectBuf;
251 int selectBufSize;
253 PrimitiveList selectedPrimitives;
254 PrimitiveList primitives;
256 QUndoStack *undoStack;
258 bool stable;
260 #ifdef ENABLE_THREADED_GL
261 QWaitCondition paintCondition;
262 QMutex renderMutex;
264 GLThread *thread;
265 #else
266 bool initialized;
267 #endif
269 GLPainter *painter;
270 Color *map; // global color map
271 Color *defaultMap; // default fall-back coloring (i.e., by elements)
272 bool updateCache; // Update engine caches in quick render?
273 bool quickRender; // Are we using quick render?
274 bool renderAxes; // Should the x, y, z axes be rendered?
275 bool renderDebug; // Should the debug information be shown?
277 GLuint dlistQuick;
278 GLuint dlistOpaque;
279 GLuint dlistTransparent;
282 * Member GLPainterDevice which is passed to the engines.
284 GLPainterDevice *pd;
287 QList<EngineFactory *> GLWidgetPrivate::engineFactories;
288 QHash<QString, EngineFactory *> GLWidgetPrivate::engineClassFactory;
290 void GLWidgetPrivate::loadEngineFactories()
292 static bool enginesLoaded = false;
293 if(!enginesLoaded)
295 QString prefixPath = QString( INSTALL_PREFIX ) + "/lib/avogadro/engines";
296 QStringList pluginPaths;
297 pluginPaths << prefixPath;
299 #ifdef WIN32
300 pluginPaths << "./engines";
301 #endif
303 if ( getenv( "AVOGADRO_ENGINES" ) != NULL ) {
304 pluginPaths = QString( getenv( "AVOGADRO_ENGINES" ) ).split( ':' );
307 // load static plugins first
309 // now load plugins from paths
310 foreach( QString path, pluginPaths ) {
311 QDir dir( path );
312 foreach( QString fileName, dir.entryList( QDir::Files ) ) {
313 QPluginLoader loader( dir.absoluteFilePath( fileName ) );
314 QObject *instance = loader.instance();
315 EngineFactory *factory = qobject_cast<EngineFactory *>( instance );
316 if ( factory )
318 engineFactories.append(factory);
319 engineClassFactory[factory->className()] = factory;
323 enginesLoaded = true;
327 void GLWidgetPrivate::updateListQuick()
329 // Create a display list cache
330 if (updateCache) {
331 qDebug() << "Making new quick display lists...";
332 if (dlistQuick == 0)
333 dlistQuick = glGenLists(1);
335 // Don't use dynamic scaling when rendering quickly
336 painter->setDynamicScaling(false);
338 glNewList(dlistQuick, GL_COMPILE);
339 foreach(Engine *engine, engines)
340 if(engine->isEnabled())
341 engine->renderQuick(pd);
342 glEndList();
344 updateCache = false;
345 painter->setDynamicScaling(true);
350 #ifdef ENABLE_THREADED_GL
351 class GLThread : public QThread
353 public:
354 GLThread( GLWidget *widget, QObject *parent );
356 void run();
357 void resize( int width, int height );
358 void stop();
360 private:
361 GLWidget *m_widget;
362 QGLContext *m_context;
363 bool m_running;
364 bool m_resize;
365 bool m_initialized;
367 int m_width;
368 int m_height;
372 GLThread::GLThread( GLWidget *widget, QObject *parent ) : QThread( parent ),
373 m_widget( widget ), m_running( true ), m_resize( false ), m_initialized( false )
376 void GLThread::run()
378 GLWidgetPrivate *d = m_widget->d;
380 while ( true ) {
381 // lock the mutex
382 d->renderMutex.lock();
384 // unlock and wait
385 d->paintCondition.wait( &( d->renderMutex ) );
386 if ( !m_running ) {
387 d->renderMutex.unlock();
388 break;
390 m_widget->makeCurrent();
392 if ( !m_initialized ) {
393 m_widget->initializeGL();
394 m_initialized = true;
398 if ( m_resize ) {
399 m_widget->resizeGL( m_width, m_height );
400 m_resize=false;
403 m_widget->qglClearColor(d->background);
404 m_widget->paintGL();
405 m_widget->swapBuffers();
406 m_widget->doneCurrent();
407 d->renderMutex.unlock();
411 void GLThread::resize( int width, int height )
413 m_resize = true;
414 m_width = width;
415 m_height = height;
418 void GLThread::stop()
420 m_running = false;
422 #endif
424 GLWidget::GLWidget( QWidget *parent )
425 : QGLWidget( parent ), d( new GLWidgetPrivate )
427 constructor();
430 GLWidget::GLWidget( const QGLFormat &format, QWidget *parent,
431 const GLWidget *shareWidget )
432 : QGLWidget( format, parent, shareWidget ), d( new GLWidgetPrivate )
434 constructor(shareWidget);
437 GLWidget::GLWidget( Molecule *molecule,
438 const QGLFormat &format, QWidget *parent,
439 const GLWidget *shareWidget )
440 : QGLWidget( format, parent, shareWidget ), d( new GLWidgetPrivate )
442 constructor(shareWidget);
443 setMolecule( molecule );
446 GLWidget::~GLWidget()
448 if(!d->painter->isShared())
450 delete d->painter;
452 else
454 d->painter->decrementShare();
457 #ifdef ENABLE_THREADED_GL
458 // cleanup our thread
459 d->thread->stop();
460 d->paintCondition.wakeAll();
461 d->thread->wait();
462 #endif
464 // delete the engines
465 foreach( Engine *engine, d->engines ) {
466 delete engine;
469 delete( d );
472 void GLWidget::constructor(const GLWidget *shareWidget)
474 d->pd = new GLPainterDevice(this);
475 if(shareWidget && isSharing()) {
476 // we are sharing contexts
477 d->painter = static_cast<GLPainter *>(shareWidget->painter());
479 else
481 d->painter = new GLPainter();
483 d->painter->incrementShare();
485 setSizePolicy( QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding );
486 d->camera->setParent( this );
487 setAutoBufferSwap( false );
488 #ifdef ENABLE_THREADED_GL
489 qDebug() << "Threaded GL enabled.";
490 d->thread = new GLThread( this, this );
491 doneCurrent();
492 d->thread->start();
493 #endif
496 GLWidget *GLWidget::m_current = 0;
498 GLWidget *GLWidget::current()
500 return m_current;
503 void GLWidget::setCurrent(GLWidget *current)
505 m_current = current;
508 void GLWidget::initializeGL()
510 qDebug() << "GLWidget initialisation...";
511 qglClearColor( d->background );
513 glShadeModel( GL_SMOOTH );
514 glEnable( GL_DEPTH_TEST );
515 glDepthFunc( GL_LEQUAL );
516 glEnable( GL_CULL_FACE );
517 glEnable( GL_COLOR_SUM_EXT );
519 // Used to display semi-transparent selection rectangle
520 // glBlendFunc(GL_ONE, GL_ONE);
521 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
523 glEnable( GL_NORMALIZE );
525 GLfloat ambientLight[] = { 0.2, 0.2, 0.2, 1.0 };
526 GLfloat diffuseLight[] = { 1.0, 1.0, 1.0, 1.0 };
527 GLfloat diffuseLight2[] = { 0.3, 0.3, 0.3, 1.0 };
528 GLfloat specularLight[] = { 1.0, 1.0, 1.0, 1.0 };
529 GLfloat specularLight2[] = { 0.5, 0.5, 0.5, 1.0 };
530 GLfloat position[] = { 0.8, 0.7, 1.0, 0.0 };
531 GLfloat position2[] = { -0.8, 0.7, -0.5, 0.0 };
533 glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL_EXT,
534 GL_SEPARATE_SPECULAR_COLOR_EXT );
536 // Due to the bug found with Mesa 6.5.3 in the Radeon DRI driver
537 // in radeon_state.c in radeonUpdateSpecular(),
538 // it is important to set GL_SEPARATE_SPECULAR_COLOR_EXT
539 // _before_ enabling lighting
540 glEnable( GL_LIGHTING );
542 glLightfv( GL_LIGHT0, GL_AMBIENT, ambientLight );
543 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseLight );
544 glLightfv( GL_LIGHT0, GL_SPECULAR, specularLight );
545 glLightfv( GL_LIGHT0, GL_POSITION, position );
546 glEnable( GL_LIGHT0 );
547 // Create a second light source to illuminate those shadows a little better
548 glLightfv( GL_LIGHT1, GL_AMBIENT, ambientLight );
549 glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuseLight2 );
550 glLightfv( GL_LIGHT1, GL_SPECULAR, specularLight2 );
551 glLightfv( GL_LIGHT1, GL_POSITION, position2 );
552 glEnable( GL_LIGHT1 );
553 qDebug() << "GLWidget initialised...";
556 void GLWidget::resizeEvent( QResizeEvent *event )
558 //qDebug() << "resizeEvent";
559 #ifdef ENABLE_THREADED_GL
560 d->thread->resize( event->size().width(), event->size().height() );
561 #else
562 if (!isValid())
563 return;
564 makeCurrent();
565 if(!d->initialized)
567 d->initialized = true;
568 initializeGL();
570 // GLXWaitX() is called by the TT resizeEvent on Linux... We may need
571 // specific functions here - need to look at Mac and Windows code.
572 resizeGL( event->size().width(), event->size().height() );
573 #endif
576 void GLWidget::resizeGL( int width, int height )
578 glViewport( 0, 0, width, height );
581 void GLWidget::setBackground( const QColor &background )
583 #ifdef ENABLE_THREADED_GL
584 d->renderMutex.lock();
585 #endif
586 d->background = background;
587 #ifdef ENABLE_THREADED_GL
588 d->renderMutex.unlock();
589 #endif
592 QColor GLWidget::background() const
594 return d->background;
597 void GLWidget::setColorMap(Color *map)
599 d->map = map;
602 Color *GLWidget::colorMap() const
604 if (d->map)
605 return d->map;
606 else
607 return d->defaultMap;
610 void GLWidget::setQuality(int quality)
612 d->painter->setQuality(quality);
615 int GLWidget::quality() const
617 return d->painter->quality();
620 void GLWidget::setRenderAxes(bool renderAxes)
622 d->renderAxes = renderAxes;
625 bool GLWidget::renderAxes()
627 return d->renderAxes;
630 void GLWidget::setRenderDebug(bool renderDebug)
632 d->renderDebug = renderDebug;
635 bool GLWidget::renderDebug()
637 return d->renderDebug;
640 void GLWidget::render()
642 d->painter->begin(this);
644 // Use renderQuick if the view is being moved, otherwise full render
645 if (d->quickRender) {
647 d->updateListQuick();
648 qDebug() << "Calling quick display lists...";
649 glCallList(d->dlistQuick);
650 if (d->uc) renderCrystal(d->dlistQuick);
653 else {
654 qDebug() << "Normal rendering...";
656 // we save a display list if we're doing a crystal
657 if (d->dlistOpaque == 0)
658 d->dlistOpaque = glGenLists(1);
659 if (d->dlistTransparent == 0)
660 d->dlistTransparent = glGenLists(1);
662 if (d->uc) glNewList(d->dlistOpaque, GL_COMPILE);
663 foreach(Engine *engine, d->engines)
664 if(engine->isEnabled())
665 engine->renderOpaque(d->pd);
666 if (d->uc) { // end the main list and render the opaque crystal
667 glEndList();
668 renderCrystal(d->dlistOpaque);
671 glDepthMask(GL_FALSE);
672 if (d->uc) glNewList(d->dlistTransparent, GL_COMPILE);
673 foreach(Engine *engine, d->engines)
674 if(engine->isEnabled() && engine->flags() & Engine::Transparent)
675 engine->renderTransparent(d->pd);
676 if (d->uc) { // end the main list and render the transparent bits
677 glEndList();
678 renderCrystal(d->dlistTransparent);
680 glDepthMask(GL_TRUE);
683 // Render all the inactive tools
684 if ( d->toolGroup ) {
685 QList<Tool *> tools = d->toolGroup->tools();
686 foreach( Tool *tool, tools ) {
687 if ( tool != d->tool ) {
688 tool->paint( this );
693 // Render the active tool
694 if ( d->tool ) {
695 d->tool->paint( this );
698 // If enabled draw the axes
699 if (d->renderAxes) renderAxesOverlay();
701 // If enabled show debug information
702 if (d->renderDebug) renderDebugOverlay();
704 d->painter->end();
707 void GLWidget::renderCrystal(GLuint displayList)
709 std::vector<vector3> cellVectors = d->uc->GetCellVectors();
711 for (int a = 0; a < d->aCells; a++) {
712 for (int b = 0; b < d->bCells; b++) {
713 for (int c = 0; c < d->cCells; c++) {
714 glPushMatrix();
715 glTranslated(
716 cellVectors[0].x() * a
717 + cellVectors[1].x() * b
718 + cellVectors[2].x() * c,
719 cellVectors[0].y() * a
720 + cellVectors[1].y() * b
721 + cellVectors[2].y() * c,
722 cellVectors[0].z() * a
723 + cellVectors[1].z() * b
724 + cellVectors[2].z() * c );
726 glCallList(displayList);
727 glPopMatrix();
730 } // end of for loops
732 renderCrystalAxes();
735 // Render the unit cell axes, indicating the frame of the cell
736 // 4---5
737 // / /|
738 // / / | (0 is the "origin" for this unit cell)
739 // 3---2 6 (7 is in the back corner = cellVector[2])
740 // | | / (3 is cellVector[1])
741 // | |/ (1 is cellVector[0])
742 // 0---1
743 void GLWidget::renderCrystalAxes()
745 std::vector<vector3> cellVectors = d->uc->GetCellVectors();
746 vector3 v0(0.0, 0.0, 0.0);
747 vector3 v1(cellVectors[0]);
748 vector3 v3(cellVectors[1]);
749 vector3 v7(cellVectors[2]);
750 vector3 v2, v4, v5, v6;
751 v2 = v1 + v3;
752 v4 = v3 + v7;
753 v6 = v1 + v7;
754 v5 = v4 + v1;
756 glDisable(GL_LIGHTING);
757 glColor4f(1.0, 1.0, 1.0, 0.7);
758 glLineWidth(2.0);
759 for (int a = 0; a < d->aCells; a++) {
760 for (int b = 0; b < d->bCells; b++) {
761 for (int c = 0; c < d->cCells; c++) {
762 glPushMatrix();
763 glTranslated(
764 cellVectors[0].x() * a
765 + cellVectors[1].x() * b
766 + cellVectors[2].x() * c,
767 cellVectors[0].y() * a
768 + cellVectors[1].y() * b
769 + cellVectors[2].y() * c,
770 cellVectors[0].z() * a
771 + cellVectors[1].z() * b
772 + cellVectors[2].z() * c );
774 glBegin(GL_LINE_STRIP);
775 glVertex3dv(v0.AsArray());
776 glVertex3dv(v1.AsArray());
777 glEnd();
779 glBegin(GL_LINE_STRIP);
780 glVertex3dv(v0.AsArray());
781 glVertex3dv(v3.AsArray());
782 glEnd();
784 glBegin(GL_LINE_STRIP);
785 glVertex3dv(v0.AsArray());
786 glVertex3dv(v7.AsArray());
787 glEnd();
789 glBegin(GL_LINE_STRIP);
790 glVertex3dv(v1.AsArray());
791 glVertex3dv(v2.AsArray());
792 glEnd();
794 glBegin(GL_LINE_STRIP);
795 glVertex3dv(v3.AsArray());
796 glVertex3dv(v2.AsArray());
797 glEnd();
799 glBegin(GL_LINE_STRIP);
800 glVertex3dv(v3.AsArray());
801 glVertex3dv(v4.AsArray());
802 glEnd();
804 glBegin(GL_LINE_STRIP);
805 glVertex3dv(v5.AsArray());
806 glVertex3dv(v4.AsArray());
807 glEnd();
809 glBegin(GL_LINE_STRIP);
810 glVertex3dv(v5.AsArray());
811 glVertex3dv(v2.AsArray());
812 glEnd();
814 glBegin(GL_LINE_STRIP);
815 glVertex3dv(v5.AsArray());
816 glVertex3dv(v6.AsArray());
817 glEnd();
819 glBegin(GL_LINE_STRIP);
820 glVertex3dv(v1.AsArray());
821 glVertex3dv(v6.AsArray());
822 glEnd();
824 glBegin(GL_LINE_STRIP);
825 glVertex3dv(v6.AsArray());
826 glVertex3dv(v7.AsArray());
827 glEnd();
829 glBegin(GL_LINE_STRIP);
830 glVertex3dv(v4.AsArray());
831 glVertex3dv(v7.AsArray());
832 glEnd();
834 glPopMatrix();
837 } // end of for loops
838 glEnable(GL_LIGHTING);
841 void GLWidget::renderAxesOverlay()
843 // Render x, y, z axes as an overlay on the widget
844 // Save the opengl projection matrix and set up an orthogonal projection
845 glMatrixMode(GL_PROJECTION);
846 glPushMatrix();
847 glLoadIdentity();
848 // Ensure the axes are of the same length
849 double aspectRatio = static_cast<double>(d->pd->width())/static_cast<double>(d->pd->height());
850 glOrtho(0, aspectRatio, 0, 1, 0, 1);
851 glMatrixMode(GL_MODELVIEW);
852 glPushMatrix();
853 glLoadIdentity();
855 // Set the origin and calculate the positions of the axes
856 Vector3d origin = Vector3d(0.07, 0.07, -.07);
857 MatrixP3d axisTranslation;
858 axisTranslation.loadTranslation(d->pd->camera()->transformedXAxis() * 0.04);
859 Vector3d aXa = axisTranslation * origin;
860 axisTranslation.loadTranslation(d->pd->camera()->transformedXAxis() * 0.06);
861 Vector3d aX = axisTranslation * origin;
862 axisTranslation.loadTranslation(d->pd->camera()->transformedYAxis() * 0.04);
863 Vector3d aYa = axisTranslation * origin;
864 axisTranslation.loadTranslation(d->pd->camera()->transformedYAxis() * 0.06);
865 Vector3d aY = axisTranslation * origin;
866 axisTranslation.loadTranslation(d->pd->camera()->transformedZAxis() * 0.04);
867 Vector3d aZa = axisTranslation * origin;
868 axisTranslation.loadTranslation(d->pd->camera()->transformedZAxis() * 0.06);
869 Vector3d aZ = axisTranslation * origin;
871 // Turn off dynamic scaling in the painter (cylinders don't render correctly)
872 d->painter->setDynamicScaling(false);
874 // x axis
875 painter()->setColor(1.0, 0.0, 0.0);
876 painter()->drawCylinder(origin, aXa, 0.005);
877 painter()->drawCone(aXa, aX, 0.01);
878 // y axis
879 painter()->setColor(0.0, 1.0, 0.0);
880 painter()->drawCylinder(origin, aYa, 0.005);
881 painter()->drawCone(aYa, aY, 0.01);
882 // y axis
883 painter()->setColor(0.0, 0.0, 1.0);
884 painter()->drawCylinder(origin, aZa, 0.005);
885 painter()->drawCone(aZa, aZ, 0.01);
887 // Turn dynamic scaling back on (default state)
888 d->painter->setDynamicScaling(true);
890 glPopMatrix();
891 glMatrixMode(GL_PROJECTION);
892 glPopMatrix();
893 glMatrixMode(GL_MODELVIEW);
896 void GLWidget::renderDebugOverlay()
898 QList<Primitive *> list;
900 // Draw all text in while
901 d->pd->painter()->setColor(1.0, 1.0, 1.0);
903 int x = 5, y = 5;
904 y += d->pd->painter()->drawText(x, y, "---- " + tr("Debug Information") + " ----");
905 y += d->pd->painter()->drawText(x, y, tr("FPS") + ": " + QString::number(computeFramesPerSecond(), 'g', 3));
907 y += d->pd->painter()->drawText(x, y, tr("View Size") + ": "
908 + QString::number(d->pd->width())
909 + " x "
910 + QString::number(d->pd->height()) );
912 list = primitives().subList(Primitive::AtomType);
913 y += d->pd->painter()->drawText(x, y, tr("Atoms") + ": " + QString::number(list.size()));
915 list = primitives().subList(Primitive::BondType);
916 y += d->pd->painter()->drawText(x, y, tr("Bonds") + ": " + QString::number(list.size()));
919 void GLWidget::paintGL()
921 resizeGL(width(), height()); // fix for bug #1797069. don't remove!
923 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
925 // setup the OpenGL projection matrix using the camera
926 glMatrixMode( GL_PROJECTION );
927 glLoadIdentity();
928 d->camera->applyPerspective();
930 // setup the OpenGL modelview matrix using the camera
931 glMatrixMode( GL_MODELVIEW );
932 glLoadIdentity();
933 d->camera->applyModelview();
935 render();
938 void GLWidget::paintEvent( QPaintEvent * )
940 //qDebug() << "paintEvent";
941 #ifdef ENABLE_THREADED_GL
942 // tell our thread to paint
943 d->paintCondition.wakeAll();
944 #else
945 makeCurrent();
946 if(!d->initialized)
948 d->initialized = true;
949 initializeGL();
951 qglClearColor(d->background);
952 paintGL();
953 swapBuffers();
954 #endif
957 bool GLWidget::event( QEvent *event )
959 if(event->type() == QEvent::Show)
961 GLWidget::setCurrent(this);
964 return QGLWidget::event(event);
967 void GLWidget::mousePressEvent( QMouseEvent * event )
969 if ( d->tool ) {
970 QUndoCommand *command = 0;
971 command = d->tool->mousePress( this, event );
973 if ( command && d->undoStack ) {
974 d->undoStack->push( command );
975 } else if ( command ) {
976 delete command;
979 #ifdef ENABLE_THREADED_GL
980 d->renderMutex.lock();
981 #endif
982 // Use quick render while the mouse is down
983 d->quickRender = true;
984 #ifdef ENABLE_THREADED_GL
985 d->renderMutex.unlock();
986 #endif
989 void GLWidget::mouseReleaseEvent( QMouseEvent * event )
991 if ( d->tool ) {
992 QUndoCommand *command = d->tool->mouseRelease( this, event );
994 if ( command && d->undoStack ) {
995 d->undoStack->push( command );
998 #ifdef ENABLE_THREADED_GL
999 d->renderMutex.lock();
1000 #endif
1001 // Stop using quickRender
1002 d->quickRender = false;
1003 #ifdef ENABLE_THREADED_GL
1004 d->renderMutex.unlock();
1005 #endif
1006 // Render the scene at full quality now the mouse button has been released
1007 update();
1010 void GLWidget::mouseMoveEvent( QMouseEvent * event )
1012 if ( d->tool ) {
1013 QUndoCommand *command = d->tool->mouseMove( this, event );
1014 if ( command && d->undoStack ) {
1015 d->undoStack->push( command );
1020 void GLWidget::wheelEvent( QWheelEvent * event )
1022 if ( d->tool ) {
1023 QUndoCommand *command = d->tool->wheel( this, event );
1024 if ( command && d->undoStack ) {
1025 d->undoStack->push( command );
1030 void A_EXPORT GLWidget::setMolecule( Molecule *molecule )
1032 if ( !molecule ) { return; }
1034 // disconnect from our old molecule
1035 if ( d->molecule ) {
1036 QObject::disconnect( d->molecule, 0, this, 0 );
1039 d->molecule = molecule;
1041 // clear our engine queues
1042 for ( int i=0; i < d->engines.size(); i++ ) {
1043 d->engines.at( i )->clearPrimitives();
1045 d->primitives.clear();
1047 // add the atoms to the default queue
1048 std::vector<OpenBabel::OBNodeBase*>::iterator i;
1049 for ( Atom *atom = ( Atom* )d->molecule->BeginAtom( i );
1050 atom; atom = ( Atom* )d->molecule->NextAtom( i ) ) {
1051 d->primitives.append( atom );
1054 // add the bonds to the default queue
1055 std::vector<OpenBabel::OBEdgeBase*>::iterator j;
1056 for ( Bond *bond = ( Bond* )d->molecule->BeginBond( j );
1057 bond; bond = ( Bond* )d->molecule->NextBond( j ) ) {
1058 d->primitives.append( bond );
1061 // add the residues to the default queue
1062 std::vector<OpenBabel::OBResidue*>::iterator k;
1063 for ( Residue *residue = ( Residue* )d->molecule->BeginResidue( k );
1064 residue; residue = ( Residue * )d->molecule->NextResidue( k ) ) {
1065 d->primitives.append( residue );
1068 d->primitives.append( d->molecule );
1070 std::cout << "SetMolecule Called!" << std::endl;
1071 // Now set the primitives for the engines
1072 for (int i = 0; i < d->engines.size(); i++)
1073 d->engines.at(i)->setPrimitives(d->primitives);
1075 // connect our signals so if the molecule gets updated
1076 connect( d->molecule, SIGNAL( primitiveAdded( Primitive* ) ),
1077 this, SLOT( addPrimitive( Primitive* ) ) );
1078 connect( d->molecule, SIGNAL( primitiveUpdated( Primitive* ) ),
1079 this, SLOT( updatePrimitive( Primitive* ) ) );
1080 connect( d->molecule, SIGNAL( primitiveRemoved( Primitive* ) ),
1081 this, SLOT( removePrimitive( Primitive* ) ) );
1083 // compute the molecule's geometric info
1084 updateGeometry();
1086 // setup the camera to have a nice viewpoint on the molecule
1087 d->camera->initializeViewPoint();
1089 update();
1092 const Molecule* GLWidget::molecule() const
1094 return d->molecule;
1097 Molecule* GLWidget::molecule()
1099 return d->molecule;
1102 const Vector3d & GLWidget::center() const
1104 return d->center;
1107 const Vector3d & GLWidget::normalVector() const
1109 return d->normalVector;
1112 const double & GLWidget::radius() const
1114 return d->radius;
1117 const Atom *GLWidget::farthestAtom() const
1119 return d->farthestAtom;
1122 void GLWidget::updateGeometry()
1124 if (d->molecule->HasData(OBGenericDataType::UnitCell))
1125 d->uc = dynamic_cast<OBUnitCell*>(d->molecule->GetData(OBGenericDataType::UnitCell));
1127 if ( !d->uc ) { // a plain molecule, no crystal cell
1128 d->center = d->molecule->center();
1129 d->normalVector = d->molecule->normalVector();
1130 d->radius = d->molecule->radius();
1131 d->farthestAtom = d->molecule->farthestAtom();
1132 } else {
1133 // render a crystal (so most geometry comes from the cell vectors)
1134 // Origin at 0.0, 0.0, 0.0
1135 // a = <x0, y0, z0>
1136 // b = <x1, y1, z1>
1137 // c = <x2, y2, z2>
1138 std::vector<vector3> cellVectors = d->uc->GetCellVectors();
1139 Vector3d a(cellVectors[0].AsArray());
1140 Vector3d b(cellVectors[1].AsArray());
1141 Vector3d c(cellVectors[2].AsArray());
1142 Vector3d centerOffset = ( a * (d->aCells - 1)
1143 + b * (d->bCells - 1)
1144 + c * (d->cCells - 1) ) / 2.0;
1145 // the center is the center of the molecule translated by centerOffset
1146 d->center = d->molecule->center() + centerOffset;
1147 // the radius is the length of centerOffset plus the molecule radius
1148 d->radius = d->molecule->radius() + centerOffset.norm();
1149 // for the normal vector, we just ask for the molecule's normal vector,
1150 // crossing our fingers hoping that it will give a nice viewpoint not only
1151 // with respect to the molecule but also with respect to the cells.
1152 d->normalVector = d->molecule->normalVector();
1153 // Computation of the farthest atom.
1154 // First case: the molecule is empty
1155 if(d->molecule->NumAtoms() == 0 ) {
1156 d->farthestAtom = 0;
1158 // Second case: there is no repetition of the molecule
1159 else if(d->aCells <= 1 && d->bCells <= 1 && d->cCells <= 1) {
1160 d->farthestAtom = d->molecule->farthestAtom();
1162 // General case: the farthest atom is the one that is located the
1163 // farthest in the direction pointed to by centerOffset.
1164 else {
1165 std::vector<OBAtom*>::iterator atom_iterator;
1166 Atom *atom;
1167 double x, max_x;
1168 for(
1169 atom = static_cast<Atom*>(d->molecule->BeginAtom(atom_iterator)),
1170 max_x = centerOffset.dot(atom->pos()),
1171 d->farthestAtom = atom;
1172 atom;
1173 atom = static_cast<Atom*>(d->molecule->NextAtom(atom_iterator))
1175 x = centerOffset.dot(atom->pos());
1176 if(x > max_x)
1178 max_x = x;
1179 d->farthestAtom = atom;
1186 Camera * GLWidget::camera() const
1188 return d->camera;
1191 QList<Engine *> GLWidget::engines() const
1193 return d->engines;
1196 QList<EngineFactory *> GLWidget::engineFactories() const
1198 return d->engineFactories;
1201 PrimitiveList GLWidget::primitives() const
1203 return d->primitives;
1206 void GLWidget::addPrimitive( Primitive *primitive )
1208 if ( primitive ) {
1209 // add the molecule to the default queue
1210 for ( int i=0; i < d->engines.size(); i++ ) {
1211 d->engines.at( i )->addPrimitive( primitive );
1213 d->primitives.append( primitive );
1217 void GLWidget::updatePrimitive( Primitive *primitive )
1219 for ( int i=0; i< d->engines.size(); i++ ) {
1220 d->engines.at( i )->updatePrimitive( primitive );
1222 updateGeometry();
1225 void GLWidget::removePrimitive( Primitive *primitive )
1227 if ( primitive ) {
1228 // add the molecule to the default queue
1229 for ( int i=0; i < d->engines.size(); i++ ) {
1230 d->engines.at( i )->removePrimitive( primitive );
1232 d->selectedPrimitives.removeAll( primitive );
1233 d->primitives.removeAll( primitive );
1237 void GLWidget::addEngine(Engine *engine)
1239 connect( engine, SIGNAL(changed()), this, SLOT(update()));
1240 connect(engine, SIGNAL(changed()), this, SLOT(invalidateDLs()));
1241 d->engines.append(engine);
1242 qSort(d->engines.begin(), d->engines.end(), engineLessThan);
1243 emit engineAdded(engine);
1244 update();
1247 void GLWidget::removeEngine(Engine *engine)
1249 disconnect(engine, SIGNAL(changed()), this, SLOT(update()));
1250 disconnect(engine, SIGNAL(changed()), this, SLOT(invalidateDLs()));
1251 d->engines.removeAll(engine);
1252 emit engineRemoved(engine);
1253 engine->deleteLater();
1254 update();
1257 void GLWidget::setTool( Tool *tool )
1259 if ( tool ) {
1260 d->tool = tool;
1264 void GLWidget::setToolGroup( ToolGroup *toolGroup )
1266 if ( d->toolGroup ) {
1267 disconnect( d->toolGroup, 0, this, 0 );
1270 if ( toolGroup ) {
1271 d->toolGroup = toolGroup;
1272 d->tool = toolGroup->activeTool();
1273 connect( toolGroup, SIGNAL( toolActivated( Tool* ) ),
1274 this, SLOT( setTool( Tool* ) ) );
1279 void GLWidget::setUndoStack( QUndoStack *undoStack )
1281 d->undoStack = undoStack;
1284 QUndoStack *GLWidget::undoStack() const
1286 return d->undoStack;
1289 Tool* GLWidget::tool() const
1291 return d->tool;
1294 ToolGroup *GLWidget::toolGroup() const
1296 return d->toolGroup;
1299 Painter *GLWidget::painter() const
1301 return d->painter;
1304 QList<GLHit> GLWidget::hits( int x, int y, int w, int h )
1306 QList<GLHit> hits;
1308 if ( !molecule() ) return hits;
1310 GLint viewport[4];
1311 unsigned int hit_count;
1313 int cx = w/2 + x;
1314 int cy = h/2 + y;
1316 // setup the selection buffer
1317 int requiredSelectBufSize = ( d->molecule->NumAtoms() + d->molecule->NumBonds() ) * 8;
1318 if ( requiredSelectBufSize > d->selectBufSize ) {
1319 //resize selection buffer
1320 if ( d->selectBuf ) delete[] d->selectBuf;
1321 // add some margin so that resizing doesn't occur every time an atom is added
1322 d->selectBufSize = requiredSelectBufSize + SEL_BUF_MARGIN;
1323 if ( d->selectBufSize > SEL_BUF_MAX_SIZE ) {
1324 d->selectBufSize = SEL_BUF_MAX_SIZE;
1326 d->selectBuf = new GLuint[d->selectBufSize];
1329 #ifdef ENABLE_THREADED_GL
1330 d->renderMutex.lock();
1331 makeCurrent();
1332 #endif
1333 //X hits.clear();
1335 glSelectBuffer( d->selectBufSize, d->selectBuf );
1336 glRenderMode( GL_SELECT );
1337 glInitNames();
1339 // Setup a projection matrix for picking in the zone delimited by (x,y,w,h).
1340 glGetIntegerv( GL_VIEWPORT, viewport );
1341 glMatrixMode( GL_PROJECTION );
1342 glPushMatrix();
1343 glLoadIdentity();
1344 gluPickMatrix( cx,viewport[3]-cy, w, h,viewport );
1346 // now multiply that projection matrix with the perspective of the camera
1347 d->camera->applyPerspective();
1349 // now load the modelview matrix from the camera
1350 glMatrixMode( GL_MODELVIEW );
1351 glPushMatrix();
1352 glLoadIdentity();
1353 d->camera->applyModelview();
1355 // now actually render using low quality a.k.a. "quickrender"
1356 bool oldQuickRender = d->quickRender;
1357 d->quickRender = true;
1358 render();
1359 d->quickRender = oldQuickRender;
1361 // returning to normal rendering mode
1362 hit_count = glRenderMode( GL_RENDER );
1364 glMatrixMode( GL_PROJECTION );
1365 glPopMatrix();
1366 glMatrixMode( GL_MODELVIEW );
1367 glPopMatrix();
1369 #ifdef ENABLE_THREADED_GL
1370 doneCurrent();
1371 d->renderMutex.unlock();
1372 #endif
1374 // if no error occurred and there are hits, process them
1375 if ( hit_count > 0 ) {
1376 unsigned int i, j;
1377 GLuint names, type, *ptr;
1378 GLuint minZ, maxZ;
1379 long name;
1381 //X printf ("hits = %d\n", hits);
1382 ptr = ( GLuint * ) d->selectBuf;
1383 // for all hits and not past end of buffer
1384 for ( i = 0; i < hit_count && !( ptr > d->selectBuf + d->selectBufSize ); i++ ) {
1385 names = *ptr++;
1386 // make sure that we won't be passing the end of bufer
1387 if ( ptr + names + 2 > d->selectBuf + d->selectBufSize ) {
1388 break;
1390 minZ = *ptr++;
1391 maxZ = *ptr++;
1393 // allow names of 0
1394 name = -1;
1395 for ( j = 0; j < names/2; j++ ) { /* for each name */
1396 type = *ptr++;
1397 name = *ptr++;
1399 if ( name > -1 ) {
1400 // printf ("%ld(%d) ", name,type);
1401 hits.append( GLHit( type,name,minZ,maxZ ) );
1404 // printf ("\n");
1405 qSort( hits );
1408 return( hits );
1411 Primitive* GLWidget::computeClickedPrimitive(const QPoint& p)
1413 QList<GLHit> chits;
1415 // Perform an OpenGL selection and retrieve the list of hits.
1416 chits = hits(p.x()-SEL_BOX_HALF_SIZE,
1417 p.y()-SEL_BOX_HALF_SIZE,
1418 SEL_BOX_SIZE, SEL_BOX_SIZE);
1420 // Find the first atom or bond (if any) in hits - this will be the closest
1421 foreach( GLHit hit, chits )
1423 //qDebug() << "Hit: " << hit.name();
1424 if(hit.type() == Primitive::AtomType) {
1425 return static_cast<Atom *>(molecule()->GetAtom(hit.name()));
1426 } else if(hit.type() == Primitive::BondType) {
1427 return static_cast<Bond *>(molecule()->GetBond(hit.name()));
1430 return 0;
1433 Atom* GLWidget::computeClickedAtom(const QPoint& p)
1435 QList<GLHit> chits;
1437 // Perform an OpenGL selection and retrieve the list of hits.
1438 chits = hits(p.x()-SEL_BOX_HALF_SIZE,
1439 p.y()-SEL_BOX_HALF_SIZE,
1440 SEL_BOX_SIZE, SEL_BOX_SIZE);
1442 // Find the first atom (if any) in hits - this will be the closest
1443 foreach( GLHit hit, chits )
1445 if(hit.type() == Primitive::AtomType)
1447 return static_cast<Atom *>(molecule()->GetAtom(hit.name()));
1450 return 0;
1453 Bond* GLWidget::computeClickedBond(const QPoint& p)
1455 QList<GLHit> chits;
1457 // Perform an OpenGL selection and retrieve the list of hits.
1458 chits = hits(p.x()-SEL_BOX_HALF_SIZE,
1459 p.y()-SEL_BOX_HALF_SIZE,
1460 SEL_BOX_SIZE, SEL_BOX_SIZE);
1462 // Find the first bond (if any) in hits - this will be the closest
1463 foreach( GLHit hit, chits )
1465 if(hit.type() == Primitive::BondType)
1467 return static_cast<Bond *>(molecule()->GetBond(hit.name()));
1470 return 0;
1473 QSize GLWidget::sizeHint() const
1475 return minimumSizeHint();
1478 QSize GLWidget::minimumSizeHint() const
1480 return QSize( 200,200 );
1483 double GLWidget::radius( const Primitive *p ) const
1485 double radius = 0.0;
1486 foreach( Engine *engine, d->engines ) {
1487 if ( engine->isEnabled() ) {
1488 double engineRadius = engine->radius( d->pd, p );
1489 if ( engineRadius > radius ) {
1490 radius = engineRadius;
1495 return radius;
1498 bool GLWidget::isStable() const
1500 return d->stable;
1503 void GLWidget::setStable( bool stable )
1505 d->stable = stable;
1508 void GLWidget::setSelected(PrimitiveList primitives, bool select)
1510 foreach( Primitive *item, primitives )
1512 if (select && !d->selectedPrimitives.contains(item))
1513 d->selectedPrimitives.append( item );
1514 else if (!select)
1515 d->selectedPrimitives.removeAll( item );
1516 // The engine caches must be invalidated
1517 d->updateCache = true;
1518 item->update();
1522 PrimitiveList GLWidget::selectedPrimitives() const
1524 return d->selectedPrimitives.list();
1527 void GLWidget::toggleSelected( PrimitiveList primitives )
1529 foreach(Primitive *item, primitives)
1531 if (d->selectedPrimitives.contains(item))
1532 d->selectedPrimitives.removeAll( item );
1533 else
1534 d->selectedPrimitives.append(item);
1536 // The engine caches must be invalidated
1537 d->updateCache = true;
1540 void GLWidget::clearSelected()
1542 d->selectedPrimitives.clear();
1543 // The engine caches must be invalidated
1544 d->updateCache = true;
1547 bool GLWidget::isSelected( const Primitive *p ) const
1549 // Return true if the item is selected
1550 return d->selectedPrimitives.contains( const_cast<Primitive *>( p ) );
1553 void GLWidget::setUnitCells( int a, int b, int c )
1555 d->aCells = a;
1556 d->bCells = b;
1557 d->cCells = c;
1558 updateGeometry();
1559 d->camera->initializeViewPoint();
1560 update();
1563 int GLWidget::aCells()
1565 return d->aCells;
1568 int GLWidget::bCells()
1570 return d->bCells;
1573 int GLWidget::cCells()
1575 return d->cCells;
1578 inline double GLWidget::computeFramesPerSecond()
1580 static QTime time;
1581 static bool firstTime = true;
1582 static int old_time, new_time;
1583 static int frames;
1584 static double fps;
1586 if( firstTime )
1588 time.start();
1589 firstTime = false;
1590 old_time = time.elapsed();
1591 frames = 0;
1592 fps = 0;
1595 new_time = time.elapsed();
1596 frames++;
1598 if( new_time - old_time > 200 )
1600 fps = 1000.0 * frames / double( new_time - old_time );
1601 frames = 0;
1602 time.restart();
1603 old_time = time.elapsed();
1606 return fps;
1609 void GLWidget::writeSettings(QSettings &settings) const
1611 settings.setValue("background", d->background);
1612 settings.setValue("quality", d->painter->quality());
1613 settings.setValue("renderAxes", d->renderAxes);
1614 settings.setValue("renderDebug", d->renderDebug);
1616 int count = d->engines.size();
1617 settings.beginWriteArray("engines");
1618 for(int i = 0; i< count; i++)
1620 settings.setArrayIndex(i);
1621 Engine *engine = d->engines.at(i);
1622 settings.setValue("engineClass", engine->metaObject()->className());
1623 engine->writeSettings(settings);
1625 settings.endArray();
1628 void GLWidget::readSettings(QSettings &settings)
1630 // Make sure to provide some default values for any settings.value("", DEFAULT) call
1631 d->painter->setQuality(settings.value("quality", 2).toInt());
1632 d->background = settings.value("background", QColor(0,0,0)).value<QColor>();
1633 d->renderAxes = settings.value("renderAxes", 1).value<bool>();
1634 d->renderDebug = settings.value("renderDebug", 0).value<bool>();
1636 int count = settings.beginReadArray("engines");
1637 for(int i=0; i<count; i++)
1639 settings.setArrayIndex(i);
1640 QString engineClass = settings.value("engineClass", QString()).toString();
1642 if(!engineClass.isEmpty() && d->engineClassFactory.contains(engineClass))
1644 EngineFactory *factory = d->engineClassFactory.value(engineClass);
1645 Engine *engine = factory->createInstance(this);
1646 engine->readSettings(settings);
1648 // eventually settings will store which has what but
1649 // for now we ignore this. (will need this when we
1650 // copy the selected primitives also).
1651 if(!engine->primitives().size())
1653 engine->setPrimitives(primitives());
1656 addEngine(engine);
1659 settings.endArray();
1661 if(!d->engines.count())
1663 loadDefaultEngines();
1667 void GLWidget::loadDefaultEngines()
1669 QList<Engine *> engines = d->engines;
1671 d->engines.clear();
1672 foreach(Engine *engine, engines)
1674 delete engine;
1677 foreach(EngineFactory *factory, d->engineFactories)
1679 Engine *engine = factory->createInstance(this);
1680 if ( engine->name() == tr("Ball and Stick") ) {
1681 engine->setEnabled( true );
1683 engine->setPrimitives(primitives());
1684 addEngine(engine);
1688 void GLWidget::invalidateDLs()
1690 // Something changed and we need to invalidate the display lists
1691 d->updateCache = true;
1695 #include "glwidget.moc"