kaddressbook Library API Documentation

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