00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <kabc/geo.h>
00025 #include <kaccelmanager.h>
00026 #include <kcombobox.h>
00027 #include <kdebug.h>
00028 #include <kiconloader.h>
00029 #include <klocale.h>
00030 #include <knuminput.h>
00031 #include <kstandarddirs.h>
00032
00033 #include <qcheckbox.h>
00034 #include <qfile.h>
00035 #include <qgroupbox.h>
00036 #include <qlabel.h>
00037 #include <qlayout.h>
00038 #include <qlistbox.h>
00039 #include <qpainter.h>
00040 #include <qpixmap.h>
00041 #include <qpushbutton.h>
00042 #include <qregexp.h>
00043 #include <qstring.h>
00044
00045 #include "geowidget.h"
00046
00047 GeoWidget::GeoWidget( QWidget *parent, const char *name )
00048 : QWidget( parent, name ), mReadOnly( false )
00049 {
00050 QLabel *label = 0;
00051
00052 QGridLayout *topLayout = new QGridLayout( this, 4, 3 );
00053 topLayout->setMargin( KDialog::marginHint() );
00054 topLayout->setSpacing( KDialog::spacingHint() );
00055
00056 label = new QLabel( this );
00057 label->setPixmap( KGlobal::iconLoader()->loadIcon( "package_network",
00058 KIcon::Desktop, KIcon::SizeMedium ) );
00059 label->setAlignment( Qt::AlignTop );
00060 topLayout->addMultiCellWidget( label, 0, 3, 0, 0 );
00061
00062 mGeoIsValid = new QCheckBox( i18n( "Use geo data" ), this );
00063 topLayout->addMultiCellWidget( mGeoIsValid, 0, 0, 1, 2 );
00064
00065 label = new QLabel( i18n( "Latitude:" ), this );
00066 topLayout->addWidget( label, 1, 1 );
00067
00068 mLatitudeBox = new KDoubleSpinBox( -90, 90, 1, 0, 6, this );
00069 mLatitudeBox->setEnabled( false );
00070 mLatitudeBox->setSuffix( "°" );
00071 topLayout->addWidget( mLatitudeBox, 1, 2 );
00072 label->setBuddy( mLatitudeBox );
00073
00074 label = new QLabel( i18n( "Longitude:" ), this );
00075 topLayout->addWidget( label, 2, 1 );
00076
00077 mLongitudeBox = new KDoubleSpinBox( -180, 180, 1, 0, 6, this );
00078 mLongitudeBox->setEnabled( false );
00079 mLongitudeBox->setSuffix( "°" );
00080 topLayout->addWidget( mLongitudeBox, 2, 2 );
00081 label->setBuddy( mLongitudeBox );
00082
00083 mExtendedButton = new QPushButton( i18n( "Edit Geo Data..." ), this );
00084 mExtendedButton->setEnabled( false );
00085 topLayout->addMultiCellWidget( mExtendedButton, 3, 3, 1, 2 );
00086
00087 connect( mLatitudeBox, SIGNAL( valueChanged( double ) ),
00088 SIGNAL( changed() ) );
00089 connect( mLongitudeBox, SIGNAL( valueChanged( double ) ),
00090 SIGNAL( changed() ) );
00091 connect( mExtendedButton, SIGNAL( clicked() ),
00092 SLOT( editGeoData() ) );
00093
00094 connect( mGeoIsValid, SIGNAL( toggled( bool ) ),
00095 mLatitudeBox, SLOT( setEnabled( bool ) ) );
00096 connect( mGeoIsValid, SIGNAL( toggled( bool ) ),
00097 mLongitudeBox, SLOT( setEnabled( bool ) ) );
00098 connect( mGeoIsValid, SIGNAL( toggled( bool ) ),
00099 mExtendedButton, SLOT( setEnabled( bool ) ) );
00100 connect( mGeoIsValid, SIGNAL( toggled( bool ) ),
00101 SIGNAL( changed() ) );
00102 }
00103
00104 GeoWidget::~GeoWidget()
00105 {
00106 }
00107
00108 void GeoWidget::setReadOnly( bool readOnly )
00109 {
00110 mReadOnly = readOnly;
00111
00112 mGeoIsValid->setEnabled( !mReadOnly );
00113 }
00114
00115 void GeoWidget::setGeo( const KABC::Geo &geo )
00116 {
00117 if ( geo.isValid() ) {
00118 if ( !mReadOnly )
00119 mGeoIsValid->setChecked( true );
00120 mLatitudeBox->setValue( geo.latitude() );
00121 mLongitudeBox->setValue( geo.longitude() );
00122 } else
00123 mGeoIsValid->setChecked( false );
00124 }
00125
00126 KABC::Geo GeoWidget::geo() const
00127 {
00128 KABC::Geo geo;
00129
00130 if ( mGeoIsValid->isChecked() ) {
00131 geo.setLatitude( mLatitudeBox->value() );
00132 geo.setLongitude( mLongitudeBox->value() );
00133 } else {
00134 geo.setLatitude( 91 );
00135 geo.setLongitude( 181 );
00136 }
00137
00138 return geo;
00139 }
00140
00141 void GeoWidget::editGeoData()
00142 {
00143 GeoDialog dlg( this );
00144
00145 dlg.setLatitude( mLatitudeBox->value() );
00146 dlg.setLongitude( mLongitudeBox->value() );
00147
00148 if ( dlg.exec() ) {
00149 mLatitudeBox->setValue( dlg.latitude() );
00150 mLongitudeBox->setValue( dlg.longitude() );
00151
00152 emit changed();
00153 }
00154 }
00155
00156
00157
00158 GeoDialog::GeoDialog( QWidget *parent, const char *name )
00159 : KDialogBase( Plain, i18n( "Geo Data Input" ), Ok | Cancel, Ok,
00160 parent, name, true, true ),
00161 mUpdateSexagesimalInput( true )
00162 {
00163 QFrame *page = plainPage();
00164
00165 QGridLayout *topLayout = new QGridLayout( page, 2, 2, marginHint(),
00166 spacingHint() );
00167 topLayout->setRowStretch( 1, 1 );
00168
00169 mMapWidget = new GeoMapWidget( page );
00170 topLayout->addMultiCellWidget( mMapWidget, 0, 1, 0, 0 );
00171
00172 mCityCombo = new KComboBox( page );
00173 topLayout->addWidget( mCityCombo, 0, 1 );
00174
00175 QGroupBox *sexagesimalGroup = new QGroupBox( 0, Vertical, i18n( "Sexagesimal" ), page );
00176 QGridLayout *sexagesimalLayout = new QGridLayout( sexagesimalGroup->layout(),
00177 2, 5, spacingHint() );
00178
00179 QLabel *label = new QLabel( i18n( "Latitude:" ), sexagesimalGroup );
00180 sexagesimalLayout->addWidget( label, 0, 0 );
00181
00182 mLatDegrees = new QSpinBox( 0, 90, 1, sexagesimalGroup );
00183 mLatDegrees->setSuffix( "°" );
00184 mLatDegrees->setWrapping( false );
00185 label->setBuddy( mLatDegrees );
00186 sexagesimalLayout->addWidget( mLatDegrees, 0, 1 );
00187
00188 mLatMinutes = new QSpinBox( 0, 59, 1, sexagesimalGroup );
00189 mLatMinutes->setSuffix( "'" );
00190 sexagesimalLayout->addWidget( mLatMinutes, 0, 2 );
00191
00192 mLatSeconds = new QSpinBox( 0, 59, 1, sexagesimalGroup );
00193 mLatSeconds->setSuffix( "\"" );
00194 sexagesimalLayout->addWidget( mLatSeconds, 0, 3 );
00195
00196 mLatDirection = new KComboBox( sexagesimalGroup );
00197 mLatDirection->insertItem( i18n( "North" ) );
00198 mLatDirection->insertItem( i18n( "South" ) );
00199 sexagesimalLayout->addWidget( mLatDirection, 0, 4 );
00200
00201 label = new QLabel( i18n( "Longitude:" ), sexagesimalGroup );
00202 sexagesimalLayout->addWidget( label, 1, 0 );
00203
00204 mLongDegrees = new QSpinBox( 0, 180, 1, sexagesimalGroup );
00205 mLongDegrees->setSuffix( "°" );
00206 label->setBuddy( mLongDegrees );
00207 sexagesimalLayout->addWidget( mLongDegrees, 1, 1 );
00208
00209 mLongMinutes = new QSpinBox( 0, 59, 1, sexagesimalGroup );
00210 mLongMinutes->setSuffix( "'" );
00211 sexagesimalLayout->addWidget( mLongMinutes, 1, 2 );
00212
00213 mLongSeconds = new QSpinBox( 0, 59, 1, sexagesimalGroup );
00214 mLongSeconds->setSuffix( "\"" );
00215 sexagesimalLayout->addWidget( mLongSeconds, 1, 3 );
00216
00217 mLongDirection = new KComboBox( sexagesimalGroup );
00218 mLongDirection->insertItem( i18n( "East" ) );
00219 mLongDirection->insertItem( i18n( "West" ) );
00220 sexagesimalLayout->addWidget( mLongDirection, 1, 4 );
00221
00222 topLayout->addWidget( sexagesimalGroup, 1, 1 );
00223
00224 loadCityList();
00225
00226 connect( mMapWidget, SIGNAL( changed() ),
00227 SLOT( geoMapChanged() ) );
00228 connect( mCityCombo, SIGNAL( activated( int ) ),
00229 SLOT( cityInputChanged() ) );
00230 connect( mLatDegrees, SIGNAL( valueChanged( int ) ),
00231 SLOT( sexagesimalInputChanged() ) );
00232 connect( mLatMinutes, SIGNAL( valueChanged( int ) ),
00233 SLOT( sexagesimalInputChanged() ) );
00234 connect( mLatSeconds, SIGNAL( valueChanged( int ) ),
00235 SLOT( sexagesimalInputChanged() ) );
00236 connect( mLatDirection, SIGNAL( activated( int ) ),
00237 SLOT( sexagesimalInputChanged() ) );
00238 connect( mLongDegrees, SIGNAL( valueChanged( int ) ),
00239 SLOT( sexagesimalInputChanged() ) );
00240 connect( mLongMinutes, SIGNAL( valueChanged( int ) ),
00241 SLOT( sexagesimalInputChanged() ) );
00242 connect( mLongSeconds, SIGNAL( valueChanged( int ) ),
00243 SLOT( sexagesimalInputChanged() ) );
00244 connect( mLongDirection, SIGNAL( activated( int ) ),
00245 SLOT( sexagesimalInputChanged() ) );
00246
00247 KAcceleratorManager::manage( this );
00248 }
00249
00250 GeoDialog::~GeoDialog()
00251 {
00252 }
00253
00254 void GeoDialog::setLatitude( double latitude )
00255 {
00256 mLatitude = latitude;
00257 updateInputs();
00258 }
00259
00260 double GeoDialog::latitude() const
00261 {
00262 return mLatitude;
00263 }
00264
00265 void GeoDialog::setLongitude( double longitude )
00266 {
00267 mLongitude = longitude;
00268 updateInputs();
00269 }
00270
00271 double GeoDialog::longitude() const
00272 {
00273 return mLongitude;
00274 }
00275
00276 void GeoDialog::sexagesimalInputChanged()
00277 {
00278 mLatitude = (double)( mLatDegrees->value() + (double)mLatMinutes->value() /
00279 60 + (double)mLatSeconds->value() / 3600 );
00280
00281 mLatitude *= ( mLatDirection->currentItem() == 1 ? -1 : 1 );
00282
00283 mLongitude = (double)( mLongDegrees->value() + (double)mLongMinutes->value() /
00284 60 + (double)mLongSeconds->value() / 3600 );
00285
00286 mLongitude *= ( mLongDirection->currentItem() == 1 ? -1 : 1 );
00287
00288 mUpdateSexagesimalInput = false;
00289
00290 updateInputs();
00291 }
00292
00293 void GeoDialog::geoMapChanged()
00294 {
00295 mLatitude = mMapWidget->latitude();
00296 mLongitude = mMapWidget->longitude();
00297
00298 updateInputs();
00299 }
00300
00301 void GeoDialog::cityInputChanged()
00302 {
00303 if ( mCityCombo->currentItem() != 0 ) {
00304 GeoData data = mGeoDataMap[ mCityCombo->currentText() ];
00305 mLatitude = data.latitude;
00306 mLongitude = data.longitude;
00307 } else
00308 mLatitude = mLongitude = 0;
00309
00310 updateInputs();
00311 }
00312
00313 void GeoDialog::updateInputs()
00314 {
00315
00316 mCityCombo->blockSignals( true );
00317 mLatDegrees->blockSignals( true );
00318 mLatMinutes->blockSignals( true );
00319 mLatSeconds->blockSignals( true );
00320 mLatDirection->blockSignals( true );
00321 mLongDegrees->blockSignals( true );
00322 mLongMinutes->blockSignals( true );
00323 mLongSeconds->blockSignals( true );
00324 mLongDirection->blockSignals( true );
00325
00326 mMapWidget->setLatitude( mLatitude );
00327 mMapWidget->setLongitude( mLongitude );
00328 mMapWidget->update();
00329
00330 if ( mUpdateSexagesimalInput ) {
00331 int degrees, minutes, seconds;
00332 double latitude = mLatitude;
00333 double longitude = mLongitude;
00334
00335 latitude *= ( mLatitude < 0 ? -1 : 1 );
00336 longitude *= ( mLongitude < 0 ? -1 : 1 );
00337
00338 degrees = (int)( latitude * 1 );
00339 minutes = (int)( ( latitude - degrees ) * 60 );
00340 seconds = (int)( (double)( (double)latitude - (double)degrees - ( (double)minutes / (double)60 ) ) * (double)3600 );
00341
00342 mLatDegrees->setValue( degrees );
00343 mLatMinutes->setValue( minutes );
00344 mLatSeconds->setValue( seconds );
00345
00346 mLatDirection->setCurrentItem( mLatitude < 0 ? 1 : 0 );
00347
00348 degrees = (int)( longitude * 1 );
00349 minutes = (int)( ( longitude - degrees ) * 60 );
00350 seconds = (int)( (double)( longitude - (double)degrees - ( (double)minutes / 60 ) ) * 3600 );
00351
00352 mLongDegrees->setValue( degrees );
00353 mLongMinutes->setValue( minutes );
00354 mLongSeconds->setValue( seconds );
00355 mLongDirection->setCurrentItem( mLongitude < 0 ? 1 : 0 );
00356 }
00357 mUpdateSexagesimalInput = true;
00358
00359 int pos = nearestCity( mLongitude, mLatitude );
00360 if ( pos != -1 )
00361 mCityCombo->setCurrentItem( pos + 1 );
00362 else
00363 mCityCombo->setCurrentItem( 0 );
00364
00365 mCityCombo->blockSignals( false );
00366 mLatDegrees->blockSignals( false );
00367 mLatMinutes->blockSignals( false );
00368 mLatSeconds->blockSignals( false );
00369 mLatDirection->blockSignals( false );
00370 mLongDegrees->blockSignals( false );
00371 mLongMinutes->blockSignals( false );
00372 mLongSeconds->blockSignals( false );
00373 mLongDirection->blockSignals( false );
00374 }
00375
00376 void GeoDialog::loadCityList()
00377 {
00378 mCityCombo->clear();
00379 mGeoDataMap.clear();
00380
00381 QFile file( locate( "data", "kaddressbook/zone.tab" ) );
00382
00383 if ( file.open( IO_ReadOnly ) ) {
00384 QTextStream s( &file );
00385
00386 QString line, country;
00387 QRegExp coord( "[+-]\\d+[+-]\\d+" );
00388 QRegExp name( "[^\\s]+/[^\\s]+" );
00389 int pos;
00390
00391 while ( !s.eof() ) {
00392 line = s.readLine().stripWhiteSpace();
00393 if ( line.isEmpty() || line[ 0 ] == '#' )
00394 continue;
00395
00396 country = line.left( 2 );
00397 QString c, n;
00398 pos = coord.search( line, 0 );
00399 if ( pos >= 0 )
00400 c = line.mid( pos, coord.matchedLength() );
00401
00402 pos = name.search(line, pos);
00403 if ( pos > 0 ) {
00404 n = line.mid( pos, name.matchedLength() ).stripWhiteSpace();
00405 n.replace( '_', " " );
00406 }
00407
00408 if ( !c.isEmpty() && !n.isEmpty() ) {
00409 pos = c.find( "+", 1 );
00410 if ( pos < 0 )
00411 pos = c.find( "-", 1 );
00412 if ( pos > 0 ) {
00413 GeoData data;
00414 data.latitude = calculateCoordinate( c.left( pos ) );
00415 data.longitude = calculateCoordinate( c.mid( pos ) );
00416 data.country = country;
00417
00418 mGeoDataMap.insert( n, data );
00419 }
00420 }
00421 }
00422 QStringList items( mGeoDataMap.keys() );
00423 items.prepend( i18n( "Undefined" ) );
00424 mCityCombo->insertStringList( items );
00425
00426 file.close();
00427 }
00428 }
00429
00430 double GeoDialog::calculateCoordinate( const QString &coordinate )
00431 {
00432 int neg;
00433 int d = 0, m = 0, s = 0;
00434 QString str = coordinate;
00435
00436 neg = str.left( 1 ) == "-";
00437 str.remove( 0, 1 );
00438
00439 switch ( str.length() ) {
00440 case 4:
00441 d = str.left( 2 ).toInt();
00442 m = str.mid( 2 ).toInt();
00443 break;
00444 case 5:
00445 d = str.left( 3 ).toInt();
00446 m = str.mid( 3 ).toInt();
00447 break;
00448 case 6:
00449 d = str.left( 2 ).toInt();
00450 m = str.mid( 2, 2 ).toInt();
00451 s = str.right( 2 ).toInt();
00452 break;
00453 case 7:
00454 d = str.left( 3 ).toInt();
00455 m = str.mid( 3, 2 ).toInt();
00456 s = str.right( 2 ).toInt();
00457 break;
00458 default:
00459 break;
00460 }
00461
00462 if ( neg )
00463 return - ( d + m / 60.0 + s / 3600.0 );
00464 else
00465 return d + m / 60.0 + s / 3600.0;
00466 }
00467
00468 int GeoDialog::nearestCity( double x, double y )
00469 {
00470 QMap<QString, GeoData>::Iterator it;
00471 int pos = 0;
00472 for ( it = mGeoDataMap.begin(); it != mGeoDataMap.end(); ++it, pos++ ) {
00473 double dist = ( (*it).longitude - x ) * ( (*it).longitude - x ) +
00474 ( (*it).latitude - y ) * ( (*it).latitude - y );
00475 if ( dist < 1.5 )
00476 return pos;
00477 }
00478
00479 return -1;
00480 }
00481
00482
00483 GeoMapWidget::GeoMapWidget( QWidget *parent, const char *name )
00484 : QWidget( parent, name ), mLatitude( 0 ), mLongitude( 0 )
00485 {
00486 setBackgroundMode( NoBackground );
00487
00488 setFixedSize( 400, 200 );
00489
00490 update();
00491 }
00492
00493 GeoMapWidget::~GeoMapWidget()
00494 {
00495 }
00496
00497 void GeoMapWidget::setLatitude( double latitude )
00498 {
00499 mLatitude = latitude;
00500 }
00501
00502 double GeoMapWidget::latitude()const
00503 {
00504 return mLatitude;
00505 }
00506
00507 void GeoMapWidget::setLongitude( double longitude )
00508 {
00509 mLongitude = longitude;
00510 }
00511
00512 double GeoMapWidget::longitude()const
00513 {
00514 return mLongitude;
00515 }
00516
00517 void GeoMapWidget::mousePressEvent( QMouseEvent *event )
00518 {
00519 double latMid = height() / 2;
00520 double longMid = width() / 2;
00521
00522 double latOffset = latMid - event->y();
00523 double longOffset = event->x() - longMid;
00524
00525 mLatitude = ( latOffset * 90 ) / latMid;
00526 mLongitude = ( longOffset * 180 ) / longMid;
00527
00528 emit changed();
00529 }
00530
00531 void GeoMapWidget::paintEvent( QPaintEvent* )
00532 {
00533 uint w = width();
00534 uint h = height();
00535
00536 QPixmap pm( w, h );
00537 QPainter p;
00538 p.begin( &pm, this );
00539
00540 p.setPen( QColor( 255, 0, 0 ) );
00541 p.setBrush( QColor( 255, 0, 0 ) );
00542
00543 QPixmap world( locate( "data", "kaddressbook/pics/world.jpg" ) );
00544 p.drawPixmap( 0, 0, world );
00545
00546 double latMid = h / 2;
00547 double longMid = w / 2;
00548
00549 double latOffset = ( mLatitude * latMid ) / 90;
00550 double longOffset = ( mLongitude * longMid ) / 180;
00551
00552 int x = (int)(longMid + longOffset);
00553 int y = (int)(latMid - latOffset);
00554 p.drawEllipse( x, y, 4, 4 );
00555
00556 p.end();
00557 bitBlt( this, 0, 0, &pm );
00558 }
00559
00560 #include "geowidget.moc"