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