1 //============================================================================
3 // KPendulum screen saver for KDE
5 // The screen saver displays a physically realistic simulation of a two-part
8 // Developed by Georg Drenkhahn, georg-d@users.sourceforge.net
13 * Copyright (C) 2004 Georg Drenkhahn
15 * KRotation is free software; you can redistribute it and/or modify it under
16 * the terms of the GNU General Public License version 2 as published by the
17 * Free Software Foundation.
19 * KRotation is distributed in the hope that it will be useful, but WITHOUT ANY
20 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
21 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License along with
24 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
25 * Place, Suite 330, Boston, MA 02110-1301 USA
27 //============================================================================
35 #include <qlineedit.h>
37 #include <qvalidator.h>
38 #include <qcolordialog.h>
39 #include <qpushbutton.h>
42 #include <QResizeEvent>
47 #include <kmessagebox.h>
49 #include "sspreviewarea.h"
51 // pendulum.moc includes pendulum.h
52 #include "pendulum.moc"
54 #define KPENDULUM_VERSION "1.1"
56 // libkscreensaver interface
59 /// application name for libkscreensaver interface
60 KDE_EXPORT
const char *kss_applicationName
= "kpendulum.kss";
61 /// application description for libkscreensaver interface
62 KDE_EXPORT
const char *kss_description
= I18N_NOOP("Simulation of\
63 a two-part pendulum");
64 /// application version for libkscreensaver interface
65 KDE_EXPORT
const char *kss_version
= KPENDULUM_VERSION
;
67 /// function to create screen saver object
68 KDE_EXPORT KScreenSaver
* kss_create(WId id
)
70 return new KPendulumSaver(id
);
73 /// function to create setup dialog for screen saver
74 KDE_EXPORT QDialog
* kss_setup()
76 return new KPendulumSetup();
80 //-----------------------------------------------------------------------------
82 //-----------------------------------------------------------------------------
84 PendulumOdeSolver::PendulumOdeSolver(
87 std::valarray
<double> &_y
,
94 : RkOdeSolver
<double>(_t
,_y
,_dt
,_eps
),
96 B1(_m2
*_l1
*_l2
), // constants for faster numeric calculation
97 B(1.0/B1
), // derived from m1,m2,l1,l2,g
98 C((_m1
+_m2
)/(_m2
*_m2
*_l2
*_l2
)),
105 std::valarray
<double> PendulumOdeSolver::f(
107 const std::valarray
<double> &y
) const
111 const double& q1
= y
[0];
112 const double& q2
= y
[1];
113 const double& p1
= y
[2];
114 const double& p2
= y
[3];
116 const double cosDq
= std::cos(q1
-q2
);
117 const double iden
= 1.0/(M
- cosDq
*cosDq
); // invers denominator
118 const double dq1dt
= (A
*p1
- B
*cosDq
*p2
)*iden
;
119 const double dq2dt
= (C
*p2
- B
*cosDq
*p1
)*iden
;
121 std::valarray
<double> ypr(y
.size());
125 const double K
= B1
* dq1dt
*dq2dt
* std::sin(q1
-q2
);
126 ypr
[2] = -K
- D
* std::sin(q1
);
127 ypr
[3] = K
- E
* std::sin(q2
);
132 //-----------------------------------------------------------------------------
133 // Rotation: screen saver widget
134 //-----------------------------------------------------------------------------
136 PendulumGLWidget::PendulumGLWidget(QWidget
* parent
, const char* name
)
137 : QGLWidget(parent
, name
),
138 eyeR(30), // eye coordinates (polar)
141 lightR(eyeR
), // light coordinates (polar)
142 lightTheta(M_PI
*0.25),
144 quadM1(gluNewQuadric()),
145 m_barColor(KPendulumSaver::barColorDefault
),
146 m_m1Color(KPendulumSaver::m1ColorDefault
),
147 m_m2Color(KPendulumSaver::m2ColorDefault
)
151 PendulumGLWidget::~PendulumGLWidget(void)
153 gluDeleteQuadric(quadM1
);
156 void PendulumGLWidget::setEyePhi(double phi
)
159 while (eyePhi
< 0) eyePhi
+= 2.*M_PI
;
160 while (eyePhi
> 2*M_PI
) eyePhi
-= 2.*M_PI
;
164 glGetIntegerv(GL_VIEWPORT
, vp
);
165 // calc new perspective, a resize event is simulated here
166 resizeGL(static_cast<int>(vp
[2]), static_cast<int>(vp
[3]));
169 void PendulumGLWidget::setAngles(const double& q1
, const double& q2
)
171 ang1
= static_cast<GLfloat
>(q1
*180./M_PI
);
172 ang2
= static_cast<GLfloat
>(q2
*180./M_PI
);
175 void PendulumGLWidget::setMasses(const double& m1
, const double& m2
)
177 sqrtm1
= static_cast<GLfloat
>(sqrt(m1
));
178 sqrtm2
= static_cast<GLfloat
>(sqrt(m2
));
181 void PendulumGLWidget::setLengths(const double& _l1
, const double& _l2
)
183 l1
= static_cast<GLfloat
>(_l1
);
184 l2
= static_cast<GLfloat
>(_l2
);
187 void PendulumGLWidget::setBarColor(const QColor
& c
)
195 void PendulumGLWidget::setM1Color(const QColor
& c
)
202 void PendulumGLWidget::setM2Color(const QColor
& c
)
210 /* --------- protected methods ----------- */
212 void PendulumGLWidget::initializeGL(void)
214 qglClearColor(QColor(Qt::black
)); // set color to clear the background
216 glClearDepth(1); // depth buffer setup
217 glEnable(GL_DEPTH_TEST
); // depth testing
218 glDepthFunc(GL_LEQUAL
); // type of depth test
220 glShadeModel(GL_SMOOTH
); // smooth color shading in poygons
222 // nice perspective calculation
223 glHint(GL_PERSPECTIVE_CORRECTION_HINT
, GL_NICEST
);
226 glEnable(GL_LIGHTING
);
230 glMatrixMode(GL_MODELVIEW
); // select modelview matrix
232 // set positon of light0
234 {lightR
* sin(lightTheta
) * sin(lightPhi
),
235 lightR
* sin(lightTheta
) * cos(lightPhi
),
236 lightR
* cos(lightTheta
),
238 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPos
);
239 // set positon of light1
240 lightPos
[0] = lightR
* sin(lightTheta
) * sin(lightPhi
+M_PI
);
241 lightPos
[1] = lightR
* sin(lightTheta
) * cos(lightPhi
+M_PI
);
242 glLightfv(GL_LIGHT1
, GL_POSITION
, lightPos
);
244 // only for lights #>0
245 GLfloat spec
[]={1,1,1,1};
246 glLightfv(GL_LIGHT1
, GL_SPECULAR
, spec
);
247 glLightfv(GL_LIGHT1
, GL_DIFFUSE
, spec
);
249 // enable setting the material colour by glColor()
250 glEnable(GL_COLOR_MATERIAL
);
252 GLfloat emi
[4] = {.13, .13, .13, 1};
253 glMaterialfv(GL_FRONT
, GL_EMISSION
, emi
);
256 void PendulumGLWidget::paintGL(void)
258 // clear color and depth buffer
259 glClear(GL_COLOR_BUFFER_BIT
|GL_DEPTH_BUFFER_BIT
);
261 glMatrixMode(GL_MODELVIEW
); // select modelview matrix
265 static const GLfloat width
= 2.0;
266 static const GLfloat masswidth
= 1.0;
267 static const int noOfSlices
= 20;
269 // top axis, left (x>0)
270 glTranslatef(0.5*width
, 0, 0);
271 glRotatef(90, 0, 1, 0);
272 qglColor(m_barColor
);
273 gluCylinder(quadM1
, 0.2, 0.2, 5, 10, 1);
274 gluSphere(quadM1
, 0.2, 10, 10);
277 glTranslatef(-0.5*width
, 0, 0);
278 glRotatef(-90, 0, 1, 0);
279 gluCylinder(quadM1
, 0.2, 0.2, 5, 10, 1);
280 gluSphere(quadM1
, 0.2, 10, 10);
283 glRotatef(ang1
, 1, 0, 0);
285 glTranslatef(0.5*width
, 0, -l1
);
286 gluCylinder(quadM1
, 0.2, 0.2, l1
, 10, 1);
291 glTranslatef(-0.5*width
, 0, -l1
);
292 gluCylinder(quadM1
, 0.2, 0.2, l1
, 10, 1);
294 glRotatef(90, 0, 1, 0);
295 gluSphere(quadM1
, 0.2, 10, 10); // bottom corner 1
296 gluCylinder(quadM1
, 0.2, 0.2, width
, 10, 1); // connection
297 glTranslatef(0, 0, 0.5*(width
-masswidth
));
299 gluCylinder(quadM1
, sqrtm1
, sqrtm1
, masswidth
, noOfSlices
, 1); // mass 1
300 gluQuadricOrientation(quadM1
, GLU_INSIDE
);
301 gluDisk(quadM1
, 0, sqrtm1
, noOfSlices
,1); // bottom of mass
302 gluQuadricOrientation(quadM1
, GLU_OUTSIDE
);
303 glTranslatef(0, 0, masswidth
);
304 gluDisk(quadM1
, 0, sqrtm1
, noOfSlices
,1); // top of mass
306 glTranslatef(0, 0, 0.5*(width
-masswidth
));
307 qglColor(m_barColor
);
308 gluSphere(quadM1
, 0.2, 10, 10); // bottom corner 2
313 glTranslatef(0, l1
*std::sin(ang1
*M_PI
/180.), -l1
*std::cos(ang1
*M_PI
/180.));
314 glRotatef(ang2
, 1, 0, 0);
315 glTranslatef(0, 0, -l2
);
316 qglColor(m_barColor
);
317 gluCylinder(quadM1
, 0.2, 0.2, l2
, 10, 1);
320 glRotatef(90, 0, 1, 0);
321 glTranslatef(0, 0, -0.5*masswidth
);
323 gluCylinder(quadM1
, sqrtm2
, sqrtm2
, masswidth
, noOfSlices
, 1);
324 gluQuadricOrientation(quadM1
, GLU_INSIDE
);
325 gluDisk(quadM1
, 0, sqrtm2
, noOfSlices
,1); // bottom of mass
326 gluQuadricOrientation(quadM1
, GLU_OUTSIDE
);
327 glTranslatef(0, 0, masswidth
);
328 gluDisk(quadM1
, 0, sqrtm2
, noOfSlices
,1); // top of mass
333 void PendulumGLWidget::resizeGL(int w
, int h
)
335 // Prevent a divide by zero
338 // set the new view port
339 glViewport(0, 0, static_cast<GLint
>(w
), static_cast<GLint
>(h
));
341 // set up projection matrix
342 glMatrixMode(GL_PROJECTION
);
345 gluPerspective(40.0f
,
346 static_cast<GLdouble
>(w
)/static_cast<GLdouble
>(h
),
349 // Viewing transformation, position for better view
350 // Theta is polar angle 0<Theta<Pi
352 eyeR
* sin(eyeTheta
) * sin(eyePhi
),
353 eyeR
* sin(eyeTheta
) * cos(eyePhi
),
354 eyeR
* cos(eyeTheta
),
359 //-----------------------------------------------------------------------------
360 // KPendulumSaver: screen saver class
361 //-----------------------------------------------------------------------------
363 KPendulumSaver::KPendulumSaver(WId id
) :
366 m_massRatio(massRatioDefault
),
367 m_lengthRatio(lengthRatioDefault
),
370 m_persChangeInterval(persChangeIntervalDefault
)
372 setEraseColor(Qt::black
);
373 erase(); // erase area
374 glArea
= new PendulumGLWidget(this); // create gl widget
375 glArea
->setEyePhi(eyePhiDefault
);
377 readSettings(); // read global settings into pars
378 initData(); // init solver and glArea with read settings
380 embed(glArea
); // embed gl widget and resize it
381 glArea
->show(); // show gl widget
383 // set up and start cyclic timer
384 timer
= new QTimer(this);
385 timer
->start(deltaT
, TRUE
);
386 connect(timer
, SIGNAL(timeout()), this, SLOT(doTimeStep()));
389 KPendulumSaver::~KPendulumSaver()
391 // time, rotation are automatically deleted with parent KPendulumSaver
396 void KPendulumSaver::readSettings()
398 // read configuration settings from config file
399 KConfig
*config
= KGlobal::config();
400 config
->setGroup("Settings");
402 // internal saver parameters are set to stored values or left at their
403 // default values if stored values are out of range
405 config
->readDoubleNumEntry(
407 KPendulumSaver::massRatioDefault
));
409 config
->readDoubleNumEntry(
411 KPendulumSaver::lengthRatioDefault
));
413 config
->readDoubleNumEntry(
415 KPendulumSaver::gDefault
));
417 config
->readDoubleNumEntry(
419 KPendulumSaver::EDefault
));
420 setPersChangeInterval(
421 config
->readUnsignedNumEntry(
422 "perspective change interval",
423 KPendulumSaver::persChangeIntervalDefault
));
426 setBarColor(config
->readColorEntry("bar color", &barColorDefault
));
427 setM1Color( config
->readColorEntry("m1 color", &m1ColorDefault
));
428 setM2Color( config
->readColorEntry("m2 color", &m2ColorDefault
));
431 void KPendulumSaver::initData()
433 const double m1plusm2
= 2; // m1+m2
434 const double m2
= m_massRatio
* m1plusm2
;
435 const double m1
= m1plusm2
- m2
;
436 glArea
->setMasses(m1
, m2
);
437 glArea
->setAngles(0, 0);
439 const double l1plusl2
= 9; // l1+l2
440 const double l2
= m_lengthRatio
* l1plusl2
;
441 const double l1
= l1plusl2
- l2
;
442 glArea
->setLengths(l1
, l2
);
444 // kinetic energy of m2 and m1
445 const double kin_energy
= m_E
* m_g
* (l1
*m1
+ (m1
+m2
)*(l1
+l2
));
446 // angular velocity for 1. and 2. pendulum
447 const double qp
= sqrt(2.*kin_energy
/((m1
+m2
)*l1
*l1
+ m2
*l2
*l2
+ m2
*l1
*l2
));
449 // assemble initial y for solver
450 std::valarray
<double> y(4);
453 y
[2] = (m1
+m2
)*l1
*l1
*qp
+ 0.5*m2
*l1
*l2
*qp
; // p1
454 y
[3] = m2
*l2
*l2
*qp
+ 0.5*m2
*l1
*l2
*qp
; // p2
457 if (solver
!=0) delete solver
;
459 solver
= new PendulumOdeSolver(
461 0.01, // first dt step size estimation
472 void KPendulumSaver::setBarColor(const QColor
& c
)
474 glArea
->setBarColor(c
);
476 QColor
KPendulumSaver::barColor(void) const
478 return glArea
->barColor();
481 const QColor
KPendulumSaver::barColorDefault(255, 255, 127);
483 void KPendulumSaver::setM1Color(const QColor
& c
)
485 glArea
->setM1Color(c
);
487 QColor
KPendulumSaver::m1Color(void) const
489 return glArea
->m1Color();
492 const QColor
KPendulumSaver::m1ColorDefault(170, 0, 127);
494 void KPendulumSaver::setM2Color(const QColor
& c
)
496 glArea
->setM2Color(c
);
498 QColor
KPendulumSaver::m2Color(void) const
500 return glArea
->m2Color();
503 const QColor
KPendulumSaver::m2ColorDefault( 85, 170, 127);
506 void KPendulumSaver::setMassRatio(const double& massRatio
)
508 // range check is not neccessary in normal operation because validators check
509 // the values at input. But the validators do not check for corrupted
510 // settings read from disk.
511 if (massRatio
>= massRatioLimitLower
512 && massRatio
<= massRatioLimitUpper
513 && m_massRatio
!= massRatio
)
515 m_massRatio
= massRatio
;
523 const double KPendulumSaver::massRatioLimitLower
= 0.01;
524 const double KPendulumSaver::massRatioLimitUpper
= 0.99;
525 const double KPendulumSaver::massRatioDefault
= 0.5;
527 void KPendulumSaver::setLengthRatio(const double& lengthRatio
)
529 if (lengthRatio
>= lengthRatioLimitLower
530 && lengthRatio
<= lengthRatioLimitUpper
531 && m_lengthRatio
!= lengthRatio
)
533 m_lengthRatio
= lengthRatio
;
541 const double KPendulumSaver::lengthRatioLimitLower
= 0.01;
542 const double KPendulumSaver::lengthRatioLimitUpper
= 0.99;
543 const double KPendulumSaver::lengthRatioDefault
= 0.5;
545 void KPendulumSaver::setG(const double& g
)
559 const double KPendulumSaver::gLimitLower
= 0.1;
560 const double KPendulumSaver::gLimitUpper
= 300.0;
561 const double KPendulumSaver::gDefault
= 40.0;
563 void KPendulumSaver::setE(const double& E
)
577 const double KPendulumSaver::ELimitLower
= 0.0;
578 const double KPendulumSaver::ELimitUpper
= 5.0;
579 const double KPendulumSaver::EDefault
= 1.2;
581 void KPendulumSaver::setPersChangeInterval(
582 const unsigned int& persChangeInterval
)
584 if (persChangeInterval
>= persChangeIntervalLimitLower
585 && persChangeInterval
<= persChangeIntervalLimitUpper
586 && m_persChangeInterval
!= persChangeInterval
)
588 m_persChangeInterval
= persChangeInterval
;
589 // do not restart simulation here
593 const double KPendulumSaver::eyePhiDefault
= 0.25*M_PI
;
596 void KPendulumSaver::doTimeStep()
598 /* time (in seconds) of perspective change.
599 * - t<0: no change yet
600 * - t=0: change starts
601 * - 0<t<moving time: change takes place
602 * - t=moving time: end of the change */
603 static double persChangeTime
= -5;
605 // integrate a step ahead
606 solver
->integrate(0.001*deltaT
);
608 // read new y from solver
609 const std::valarray
<double> y
= solver
->Y();
611 // tell glArea the new coordinates/angles of the pendulum
612 glArea
->setAngles(y
[0], y
[1]);
614 // handle perspective change
615 persChangeTime
+= 0.001*deltaT
;
616 if (persChangeTime
> 0)
618 // phi value at the start of a perspective change
619 static double eyePhi0
= eyePhiDefault
;
620 // phi value at the end of a perspective change
621 static double eyePhi1
= 0.75*M_PI
;
622 static double deltaEyePhi
= eyePhi1
-eyePhi0
;
624 // movement acceleration/deceleration
626 // duration of the change period
627 const double movingTime
= 2.*sqrt(fabs(deltaEyePhi
)/a
);
629 // new current phi of eye
630 double eyePhi
= persChangeTime
< 0.5*movingTime
?
631 // accelerating phase
632 eyePhi0
+ (deltaEyePhi
>0?1:-1)
633 * 0.5*a
*persChangeTime
*persChangeTime
:
634 // decellerating phase
635 eyePhi1
- (deltaEyePhi
>0?1:-1)
636 * 0.5*a
*(movingTime
-persChangeTime
)*(movingTime
-persChangeTime
);
638 if (persChangeTime
>movingTime
)
639 { // perspective change has finished
640 // set new time till next change
641 persChangeTime
= -double(m_persChangeInterval
);
642 eyePhi0
= eyePhi
= eyePhi1
;
643 // find new phi value with angleLimit < phi < Pi-angleLimit or
644 // Pi+angleLimit < phi < 2*Pi-angleLimit
645 const double angleLimit
= M_PI
*0.2;
648 || (eyePhi1
<M_PI
+angleLimit
&& eyePhi1
>M_PI
-angleLimit
)
649 || eyePhi1
>2*M_PI
-angleLimit
;
650 eyePhi1
= double(rand())/RAND_MAX
* 2*M_PI
)
652 // new delta phi for next change
653 deltaEyePhi
= eyePhi1
- eyePhi0
;
654 // find shortest perspective change
655 if (deltaEyePhi
< -M_PI
) deltaEyePhi
+= 2*M_PI
;
658 glArea
->setEyePhi(eyePhi
); // set new perspective
661 glArea
->updateGL(); // repaint scenery
662 timer
->start(deltaT
, TRUE
); // restart timer
665 // public slot of KPendulumSaver, forward resize event to public slot of glArea
666 // to allow the resizing of the gl area withing the setup dialog
667 void KPendulumSaver::resizeGlArea(QResizeEvent
* e
)
669 glArea
->resize(e
->size());
672 //-----------------------------------------------------------------------------
673 // KPendulumSetup: dialog to setup screen saver parameters
674 //-----------------------------------------------------------------------------
676 KPendulumSetup::KPendulumSetup(QWidget
* parent
, const char* name
)
677 : KPendulumSetupUi(parent
, name
),
678 // create saver and give it the WinID of the preview area
679 saver(new KPendulumSaver(preview
->winId()))
681 // the dialog should block, no other control center input should be possible
682 // until the dialog is closed
685 // create input validators
686 mEdit
->setValidator(new QDoubleValidator(
687 KPendulumSaver::massRatioLimitLower
,
688 KPendulumSaver::massRatioLimitUpper
,
690 lEdit
->setValidator(new QDoubleValidator(
691 KPendulumSaver::lengthRatioLimitLower
,
692 KPendulumSaver::lengthRatioLimitUpper
,
694 gEdit
->setValidator(new QDoubleValidator(
695 KPendulumSaver::gLimitLower
,
696 KPendulumSaver::gLimitUpper
,
698 eEdit
->setValidator(new QDoubleValidator(
699 KPendulumSaver::ELimitLower
,
700 KPendulumSaver::ELimitUpper
,
703 // set input limits for the perspective change interval time
704 persSpinBox
->setMinValue(KPendulumSaver::persChangeIntervalLimitLower
);
705 persSpinBox
->setMaxValue(KPendulumSaver::persChangeIntervalLimitUpper
);
707 // set tool tips of editable fields
710 i18n("Ratio of 2nd mass to sum of both masses.\nValid values from %1 to %2.")
711 .arg(KPendulumSaver::massRatioLimitLower
, 0, 'f', 2)
712 .arg(KPendulumSaver::massRatioLimitUpper
, 0, 'f', 2));
715 i18n("Ratio of 2nd pendulum part length to the sum of both part lengths.\nValid values from %1 to %2.")
716 .arg(KPendulumSaver::lengthRatioLimitLower
, 0, 'f', 2)
717 .arg(KPendulumSaver::lengthRatioLimitUpper
, 0, 'f', 2));
720 i18n("Gravitational constant in arbitrary units.\nValid values from %1 to %2.")
721 .arg(KPendulumSaver::gLimitLower
, 0, 'f', 2)
722 .arg(KPendulumSaver::gLimitUpper
, 0, 'f', 2));
725 i18n("Energy in units of the maximum potential energy of the given configuration.\nValid values from %1 to %2.")
726 .arg(KPendulumSaver::ELimitLower
, 0, 'f', 2)
727 .arg(KPendulumSaver::ELimitUpper
, 0, 'f', 2));
730 i18n("Time in seconds after which a random perspective change occurs.\nValid values from %1 to %2.")
731 .arg(KPendulumSaver::persChangeIntervalLimitLower
)
732 .arg(KPendulumSaver::persChangeIntervalLimitUpper
));
735 preview
->setBackgroundColor(Qt::black
);
736 preview
->show(); // otherwise saver does not get correct size
738 // read settings from saver and update GUI elements with these values, saver
739 // has read settings in its constructor
741 // set editable fields with stored values as defaults
743 text
.setNum(saver
->massRatio());
744 mEdit
->setText(text
);
745 text
.setNum(saver
->lengthRatio());
746 lEdit
->setText(text
);
747 text
.setNum(saver
->g());
748 gEdit
->setText(text
);
749 text
.setNum(saver
->E());
750 eEdit
->setText(text
);
752 persSpinBox
->setValue(saver
->persChangeInterval());
754 barColorButton
->setPaletteBackgroundColor(saver
->barColor());
755 m1ColorButton
->setPaletteBackgroundColor(saver
->m1Color());
756 m2ColorButton
->setPaletteBackgroundColor(saver
->m2Color());
758 // if the preview area is resized it emmits the resized() event which is
759 // caught by the saver. The embedded GlArea is resized to fit into the
761 connect(preview
, SIGNAL(resized(QResizeEvent
*)),
762 saver
, SLOT(resizeGlArea(QResizeEvent
*)));
765 KPendulumSetup::~KPendulumSetup()
770 // Ok pressed - save settings and exit
771 void KPendulumSetup::okButtonClickedSlot()
773 KConfig
* config
= KGlobal::config();
774 config
->setGroup("Settings");
776 config
->writeEntry("mass ratio", saver
->massRatio());
777 config
->writeEntry("length ratio", saver
->lengthRatio());
778 config
->writeEntry("g", saver
->g());
779 config
->writeEntry("E", saver
->E());
780 config
->writeEntry("perspective change interval",
781 saver
->persChangeInterval());
782 config
->writeEntry("bar color", saver
->barColor());
783 config
->writeEntry("m1 color", saver
->m1Color());
784 config
->writeEntry("m2 color", saver
->m2Color());
790 void KPendulumSetup::aboutButtonClickedSlot()
792 KMessageBox::about(this, i18n("\
793 <h3>KPendulum Screen Saver for KDE</h3>\
794 <p>Simulation of a two-part pendulum</p>\
795 <p>Copyright (c) Georg Drenkhahn 2004</p>\
796 <p><tt>georg-d@users.sourceforge.net</tt></p>"));
799 void KPendulumSetup::mEditLostFocusSlot(void)
801 if (mEdit
->hasAcceptableInput())
803 saver
->setMassRatio(mEdit
->text().toDouble());
806 { // write current setting back into input field
808 text
.setNum(saver
->massRatio());
809 mEdit
->setText(text
);
812 void KPendulumSetup::lEditLostFocusSlot(void)
814 if (lEdit
->hasAcceptableInput())
816 saver
->setLengthRatio(lEdit
->text().toDouble());
819 { // write current setting back into input field
821 text
.setNum(saver
->lengthRatio());
822 lEdit
->setText(text
);
825 void KPendulumSetup::gEditLostFocusSlot(void)
827 if (gEdit
->hasAcceptableInput())
829 saver
->setG(gEdit
->text().toDouble());
832 { // write current setting back into input field
834 text
.setNum(saver
->g());
835 gEdit
->setText(text
);
838 void KPendulumSetup::eEditLostFocusSlot(void)
840 if (eEdit
->hasAcceptableInput())
842 saver
->setE(eEdit
->text().toDouble());
845 { // write current setting back into input field
847 text
.setNum(saver
->E());
848 eEdit
->setText(text
);
851 void KPendulumSetup::persChangeEnteredSlot(int t
)
853 saver
->setPersChangeInterval(t
);
856 void KPendulumSetup::barColorButtonClickedSlot(void)
858 QColor color
= QColorDialog::getColor(
859 saver
->barColor(), this, QString("bar color dialog"));
862 saver
->setBarColor(color
);
863 barColorButton
->setPaletteBackgroundColor(color
);
866 void KPendulumSetup::m1ColorButtonClickedSlot(void)
868 QColor color
= QColorDialog::getColor(
869 saver
->m1Color(), this, QString("mass 1 color dialog"));
872 saver
->setM1Color(color
);
873 m1ColorButton
->setPaletteBackgroundColor(color
);
876 void KPendulumSetup::m2ColorButtonClickedSlot(void)
878 QColor color
= QColorDialog::getColor(
879 saver
->m2Color(), this, QString("mass 2 color dialog"));
882 saver
->setM2Color(color
);
883 m2ColorButton
->setPaletteBackgroundColor(color
);