kaddressbook

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 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( 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   // 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   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"
KDE Home | KDE Accessibility Home | Description of Access Keys