docs update, needs proofreading
[kdepim.git] / libkleo / ui / directoryserviceswidget.cpp
blobf0f4b308c8f94e7a243cd57fb2243153647b11a2
1 /*
2 directoryserviceswidget.cpp
4 This file is part of Kleopatra, the KDE keymanager
5 Copyright (c) 2001,2002,2004 Klar�vdalens Datakonsult AB
7 Kleopatra is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Kleopatra is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
30 your version.
33 #include "directoryserviceswidget.h"
35 #include "ui_directoryserviceswidget.h"
37 #include <KIcon>
38 #include <KDebug>
40 #include <QItemDelegate>
41 #include <QAbstractTableModel>
42 #include <QSpinBox>
43 #include <QComboBox>
44 #include <QHeaderView>
45 #include <QMenu>
46 #include <QAction>
48 #include <boost/bind.hpp>
50 #include <vector>
52 #include <climits>
53 #include <cassert>
54 #include <algorithm>
56 using namespace Kleo;
57 using namespace boost;
59 namespace {
61 static KUrl defaultX509Service() {
62 KUrl url;
63 url.setProtocol( "ldap" );
64 url.setHost( i18nc("default server name, keep it a valid domain name, ie. no spaces", "server") );
65 return url;
67 static KUrl defaultOpenPGPService() {
68 KUrl url;
69 url.setProtocol( "hkp" );
70 url.setHost( "keys.gnupg.net" );
71 return url;
74 static bool is_ldap_scheme( const KUrl & url ) {
75 const QString scheme = url.protocol();
76 return QString::compare( scheme, QLatin1String( "ldap" ), Qt::CaseInsensitive ) == 0
77 || QString::compare( scheme, QLatin1String( "ldaps" ), Qt::CaseInsensitive ) == 0;
80 static const struct {
81 const char label[6];
82 unsigned short port;
83 DirectoryServicesWidget::Scheme base;
84 } protocols[] = {
85 { I18N_NOOP("hkp"), 11371, DirectoryServicesWidget::HKP },
86 { I18N_NOOP("http"), 80, DirectoryServicesWidget::HTTP },
87 { I18N_NOOP("https"), 443, DirectoryServicesWidget::HTTP },
88 { I18N_NOOP("ftp"), 21, DirectoryServicesWidget::FTP },
89 { I18N_NOOP("ftps"), 990, DirectoryServicesWidget::FTP },
90 { I18N_NOOP("ldap"), 389, DirectoryServicesWidget::LDAP },
91 { I18N_NOOP("ldaps"), 636, DirectoryServicesWidget::LDAP },
93 static const unsigned int numProtocols = sizeof protocols / sizeof *protocols;
95 static unsigned short default_port( const QString & scheme ) {
96 for ( unsigned int i = 0 ; i < numProtocols ; ++i )
97 if ( QString::compare( scheme, QLatin1String( protocols[i].label ), Qt::CaseInsensitive ) == 0 )
98 return protocols[i].port;
99 return 0;
102 static QString display_scheme( const KUrl & url ) {
103 if ( url.scheme().isEmpty() )
104 return QLatin1String( "hkp" );
105 else
106 return url.scheme();
109 static QString display_host( const KUrl & url ) {
110 // work around "subkeys.pgp.net" being interpreted as a path, not host
111 if ( url.host().isEmpty() )
112 return url.path();
113 else
114 return url.host();
117 static unsigned short display_port( const KUrl & url ) {
118 if ( url.port() > 0 )
119 return url.port();
120 else
121 return default_port( display_scheme( url ) );
124 static bool is_default_port( const KUrl & url ) {
125 return display_port( url ) == default_port( display_scheme( url ) ) ;
128 static QRect calculate_geometry( const QRect & cell, const QSize & sizeHint ) {
129 const int height = qMax( cell.height(), sizeHint.height() );
130 return QRect( cell.left(), cell.top() - ( height - cell.height() ) / 2,
131 cell.width(), height );
134 struct KUrl_compare : std::binary_function<KUrl,KUrl,bool> {
135 bool operator()( const KUrl & lhs, const KUrl & rhs ) const {
136 return QString::compare( display_scheme( lhs ), display_scheme( rhs ), Qt::CaseInsensitive ) == 0
137 && QString::compare( display_host( lhs ), display_host( rhs ), Qt::CaseInsensitive ) == 0
138 && lhs.port() == rhs.port()
139 && lhs.user() == rhs.user()
140 // ... ignore password...
141 && ( !is_ldap_scheme( lhs )
142 || KUrl::fromPercentEncoding( lhs.query().mid( 1 ).toLatin1() )
143 == KUrl::fromPercentEncoding( rhs.query().mid( 1 ).toLatin1() ) ) ;
147 class Model : public QAbstractTableModel {
148 Q_OBJECT
149 public:
150 explicit Model( QObject * parent=0 )
151 : QAbstractTableModel( parent ),
152 m_items(),
153 m_openPGPReadOnly( false ),
154 m_x509ReadOnly( false ),
155 m_schemes( DirectoryServicesWidget::AllSchemes )
160 void setOpenPGPReadOnly( bool ro ) {
161 if ( ro == m_openPGPReadOnly )
162 return;
163 m_openPGPReadOnly = ro;
164 for ( unsigned int row = 0, end = rowCount() ; row != end ; ++row )
165 if ( isOpenPGPService( row ) )
166 emit dataChanged( index( row, 0 ), index( row, NumColumns ) );
169 void setX509ReadOnly( bool ro ) {
170 if ( ro == m_x509ReadOnly )
171 return;
172 m_x509ReadOnly = ro;
173 for ( unsigned int row = 0, end = rowCount() ; row != end ; ++row )
174 if ( isX509Service( row ) )
175 emit dataChanged( index( row, 0 ), index( row, NumColumns ) );
178 QModelIndex addOpenPGPService( const KUrl & url, bool force=false ) {
179 return addService( url, false, true, force );
181 QModelIndex addX509Service( const KUrl & url, bool force=false ) {
182 return addService( url, true, false, force );
184 QModelIndex addService( const KUrl & url, bool x509, bool pgp, bool force ) {
185 const std::vector<Item>::iterator it = force ? m_items.end() : findExistingUrl( url ) ;
186 unsigned int row;
187 if ( it != m_items.end() ) {
188 // existing item:
189 it->x509 |= x509;
190 it->pgp |= pgp;
191 row = it - m_items.begin() ;
192 emit dataChanged( index( row, std::min( X509, OpenPGP ) ), index( row, std::max( X509, OpenPGP ) ) );
193 } else {
194 // append new item
195 const Item item = { url, x509, pgp };
196 row = m_items.size();
197 beginInsertRows( QModelIndex(), row, row );
198 m_items.push_back( item );
199 endInsertRows();
201 return index( row, firstEditableColumn( row ) );
204 unsigned int numServices() const { return m_items.size(); }
205 bool isOpenPGPService( unsigned int row ) const { return row < m_items.size() && m_items[row].pgp; }
206 bool isX509Service( unsigned int row ) const { return row < m_items.size() && m_items[row].x509 && isLdapRow( row ) ; }
207 KUrl service( unsigned int row ) const { return row < m_items.size() ? m_items[row].url : KUrl() ; }
209 bool isReadOnlyRow( unsigned int row ) const {
210 return ( isX509Service( row ) && m_x509ReadOnly )
211 || ( isOpenPGPService( row ) && m_openPGPReadOnly );
214 enum Columns {
215 Scheme,
216 Host,
217 Port,
218 BaseDN,
219 UserName,
220 Password,
221 X509,
222 OpenPGP,
224 NumColumns
227 QModelIndex duplicateRow( unsigned int row ) {
228 if ( row >= m_items.size() )
229 return QModelIndex();
231 beginInsertRows( QModelIndex(), row+1, row+1 );
232 m_items.insert( m_items.begin() + row + 1, m_items[row] );
233 if ( m_items[row].pgp )
234 m_items[row+1].pgp = false; // enforce pgp exclusivitiy
235 endInsertRows();
236 return index( row+1, 0 );
239 void deleteRow( unsigned int row ) {
240 if ( row >= m_items.size() )
241 return;
243 beginRemoveRows( QModelIndex(), row, row );
244 m_items.erase( m_items.begin() + row );
245 endInsertRows();
248 void clear() {
249 if ( m_items.empty() )
250 return;
251 beginRemoveRows( QModelIndex(), 0, m_items.size()-1 );
252 m_items.clear();
253 endRemoveRows();
256 /* reimp */ int columnCount( const QModelIndex & =QModelIndex() ) const { return NumColumns; }
257 /* reimp */ int rowCount( const QModelIndex & =QModelIndex() ) const { return m_items.size(); }
259 /* reimp */ QVariant data( const QModelIndex & idx, int role ) const;
260 /* reimp */ QVariant headerData( int section, Qt::Orientation o, int role ) const;
262 /* reimp */ Qt::ItemFlags flags( const QModelIndex & idx ) const;
263 /* reimp */ bool setData( const QModelIndex & idx, const QVariant & value, int role );
265 private:
266 bool doSetData( unsigned int row, unsigned int column, const QVariant & value, int role );
267 void setExclusivePgpFlag( unsigned int row );
269 static QString toolTipForColumn( int column );
270 bool isLdapRow( unsigned int row ) const;
271 int firstEditableColumn( unsigned int ) const {
272 return Host;
275 private:
276 struct Item {
277 KUrl url;
278 bool x509 : 1;
279 bool pgp : 1;
281 std::vector<Item> m_items;
282 bool m_openPGPReadOnly : 1;
283 bool m_x509ReadOnly : 1;
284 DirectoryServicesWidget::Schemes m_schemes;
286 private:
287 std::vector<Item>::iterator findExistingUrl( const KUrl & url ) {
288 return std::find_if( m_items.begin(), m_items.end(),
289 bind( KUrl_compare(), url, bind( &Item::url, _1 ) ) );
293 class Delegate : public QItemDelegate {
294 Q_OBJECT
295 public:
296 explicit Delegate( QObject * parent=0 )
297 : QItemDelegate( parent ),
298 m_schemes( DirectoryServicesWidget::AllSchemes )
303 void setAllowedSchemes( const DirectoryServicesWidget::Schemes schemes ) {
304 m_schemes = schemes;
306 DirectoryServicesWidget::Schemes allowedSchemes() const { return m_schemes; }
308 /* reimp */
309 QWidget * createEditor( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & idx ) const {
310 switch ( idx.column() ) {
311 case Model::Scheme:
312 return createSchemeWidget( parent );
313 case Model::Port:
314 return createPortWidget( parent );
316 return QItemDelegate::createEditor( parent, option, idx );
319 /* reimp */
320 void setEditorData( QWidget * editor, const QModelIndex & idx ) const {
321 switch ( idx.column() ) {
322 case Model::Scheme:
323 setSchemeEditorData( qobject_cast<QComboBox*>( editor ), idx.data( Qt::EditRole ).toString() );
324 break;
325 case Model::Port:
326 setPortEditorData( qobject_cast<QSpinBox*>( editor ), idx.data( Qt::EditRole ).toInt() );
327 break;
328 default:
329 QItemDelegate::setEditorData( editor, idx );
330 break;
334 /* reimp */
335 void setModelData( QWidget * editor, QAbstractItemModel * model, const QModelIndex & idx ) const {
336 switch ( idx.column() ) {
337 case Model::Scheme:
338 setSchemeModelData( qobject_cast<QComboBox*>( editor ), model, idx );
339 break;
340 case Model::Port:
341 setPortModelData( qobject_cast<QSpinBox*>( editor ), model, idx );
342 break;
343 default:
344 QItemDelegate::setModelData( editor, model, idx );
345 break;
349 /* reimp */
350 void updateEditorGeometry( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const {
351 if ( index.column() == Model::Scheme || index.column() == Model::Port )
352 editor->setGeometry( calculate_geometry( option.rect, editor->sizeHint() ) );
353 else
354 QItemDelegate::updateEditorGeometry( editor, option, index );
357 private:
358 QWidget * createSchemeWidget( QWidget * parent ) const {
359 if ( !m_schemes )
360 return 0;
361 QComboBox * cb = new QComboBox( parent );
362 for ( unsigned int i = 0 ; i < numProtocols ; ++i )
363 if ( m_schemes & protocols[i].base )
364 cb->addItem( i18n( protocols[i].label ), protocols[i].label );
365 assert( cb->count() > 0 );
366 return cb;
368 void setSchemeEditorData( QComboBox * cb, const QString & scheme ) const {
369 assert( cb );
370 cb->setCurrentIndex( cb->findData( scheme, Qt::UserRole, Qt::MatchFixedString ) );
372 void setSchemeModelData( const QComboBox * cb, QAbstractItemModel * model, const QModelIndex & idx ) const {
373 assert( cb );
374 assert( model );
375 model->setData( idx, cb->itemData( cb->currentIndex() ) );
378 QWidget * createPortWidget( QWidget * parent ) const {
379 QSpinBox * sb = new QSpinBox( parent );
380 sb->setRange( 1, USHRT_MAX ); // valid port numbers
381 return sb;
383 void setPortEditorData( QSpinBox * sb, unsigned short port ) const {
384 assert( sb );
385 sb->setValue( port );
387 void setPortModelData( const QSpinBox * sb, QAbstractItemModel * model, const QModelIndex & idx ) const {
388 assert( sb );
389 assert( model );
390 model->setData( idx, sb->value() );
393 private:
394 DirectoryServicesWidget::Schemes m_schemes;
399 class DirectoryServicesWidget::Private {
400 friend class ::Kleo::DirectoryServicesWidget;
401 DirectoryServicesWidget * const q;
402 public:
403 explicit Private( DirectoryServicesWidget * qq )
404 : q( qq ),
405 protocols( AllProtocols ),
406 readOnlyProtocols( NoProtocol ),
407 model(),
408 delegate(),
409 ui( q )
411 ui.treeView->setModel( &model );
412 ui.treeView->setItemDelegate( &delegate );
414 connect( &model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
415 q, SIGNAL(changed()) );
416 connect( &model, SIGNAL(rowsInserted(QModelIndex,int,int)),
417 q, SIGNAL(changed()) );
418 connect( &model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
419 q, SIGNAL(changed()) );
420 connect( ui.treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
421 q, SLOT(slotSelectionChanged()) );
423 slotShowUserAndPasswordToggled( false );
426 private:
427 void slotNewClicked() {
428 int row = selectedRow();
429 if ( row < 0 )
430 row = currentRow();
431 if ( row < 0 || model.isReadOnlyRow( row ) )
432 if ( protocols & OpenPGPProtocol )
433 slotNewOpenPGPClicked();
434 else if ( protocols & X509Protocol )
435 slotNewX509Clicked();
436 else
437 assert( !"This should not happen.");
438 else
439 edit( model.duplicateRow( row ) );
441 void edit( const QModelIndex & index ) {
442 if ( index.isValid() ) {
443 ui.treeView->clearSelection();
444 ui.treeView->selectionModel()->setCurrentIndex( index, QItemSelectionModel::Select|QItemSelectionModel::Rows );
445 ui.treeView->edit( index );
448 void slotNewX509Clicked() {
449 edit( model.addX509Service( defaultX509Service(), true ) );
451 void slotNewOpenPGPClicked() {
452 edit( model.addOpenPGPService( defaultOpenPGPService(), true ) );
454 void slotDeleteClicked() {
455 model.deleteRow( selectedRow() );
457 void slotSelectionChanged() {
458 enableDisableActions();
460 void slotShowUserAndPasswordToggled( bool on ) {
461 QHeaderView * const hv = ui.treeView->header();
462 assert( hv );
463 hv->setSectionHidden( Model::UserName, !on );
464 hv->setSectionHidden( Model::Password, !on );
467 int selectedRow() const {
468 const QModelIndexList mil = ui.treeView->selectionModel()->selectedRows();
469 return mil.empty() ? -1 : mil.front().row();
471 int currentRow() const {
472 const QModelIndex idx = ui.treeView->selectionModel()->currentIndex();
473 return idx.isValid() ? idx.row() : -1 ;
476 void showHideColumns();
478 void enableDisableActions() {
479 const bool x509 = ( protocols & X509Protocol ) && !( readOnlyProtocols & X509Protocol ) ;
480 const bool pgp = ( protocols & OpenPGPProtocol ) && !( readOnlyProtocols & OpenPGPProtocol ) ;
481 ui.newX509Action.setEnabled( x509 );
482 ui.newOpenPGPAction.setEnabled( pgp );
483 if ( x509 && pgp ) {
484 ui.newTB->setMenu( &ui.newMenu );
485 ui.newTB->setPopupMode( QToolButton::MenuButtonPopup );
486 } else {
487 ui.newTB->setMenu( 0 );
488 ui.newTB->setPopupMode( QToolButton::DelayedPopup );
489 ui.newTB->setEnabled( x509 || pgp );
491 const int row = selectedRow();
492 ui.deleteTB->setEnabled( row >= 0 && !model.isReadOnlyRow( row ) );
495 private:
496 Protocols protocols;
497 Protocols readOnlyProtocols;
498 Model model;
499 Delegate delegate;
500 struct UI : Ui_DirectoryServicesWidget {
501 QAction newX509Action;
502 QAction newOpenPGPAction;
503 QMenu newMenu;
505 explicit UI( DirectoryServicesWidget * q )
506 : Ui_DirectoryServicesWidget(),
507 newX509Action( i18nc("New X.509 Directory Server", "X.509"), q ),
508 newOpenPGPAction( i18nc("New OpenPGP Directory Server", "OpenPGP"), q ),
509 newMenu( q )
511 newX509Action.setObjectName( "newX509Action" );
512 newOpenPGPAction.setObjectName( "newOpenPGPAction" );
513 newMenu.setObjectName( "newMenu" );
515 setupUi( q );
517 connect( &newX509Action, SIGNAL(triggered()), q, SLOT(slotNewX509Clicked()) );
518 connect( &newOpenPGPAction, SIGNAL(triggered()), q, SLOT(slotNewOpenPGPClicked()) );
520 newMenu.addAction( &newX509Action );
521 newMenu.addAction( &newOpenPGPAction );
523 newTB->setMenu( &newMenu );
526 } ui;
529 DirectoryServicesWidget::DirectoryServicesWidget( QWidget * p, Qt::WindowFlags f )
530 : QWidget( p, f ), d( new Private( this ) )
536 DirectoryServicesWidget::~DirectoryServicesWidget() {
537 delete d;
540 void DirectoryServicesWidget::setAllowedSchemes( Schemes schemes ) {
541 d->delegate.setAllowedSchemes( schemes );
542 d->showHideColumns();
545 DirectoryServicesWidget::Schemes DirectoryServicesWidget::allowedSchemes() const {
546 return d->delegate.allowedSchemes();
549 void DirectoryServicesWidget::setAllowedProtocols( Protocols protocols ) {
550 if ( d->protocols == protocols )
551 return;
552 d->protocols = protocols;
553 d->showHideColumns();
554 d->enableDisableActions();
557 DirectoryServicesWidget::Protocols DirectoryServicesWidget::allowedProtocols() const {
558 return d->protocols;
561 void DirectoryServicesWidget::setReadOnlyProtocols( Protocols protocols ) {
562 if ( d->readOnlyProtocols == protocols )
563 return;
564 d->readOnlyProtocols = protocols;
565 d->model.setOpenPGPReadOnly( protocols & OpenPGPProtocol );
566 d->model.setX509ReadOnly( protocols & X509Protocol );
567 d->enableDisableActions();
570 DirectoryServicesWidget::Protocols DirectoryServicesWidget::readOnlyProtocols() const {
571 return d->readOnlyProtocols;
574 void DirectoryServicesWidget::addOpenPGPServices( const KUrl::List & urls ) {
575 Q_FOREACH( const KUrl & url, urls )
576 d->model.addOpenPGPService( url );
579 KUrl::List DirectoryServicesWidget::openPGPServices() const {
580 KUrl::List result;
581 for ( unsigned int i = 0, end = d->model.numServices() ; i != end ; ++i )
582 if ( d->model.isOpenPGPService( i ) )
583 result.push_back( d->model.service( i ) );
584 return result;
587 void DirectoryServicesWidget::addX509Services( const KUrl::List & urls ) {
588 Q_FOREACH( const KUrl & url, urls )
589 d->model.addX509Service( url );
592 KUrl::List DirectoryServicesWidget::x509Services() const {
593 KUrl::List result;
594 for ( unsigned int i = 0, end = d->model.numServices() ; i != end ; ++i )
595 if ( d->model.isX509Service( i ) )
596 result.push_back( d->model.service( i ) );
597 return result;
600 void DirectoryServicesWidget::clear() {
601 if ( !d->model.numServices() )
602 return;
603 d->model.clear();
604 emit changed();
607 void DirectoryServicesWidget::Private::showHideColumns() {
608 QHeaderView * const hv = ui.treeView->header();
609 assert( hv );
610 // don't show 'scheme' column when only accepting X509Protocol (###?)
611 hv->setSectionHidden( Model::Scheme, protocols == X509Protocol );
612 // hide the protocol selection columns for if only one protocol is allowed anyway:
613 hv->setSectionHidden( Model::X509, protocols != AllProtocols );
614 hv->setSectionHidden( Model::OpenPGP, protocols != AllProtocols );
618 // Model
621 QVariant Model::headerData( int section, Qt::Orientation orientation, int role ) const {
622 if ( orientation == Qt::Horizontal )
623 if ( role == Qt::ToolTipRole )
624 return toolTipForColumn( section );
625 else if ( role == Qt::DisplayRole )
626 switch ( section ) {
627 case Scheme: return i18n("Scheme");
628 case Host: return i18n("Server Name");
629 case Port: return i18n("Server Port");
630 case BaseDN: return i18n("Base DN");
631 case UserName: return i18n("User Name");
632 case Password: return i18n("Password");
633 case X509: return i18n("X.509");
634 case OpenPGP: return i18n("OpenPGP");
635 default: return QVariant();
637 else
638 return QVariant();
639 else
640 return QAbstractTableModel::headerData( section, orientation, role );
643 QVariant Model::data( const QModelIndex & index, int role ) const {
644 const unsigned int row = index.row();
645 if ( index.isValid() && row < m_items.size() )
646 switch ( role ) {
647 case Qt::ToolTipRole: {
648 const QString tt = toolTipForColumn( index.column() );
649 if ( !isReadOnlyRow( index.row() ) )
650 return tt;
651 else
652 return tt.isEmpty()
653 ? i18n("(read-only)")
654 : i18nc("amended tooltip; %1: original tooltip",
655 "%1 (read-only)", tt );
657 case Qt::DisplayRole:
658 case Qt::EditRole:
659 switch ( index.column() ) {
660 case Scheme:
661 return display_scheme( m_items[row].url );
662 case Host:
663 return display_host( m_items[row].url );
664 case Port:
665 return display_port( m_items[row].url );
666 case BaseDN:
667 if ( isLdapRow( row ) )
668 return KUrl::fromPercentEncoding( m_items[row].url.query().mid( 1 ).toLatin1() ); // decode query and skip leading '?'
669 else
670 return QVariant();
671 case UserName:
672 return m_items[row].url.user();
673 case Password:
674 return m_items[row].url.pass();
675 case X509:
676 case OpenPGP:
677 default:
678 return QVariant();
680 case Qt::CheckStateRole:
681 switch ( index.column() ) {
682 case X509:
683 return m_items[row].x509 && isLdapRow( row ) ? Qt::Checked : Qt::Unchecked ;
684 case OpenPGP:
685 return m_items[row].pgp ? Qt::Checked : Qt::Unchecked ;
686 default:
687 return QVariant();
690 return QVariant();
693 bool Model::isLdapRow( unsigned int row ) const {
694 if ( row >= m_items.size() )
695 return false;
696 return is_ldap_scheme( m_items[row].url );
699 Qt::ItemFlags Model::flags( const QModelIndex & index ) const {
700 const unsigned int row = index.row();
701 Qt::ItemFlags flags = QAbstractTableModel::flags( index );
702 if ( isReadOnlyRow( row ) )
703 flags &= ~Qt::ItemIsSelectable ;
704 if ( index.isValid() && row < m_items.size() )
705 switch ( index.column() ) {
706 case Scheme:
707 switch ( m_schemes ) {
708 default:
709 if ( !isReadOnlyRow( row ) )
710 return flags | Qt::ItemIsEditable ;
711 // else fall through
712 case DirectoryServicesWidget::HKP:
713 case DirectoryServicesWidget::HTTP:
714 case DirectoryServicesWidget::FTP:
715 case DirectoryServicesWidget::LDAP:
716 // only one scheme allowed -> no editing possible
717 return flags & ~(Qt::ItemIsEditable|Qt::ItemIsEnabled) ;
719 case Host:
720 case Port:
721 if ( isReadOnlyRow( row ) )
722 return flags & ~(Qt::ItemIsEnabled|Qt::ItemIsEnabled) ;
723 else
724 return flags | Qt::ItemIsEditable ;
725 case BaseDN:
726 if ( isLdapRow( row ) && !isReadOnlyRow( row ) )
727 return flags | Qt::ItemIsEditable ;
728 else
729 return flags & ~(Qt::ItemIsEditable|Qt::ItemIsEnabled) ;
730 case UserName:
731 case Password:
732 if ( isReadOnlyRow( row ) )
733 return flags & ~(Qt::ItemIsEditable|Qt::ItemIsEnabled) ;
734 else
735 return flags | Qt::ItemIsEditable ;
736 case X509:
737 if ( !isLdapRow( row ) )
738 return flags & ~(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled) ;
739 // fall through
740 case OpenPGP:
741 if ( isReadOnlyRow( row ) )
742 return flags & ~(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled) ;
743 else
744 return flags | Qt::ItemIsUserCheckable ;
746 return flags;
749 bool Model::setData( const QModelIndex & idx, const QVariant & value, int role ) {
750 const unsigned int row = idx.row();
751 if ( !idx.isValid() || row >= m_items.size() )
752 return false;
753 if ( isReadOnlyRow( row ) )
754 return false;
755 if ( !doSetData( row, idx.column(), value, role ) )
756 return false;
757 emit dataChanged( idx, idx );
758 return true;
761 bool Model::doSetData( unsigned int row, unsigned int column, const QVariant & value, int role ) {
762 if ( role == Qt::EditRole )
763 switch ( column ) {
764 case Scheme:
765 if ( is_default_port( m_items[row].url ) ) {
766 // drag the port along with scheme changes
767 m_items[row].url.setPort( -1 );
768 const QModelIndex changed = index( row, Port );
769 emit dataChanged( changed, changed );
771 m_items[row].url.setProtocol( value.toString() );
772 return true;
773 case Host:
774 if ( display_host( m_items[row].url ) != m_items[row].url.host() ) {
775 m_items[row].url.setProtocol( display_scheme( m_items[row].url ) );
776 m_items[row].url.setPath( "/" );
778 m_items[row].url.setHost( value.toString() );
779 return true;
780 case Port:
781 if ( value.toUInt() == default_port( display_scheme( m_items[row].url ) ) )
782 m_items[row].url.setPort( -1 );
783 else
784 m_items[row].url.setPort( value.toUInt() );
785 return true;
786 case BaseDN:
787 if ( value.toString().isEmpty() ) {
788 m_items[row].url.setPath( QString() );
789 m_items[row].url.setQuery( QString() );
790 } else {
791 m_items[row].url.setPath( "/" ); // workaround KUrl parsing bug
792 m_items[row].url.setQuery( value.toString() );
794 return true;
795 case UserName:
796 m_items[row].url.setUser( value.toString() );
797 return true;
798 case Password:
799 m_items[row].url.setPass( value.toString() );
800 return true;
802 if ( role == Qt::CheckStateRole )
803 switch ( column ) {
804 case X509:
805 m_items[row].x509 = value.toInt() == Qt::Checked ;
806 return true;
807 case OpenPGP:
809 const bool on = value.toInt() == Qt::Checked ;
810 if ( on )
811 setExclusivePgpFlag( row );
812 else
813 m_items[row].pgp = false;
815 return true;
817 return false;
820 void Model::setExclusivePgpFlag( unsigned int row ) {
821 if ( row >= m_items.size() || m_items[row].pgp )
822 return;
823 m_items[row].pgp = true; // dataChanged() for this one is supposed to be emitted by the caller
824 for ( unsigned int i = 0, end = m_items.size() ; i < end ; ++i )
825 if ( i != row )
826 if ( m_items[i].pgp ) {
827 m_items[i].pgp = false;
828 const QModelIndex changed = index( i, OpenPGP );
829 emit dataChanged( changed, changed );
830 break;
834 // static
835 QString Model::toolTipForColumn( int column ) {
836 switch ( column ) {
837 case Scheme: return i18n("Select the access protocol (scheme) that the "
838 "directory service is available through.");
839 case Host: return i18n("Enter the name or IP address of the server "
840 "hosting the directory service.");
841 case Port: return i18n("<b>(Optional, the default is fine in most cases)</b> "
842 "Pick the port number the directory service is "
843 "listening on.");
844 case BaseDN: return i18n("<b>(Only for LDAP)</b> "
845 "Enter the base DN for this LDAP server to "
846 "limit searches to only that subtree of the directory.");
847 case UserName: return i18n("<b>(Optional)</b> "
848 "Enter your user name here, if needed.");
849 case Password: return i18n("<b>(Optional, not recommended)</b> "
850 "Enter your password here, if needed. "
851 "Note that the password will be saved in the clear "
852 "in a config file in your home directory.");
853 case X509: return i18n("Check this column if this directory service is "
854 "providing S/MIME (X.509) certificates.");
855 case OpenPGP: return i18n("Check this column if this directory service is "
856 "providing OpenPGP certificates.");
857 default:
858 return QString();
862 #include "directoryserviceswidget.moc"
863 #include "moc_directoryserviceswidget.cpp"