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