geowidget.cpp
00001 /* 00002 This file is part of KAddressBook. 00003 Copyright (c) 2002 Tobias Koenig <tokoe@kde.org> 00004 00005 This program is free software; you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or 00008 (at your option) any later version. 00009 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with this program; if not, write to the Free Software 00017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 00019 As a special exception, permission is given to link this program 00020 with any edition of TQt, and distribute the resulting executable, 00021 without including the source code for TQt in the source distribution. 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 <tqcheckbox.h> 00034 #include <tqfile.h> 00035 #include <tqgroupbox.h> 00036 #include <tqlabel.h> 00037 #include <tqlayout.h> 00038 #include <tqlistbox.h> 00039 #include <tqpainter.h> 00040 #include <tqpixmap.h> 00041 #include <tqpushbutton.h> 00042 #include <tqregexp.h> 00043 #include <tqstring.h> 00044 00045 #include "geowidget.h" 00046 00047 GeoWidget::GeoWidget( KABC::AddressBook *ab, TQWidget *parent, const char *name ) 00048 : KAB::ContactEditorWidget( ab, parent, name ), mReadOnly( false ) 00049 { 00050 TQLabel *label = 0; 00051 00052 TQGridLayout *topLayout = new TQGridLayout( this, 4, 3 ); 00053 topLayout->setMargin( KDialog::marginHint() ); 00054 topLayout->setSpacing( KDialog::spacingHint() ); 00055 00056 label = new TQLabel( this ); 00057 label->setPixmap( KGlobal::iconLoader()->loadIcon( "package_network", 00058 KIcon::Desktop, KIcon::SizeMedium ) ); 00059 label->setAlignment( TQt::AlignTop ); 00060 topLayout->addMultiCellWidget( label, 0, 3, 0, 0 ); 00061 00062 mGeoIsValid = new TQCheckBox( i18n( "Use geo data" ), this ); 00063 topLayout->addMultiCellWidget( mGeoIsValid, 0, 0, 1, 2 ); 00064 00065 label = new TQLabel( 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 TQLabel( 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 TQPushButton( i18n( "Edit Geo Data..." ), this ); 00084 mExtendedButton->setEnabled( false ); 00085 topLayout->addMultiCellWidget( mExtendedButton, 3, 3, 1, 2 ); 00086 00087 connect( mLatitudeBox, TQT_SIGNAL( valueChanged( double ) ), 00088 TQT_SLOT( setModified() ) ); 00089 connect( mLongitudeBox, TQT_SIGNAL( valueChanged( double ) ), 00090 TQT_SLOT( setModified() ) ); 00091 connect( mExtendedButton, TQT_SIGNAL( clicked() ), 00092 TQT_SLOT( editGeoData() ) ); 00093 00094 connect( mGeoIsValid, TQT_SIGNAL( toggled( bool ) ), 00095 mLatitudeBox, TQT_SLOT( setEnabled( bool ) ) ); 00096 connect( mGeoIsValid, TQT_SIGNAL( toggled( bool ) ), 00097 mLongitudeBox, TQT_SLOT( setEnabled( bool ) ) ); 00098 connect( mGeoIsValid, TQT_SIGNAL( toggled( bool ) ), 00099 mExtendedButton, TQT_SLOT( setEnabled( bool ) ) ); 00100 connect( mGeoIsValid, TQT_SIGNAL( toggled( bool ) ), 00101 TQT_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( TQWidget *parent, const char *name ) 00161 : KDialogBase( Plain, i18n( "Geo Data Input" ), Ok | Cancel, Ok, 00162 parent, name, true, true ), 00163 mUpdateSexagesimalInput( true ) 00164 { 00165 TQFrame *page = plainPage(); 00166 00167 TQGridLayout *topLayout = new TQGridLayout( 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 TQGroupBox *sexagesimalGroup = new TQGroupBox( 0, Qt::Vertical, i18n( "Sexagesimal" ), page ); 00178 TQGridLayout *sexagesimalLayout = new TQGridLayout( sexagesimalGroup->layout(), 00179 2, 5, spacingHint() ); 00180 00181 TQLabel *label = new TQLabel( i18n( "Latitude:" ), sexagesimalGroup ); 00182 sexagesimalLayout->addWidget( label, 0, 0 ); 00183 00184 mLatDegrees = new TQSpinBox( 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 TQSpinBox( 0, 59, 1, sexagesimalGroup ); 00191 mLatMinutes->setSuffix( "'" ); 00192 sexagesimalLayout->addWidget( mLatMinutes, 0, 2 ); 00193 00194 mLatSeconds = new TQSpinBox( 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 TQLabel( i18n( "Longitude:" ), sexagesimalGroup ); 00204 sexagesimalLayout->addWidget( label, 1, 0 ); 00205 00206 mLongDegrees = new TQSpinBox( 0, 180, 1, sexagesimalGroup ); 00207 mLongDegrees->setSuffix( "�" ); 00208 label->setBuddy( mLongDegrees ); 00209 sexagesimalLayout->addWidget( mLongDegrees, 1, 1 ); 00210 00211 mLongMinutes = new TQSpinBox( 0, 59, 1, sexagesimalGroup ); 00212 mLongMinutes->setSuffix( "'" ); 00213 sexagesimalLayout->addWidget( mLongMinutes, 1, 2 ); 00214 00215 mLongSeconds = new TQSpinBox( 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, TQT_SIGNAL( changed() ), 00229 TQT_SLOT( geoMapChanged() ) ); 00230 connect( mCityCombo, TQT_SIGNAL( activated( int ) ), 00231 TQT_SLOT( cityInputChanged() ) ); 00232 connect( mLatDegrees, TQT_SIGNAL( valueChanged( int ) ), 00233 TQT_SLOT( sexagesimalInputChanged() ) ); 00234 connect( mLatMinutes, TQT_SIGNAL( valueChanged( int ) ), 00235 TQT_SLOT( sexagesimalInputChanged() ) ); 00236 connect( mLatSeconds, TQT_SIGNAL( valueChanged( int ) ), 00237 TQT_SLOT( sexagesimalInputChanged() ) ); 00238 connect( mLatDirection, TQT_SIGNAL( activated( int ) ), 00239 TQT_SLOT( sexagesimalInputChanged() ) ); 00240 connect( mLongDegrees, TQT_SIGNAL( valueChanged( int ) ), 00241 TQT_SLOT( sexagesimalInputChanged() ) ); 00242 connect( mLongMinutes, TQT_SIGNAL( valueChanged( int ) ), 00243 TQT_SLOT( sexagesimalInputChanged() ) ); 00244 connect( mLongSeconds, TQT_SIGNAL( valueChanged( int ) ), 00245 TQT_SLOT( sexagesimalInputChanged() ) ); 00246 connect( mLongDirection, TQT_SIGNAL( activated( int ) ), 00247 TQT_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 // hmm, doesn't look nice, but there is no better way AFAIK 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 TQFile file( locate( "data", "kaddressbook/zone.tab" ) ); 00384 00385 if ( file.open( IO_ReadOnly ) ) { 00386 TQTextStream s( &file ); 00387 00388 TQString line, country; 00389 TQRegExp coord( "[+-]\\d+[+-]\\d+" ); 00390 TQRegExp 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 TQString 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 TQStringList items( mGeoDataMap.keys() ); 00425 items.prepend( i18n( "Undefined" ) ); 00426 mCityCombo->insertStringList( items ); 00427 00428 file.close(); 00429 } 00430 } 00431 00432 double GeoDialog::calculateCoordinate( const TQString &coordinate ) const 00433 { 00434 int neg; 00435 int d = 0, m = 0, s = 0; 00436 TQString 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 TQMap<TQString, 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( TQWidget *parent, const char *name ) 00486 : TQWidget( 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( TQMouseEvent *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( TQPaintEvent* ) 00534 { 00535 uint w = width(); 00536 uint h = height(); 00537 00538 TQPixmap pm( w, h ); 00539 TQPainter p; 00540 p.begin( &pm, this ); 00541 00542 p.setPen( TQColor( 255, 0, 0 ) ); 00543 p.setBrush( TQColor( 255, 0, 0 ) ); 00544 00545 TQPixmap 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"