diff options
Diffstat (limited to 'tdescreensaver/kdesavers/pendulum.cpp')
-rw-r--r-- | tdescreensaver/kdesavers/pendulum.cpp | 881 |
1 files changed, 881 insertions, 0 deletions
diff --git a/tdescreensaver/kdesavers/pendulum.cpp b/tdescreensaver/kdesavers/pendulum.cpp new file mode 100644 index 00000000..d1889877 --- /dev/null +++ b/tdescreensaver/kdesavers/pendulum.cpp @@ -0,0 +1,881 @@ +//============================================================================ +// +// KPendulum screen saver for KDE +// +// The screen saver displays a physically realistic simulation of a two-part +// pendulum. +// +// Developed by Georg Drenkhahn, georg-d@users.sourceforge.net +// +// $Id$ +// +/* + * Copyright (C) 2004 Georg Drenkhahn + * + * KRotation is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * KRotation is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02110-1301 USA + */ +//============================================================================ + +// std. C++ headers +#include <cstdlib> + +// TQt headers +#include <tqlineedit.h> +#include <tqspinbox.h> +#include <tqvalidator.h> +#include <tqcolordialog.h> +#include <tqpushbutton.h> +#include <tqtooltip.h> +// KDE headers +#include <klocale.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kmessagebox.h> + +#include "sspreviewarea.h" + +// pendulum.moc includes pendulum.h +#include "pendulum.moc" + +#define KPENDULUM_VERSION "1.1" + +// libtdescreensaver interface +extern "C" +{ + /// application name for libtdescreensaver interface + KDE_EXPORT const char *kss_applicationName = "kpendulum.kss"; + /// application description for libtdescreensaver interface + KDE_EXPORT const char *kss_description = I18N_NOOP("Simulation of\ + a two-part pendulum"); + /// application version for libtdescreensaver interface + KDE_EXPORT const char *kss_version = KPENDULUM_VERSION; + + /// function to create screen saver object + KDE_EXPORT KScreenSaver* kss_create(WId id) + { + return new KPendulumSaver(id); + } + + /// function to create setup dialog for screen saver + KDE_EXPORT TQDialog* kss_setup() + { + return new KPendulumSetup(); + } +} + +//----------------------------------------------------------------------------- +// PendulumOdeSolver +//----------------------------------------------------------------------------- + +PendulumOdeSolver::PendulumOdeSolver( + const double &_t, + const double &_dt, + std::valarray<double> &_y, + const double &_eps, + const double &_m1, + const double &_m2, + const double &_l1, + const double &_l2, + const double &_g) + : RkOdeSolver<double>(_t,_y,_dt,_eps), + A(1.0/(_m2*_l1*_l1)), + B1(_m2*_l1*_l2), // constants for faster numeric calculation + B(1.0/B1), // derived from m1,m2,l1,l2,g + C((_m1+_m2)/(_m2*_m2*_l2*_l2)), + D(_g*(_m1+_m2)*_l1), + E(_g*_m2*_l2), + M((_m1+_m2)/_m2) +{ +} + +std::valarray<double> PendulumOdeSolver::f( + const double &x, + const std::valarray<double> &y) const +{ + (void)x; // unused + + const double& q1 = y[0]; + const double& q2 = y[1]; + const double& p1 = y[2]; + const double& p2 = y[3]; + + const double cosDq = std::cos(q1-q2); + const double iden = 1.0/(M - cosDq*cosDq); // invers denominator + const double dq1dt = (A*p1 - B*cosDq*p2)*iden; + const double dq2dt = (C*p2 - B*cosDq*p1)*iden; + + std::valarray<double> ypr(y.size()); + ypr[0] = dq1dt; + ypr[1] = dq2dt; + + const double K = B1 * dq1dt*dq2dt * std::sin(q1-q2); + ypr[2] = -K - D * std::sin(q1); + ypr[3] = K - E * std::sin(q2); + + return ypr; +} + +//----------------------------------------------------------------------------- +// Rotation: screen saver widget +//----------------------------------------------------------------------------- + +PendulumGLWidget::PendulumGLWidget(TQWidget* parent, const char* name) + : TQGLWidget(parent, name), + eyeR(30), // eye coordinates (polar) + eyeTheta(M_PI*0.45), + eyePhi(0), + lightR(eyeR), // light coordinates (polar) + lightTheta(M_PI*0.25), + lightPhi(M_PI*0.25), + quadM1(gluNewQuadric()), + m_barColor(KPendulumSaver::barColorDefault), + m_m1Color(KPendulumSaver::m1ColorDefault), + m_m2Color(KPendulumSaver::m2ColorDefault) +{ +} + +PendulumGLWidget::~PendulumGLWidget(void) +{ + gluDeleteQuadric(quadM1); +} + +void PendulumGLWidget::setEyePhi(double phi) +{ + eyePhi = phi; + while (eyePhi < 0) eyePhi += 2.*M_PI; + while (eyePhi > 2*M_PI) eyePhi -= 2.*M_PI; + + // get the view port + static GLint vp[4]; + glGetIntegerv(GL_VIEWPORT, vp); + // calc new perspective, a resize event is simulated here + resizeGL(static_cast<int>(vp[2]), static_cast<int>(vp[3])); +} + +void PendulumGLWidget::setAngles(const double& q1, const double& q2) +{ + ang1 = static_cast<GLfloat>(q1*180./M_PI); + ang2 = static_cast<GLfloat>(q2*180./M_PI); +} + +void PendulumGLWidget::setMasses(const double& m1, const double& m2) +{ + sqrtm1 = static_cast<GLfloat>(sqrt(m1)); + sqrtm2 = static_cast<GLfloat>(sqrt(m2)); +} + +void PendulumGLWidget::setLengths(const double& _l1, const double& _l2) +{ + l1 = static_cast<GLfloat>(_l1); + l2 = static_cast<GLfloat>(_l2); +} + +void PendulumGLWidget::setBarColor(const TQColor& c) +{ + if (c.isValid()) + { + m_barColor = c; + } +} + +void PendulumGLWidget::setM1Color(const TQColor& c) +{ + if (c.isValid()) + { + m_m1Color = c; + } +} +void PendulumGLWidget::setM2Color(const TQColor& c) +{ + if (c.isValid()) + { + m_m2Color = c; + } +} + +/* --------- protected methods ----------- */ + +void PendulumGLWidget::initializeGL(void) +{ + qglClearColor(TQColor(black)); // set color to clear the background + + glClearDepth(1); // depth buffer setup + glEnable(GL_DEPTH_TEST); // depth testing + glDepthFunc(GL_LEQUAL); // type of depth test + + glShadeModel(GL_SMOOTH); // smooth color shading in poygons + + // nice perspective calculation + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + // set up the light + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + + glMatrixMode(GL_MODELVIEW); // select modelview matrix + glLoadIdentity(); + // set positon of light0 + GLfloat lightPos[4]= + {lightR * sin(lightTheta) * sin(lightPhi), + lightR * sin(lightTheta) * cos(lightPhi), + lightR * cos(lightTheta), + 0}; + glLightfv(GL_LIGHT0, GL_POSITION, lightPos); + // set positon of light1 + lightPos[0] = lightR * sin(lightTheta) * sin(lightPhi+M_PI); + lightPos[1] = lightR * sin(lightTheta) * cos(lightPhi+M_PI); + glLightfv(GL_LIGHT1, GL_POSITION, lightPos); + + // only for lights #>0 + GLfloat spec[]={1,1,1,1}; + glLightfv(GL_LIGHT1, GL_SPECULAR, spec); + glLightfv(GL_LIGHT1, GL_DIFFUSE, spec); + + // enable setting the material colour by glColor() + glEnable(GL_COLOR_MATERIAL); + + GLfloat emi[4] = {.13, .13, .13, 1}; + glMaterialfv(GL_FRONT, GL_EMISSION, emi); +} + +void PendulumGLWidget::paintGL(void) +{ + // clear color and depth buffer + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_MODELVIEW); // select modelview matrix + + glLoadIdentity(); + + static const GLfloat width = 2.0; + static const GLfloat masswidth = 1.0; + static const int noOfSlices = 20; + + // top axis, left (x>0) + glTranslatef(0.5*width, 0, 0); + glRotatef(90, 0, 1, 0); + qglColor(m_barColor); + gluCylinder(quadM1, 0.2, 0.2, 5, 10, 1); + gluSphere(quadM1, 0.2, 10, 10); + // top axis, right + glLoadIdentity(); + glTranslatef(-0.5*width, 0, 0); + glRotatef(-90, 0, 1, 0); + gluCylinder(quadM1, 0.2, 0.2, 5, 10, 1); + gluSphere(quadM1, 0.2, 10, 10); + // 1. part, left + glLoadIdentity(); + glRotatef(ang1, 1, 0, 0); + glPushMatrix(); + glTranslatef(0.5*width, 0, -l1); + gluCylinder(quadM1, 0.2, 0.2, l1, 10, 1); + glPopMatrix(); + + // 1. part, right + glPushMatrix(); + glTranslatef(-0.5*width, 0, -l1); + gluCylinder(quadM1, 0.2, 0.2, l1, 10, 1); + // 1. part, bottom + glRotatef(90, 0, 1, 0); + gluSphere(quadM1, 0.2, 10, 10); // bottom corner 1 + gluCylinder(quadM1, 0.2, 0.2, width, 10, 1); // connection + glTranslatef(0, 0, 0.5*(width-masswidth)); + qglColor(m_m1Color); + gluCylinder(quadM1, sqrtm1, sqrtm1, masswidth, noOfSlices, 1); // mass 1 + gluQuadricOrientation(quadM1, GLU_INSIDE); + gluDisk(quadM1, 0, sqrtm1, noOfSlices,1); // bottom of mass + gluQuadricOrientation(quadM1, GLU_OUTSIDE); + glTranslatef(0, 0, masswidth); + gluDisk(quadM1, 0, sqrtm1, noOfSlices,1); // top of mass + + glTranslatef(0, 0, 0.5*(width-masswidth)); + qglColor(m_barColor); + gluSphere(quadM1, 0.2, 10, 10); // bottom corner 2 + glPopMatrix(); + + // 2. pendulum bar + glLoadIdentity(); + glTranslatef(0, l1*std::sin(ang1*M_PI/180.), -l1*std::cos(ang1*M_PI/180.)); + glRotatef(ang2, 1, 0, 0); + glTranslatef(0, 0, -l2); + qglColor(m_barColor); + gluCylinder(quadM1, 0.2, 0.2, l2, 10, 1); + + // mass 2 + glRotatef(90, 0, 1, 0); + glTranslatef(0, 0, -0.5*masswidth); + qglColor(m_m2Color); + gluCylinder(quadM1, sqrtm2, sqrtm2, masswidth, noOfSlices, 1); + gluQuadricOrientation(quadM1, GLU_INSIDE); + gluDisk(quadM1, 0, sqrtm2, noOfSlices,1); // bottom of mass + gluQuadricOrientation(quadM1, GLU_OUTSIDE); + glTranslatef(0, 0, masswidth); + gluDisk(quadM1, 0, sqrtm2, noOfSlices,1); // top of mass + + glFlush(); +} + +void PendulumGLWidget::resizeGL(int w, int h) +{ + // Prevent a divide by zero + if (h == 0) h = 1; + + // set the new view port + glViewport(0, 0, static_cast<GLint>(w), static_cast<GLint>(h)); + + // set up projection matrix + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + // Perspective view + gluPerspective(40.0f, + static_cast<GLdouble>(w)/static_cast<GLdouble>(h), + 1.0, 100.0f); + + // Viewing transformation, position for better view + // Theta is polar angle 0<Theta<Pi + gluLookAt( + eyeR * sin(eyeTheta) * sin(eyePhi), + eyeR * sin(eyeTheta) * cos(eyePhi), + eyeR * cos(eyeTheta), + 0,0,0, + 0,0,1); +} + +//----------------------------------------------------------------------------- +// KPendulumSaver: screen saver class +//----------------------------------------------------------------------------- + +KPendulumSaver::KPendulumSaver(WId id) : + KScreenSaver(id), + solver(0), + m_massRatio(massRatioDefault), + m_lengthRatio(lengthRatioDefault), + m_g(gDefault), + m_E(EDefault), + m_persChangeInterval(persChangeIntervalDefault) +{ + setEraseColor(black); + erase(); // erase area + glArea = new PendulumGLWidget(this); // create gl widget + glArea->setEyePhi(eyePhiDefault); + + readSettings(); // read global settings into pars + initData(); // init solver and glArea with read settings + + embed(glArea); // embed gl widget and resize it + glArea->show(); // show gl widget + + // set up and start cyclic timer + timer = new TQTimer(this); + timer->start(deltaT, TRUE); + connect(timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(doTimeStep())); +} + +KPendulumSaver::~KPendulumSaver() +{ + // time, rotation are automatically deleted with parent KPendulumSaver + delete solver; +} + + +void KPendulumSaver::readSettings() +{ + // read configuration settings from config file + TDEConfig *config = TDEGlobal::config(); + config->setGroup("Settings"); + + // internal saver parameters are set to stored values or left at their + // default values if stored values are out of range + setMassRatio( + config->readDoubleNumEntry( + "mass ratio", + KPendulumSaver::massRatioDefault)); + setLengthRatio( + config->readDoubleNumEntry( + "length ratio", + KPendulumSaver::lengthRatioDefault)); + setG( + config->readDoubleNumEntry( + "g", + KPendulumSaver::gDefault)); + setE( + config->readDoubleNumEntry( + "E", + KPendulumSaver::EDefault)); + setPersChangeInterval( + config->readUnsignedNumEntry( + "perspective change interval", + KPendulumSaver::persChangeIntervalDefault)); + + // set the colours + setBarColor(config->readColorEntry("bar color", &barColorDefault)); + setM1Color( config->readColorEntry("m1 color", &m1ColorDefault)); + setM2Color( config->readColorEntry("m2 color", &m2ColorDefault)); +} + +void KPendulumSaver::initData() +{ + const double m1plusm2 = 2; // m1+m2 + const double m2 = m_massRatio * m1plusm2; + const double m1 = m1plusm2 - m2; + glArea->setMasses(m1, m2); + glArea->setAngles(0, 0); + + const double l1plusl2 = 9; // l1+l2 + const double l2 = m_lengthRatio * l1plusl2; + const double l1 = l1plusl2 - l2; + glArea->setLengths(l1, l2); + + // kinetic energy of m2 and m1 + const double kin_energy = m_E * m_g * (l1*m1 + (m1+m2)*(l1+l2)); + // angular velocity for 1. and 2. pendulum + const double qp = sqrt(2.*kin_energy/((m1+m2)*l1*l1 + m2*l2*l2 + m2*l1*l2)); + + // assemble initial y for solver + std::valarray<double> y(4); + y[0] = 0; // q1 + y[1] = 0; // q2 + y[2] = (m1+m2)*l1*l1*qp + 0.5*m2*l1*l2*qp; // p1 + y[3] = m2*l2*l2*qp + 0.5*m2*l1*l2*qp; // p2 + + // delete old solver + if (solver!=0) delete solver; + // init new solver + solver = new PendulumOdeSolver( + 0.0, // t + 0.01, // first dt step size estimation + y, + 1e-5, // eps + m1, + m2, + l1, + l2, + m_g); +} + + +void KPendulumSaver::setBarColor(const TQColor& c) +{ + glArea->setBarColor(c); +} +TQColor KPendulumSaver::barColor(void) const +{ + return glArea->barColor(); +} + +const TQColor KPendulumSaver::barColorDefault(255, 255, 127); + +void KPendulumSaver::setM1Color(const TQColor& c) +{ + glArea->setM1Color(c); +} +TQColor KPendulumSaver::m1Color(void) const +{ + return glArea->m1Color(); +} + +const TQColor KPendulumSaver::m1ColorDefault(170, 0, 127); + +void KPendulumSaver::setM2Color(const TQColor& c) +{ + glArea->setM2Color(c); +} +TQColor KPendulumSaver::m2Color(void) const +{ + return glArea->m2Color(); +} + +const TQColor KPendulumSaver::m2ColorDefault( 85, 170, 127); + + +void KPendulumSaver::setMassRatio(const double& massRatio) +{ + // range check is not neccessary in normal operation because validators check + // the values at input. But the validators do not check for corrupted + // settings read from disk. + if (massRatio >= massRatioLimitLower + && massRatio <= massRatioLimitUpper + && m_massRatio != massRatio) + { + m_massRatio = massRatio; + if (timer!=0) + { + initData(); + } + } +} + +const double KPendulumSaver::massRatioLimitLower = 0.01; +const double KPendulumSaver::massRatioLimitUpper = 0.99; +const double KPendulumSaver::massRatioDefault = 0.5; + +void KPendulumSaver::setLengthRatio(const double& lengthRatio) +{ + if (lengthRatio >= lengthRatioLimitLower + && lengthRatio <= lengthRatioLimitUpper + && m_lengthRatio != lengthRatio) + { + m_lengthRatio = lengthRatio; + if (timer!=0) + { + initData(); + } + } +} + +const double KPendulumSaver::lengthRatioLimitLower = 0.01; +const double KPendulumSaver::lengthRatioLimitUpper = 0.99; +const double KPendulumSaver::lengthRatioDefault = 0.5; + +void KPendulumSaver::setG(const double& g) +{ + if (g >= gLimitLower + && g <= gLimitUpper + && m_g != g) + { + m_g = g; + if (timer!=0) + { + initData(); + } + } +} + +const double KPendulumSaver::gLimitLower = 0.1; +const double KPendulumSaver::gLimitUpper = 300.0; +const double KPendulumSaver::gDefault = 40.0; + +void KPendulumSaver::setE(const double& E) +{ + if (E >= ELimitLower + && E <= ELimitUpper + && m_E != E) + { + m_E = E; + if (timer!=0) + { + initData(); + } + } +} + +const double KPendulumSaver::ELimitLower = 0.0; +const double KPendulumSaver::ELimitUpper = 5.0; +const double KPendulumSaver::EDefault = 1.2; + +void KPendulumSaver::setPersChangeInterval( + const unsigned int& persChangeInterval) +{ + if (persChangeInterval >= persChangeIntervalLimitLower + && persChangeInterval <= persChangeIntervalLimitUpper + && m_persChangeInterval != persChangeInterval) + { + m_persChangeInterval = persChangeInterval; + // do not restart simulation here + } +} + +const double KPendulumSaver::eyePhiDefault = 0.25*M_PI; + + +void KPendulumSaver::doTimeStep() +{ + /* time (in seconds) of perspective change. + * - t<0: no change yet + * - t=0: change starts + * - 0<t<moving time: change takes place + * - t=moving time: end of the change */ + static double persChangeTime = -5; + + // integrate a step ahead + solver->integrate(0.001*deltaT); + + // read new y from solver + const std::valarray<double> y = solver->Y(); + + // tell glArea the new coordinates/angles of the pendulum + glArea->setAngles(y[0], y[1]); + + // handle perspective change + persChangeTime += 0.001*deltaT; + if (persChangeTime > 0) + { + // phi value at the start of a perspective change + static double eyePhi0 = eyePhiDefault; + // phi value at the end of a perspective change + static double eyePhi1 = 0.75*M_PI; + static double deltaEyePhi = eyePhi1-eyePhi0; + + // movement acceleration/deceleration + const double a = 3; + // duration of the change period + const double movingTime = 2.*sqrt(fabs(deltaEyePhi)/a); + + // new current phi of eye + double eyePhi = persChangeTime < 0.5*movingTime ? + // accelerating phase + eyePhi0 + (deltaEyePhi>0?1:-1) + * 0.5*a*persChangeTime*persChangeTime: + // decellerating phase + eyePhi1 - (deltaEyePhi>0?1:-1) + * 0.5*a*(movingTime-persChangeTime)*(movingTime-persChangeTime); + + if (persChangeTime>movingTime) + { // perspective change has finished + // set new time till next change + persChangeTime = -double(m_persChangeInterval); + eyePhi0 = eyePhi = eyePhi1; + // find new phi value with angleLimit < phi < Pi-angleLimit or + // Pi+angleLimit < phi < 2*Pi-angleLimit + const double angleLimit = M_PI*0.2; + for (eyePhi1 = 0; + eyePhi1<angleLimit + || (eyePhi1<M_PI+angleLimit && eyePhi1>M_PI-angleLimit) + || eyePhi1>2*M_PI-angleLimit; + eyePhi1 = double(rand())/RAND_MAX * 2*M_PI) + {} + // new delta phi for next change + deltaEyePhi = eyePhi1 - eyePhi0; + // find shortest perspective change + if (deltaEyePhi < -M_PI) deltaEyePhi += 2*M_PI; + } + + glArea->setEyePhi(eyePhi); // set new perspective + } + + glArea->updateGL(); // repaint scenery + timer->start(deltaT, TRUE); // restart timer +} + +// public slot of KPendulumSaver, forward resize event to public slot of glArea +// to allow the resizing of the gl area withing the setup dialog +void KPendulumSaver::resizeGlArea(TQResizeEvent* e) +{ + glArea->resize(e->size()); +} + +//----------------------------------------------------------------------------- +// KPendulumSetup: dialog to setup screen saver parameters +//----------------------------------------------------------------------------- + +KPendulumSetup::KPendulumSetup(TQWidget* parent, const char* name) + : KPendulumSetupUi(parent, name), + // create saver and give it the WinID of the preview area + saver(new KPendulumSaver(preview->winId())) +{ + // the dialog should block, no other control center input should be possible + // until the dialog is closed + setModal(TRUE); + + // create input validators + mEdit->setValidator(new TQDoubleValidator( + KPendulumSaver::massRatioLimitLower, + KPendulumSaver::massRatioLimitUpper, + 5, mEdit)); + lEdit->setValidator(new TQDoubleValidator( + KPendulumSaver::lengthRatioLimitLower, + KPendulumSaver::lengthRatioLimitUpper, + 5, lEdit)); + gEdit->setValidator(new TQDoubleValidator( + KPendulumSaver::gLimitLower, + KPendulumSaver::gLimitUpper, + 5, gEdit)); + eEdit->setValidator(new TQDoubleValidator( + KPendulumSaver::ELimitLower, + KPendulumSaver::ELimitUpper, + 5, eEdit)); + + // set input limits for the perspective change interval time + persSpinBox->setMinValue(KPendulumSaver::persChangeIntervalLimitLower); + persSpinBox->setMaxValue(KPendulumSaver::persChangeIntervalLimitUpper); + + // set tool tips of editable fields + TQToolTip::add( + mEdit, + i18n("Ratio of 2nd mass to sum of both masses.\nValid values from %1 to %2.") + .arg(KPendulumSaver::massRatioLimitLower, 0, 'f', 2) + .arg(KPendulumSaver::massRatioLimitUpper, 0, 'f', 2)); + TQToolTip::add( + lEdit, + i18n("Ratio of 2nd pendulum part length to the sum of both part lengths.\nValid values from %1 to %2.") + .arg(KPendulumSaver::lengthRatioLimitLower, 0, 'f', 2) + .arg(KPendulumSaver::lengthRatioLimitUpper, 0, 'f', 2)); + TQToolTip::add( + gEdit, + i18n("Gravitational constant in arbitrary units.\nValid values from %1 to %2.") + .arg(KPendulumSaver::gLimitLower, 0, 'f', 2) + .arg(KPendulumSaver::gLimitUpper, 0, 'f', 2)); + TQToolTip::add( + eEdit, + i18n("Energy in units of the maximum potential energy of the given configuration.\nValid values from %1 to %2.") + .arg(KPendulumSaver::ELimitLower, 0, 'f', 2) + .arg(KPendulumSaver::ELimitUpper, 0, 'f', 2)); + TQToolTip::add( + persSpinBox, + i18n("Time in seconds after which a random perspective change occurs.\nValid values from %1 to %2.") + .arg(KPendulumSaver::persChangeIntervalLimitLower) + .arg(KPendulumSaver::persChangeIntervalLimitUpper)); + + // init preview area + preview->setBackgroundColor(black); + preview->show(); // otherwise saver does not get correct size + + // read settings from saver and update GUI elements with these values, saver + // has read settings in its constructor + + // set editable fields with stored values as defaults + TQString text; + text.setNum(saver->massRatio()); + mEdit->setText(text); + text.setNum(saver->lengthRatio()); + lEdit->setText(text); + text.setNum(saver->g()); + gEdit->setText(text); + text.setNum(saver->E()); + eEdit->setText(text); + + persSpinBox->setValue(saver->persChangeInterval()); + + barColorButton->setPaletteBackgroundColor(saver->barColor()); + m1ColorButton->setPaletteBackgroundColor(saver->m1Color()); + m2ColorButton->setPaletteBackgroundColor(saver->m2Color()); + + // if the preview area is resized it emmits the resized() event which is + // caught by the saver. The embedded GlArea is resized to fit into the + // preview area. + connect(preview, TQT_SIGNAL(resized(TQResizeEvent*)), + saver, TQT_SLOT(resizeGlArea(TQResizeEvent*))); +} + +KPendulumSetup::~KPendulumSetup() +{ + delete saver; +} + +// Ok pressed - save settings and exit +void KPendulumSetup::okButtonClickedSlot() +{ + TDEConfig* config = TDEGlobal::config(); + config->setGroup("Settings"); + + config->writeEntry("mass ratio", saver->massRatio()); + config->writeEntry("length ratio", saver->lengthRatio()); + config->writeEntry("g", saver->g()); + config->writeEntry("E", saver->E()); + config->writeEntry("perspective change interval", + saver->persChangeInterval()); + config->writeEntry("bar color", saver->barColor()); + config->writeEntry("m1 color", saver->m1Color()); + config->writeEntry("m2 color", saver->m2Color()); + + config->sync(); + accept(); +} + +void KPendulumSetup::aboutButtonClickedSlot() +{ + KMessageBox::about(this, i18n("\ +<h3>KPendulum Screen Saver for KDE</h3>\ +<p>Simulation of a two-part pendulum</p>\ +<p>Copyright (c) Georg Drenkhahn 2004</p>\ +<p><tt>georg-d@users.sourceforge.net</tt></p>")); +} + +void KPendulumSetup::mEditLostFocusSlot(void) +{ + if (mEdit->hasAcceptableInput()) + { + saver->setMassRatio(mEdit->text().toDouble()); + } + else + { // write current setting back into input field + TQString text; + text.setNum(saver->massRatio()); + mEdit->setText(text); + } +} +void KPendulumSetup::lEditLostFocusSlot(void) +{ + if (lEdit->hasAcceptableInput()) + { + saver->setLengthRatio(lEdit->text().toDouble()); + } + else + { // write current setting back into input field + TQString text; + text.setNum(saver->lengthRatio()); + lEdit->setText(text); + } +} +void KPendulumSetup::gEditLostFocusSlot(void) +{ + if (gEdit->hasAcceptableInput()) + { + saver->setG(gEdit->text().toDouble()); + } + else + { // write current setting back into input field + TQString text; + text.setNum(saver->g()); + gEdit->setText(text); + } +} +void KPendulumSetup::eEditLostFocusSlot(void) +{ + if (eEdit->hasAcceptableInput()) + { + saver->setE(eEdit->text().toDouble()); + } + else + { // write current setting back into input field + TQString text; + text.setNum(saver->E()); + eEdit->setText(text); + } +} +void KPendulumSetup::persChangeEnteredSlot(int t) +{ + saver->setPersChangeInterval(t); +} + +void KPendulumSetup::barColorButtonClickedSlot(void) +{ + TQColor color = TQColorDialog::getColor( + saver->barColor(), this, "bar color dialog"); + if (color.isValid()) + { + saver->setBarColor(color); + barColorButton->setPaletteBackgroundColor(color); + } +} +void KPendulumSetup::m1ColorButtonClickedSlot(void) +{ + TQColor color = TQColorDialog::getColor( + saver->m1Color(), this, "mass 1 color dialog"); + if (color.isValid()) + { + saver->setM1Color(color); + m1ColorButton->setPaletteBackgroundColor(color); + } +} +void KPendulumSetup::m2ColorButtonClickedSlot(void) +{ + TQColor color = TQColorDialog::getColor( + saver->m2Color(), this, "mass 2 color dialog"); + if (color.isValid()) + { + saver->setM2Color(color); + m2ColorButton->setPaletteBackgroundColor(color); + } +} |