kaddressbook

geowidget.cpp
1 /*
2  This file is part of KAddressBook.
3  Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 
19  As a special exception, permission is given to link this program
20  with any edition of TQt, and distribute the resulting executable,
21  without including the source code for TQt in the source distribution.
22 */
23 
24 #include <tdeabc/geo.h>
25 #include <tdeaccelmanager.h>
26 #include <kcombobox.h>
27 #include <kdebug.h>
28 #include <kiconloader.h>
29 #include <tdelocale.h>
30 #include <knuminput.h>
31 #include <kstandarddirs.h>
32 
33 #include <tqcheckbox.h>
34 #include <tqfile.h>
35 #include <tqgroupbox.h>
36 #include <tqlabel.h>
37 #include <tqlayout.h>
38 #include <tqlistbox.h>
39 #include <tqpainter.h>
40 #include <tqpixmap.h>
41 #include <tqpushbutton.h>
42 #include <tqregexp.h>
43 #include <tqstring.h>
44 
45 #include "geowidget.h"
46 
47 GeoWidget::GeoWidget( TDEABC::AddressBook *ab, TQWidget *parent, const char *name )
48  : KAB::ContactEditorWidget( ab, parent, name ), mReadOnly( false )
49 {
50  TQLabel *label = 0;
51 
52  TQGridLayout *topLayout = new TQGridLayout( this, 4, 3 );
53  topLayout->setMargin( KDialog::marginHint() );
54  topLayout->setSpacing( KDialog::spacingHint() );
55 
56  label = new TQLabel( this );
57  label->setPixmap( TDEGlobal::iconLoader()->loadIcon( "applications-internet",
58  TDEIcon::Desktop, TDEIcon::SizeMedium ) );
59  label->setAlignment( TQt::AlignTop );
60  topLayout->addMultiCellWidget( label, 0, 3, 0, 0 );
61 
62  mGeoIsValid = new TQCheckBox( i18n( "Use geo data" ), this );
63  topLayout->addMultiCellWidget( mGeoIsValid, 0, 0, 1, 2 );
64 
65  label = new TQLabel( i18n( "Latitude:" ), this );
66  topLayout->addWidget( label, 1, 1 );
67 
68  mLatitudeBox = new KDoubleSpinBox( -90, 90, 1, 0, 6, this );
69  mLatitudeBox->setEnabled( false );
70  mLatitudeBox->setSuffix( "°" );
71  topLayout->addWidget( mLatitudeBox, 1, 2 );
72  label->setBuddy( mLatitudeBox );
73 
74  label = new TQLabel( i18n( "Longitude:" ), this );
75  topLayout->addWidget( label, 2, 1 );
76 
77  mLongitudeBox = new KDoubleSpinBox( -180, 180, 1, 0, 6, this );
78  mLongitudeBox->setEnabled( false );
79  mLongitudeBox->setSuffix( "°" );
80  topLayout->addWidget( mLongitudeBox, 2, 2 );
81  label->setBuddy( mLongitudeBox );
82 
83  mExtendedButton = new TQPushButton( i18n( "Edit Geo Data..." ), this );
84  mExtendedButton->setEnabled( false );
85  topLayout->addMultiCellWidget( mExtendedButton, 3, 3, 1, 2 );
86 
87  connect( mLatitudeBox, TQ_SIGNAL( valueChanged( double ) ),
88  TQ_SLOT( setModified() ) );
89  connect( mLongitudeBox, TQ_SIGNAL( valueChanged( double ) ),
90  TQ_SLOT( setModified() ) );
91  connect( mExtendedButton, TQ_SIGNAL( clicked() ),
92  TQ_SLOT( editGeoData() ) );
93 
94  connect( mGeoIsValid, TQ_SIGNAL( toggled( bool ) ),
95  mLatitudeBox, TQ_SLOT( setEnabled( bool ) ) );
96  connect( mGeoIsValid, TQ_SIGNAL( toggled( bool ) ),
97  mLongitudeBox, TQ_SLOT( setEnabled( bool ) ) );
98  connect( mGeoIsValid, TQ_SIGNAL( toggled( bool ) ),
99  mExtendedButton, TQ_SLOT( setEnabled( bool ) ) );
100  connect( mGeoIsValid, TQ_SIGNAL( toggled( bool ) ),
101  TQ_SLOT( setModified() ) );
102 }
103 
104 GeoWidget::~GeoWidget()
105 {
106 }
107 
108 void GeoWidget::loadContact( TDEABC::Addressee *addr )
109 {
110  TDEABC::Geo geo = addr->geo();
111 
112  if ( geo.isValid() ) {
113  if ( !mReadOnly )
114  mGeoIsValid->setChecked( true );
115  mLatitudeBox->setValue( geo.latitude() );
116  mLongitudeBox->setValue( geo.longitude() );
117  } else
118  mGeoIsValid->setChecked( false );
119 }
120 
121 void GeoWidget::storeContact( TDEABC::Addressee *addr )
122 {
123  TDEABC::Geo geo;
124 
125  if ( mGeoIsValid->isChecked() ) {
126  geo.setLatitude( mLatitudeBox->value() );
127  geo.setLongitude( mLongitudeBox->value() );
128  } else {
129  geo.setLatitude( 91 );
130  geo.setLongitude( 181 );
131  }
132 
133  addr->setGeo( geo );
134 }
135 
136 void GeoWidget::setReadOnly( bool readOnly )
137 {
138  mReadOnly = readOnly;
139 
140  mGeoIsValid->setEnabled( !mReadOnly );
141 }
142 
143 void GeoWidget::editGeoData()
144 {
145  GeoDialog dlg( this );
146 
147  dlg.setLatitude( mLatitudeBox->value() );
148  dlg.setLongitude( mLongitudeBox->value() );
149 
150  if ( dlg.exec() ) {
151  mLatitudeBox->setValue( dlg.latitude() );
152  mLongitudeBox->setValue( dlg.longitude() );
153 
154  setModified( true );
155  }
156 }
157 
158 
159 
160 GeoDialog::GeoDialog( TQWidget *parent, const char *name )
161  : KDialogBase( Plain, i18n( "Geo Data Input" ), Ok | Cancel, Ok,
162  parent, name, true, true ),
163  mUpdateSexagesimalInput( true )
164 {
165  TQFrame *page = plainPage();
166 
167  TQGridLayout *topLayout = new TQGridLayout( page, 2, 2, marginHint(),
168  spacingHint() );
169  topLayout->setRowStretch( 1, 1 );
170 
171  mMapWidget = new GeoMapWidget( page );
172  topLayout->addMultiCellWidget( mMapWidget, 0, 1, 0, 0 );
173 
174  mCityCombo = new KComboBox( page );
175  topLayout->addWidget( mCityCombo, 0, 1 );
176 
177  TQGroupBox *sexagesimalGroup = new TQGroupBox( 0, TQt::Vertical, i18n( "Sexagesimal" ), page );
178  TQGridLayout *sexagesimalLayout = new TQGridLayout( sexagesimalGroup->layout(),
179  2, 5, spacingHint() );
180 
181  TQLabel *label = new TQLabel( i18n( "Latitude:" ), sexagesimalGroup );
182  sexagesimalLayout->addWidget( label, 0, 0 );
183 
184  mLatDegrees = new TQSpinBox( 0, 90, 1, sexagesimalGroup );
185  mLatDegrees->setSuffix( "°" );
186  mLatDegrees->setWrapping( false );
187  label->setBuddy( mLatDegrees );
188  sexagesimalLayout->addWidget( mLatDegrees, 0, 1 );
189 
190  mLatMinutes = new TQSpinBox( 0, 59, 1, sexagesimalGroup );
191  mLatMinutes->setSuffix( "'" );
192  sexagesimalLayout->addWidget( mLatMinutes, 0, 2 );
193 
194  mLatSeconds = new TQSpinBox( 0, 59, 1, sexagesimalGroup );
195  mLatSeconds->setSuffix( "\"" );
196  sexagesimalLayout->addWidget( mLatSeconds, 0, 3 );
197 
198  mLatDirection = new KComboBox( sexagesimalGroup );
199  mLatDirection->insertItem( i18n( "North" ) );
200  mLatDirection->insertItem( i18n( "South" ) );
201  sexagesimalLayout->addWidget( mLatDirection, 0, 4 );
202 
203  label = new TQLabel( i18n( "Longitude:" ), sexagesimalGroup );
204  sexagesimalLayout->addWidget( label, 1, 0 );
205 
206  mLongDegrees = new TQSpinBox( 0, 180, 1, sexagesimalGroup );
207  mLongDegrees->setSuffix( "°" );
208  label->setBuddy( mLongDegrees );
209  sexagesimalLayout->addWidget( mLongDegrees, 1, 1 );
210 
211  mLongMinutes = new TQSpinBox( 0, 59, 1, sexagesimalGroup );
212  mLongMinutes->setSuffix( "'" );
213  sexagesimalLayout->addWidget( mLongMinutes, 1, 2 );
214 
215  mLongSeconds = new TQSpinBox( 0, 59, 1, sexagesimalGroup );
216  mLongSeconds->setSuffix( "\"" );
217  sexagesimalLayout->addWidget( mLongSeconds, 1, 3 );
218 
219  mLongDirection = new KComboBox( sexagesimalGroup );
220  mLongDirection->insertItem( i18n( "East" ) );
221  mLongDirection->insertItem( i18n( "West" ) );
222  sexagesimalLayout->addWidget( mLongDirection, 1, 4 );
223 
224  topLayout->addWidget( sexagesimalGroup, 1, 1 );
225 
226  loadCityList();
227 
228  connect( mMapWidget, TQ_SIGNAL( changed() ),
229  TQ_SLOT( geoMapChanged() ) );
230  connect( mCityCombo, TQ_SIGNAL( activated( int ) ),
231  TQ_SLOT( cityInputChanged() ) );
232  connect( mLatDegrees, TQ_SIGNAL( valueChanged( int ) ),
233  TQ_SLOT( sexagesimalInputChanged() ) );
234  connect( mLatMinutes, TQ_SIGNAL( valueChanged( int ) ),
235  TQ_SLOT( sexagesimalInputChanged() ) );
236  connect( mLatSeconds, TQ_SIGNAL( valueChanged( int ) ),
237  TQ_SLOT( sexagesimalInputChanged() ) );
238  connect( mLatDirection, TQ_SIGNAL( activated( int ) ),
239  TQ_SLOT( sexagesimalInputChanged() ) );
240  connect( mLongDegrees, TQ_SIGNAL( valueChanged( int ) ),
241  TQ_SLOT( sexagesimalInputChanged() ) );
242  connect( mLongMinutes, TQ_SIGNAL( valueChanged( int ) ),
243  TQ_SLOT( sexagesimalInputChanged() ) );
244  connect( mLongSeconds, TQ_SIGNAL( valueChanged( int ) ),
245  TQ_SLOT( sexagesimalInputChanged() ) );
246  connect( mLongDirection, TQ_SIGNAL( activated( int ) ),
247  TQ_SLOT( sexagesimalInputChanged() ) );
248 
249  TDEAcceleratorManager::manage( this );
250 }
251 
252 GeoDialog::~GeoDialog()
253 {
254 }
255 
256 void GeoDialog::setLatitude( double latitude )
257 {
258  mLatitude = latitude;
259  updateInputs();
260 }
261 
262 double GeoDialog::latitude() const
263 {
264  return mLatitude;
265 }
266 
267 void GeoDialog::setLongitude( double longitude )
268 {
269  mLongitude = longitude;
270  updateInputs();
271 }
272 
273 double GeoDialog::longitude() const
274 {
275  return mLongitude;
276 }
277 
278 void GeoDialog::sexagesimalInputChanged()
279 {
280  mLatitude = (double)( mLatDegrees->value() + (double)mLatMinutes->value() /
281  60 + (double)mLatSeconds->value() / 3600 );
282 
283  mLatitude *= ( mLatDirection->currentItem() == 1 ? -1 : 1 );
284 
285  mLongitude = (double)( mLongDegrees->value() + (double)mLongMinutes->value() /
286  60 + (double)mLongSeconds->value() / 3600 );
287 
288  mLongitude *= ( mLongDirection->currentItem() == 1 ? -1 : 1 );
289 
290  mUpdateSexagesimalInput = false;
291 
292  updateInputs();
293 }
294 
295 void GeoDialog::geoMapChanged()
296 {
297  mLatitude = mMapWidget->latitude();
298  mLongitude = mMapWidget->longitude();
299 
300  updateInputs();
301 }
302 
303 void GeoDialog::cityInputChanged()
304 {
305  if ( mCityCombo->currentItem() != 0 ) {
306  GeoData data = mGeoDataMap[ mCityCombo->currentText() ];
307  mLatitude = data.latitude;
308  mLongitude = data.longitude;
309  } else
310  mLatitude = mLongitude = 0;
311 
312  updateInputs();
313 }
314 
315 void GeoDialog::updateInputs()
316 {
317  // hmm, doesn't look nice, but there is no better way AFAIK
318  mCityCombo->blockSignals( true );
319  mLatDegrees->blockSignals( true );
320  mLatMinutes->blockSignals( true );
321  mLatSeconds->blockSignals( true );
322  mLatDirection->blockSignals( true );
323  mLongDegrees->blockSignals( true );
324  mLongMinutes->blockSignals( true );
325  mLongSeconds->blockSignals( true );
326  mLongDirection->blockSignals( true );
327 
328  mMapWidget->setLatitude( mLatitude );
329  mMapWidget->setLongitude( mLongitude );
330  mMapWidget->update();
331 
332  if ( mUpdateSexagesimalInput ) {
333  int degrees, minutes, seconds;
334  double latitude = mLatitude;
335  double longitude = mLongitude;
336 
337  latitude *= ( mLatitude < 0 ? -1 : 1 );
338  longitude *= ( mLongitude < 0 ? -1 : 1 );
339 
340  degrees = (int)( latitude * 1 );
341  minutes = (int)( ( latitude - degrees ) * 60 );
342  seconds = (int)( (double)( (double)latitude - (double)degrees - ( (double)minutes / (double)60 ) ) * (double)3600 );
343 
344  mLatDegrees->setValue( degrees );
345  mLatMinutes->setValue( minutes );
346  mLatSeconds->setValue( seconds );
347 
348  mLatDirection->setCurrentItem( mLatitude < 0 ? 1 : 0 );
349 
350  degrees = (int)( longitude * 1 );
351  minutes = (int)( ( longitude - degrees ) * 60 );
352  seconds = (int)( (double)( longitude - (double)degrees - ( (double)minutes / 60 ) ) * 3600 );
353 
354  mLongDegrees->setValue( degrees );
355  mLongMinutes->setValue( minutes );
356  mLongSeconds->setValue( seconds );
357  mLongDirection->setCurrentItem( mLongitude < 0 ? 1 : 0 );
358  }
359  mUpdateSexagesimalInput = true;
360 
361  int pos = nearestCity( mLongitude, mLatitude );
362  if ( pos != -1 )
363  mCityCombo->setCurrentItem( pos + 1 );
364  else
365  mCityCombo->setCurrentItem( 0 );
366 
367  mCityCombo->blockSignals( false );
368  mLatDegrees->blockSignals( false );
369  mLatMinutes->blockSignals( false );
370  mLatSeconds->blockSignals( false );
371  mLatDirection->blockSignals( false );
372  mLongDegrees->blockSignals( false );
373  mLongMinutes->blockSignals( false );
374  mLongSeconds->blockSignals( false );
375  mLongDirection->blockSignals( false );
376 }
377 
378 void GeoDialog::loadCityList()
379 {
380  mCityCombo->clear();
381  mGeoDataMap.clear();
382 
383  TQFile file( locate( "data", "kaddressbook/zone.tab" ) );
384 
385  if ( file.open( IO_ReadOnly ) ) {
386  TQTextStream s( &file );
387 
388  TQString line, country;
389  TQRegExp coord( "[+-]\\d+[+-]\\d+" );
390  TQRegExp name( "[^\\s]+/[^\\s]+" );
391  int pos;
392 
393  while ( !s.eof() ) {
394  line = s.readLine().stripWhiteSpace();
395  if ( line.isEmpty() || line[ 0 ] == '#' )
396  continue;
397 
398  country = line.left( 2 );
399  TQString c, n;
400  pos = coord.search( line, 0 );
401  if ( pos >= 0 )
402  c = line.mid( pos, coord.matchedLength() );
403 
404  pos = name.search(line, pos);
405  if ( pos > 0 ) {
406  n = line.mid( pos, name.matchedLength() ).stripWhiteSpace();
407  n.replace( '_', " " );
408  }
409 
410  if ( !c.isEmpty() && !n.isEmpty() ) {
411  pos = c.find( "+", 1 );
412  if ( pos < 0 )
413  pos = c.find( "-", 1 );
414  if ( pos > 0 ) {
415  GeoData data;
416  data.latitude = calculateCoordinate( c.left( pos ) );
417  data.longitude = calculateCoordinate( c.mid( pos ) );
418  data.country = country;
419 
420  mGeoDataMap.insert( n, data );
421  }
422  }
423  }
424  TQStringList items( mGeoDataMap.keys() );
425  items.prepend( i18n( "Undefined" ) );
426  mCityCombo->insertStringList( items );
427 
428  file.close();
429  }
430 }
431 
432 double GeoDialog::calculateCoordinate( const TQString &coordinate ) const
433 {
434  int neg;
435  int d = 0, m = 0, s = 0;
436  TQString str = coordinate;
437 
438  neg = str.left( 1 ) == "-";
439  str.remove( 0, 1 );
440 
441  switch ( str.length() ) {
442  case 4:
443  d = str.left( 2 ).toInt();
444  m = str.mid( 2 ).toInt();
445  break;
446  case 5:
447  d = str.left( 3 ).toInt();
448  m = str.mid( 3 ).toInt();
449  break;
450  case 6:
451  d = str.left( 2 ).toInt();
452  m = str.mid( 2, 2 ).toInt();
453  s = str.right( 2 ).toInt();
454  break;
455  case 7:
456  d = str.left( 3 ).toInt();
457  m = str.mid( 3, 2 ).toInt();
458  s = str.right( 2 ).toInt();
459  break;
460  default:
461  break;
462  }
463 
464  if ( neg )
465  return - ( d + m / 60.0 + s / 3600.0 );
466  else
467  return d + m / 60.0 + s / 3600.0;
468 }
469 
470 int GeoDialog::nearestCity( double x, double y ) const
471 {
472  TQMap<TQString, GeoData>::ConstIterator it;
473  int pos = 0;
474  for ( it = mGeoDataMap.begin(); it != mGeoDataMap.end(); ++it, ++pos ) {
475  double dist = ( (*it).longitude - x ) * ( (*it).longitude - x ) +
476  ( (*it).latitude - y ) * ( (*it).latitude - y );
477  if ( dist < 1.5 )
478  return pos;
479  }
480 
481  return -1;
482 }
483 
484 
485 GeoMapWidget::GeoMapWidget( TQWidget *parent, const char *name )
486  : TQWidget( parent, name ), mLatitude( 0 ), mLongitude( 0 )
487 {
488  setBackgroundMode( NoBackground );
489 
490  setFixedSize( 400, 200 );
491 
492  update();
493 }
494 
495 GeoMapWidget::~GeoMapWidget()
496 {
497 }
498 
499 void GeoMapWidget::setLatitude( double latitude )
500 {
501  mLatitude = latitude;
502 }
503 
504 double GeoMapWidget::latitude()const
505 {
506  return mLatitude;
507 }
508 
509 void GeoMapWidget::setLongitude( double longitude )
510 {
511  mLongitude = longitude;
512 }
513 
514 double GeoMapWidget::longitude()const
515 {
516  return mLongitude;
517 }
518 
519 void GeoMapWidget::mousePressEvent( TQMouseEvent *event )
520 {
521  double latMid = height() / 2;
522  double longMid = width() / 2;
523 
524  double latOffset = latMid - event->y();
525  double longOffset = event->x() - longMid;
526 
527  mLatitude = ( latOffset * 90 ) / latMid;
528  mLongitude = ( longOffset * 180 ) / longMid;
529 
530  emit changed();
531 }
532 
533 void GeoMapWidget::paintEvent( TQPaintEvent* )
534 {
535  uint w = width();
536  uint h = height();
537 
538  TQPixmap pm( w, h );
539  TQPainter p;
540  p.begin( &pm, this );
541 
542  p.setPen( TQColor( 255, 0, 0 ) );
543  p.setBrush( TQColor( 255, 0, 0 ) );
544 
545  TQPixmap world( locate( "data", "kaddressbook/pics/world.jpg" ) );
546  p.drawPixmap( 0, 0, world );
547 
548  double latMid = h / 2;
549  double longMid = w / 2;
550 
551  double latOffset = ( mLatitude * latMid ) / 90;
552  double longOffset = ( mLongitude * longMid ) / 180;
553 
554  int x = (int)(longMid + longOffset);
555  int y = (int)(latMid - latOffset);
556  p.drawEllipse( x, y, 4, 4 );
557 
558  p.end();
559  bitBlt( this, 0, 0, &pm );
560 }
561 
562 #include "geowidget.moc"