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.
601 lines
16 KiB
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 = KApplication::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, KGlobal::instance()->aboutData(), false);
|
|
KPopupMenu* helpMnu = help->menu();
|
|
|
|
// Make the popup menu
|
|
menu = new KPopupMenu();
|
|
|
|
menu->insertTitle(kapp->miniIcon(), KGlobal::instance()->aboutData()->programName());
|
|
|
|
enabledID = menu->insertItem(i18n("&Enable"), this, TQT_SLOT(toggleEnabled()));
|
|
metricID = menu->insertItem(i18n("&Metric Display"), this,
|
|
TQT_SLOT(toggleUnits()));
|
|
autoResetID = menu->insertItem(i18n("Auto &Reset Trip"), this,
|
|
TQT_SLOT(toggleAutoReset()));
|
|
menu->insertItem(i18n("Reset &Trip"), this, TQT_SLOT(resetTrip()));
|
|
menu->insertItem(i18n("Reset &Odometer"), this, TQT_SLOT(resetTotal()));
|
|
menu->insertSeparator();
|
|
|
|
menu->insertItem(SmallIconSet("help"), i18n("&Help"), helpMnu);
|
|
|
|
menu->insertItem(SmallIconSet("exit"), i18n("&Quit"), this, TQT_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() << " Qt::Vertical pixels/mm are " << vPixelsPerMM <<
|
|
// "mm" << endl;
|
|
// kdDebug() << " Qt::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 KConfig 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)
|
|
{
|
|
KConfig* config = KGlobal::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)
|
|
{
|
|
KConfig* config = KGlobal::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"
|