kaddressbook

customfieldswidget.cpp
1 /*
2  This file is part of KAddressbook.
3  Copyright (c) 2004 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 <tqcheckbox.h>
25 #include <tqdatetimeedit.h>
26 #include <tqframe.h>
27 #include <tqlabel.h>
28 #include <tqlayout.h>
29 #include <tqpushbutton.h>
30 #include <tqvalidator.h>
31 #include <tqspinbox.h>
32 
33 #include <tdeaccelmanager.h>
34 #include <kcombobox.h>
35 #include <kinputdialog.h>
36 #include <klineedit.h>
37 #include <tdemessagebox.h>
38 
39 #include "addresseeconfig.h"
40 #include "kabprefs.h"
41 
42 #include "customfieldswidget.h"
43 
44 
45 AddFieldDialog::AddFieldDialog( TQWidget *parent, const char *name )
46  : KDialogBase( Plain, i18n( "Add Field" ), Ok | Cancel,
47  Ok, parent, name, true, true )
48 {
49  TQWidget *page = plainPage();
50 
51  TQGridLayout *layout = new TQGridLayout( page, 3, 2, marginHint(), spacingHint() );
52 
53  TQLabel *label = new TQLabel( i18n( "Title:" ), page );
54  layout->addWidget( label, 0, 0 );
55 
56  mTitle = new KLineEdit( page );
57  mTitle->setValidator( new TQRegExpValidator( TQRegExp( "([a-zA-Z]|\\d|-)+" ), mTitle ) );
58  label->setBuddy( mTitle );
59  layout->addWidget( mTitle, 0, 1 );
60 
61  label = new TQLabel( i18n( "Type:" ), page );
62  layout->addWidget( label, 1, 0 );
63 
64  mType = new KComboBox( page );
65  label->setBuddy( mType );
66  layout->addWidget( mType, 1, 1 );
67 
68  mGlobal = new TQCheckBox( i18n( "Is available for all contacts" ), page );
69  mGlobal->setChecked( true );
70  layout->addMultiCellWidget( mGlobal, 2, 2, 0, 1 );
71 
72  connect( mTitle, TQ_SIGNAL( textChanged( const TQString& ) ),
73  this, TQ_SLOT( nameChanged( const TQString& ) ) );
74 
75  TDEAcceleratorManager::manage( this );
76 
77  mTypeList.append( "text" );
78  mTypeName.append( i18n( "Text" ) );
79  mTypeList.append( "integer" );
80  mTypeName.append( i18n( "Numeric Value" ) );
81  mTypeList.append( "boolean" );
82  mTypeName.append( i18n( "Boolean" ) );
83  mTypeList.append( "date" );
84  mTypeName.append( i18n( "Date" ) );
85  mTypeList.append( "time" );
86  mTypeName.append( i18n( "Time" ) );
87  mTypeList.append( "datetime" );
88  mTypeName.append( i18n( "Date & Time" ) );
89 
90  for ( uint i = 0; i < mTypeName.count(); ++i )
91  mType->insertItem( mTypeName[ i ] );
92 
93  nameChanged( "" );
94 
95  mTitle->setFocus();
96 }
97 
98 TQString AddFieldDialog::title() const
99 {
100  return mTitle->text();
101 }
102 
103 TQString AddFieldDialog::identifier() const
104 {
105  TQString id = mTitle->text().lower();
106  return id.replace( ",", "_" ).replace( " ", "_" );
107 }
108 
109 TQString AddFieldDialog::type() const
110 {
111  return mTypeList[ mType->currentItem() ];
112 }
113 
114 bool AddFieldDialog::isGlobal() const
115 {
116  return mGlobal->isChecked();
117 }
118 
119 void AddFieldDialog::nameChanged( const TQString &name )
120 {
121  enableButton( Ok, !name.isEmpty() );
122 }
123 
124 FieldWidget::FieldWidget( TQWidget *parent, const char *name )
125  : TQWidget( parent, name )
126 {
127  TQVBoxLayout *layout = new TQVBoxLayout( this, KDialog::marginHint(),
128  KDialog::spacingHint() );
129 
130  mGlobalLayout = new TQVBoxLayout( layout, KDialog::spacingHint() );
131  mGlobalLayout->setAlignment( TQt::AlignTop );
132 
133  mSeparator = new TQFrame( this );
134  mSeparator->setFrameStyle( TQFrame::HLine | TQFrame::Sunken );
135  mSeparator->hide();
136  layout->addWidget( mSeparator );
137 
138  mLocalLayout = new TQVBoxLayout( layout, KDialog::spacingHint() );
139  mLocalLayout->setAlignment( TQt::AlignTop );
140 }
141 
142 void FieldWidget::addField( const TQString &identifier, const TQString &title,
143  const TQString &type, bool isGlobal )
144 {
145  FieldRecord record;
146 
147  record.mIdentifier = identifier;
148  record.mTitle = title;
149  record.mLabel = new TQLabel( title + ":", this );
150  record.mGlobal = isGlobal;
151  if ( type == "integer" ) {
152  TQSpinBox *wdg = new TQSpinBox( 0, 1000, 1, this );
153  record.mWidget = wdg;
154  connect( wdg, TQ_SIGNAL( valueChanged( int ) ),
155  this, TQ_SIGNAL( changed() ) );
156  } else if ( type == "boolean" ) {
157  TQCheckBox *wdg = new TQCheckBox( this );
158  record.mWidget = wdg;
159  connect( wdg, TQ_SIGNAL( toggled( bool ) ),
160  this, TQ_SIGNAL( changed() ) );
161  } else if ( type == "date" ) {
162  TQDateEdit *wdg = new TQDateEdit( this );
163  record.mWidget = wdg;
164  connect( wdg, TQ_SIGNAL( valueChanged( const TQDate& ) ),
165  this, TQ_SIGNAL( changed() ) );
166  } else if ( type == "time" ) {
167  TQTimeEdit *wdg = new TQTimeEdit( this );
168  record.mWidget = wdg;
169  connect( wdg, TQ_SIGNAL( valueChanged( const TQTime& ) ),
170  this, TQ_SIGNAL( changed() ) );
171  } else if ( type == "datetime" ) {
172  TQDateTimeEdit *wdg = new TQDateTimeEdit( this );
173  record.mWidget = wdg;
174  connect( wdg, TQ_SIGNAL( valueChanged( const TQDateTime& ) ),
175  this, TQ_SIGNAL( changed() ) );
176  } else if ( type == "text" ) {
177  TQLineEdit *wdg = new TQLineEdit( this );
178  record.mWidget = wdg;
179  connect( wdg, TQ_SIGNAL( textChanged( const TQString& ) ),
180  this, TQ_SIGNAL( changed() ) );
181  }
182 
183  record.mLabel->show();
184  record.mWidget->show();
185 
186  if ( isGlobal ) {
187  record.mLayout = new TQHBoxLayout( mGlobalLayout );
188  record.mLayout->addWidget( record.mLabel );
189  record.mLayout->addWidget( record.mWidget, TQt::AlignLeft );
190  } else {
191  record.mLayout = new TQHBoxLayout( mLocalLayout );
192  record.mLayout->addWidget( record.mLabel );
193  record.mLayout->addWidget( record.mWidget, TQt::AlignLeft );
194  mSeparator->show();
195  }
196 
197  mFieldList.append( record );
198 
199  recalculateLayout();
200 }
201 
202 void FieldWidget::removeField( const TQString &identifier )
203 {
204  FieldRecordList::Iterator it;
205  for ( it = mFieldList.begin(); it != mFieldList.end(); ++it ) {
206  if ( (*it).mIdentifier == identifier ) {
207  delete (*it).mLabel;
208  delete (*it).mWidget;
209  delete (*it).mLayout;
210 
211  mFieldList.remove( it );
212  recalculateLayout();
213 
214  bool hasLocal = false;
215  for ( it = mFieldList.begin(); it != mFieldList.end(); ++it )
216  hasLocal = hasLocal || !(*it).mGlobal;
217 
218  if ( !hasLocal )
219  mSeparator->hide();
220 
221  return;
222  }
223  }
224 }
225 
226 void FieldWidget::clearFields()
227 {
228  FieldRecordList::ConstIterator fieldIt;
229  for ( fieldIt = mFieldList.begin(); fieldIt != mFieldList.end(); ++fieldIt ) {
230  if ( (*fieldIt).mWidget->isA( "TQLineEdit" ) ) {
231  TQLineEdit *wdg = static_cast<TQLineEdit*>( (*fieldIt).mWidget );
232  wdg->setText( TQString() );
233  } else if ( (*fieldIt).mWidget->isA( "TQSpinBox" ) ) {
234  TQSpinBox *wdg = static_cast<TQSpinBox*>( (*fieldIt).mWidget );
235  wdg->setValue( 0 );
236  } else if ( (*fieldIt).mWidget->isA( "TQCheckBox" ) ) {
237  TQCheckBox *wdg = static_cast<TQCheckBox*>( (*fieldIt).mWidget );
238  wdg->setChecked( true );
239  } else if ( (*fieldIt).mWidget->isA( "TQDateEdit" ) ) {
240  TQDateEdit *wdg = static_cast<TQDateEdit*>( (*fieldIt).mWidget );
241  wdg->setDate( TQDate::currentDate() );
242  } else if ( (*fieldIt).mWidget->isA( "TQTimeEdit" ) ) {
243  TQTimeEdit *wdg = static_cast<TQTimeEdit*>( (*fieldIt).mWidget );
244  wdg->setTime( TQTime::currentTime() );
245  } else if ( (*fieldIt).mWidget->isA( "TQDateTimeEdit" ) ) {
246  TQDateTimeEdit *wdg = static_cast<TQDateTimeEdit*>( (*fieldIt).mWidget );
247  wdg->setDateTime( TQDateTime::currentDateTime() );
248  }
249  }
250 }
251 
252 void FieldWidget::loadContact( TDEABC::Addressee *addr )
253 {
254  const TQStringList customs = addr->customs();
255 
256  clearFields();
257 
258  TQStringList::ConstIterator it;
259  for ( it = customs.begin(); it != customs.end(); ++it ) {
260  TQString app, name, value;
261  splitField( *it, app, name, value );
262  if ( app != "KADDRESSBOOK" )
263  continue;
264 
265  FieldRecordList::ConstIterator fieldIt;
266  for ( fieldIt = mFieldList.begin(); fieldIt != mFieldList.end(); ++fieldIt ) {
267  if ( (*fieldIt).mIdentifier == name ) {
268  if ( (*fieldIt).mWidget->isA( "TQLineEdit" ) ) {
269  TQLineEdit *wdg = static_cast<TQLineEdit*>( (*fieldIt).mWidget );
270  wdg->setText( value );
271  } else if ( (*fieldIt).mWidget->isA( "TQSpinBox" ) ) {
272  TQSpinBox *wdg = static_cast<TQSpinBox*>( (*fieldIt).mWidget );
273  wdg->setValue( value.toInt() );
274  } else if ( (*fieldIt).mWidget->isA( "TQCheckBox" ) ) {
275  TQCheckBox *wdg = static_cast<TQCheckBox*>( (*fieldIt).mWidget );
276  wdg->setChecked( value == "true" || value == "1" );
277  } else if ( (*fieldIt).mWidget->isA( "TQDateEdit" ) ) {
278  TQDateEdit *wdg = static_cast<TQDateEdit*>( (*fieldIt).mWidget );
279  wdg->setDate( TQDate::fromString( value, TQt::ISODate ) );
280  } else if ( (*fieldIt).mWidget->isA( "TQTimeEdit" ) ) {
281  TQTimeEdit *wdg = static_cast<TQTimeEdit*>( (*fieldIt).mWidget );
282  wdg->setTime( TQTime::fromString( value, TQt::ISODate ) );
283  } else if ( (*fieldIt).mWidget->isA( "TQDateTimeEdit" ) ) {
284  TQDateTimeEdit *wdg = static_cast<TQDateTimeEdit*>( (*fieldIt).mWidget );
285  wdg->setDateTime( TQDateTime::fromString( value, TQt::ISODate ) );
286  }
287  }
288  }
289  }
290 }
291 
292 void FieldWidget::setReadOnly( bool readOnly )
293 {
294  FieldRecordList::ConstIterator it;
295  for ( it = mFieldList.begin(); it != mFieldList.end(); ++it ) {
296  TQString value;
297  if ( (*it).mWidget->isA( "TQLineEdit" ) ) {
298  TQLineEdit *wdg = static_cast<TQLineEdit*>( (*it).mWidget );
299  wdg->setReadOnly(readOnly);
300  } else if ( (*it).mWidget->isA( "TQSpinBox" ) ) {
301  TQSpinBox *wdg = static_cast<TQSpinBox*>( (*it).mWidget );
302  wdg->setEnabled( !readOnly );
303  } else if ( (*it).mWidget->isA( "TQCheckBox" ) ) {
304  TQCheckBox *wdg = static_cast<TQCheckBox*>( (*it).mWidget );
305  wdg->setEnabled( !readOnly );
306  } else if ( (*it).mWidget->isA( "TQDateEdit" ) ) {
307  TQDateEdit *wdg = static_cast<TQDateEdit*>( (*it).mWidget );
308  wdg->setEnabled( !readOnly );
309  } else if ( (*it).mWidget->isA( "TQTimeEdit" ) ) {
310  TQTimeEdit *wdg = static_cast<TQTimeEdit*>( (*it).mWidget );
311  wdg->setEnabled( !readOnly );
312  } else if ( (*it).mWidget->isA( "TQDateTimeEdit" ) ) {
313  TQDateTimeEdit *wdg = static_cast<TQDateTimeEdit*>( (*it).mWidget );
314  wdg->setEnabled( !readOnly );
315  }
316  }
317 }
318 
319 void FieldWidget::storeContact( TDEABC::Addressee *addr )
320 {
321  FieldRecordList::ConstIterator it;
322  for ( it = mFieldList.begin(); it != mFieldList.end(); ++it ) {
323  TQString value;
324  if ( (*it).mWidget->isA( "TQLineEdit" ) ) {
325  TQLineEdit *wdg = static_cast<TQLineEdit*>( (*it).mWidget );
326  value = wdg->text();
327  } else if ( (*it).mWidget->isA( "TQSpinBox" ) ) {
328  TQSpinBox *wdg = static_cast<TQSpinBox*>( (*it).mWidget );
329  value = TQString::number( wdg->value() );
330  } else if ( (*it).mWidget->isA( "TQCheckBox" ) ) {
331  TQCheckBox *wdg = static_cast<TQCheckBox*>( (*it).mWidget );
332  value = ( wdg->isChecked() ? "true" : "false" );
333  } else if ( (*it).mWidget->isA( "TQDateEdit" ) ) {
334  TQDateEdit *wdg = static_cast<TQDateEdit*>( (*it).mWidget );
335  value = wdg->date().toString( TQt::ISODate );
336  } else if ( (*it).mWidget->isA( "TQTimeEdit" ) ) {
337  TQTimeEdit *wdg = static_cast<TQTimeEdit*>( (*it).mWidget );
338  value = wdg->time().toString( TQt::ISODate );
339  } else if ( (*it).mWidget->isA( "TQDateTimeEdit" ) ) {
340  TQDateTimeEdit *wdg = static_cast<TQDateTimeEdit*>( (*it).mWidget );
341  value = wdg->dateTime().toString( TQt::ISODate );
342  }
343 
344  if ( value.isEmpty() )
345  addr->removeCustom( "KADDRESSBOOK", (*it).mIdentifier );
346  else
347  addr->insertCustom( "KADDRESSBOOK", (*it).mIdentifier, value );
348  }
349 }
350 
351 void FieldWidget::removeLocalFields()
352 {
353  FieldRecordList::Iterator it;
354  for ( it = mFieldList.begin(); it != mFieldList.end(); ++it ) {
355  if ( !(*it).mGlobal ) {
356  delete (*it).mLabel;
357  delete (*it).mWidget;
358  delete (*it).mLayout;
359 
360  it = mFieldList.remove( it );
361  it--;
362  recalculateLayout();
363  }
364  }
365 }
366 
367 void FieldWidget::recalculateLayout()
368 {
369  int maxWidth = 0;
370 
371  FieldRecordList::ConstIterator it;
372  for ( it = mFieldList.begin(); it != mFieldList.end(); ++it )
373  maxWidth = TQMAX( maxWidth, (*it).mLabel->minimumSizeHint().width() );
374 
375  for ( it = mFieldList.begin(); it != mFieldList.end(); ++it )
376  (*it).mLabel->setMinimumWidth( maxWidth );
377 }
378 
379 CustomFieldsWidget::CustomFieldsWidget( TDEABC::AddressBook *ab,
380  TQWidget *parent, const char *name )
381  : KAB::ContactEditorWidget( ab, parent, name )
382 {
383  initGUI();
384 
385  connect( mAddButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( addField() ) );
386  connect( mRemoveButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( removeField() ) );
387 
388  connect( mFieldWidget, TQ_SIGNAL( changed() ), this, TQ_SLOT( setModified() ) );
389 }
390 
391 void CustomFieldsWidget::loadContact( TDEABC::Addressee *addr )
392 {
393  mAddressee = *addr;
394 
395  mFieldWidget->removeLocalFields();
396 
397  AddresseeConfig addrConfig( mAddressee );
398  TQStringList fields = addrConfig.customFields();
399 
400  if ( !fields.isEmpty() ) {
401  for ( uint i = 0; i < fields.count(); i += 3 ) {
402  mFieldWidget->addField( fields[ i ], fields[ i + 1 ],
403  fields[ i + 2 ] , false );
404  mRemoveButton->setEnabled( true );
405  }
406  }
407 
408  mFieldWidget->loadContact( addr );
409 }
410 
411 void CustomFieldsWidget::storeContact( TDEABC::Addressee *addr )
412 {
413  mFieldWidget->storeContact( addr );
414 }
415 
416 void CustomFieldsWidget::setReadOnly( bool readOnly )
417 {
418  mAddButton->setEnabled( !readOnly );
419  mRemoveButton->setEnabled( !readOnly && !mFieldWidget->fields().isEmpty() );
420  mFieldWidget->setReadOnly( readOnly );
421 }
422 
423 void CustomFieldsWidget::addField()
424 {
425  AddFieldDialog dlg( this );
426 
427  if ( dlg.exec() ) {
428  FieldRecordList list = mFieldWidget->fields();
429 
430  FieldRecordList::ConstIterator it;
431  for ( it = list.begin(); it != list.end(); ++it )
432  if ( (*it).mIdentifier == dlg.identifier() ) {
433  KMessageBox::sorry( this, i18n( "A field with the same name already exists, please choose another one." ) );
434  return;
435  }
436 
437  mFieldWidget->addField( dlg.identifier(), dlg.title(),
438  dlg.type(), dlg.isGlobal() );
439 
440  if ( dlg.isGlobal() ) {
441  KABPrefs::instance()->setGlobalCustomFields( marshallFields( true ) );
442  } else {
443  AddresseeConfig addrConfig( mAddressee );
444  addrConfig.setCustomFields( marshallFields( false ) );
445  }
446 
447  mRemoveButton->setEnabled( true );
448  }
449 }
450 
451 void CustomFieldsWidget::removeField()
452 {
453  const FieldRecordList list = mFieldWidget->fields();
454 
455  TQStringList fields;
456 
457  FieldRecordList::ConstIterator it;
458  for ( it = list.begin(); it != list.end(); ++it )
459  fields.append( (*it).mTitle );
460 
461  bool ok;
462  TQString title = KInputDialog::getItem( i18n( "Remove Field" ),
463  i18n( "Select the field you want to remove:" ),
464  fields, 0, false, &ok, this );
465 
466  if ( ok ) {
467  for ( it = list.begin(); it != list.end(); ++it )
468  if ( (*it).mTitle == title ) {
469  mFieldWidget->removeField( (*it).mIdentifier );
470 
471  if ( list.count() == 1 )
472  mRemoveButton->setEnabled( false );
473 
474  if ( (*it).mGlobal ) {
475  KABPrefs::instance()->setGlobalCustomFields( marshallFields( true ) );
476  } else {
477  AddresseeConfig addrConfig( mAddressee );
478  addrConfig.setCustomFields( marshallFields( false ) );
479  }
480 
481  return;
482  }
483  }
484 }
485 
486 void CustomFieldsWidget::initGUI()
487 {
488  TQGridLayout *layout = new TQGridLayout( this, 2, 3, KDialog::marginHint(),
489  KDialog::spacingHint() );
490 
491  mFieldWidget = new FieldWidget( this );
492  layout->addMultiCellWidget( mFieldWidget, 0, 0, 0, 2 );
493 
494  mAddButton = new TQPushButton( i18n( "Add Field..." ), this );
495  layout->addWidget( mAddButton, 1, 1, TQt::AlignRight );
496 
497  mRemoveButton = new TQPushButton( i18n( "Remove Field..." ), this );
498  mRemoveButton->setEnabled( false );
499  layout->addWidget( mRemoveButton, 1, 2, TQt::AlignRight );
500 
501  // load global fields
502  TQStringList globalFields = KABPrefs::instance()->globalCustomFields();
503 
504  if ( globalFields.isEmpty() )
505  return;
506 
507  for ( uint i = 0; i < globalFields.count(); i += 3 ) {
508  mFieldWidget->addField( globalFields[ i ], globalFields[ i + 1 ],
509  globalFields[ i + 2 ] , true );
510  mRemoveButton->setEnabled( true );
511  }
512 }
513 
514 TQStringList CustomFieldsWidget::marshallFields( bool global ) const
515 {
516  TQStringList retval;
517 
518  const FieldRecordList list = mFieldWidget->fields();
519  FieldRecordList::ConstIterator it;
520  for ( it = list.begin(); it != list.end(); ++it ) {
521  if ( (*it).mGlobal == global ) {
522  retval.append( (*it).mIdentifier );
523  retval.append( (*it).mTitle );
524 
525  TQString type = "text";
526  if ( (*it).mWidget->isA( "TQSpinBox" ) ) {
527  type = "integer";
528  } else if ( (*it).mWidget->isA( "TQCheckBox" ) ) {
529  type = "boolean";
530  } else if ( (*it).mWidget->isA( "TQDateEdit" ) ) {
531  type = "date";
532  } else if ( (*it).mWidget->isA( "TQTimeEdit" ) ) {
533  type = "time";
534  } else if ( (*it).mWidget->isA( "TQDateTimeEdit" ) ) {
535  type = "datetime";
536  } else if ( (*it).mWidget->isA( "TQLineEdit" ) ) {
537  type = "text";
538  }
539 
540  retval.append( type );
541  }
542  }
543 
544  return retval;
545 }
546 
547 
548 void splitField( const TQString &str, TQString &app, TQString &name, TQString &value )
549 {
550  int colon = str.find( ':' );
551  if ( colon != -1 ) {
552  TQString tmp = str.left( colon );
553  value = str.mid( colon + 1 );
554 
555  int dash = tmp.find( '-' );
556  if ( dash != -1 ) {
557  app = tmp.left( dash );
558  name = tmp.mid( dash + 1 );
559  }
560  }
561 }
562 
563 #include "customfieldswidget.moc"