00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "sun.h"
00024 #include "zonemap.h"
00025
00026
00027 #include <opie2/odebug.h>
00028 #include <opie2/oresource.h>
00029
00030 #include <qpe/qpeapplication.h>
00031
00032
00033 #include <qfile.h>
00034 #include <qlabel.h>
00035 #include <qmessagebox.h>
00036 #include <qtextstream.h>
00037 #include <qtimer.h>
00038 #include <qtoolbutton.h>
00039 #include <qlayout.h>
00040 #include <qhbox.h>
00041 #include <qlistview.h>
00042 #include <qwhatsthis.h>
00043
00044
00045 #include <limits.h>
00046
00047
00048 static const char strZONEINFO[] = "/usr/share/zoneinfo/zone.tab";
00049 static const char strMAP[] = "citytime/simple_grid_400";
00050
00051
00052
00053 static const int iTHRESHOLD = 50000;
00054
00055
00056 static const int iLABELOFFSET = 8;
00057
00058
00059 static const int iCITYSIZE = 3;
00060 const int iCITYOFFSET = 2;
00061
00062
00063 static inline void darken( QImage *pImage, int start, int stop, int row );
00064 static void dayNight( QImage *pImage );
00065
00066 ZoneField::ZoneField( const QString& strLine )
00067 {
00068
00069 QRegExp regCoord( "[-+][0-9]+" );
00070 QRegExp regCountry( "[A-Za-z]+/" );
00071 QRegExp regCity( "[A-Za-z_-]*" );
00072
00073 int iStart,
00074 iStop,
00075 iLen,
00076 tmp;
00077 QString strTmp;
00078
00079
00080 strCountryCode = strLine.left( 2 );
00081 iStart = regCoord.match( strLine, 0, &iLen );
00082 if ( iStart >= 0 ) {
00083 strTmp = strLine.mid( iStart, iLen );
00084 tmp = strTmp.toInt();
00085
00086
00087
00088 if ( iLen < 7 ) {
00089 _y = tmp / 100;
00090 _y *= 60;
00091 _y += tmp % 100;
00092 _y *= 60;
00093 } else {
00094 _y = tmp / 10000;
00095 _y *= 60;
00096 tmp %= 10000;
00097 _y += tmp / 100;
00098 _y *= 60;
00099 tmp %= 100;
00100 _y += tmp;
00101 }
00102 }
00103 iStart = regCoord.match( strLine, iStart + iLen, &iLen );
00104 if ( iStart >= 0 ) {
00105 strTmp = strLine.mid( iStart, iLen );
00106 tmp = strTmp.toInt();
00107 if ( iLen < 8 ) {
00108 _x = tmp / 100;
00109 _x *= 60;
00110 _x += tmp % 100;
00111 _x *= 60;
00112 } else {
00113 _x = tmp / 10000;
00114 _x *= 60;
00115 tmp %= 10000;
00116 _x += tmp / 100;
00117 _x *= 60;
00118 tmp %= 100;
00119 _x += tmp;
00120 }
00121 }
00122 iStart = regCountry.match( strLine, 0, &iLen );
00123
00124 iStop = strLine.findRev( '/' );
00125 if ( iStart >= 0 ) {
00126 iLen = (iStop - iStart) + 1;
00127 strCountry = strLine.mid( iStart, iLen );
00128 }
00129
00130 iStart = regCity.match( strLine, iStart + iLen, &iLen );
00131 if ( iStart >= 0 ) {
00132 strCity = strLine.mid( iStart, iLen );
00133 }
00134 }
00135
00136 void ZoneField::showStructure( void ) const
00137 {
00138 odebug << "Country: " << strCountry << "" << oendl;
00139 odebug << "City: " << strCity << "" << oendl;
00140 odebug << "x: " << _x << "" << oendl;
00141 odebug << "y: " << _y << "\n" << oendl;
00142 }
00143
00144 ZoneMap::ZoneMap( QWidget *parent, const char* name )
00145 : QScrollView( parent, name ),
00146 pLast( 0 ),
00147 pRepaint( 0 ),
00148 ox( 0 ),
00149 oy( 0 ),
00150 drawableW( -1 ),
00151 drawableH( -1 ),
00152 bZoom( FALSE ),
00153 bIllum( TRUE ),
00154 cursor( 0 )
00155 {
00156 viewport()->setFocusPolicy( StrongFocus );
00157
00158
00159 zones.setAutoDelete( true );
00160
00161
00162 pixCurr = new QPixmap();
00163
00164 QPixmap pixZoom = Opie::Core::OResource::loadPixmap( "mag", Opie::Core::OResource::SmallIcon );
00165
00166 cmdZoom = new QToolButton( this, "Zoom command" );
00167 cmdZoom->setUsesBigPixmap( qApp->desktop()->size().width() > 330 );
00168 cmdZoom->setPixmap( pixZoom );
00169 cmdZoom->setToggleButton( true );
00170
00171 cmdZoom->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)0,
00172 (QSizePolicy::SizeType)0,
00173 cmdZoom->sizePolicy().hasHeightForWidth() ) );
00174 cmdZoom->setMaximumSize( cmdZoom->sizeHint() );
00175
00176 cmdZoom->move( width() - cmdZoom->width(), height() - cmdZoom->height() );
00177
00178
00179 lblCity = new QLabel( tr( "CITY" ), this, "City Label" );
00180 lblCity->setMinimumSize( lblCity->sizeHint() );
00181 lblCity->setFrameStyle( QFrame::Plain | QFrame::Box );
00182 lblCity->setBackgroundColor( yellow );
00183 lblCity->hide();
00184
00185
00186 tHide = new QTimer( this, "Label Timer" );
00187 QObject::connect( tHide, SIGNAL( timeout() ),
00188 lblCity, SLOT( hide() ) );
00189 QObject::connect( tHide, SIGNAL( timeout() ),
00190 this, SLOT( slotRedraw() ) );
00191 QTimer *tUpdate = new QTimer( this, "Update Timer" );
00192 QObject::connect( tUpdate, SIGNAL( timeout() ),
00193 this, SLOT( slotUpdate() ) );
00194 QObject::connect( qApp, SIGNAL( timeChanged() ),
00195 this, SLOT( slotUpdate() ) );
00196 QObject::connect( cmdZoom, SIGNAL( toggled(bool) ),
00197 this, SLOT( slotZoom(bool) ) );
00198 QObject::connect( &norm, SIGNAL( signalNewPoint(const QPoint&) ),
00199 this, SLOT( slotFindCity(const QPoint&) ) );
00200 QObject::connect( qApp, SIGNAL( clockChanged(bool) ),
00201 this, SLOT( changeClock(bool) ) );
00202
00203 tUpdate->start( 5 * 60 * 1000 );
00204
00205 readZones();
00206 }
00207
00208 ZoneMap::~ZoneMap()
00209 {
00210 }
00211
00212 void ZoneMap::readZones( void )
00213 {
00214 QFile fZone( strZONEINFO );
00215 if ( !fZone.open( IO_ReadOnly ) ) {
00216 QMessageBox::warning (this,
00217 tr( "Unable to Find Timezone Info" ),
00218 tr( "<p>Unable to find any timezone information in %1" )
00219 .arg( strZONEINFO ));
00220 exit(-1);
00221 } else {
00222 QTextStream tZone( &fZone );
00223 while ( !tZone.atEnd() ) {
00224 QString strLine = tZone.readLine();
00225
00226 if ( strLine[0] != '#' ) {
00227 zones.append( new ZoneField( strLine ) );
00228 }
00229 }
00230 fZone.close();
00231 }
00232 }
00233
00234 void ZoneMap::viewportMousePressEvent( QMouseEvent* event )
00235 {
00236
00237
00238 slotRedraw();
00239 norm.start();
00240 norm.addEvent( event->pos() );
00241 }
00242
00243 void ZoneMap::viewportMouseMoveEvent( QMouseEvent* event )
00244 {
00245 norm.addEvent( event->pos() );
00246 }
00247
00248 void ZoneMap::viewportMouseReleaseEvent( QMouseEvent* )
00249 {
00250
00251
00252 norm.stop();
00253 if ( pLast != NULL ) {
00254 emit signalTz( pLast->country(), pLast->city() );
00255 pLast = NULL;
00256 }
00257 tHide->start( 2000, true );
00258 }
00259
00260 void ZoneMap::keyPressEvent( QKeyEvent *ke )
00261 {
00262 switch ( ke->key() ) {
00263 case Key_Left:
00264 case Key_Right:
00265 case Key_Up:
00266 case Key_Down: {
00267 tHide->stop();
00268 if ( !cursor )
00269 slotFindCity( QPoint( contentsWidth(), contentsHeight() ) / 2 );
00270 ZoneField *city = findCityNear( cursor, ke->key() );
00271 if ( city ) {
00272 cursor = city;
00273 int tmpx, tmpy;
00274 zoneToWin( cursor->x(), cursor->y(), tmpx, tmpy );
00275 ensureVisible( tmpx, tmpy );
00276 showCity( cursor );
00277 tHide->start( 3000, true );
00278 }
00279 }
00280 break;
00281
00282 case Key_Space:
00283 case Key_Enter:
00284 case Key_Return:
00285 if ( cursor ) {
00286 emit signalTz( cursor->country(), cursor->city() );
00287 tHide->start( 0, true );
00288 }
00289 break;
00290 }
00291 }
00292
00293 ZoneField *ZoneMap::findCityNear( ZoneField *city, int key )
00294 {
00295 ZoneField *pZone;
00296 ZoneField *pClosest = 0;
00297 long ddist = LONG_MAX;
00298
00299 QListIterator<ZoneField> it( zones );
00300 for (; it.current(); ++it) {
00301 pZone = it.current();
00302 long dx = (pZone->x() - city->x())/100;
00303 long dy = (pZone->y() - city->y())/100;
00304 switch ( key ) {
00305 case Key_Right:
00306 case Key_Left:
00307 if ( key == Key_Left )
00308 dx = -dx;
00309 if ( dx > 0 ) {
00310 long dist = QABS(dy)*4 + dx;
00311 if ( dist < ddist ) {
00312 ddist = dist;
00313 pClosest = pZone;
00314 }
00315 }
00316 break;
00317 case Key_Down:
00318 case Key_Up:
00319 if ( key == Key_Down )
00320 dy = -dy;
00321 if ( dy > 0 ) {
00322 long dist = QABS(dx)*4 + dy;
00323 if ( dist < ddist ) {
00324 ddist = dist;
00325 pClosest = pZone;
00326 }
00327 }
00328 break;
00329 }
00330 }
00331
00332 return pClosest;
00333 }
00334
00335 void ZoneMap::slotFindCity( const QPoint &pos )
00336 {
00337 lblCity->hide();
00338
00339
00340 int tmpx, tmpy, x, y;
00341 long lDistance,
00342 lClosest;
00343 ZoneField *pZone,
00344 *pClosest;
00345
00346 if ( tHide->isActive() ) {
00347 tHide->stop();
00348 }
00349 viewportToContents(pos.x(), pos.y(), tmpx, tmpy);
00350 winToZone( tmpx, tmpy, x, y );
00351
00352
00353
00354
00355
00356 QListIterator<ZoneField> it( zones );
00357 pClosest = 0;
00358 lClosest = LONG_MAX;
00359 for (; it.current(); ++it) {
00360 pZone = it.current();
00361
00362 lDistance = QABS( x - pZone->x() ) + QABS( y - pZone->y() );
00363
00364 if ( lDistance < lClosest ) {
00365 lClosest = lDistance;
00366 pClosest = pZone;
00367 }
00368 }
00369
00370
00371 if ( lClosest <= iTHRESHOLD ) {
00372 showCity( pClosest );
00373 cursor = pClosest;
00374 }
00375 }
00376
00377 void ZoneMap::showCity( ZoneField *city )
00378 {
00379 pLast = city;
00380
00381
00382 QString strCity = pLast->city();
00383 QString strCountry = pLast->country();
00384
00385
00386
00387 QString strSave;
00388 char *p = getenv( "TZ" );
00389 if ( p ) {
00390 strSave = p;
00391 }
00392
00393 setenv( "TZ", strCountry + strCity, true );
00394 lblCity->setText( strCity.replace( QRegExp("_"), " ") + "\n" +
00395 TimeString::shortTime( ampm ) );
00396 lblCity->setMinimumSize( lblCity->sizeHint() );
00397
00398 unsetenv( "TZ" );
00399 if ( p )
00400 setenv( "TZ", strSave, true );
00401
00402 int tmpx, tmpy, x, y;
00403 zoneToWin( pLast->x(), pLast->y(), tmpx, tmpy );
00404 contentsToViewport(tmpx, tmpy, x, y);
00405 if ( lblCity->width() > drawableW - x ) {
00406
00407 x = x - lblCity->width() - iLABELOFFSET;
00408 } else {
00409
00410 x += iLABELOFFSET;
00411 }
00412 if ( lblCity->height() > drawableH - y ) {
00413
00414 y = y - lblCity->height() - iLABELOFFSET;
00415 } else if ( y < 0 ) {
00416
00417
00418
00419 y = iLABELOFFSET;
00420 } else {
00421
00422 y += iLABELOFFSET;
00423 }
00424
00425
00426 if ( pRepaint ) {
00427 int repx,
00428 repy;
00429 zoneToWin( pRepaint->x(), pRepaint->y(), repx, repy );
00430 updateContents( repx - iCITYOFFSET, repy - iCITYOFFSET,
00431 iCITYSIZE, iCITYSIZE );
00432 }
00433 updateContents( tmpx - iCITYOFFSET, tmpy - iCITYOFFSET, iCITYSIZE,
00434 iCITYSIZE );
00435 pRepaint = pLast;
00436
00437 lblCity->move( x, y );
00438 lblCity->show();
00439 }
00440
00441 void ZoneMap::resizeEvent( QResizeEvent *e )
00442 {
00443
00444 QSize _size = e->size();
00445 cmdZoom->move( _size.width() - cmdZoom->width(),
00446 _size.height() - cmdZoom->height() );
00447 if ( !bZoom ) {
00448 drawableW = width() - 2 * frameWidth();
00449 drawableH = height() - 2 * frameWidth();
00450 makeMap( drawableW, drawableH );
00451 resizeContents( drawableW, drawableH );
00452 }
00453 }
00454
00455 void ZoneMap::showZones( void ) const
00456 {
00457
00458 QListIterator<ZoneField> itZone( zones );
00459 for ( itZone.toFirst(); itZone.current(); ++itZone ) {
00460 ZoneField *pZone = itZone.current();
00461 pZone->showStructure();
00462 }
00463 }
00464
00465
00466 QWidget* ZoneMap::selectionWidget( QWidget *parent) {
00467
00468 QWidget *returnWidget = new QWidget( parent );
00469
00470 QVBoxLayout *layout = new QVBoxLayout( returnWidget );
00471 QHBox *hBox = new QHBox( returnWidget );
00472 QListView *continentView = new QListView( hBox );
00473 continentView->addColumn( tr("Continent") );
00474 QWhatsThis::add( continentView, tr("Select a continent/country here, then select a city") );
00475 connect ( continentView, SIGNAL( clicked(QListViewItem*) ), this, SLOT( slotGetCities(QListViewItem*) ) );
00476
00477 QStringList continentList;
00478 QListIterator<ZoneField> itZone( zones );
00479 for ( itZone.toFirst(); itZone.current(); ++itZone ) {
00480 ZoneField *pZone = itZone.current();
00481 if ( continentList.contains( pZone->country() ) == 0 ) {
00482 QString name;
00483 QListViewItem *item;
00484 if ( !(pZone->country().length() > 24) ) {
00485 name = pZone->country().left(pZone->country().length()-1 );
00486 } else {
00487 name = pZone->country().left( 24 );
00488 }
00489 item = new QListViewItem( continentView, name, pZone->country() );
00490 continentList.append( pZone->country() );
00491 }
00492 }
00493
00494 cityView = new QListView( hBox );
00495 cityView->addColumn( tr("City") );
00496
00497 layout->addWidget( hBox );
00498 return returnWidget;
00499 }
00500
00501 void ZoneMap::slotGetCities( QListViewItem * contItem) {
00502
00503 cityView->clear();
00504 selectedCont = contItem->text( 1 );
00505 QListIterator<ZoneField> itZone( zones );
00506 for ( itZone.toFirst(); itZone.current(); ++itZone ) {
00507 ZoneField *pZone = itZone.current();
00508 if ( pZone->country() == contItem->text( 1 ) ) {
00509 QListViewItem *item;
00510 item = new QListViewItem( cityView, pZone->city() );
00511 connect ( cityView, SIGNAL( clicked(QListViewItem*) ), this, SLOT( slotCitySelected(QListViewItem*) ) );
00512 }
00513 }
00514 }
00515
00516 void ZoneMap::slotCitySelected( QListViewItem *cityItem ) {
00517 if ( cityItem ) {
00518 emit signalTz( selectedCont, cityItem->text( 0 ) );
00519 }
00520 }
00521
00522 void ZoneMap::drawCities( QPainter *p )
00523 {
00524 int x, y, j;
00525
00526
00527
00528 p->setPen( red );
00529 QListIterator<ZoneField> itZone( zones );
00530 for ( itZone.toFirst(), j = 0; itZone.current(); ++itZone, j++ ) {
00531 ZoneField *pZone = itZone.current();
00532 zoneToWin( pZone->x(), pZone->y(), x, y );
00533 if ( x > wImg )
00534 x = x - wImg;
00535 p->drawRect( x - iCITYOFFSET, y - iCITYOFFSET, iCITYSIZE, iCITYSIZE);
00536 }
00537 }
00538
00539 static void dayNight(QImage *pImage)
00540 {
00541
00542 double dJulian,
00543 dSunRad,
00544 dSunDecl,
00545 dSunRadius,
00546 dSunLong;
00547 int wImage = pImage->width(),
00548 hImage = pImage->height(),
00549 iStart,
00550 iStop,
00551 iMid,
00552 relw,
00553 i;
00554 short wtab[ wImage ];
00555 time_t tCurrent;
00556 struct tm *pTm;
00557
00558
00559 tCurrent = time( NULL );
00560 pTm = gmtime( &tCurrent );
00561 dJulian = jtime( pTm );
00562 sunpos( dJulian, 0, &dSunRad, &dSunDecl, &dSunRadius, &dSunLong );
00563
00564
00565 projillum( wtab, wImage, hImage, dSunDecl );
00566 relw = wImage - int( wImage * 0.0275 );
00567
00568
00569 iMid = ( relw * ( 24*60 - pTm->tm_hour * 60 - pTm->tm_min ) ) / ( 24*60 );
00570
00571 for ( i = 0; i < hImage; i++ ) {
00572 if ( wtab[i] > 0 ) {
00573 iStart = iMid - wtab[i];
00574 iStop = iMid + wtab[i];
00575 if ( iStart < 0 ) {
00576 darken( pImage, iStop, wImage + iStart, i );
00577 } else if ( iStop > wImage ) {
00578 darken( pImage, iStop - wImage, iStart, i );
00579 } else {
00580 darken( pImage, 0, iStart, i );
00581 darken( pImage, iStop, wImage, i );
00582 }
00583 } else {
00584 darken( pImage, 0, wImage, i );
00585 }
00586 }
00587 }
00588
00589 static inline void darken( QImage *pImage, int start, int stop, int row )
00590 {
00591 int colors,
00592 j;
00593 uchar *p;
00594
00595
00596 colors = pImage->numColors() / 2;
00597
00598 p = pImage->scanLine( row );
00599 for ( j = start; j <= stop; j++ ) {
00600 if ( p[j] < colors )
00601 p[j] += colors;
00602 }
00603 }
00604
00605 void ZoneMap::makeMap( int w, int h )
00606 {
00607 QImage imgOrig = Opie::Core::OResource::loadImage( strMAP );
00608 if ( imgOrig.isNull() ) {
00609 QMessageBox::warning( this,
00610 tr( "Couldn't Find Map" ),
00611 tr( "<p>Couldn't load map: %1, exiting")
00612 .arg( strMAP ) );
00613 exit(-1);
00614 }
00615
00616
00617 imgOrig = imgOrig.convertDepth( 8 );
00618 int numColors = imgOrig.numColors();
00619
00620 imgOrig.setNumColors( 2 * numColors );
00621
00622 for ( int i = 0; i < numColors; i++ ) {
00623 QRgb rgb = imgOrig.color( i );
00624 imgOrig.setColor ( i + numColors, qRgb( 2 * qRed( rgb ) / 3,
00625 2 * qGreen( rgb ) / 3, 2 * qBlue( rgb ) / 3 ) );
00626 }
00627
00628
00629 if ( bIllum ) {
00630
00631 dayNight(&imgOrig);
00632 }
00633
00634 wImg = w;
00635 hImg = h;
00636 ox = ( wImg / 2 ) - int( wImg * 0.0275 );
00637 oy = hImg / 2;
00638 pixCurr->convertFromImage( imgOrig.smoothScale(w, h),
00639 QPixmap::ThresholdDither );
00640 }
00641
00642 void ZoneMap::drawCity( QPainter *p, const ZoneField *pCity )
00643 {
00644 int x,
00645 y;
00646
00647 p->setPen( red );
00648 zoneToWin( pCity->x(), pCity->y(), x, y );
00649 p->drawRect( x - iCITYOFFSET, y - iCITYOFFSET, iCITYSIZE, iCITYSIZE );
00650 }
00651
00652 void ZoneMap::drawContents( QPainter *p, int cx, int cy, int cw, int ch )
00653 {
00654
00655
00656 drawableW = width() - 2 * frameWidth();
00657 drawableH = height() - 2 * frameWidth();
00658
00659 int pixmapW = pixCurr->width(),
00660 pixmapH = pixCurr->height();
00661 if ( !bZoom && ( ( pixmapW != drawableW ) ||
00662 ( pixmapH != drawableH) ) ) {
00663 makeMap( drawableW, drawableH );
00664 }
00665
00666
00667 int rowheight = pixCurr->height();
00668 int toprow = cy / rowheight;
00669 int bottomrow = ( cy + ch + rowheight - 1 ) / rowheight;
00670 int colwidth = pixCurr->width();
00671 int leftcol= cx / colwidth;
00672 int rightcol= ( cx + cw + colwidth - 1 ) / colwidth;
00673 for ( int r = toprow; r <= bottomrow; r++ ) {
00674 int py = r * rowheight;
00675 for ( int c = leftcol; c <= rightcol; c++ ) {
00676 int px = c * colwidth;
00677 p->drawPixmap( px, py, *pixCurr );
00678 }
00679 }
00680
00681
00682 if ( pLast )
00683 drawCity( p, pLast );
00684 }
00685
00686 void ZoneMap::slotZoom( bool setZoom )
00687 {
00688 bZoom = setZoom;
00689 if ( bZoom ) {
00690 makeMap( 2 * wImg , 2 * hImg );
00691 resizeContents( wImg, hImg );
00692 } else {
00693 makeMap( drawableW, drawableH );
00694 resizeContents( drawableW, drawableH );
00695 }
00696 }
00697
00698 void ZoneMap::slotIllum( bool setIllum )
00699 {
00700 bIllum = !setIllum;
00701
00702 makeMap( pixCurr->width(), pixCurr->height() );
00703 updateContents( 0, 0, wImg, hImg );
00704 }
00705
00706 void ZoneMap::slotUpdate( void )
00707 {
00708
00709
00710 makeMap ( pixCurr->width(), pixCurr->height() );
00711 updateContents( contentsX(), contentsY(), drawableW, drawableH );
00712 }
00713
00714 void ZoneMap::slotRedraw( void )
00715 {
00716
00717 int x,
00718 y;
00719 if ( pRepaint ) {
00720 pLast = 0;
00721 zoneToWin(pRepaint->x(), pRepaint->y(), x, y);
00722 updateContents( x - iCITYOFFSET, y - iCITYOFFSET, iCITYSIZE, iCITYSIZE);
00723 pRepaint = 0;
00724 }
00725 }
00726
00727 void ZoneMap::changeClock( bool whichClock )
00728 {
00729 ampm = whichClock;
00730 }