35 #include "ui_directoryserviceswidget.h"
40 #include <QItemDelegate>
41 #include <QAbstractTableModel>
44 #include <QHeaderView>
49 #include <boost/bind.hpp>
60 using namespace boost;
64 static KUrl defaultX509Service() {
67 url.setHost( i18nc(
"default server name, keep it a valid domain name, ie. no spaces",
"server") );
70 static KUrl defaultOpenPGPService() {
77 static bool is_ldap_scheme(
const KUrl & url ) {
78 const QString scheme = url.protocol();
96 static const unsigned int numProtocols =
sizeof protocols /
sizeof *protocols;
98 static unsigned short default_port(
const QString & scheme ) {
99 for (
unsigned int i = 0 ; i < numProtocols ; ++i )
101 return protocols[i].port;
105 static QString display_scheme(
const KUrl & url ) {
106 if ( url.scheme().isEmpty() )
112 static QString display_host(
const KUrl & url ) {
114 if ( url.host().isEmpty() )
120 static unsigned short display_port(
const KUrl & url ) {
121 if ( url.port() > 0 )
124 return default_port( display_scheme( url ) );
127 static bool is_default_port(
const KUrl & url ) {
128 return display_port( url ) == default_port( display_scheme( url ) ) ;
131 static QRect calculate_geometry(
const QRect & cell,
const QSize & sizeHint ) {
132 const int height = qMax( cell.
height(), sizeHint.
height() );
134 cell.
width(), height );
137 struct KUrl_compare : std::binary_function<KUrl,KUrl,bool> {
138 bool operator()(
const KUrl & lhs,
const KUrl & rhs )
const {
139 return QString::compare( display_scheme( lhs ), display_scheme( rhs ), Qt::CaseInsensitive ) == 0
140 &&
QString::compare( display_host( lhs ), display_host( rhs ), Qt::CaseInsensitive ) == 0
141 && lhs.port() == rhs.port()
142 && lhs.user() == rhs.user()
144 && ( !is_ldap_scheme( lhs )
145 || KUrl::fromPercentEncoding( lhs.query().mid( 1 ).toLatin1() )
146 == KUrl::fromPercentEncoding( rhs.query().mid( 1 ).toLatin1() ) ) ;
153 explicit Model(
QObject * parent=0 )
156 m_openPGPReadOnly( false ),
157 m_x509ReadOnly( false ),
163 void setOpenPGPReadOnly(
bool ro ) {
164 if ( ro == m_openPGPReadOnly )
166 m_openPGPReadOnly = ro;
167 for (
unsigned int row = 0, end = rowCount() ; row != end ; ++row )
168 if ( isOpenPGPService( row ) )
169 emit dataChanged( index( row, 0 ), index( row, NumColumns ) );
172 void setX509ReadOnly(
bool ro ) {
173 if ( ro == m_x509ReadOnly )
176 for (
unsigned int row = 0, end = rowCount() ; row != end ; ++row )
177 if ( isX509Service( row ) )
178 emit dataChanged( index( row, 0 ), index( row, NumColumns ) );
181 QModelIndex addOpenPGPService(
const KUrl & url,
bool force=
false ) {
182 return addService( url,
false,
true, force );
184 QModelIndex addX509Service(
const KUrl & url,
bool force=
false ) {
185 return addService( url,
true,
false, force );
187 QModelIndex addService(
const KUrl & url,
bool x509,
bool pgp,
bool force ) {
188 const std::vector<Item>::iterator it = force ? m_items.end() : findExistingUrl( url ) ;
190 if ( it != m_items.end() ) {
194 row = it - m_items.begin() ;
195 emit dataChanged( index( row, std::min( X509, OpenPGP ) ), index( row, std::max( X509, OpenPGP ) ) );
198 const Item item = { url, x509, pgp };
199 row = m_items.size();
201 m_items.push_back( item );
204 return index( row, firstEditableColumn( row ) );
207 unsigned int numServices()
const {
return m_items.size(); }
208 bool isOpenPGPService(
unsigned int row )
const {
return row < m_items.size() && m_items[row].pgp; }
209 bool isX509Service(
unsigned int row )
const {
return row < m_items.size() && m_items[row].x509 && isLdapRow( row ) ; }
210 KUrl service(
unsigned int row )
const {
return row < m_items.size() ? m_items[row].url : KUrl() ; }
212 bool isReadOnlyRow(
unsigned int row )
const {
213 return ( isX509Service( row ) && m_x509ReadOnly )
214 || ( isOpenPGPService( row ) && m_openPGPReadOnly );
231 if ( row >= m_items.size() )
235 m_items.insert( m_items.begin() + row + 1, m_items[row] );
236 if ( m_items[row].pgp )
237 m_items[row+1].pgp =
false;
239 return index( row+1, 0 );
242 void deleteRow(
unsigned int row ) {
243 if ( row >= m_items.size() )
247 m_items.erase( m_items.begin() + row );
252 if ( m_items.empty() )
254 beginRemoveRows(
QModelIndex(), 0, m_items.size()-1 );
263 QVariant headerData(
int section, Qt::Orientation o,
int role )
const;
269 bool doSetData(
unsigned int row,
unsigned int column,
const QVariant & value,
int role );
270 void setExclusivePgpFlag(
unsigned int row );
272 static QString toolTipForColumn(
int column );
273 bool isLdapRow(
unsigned int row )
const;
274 int firstEditableColumn(
unsigned int )
const {
284 std::vector<Item> m_items;
285 bool m_openPGPReadOnly : 1;
286 bool m_x509ReadOnly : 1;
287 DirectoryServicesWidget::Schemes m_schemes;
290 std::vector<Item>::iterator findExistingUrl(
const KUrl & url ) {
292 boost::bind( KUrl_compare(), url, boost::bind( &Item::url, _1 ) ) );
299 explicit Delegate(
QObject * parent=0 )
306 void setAllowedSchemes(
const DirectoryServicesWidget::Schemes schemes ) {
309 DirectoryServicesWidget::Schemes allowedSchemes()
const {
return m_schemes; }
315 return createSchemeWidget( parent );
317 return createPortWidget( parent );
326 setSchemeEditorData( qobject_cast<QComboBox*>( editor ), idx.
data( Qt::EditRole ).
toString() );
329 setPortEditorData( qobject_cast<QSpinBox*>( editor ), idx.
data( Qt::EditRole ).
toInt() );
341 setSchemeModelData( qobject_cast<QComboBox*>( editor ), model, idx );
344 setPortModelData( qobject_cast<QSpinBox*>( editor ), model, idx );
354 if ( index.
column() == Model::Scheme || index.
column() == Model::Port )
365 for (
unsigned int i = 0 ; i < numProtocols ; ++i )
366 if ( m_schemes & protocols[i].base )
368 assert( cb->
count() > 0 );
386 void setPortEditorData(
QSpinBox * sb,
unsigned short port )
const {
397 DirectoryServicesWidget::Schemes m_schemes;
402 class DirectoryServicesWidget::Private {
403 friend class ::Kleo::DirectoryServicesWidget;
408 protocols( AllProtocols ),
409 readOnlyProtocols( NoProtocol ),
414 ui.treeView->setModel( &model );
415 ui.treeView->setItemDelegate( &delegate );
418 q, SIGNAL(changed()) );
419 connect( &model, SIGNAL(rowsInserted(
QModelIndex,
int,
int)),
420 q, SIGNAL(changed()) );
421 connect( &model, SIGNAL(rowsRemoved(
QModelIndex,
int,
int)),
422 q, SIGNAL(changed()) );
424 q, SLOT(slotSelectionChanged()) );
426 slotShowUserAndPasswordToggled(
false );
430 void slotNewClicked() {
431 int row = selectedRow();
434 if ( row < 0 || model.isReadOnlyRow( row ) )
435 if ( protocols & OpenPGPProtocol )
436 slotNewOpenPGPClicked();
437 else if ( protocols & X509Protocol )
438 slotNewX509Clicked();
440 assert( !
"This should not happen.");
442 edit( model.duplicateRow( row ) );
446 ui.treeView->clearSelection();
447 ui.treeView->selectionModel()->setCurrentIndex( index, QItemSelectionModel::Select|QItemSelectionModel::Rows );
448 ui.treeView->edit( index );
451 void slotNewX509Clicked() {
452 edit( model.addX509Service( defaultX509Service(),
true ) );
454 void slotNewOpenPGPClicked() {
455 edit( model.addOpenPGPService( defaultOpenPGPService(),
true ) );
457 void slotDeleteClicked() {
458 model.deleteRow( selectedRow() );
460 void slotSelectionChanged() {
461 enableDisableActions();
463 void slotShowUserAndPasswordToggled(
bool on ) {
470 int selectedRow()
const {
471 const QModelIndexList mil = ui.treeView->selectionModel()->selectedRows();
472 return mil.empty() ? -1 : mil.front().row();
474 int currentRow()
const {
475 const QModelIndex idx = ui.treeView->selectionModel()->currentIndex();
479 void showHideColumns();
481 void enableDisableActions() {
482 const bool x509 = ( protocols & X509Protocol ) && !( readOnlyProtocols & X509Protocol ) ;
483 const bool pgp = ( protocols & OpenPGPProtocol ) && !( readOnlyProtocols & OpenPGPProtocol ) ;
484 ui.newX509Action.setEnabled( x509 );
485 ui.newOpenPGPAction.setEnabled( pgp );
487 ui.newTB->setMenu( &ui.newMenu );
488 ui.newTB->setPopupMode( QToolButton::MenuButtonPopup );
490 ui.newTB->setMenu( 0 );
491 ui.newTB->setPopupMode( QToolButton::DelayedPopup );
492 ui.newTB->setEnabled( x509 || pgp );
494 const int row = selectedRow();
495 ui.deleteTB->setEnabled( row >= 0 && !model.isReadOnlyRow( row ) );
500 Protocols readOnlyProtocols;
503 struct UI : Ui_DirectoryServicesWidget {
509 : Ui_DirectoryServicesWidget(),
510 newX509Action( i18nc(
"New X.509 Directory Server",
"X.509"), q ),
511 newOpenPGPAction( i18nc(
"New OpenPGP Directory Server",
"OpenPGP"), q ),
514 newX509Action.setObjectName(
QLatin1String(
"newX509Action") );
515 newOpenPGPAction.setObjectName(
QLatin1String(
"newOpenPGPAction") );
520 connect( &newX509Action, SIGNAL(triggered()), q, SLOT(slotNewX509Clicked()) );
521 connect( &newOpenPGPAction, SIGNAL(triggered()), q, SLOT(slotNewOpenPGPClicked()) );
523 newMenu.addAction( &newX509Action );
524 newMenu.addAction( &newOpenPGPAction );
526 newTB->setMenu( &newMenu );
533 :
QWidget( p, f ), d( new Private( this ) )
544 d->delegate.setAllowedSchemes( schemes );
545 d->showHideColumns();
549 return d->delegate.allowedSchemes();
553 if ( d->protocols == protocols )
555 d->protocols = protocols;
556 d->showHideColumns();
557 d->enableDisableActions();
565 if ( d->readOnlyProtocols == protocols )
567 d->readOnlyProtocols = protocols;
568 d->model.setOpenPGPReadOnly( protocols & OpenPGPProtocol );
569 d->model.setX509ReadOnly( protocols & X509Protocol );
570 d->enableDisableActions();
574 return d->readOnlyProtocols;
578 Q_FOREACH(
const KUrl & url, urls )
579 d->model.addOpenPGPService( url );
584 for (
unsigned int i = 0, end = d->model.numServices() ; i != end ; ++i )
585 if ( d->model.isOpenPGPService( i ) )
586 result.push_back( d->model.service( i ) );
591 Q_FOREACH(
const KUrl & url, urls )
592 d->model.addX509Service( url );
597 for (
unsigned int i = 0, end = d->model.numServices() ; i != end ; ++i )
598 if ( d->model.isX509Service( i ) )
599 result.push_back( d->model.service( i ) );
604 if ( !d->model.numServices() )
610 void DirectoryServicesWidget::Private::showHideColumns() {
624 QVariant Model::headerData(
int section, Qt::Orientation orientation,
int role )
const {
625 if ( orientation == Qt::Horizontal )
626 if ( role == Qt::ToolTipRole )
627 return toolTipForColumn( section );
628 else if ( role == Qt::DisplayRole )
630 case Scheme:
return i18n(
"Scheme");
631 case Host:
return i18n(
"Server Name");
632 case Port:
return i18n(
"Server Port");
633 case BaseDN:
return i18n(
"Base DN");
634 case UserName:
return i18n(
"User Name");
635 case Password:
return i18n(
"Password");
636 case X509:
return i18n(
"X.509");
637 case OpenPGP:
return i18n(
"OpenPGP");
647 const unsigned int row = index.
row();
648 if ( index.
isValid() && row < m_items.size() )
650 case Qt::ToolTipRole: {
652 if ( !isReadOnlyRow( index.
row() ) )
656 ? i18n(
"(read-only)")
657 : i18nc(
"amended tooltip; %1: original tooltip",
658 "%1 (read-only)", tt );
660 case Qt::DisplayRole:
662 switch ( index.
column() ) {
664 return display_scheme( m_items[row].url );
666 return display_host( m_items[row].url );
668 return display_port( m_items[row].url );
670 if ( isLdapRow( row ) )
671 return KUrl::fromPercentEncoding( m_items[row].url.query().mid( 1 ).toLatin1() );
675 return m_items[row].url.user();
677 return m_items[row].url.pass();
683 case Qt::CheckStateRole:
684 switch ( index.
column() ) {
686 return m_items[row].x509 && isLdapRow( row ) ? Qt::Checked : Qt::Unchecked ;
688 return m_items[row].pgp ? Qt::Checked : Qt::Unchecked ;
696 bool Model::isLdapRow(
unsigned int row )
const {
697 if ( row >= m_items.size() )
699 return is_ldap_scheme( m_items[row].url );
703 const unsigned int row = index.
row();
705 if ( isReadOnlyRow( row ) )
706 flags &= ~Qt::ItemIsSelectable ;
707 if ( index.
isValid() && row < m_items.size() )
708 switch ( index.
column() ) {
710 switch ( m_schemes ) {
712 if ( !isReadOnlyRow( row ) )
713 return flags | Qt::ItemIsEditable ;
720 return flags & ~(Qt::ItemIsEditable|Qt::ItemIsEnabled) ;
724 if ( isReadOnlyRow( row ) )
725 return flags & ~(Qt::ItemIsEditable|Qt::ItemIsEnabled) ;
727 return flags | Qt::ItemIsEditable ;
729 if ( isLdapRow( row ) && !isReadOnlyRow( row ) )
730 return flags | Qt::ItemIsEditable ;
732 return flags & ~(Qt::ItemIsEditable|Qt::ItemIsEnabled) ;
735 if ( isReadOnlyRow( row ) )
736 return flags & ~(Qt::ItemIsEditable|Qt::ItemIsEnabled) ;
738 return flags | Qt::ItemIsEditable ;
740 if ( !isLdapRow( row ) )
741 return flags & ~(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled) ;
744 if ( isReadOnlyRow( row ) )
745 return flags & ~(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled) ;
747 return flags | Qt::ItemIsUserCheckable ;
753 const unsigned int row = idx.
row();
754 if ( !idx.
isValid() || row >= m_items.size() )
756 if ( isReadOnlyRow( row ) )
758 if ( !doSetData( row, idx.
column(), value, role ) )
760 emit dataChanged( idx, idx );
764 bool Model::doSetData(
unsigned int row,
unsigned int column,
const QVariant & value,
int role ) {
765 if ( role == Qt::EditRole )
768 if ( is_default_port( m_items[row].url ) ) {
770 m_items[row].url.setPort( -1 );
772 emit dataChanged( changed, changed );
774 m_items[row].url.setProtocol( value.
toString() );
777 if ( display_host( m_items[row].url ) != m_items[row].url.host() ) {
778 m_items[row].url.setProtocol( display_scheme( m_items[row].url ) );
781 m_items[row].url.setHost( value.
toString() );
784 if ( value.
toUInt() == default_port( display_scheme( m_items[row].url ) ) )
785 m_items[row].url.setPort( -1 );
787 m_items[row].url.setPort( value.
toUInt() );
791 m_items[row].url.setPath(
QString() );
792 m_items[row].url.setQuery(
QString() );
795 m_items[row].url.setQuery( value.
toString() );
799 m_items[row].url.setUserName( value.
toString() );
802 m_items[row].url.setPassword( value.
toString() );
805 if ( role == Qt::CheckStateRole )
808 m_items[row].x509 = value.
toInt() == Qt::Checked ;
812 const bool on = value.
toInt() == Qt::Checked ;
814 setExclusivePgpFlag( row );
816 m_items[row].pgp =
false;
823 void Model::setExclusivePgpFlag(
unsigned int row ) {
824 if ( row >= m_items.size() || m_items[row].pgp )
826 m_items[row].pgp =
true;
827 for (
unsigned int i = 0, end = m_items.size() ; i < end ; ++i )
829 if ( m_items[i].pgp ) {
830 m_items[i].pgp =
false;
832 emit dataChanged( changed, changed );
838 QString Model::toolTipForColumn(
int column ) {
840 case Scheme:
return i18n(
"Select the access protocol (scheme) that the "
841 "directory service is available through.");
842 case Host:
return i18n(
"Enter the name or IP address of the server "
843 "hosting the directory service.");
844 case Port:
return i18n(
"<b>(Optional, the default is fine in most cases)</b> "
845 "Pick the port number the directory service is "
847 case BaseDN:
return i18n(
"<b>(Only for LDAP)</b> "
848 "Enter the base DN for this LDAP server to "
849 "limit searches to only that subtree of the directory.");
850 case UserName:
return i18n(
"<b>(Optional)</b> "
851 "Enter your user name here, if needed.");
852 case Password:
return i18n(
"<b>(Optional, not recommended)</b> "
853 "Enter your password here, if needed. "
854 "Note that the password will be saved in the clear "
855 "in a config file in your home directory.");
856 case X509:
return i18n(
"Check this column if this directory service is "
857 "providing S/MIME (X.509) certificates.");
858 case OpenPGP:
return i18n(
"Check this column if this directory service is "
859 "providing OpenPGP certificates.");
865 #include "directoryserviceswidget.moc"
866 #include "moc_directoryserviceswidget.cpp"
void setRange(int minimum, int maximum)
void addItem(const QString &text, const QVariant &userData)
uint toUInt(bool *ok) const
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const
int toInt(bool *ok) const
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
QVariant itemData(int index, int role) const
virtual QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
int findData(const QVariant &data, int role, QFlags< Qt::MatchFlag > flags) const
QVariant data(int role) const
void setCurrentIndex(int index)
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
virtual Qt::ItemFlags flags(const QModelIndex &index) const
int compare(const QString &other) const