00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "geoeditwidget.h"
00023
00024 #include "autoqpointer_p.h"
00025
00026 #include <kabc/addressee.h>
00027 #include <kabc/geo.h>
00028 #include <kcombobox.h>
00029 #include <klocale.h>
00030 #include <kstandarddirs.h>
00031
00032 #include <QtCore/QFile>
00033 #include <QtCore/QTextStream>
00034 #include <QtGui/QDoubleSpinBox>
00035 #include <QtGui/QGridLayout>
00036 #include <QtGui/QGroupBox>
00037 #include <QtGui/QLabel>
00038 #include <QtGui/QPainter>
00039 #include <QtGui/QPushButton>
00040 #include <QtGui/QSpinBox>
00041
00042 class GeoMapWidget : public QWidget
00043 {
00044 public:
00045 GeoMapWidget( QWidget *parent = 0 )
00046 : QWidget( parent )
00047 {
00048 mWorld = QPixmap( KStandardDirs::locate( "data", QLatin1String( "akonadi/contact/pics/world.jpg" ) ) );
00049
00050 setAttribute( Qt::WA_NoSystemBackground, true );
00051 setFixedSize( 400, 200 );
00052
00053 update();
00054 }
00055
00056 void setCoordinates( const KABC::Geo &coordinates )
00057 {
00058 mCoordinates = coordinates;
00059
00060 update();
00061 }
00062
00063 protected:
00064 virtual void paintEvent( QPaintEvent* )
00065 {
00066 QPainter p;
00067 p.begin( this );
00068 p.setPen( QColor( 255, 0, 0 ) );
00069 p.setBrush( QColor( 255, 0, 0 ) );
00070
00071 p.drawPixmap( 0, 0, mWorld );
00072
00073 if ( mCoordinates.isValid() ) {
00074 const double latMid = height() / 2;
00075 const double longMid = width() / 2;
00076 const double latOffset = ( mCoordinates.latitude() * latMid ) / 90;
00077 const double longOffset = ( mCoordinates.longitude() * longMid ) / 180;
00078
00079 const int x = (int)(longMid + longOffset);
00080 const int y = (int)(latMid - latOffset);
00081 p.drawEllipse( x, y, 4, 4 );
00082 }
00083
00084 p.end();
00085 }
00086
00087 private:
00088 QPixmap mWorld;
00089 KABC::Geo mCoordinates;
00090 };
00091
00092
00093 GeoEditWidget::GeoEditWidget( QWidget *parent )
00094 : QWidget( parent )
00095 {
00096 QGridLayout *layout = new QGridLayout( this );
00097 layout->setMargin( 0 );
00098
00099 mMap = new GeoMapWidget;
00100 layout->addWidget( mMap, 0, 0, 1, 4, Qt::AlignCenter|Qt::AlignVCenter );
00101
00102 QLabel *label = new QLabel( i18nc( "@label", "Latitude:" ) );
00103 label->setAlignment( Qt::AlignRight );
00104 layout->addWidget( label, 1, 0 );
00105
00106 mLatitudeLabel = new QLabel;
00107 layout->addWidget( mLatitudeLabel, 1, 1 );
00108
00109 label = new QLabel( i18nc( "@label", "Longitude:" ) );
00110 label->setAlignment( Qt::AlignRight );
00111 layout->addWidget( label, 1, 2 );
00112
00113 mLongitudeLabel = new QLabel;
00114 layout->addWidget( mLongitudeLabel, 1, 3 );
00115
00116 mChangeButton = new QPushButton( i18nc( "@label Change the coordinates", "Change..." ) );
00117 layout->addWidget( mChangeButton, 2, 0, 1, 4, Qt::AlignRight );
00118
00119 layout->setRowStretch( 3, 1 );
00120
00121 connect( mChangeButton, SIGNAL( clicked() ), SLOT( changeClicked() ) );
00122
00123 updateView();
00124 }
00125
00126 GeoEditWidget::~GeoEditWidget()
00127 {
00128 }
00129
00130 void GeoEditWidget::loadContact( const KABC::Addressee &contact )
00131 {
00132 mCoordinates = contact.geo();
00133 updateView();
00134 }
00135
00136 void GeoEditWidget::storeContact( KABC::Addressee &contact ) const
00137 {
00138 contact.setGeo( mCoordinates );
00139 }
00140
00141 void GeoEditWidget::setReadOnly( bool readOnly )
00142 {
00143 mChangeButton->setEnabled( !readOnly );
00144 }
00145
00146 void GeoEditWidget::updateView()
00147 {
00148 if ( !mCoordinates.isValid() ) {
00149 mLatitudeLabel->setText( i18nc( "@label Coordinates are not available", "n/a" ) );
00150 mLongitudeLabel->setText( i18nc( "@label Coordinates are not available", "n/a" ) );
00151 } else {
00152 mLatitudeLabel->setText( i18nc( "@label The formatted coordinates", "%1 %2", mCoordinates.latitude(), QChar( 176 ) ) );
00153 mLongitudeLabel->setText( i18nc( "@label The formatted coordinates", "%1 %2", mCoordinates.longitude(), QChar( 176 ) ) );
00154 }
00155 mMap->setCoordinates( mCoordinates );
00156 }
00157
00158 void GeoEditWidget::changeClicked()
00159 {
00160 AutoQPointer<GeoDialog> dlg = new GeoDialog( mCoordinates, this );
00161 if ( dlg->exec() ) {
00162 mCoordinates = dlg->coordinates();
00163 updateView();
00164 }
00165 }
00166
00167 static double calculateCoordinate( const QString &coordinate )
00168 {
00169 int neg;
00170 int d = 0, m = 0, s = 0;
00171 QString str = coordinate;
00172
00173 neg = str.left( 1 ) == QLatin1String( "-" );
00174 str.remove( 0, 1 );
00175
00176 switch ( str.length() ) {
00177 case 4:
00178 d = str.left( 2 ).toInt();
00179 m = str.mid( 2 ).toInt();
00180 break;
00181 case 5:
00182 d = str.left( 3 ).toInt();
00183 m = str.mid( 3 ).toInt();
00184 break;
00185 case 6:
00186 d = str.left( 2 ).toInt();
00187 m = str.mid( 2, 2 ).toInt();
00188 s = str.right( 2 ).toInt();
00189 break;
00190 case 7:
00191 d = str.left( 3 ).toInt();
00192 m = str.mid( 3, 2 ).toInt();
00193 s = str.right( 2 ).toInt();
00194 break;
00195 default:
00196 break;
00197 }
00198
00199 if ( neg )
00200 return - ( d + m / 60.0 + s / 3600.0 );
00201 else
00202 return d + m / 60.0 + s / 3600.0;
00203 }
00204
00205 GeoDialog::GeoDialog( const KABC::Geo &coordinates, QWidget *parent )
00206 : KDialog( parent ),
00207 mCoordinates( coordinates )
00208 {
00209 KGlobal::locale()->insertCatalog( QLatin1String( "timezones4" ) );
00210 setCaption( i18nc( "@title:window", "Coordinate Selection" ) );
00211 setButtons( Ok | Cancel );
00212 setDefaultButton( Ok );
00213 showButtonSeparator( true );
00214 setModal( true );
00215
00216 QFrame *page = new QFrame(this);
00217 setMainWidget( page );
00218
00219 QVBoxLayout *layout = new QVBoxLayout( page );
00220
00221 mCityCombo = new KComboBox( page );
00222 layout->addWidget( mCityCombo );
00223
00224 QGroupBox *decimalGroup = new QGroupBox( i18nc( "@title:group Decimal representation of coordinates", "Decimal" ), page );
00225 QGridLayout *decimalLayout = new QGridLayout();
00226 decimalGroup->setLayout( decimalLayout );
00227 decimalLayout->setSpacing( spacingHint() );
00228
00229 QLabel *label = new QLabel( i18nc( "@label:spinbox", "Latitude:" ), decimalGroup );
00230 decimalLayout->addWidget( label, 0, 0 );
00231
00232 mLatitude = new QDoubleSpinBox( decimalGroup );
00233 mLatitude->setMinimum( -90 );
00234 mLatitude->setMaximum( 90 );
00235 mLatitude->setSingleStep( 1 );
00236 mLatitude->setValue( 0 );
00237 mLatitude->setDecimals( 6 );
00238 mLatitude->setSuffix( QChar( 176 ) );
00239 decimalLayout->addWidget( mLatitude, 0, 1 );
00240
00241 label = new QLabel( i18nc( "@label:spinbox", "Longitude:" ), decimalGroup );
00242 decimalLayout->addWidget( label, 1, 0 );
00243
00244 mLongitude = new QDoubleSpinBox( decimalGroup );
00245 mLongitude->setMinimum( -180 );
00246 mLongitude->setMaximum( 180 );
00247 mLongitude->setSingleStep( 1 );
00248 mLongitude->setValue( 0 );
00249 mLongitude->setDecimals( 6 );
00250 mLongitude->setSuffix( QChar( 176 ) );
00251 decimalLayout->addWidget( mLongitude, 1, 1 );
00252
00253 QGroupBox *sexagesimalGroup = new QGroupBox( i18nc( "@title:group", "Sexagesimal" ), page );
00254 QGridLayout *sexagesimalLayout = new QGridLayout();
00255 sexagesimalGroup->setLayout( sexagesimalLayout );
00256 sexagesimalLayout->setSpacing( spacingHint() );
00257
00258 label = new QLabel( i18nc( "@label:spinbox", "Latitude:" ), sexagesimalGroup );
00259 sexagesimalLayout->addWidget( label, 0, 0 );
00260
00261 mLatDegrees = new QSpinBox( sexagesimalGroup );
00262 mLatDegrees->setMinimum( 0 );
00263 mLatDegrees->setMaximum( 90 );
00264 mLatDegrees->setValue( 1 );
00265 mLatDegrees->setSuffix( QChar( 176 ) );
00266 mLatDegrees->setWrapping( false );
00267 label->setBuddy( mLatDegrees );
00268 sexagesimalLayout->addWidget( mLatDegrees, 0, 1 );
00269
00270 mLatMinutes = new QSpinBox( sexagesimalGroup );
00271 mLatMinutes->setMinimum( 0 );
00272 mLatMinutes->setMaximum( 59 );
00273 mLatMinutes->setValue( 1 );
00274
00275 mLatMinutes->setSuffix( QLatin1String( "'" ) );
00276 sexagesimalLayout->addWidget( mLatMinutes, 0, 2 );
00277
00278 mLatSeconds = new QSpinBox( sexagesimalGroup );
00279 mLatSeconds->setMinimum( 0 );
00280 mLatSeconds->setMaximum( 59 );
00281 mLatSeconds->setValue( 1 );
00282 mLatSeconds->setSuffix( QLatin1String( "\"" ) );
00283 sexagesimalLayout->addWidget( mLatSeconds, 0, 3 );
00284
00285 mLatDirection = new KComboBox( sexagesimalGroup );
00286 mLatDirection->addItem( i18nc( "@item:inlistbox Latitude direction", "North" ) );
00287 mLatDirection->addItem( i18nc( "@item:inlistbox Latitude direction", "South" ) );
00288 sexagesimalLayout->addWidget( mLatDirection, 0, 4 );
00289
00290 label = new QLabel( i18nc( "@label:spinbox", "Longitude:" ), sexagesimalGroup );
00291 sexagesimalLayout->addWidget( label, 1, 0 );
00292
00293 mLongDegrees = new QSpinBox( sexagesimalGroup );
00294 mLongDegrees->setMinimum( 0 );
00295 mLongDegrees->setMaximum( 180 );
00296 mLongDegrees->setValue( 1 );
00297 mLongDegrees->setSuffix( QChar( 176 ) );
00298 label->setBuddy( mLongDegrees );
00299 sexagesimalLayout->addWidget( mLongDegrees, 1, 1 );
00300
00301 mLongMinutes = new QSpinBox( sexagesimalGroup );
00302 mLongMinutes->setMinimum( 0 );
00303 mLongMinutes->setMaximum( 59 );
00304 mLongMinutes->setValue( 1 );
00305 mLongMinutes->setSuffix( QLatin1String( "'" ) );
00306 sexagesimalLayout->addWidget( mLongMinutes, 1, 2 );
00307
00308 mLongSeconds = new QSpinBox( sexagesimalGroup );
00309 mLongSeconds->setMinimum( 0 );
00310 mLongSeconds->setMaximum( 59 );
00311 mLongSeconds->setValue( 1 );
00312 mLongSeconds->setSuffix( QLatin1String( "\"" ) );
00313 sexagesimalLayout->addWidget( mLongSeconds, 1, 3 );
00314
00315 mLongDirection = new KComboBox( sexagesimalGroup );
00316 mLongDirection->addItem( i18nc( "@item:inlistbox Longtitude direction", "East" ) );
00317 mLongDirection->addItem( i18nc( "@item:inlistbox Longtitude direction", "West" ) );
00318 sexagesimalLayout->addWidget( mLongDirection, 1, 4 );
00319
00320 layout->addWidget( decimalGroup );
00321 layout->addWidget( sexagesimalGroup );
00322
00323 loadCityList();
00324
00325 connect( mCityCombo, SIGNAL( activated( int ) ),
00326 SLOT( cityInputChanged() ) );
00327 connect( mLatitude, SIGNAL( valueChanged( double ) ),
00328 SLOT( decimalInputChanged() ) );
00329 connect( mLongitude, SIGNAL( valueChanged( double ) ),
00330 SLOT( decimalInputChanged() ) );
00331 connect( mLatDegrees, SIGNAL( valueChanged( int ) ),
00332 SLOT( sexagesimalInputChanged() ) );
00333 connect( mLatMinutes, SIGNAL( valueChanged( int ) ),
00334 SLOT( sexagesimalInputChanged() ) );
00335 connect( mLatSeconds, SIGNAL( valueChanged( int ) ),
00336 SLOT( sexagesimalInputChanged() ) );
00337 connect( mLatDirection, SIGNAL( activated( int ) ),
00338 SLOT( sexagesimalInputChanged() ) );
00339 connect( mLongDegrees, SIGNAL( valueChanged( int ) ),
00340 SLOT( sexagesimalInputChanged() ) );
00341 connect( mLongMinutes, SIGNAL( valueChanged( int ) ),
00342 SLOT( sexagesimalInputChanged() ) );
00343 connect( mLongSeconds, SIGNAL( valueChanged( int ) ),
00344 SLOT( sexagesimalInputChanged() ) );
00345 connect( mLongDirection, SIGNAL( activated( int ) ),
00346 SLOT( sexagesimalInputChanged() ) );
00347
00348 updateInputs();
00349 }
00350
00351 KABC::Geo GeoDialog::coordinates() const
00352 {
00353 return mCoordinates;
00354 }
00355
00356 void GeoDialog::cityInputChanged()
00357 {
00358 if ( mCityCombo->currentIndex() != 0 ) {
00359 GeoData geoData = mGeoDataMap[ mCityCombo->currentText() ];
00360 mCoordinates.setLatitude( geoData.latitude );
00361 mCoordinates.setLongitude( geoData.longitude );
00362 } else {
00363 mCoordinates.setLatitude( 0 );
00364 mCoordinates.setLongitude( 0 );
00365 }
00366
00367 updateInputs( ExceptCity );
00368 }
00369
00370 void GeoDialog::decimalInputChanged()
00371 {
00372 mCoordinates.setLatitude( mLatitude->value() );
00373 mCoordinates.setLongitude( mLongitude->value() );
00374
00375 updateInputs( ExceptDecimal );
00376 }
00377
00378 void GeoDialog::sexagesimalInputChanged()
00379 {
00380 double latitude = (double)( mLatDegrees->value() + (double)mLatMinutes->value() /
00381 60 + (double)mLatSeconds->value() / 3600 );
00382 latitude *= ( mLatDirection->currentIndex() == 1 ? -1 : 1 );
00383
00384 double longitude = (double)( mLongDegrees->value() + (double)mLongMinutes->value() /
00385 60 + (double)mLongSeconds->value() / 3600 );
00386 longitude *= ( mLongDirection->currentIndex() == 1 ? -1 : 1 );
00387
00388 mCoordinates.setLatitude( latitude );
00389 mCoordinates.setLongitude( longitude );
00390
00391 updateInputs( ExceptSexagesimal );
00392 }
00393
00394 void GeoDialog::updateInputs( ExceptType type )
00395 {
00396 mCityCombo->blockSignals( true );
00397 mLatitude->blockSignals( true );
00398 mLongitude->blockSignals( true );
00399 mLatDegrees->blockSignals( true );
00400 mLatMinutes->blockSignals( true );
00401 mLatSeconds->blockSignals( true );
00402 mLatDirection->blockSignals( true );
00403 mLongDegrees->blockSignals( true );
00404 mLongMinutes->blockSignals( true );
00405 mLongSeconds->blockSignals( true );
00406 mLongDirection->blockSignals( true );
00407
00408 if ( !(type & ExceptSexagesimal) ) {
00409 int degrees, minutes, seconds;
00410 double latitude = mCoordinates.latitude();
00411 double longitude = mCoordinates.longitude();
00412
00413 latitude *= ( latitude < 0 ? -1 : 1 );
00414 longitude *= ( longitude < 0 ? -1 : 1 );
00415
00416 degrees = (int)( latitude * 1 );
00417 minutes = (int)( ( latitude - degrees ) * 60 );
00418 seconds = (int)( (double)( (double)latitude - (double)degrees - ( (double)minutes / (double)60 ) ) * (double)3600 );
00419
00420 mLatDegrees->setValue( degrees );
00421 mLatMinutes->setValue( minutes );
00422 mLatSeconds->setValue( seconds );
00423
00424 mLatDirection->setCurrentIndex( mLatitude < 0 ? 1 : 0 );
00425
00426 degrees = (int)( longitude * 1 );
00427 minutes = (int)( ( longitude - degrees ) * 60 );
00428 seconds = (int)( (double)( longitude - (double)degrees - ( (double)minutes / 60 ) ) * 3600 );
00429
00430 mLongDegrees->setValue( degrees );
00431 mLongMinutes->setValue( minutes );
00432 mLongSeconds->setValue( seconds );
00433 mLongDirection->setCurrentIndex( mLongitude < 0 ? 1 : 0 );
00434 }
00435
00436 if ( !(type & ExceptDecimal) ) {
00437 mLatitude->setValue( mCoordinates.latitude() );
00438 mLongitude->setValue( mCoordinates.longitude() );
00439 }
00440
00441 if ( !(type & ExceptCity) ) {
00442 const int index = nearestCity( mCoordinates.longitude(), mCoordinates.latitude() );
00443 if ( index != -1 )
00444 mCityCombo->setCurrentIndex( index + 1 );
00445 else
00446 mCityCombo->setCurrentIndex( 0 );
00447 }
00448
00449 mCityCombo->blockSignals( false );
00450 mLatitude->blockSignals( false );
00451 mLongitude->blockSignals( false );
00452 mLatDegrees->blockSignals( false );
00453 mLatMinutes->blockSignals( false );
00454 mLatSeconds->blockSignals( false );
00455 mLatDirection->blockSignals( false );
00456 mLongDegrees->blockSignals( false );
00457 mLongMinutes->blockSignals( false );
00458 mLongSeconds->blockSignals( false );
00459 mLongDirection->blockSignals( false );
00460 }
00461
00462 void GeoDialog::loadCityList()
00463 {
00464 mCityCombo->clear();
00465 mGeoDataMap.clear();
00466
00467 QFile file( KStandardDirs::locate( "data", QLatin1String( "akonadi/contact/data/zone.tab" ) ) );
00468
00469 if ( file.open( QIODevice::ReadOnly ) ) {
00470 QTextStream s( &file );
00471
00472 QString line, country;
00473 QRegExp coord( QLatin1String( "[+-]\\d+[+-]\\d+" ) );
00474 QRegExp name( QLatin1String( "[^\\s]+/[^\\s]+" ) );
00475 int pos;
00476
00477 while ( !s.atEnd() ) {
00478 line = s.readLine().trimmed();
00479 if ( line.isEmpty() || line[ 0 ] == QLatin1Char( '#' ) )
00480 continue;
00481
00482 country = line.left( 2 );
00483 QString c, n;
00484 pos = coord.indexIn( line, 0 );
00485 if ( pos >= 0 )
00486 c = line.mid( pos, coord.matchedLength() );
00487
00488 pos = name.indexIn(line, pos);
00489 if ( pos > 0 ) {
00490 n = line.mid( pos, name.matchedLength() ).trimmed();
00491 }
00492
00493 if ( !c.isEmpty() && !n.isEmpty() ) {
00494 pos = c.indexOf( QLatin1Char( '+' ), 1 );
00495 if ( pos < 0 )
00496 pos = c.indexOf( QLatin1Char( '-' ), 1 );
00497 if ( pos > 0 ) {
00498 GeoData geoData;
00499 geoData.latitude = calculateCoordinate( c.left( pos ) );
00500 geoData.longitude = calculateCoordinate( c.mid( pos ) );
00501 geoData.country = country;
00502
00503 mGeoDataMap.insert( i18n( qPrintable ( n ) ).replace( QLatin1Char( '_' ), QLatin1Char( ' ' ) ), geoData );
00504 }
00505 }
00506 }
00507
00508 QStringList items( mGeoDataMap.keys() );
00509 items.prepend( i18nc( "@item:inlistbox Undefined location", "Undefined" ) );
00510 mCityCombo->addItems( items );
00511
00512 file.close();
00513 }
00514 }
00515
00516 int GeoDialog::nearestCity( double x, double y ) const
00517 {
00518 QMap<QString, GeoData>::ConstIterator it;
00519 int pos = 0;
00520 for ( it = mGeoDataMap.begin(); it != mGeoDataMap.end(); ++it, ++pos ) {
00521 double dist = ( (*it).longitude - x ) * ( (*it).longitude - x ) +
00522 ( (*it).latitude - y ) * ( (*it).latitude - y );
00523 if ( dist < 0.0005 )
00524 return pos;
00525 }
00526
00527 return -1;
00528 }
00529
00530 #include "geoeditwidget.moc"