akonadi
geoeditwidget.cpp
00001 /* 00002 This file is part of Akonadi Contact. 00003 00004 Copyright (c) 2009 Tobias Koenig <tokoe@kde.org> 00005 00006 This library is free software; you can redistribute it and/or modify it 00007 under the terms of the GNU Library General Public License as published by 00008 the Free Software Foundation; either version 2 of the License, or (at your 00009 option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, but WITHOUT 00012 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00013 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00014 License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to the 00018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00019 02110-1301, USA. 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"