You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdetoys/kodo/kodometer.cpp

601 lines
16 KiB

/*
* Mouspedometa
* Based on the original Xodometer VMS/Motif sources.
*
* Written by Armen Nakashian
* Compaq Computer Corporation
* Houston TX
* 22 May 1998
*
* If you make improvements or enhancements to Mouspedometa, please send
* them back to the author at any of the following addresses:
*
* armen@nakashian.com
*
* Thanks to Mark Granoff for writing the original Xodometer, and
* the whole KDE team for making such a nice environment to write
* programs in.
*
*
* This software is provided as is with no warranty of any kind,
* expressed or implied. Neither Digital Equipment Corporation nor
* Armen Nakashian will be held accountable for your use of this
* software.
*/
#include "kodometer.h"
const double speedInterval = 500.0;
const double distanceInterval = 10.0;
const int speedSamples = 10;
static struct conversionEntry ConversionTable[MAX_UNIT] = {
{inch, I18N_NOOP("inch"), I18N_NOOP("inches"), 12.0, 2.54,
I18N_NOOP("cm"), I18N_NOOP("cm"), 100.0, 3},
{foot, I18N_NOOP("foot"), I18N_NOOP("feet"), 5280.0, 0.3048,
I18N_NOOP("meter"), I18N_NOOP("meters"), 1000.0, 4},
{mile, I18N_NOOP("mile"), I18N_NOOP("miles"), -1.0, 1.609344,
I18N_NOOP("km"), I18N_NOOP("km"), -1.0, 5}};
/*
* Set the program up, do lots of ugly initialization.
* Note that we use installEventFilter on the two KImageNumber's
* to make clicks on them bring up the context-menu.
*/
Kodometer::Kodometer(TQWidget* parent, const char* name)
: TQFrame(parent, name),
dontRefresh(false),
speed(0.0),
lastDistance(0.0),
XCoord(0), YCoord(0),
lastXCoord(0), lastYCoord(0),
pointerScreen(-1),
lastPointerScreen(-1),
Enabled(true),
cyclesSinceLastSave(0),
pollInterval(10),
saveFrequency(10)
{
display = TDEApplication::kApplication()->getDisplay();
FindAllScreens();
root = RootWindow(display, DefaultScreen(display));
readSettings();
if(AutoReset) {
dontRefresh = true;
resetTrip();
dontRefresh = false;
}
lastDistance = Distance;
lastDUnit = distanceUnit;
lastTUnit = tripDistanceUnit;
totalLabel = new KImageNumber(locate("appdata", "numbers.png"), this);
tripLabel = new KImageNumber(locate("appdata", "numbers.png"), this);
totalLabel->installEventFilter(this);
tripLabel->installEventFilter(this);
// setup help menu
help = new KHelpMenu(this, TDEGlobal::instance()->aboutData(), false);
TDEPopupMenu* helpMnu = help->menu();
// Make the popup menu
menu = new TDEPopupMenu();
menu->insertTitle(kapp->miniIcon(), TDEGlobal::instance()->aboutData()->programName());
enabledID = menu->insertItem(i18n("&Enable"), this, TQ_SLOT(toggleEnabled()));
metricID = menu->insertItem(i18n("&Metric Display"), this,
TQ_SLOT(toggleUnits()));
autoResetID = menu->insertItem(i18n("Auto &Reset Trip"), this,
TQ_SLOT(toggleAutoReset()));
menu->insertItem(i18n("Reset &Trip"), this, TQ_SLOT(resetTrip()));
menu->insertItem(i18n("Reset &Odometer"), this, TQ_SLOT(resetTotal()));
menu->insertSeparator();
menu->insertItem(SmallIconSet("help"), i18n("&Help"), helpMnu);
menu->insertItem(SmallIconSet("system-log-out"), i18n("&Quit"), this, TQ_SLOT(quit()));
menu->setCheckable(true);
menu->setItemChecked(enabledID, Enabled);
menu->setItemChecked(metricID, UseMetric);
menu->setItemChecked(autoResetID, AutoReset);
//start the timers that will rifresh the counter
distanceID = startTimer((int)distanceInterval);
speedID = startTimer((int)speedInterval);
tripLabel->move(0, totalLabel->height());
setFixedSize(tripLabel->width(),
totalLabel->height() + tripLabel->height());
UseMetric =! UseMetric;
toggleUnits();
refresh();
}
/*
* Now I'm not really sure what this does. I assume its here to find
* all the displays on your system, and measure them. During the mouse
* tracking phase, we use the information stored here to determine how
* far the mouse moved on a given screen.
*
* The point is, since you might have one 17" screen and on 21" screen,
* lets measure them differently. Surely this level of accurasy is
* only provide to prove that the original author was a man's man.
*/
void Kodometer::FindAllScreens(void)
{
int Dh, DhMM, Dw, DwMM;
double vPixelsPerMM, hPixelsPerMM;
screenCount = ScreenCount(display);
// kdDebug() << "Display has " << screenCount <<
// " screen" << (screenCount == 1 ? "" : "s") << endl;
for(int i = 0; i < screenCount; i++) {
// kdDebug() << "Screen " << i << endl;
screenInfo[i].root = RootWindow(display, i);
screenInfo[i].scr = XScreenOfDisplay(display, i);
screenInfo[i].height = Dh = HeightOfScreen(screenInfo[i].scr);
DhMM = HeightMMOfScreen(screenInfo[i].scr);
screenInfo[i].width = Dw = WidthOfScreen(screenInfo[i].scr);
DwMM = WidthMMOfScreen(screenInfo[i].scr);
// kdDebug() << " Height is " << Dh << " pixels (" << DhMM <<
// "mm)" << endl;
// kdDebug() << " Width is " << Dw << " pixels (" << DwMM <<
// "mm)" << endl;
vPixelsPerMM = (double)Dh / (double)DhMM;
hPixelsPerMM = (double)Dw / (double)DwMM;
screenInfo[i].PixelsPerMM = (vPixelsPerMM + hPixelsPerMM) / 2.0;
// kdDebug() << " Vertical pixels/mm are " << vPixelsPerMM <<
// "mm" << endl;
// kdDebug() << " Horizontal pixels/mm are " << hPixelsPerMM <<
// "mm" << endl;
// kdDebug() << " Average pixels/mm are " <<
// screenInfo[i].PixelsPerMM << "mm" << endl;
}
}
/*
* Here's where we override events to the KImgNum's to display
* the context menu
*/
bool Kodometer::eventFilter( TQObject *, TQEvent *e )
{
if ( e->type() == TQEvent::MouseButtonPress ) {
mousePressEvent((TQMouseEvent*)e);
return true;
}
return false;
}
/*
* Show the context menu
*/
void Kodometer::mousePressEvent(TQMouseEvent* e)
{
//FIXME fix this!
//dontRefresh = true;
menu->popup(mapToGlobal(e->pos()));
}
/*
* Called when the timer expires to query the pointer position,
* compare it to the last known position, and then to calculate
* the distance moved.
*/
void Kodometer::timerEvent(TQTimerEvent* e)
{
if (Enabled) {
if(e->timerId() == distanceID) {
lastPointerScreen = pointerScreen;
lastXCoord = XCoord;
lastYCoord = YCoord;
XQueryPointer (display, root, &RootIDRet, &ChildIDRet, &XCoord,
&YCoord, &WinX, &WinY, &StateMask);
if (CalcDistance()) {
// kdDebug() << "Mouse moved" << endl;
if (!dontRefresh) {
refresh();
cyclesSinceLastSave++;
}
}
}
}
}
// Guess!
void Kodometer::toggleEnabled()
{
Enabled = !Enabled;
menu->setItemChecked(enabledID,Enabled);
refresh();
}
// Try again!
void Kodometer::toggleAutoReset()
{
AutoReset = !AutoReset;
menu->setItemChecked(autoResetID,AutoReset);
refresh();
}
// You're getting warm!
void Kodometer::toggleUnits()
{
UseMetric =! UseMetric;
menu->setItemChecked(metricID, UseMetric);
TQToolTip::remove(totalLabel);
TQToolTip::remove(tripLabel);
if(!UseMetric) {
TQToolTip::add(totalLabel,
i18n(ConversionTable[distanceUnit].fromUnitTagPlural));
TQToolTip::add(tripLabel,
i18n(ConversionTable[tripDistanceUnit].fromUnitTagPlural));
} else {
TQToolTip::add(totalLabel,
i18n(ConversionTable[distanceUnit].toUnitTagPlural));
TQToolTip::add(tripLabel,
i18n(ConversionTable[tripDistanceUnit].toUnitTagPlural));
}
refresh();
}
// Were you dropped on your head as a child?
void Kodometer::resetTrip()
{
TripDistance = 0.0;
tripDistanceUnit = inch;
if (!dontRefresh)
refresh();
}
// I was!
void Kodometer::resetTotal()
{
resetTrip();
Distance = 0.0;
distanceUnit = inch;
TripDistance = 0.0;
tripDistanceUnit = inch;
refresh();
}
/*
* Set the values in all the KImgNums, do metric conversions,
* and make the screen look like reality.
*/
void Kodometer::refresh(void)
{
if(distanceUnit != lastDUnit) {
lastDUnit = distanceUnit;
TQToolTip::remove(totalLabel);
if(!UseMetric)
TQToolTip::add(totalLabel,
i18n(ConversionTable[distanceUnit].fromUnitTagPlural));
else
TQToolTip::add(totalLabel,
i18n(ConversionTable[distanceUnit].toUnitTagPlural));
}
if(tripDistanceUnit != lastTUnit) {
lastTUnit = tripDistanceUnit;
TQToolTip::remove(tripLabel);
if(!UseMetric)
TQToolTip::add(tripLabel,
i18n(ConversionTable[tripDistanceUnit].fromUnitTagPlural));
else
TQToolTip::add(tripLabel,
i18n(ConversionTable[tripDistanceUnit].toUnitTagPlural));
}
//now draw everything
TQString distance_s;
TQString trip_s;
double distance_d = 0;
double trip_d = 0;
if (Enabled) {
distance_d = Distance;
distance_s = FormatDistance(distance_d, distanceUnit);
trip_d = TripDistance;
trip_s = FormatDistance(trip_d, tripDistanceUnit);
} else {
distance_s = "------";
trip_s = "------";
}
totalLabel->setValue(distance_d);
tripLabel->setValue(trip_d);
}
/*
* Not sure what this does, its from the original program.
*/
double Kodometer::multiplier(Units unit)
{
double m = 10;
switch (unit) {
case mile : m *= 10.0;
case foot : m *= 10.0;
case inch : m *= 10.0; break;
}
return m;
}
/*
* This is the bitch function where the _real_ work is done. I
* could have re-invented the query_pointer code, but this one is a best.
*
* This is code from the original program, responsible for converting the
* number of pixels traveled into a real-world coordinates.
*/
int Kodometer::CalcDistance(void)
{
double dist, sum;
int X, Y;
double distMM, distInches, finalNewDist;
double oldDistance, oldTripDistance;
double newDistance, newTripDistance;
int i, j, finalScreen, increment;
Units oldDistanceUnit, oldTripDistanceUnit, currentUnit;
int distanceChanged, tripDistanceChanged;
int screenOrientation = K_Left;
i = j = finalScreen = increment = 0;
if ((lastXCoord == 0) && (lastYCoord == 0))
return false;
if ((lastXCoord == XCoord) && (lastYCoord == YCoord))
return false;
//Figure out which screen the pointer is on
if (screenCount > 1) {
while (i < screenCount)
if (RootIDRet == screenInfo[i].root)
break;
else
i++;
}
pointerScreen = i;
// kdDebug() << "CalcDistance: screen: " << pointerScreen <<
// " x: " << XCoord << " y: " << YCoord << endl;
// Adjust XCoord or YCoord for the screen its on, relative to screen 0
// and screenOrientation.
if (lastPointerScreen != -1 && pointerScreen != lastPointerScreen) {
switch (screenOrientation) {
case K_Left:
case K_Top:
finalScreen = 0;
j = TQMAX(pointerScreen,lastPointerScreen) - 1;
increment = -1;
break;
case K_Right:
case K_Bottom:
finalScreen = TQMAX(pointerScreen,lastPointerScreen) - 1;
j = 0;
increment = 1;
break;
}
do {
switch (screenOrientation) {
case K_Left:
case K_Right:
if (pointerScreen > lastPointerScreen)
XCoord += screenInfo[j].width;
else
lastXCoord += screenInfo[j].width;
break;
case K_Top:
case K_Bottom:
if (pointerScreen > lastPointerScreen)
YCoord += screenInfo[j].height;
else
lastYCoord += screenInfo[j].height;
break;
}
if (j != finalScreen)
j += increment;
} while (j != finalScreen);
// kdDebug() << " Adjusted for screen ch: x: " << XCoord <<
// " y: " << YCoord << endl;
}
// kdDebug() << "In: Distance: " << Distance <<
// " Trip Distance: " << TripDistance << endl;
// Calculate distance in pixels first
// using Pitagora
X = XCoord - lastXCoord;
X = X*X;
Y = YCoord - lastYCoord;
Y = Y*Y;
sum = (double)X + (double)Y;
dist = sqrt(sum);
// Convert to millimeters
distMM = dist / screenInfo[pointerScreen].PixelsPerMM;
// Convert to inches
distInches = distMM * 0.04;
// Add an appropriate value to Distance, which may be
// in a unit other than inches
currentUnit = inch;
finalNewDist = distInches;
while (currentUnit < distanceUnit) {
finalNewDist =
finalNewDist / ConversionTable[currentUnit].maxFromBeforeNext;
currentUnit++;
// kdDebug() << " New dist: " << dist << "p, " << distMM << "mm, " <<
// distInches << "in, " << finalNewDist << " " <<
// ConversionTable[currentUnit+1].fromUnitTagPlural << endl;
}
// kdDebug() << " Next part" << endl;
oldDistance = Distance * multiplier(distanceUnit);
Distance += finalNewDist;
oldDistanceUnit = distanceUnit;
if (ConversionTable[distanceUnit].maxFromBeforeNext != -1.0 &&
Distance >= ConversionTable[distanceUnit].maxFromBeforeNext)
{
Distance = Distance / ConversionTable[distanceUnit].maxFromBeforeNext;
distanceUnit++;
}
newDistance = Distance * multiplier(distanceUnit);
distanceChanged = (distanceUnit != oldDistanceUnit ||
(unsigned int)oldDistance != (unsigned int)newDistance);
// Add an appropriate value to TripDistance, which may be
// in a unit other than inches
currentUnit = inch;
finalNewDist = distInches;
while (currentUnit < tripDistanceUnit) {
finalNewDist = finalNewDist /
ConversionTable[currentUnit].maxFromBeforeNext;
currentUnit++;
}
oldTripDistance = TripDistance * multiplier(tripDistanceUnit);
TripDistance += finalNewDist;
oldTripDistanceUnit = tripDistanceUnit;
if (ConversionTable[tripDistanceUnit].maxFromBeforeNext != -1.0 &&
TripDistance >= ConversionTable[tripDistanceUnit].maxFromBeforeNext)
{
TripDistance = TripDistance /
ConversionTable[tripDistanceUnit].maxFromBeforeNext;
tripDistanceUnit++;
}
newTripDistance = TripDistance * multiplier(tripDistanceUnit);
tripDistanceChanged = ((tripDistanceUnit != oldTripDistanceUnit) ||
((unsigned int)oldTripDistance != (unsigned int)newTripDistance));
// kdDebug() << "Out: Distance: " << Distance <<
// "Trip Distance: " << TripDistance << endl;
if ((distanceChanged) || (tripDistanceChanged))
return true;
else
return false;
}
/*
* This code can probably go away. Its doing conversions from inches to
* other units. Its ugly C-style stuff, that should't be done in a
* pretty OO world.
*/
#define THERE_IS_A_NEXT (ConversionTable[unit].maxToBeforeNext != -1.0)
TQString Kodometer::FormatDistance(double &dist, Units unit)
{
TQString string;
const char *tag;
int precision;
if (UseMetric) {
dist = dist * ConversionTable[unit].conversionFactor;
if ((THERE_IS_A_NEXT) &&
(dist > ConversionTable[unit].maxToBeforeNext))
{
dist = dist / ConversionTable[unit].maxToBeforeNext;
unit++;
}
if (dist == 1.0)
tag = ConversionTable[unit].toUnitTag;
else
tag = ConversionTable[unit].toUnitTagPlural;
} else {
if (dist == 1.0)
tag = ConversionTable[unit].fromUnitTag;
else
tag = ConversionTable[unit].fromUnitTagPlural;
}
precision = ConversionTable[unit].printPrecision;
string.sprintf ("%.*f %s", precision, dist, tag);
return string;
}
/*
* Use TDEConfig to read all settings from disk. Note that whatever
* happens here overrides the defaults, but there's not much
* sanity-checking.
*/
void Kodometer::readSettings(void)
{
TDEConfig* config = TDEGlobal::config();
config->setGroup("Settings");
UseMetric = config->readNumEntry("UseMetric", false);
AutoReset = config->readNumEntry("AutoReset", true);
TripDistance = config->readDoubleNumEntry("Trip", 0.0);
Distance = config->readDoubleNumEntry("Distance", 0.0);
distanceUnit = config->readNumEntry("DistanceUnit", inch);
tripDistanceUnit = config->readNumEntry("TripUnit", inch);
}
/*
* Save reality for use in the next session.
*/
void Kodometer::saveSettings(void)
{
TDEConfig* config = TDEGlobal::config();
config->setGroup("Settings");
config->writeEntry("UseMetric", UseMetric);
config->writeEntry("AutoReset", AutoReset);
config->writeEntry("Trip", TripDistance);
config->writeEntry("Distance", Distance);
config->writeEntry("TripUnit", tripDistanceUnit);
config->writeEntry("DistanceUnit", distanceUnit);
config->sync();
}
// What in the world can this do?
void Kodometer::quit()
{
saveSettings();
kapp->quit();
}
#include "kodometer.moc"