3 * KPendulum screen saver for KDE
5 * The screen saver displays a physically realistic simulation of a two-part
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
32 #include <QColorDialog>
33 #include <QPushButton>
35 #include <QResizeEvent>
42 #include <KMessageBox>
44 // Eigen2 from KDE support
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"
59 #define M_PI 3.14159265358979323846
62 // libkscreensaver interface
63 class KPendulumSaverInterface
: public KScreenSaverInterface
66 /// aboutdata instance for libkscreensaver interface
67 virtual KAboutData
* aboutData()
69 return new KAboutData(
71 ki18n("Simulation of a two-part pendulum"),
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 //-----------------------------------------------------------------------------
97 //-----------------------------------------------------------------------------
99 PendulumOdeSolver::PendulumOdeSolver(
109 : RkOdeSolver
<double,4>(t
, y
, dt
, eps
),
110 // constants for faster numeric calculation, derived from m1,m2,l1,l2,g
114 m_C((m1
+m2
)/(m2
*m2
*l2
*l2
)),
121 Vector4d
PendulumOdeSolver::f(const double& x
, const Vector4d
& y
) const
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
;
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
);
146 //-----------------------------------------------------------------------------
147 // Rotation: screen saver widget
148 //-----------------------------------------------------------------------------
150 PendulumGLWidget::PendulumGLWidget(QWidget
* parent
)
152 m_eyeR(30), // eye coordinates (polar)
153 m_eyeTheta(M_PI
*0.45),
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
)
177 while (m_eyePhi
> 2*M_PI
)
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
)
216 void PendulumGLWidget::setM1Color(const QColor
& c
)
223 void PendulumGLWidget::setM2Color(const QColor
& 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
);
247 glEnable(GL_LIGHTING
);
251 glMatrixMode(GL_MODELVIEW
); // select modelview matrix
253 // set position of light0
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
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);
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);
304 glRotatef(m_ang1
, 1, 0, 0);
306 glTranslatef(0.5*width
, 0, -m_l1
);
307 gluCylinder(m_quadM1
, 0.2, 0.2, m_l1
, 10, 1);
312 glTranslatef(-0.5*width
, 0, -m_l1
);
313 gluCylinder(m_quadM1
, 0.2, 0.2, m_l1
, 10, 1);
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
));
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
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);
341 glRotatef(90, 0, 1, 0);
342 glTranslatef(0, 0, -0.5*masswidth
);
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
354 void PendulumGLWidget::resizeGL(int w
, int h
)
356 kDebug() << "w=" << w
<< ", h=" << h
<< "\n";
358 // prevent a divide by zero
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
);
373 static_cast<GLdouble
>(w
)/static_cast<GLdouble
>(h
),
376 // Viewing transformation, position for better view
377 // Theta is polar angle 0<Theta<Pi
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
),
386 //-----------------------------------------------------------------------------
387 // KPendulumSaver: screen saver class
388 //-----------------------------------------------------------------------------
392 KPendulumSaver::KPendulumSaver(WId id
) :
395 m_massRatio(sm_massRatioDefault
),
396 m_lengthRatio(sm_lengthRatioDefault
),
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
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()
425 // m_timer is automatically deleted with parent KPendulumSaver
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
441 KPendulumSaver::sm_massRatioDefault
));
445 KPendulumSaver::sm_lengthRatioDefault
));
449 KPendulumSaver::sm_gDefault
));
453 KPendulumSaver::sm_EDefault
));
454 setPersChangeInterval(
456 "perspective change interval",
457 (uint
)KPendulumSaver::sm_persChangeIntervalDefault
));
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
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
496 m_solver
= new PendulumOdeSolver(
498 0.01, // first dt step size estimation
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
;
554 void KPendulumSaver::setLengthRatio(const double& lengthRatio
)
556 if ((lengthRatio
>= sm_lengthRatioLimitLower
)
557 && (lengthRatio
<= sm_lengthRatioLimitUpper
)
558 && (m_lengthRatio
!= lengthRatio
))
560 m_lengthRatio
= lengthRatio
;
568 void KPendulumSaver::setG(const double& g
)
570 if ((g
>= sm_gLimitLower
)
571 && (g
<= sm_gLimitUpper
)
582 void KPendulumSaver::setE(const double& E
)
584 if ((E
>= sm_ELimitLower
)
585 && (E
<= sm_ELimitUpper
)
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
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;
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
)
729 // the dialog should block, no other control center input should be possible
730 // until the dialog is closed
733 // create input validators
734 m_mEdit
->setValidator(
735 new QDoubleValidator(
736 KPendulumSaver::sm_massRatioLimitLower
,
737 KPendulumSaver::sm_massRatioLimitUpper
,
739 m_lEdit
->setValidator(
740 new QDoubleValidator(
741 KPendulumSaver::sm_lengthRatioLimitLower
,
742 KPendulumSaver::sm_lengthRatioLimitUpper
,
744 m_gEdit
->setValidator(
745 new QDoubleValidator(
746 KPendulumSaver::sm_gLimitLower
,
747 KPendulumSaver::sm_gLimitUpper
,
749 m_eEdit
->setValidator(
750 new QDoubleValidator(
751 KPendulumSaver::sm_ELimitLower
,
752 KPendulumSaver::sm_ELimitUpper
,
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
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)
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)
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)
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)
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
)
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
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
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()
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());
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 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());
877 { // write current setting back into input field
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());
890 { // write current setting back into input field
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());
903 { // write current setting back into input field
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());
916 { // write current setting back into input field
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);
933 m_saver
->setBarColor(color
);
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);
945 m_saver
->setM1Color(color
);
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);
957 m_saver
->setM2Color(color
);
959 palette
.setColor(m_m2ColorButton
->backgroundRole(), color
);
960 m_m2ColorButton
->setPalette(palette
);