SVN_SILENT made messages (.desktop file)
[kdeartwork.git] / kscreensaver / kdesavers / pendulum.cpp
blobb397d56dd5fd0e57a4dc94fbe4b67ad5d618b568
1 /** @file
3 * KPendulum screen saver for KDE
5 * The screen saver displays a physically realistic simulation of a two-part
6 * pendulum.
8 * Copyright (C) 2004 Georg Drenkhahn, Georg.Drenkhahn@gmx.net
10 * This program is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free Software
12 * Foundation; either version 2 of the License or (at your option) version 3 or
13 * any later version accepted by the membership of KDE e.V. (or its successor
14 * approved by the membership of KDE e.V.), which shall act as a proxy defined
15 * in Section 14 of version 3 of the license.
17 * This program is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
20 * details.
23 #define QT_NO_COMPAT
25 // std. C++ headers
26 #include <cstdlib>
28 // Qt headers
29 #include <QLineEdit>
30 #include <QSpinBox>
31 #include <QValidator>
32 #include <QColorDialog>
33 #include <QPushButton>
34 #include <QToolTip>
35 #include <QResizeEvent>
37 // KDE headers
38 #include <KLocale>
39 #include <KGlobal>
40 #include <KConfig>
41 #include <KDebug>
42 #include <KMessageBox>
44 // Eigen2 from KDE support
45 #include <Eigen/Core>
46 #include <Eigen/Geometry>
47 // import most common Eigen types
48 USING_PART_OF_NAMESPACE_EIGEN
50 // the screen saver preview area class
51 #include "sspreviewarea.h"
53 #include "pendulum.h" // own interfaces
54 #include "pendulum.moc"
56 #define KPENDULUM_VERSION "2.0"
58 #ifndef M_PI
59 #define M_PI 3.14159265358979323846
60 #endif
62 // libkscreensaver interface
63 class KPendulumSaverInterface : public KScreenSaverInterface
65 public:
66 /// aboutdata instance for libkscreensaver interface
67 virtual KAboutData* aboutData()
69 return new KAboutData(
70 "kpendulum.kss", 0,
71 ki18n("Simulation of a two-part pendulum"),
72 KPENDULUM_VERSION,
73 ki18n("Simulation of a two-part pendulum"));
76 /// function to create screen saver object
77 virtual KScreenSaver* create(WId id)
79 return new KPendulumSaver(id);
82 /// function to create setup dialog for screen saver
83 virtual QDialog* setup()
85 return new KPendulumSetup();
89 int main( int argc, char *argv[] )
91 KPendulumSaverInterface kss;
92 return kScreenSaverMain(argc, argv, kss);
95 //-----------------------------------------------------------------------------
96 // PendulumOdeSolver
97 //-----------------------------------------------------------------------------
99 PendulumOdeSolver::PendulumOdeSolver(
100 const double& t,
101 const double& dt,
102 const Vector4d& y,
103 const double& eps,
104 const double& m1,
105 const double& m2,
106 const double& l1,
107 const double& l2,
108 const double& g)
109 : RkOdeSolver<double,4>(t, y, dt, eps),
110 // constants for faster numeric calculation, derived from m1,m2,l1,l2,g
111 m_A(1.0/(m2*l1*l1)),
112 m_B1(m2*l1*l2),
113 m_B(1.0/m_B1),
114 m_C((m1+m2)/(m2*m2*l2*l2)),
115 m_D(g*(m1+m2)*l1),
116 m_E(g*m2*l2),
117 m_M((m1+m2)/m2)
121 Vector4d PendulumOdeSolver::f(const double& x, const Vector4d& y) const
123 (void)x; // unused
125 const double& q1 = y[0];
126 const double& q2 = y[1];
127 const double& p1 = y[2];
128 const double& p2 = y[3];
130 const double cosDq = std::cos(q1-q2);
131 const double iden = 1.0/(m_M - cosDq*cosDq); // invers denominator
132 const double dq1dt = (m_A*p1 - m_B*cosDq*p2)*iden;
133 const double dq2dt = (m_C*p2 - m_B*cosDq*p1)*iden;
135 Vector4d ypr;
136 ypr[0] = dq1dt;
137 ypr[1] = dq2dt;
139 const double K = m_B1 * dq1dt*dq2dt * std::sin(q1-q2);
140 ypr[2] = -K - m_D * std::sin(q1);
141 ypr[3] = K - m_E * std::sin(q2);
143 return ypr;
146 //-----------------------------------------------------------------------------
147 // Rotation: screen saver widget
148 //-----------------------------------------------------------------------------
150 PendulumGLWidget::PendulumGLWidget(QWidget* parent)
151 : QGLWidget(parent),
152 m_eyeR(30), // eye coordinates (polar)
153 m_eyeTheta(M_PI*0.45),
154 m_eyePhi(0),
155 m_lightR(m_eyeR), // light coordinates (polar)
156 m_lightTheta(M_PI*0.25),
157 m_lightPhi(M_PI*0.25),
158 m_quadM1(gluNewQuadric()),
159 m_barColor(KPendulumSaver::sm_barColorDefault),
160 m_m1Color(KPendulumSaver::sm_m1ColorDefault),
161 m_m2Color(KPendulumSaver::sm_m2ColorDefault)
165 PendulumGLWidget::~PendulumGLWidget(void)
167 gluDeleteQuadric(m_quadM1);
170 void PendulumGLWidget::setEyePhi(double phi)
172 m_eyePhi = phi;
173 while (m_eyePhi < 0)
175 m_eyePhi += 2.*M_PI;
177 while (m_eyePhi > 2*M_PI)
179 m_eyePhi -= 2.*M_PI;
182 // get the view port
183 GLint vp[4];
184 glGetIntegerv(GL_VIEWPORT, vp);
186 // calc new perspective, a resize event is simulated here
187 resizeGL(static_cast<int>(vp[2]), static_cast<int>(vp[3]));
190 void PendulumGLWidget::setAngles(const double& q1, const double& q2)
192 m_ang1 = static_cast<GLfloat>(q1*180./M_PI);
193 m_ang2 = static_cast<GLfloat>(q2*180./M_PI);
196 void PendulumGLWidget::setMasses(const double& m1, const double& m2)
198 m_sqrtm1 = static_cast<GLfloat>(std::sqrt(m1));
199 m_sqrtm2 = static_cast<GLfloat>(std::sqrt(m2));
202 void PendulumGLWidget::setLengths(const double& l1, const double& l2)
204 m_l1 = static_cast<GLfloat>(l1);
205 m_l2 = static_cast<GLfloat>(l2);
208 void PendulumGLWidget::setBarColor(const QColor& c)
210 if (c.isValid())
212 m_barColor = c;
216 void PendulumGLWidget::setM1Color(const QColor& c)
218 if (c.isValid())
220 m_m1Color = c;
223 void PendulumGLWidget::setM2Color(const QColor& c)
225 if (c.isValid())
227 m_m2Color = c;
231 /* --------- protected methods ----------- */
233 void PendulumGLWidget::initializeGL(void)
235 qglClearColor(QColor(Qt::black)); // set color to clear the background
237 glClearDepth(1); // depth buffer setup
238 glEnable(GL_DEPTH_TEST); // depth testing
239 glDepthFunc(GL_LEQUAL); // type of depth test
241 glShadeModel(GL_SMOOTH); // smooth color shading in poygons
243 // nice perspective calculation
244 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
246 // set up the light
247 glEnable(GL_LIGHTING);
248 glEnable(GL_LIGHT0);
249 glEnable(GL_LIGHT1);
251 glMatrixMode(GL_MODELVIEW); // select modelview matrix
252 glLoadIdentity();
253 // set position of light0
254 GLfloat lightPos[4]=
255 {m_lightR * std::sin(m_lightTheta) * std::sin(m_lightPhi),
256 m_lightR * std::sin(m_lightTheta) * std::cos(m_lightPhi),
257 m_lightR * std::cos(m_lightTheta),
259 glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
260 // set position of light1
261 lightPos[0] = m_lightR * std::sin(m_lightTheta) * std::sin(m_lightPhi+M_PI);
262 lightPos[1] = m_lightR * std::sin(m_lightTheta) * std::cos(m_lightPhi+M_PI);
263 glLightfv(GL_LIGHT1, GL_POSITION, lightPos);
265 // only for lights #>0
266 GLfloat spec[] = {1,1,1,1};
267 glLightfv(GL_LIGHT1, GL_SPECULAR, spec);
268 glLightfv(GL_LIGHT1, GL_DIFFUSE, spec);
270 // enable setting the material colour by glColor()
271 glEnable(GL_COLOR_MATERIAL);
273 GLfloat emi[4] = {.13, .13, .13, 1};
274 glMaterialfv(GL_FRONT, GL_EMISSION, emi);
277 void PendulumGLWidget::paintGL(void)
279 // clear color and depth buffer
280 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
282 glMatrixMode(GL_MODELVIEW); // select modelview matrix
284 glLoadIdentity();
286 const GLfloat width = 2.0;
287 const GLfloat masswidth = 1.0;
288 const int noOfSlices = 20;
290 // top axis, left (x>0)
291 glTranslatef(0.5*width, 0, 0);
292 glRotatef(90, 0, 1, 0);
293 qglColor(m_barColor);
294 gluCylinder(m_quadM1, 0.2, 0.2, 5, 10, 1);
295 gluSphere(m_quadM1, 0.2, 10, 10);
296 // top axis, right
297 glLoadIdentity();
298 glTranslatef(-0.5*width, 0, 0);
299 glRotatef(-90, 0, 1, 0);
300 gluCylinder(m_quadM1, 0.2, 0.2, 5, 10, 1);
301 gluSphere(m_quadM1, 0.2, 10, 10);
302 // 1. part, left
303 glLoadIdentity();
304 glRotatef(m_ang1, 1, 0, 0);
305 glPushMatrix();
306 glTranslatef(0.5*width, 0, -m_l1);
307 gluCylinder(m_quadM1, 0.2, 0.2, m_l1, 10, 1);
308 glPopMatrix();
310 // 1. part, right
311 glPushMatrix();
312 glTranslatef(-0.5*width, 0, -m_l1);
313 gluCylinder(m_quadM1, 0.2, 0.2, m_l1, 10, 1);
314 // 1. part, bottom
315 glRotatef(90, 0, 1, 0);
316 gluSphere(m_quadM1, 0.2, 10, 10); // bottom corner 1
317 gluCylinder(m_quadM1, 0.2, 0.2, width, 10, 1); // connection
318 glTranslatef(0, 0, 0.5*(width-masswidth));
319 qglColor(m_m1Color);
320 gluCylinder(m_quadM1, m_sqrtm1, m_sqrtm1, masswidth, noOfSlices, 1); // mass 1
321 gluQuadricOrientation(m_quadM1, GLU_INSIDE);
322 gluDisk(m_quadM1, 0, m_sqrtm1, noOfSlices, 1); // bottom of mass
323 gluQuadricOrientation(m_quadM1, GLU_OUTSIDE);
324 glTranslatef(0, 0, masswidth);
325 gluDisk(m_quadM1, 0, m_sqrtm1, noOfSlices, 1); // top of mass
327 glTranslatef(0, 0, 0.5*(width-masswidth));
328 qglColor(m_barColor);
329 gluSphere(m_quadM1, 0.2, 10, 10); // bottom corner 2
330 glPopMatrix();
332 // 2. pendulum bar
333 glLoadIdentity();
334 glTranslatef(0, m_l1*std::sin(m_ang1*M_PI/180.), -m_l1*std::cos(m_ang1*M_PI/180.));
335 glRotatef(m_ang2, 1, 0, 0);
336 glTranslatef(0, 0, -m_l2);
337 qglColor(m_barColor);
338 gluCylinder(m_quadM1, 0.2, 0.2, m_l2, 10, 1);
340 // mass 2
341 glRotatef(90, 0, 1, 0);
342 glTranslatef(0, 0, -0.5*masswidth);
343 qglColor(m_m2Color);
344 gluCylinder(m_quadM1, m_sqrtm2, m_sqrtm2, masswidth, noOfSlices, 1);
345 gluQuadricOrientation(m_quadM1, GLU_INSIDE);
346 gluDisk(m_quadM1, 0, m_sqrtm2, noOfSlices, 1); // bottom of mass
347 gluQuadricOrientation(m_quadM1, GLU_OUTSIDE);
348 glTranslatef(0, 0, masswidth);
349 gluDisk(m_quadM1, 0, m_sqrtm2, noOfSlices, 1); // top of mass
351 glFlush();
354 void PendulumGLWidget::resizeGL(int w, int h)
356 kDebug() << "w=" << w << ", h=" << h << "\n";
358 // prevent a divide by zero
359 if (h == 0)
361 return;
364 // set the new view port
365 glViewport(0, 0, static_cast<GLint>(w), static_cast<GLint>(h));
367 // set up projection matrix
368 glMatrixMode(GL_PROJECTION);
369 glLoadIdentity();
370 // Perspective view
371 gluPerspective(
372 40.0f,
373 static_cast<GLdouble>(w)/static_cast<GLdouble>(h),
374 1.0, 100.0f);
376 // Viewing transformation, position for better view
377 // Theta is polar angle 0<Theta<Pi
378 gluLookAt(
379 m_eyeR * std::sin(m_eyeTheta) * std::sin(m_eyePhi),
380 m_eyeR * std::sin(m_eyeTheta) * std::cos(m_eyePhi),
381 m_eyeR * std::cos(m_eyeTheta),
382 0,0,0,
383 0,0,1);
386 //-----------------------------------------------------------------------------
387 // KPendulumSaver: screen saver class
388 //-----------------------------------------------------------------------------
390 // class methods
392 KPendulumSaver::KPendulumSaver(WId id) :
393 KScreenSaver(id),
394 m_solver(0),
395 m_massRatio(sm_massRatioDefault),
396 m_lengthRatio(sm_lengthRatioDefault),
397 m_g(sm_gDefault),
398 m_E(sm_EDefault),
399 m_persChangeInterval(sm_persChangeIntervalDefault)
401 // no need to set our parent widget's background here, the GL widget sets its
402 // own background color
404 readSettings(); // read global settings into pars
406 m_glArea = new PendulumGLWidget(); // create gl widget w/o parent
407 m_glArea->setEyePhi(sm_eyePhiDefault);
409 // init m_glArea with read settings, construct and init m_solver
410 initData();
412 embed(m_glArea); // embed gl widget and resize it
413 m_glArea->show(); // show embedded gl widget
415 // set up and start cyclic timer
416 m_timer = new QTimer(this);
417 m_timer->start(sm_deltaT);
418 connect(m_timer, SIGNAL(timeout()), this, SLOT(doTimeStep()));
421 KPendulumSaver::~KPendulumSaver()
423 m_timer->stop();
425 // m_timer is automatically deleted with parent KPendulumSaver
426 delete m_solver;
427 delete m_glArea;
431 void KPendulumSaver::readSettings()
433 // read configuration settings from config file
434 KConfigGroup config(KGlobal::config(), "Settings");
436 // internal saver parameters are set to stored values or left at their
437 // default values if stored values are out of range
438 setMassRatio(
439 config.readEntry(
440 "mass ratio",
441 KPendulumSaver::sm_massRatioDefault));
442 setLengthRatio(
443 config.readEntry(
444 "length ratio",
445 KPendulumSaver::sm_lengthRatioDefault));
446 setG(
447 config.readEntry(
448 "g",
449 KPendulumSaver::sm_gDefault));
450 setE(
451 config.readEntry(
452 "E",
453 KPendulumSaver::sm_EDefault));
454 setPersChangeInterval(
455 config.readEntry(
456 "perspective change interval",
457 (uint)KPendulumSaver::sm_persChangeIntervalDefault));
459 // set the colours
460 setBarColor(config.readEntry("bar color", sm_barColorDefault));
461 setM1Color( config.readEntry("m1 color", sm_m1ColorDefault));
462 setM2Color( config.readEntry("m2 color", sm_m2ColorDefault));
465 void KPendulumSaver::initData()
467 const double m1plusm2 = 2; // m1+m2
468 const double m2 = m_massRatio * m1plusm2;
469 const double m1 = m1plusm2 - m2;
470 m_glArea->setMasses(m1, m2);
471 m_glArea->setAngles(0, 0);
473 const double l1plusl2 = 9; // l1+l2
474 const double l2 = m_lengthRatio * l1plusl2;
475 const double l1 = l1plusl2 - l2;
476 m_glArea->setLengths(l1, l2);
478 // kinetic energy of m2 and m1
479 const double kin_energy = m_E * m_g * (l1*m1 + (m1+m2)*(l1+l2));
480 // angular velocity for 1. and 2. pendulum
481 const double qp = std::sqrt(2.*kin_energy/((m1+m2)*l1*l1 + m2*l2*l2 + m2*l1*l2));
483 // assemble initial y for solver
484 Vector4d y;
485 y[0] = 0; // q1
486 y[1] = 0; // q2
487 y[2] = (m1+m2)*l1*l1*qp + 0.5*m2*l1*l2*qp; // p1
488 y[3] = m2*l2*l2*qp + 0.5*m2*l1*l2*qp; // p2
490 // delete old solver
491 if (m_solver!=0)
493 delete m_solver;
495 // init new solver
496 m_solver = new PendulumOdeSolver(
497 0.0, // t
498 0.01, // first dt step size estimation
500 1e-5, // eps
505 m_g);
509 void KPendulumSaver::setBarColor(const QColor& c)
511 m_glArea->setBarColor(c);
513 QColor KPendulumSaver::barColor(void) const
515 return m_glArea->barColor();
518 void KPendulumSaver::setM1Color(const QColor& c)
520 m_glArea->setM1Color(c);
522 QColor KPendulumSaver::m1Color(void) const
524 return m_glArea->m1Color();
527 void KPendulumSaver::setM2Color(const QColor& c)
529 m_glArea->setM2Color(c);
531 QColor KPendulumSaver::m2Color(void) const
533 return m_glArea->m2Color();
537 void KPendulumSaver::setMassRatio(const double& massRatio)
539 // range check is not necessary in normal operation because validators check
540 // the values at input. But the validators do not check for corrupted
541 // settings read from disk.
542 if ((massRatio >= sm_massRatioLimitLower)
543 && (massRatio <= sm_massRatioLimitUpper)
544 && (m_massRatio != massRatio))
546 m_massRatio = massRatio;
547 if (m_timer!=0)
549 initData();
554 void KPendulumSaver::setLengthRatio(const double& lengthRatio)
556 if ((lengthRatio >= sm_lengthRatioLimitLower)
557 && (lengthRatio <= sm_lengthRatioLimitUpper)
558 && (m_lengthRatio != lengthRatio))
560 m_lengthRatio = lengthRatio;
561 if (m_timer!=0)
563 initData();
568 void KPendulumSaver::setG(const double& g)
570 if ((g >= sm_gLimitLower)
571 && (g <= sm_gLimitUpper)
572 && (m_g != g))
574 m_g = g;
575 if (m_timer!=0)
577 initData();
582 void KPendulumSaver::setE(const double& E)
584 if ((E >= sm_ELimitLower)
585 && (E <= sm_ELimitUpper)
586 && (m_E != E))
588 m_E = E;
589 if (m_timer!=0)
591 initData();
596 void KPendulumSaver::setPersChangeInterval(
597 const unsigned int& persChangeInterval)
599 if ((persChangeInterval >= sm_persChangeIntervalLimitLower)
600 && (persChangeInterval <= sm_persChangeIntervalLimitUpper)
601 && (m_persChangeInterval != persChangeInterval))
603 m_persChangeInterval = persChangeInterval;
604 // do not restart simulation here
608 void KPendulumSaver::doTimeStep()
610 /* time (in seconds) of perspective change.
611 * - t<0: no change yet
612 * - t=0: change starts
613 * - 0<t<moving time: change takes place
614 * - t=moving time: end of the change */
615 static double persChangeTime = -5;
617 // integrate a step ahead
618 m_solver->integrate(0.001 * sm_deltaT);
620 // read new y from solver
621 const Vector4d& y = m_solver->Y();
623 // tell glArea the new coordinates/angles of the pendulum
624 m_glArea->setAngles(y[0], y[1]);
626 // handle perspective change
627 persChangeTime += 0.001 * sm_deltaT;
628 if (persChangeTime > 0)
630 // phi value at the start of a perspective change
631 static double eyePhi0 = sm_eyePhiDefault;
632 // phi value at the end of a perspective change
633 static double eyePhi1 = 0.75*M_PI;
634 static double deltaEyePhi = eyePhi1-eyePhi0;
636 // movement acceleration/deceleration
637 const double a = 3;
638 // duration of the change period
639 const double movingTime = 2.*std::sqrt(std::abs(deltaEyePhi)/a);
641 // new current phi of eye
642 double eyePhi = persChangeTime < 0.5*movingTime ?
643 // accelerating phase
644 eyePhi0 + (deltaEyePhi>0?1:-1)
645 * 0.5*a*persChangeTime*persChangeTime:
646 // decellerating phase
647 eyePhi1 - (deltaEyePhi>0?1:-1)
648 * 0.5*a*(movingTime-persChangeTime)*(movingTime-persChangeTime);
650 if (persChangeTime > movingTime)
651 { // perspective change has finished
652 // set new time till next change
653 persChangeTime = -double(m_persChangeInterval);
654 eyePhi0 = eyePhi = eyePhi1;
655 // find new phi value with angleLimit < phi < Pi-angleLimit or
656 // Pi+angleLimit < phi < 2*Pi-angleLimit
657 const double angleLimit = M_PI*0.2;
658 for (eyePhi1 = 0;
659 (eyePhi1<angleLimit)
660 || ((eyePhi1<M_PI+angleLimit) && (eyePhi1>M_PI-angleLimit))
661 || (eyePhi1>2*M_PI-angleLimit);
662 eyePhi1 = double(rand())/RAND_MAX * 2*M_PI)
665 // new delta phi for next change
666 deltaEyePhi = eyePhi1 - eyePhi0;
667 // find shortest perspective change
668 if (deltaEyePhi < -M_PI)
670 deltaEyePhi += 2*M_PI;
674 m_glArea->setEyePhi(eyePhi); // set new perspective
677 m_glArea->updateGL(); // repaint scenery
679 // restarting timer not necessary here, it is a cyclic timer
682 // public slot of KPendulumSaver, forward resize event to public slot of glArea
683 // to allow the resizing of the gl area withing the setup dialog
684 void KPendulumSaver::resizeGlArea(QResizeEvent* e)
686 m_glArea->resize(e->size());
689 // public static class member variables
691 const QColor KPendulumSaver::sm_barColorDefault(255, 255, 127);
692 const QColor KPendulumSaver::sm_m1ColorDefault( 170, 0, 127);
693 const QColor KPendulumSaver::sm_m2ColorDefault( 85, 170, 127);
695 const double KPendulumSaver::sm_massRatioLimitLower = 0.01;
696 const double KPendulumSaver::sm_massRatioLimitUpper = 0.99;
697 const double KPendulumSaver::sm_massRatioDefault = 0.5;
699 const double KPendulumSaver::sm_lengthRatioLimitLower = 0.01;
700 const double KPendulumSaver::sm_lengthRatioLimitUpper = 0.99;
701 const double KPendulumSaver::sm_lengthRatioDefault = 0.5;
703 const double KPendulumSaver::sm_gLimitLower = 0.1;
704 const double KPendulumSaver::sm_gLimitUpper = 300.0;
705 const double KPendulumSaver::sm_gDefault = 40.0;
707 const double KPendulumSaver::sm_ELimitLower = 0.0;
708 const double KPendulumSaver::sm_ELimitUpper = 5.0;
709 const double KPendulumSaver::sm_EDefault = 1.2;
711 const unsigned int KPendulumSaver::sm_persChangeIntervalLimitLower = 5;
712 const unsigned int KPendulumSaver::sm_persChangeIntervalLimitUpper = 600;
713 const unsigned int KPendulumSaver::sm_persChangeIntervalDefault = 15;
715 // private static class member variables
717 const unsigned int KPendulumSaver::sm_deltaT = 20;
718 const double KPendulumSaver::sm_eyePhiDefault = 0.25 * M_PI;
720 //-----------------------------------------------------------------------------
721 // KPendulumSetup: dialog to setup screen saver parameters
722 //-----------------------------------------------------------------------------
724 KPendulumSetup::KPendulumSetup(QWidget* parent)
725 : QDialog(parent)
727 setupUi(this);
729 // the dialog should block, no other control center input should be possible
730 // until the dialog is closed
731 setModal(true);
733 // create input validators
734 m_mEdit->setValidator(
735 new QDoubleValidator(
736 KPendulumSaver::sm_massRatioLimitLower,
737 KPendulumSaver::sm_massRatioLimitUpper,
738 5, m_mEdit));
739 m_lEdit->setValidator(
740 new QDoubleValidator(
741 KPendulumSaver::sm_lengthRatioLimitLower,
742 KPendulumSaver::sm_lengthRatioLimitUpper,
743 5, m_lEdit));
744 m_gEdit->setValidator(
745 new QDoubleValidator(
746 KPendulumSaver::sm_gLimitLower,
747 KPendulumSaver::sm_gLimitUpper,
748 5, m_gEdit));
749 m_eEdit->setValidator(
750 new QDoubleValidator(
751 KPendulumSaver::sm_ELimitLower,
752 KPendulumSaver::sm_ELimitUpper,
753 5, m_eEdit));
755 // set input limits for the perspective change interval time
756 m_persSpinBox->setMinimum(KPendulumSaver::sm_persChangeIntervalLimitLower);
757 m_persSpinBox->setMaximum(KPendulumSaver::sm_persChangeIntervalLimitUpper);
759 // set tool tips of editable fields
760 m_mEdit->setToolTip(
761 ki18n("Ratio of 2nd mass to sum of both masses.\nValid values from %1 to %2.")
762 .subs(KPendulumSaver::sm_massRatioLimitLower, 0, 'f', 2)
763 .subs(KPendulumSaver::sm_massRatioLimitUpper, 0, 'f', 2)
764 .toString());
765 m_lEdit->setToolTip(
766 ki18n("Ratio of 2nd pendulum part length to the sum of both part lengths.\nValid values from %1 to %2.")
767 .subs(KPendulumSaver::sm_lengthRatioLimitLower, 0, 'f', 2)
768 .subs(KPendulumSaver::sm_lengthRatioLimitUpper, 0, 'f', 2)
769 .toString());
770 m_gEdit->setToolTip(
771 ki18n("Gravitational constant in arbitrary units.\nValid values from %1 to %2.")
772 .subs(KPendulumSaver::sm_gLimitLower, 0, 'f', 2)
773 .subs(KPendulumSaver::sm_gLimitUpper, 0, 'f', 2)
774 .toString());
775 m_eEdit->setToolTip(
776 ki18n("Energy in units of the maximum potential energy of the given configuration.\nValid values from %1 to %2.")
777 .subs(KPendulumSaver::sm_ELimitLower, 0, 'f', 2)
778 .subs(KPendulumSaver::sm_ELimitUpper, 0, 'f', 2)
779 .toString());
780 m_persSpinBox->setToolTip(
781 ki18n("Time in seconds after which a random perspective change occurs.\nValid values from %1 to %2.")
782 .subs(KPendulumSaver::sm_persChangeIntervalLimitLower)
783 .subs(KPendulumSaver::sm_persChangeIntervalLimitUpper)
784 .toString());
786 // init preview area
787 QPalette palette;
788 palette.setColor(m_preview->backgroundRole(), Qt::black);
789 m_preview->setPalette(palette);
790 m_preview->show(); // otherwise saver does not get correct size
792 // create saver and give it the WinID of the preview area
793 m_saver = new KPendulumSaver(m_preview->winId());
795 // read settings from saver and update GUI elements with these values, saver
796 // has read settings in its constructor
798 // set editable fields with stored values as defaults
799 QString text;
800 text.setNum(m_saver->massRatio());
801 m_mEdit->setText(text);
802 text.setNum(m_saver->lengthRatio());
803 m_lEdit->setText(text);
804 text.setNum(m_saver->g());
805 m_gEdit->setText(text);
806 text.setNum(m_saver->E());
807 m_eEdit->setText(text);
809 m_persSpinBox->setValue(m_saver->persChangeInterval());
811 palette.setColor(m_barColorButton->backgroundRole(), m_saver->barColor());
812 m_barColorButton->setPalette(palette);
813 palette.setColor(m_m1ColorButton->backgroundRole(), m_saver->m1Color());
814 m_m1ColorButton->setPalette(palette);
815 palette.setColor(m_m2ColorButton->backgroundRole(), m_saver->m2Color());
816 m_m2ColorButton->setPalette(palette);
818 // if the preview area is resized it emits the resized() event which is
819 // caught by m_saver. The embedded GLArea is resized to fit into the preview
820 // area.
821 connect(m_preview, SIGNAL(resized(QResizeEvent*)),
822 m_saver, SLOT(resizeGlArea(QResizeEvent*)));
824 connect(m_okButton, SIGNAL(clicked()), this, SLOT(okButtonClickedSlot()));
825 connect(m_aboutButton, SIGNAL(clicked()), this, SLOT(aboutButtonClickedSlot()));
827 connect(m_lEdit, SIGNAL(lostFocus()), this, SLOT(lEditLostFocusSlot()));
828 connect(m_gEdit, SIGNAL(lostFocus()), this, SLOT(gEditLostFocusSlot()));
829 connect(m_eEdit, SIGNAL(lostFocus()), this, SLOT(eEditLostFocusSlot()));
830 connect(m_persSpinBox, SIGNAL(valueChanged(int)), this, SLOT(persChangeEnteredSlot(int)));
831 connect(m_mEdit, SIGNAL(lostFocus()), this, SLOT(mEditLostFocusSlot()));
832 connect(m_barColorButton, SIGNAL(clicked()), this, SLOT(barColorButtonClickedSlot()));
833 connect(m_m1ColorButton, SIGNAL(clicked()), this, SLOT(m1ColorButtonClickedSlot()));
834 connect(m_m2ColorButton, SIGNAL(clicked()), this, SLOT(m2ColorButtonClickedSlot()));
837 KPendulumSetup::~KPendulumSetup()
839 delete m_saver;
842 // Ok pressed - save settings and exit
843 void KPendulumSetup::okButtonClickedSlot()
845 KConfigGroup config(KGlobal::config(), "Settings");
847 config.writeEntry("mass ratio", m_saver->massRatio());
848 config.writeEntry("length ratio", m_saver->lengthRatio());
849 config.writeEntry("g", m_saver->g());
850 config.writeEntry("E", m_saver->E());
851 config.writeEntry("perspective change interval",
852 m_saver->persChangeInterval());
853 config.writeEntry("bar color", m_saver->barColor());
854 config.writeEntry("m1 color", m_saver->m1Color());
855 config.writeEntry("m2 color", m_saver->m2Color());
857 config.sync();
858 accept();
861 void KPendulumSetup::aboutButtonClickedSlot()
863 KMessageBox::about(this, i18n("\
864 <h3>KPendulum Screen Saver for KDE</h3>\
865 <p>Simulation of a two-part pendulum</p>\
866 <p>Copyright (c) Georg&nbsp;Drenkhahn 2004</p>\
867 <p><tt>Georg.Drenkhahn@gmx.net</tt></p>"));
870 void KPendulumSetup::mEditLostFocusSlot(void)
872 if (m_mEdit->hasAcceptableInput())
874 m_saver->setMassRatio(m_mEdit->text().toDouble());
876 else
877 { // write current setting back into input field
878 QString text;
879 text.setNum(m_saver->massRatio());
880 m_mEdit->setText(text);
883 void KPendulumSetup::lEditLostFocusSlot(void)
885 if (m_lEdit->hasAcceptableInput())
887 m_saver->setLengthRatio(m_lEdit->text().toDouble());
889 else
890 { // write current setting back into input field
891 QString text;
892 text.setNum(m_saver->lengthRatio());
893 m_lEdit->setText(text);
896 void KPendulumSetup::gEditLostFocusSlot(void)
898 if (m_gEdit->hasAcceptableInput())
900 m_saver->setG(m_gEdit->text().toDouble());
902 else
903 { // write current setting back into input field
904 QString text;
905 text.setNum(m_saver->g());
906 m_gEdit->setText(text);
909 void KPendulumSetup::eEditLostFocusSlot(void)
911 if (m_eEdit->hasAcceptableInput())
913 m_saver->setE(m_eEdit->text().toDouble());
915 else
916 { // write current setting back into input field
917 QString text;
918 text.setNum(m_saver->E());
919 m_eEdit->setText(text);
922 void KPendulumSetup::persChangeEnteredSlot(int t)
924 m_saver->setPersChangeInterval(t);
927 void KPendulumSetup::barColorButtonClickedSlot(void)
929 QColor color = QColorDialog::getColor(
930 m_saver->barColor(), this);
931 if (color.isValid())
933 m_saver->setBarColor(color);
934 QPalette palette;
935 palette.setColor(m_barColorButton->backgroundRole(), color);
936 m_barColorButton->setPalette(palette);
939 void KPendulumSetup::m1ColorButtonClickedSlot(void)
941 QColor color = QColorDialog::getColor(
942 m_saver->m1Color(), this);
943 if (color.isValid())
945 m_saver->setM1Color(color);
946 QPalette palette;
947 palette.setColor(m_m1ColorButton->backgroundRole(), color);
948 m_m1ColorButton->setPalette(palette);
951 void KPendulumSetup::m2ColorButtonClickedSlot(void)
953 QColor color = QColorDialog::getColor(
954 m_saver->m2Color(), this);
955 if (color.isValid())
957 m_saver->setM2Color(color);
958 QPalette palette;
959 palette.setColor(m_m2ColorButton->backgroundRole(), color);
960 m_m2ColorButton->setPalette(palette);