Index: akonadi/contact/customfieldmanager.cpp =================================================================== --- akonadi/contact/customfieldmanager.cpp (révision 0) +++ akonadi/contact/customfieldmanager.cpp (révision 1101153) @@ -0,0 +1,65 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "customfieldmanager_p.h" + +#include <kconfig.h> +#include <kconfiggroup.h> + +void CustomFieldManager::setGlobalCustomFieldDescriptions( const CustomField::List &customFields ) +{ + KConfig config( QLatin1String( "akonadi_contactrc" ) ); + KConfigGroup group( &config, QLatin1String( "GlobalCustomFields" ) ); + + group.deleteGroup(); + foreach ( const CustomField &field, customFields ) { + const QString key = field.key(); + const QString value = CustomField::typeToString( field.type() ) + QLatin1Char( ':' ) + field.title(); + + group.writeEntry( key, value ); + } +} + +CustomField::List CustomFieldManager::globalCustomFieldDescriptions() +{ + KConfig config( QLatin1String( "akonadi_contactrc" ) ); + const KConfigGroup group( &config, QLatin1String( "GlobalCustomFields" ) ); + + CustomField::List customFields; + + const QStringList keys = group.keyList(); + foreach ( const QString &key, keys ) { + CustomField field; + field.setKey( key ); + field.setScope( CustomField::GlobalScope ); + + const QString value = group.readEntry( key, QString() ); + const int pos = value.indexOf( QLatin1Char( ':' ) ); + if ( pos != -1 ) { + field.setType( CustomField::stringToType( value.left( pos - 1 ) ) ); + field.setTitle( value.mid( pos + 1 ) ); + } + + customFields << field; + } + + return customFields; +} Index: akonadi/contact/contactmetadata_p.h =================================================================== --- akonadi/contact/contactmetadata_p.h (révision 1101152) +++ akonadi/contact/contactmetadata_p.h (révision 1101153) @@ -23,6 +23,7 @@ #define AKONADI_CONTACTMETADATA_P_H #include <QtCore/QStringList> +#include <QtCore/QVariant> namespace Akonadi { @@ -67,6 +68,29 @@ */ int displayNameMode() const; + /** + * Sets the @p descriptions of the custom fields of that contact. + * + * The description list contains a QVariantMap for each custom field + * with the following keys: + * - key (string) The identifier of the field + * - title (string) The i18n'ed title of the field + * - type (string) The type description of the field + * Possible values for type description are + * - text + * - numeric + * - boolean + * - date + * - time + * - datetime + */ + void setCustomFieldDescriptions( const QVariantList &descriptions ); + + /** + * Returns the descriptions of the custom fields of the contact. + */ + QVariantList customFieldDescriptions() const; + private: //@cond PRIVATE Q_DISABLE_COPY( ContactMetaData ) Index: akonadi/contact/customfields_p.h =================================================================== --- akonadi/contact/customfields_p.h (révision 0) +++ akonadi/contact/customfields_p.h (révision 1101153) @@ -0,0 +1,100 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef CUSTOMFIELDS_P_H +#define CUSTOMFIELDS_P_H + +#include <QtCore/QList> +#include <QtCore/QString> +#include <QtCore/QVariant> + +/** + * @short A class that represents non-standard contact fields. + * + * There exists three scopes of fields. To the local scope belong all + * custom fields that are defined by the user and that exists only for one + * contact. The description for these fields are stored inside ContactMetaData + * as custom attribute of the Akonadi item that represents the contact. + * To the global scope belong all custom fields that are defined by the user but + * shall be available in all contacts of the address book. Their description + * is stored by CustomFieldManager in $HOME/.kde/share/config/akonadi_contactrc. + * All other custom fields belong to the external scope, they come with import + * of contacts from other PIM applications (e.g. further X- entries in vCards). + * Their description is created on the fly when editing the custom fields. + * + * The description of a custom field covers the key, title and type. + */ +class CustomField +{ + public: + typedef QList<CustomField> List; + + enum Type + { + TextType, + NumericType, + BooleanType, + DateType, + TimeType, + DateTimeType + }; + + enum Scope + { + LocalScope, ///< Field has been defined by user for one contact + GlobalScope, ///< Field has been defined by user for all contacts + ExternalScope ///< Field has been defined by the external data source (e.g. vCard) + }; + + CustomField(); + CustomField( const QString &key, const QString &title, Type type, Scope scope ); + + static CustomField fromVariantMap( const QVariantMap &map, Scope scope ); + + void setKey( const QString &key ); + QString key() const; + + void setTitle( const QString &title ); + QString title() const; + + void setType( Type type ); + Type type() const; + + void setScope( Scope scope ); + Scope scope() const; + + void setValue( const QString &value ); + QString value() const; + + QVariantMap toVariantMap() const; + + static QString typeToString( Type type ); + static Type stringToType( const QString &type ); + + private: + QString mKey; + QString mTitle; + Type mType; + Scope mScope; + QString mValue; +}; + +#endif Index: akonadi/contact/contactviewer.cpp =================================================================== --- akonadi/contact/contactviewer.cpp (révision 1101152) +++ akonadi/contact/contactviewer.cpp (révision 1101153) @@ -21,6 +21,9 @@ #include "contactviewer.h" +#include "contactmetadata_p.h" +#include "contactmetadataattribute_p.h" +#include "customfieldmanager_p.h" #include "textbrowser_p.h" #include <akonadi/item.h> @@ -36,7 +39,7 @@ using namespace Akonadi; -static QString contactAsHtml( const KABC::Addressee &contact ); +static QString contactAsHtml( const KABC::Addressee &contact, const QVariantList &customFieldDescriptions ); class ContactViewer::Private { @@ -103,6 +106,7 @@ // always fetch full payload for contacts fetchScope().fetchFullPayload(); + fetchScope().fetchAttribute<ContactMetaDataAttribute>(); } ContactViewer::~ContactViewer() @@ -141,7 +145,23 @@ defaultPixmap ); } - d->mBrowser->setHtml( contactAsHtml( d->mCurrentContact ) ); + // merge the local... + ContactMetaData metaData; + metaData.load( contactItem ); + + QVariantList customFieldDescriptions = metaData.customFieldDescriptions(); + + // ... and global custom field descriptions + const CustomField::List globalCustomFields = CustomFieldManager::globalCustomFieldDescriptions(); + foreach ( const CustomField &field, globalCustomFields ) { + QVariantMap description; + description.insert( QLatin1String( "key" ), field.key() ); + description.insert( QLatin1String( "title" ), field.title() ); + + customFieldDescriptions << description; + } + + d->mBrowser->setHtml( contactAsHtml( d->mCurrentContact, customFieldDescriptions ) ); } void ContactViewer::itemRemoved() @@ -149,7 +169,7 @@ d->mBrowser->clear(); } -static QString contactAsHtml( const KABC::Addressee &contact ) +static QString contactAsHtml( const KABC::Addressee &contact, const QVariantList &customFieldDescriptions ) { // We'll be building a table to display the vCard in. // Each row of the table will be built using this string for its HTML. @@ -282,9 +302,20 @@ if ( blacklistedKeys.contains( key ) ) continue; + // check whether we have a mapping for the title const QMap<QString, QString>::ConstIterator keyIt = titleMap.constFind( key ); - if ( keyIt != titleMap.constEnd() ) + if ( keyIt != titleMap.constEnd() ) { key = keyIt.value(); + } else { + // check whether it is a custom local field + foreach ( const QVariant &description, customFieldDescriptions ) { + const QVariantMap field = description.toMap(); + if ( field.value( QLatin1String( "key" ) ).toString() == key ) { + key = field.value( QLatin1String( "title" ) ).toString(); + break; + } + } + } customData += rowFmtStr.arg( key ).arg( value ) ; } Index: akonadi/contact/contactmetadata.cpp =================================================================== --- akonadi/contact/contactmetadata.cpp (révision 1101152) +++ akonadi/contact/contactmetadata.cpp (révision 1101153) @@ -36,6 +36,7 @@ } int mDisplayNameMode; + QVariantList mCustomFieldDescriptions; }; ContactMetaData::ContactMetaData() @@ -60,6 +61,8 @@ d->mDisplayNameMode = metaData.value( QLatin1String( "DisplayNameMode" ) ).toInt(); else d->mDisplayNameMode = -1; + + d->mCustomFieldDescriptions = metaData.value( QLatin1String( "CustomFieldDescriptions" ) ).toList(); } void ContactMetaData::store( Akonadi::Item &contact ) @@ -70,6 +73,9 @@ if ( d->mDisplayNameMode != -1 ) metaData.insert( QLatin1String( "DisplayNameMode" ), QVariant( d->mDisplayNameMode ) ); + if ( !d->mCustomFieldDescriptions.isEmpty() ) + metaData.insert( QLatin1String( "CustomFieldDescriptions" ), d->mCustomFieldDescriptions ); + attribute->setMetaData( metaData ); } @@ -82,3 +88,13 @@ { return d->mDisplayNameMode; } + +void ContactMetaData::setCustomFieldDescriptions( const QVariantList &descriptions ) +{ + d->mCustomFieldDescriptions = descriptions; +} + +QVariantList ContactMetaData::customFieldDescriptions() const +{ + return d->mCustomFieldDescriptions; +} Index: akonadi/contact/customfields.cpp =================================================================== --- akonadi/contact/customfields.cpp (révision 0) +++ akonadi/contact/customfields.cpp (révision 1101153) @@ -0,0 +1,143 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "customfields_p.h" + +CustomField::CustomField() + : mType( TextType ), mScope( LocalScope ) +{ +} + +CustomField::CustomField( const QString &key, const QString &title, Type type, Scope scope ) + : mKey( key ), mTitle( title ), mType( type ), mScope( scope ) +{ +} + +CustomField CustomField::fromVariantMap( const QVariantMap &map, Scope scope ) +{ + return CustomField( map.value( QLatin1String( "key" ) ).toString(), + map.value( QLatin1String( "title" ) ).toString(), + stringToType( map.value( QLatin1String( "type" ) ).toString() ), + scope ); +} + +void CustomField::setKey( const QString &key ) +{ + mKey = key; +} + +QString CustomField::key() const +{ + return mKey; +} + +void CustomField::setTitle( const QString &title ) +{ + mTitle = title; +} + +QString CustomField::title() const +{ + return mTitle; +} + +void CustomField::setType( Type type ) +{ + mType = type; +} + +CustomField::Type CustomField::type() const +{ + return mType; +} + +void CustomField::setScope( Scope scope ) +{ + mScope = scope; +} + +CustomField::Scope CustomField::scope() const +{ + return mScope; +} + +void CustomField::setValue( const QString &value ) +{ + mValue = value; +} + +QString CustomField::value() const +{ + return mValue; +} + +QVariantMap CustomField::toVariantMap() const +{ + QVariantMap map; + map.insert( QLatin1String( "key" ), mKey ); + map.insert( QLatin1String( "title" ), mTitle ); + map.insert( QLatin1String( "type" ), typeToString( mType ) ); + + return map; +} + +CustomField::Type CustomField::stringToType( const QString &type ) +{ + if ( type == QLatin1String( "text" ) ) + return CustomField::TextType; + if ( type == QLatin1String( "numeric" ) ) + return CustomField::NumericType; + if ( type == QLatin1String( "boolean" ) ) + return CustomField::BooleanType; + if ( type == QLatin1String( "date" ) ) + return CustomField::DateType; + if ( type == QLatin1String( "time" ) ) + return CustomField::TimeType; + if ( type == QLatin1String( "datetime" ) ) + return CustomField::DateTimeType; + + return CustomField::TextType; +} + +QString CustomField::typeToString( CustomField::Type type ) +{ + switch ( type ) { + case CustomField::TextType: + default: + return QLatin1String( "text" ); + break; + case CustomField::NumericType: + return QLatin1String( "numeric" ); + break; + case CustomField::BooleanType: + return QLatin1String( "boolean" ); + break; + case CustomField::DateType: + return QLatin1String( "date" ); + break; + case CustomField::TimeType: + return QLatin1String( "time" ); + break; + case CustomField::DateTimeType: + return QLatin1String( "datetime" ); + break; + } +} Index: akonadi/contact/customfieldmanager_p.h =================================================================== --- akonadi/contact/customfieldmanager_p.h (révision 0) +++ akonadi/contact/customfieldmanager_p.h (révision 1101153) @@ -0,0 +1,37 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef CUSTOMFIELDMANAGER_P_H +#define CUSTOMFIELDMANAGER_P_H + +#include "customfields_p.h" + +/** + * @short A class that manages the descriptions of all custom fields with global scope. + */ +class CustomFieldManager +{ + public: + static void setGlobalCustomFieldDescriptions( const CustomField::List &customFields ); + static CustomField::List globalCustomFieldDescriptions(); +}; + +#endif Index: akonadi/contact/editor/customfieldsdelegate.h =================================================================== --- akonadi/contact/editor/customfieldsdelegate.h (révision 0) +++ akonadi/contact/editor/customfieldsdelegate.h (révision 1101153) @@ -0,0 +1,40 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef CUSTOMFIELDSDELEGATE_H +#define CUSTOMFIELDSDELEGATE_H + +#include <QtGui/QStyledItemDelegate> + +class CustomFieldsDelegate : public QStyledItemDelegate +{ + public: + explicit CustomFieldsDelegate( QObject *parent = 0 ); + ~CustomFieldsDelegate(); + + virtual QWidget* createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const; + + virtual void setEditorData( QWidget *editor, const QModelIndex &index ) const; + virtual void setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const; + virtual void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const; +}; + +#endif Index: akonadi/contact/editor/customfieldeditordialog.cpp =================================================================== --- akonadi/contact/editor/customfieldeditordialog.cpp (révision 0) +++ akonadi/contact/editor/customfieldeditordialog.cpp (révision 1101153) @@ -0,0 +1,93 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "customfieldeditordialog.h" + +#include <kcombobox.h> +#include <klineedit.h> +#include <klocale.h> + +#include <QtGui/QCheckBox> +#include <QtGui/QFormLayout> +#include <QtGui/QRegExpValidator> + +CustomFieldEditorDialog::CustomFieldEditorDialog( QWidget *parent ) + : KDialog( parent ) +{ + setCaption( i18n( "Edit Custom Field" ) ); + setButtons( Ok | Cancel | Details ); + + QWidget *widget = new QWidget( this ); + setMainWidget( widget ); + + QFormLayout *layout = new QFormLayout( widget ); + + mKey = new KLineEdit; + mTitle = new KLineEdit; + mType = new KComboBox; + mScope = new QCheckBox( i18n( "Use field for all contacts" ) ); + + layout->addRow( i18n( "Title" ), mTitle ); + layout->addRow( i18n( "Type" ), mType ); + layout->addRow( QString(), mScope ); + + QWidget *detailsWidget = new QWidget; + QFormLayout *detailsLayout = new QFormLayout( detailsWidget ); + detailsLayout->addRow( i18n( "Key" ), mKey ); + + setDetailsWidget( detailsWidget ); + setButtonText( Details, i18n( "Advanced" ) ); + + mType->addItem( i18n( "Text" ), CustomField::TextType ); + mType->addItem( i18n( "Numeric" ), CustomField::NumericType ); + mType->addItem( i18n( "Boolean" ), CustomField::BooleanType ); + mType->addItem( i18n( "Date" ), CustomField::DateType ); + mType->addItem( i18n( "Time" ), CustomField::TimeType ); + mType->addItem( i18n( "DateTime" ), CustomField::DateTimeType ); + + mKey->setValidator( new QRegExpValidator( QRegExp( QLatin1String( "[a-zA-Z0-9\\-]+" ) ), this ) ); +} + +void CustomFieldEditorDialog::setCustomField( const CustomField &field ) +{ + mCustomField = field; + + mKey->setText( mCustomField.key() ); + mTitle->setText( mCustomField.title() ); + mType->setCurrentIndex( mType->findData( mCustomField.type() ) ); + mScope->setChecked( (mCustomField.scope() == CustomField::GlobalScope) ); +} + +CustomField CustomFieldEditorDialog::customField() const +{ + CustomField customField( mCustomField ); + + customField.setKey( mKey->text() ); + customField.setTitle( mTitle->text() ); + customField.setType( static_cast<CustomField::Type>( mType->itemData( mType->currentIndex() ).toInt() ) ); + + if ( customField.scope() != CustomField::ExternalScope ) { + // do not change the scope for externally defined custom fields + customField.setScope( mScope->isChecked() ? CustomField::GlobalScope : CustomField::LocalScope ); + } + + return customField; +} Index: akonadi/contact/editor/customfieldseditwidget.h =================================================================== --- akonadi/contact/editor/customfieldseditwidget.h (révision 0) +++ akonadi/contact/editor/customfieldseditwidget.h (révision 1101153) @@ -0,0 +1,70 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef CUSTOMFIELDSEDITWIDGET_H +#define CUSTOMFIELDSEDITWIDGET_H + +#include <QtGui/QWidget> + +#include "customfieldsmodel.h" + +namespace KABC { +class Addressee; +} + +class QPushButton; +class QTreeView; + +class CustomFieldsEditWidget : public QWidget +{ + Q_OBJECT + + public: + CustomFieldsEditWidget( QWidget *parent = 0 ); + ~CustomFieldsEditWidget(); + + void loadContact( const KABC::Addressee &contact ); + void storeContact( KABC::Addressee &contact ) const; + + void setReadOnly( bool readOnly ); + + void setLocalCustomFieldDescriptions( const QVariantList &descriptions ); + QVariantList localCustomFieldDescriptions() const; + + private Q_SLOTS: + void slotAdd(); + void slotEdit(); + void slotRemove(); + void slotUpdateButtons(); + + private: + QTreeView *mView; + + QPushButton *mAddButton; + QPushButton *mEditButton; + QPushButton *mRemoveButton; + + bool mReadOnly; + CustomFieldsModel *mModel; + CustomField::List mLocalCustomFields; +}; + +#endif Index: akonadi/contact/editor/customfieldsmodel.cpp =================================================================== --- akonadi/contact/editor/customfieldsmodel.cpp (révision 0) +++ akonadi/contact/editor/customfieldsmodel.cpp (révision 1101153) @@ -0,0 +1,250 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "customfieldsmodel.h" + +#include <kglobal.h> +#include <kicon.h> +#include <klocale.h> + +#include <QtCore/QDateTime> + +CustomFieldsModel::CustomFieldsModel( QObject *parent ) + : QAbstractItemModel( parent ) +{ +} + +CustomFieldsModel::~CustomFieldsModel() +{ +} + +void CustomFieldsModel::setCustomFields( const CustomField::List &customFields ) +{ + emit layoutAboutToBeChanged(); + + mCustomFields = customFields; + + emit layoutChanged(); +} + +CustomField::List CustomFieldsModel::customFields() const +{ + return mCustomFields; +} + +QModelIndex CustomFieldsModel::index( int row, int column, const QModelIndex& ) const +{ + return createIndex( row, column, 0 ); +} + +QModelIndex CustomFieldsModel::parent( const QModelIndex& ) const +{ + return QModelIndex(); +} + +QVariant CustomFieldsModel::data( const QModelIndex &index, int role ) const +{ + if ( !index.isValid() ) + return QVariant(); + + if ( index.row() < 0 || index.row() >= mCustomFields.count() ) + return QVariant(); + + if ( index.column() < 0 || index.column() > 2 ) + return QVariant(); + + const CustomField &customField = mCustomFields[ index.row() ]; + + if ( role == Qt::DecorationRole ) { + if ( index.column() == 1 ) { + if ( customField.type() == CustomField::BooleanType ) { + if ( customField.value() == QLatin1String( "true" ) ) + return KIcon( QLatin1String( "button_more" ) ); + else + return KIcon( QLatin1String( "button_fewer" ) ); + } + } + + return QVariant(); + } + + if ( role == Qt::DisplayRole ) { + if ( index.column() == 0 ) + return customField.title(); + else if ( index.column() == 1 ) { + switch ( customField.type() ) { + case CustomField::TextType: + case CustomField::NumericType: + return customField.value(); + break; + case CustomField::BooleanType: + return QString(); // we use icons here + break; + case CustomField::DateType: + { + const QDate value = QDate::fromString( customField.value(), Qt::ISODate ); + return KGlobal::locale()->formatDate( value, KLocale::ShortDate ); + } + break; + case CustomField::TimeType: + { + const QTime value = QTime::fromString( customField.value(), Qt::ISODate ); + return KGlobal::locale()->formatTime( value ); + } + break; + case CustomField::DateTimeType: + { + const QDateTime value = QDateTime::fromString( customField.value(), Qt::ISODate ); + return KGlobal::locale()->formatDateTime( value ); + } + break; + } + return customField.value(); + } else + return customField.key(); + } + + if ( role == Qt::EditRole ) { + if ( index.column() == 0 ) + return customField.title(); + else if ( index.column() == 1 ) + return customField.value(); + else + return customField.key(); + } + + if ( role == TypeRole ) + return customField.type(); + + if ( role == ScopeRole ) + return customField.scope(); + + return QVariant(); +} + +bool CustomFieldsModel::setData( const QModelIndex &index, const QVariant &value, int role ) +{ + if ( !index.isValid() ) + return false; + + if ( index.row() < 0 || index.row() >= mCustomFields.count() ) + return false; + + if ( index.column() < 0 || index.column() > 2 ) + return false; + + CustomField &customField = mCustomFields[ index.row() ]; + + if ( role == Qt::EditRole ) { + if ( index.column() == 0 ) + customField.setTitle( value.toString() ); + else if ( index.column() == 1 ) + customField.setValue( value.toString() ); + else + customField.setKey( value.toString() ); + + emit dataChanged( index, index ); + return true; + } + + if ( role == TypeRole ) { + customField.setType( (CustomField::Type)value.toInt() ); + emit dataChanged( index, index ); + return true; + } + + if ( role == ScopeRole ) { + customField.setScope( (CustomField::Scope)value.toInt() ); + emit dataChanged( index, index ); + return true; + } + + return false; +} + +QVariant CustomFieldsModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + if ( section < 0 || section > 1 ) + return QVariant(); + + if ( orientation != Qt::Horizontal ) + return QVariant(); + + if ( role != Qt::DisplayRole ) + return QVariant(); + + if ( section == 0 ) + return i18nc( "custom field title", "Title" ); + else + return i18nc( "custom field value", "Value" ); +} + +Qt::ItemFlags CustomFieldsModel::flags( const QModelIndex &index ) const +{ + if ( !index.isValid() || index.row() < 0 || index.row() >= mCustomFields.count() ) + return QAbstractItemModel::flags( index ); + + const Qt::ItemFlags parentFlags = QAbstractItemModel::flags( index ); + return (parentFlags | Qt::ItemIsEnabled | Qt::ItemIsEditable); +} + +int CustomFieldsModel::columnCount( const QModelIndex &parent ) const +{ + if ( !parent.isValid() ) + return 3; + else + return 0; +} + +int CustomFieldsModel::rowCount( const QModelIndex &parent ) const +{ + if ( !parent.isValid() ) + return mCustomFields.count(); + else + return 0; +} + +bool CustomFieldsModel::insertRows( int row, int count, const QModelIndex &parent ) +{ + if ( parent.isValid() ) + return false; + + beginInsertRows( parent, row, row + count - 1 ); + for ( int i = 0; i < count; ++i ) + mCustomFields.insert( row, CustomField() ); + endInsertRows(); + + return true; +} + +bool CustomFieldsModel::removeRows( int row, int count, const QModelIndex &parent ) +{ + if ( parent.isValid() ) + return false; + + beginRemoveRows( parent, row, row + count - 1 ); + for ( int i = 0; i < count; ++i ) + mCustomFields.removeAt( row ); + endRemoveRows(); + + return true; +} + Index: akonadi/contact/editor/customfieldsdelegate.cpp =================================================================== --- akonadi/contact/editor/customfieldsdelegate.cpp (révision 0) +++ akonadi/contact/editor/customfieldsdelegate.cpp (révision 1101153) @@ -0,0 +1,194 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "customfieldsdelegate.h" + +#include "customfieldsmodel.h" + +#include <kicon.h> +#include <klocale.h> + +#include <QtGui/QDateEdit> +#include <QtGui/QDateTimeEdit> +#include <QtGui/QCheckBox> +#include <QtGui/QSpinBox> +#include <QtGui/QTimeEdit> + +CustomFieldsDelegate::CustomFieldsDelegate( QObject *parent ) + : QStyledItemDelegate( parent ) +{ +} + +CustomFieldsDelegate::~CustomFieldsDelegate() +{ +} + +QWidget* CustomFieldsDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &item, const QModelIndex &index ) const +{ + if ( index.column() == 1 ) { + const CustomField::Type type = static_cast<CustomField::Type>( index.data( CustomFieldsModel::TypeRole ).toInt() ); + + switch ( type ) { + case CustomField::TextType: + default: + return QStyledItemDelegate::createEditor( parent, item, index ); + break; + case CustomField::NumericType: + { + QSpinBox *editor = new QSpinBox( parent ); + editor->setFrame( false ); + editor->setAutoFillBackground( true ); + return editor; + } + break; + case CustomField::BooleanType: + { + QCheckBox *editor = new QCheckBox( parent ); + return editor; + } + break; + case CustomField::DateType: + { + QDateEdit *editor = new QDateEdit( parent ); + editor->setFrame( false ); + editor->setAutoFillBackground( true ); + return editor; + } + break; + case CustomField::TimeType: + { + QTimeEdit *editor = new QTimeEdit( parent ); + editor->setFrame( false ); + editor->setAutoFillBackground( true ); + return editor; + } + break; + case CustomField::DateTimeType: + { + QDateTimeEdit *editor = new QDateTimeEdit( parent ); + editor->setFrame( false ); + editor->setAutoFillBackground( true ); + return editor; + } + break; + } + } else { + return QStyledItemDelegate::createEditor( parent, item, index ); + } +} + +void CustomFieldsDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const +{ + if ( index.column() == 1 ) { + const CustomField::Type type = static_cast<CustomField::Type>( index.data( CustomFieldsModel::TypeRole ).toInt() ); + + switch ( type ) { + case CustomField::TextType: + QStyledItemDelegate::setEditorData( editor, index ); + break; + case CustomField::NumericType: + { + QSpinBox *widget = qobject_cast<QSpinBox*>( editor ); + widget->setValue( index.data( Qt::EditRole ).toInt() ); + } + break; + case CustomField::BooleanType: + { + QCheckBox *widget = qobject_cast<QCheckBox*>( editor ); + widget->setChecked( index.data( Qt::EditRole ).toString() == QLatin1String( "true" ) ); + } + break; + case CustomField::DateType: + { + QDateEdit *widget = qobject_cast<QDateEdit*>( editor ); + widget->setDate( QDate::fromString( index.data( Qt::EditRole ).toString(), Qt::ISODate ) ); + } + break; + case CustomField::TimeType: + { + QTimeEdit *widget = qobject_cast<QTimeEdit*>( editor ); + widget->setTime( QTime::fromString( index.data( Qt::EditRole ).toString(), Qt::ISODate ) ); + } + break; + case CustomField::DateTimeType: + { + QDateTimeEdit *widget = qobject_cast<QDateTimeEdit*>( editor ); + widget->setDateTime( QDateTime::fromString( index.data( Qt::EditRole ).toString(), Qt::ISODate ) ); + } + break; + } + } else { + QStyledItemDelegate::setEditorData( editor, index ); + } +} + +void CustomFieldsDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const +{ + if ( index.column() == 1 ) { + const CustomField::Type type = static_cast<CustomField::Type>( index.data( CustomFieldsModel::TypeRole ).toInt() ); + + switch ( type ) { + case CustomField::TextType: + QStyledItemDelegate::setModelData( editor, model, index ); + break; + case CustomField::NumericType: + { + QSpinBox *widget = qobject_cast<QSpinBox*>( editor ); + model->setData( index, QString::number( widget->value() ) ); + } + break; + case CustomField::BooleanType: + { + QCheckBox *widget = qobject_cast<QCheckBox*>( editor ); + model->setData( index, widget->isChecked() ? QLatin1String( "true" ) : QLatin1String( "false" ) ); + } + break; + case CustomField::DateType: + { + QDateEdit *widget = qobject_cast<QDateEdit*>( editor ); + model->setData( index, widget->date().toString( Qt::ISODate ) ); + } + break; + case CustomField::TimeType: + { + QTimeEdit *widget = qobject_cast<QTimeEdit*>( editor ); + model->setData( index, widget->time().toString( Qt::ISODate ) ); + } + break; + case CustomField::DateTimeType: + { + QDateTimeEdit *widget = qobject_cast<QDateTimeEdit*>( editor ); + model->setData( index, widget->dateTime().toString( Qt::ISODate ) ); + } + break; + } + } else { + QStyledItemDelegate::setModelData( editor, model, index ); + } +} + +void CustomFieldsDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const +{ + //TODO: somehow mark local/global/external fields + QStyledItemDelegate::paint( painter, option, index ); +} + +#include "customfieldsdelegate.moc" Index: akonadi/contact/editor/customfieldeditordialog.h =================================================================== --- akonadi/contact/editor/customfieldeditordialog.h (révision 0) +++ akonadi/contact/editor/customfieldeditordialog.h (révision 1101153) @@ -0,0 +1,51 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef CUSTOMFIELDEDITORDIALOG_H +#define CUSTOMFIELDEDITORDIALOG_H + +#include <kdialog.h> + +#include "customfieldsmodel.h" + +class KComboBox; +class KLineEdit; +class QCheckBox; + +class CustomFieldEditorDialog : public KDialog +{ + Q_OBJECT + + public: + CustomFieldEditorDialog( QWidget *parent = 0 ); + + void setCustomField( const CustomField &field ); + CustomField customField() const; + + private: + KLineEdit *mTitle; + KComboBox *mType; + QCheckBox *mScope; + KLineEdit *mKey; + CustomField mCustomField; +}; + +#endif Index: akonadi/contact/editor/customfieldseditwidget.cpp =================================================================== --- akonadi/contact/editor/customfieldseditwidget.cpp (révision 0) +++ akonadi/contact/editor/customfieldseditwidget.cpp (révision 1101153) @@ -0,0 +1,318 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "customfieldseditwidget.h" + +#include "customfieldeditordialog.h" +#include "customfieldmanager_p.h" +#include "customfieldsdelegate.h" +#include "customfieldsmodel.h" + +#include <kabc/addressee.h> +#include <klocale.h> +#include <kmessagebox.h> + +#include <QtCore/QUuid> +#include <QtGui/QGridLayout> +#include <QtGui/QPushButton> +#include <QtGui/QTreeView> + +static void splitCustomField( const QString &str, QString &app, QString &name, QString &value ) +{ + const int colon = str.indexOf( QLatin1Char( ':' ) ); + if ( colon != -1 ) { + const QString tmp = str.left( colon ); + value = str.mid( colon + 1 ); + + const int dash = tmp.indexOf( QLatin1Char( '-' ) ); + if ( dash != -1 ) { + app = tmp.left( dash ); + name = tmp.mid( dash + 1 ); + } + } +} + +CustomFieldsEditWidget::CustomFieldsEditWidget( QWidget *parent ) + : QWidget( parent ), mReadOnly( false ) +{ + QGridLayout *layout = new QGridLayout( this ); + layout->setMargin( 0 ); + + mView = new QTreeView; + mView->setRootIsDecorated( false ); + mView->setItemDelegate( new CustomFieldsDelegate( this ) ); + + mAddButton = new QPushButton( i18n( "Add..." ) ); + mEditButton = new QPushButton( i18n( "Edit..." ) ); + mRemoveButton = new QPushButton( i18n( "Remove" ) ); + + layout->addWidget( mView, 0, 0, 4, 1 ); + layout->addWidget( mAddButton, 0, 1 ); + layout->addWidget( mEditButton, 1, 1 ); + layout->addWidget( mRemoveButton, 2, 1 ); + + mModel = new CustomFieldsModel( this ); + mView->setModel( mModel ); + mView->setColumnHidden( 2, true ); // hide the 'key' column + + connect( mView->selectionModel(), SIGNAL( currentChanged( const QModelIndex&, const QModelIndex& ) ), + this, SLOT( slotUpdateButtons() ) ); + connect( mAddButton, SIGNAL( clicked() ), this, SLOT( slotAdd() ) ); + connect( mEditButton, SIGNAL( clicked() ), this, SLOT( slotEdit() ) ); + connect( mRemoveButton, SIGNAL( clicked() ), this, SLOT( slotRemove() ) ); +} + +CustomFieldsEditWidget::~CustomFieldsEditWidget() +{ +} + +void CustomFieldsEditWidget::loadContact( const KABC::Addressee &contact ) +{ + CustomField::List externalCustomFields; + + CustomField::List globalCustomFields = CustomFieldManager::globalCustomFieldDescriptions(); + + const QStringList customs = contact.customs(); + foreach ( const QString &custom, customs ) { + + QString app, name, value; + splitCustomField( custom, app, name, value ); + + // skip all well-known fields that have separated editor widgets + if ( custom.startsWith( QLatin1String( "messaging/" ) ) ) // IM addresses + continue; + + if ( app == QLatin1String( "KADDRESSBOOK" ) ) { + static QSet<QString> blacklist; + if ( blacklist.isEmpty() ) { + blacklist << QLatin1String( "BlogFeed" ) + << QLatin1String( "X-IMAddress" ) + << QLatin1String( "X-Profession" ) + << QLatin1String( "X-Office" ) + << QLatin1String( "X-ManagersName" ) + << QLatin1String( "X-AssistantsName" ) + << QLatin1String( "X-Anniversary" ) + << QLatin1String( "X-SpousesName" ) + << QLatin1String( "X-Profession" ); + } + + if ( blacklist.contains( name ) ) // several KAddressBook specific fields + continue; + } + + // check whether it correspond to a local custom field + bool isLocalCustomField = false; + for ( int i = 0; i < mLocalCustomFields.count(); ++i ) { + if ( mLocalCustomFields[ i ].key() == name ) { + mLocalCustomFields[ i ].setValue( value ); + isLocalCustomField = true; + break; + } + } + + // check whether it correspond to a global custom field + bool isGlobalCustomField = false; + for ( int i = 0; i < globalCustomFields.count(); ++i ) { + if ( globalCustomFields[ i ].key() == name ) { + globalCustomFields[ i ].setValue( value ); + isGlobalCustomField = true; + break; + } + } + + // if not local and not global it must be external + if ( !isLocalCustomField && !isGlobalCustomField ) { + if ( app == QLatin1String( "KADDRESSBOOK" ) ) { + // however if it starts with our prefix it might be that this is an outdated + // global custom field, in this case treat it as local field of type text + CustomField customField( name, name, CustomField::TextType, CustomField::LocalScope ); + customField.setValue( value ); + + mLocalCustomFields << customField; + } else { + // it is really an external custom field + const QString key = app + QLatin1Char( '-' ) + name; + CustomField customField( key, key, CustomField::TextType, CustomField::ExternalScope ); + customField.setValue( value ); + + externalCustomFields << customField; + } + } + } + + mModel->setCustomFields( CustomField::List() << mLocalCustomFields << globalCustomFields << externalCustomFields ); +} + +void CustomFieldsEditWidget::storeContact( KABC::Addressee &contact ) const +{ + const CustomField::List customFields = mModel->customFields(); + foreach ( const CustomField &customField, customFields ) { + // write back values for local and global scope, leave external untouched + if ( customField.scope() != CustomField::ExternalScope ) { + if ( !customField.value().isEmpty() ) + contact.insertCustom( QLatin1String( "KADDRESSBOOK" ), customField.key(), customField.value() ); + else + contact.removeCustom( QLatin1String( "KADDRESSBOOK" ), customField.key() ); + } + } + + // Now remove all fields that were available in loadContact (these are stored in mLocalCustomFields) + // but are not part of customFields now, which means they have been removed or renamed by the user + // in the editor dialog. + foreach ( const CustomField &oldCustomField, mLocalCustomFields ) { + if ( oldCustomField.scope() != CustomField::ExternalScope ) { + + bool fieldStillExists = false; + foreach ( const CustomField &newCustomField, customFields ) { + if ( newCustomField.scope() != CustomField::ExternalScope ) { + if ( newCustomField.key() == oldCustomField.key() ) { + fieldStillExists = true; + break; + } + } + } + + if ( !fieldStillExists ) + contact.removeCustom( QLatin1String( "KADDRESSBOOK" ), oldCustomField.key() ); + } + } + + // And store the global custom fields descriptions as well + CustomField::List globalCustomFields; + foreach ( const CustomField &customField, customFields ) { + if ( customField.scope() == CustomField::GlobalScope ) { + globalCustomFields << customField; + } + } + + CustomFieldManager::setGlobalCustomFieldDescriptions( globalCustomFields ); +} + +void CustomFieldsEditWidget::setReadOnly( bool readOnly ) +{ + mReadOnly = readOnly; + + mView->setEnabled( !mReadOnly ); + + slotUpdateButtons(); +} + +void CustomFieldsEditWidget::setLocalCustomFieldDescriptions( const QVariantList &descriptions ) +{ + mLocalCustomFields.clear(); + + foreach ( const QVariant &description, descriptions ) + mLocalCustomFields.append( CustomField::fromVariantMap( description.toMap(), CustomField::LocalScope ) ); +} + +QVariantList CustomFieldsEditWidget::localCustomFieldDescriptions() const +{ + const CustomField::List customFields = mModel->customFields(); + + QVariantList descriptions; + foreach ( const CustomField &field, customFields ) { + if ( field.scope() == CustomField::LocalScope ) + descriptions.append( field.toVariantMap() ); + } + + return descriptions; +} + +void CustomFieldsEditWidget::slotAdd() +{ + CustomField field; + + // We use a Uuid as default key, so we won't have any duplicated keys, + // the user can still change it to something else in the editor dialog. + // Since the key only allows [A-Za-z0-9\-]*, we have to remove the curly + // braces as well. + QString key = QUuid::createUuid().toString(); + key.remove( QLatin1Char( '{' ) ); + key.remove( QLatin1Char( '}' ) ); + + field.setKey( key ); + + CustomFieldEditorDialog dlg( this ); + dlg.setCustomField( field ); + + if ( dlg.exec() ) { + const int lastRow = mModel->rowCount(); + mModel->insertRow( lastRow ); + + field = dlg.customField(); + mModel->setData( mModel->index( lastRow, 2 ), field.key(), Qt::EditRole ); + mModel->setData( mModel->index( lastRow, 0 ), field.title(), Qt::EditRole ); + mModel->setData( mModel->index( lastRow, 0 ), field.type(), CustomFieldsModel::TypeRole ); + mModel->setData( mModel->index( lastRow, 0 ), field.scope(), CustomFieldsModel::ScopeRole ); + } +} + +void CustomFieldsEditWidget::slotEdit() +{ + const QModelIndex currentIndex = mView->currentIndex(); + if ( !currentIndex.isValid() ) + return; + + CustomField field; + field.setKey( mModel->index( currentIndex.row(), 2 ).data( Qt::DisplayRole ).toString() ); + field.setTitle( mModel->index( currentIndex.row(), 0 ).data( Qt::DisplayRole ).toString() ); + field.setType( static_cast<CustomField::Type>( currentIndex.data( CustomFieldsModel::TypeRole ).toInt() ) ); + field.setScope( static_cast<CustomField::Scope>( currentIndex.data( CustomFieldsModel::ScopeRole ).toInt() ) ); + + CustomFieldEditorDialog dlg( this ); + dlg.setCustomField( field ); + + if ( dlg.exec() ) { + field = dlg.customField(); + mModel->setData( mModel->index( currentIndex.row(), 2 ), field.key(), Qt::EditRole ); + mModel->setData( mModel->index( currentIndex.row(), 0 ), field.title(), Qt::EditRole ); + mModel->setData( currentIndex, field.type(), CustomFieldsModel::TypeRole ); + mModel->setData( currentIndex, field.scope(), CustomFieldsModel::ScopeRole ); + } +} + +void CustomFieldsEditWidget::slotRemove() +{ + const QModelIndex currentIndex = mView->currentIndex(); + if ( !currentIndex.isValid() ) + return; + + if ( KMessageBox::warningContinueCancel( this, + i18nc( "Custom Fields", "Do you really want to delete the selected custom field?" ), + i18n( "Confirm Delete" ), KStandardGuiItem::del() ) != KMessageBox::Continue ) { + return; + } + + mModel->removeRow( currentIndex.row() ); +} + +void CustomFieldsEditWidget::slotUpdateButtons() +{ + const bool hasCurrent = mView->currentIndex().isValid(); + const bool isExternal = (hasCurrent && + (static_cast<CustomField::Scope>( mView->currentIndex().data( CustomFieldsModel::ScopeRole ).toInt() ) == CustomField::ExternalScope) ); + + mAddButton->setEnabled( !mReadOnly ); + mEditButton->setEnabled( !mReadOnly && hasCurrent && !isExternal ); + mRemoveButton->setEnabled( !mReadOnly && hasCurrent && !isExternal ); +} + +#include "customfieldseditwidget.moc" Index: akonadi/contact/editor/customfieldsmodel.h =================================================================== --- akonadi/contact/editor/customfieldsmodel.h (révision 0) +++ akonadi/contact/editor/customfieldsmodel.h (révision 1101153) @@ -0,0 +1,61 @@ +/* + This file is part of Akonadi Contact. + + Copyright (c) 2010 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef CUSTOMFIELDSMODEL_H +#define CUSTOMFIELDSMODEL_H + +#include "../customfields_p.h" + +#include <QtCore/QAbstractItemModel> +#include <QtCore/QList> + +class CustomFieldsModel : public QAbstractItemModel +{ + public: + enum Role + { + TypeRole = Qt::UserRole, + ScopeRole + }; + + CustomFieldsModel( QObject *parent = 0 ); + ~CustomFieldsModel(); + + void setCustomFields( const CustomField::List &addresses ); + CustomField::List customFields() const; + + virtual QModelIndex index( int row, int col, const QModelIndex &parent = QModelIndex() ) const; + virtual QModelIndex parent( const QModelIndex &child ) const; + virtual QVariant data( const QModelIndex &index, int role ) const; + virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ); + virtual QVariant headerData( int section, Qt::Orientation orientation, int role ) const; + virtual Qt::ItemFlags flags( const QModelIndex &index ) const; + virtual int columnCount( const QModelIndex &parent = QModelIndex() ) const; + virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const; + + virtual bool insertRows( int row, int count, const QModelIndex &parent = QModelIndex() ); + virtual bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() ); + + private: + CustomField::List mCustomFields; +}; + +#endif Index: akonadi/contact/CMakeLists.txt =================================================================== --- akonadi/contact/CMakeLists.txt +++ akonadi/contact/CMakeLists.txt 2010-04-03 18:27:57.000000000 +0200 @@ -22,6 +22,10 @@ set(akonadicontact_editor_SRCS editor/addresseditwidget.cpp editor/contacteditorwidget.cpp + editor/customfieldeditordialog.cpp + editor/customfieldsdelegate.cpp + editor/customfieldseditwidget.cpp + editor/customfieldsmodel.cpp editor/dateeditwidget.cpp editor/displaynameeditwidget.cpp editor/emaileditwidget.cpp @@ -58,6 +62,8 @@ contactsearchjob.cpp contactviewer.cpp contactviewerdialog.cpp + customfields.cpp + customfieldmanager.cpp recentcontactscollections.cpp recentcontactscollectionrequestjob.cpp waitingoverlay.cpp Index: akonadi/contact/editor/contacteditorwidget.cpp =================================================================== --- akonadi/contact/editor/contacteditorwidget.cpp +++ akonadi/contact/editor/contacteditorwidget.cpp 2010-04-03 18:33:01.000000000 +0200 @@ -24,6 +24,7 @@ #include "addresseditwidget.h" #include "contacteditorpageplugin.h" #include "contactmetadata_p.h" +#include "customfieldseditwidget.h" #include "dateeditwidget.h" #include "displaynameeditwidget.h" #include "emaileditwidget.h" @@ -63,6 +64,7 @@ void initGuiLocationTab(); void initGuiBusinessTab(); void initGuiPersonalTab(); + void initGuiCustomFieldsTab(); void loadCustomPages(); @@ -117,6 +119,9 @@ // widgets from family group KLineEdit *mPartnerWidget; + // widgets from custom fields group + CustomFieldsEditWidget *mCustomFieldsWidget; + // custom editor pages QList<Akonadi::ContactEditorPagePlugin*> mCustomPages; }; @@ -133,6 +138,7 @@ initGuiLocationTab(); initGuiBusinessTab(); initGuiPersonalTab(); + initGuiCustomFieldsTab(); loadCustomPages(); } @@ -416,6 +422,17 @@ familyLayout->setRowStretch( 1, 1 ); } +void ContactEditorWidget::Private::initGuiCustomFieldsTab() +{ + QWidget *widget = new QWidget; + QVBoxLayout *layout = new QVBoxLayout( widget ); + + mTabWidget->addTab( widget, i18n( "Custom Fields" ) ); + + mCustomFieldsWidget = new CustomFieldsEditWidget; + layout->addWidget( mCustomFieldsWidget ); +} + void ContactEditorWidget::Private::loadCustomPages() { qDeleteAll( mCustomPages ); @@ -518,6 +535,10 @@ d->mDisplayNameWidget->setDisplayType( (DisplayNameEditWidget::DisplayType)metaData.displayNameMode() ); + // custom fields group + d->mCustomFieldsWidget->setLocalCustomFieldDescriptions( metaData.customFieldDescriptions() ); + d->mCustomFieldsWidget->loadContact( contact ); + // custom pages foreach ( Akonadi::ContactEditorPagePlugin *plugin, d->mCustomPages ) plugin->loadContact( contact ); @@ -570,6 +591,10 @@ // family group d->storeCustom( contact, QLatin1String( "X-SpousesName" ), d->mPartnerWidget->text().trimmed() ); + // custom fields group + d->mCustomFieldsWidget->storeContact( contact ); + metaData.setCustomFieldDescriptions( d->mCustomFieldsWidget->localCustomFieldDescriptions() ); + metaData.setDisplayNameMode( d->mDisplayNameWidget->displayType() ); // custom pages @@ -624,6 +649,9 @@ // widgets from family group d->mPartnerWidget->setReadOnly( readOnly ); + // widgets from custom fields group + d->mCustomFieldsWidget->setReadOnly( readOnly ); + // custom pages foreach ( Akonadi::ContactEditorPagePlugin *plugin, d->mCustomPages ) plugin->setReadOnly( readOnly );