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 <tdestandarddirs.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
47GeoWidget::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
104GeoWidget::~GeoWidget()
105{
106}
107
108void 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
121void 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
136void GeoWidget::setReadOnly( bool readOnly )
137{
138 mReadOnly = readOnly;
139
140 mGeoIsValid->setEnabled( !mReadOnly );
141}
142
143void 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
160GeoDialog::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
252GeoDialog::~GeoDialog()
253{
254}
255
256void GeoDialog::setLatitude( double latitude )
257{
258 mLatitude = latitude;
259 updateInputs();
260}
261
262double GeoDialog::latitude() const
263{
264 return mLatitude;
265}
266
267void GeoDialog::setLongitude( double longitude )
268{
269 mLongitude = longitude;
270 updateInputs();
271}
272
273double GeoDialog::longitude() const
274{
275 return mLongitude;
276}
277
278void 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
295void GeoDialog::geoMapChanged()
296{
297 mLatitude = mMapWidget->latitude();
298 mLongitude = mMapWidget->longitude();
299
300 updateInputs();
301}
302
303void 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
315void 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
378void 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
432double 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
470int 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
485GeoMapWidget::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
495GeoMapWidget::~GeoMapWidget()
496{
497}
498
499void GeoMapWidget::setLatitude( double latitude )
500{
501 mLatitude = latitude;
502}
503
504double GeoMapWidget::latitude()const
505{
506 return mLatitude;
507}
508
509void GeoMapWidget::setLongitude( double longitude )
510{
511 mLongitude = longitude;
512}
513
514double GeoMapWidget::longitude()const
515{
516 return mLongitude;
517}
518
519void 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
533void 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"