[auth] Add authcfg ID edit widget and support for changing the ID

This commit is contained in:
Larry Shaffer 2015-09-24 23:12:05 -06:00
parent 0975c9f7aa
commit 12de332ebd
14 changed files with 435 additions and 65 deletions

View File

@ -5,14 +5,14 @@ class QgsAuthConfigEditor : QWidget
%End
public:
explicit QgsAuthConfigEditor( QWidget *parent /TransferThis/ = 0, bool showUtilities = true, QgsMessageBar *msgbar /TransferThis/ = 0 );
explicit QgsAuthConfigEditor( QWidget *parent /TransferThis/ = 0, bool showUtilities = true, bool relayMessages = true );
~QgsAuthConfigEditor();
void toggleTitleVisibility( bool visible );
public slots:
void showUtilitiesButton( bool show = true );
void setShowUtilitiesButton( bool show = true );
void setMessageBar( QgsMessageBar *msgbar /TransferThis/ = 0 );
void setRelayMessages( bool relay = true );
};

View File

@ -915,10 +915,24 @@ bool QgsAuthManager::storeAuthenticationConfig( QgsAuthMethodConfig &mconfig )
return false;
}
QString uid = mconfig.id();
bool passedinID = !uid.isEmpty();
if ( uid.isEmpty() )
{
uid = uniqueConfigId();
}
else if ( configIds().contains( uid ) )
{
const char* err = QT_TR_NOOP( "Store config: FAILED because pre-defined config ID is not unique" );
QgsDebugMsg( err );
emit messageOut( tr( err ), authManTag(), WARNING );
return false;
}
QString configstring = mconfig.configString();
if ( configstring.isEmpty() )
{
const char* err = QT_TR_NOOP( "Store config: FAILED because config is empty" );
const char* err = QT_TR_NOOP( "Store config: FAILED because config string is empty" );
QgsDebugMsg( err );
emit messageOut( tr( err ), authManTag(), WARNING );
return false;
@ -936,8 +950,6 @@ bool QgsAuthManager::storeAuthenticationConfig( QgsAuthMethodConfig &mconfig )
query.prepare( QString( "INSERT INTO %1 (id, name, uri, type, version, config) "
"VALUES (:id, :name, :uri, :type, :version, :config)" ).arg( authDbConfigTable() ) );
QString uid = uniqueConfigId();
query.bindValue( ":id", uid );
query.bindValue( ":name", mconfig.name() );
query.bindValue( ":uri", mconfig.uri() );
@ -955,7 +967,8 @@ bool QgsAuthManager::storeAuthenticationConfig( QgsAuthMethodConfig &mconfig )
return false;
// passed-in config should now be like as if it was just loaded from db
mconfig.setId( uid );
if ( !passedinID )
mconfig.setId( uid );
updateConfigAuthMethods();

View File

@ -65,6 +65,7 @@ SET(QGIS_GUI_SRCS
auth/qgsauthcerttrustpolicycombobox.cpp
auth/qgsauthconfigedit.cpp
auth/qgsauthconfigeditor.cpp
auth/qgsauthconfigidedit.cpp
auth/qgsauthconfigselect.cpp
auth/qgsautheditorwidgets.cpp
auth/qgsauthguiutils.cpp
@ -450,6 +451,7 @@ SET(QGIS_GUI_MOC_HDRS
auth/qgsauthcerttrustpolicycombobox.h
auth/qgsauthconfigedit.h
auth/qgsauthconfigeditor.h
auth/qgsauthconfigidedit.h
auth/qgsauthconfigselect.h
auth/qgsautheditorwidgets.h
auth/qgsauthidentitieseditor.h
@ -567,6 +569,7 @@ SET(QGIS_GUI_HDRS
auth/qgsauthcerttrustpolicycombobox.h
auth/qgsauthconfigedit.h
auth/qgsauthconfigeditor.h
auth/qgsauthconfigidedit.h
auth/qgsauthconfigselect.h
auth/qgsautheditorwidgets.h
auth/qgsauthguiutils.h

View File

@ -19,6 +19,7 @@
#include <QPushButton>
#include "qgsauthconfig.h"
#include "qgsauthconfigidedit.h"
#include "qgsauthmanager.h"
#include "qgsauthmethodedit.h"
#include "qgslogger.h"
@ -53,6 +54,7 @@ QgsAuthConfigEdit::QgsAuthConfigEdit( QWidget *parent , const QString& authcfg ,
mAuthNotifyLayout->addWidget( mAuthNotify );
mAuthCfg.clear(); // otherwise will contiue to try authenticate (and fail) after save
buttonBox->button( QDialogButtonBox::Save )->setEnabled( false );
}
else
{
@ -68,6 +70,8 @@ QgsAuthConfigEdit::QgsAuthConfigEdit( QWidget *parent , const QString& authcfg ,
connect( cmbAuthMethods, SIGNAL( currentIndexChanged( int ) ),
this, SLOT( validateAuth() ) );
connect( authCfgEdit, SIGNAL( validityChanged( bool ) ), this, SLOT( validateAuth() ) );
// needed (if only combobox is ever changed)?
// connect( stkwAuthMethods, SIGNAL( currentChanged( int ) ),
// cmbAuthMethods, SLOT( setCurrentIndex( int ) ) );
@ -128,7 +132,9 @@ void QgsAuthConfigEdit::populateAuthMethods()
void QgsAuthConfigEdit::loadConfig()
{
if ( mAuthCfg.isEmpty() )
bool emptyAuthCfg = mAuthCfg.isEmpty();
authCfgEdit->setAllowEmptyId( emptyAuthCfg );
if ( emptyAuthCfg )
{
return;
}
@ -156,7 +162,7 @@ void QgsAuthConfigEdit::loadConfig()
// load basic info
leName->setText( mconfig.name() );
leResource->setText( mconfig.uri() );
leAuthCfg->setText( mconfig.id() );
authCfgEdit->setAuthConfigId( mconfig.id() );
QString authMethodKey = QgsAuthManager::instance()->configAuthMethodKey( mAuthCfg );
@ -243,28 +249,65 @@ void QgsAuthConfigEdit::saveConfig()
return;
}
if ( !mAuthCfg.isEmpty() ) // update
QString authCfgId( authCfgEdit->configId() );
if ( !mAuthCfg.isEmpty() )
{
mconfig.setId( mAuthCfg );
if ( QgsAuthManager::instance()->updateAuthenticationConfig( mconfig ) )
if ( authCfgId == mAuthCfg ) // update
{
emit authenticationConfigUpdated( mAuthCfg );
mconfig.setId( mAuthCfg );
if ( QgsAuthManager::instance()->updateAuthenticationConfig( mconfig ) )
{
emit authenticationConfigUpdated( mAuthCfg );
}
else
{
QgsDebugMsg( QString( "Updating auth config FAILED for authcfg: %1" ).arg( mAuthCfg ) );
}
}
else
else // store new with unique ID, then delete previous
{
QgsDebugMsg( QString( "Updating auth config FAILED for authcfg: %1" ).arg( mAuthCfg ) );
mconfig.setId( authCfgId );
if ( QgsAuthManager::instance()->storeAuthenticationConfig( mconfig ) )
{
emit authenticationConfigStored( authCfgId );
if ( !QgsAuthManager::instance()->removeAuthenticationConfig( mAuthCfg ) )
{
QgsDebugMsg( QString( "Removal of older auth config FAILED" ) );
}
mAuthCfg = authCfgId;
}
else
{
QgsDebugMsg( QString( "Storing new auth config with user-created unique ID FAILED" ) );
}
}
}
else // create new
else if ( mAuthCfg.isEmpty() )
{
if ( QgsAuthManager::instance()->storeAuthenticationConfig( mconfig ) )
if ( authCfgId.isEmpty() ) // create new with generated ID
{
mAuthCfg = mconfig.id();
emit authenticationConfigStored( mAuthCfg );
if ( QgsAuthManager::instance()->storeAuthenticationConfig( mconfig ) )
{
mAuthCfg = mconfig.id();
emit authenticationConfigStored( mAuthCfg );
}
else
{
QgsDebugMsg( QString( "Storing new auth config FAILED" ) );
}
}
else
else // create new with user-created unique ID
{
QgsDebugMsg( QString( "Storing new auth config FAILED" ) );
mconfig.setId( authCfgId );
if ( QgsAuthManager::instance()->storeAuthenticationConfig( mconfig ) )
{
mAuthCfg = authCfgId;
emit authenticationConfigStored( mAuthCfg );
}
else
{
QgsDebugMsg( QString( "Storing new auth config with user-created unique ID FAILED" ) );
}
}
}
@ -289,7 +332,7 @@ void QgsAuthConfigEdit::clearAll()
{
leName->clear();
leResource->clear();
leAuthCfg->clear();
authCfgEdit->clear();
for ( int i = 0; i < stkwAuthMethods->count(); i++ )
{
@ -315,6 +358,8 @@ void QgsAuthConfigEdit::validateAuth()
authok = authok && editWidget->validateConfig();
authok = authok && authCfgEdit->validate();
buttonBox->button( QDialogButtonBox::Save )->setEnabled( authok );
}

View File

@ -26,8 +26,9 @@
#include "qgsauthconfigedit.h"
#include "qgsauthguiutils.h"
QgsAuthConfigEditor::QgsAuthConfigEditor( QWidget *parent, bool showUtilities, QgsMessageBar *msgbar )
QgsAuthConfigEditor::QgsAuthConfigEditor( QWidget *parent, bool showUtilities, bool relayMessages )
: QWidget( parent )
, mRelayMessages( relayMessages )
, mConfigModel( 0 )
, mAuthUtilitiesMenu( 0 )
, mActionSetMasterPassword( 0 )
@ -50,8 +51,7 @@ QgsAuthConfigEditor::QgsAuthConfigEditor( QWidget *parent, bool showUtilities, Q
{
setupUi( this );
setMessageBar( msgbar );
showUtilitiesButton( showUtilities );
setShowUtilitiesButton( showUtilities );
mConfigModel = new QSqlTableModel( this, QgsAuthManager::instance()->authDbConnection() );
mConfigModel->setTable( QgsAuthManager::instance()->authDbConfigTable() );
@ -83,8 +83,11 @@ QgsAuthConfigEditor::QgsAuthConfigEditor( QWidget *parent, bool showUtilities, Q
connect( tableViewConfigs, SIGNAL( doubleClicked( QModelIndex ) ),
this, SLOT( on_btnEditConfig_clicked() ) );
connect( QgsAuthManager::instance(), SIGNAL( messageOut( const QString&, const QString&, QgsAuthManager::MessageLevel ) ),
this, SLOT( authMessageOut( const QString&, const QString&, QgsAuthManager::MessageLevel ) ) );
if ( mRelayMessages )
{
connect( QgsAuthManager::instance(), SIGNAL( messageOut( const QString&, const QString&, QgsAuthManager::MessageLevel ) ),
this, SLOT( authMessageOut( const QString&, const QString&, QgsAuthManager::MessageLevel ) ) );
}
connect( QgsAuthManager::instance(), SIGNAL( authDatabaseChanged() ),
this, SLOT( refreshTableView() ) );
@ -169,18 +172,29 @@ void QgsAuthConfigEditor::toggleTitleVisibility( bool visible )
}
}
void QgsAuthConfigEditor::showUtilitiesButton( bool show )
void QgsAuthConfigEditor::setShowUtilitiesButton( bool show )
{
btnAuthUtilities->setVisible( show );
}
void QgsAuthConfigEditor::setMessageBar( QgsMessageBar *msgbar )
void QgsAuthConfigEditor::setRelayMessages( bool relay )
{
if ( msgbar )
if ( relay == mRelayMessages )
{
delete mMsgBar;
mMsgBar = msgbar;
return;
}
if ( mRelayMessages )
{
disconnect( QgsAuthManager::instance(), SIGNAL( messageOut( const QString&, const QString&, QgsAuthManager::MessageLevel ) ),
this, SLOT( authMessageOut( const QString&, const QString&, QgsAuthManager::MessageLevel ) ) );
mRelayMessages = relay;
return;
}
connect( QgsAuthManager::instance(), SIGNAL( messageOut( const QString&, const QString&, QgsAuthManager::MessageLevel ) ),
this, SLOT( authMessageOut( const QString&, const QString&, QgsAuthManager::MessageLevel ) ) );
mRelayMessages = relay;
}
void QgsAuthConfigEditor::refreshTableView()

View File

@ -35,21 +35,22 @@ class GUI_EXPORT QgsAuthConfigEditor : public QWidget, private Ui::QgsAuthConfig
public:
/**
* Widget for editing authentication configurations directly in database
* @param parent Parent widget
* @param showUtilities Whether to show the widget's utilities button
* @param msgbar Substitute internal message bar for another
* @param relayMessages Whether to relay auth manager messages to internal message bar
*/
explicit QgsAuthConfigEditor( QWidget *parent = 0, bool showUtilities = true, QgsMessageBar *msgbar = 0 );
explicit QgsAuthConfigEditor( QWidget *parent = 0, bool showUtilities = true, bool relayMessages = true );
~QgsAuthConfigEditor();
/** Hide the widget's title, e.g. when embedding */
void toggleTitleVisibility( bool visible );
public slots:
/** Whether to show the widget's utilities button, e.g. when embedding */
void showUtilitiesButton( bool show = true );
/** Set whether to show the widget's utilities button, e.g. when embedding */
void setShowUtilitiesButton( bool show = true );
/** Substitute internal message bar for another, e.g. when embedding */
void setMessageBar( QgsMessageBar *msgbar = 0 );
/** Set whether to relay auth manager messages to internal message bar, e.g. when embedding */
void setRelayMessages( bool relay = true );
private slots:
/** Repopulate the view with table contents */
@ -89,6 +90,7 @@ class GUI_EXPORT QgsAuthConfigEditor : public QWidget, private Ui::QgsAuthConfig
void on_btnRemoveConfig_clicked();
private:
bool mRelayMessages;
QgsMessageBar * messageBar();
int messageTimeout();
QString selectedConfigId();

View File

@ -0,0 +1,124 @@
/***************************************************************************
qgsauthconfigidedit.cpp
---------------------
begin : September, 2015
copyright : (C) 2015 by Boundless Spatial, Inc. USA
author : Larry Shaffer
email : lshaffer at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsauthconfigidedit.h"
#include "ui_qgsauthconfigidedit.h"
#include "qgsauthguiutils.h"
#include "qgsauthmanager.h"
QgsAuthConfigIdEdit::QgsAuthConfigIdEdit( QWidget *parent, const QString &authcfg , bool allowEmpty )
: QWidget( parent )
, mAuthCfgOrig( authcfg )
, mValid( false )
, mAllowEmpty( allowEmpty )
{
setupUi( this );
leAuthCfg->setReadOnly( true );
connect( this, SIGNAL( validityChanged( bool ) ), this, SLOT( updateValidityStyle( bool ) ) );
leAuthCfg->setText( authcfg );
updateValidityStyle( validate() );
}
QgsAuthConfigIdEdit::~QgsAuthConfigIdEdit()
{
}
const QString QgsAuthConfigIdEdit::configId()
{
if ( validate() )
{
return leAuthCfg->text();
}
return QString();
}
bool QgsAuthConfigIdEdit::validate()
{
QString authcfg( leAuthCfg->text() );
bool curvalid = (( authcfg == mAuthCfgOrig && authcfg.size() == 7 )
|| ( mAllowEmpty && authcfg.size() == 0 ) );
if ( !curvalid && authcfg.size() == 7 && isAlphaNumeric( authcfg ) )
{
curvalid = QgsAuthManager::instance()->configIdUnique( authcfg );
}
if ( mValid != curvalid )
{
mValid = curvalid;
emit validityChanged( curvalid );
}
return curvalid;
}
void QgsAuthConfigIdEdit::setAuthConfigId( const QString &authcfg )
{
if ( mAuthCfgOrig.isEmpty() )
{
mAuthCfgOrig = authcfg;
}
leAuthCfg->setText( authcfg );
validate();
}
void QgsAuthConfigIdEdit::setAllowEmptyId( bool allowed )
{
mAllowEmpty = allowed;
validate();
}
void QgsAuthConfigIdEdit::clear()
{
leAuthCfg->setText( mAuthCfgOrig );
updateValidityStyle( true );
}
void QgsAuthConfigIdEdit::updateValidityStyle( bool valid )
{
QString ss( "QLineEdit{" );
ss += valid ? "" : QString( "color: %1;" ).arg( QgsAuthGuiUtils::redColor().name() );
ss += !btnLock->isChecked() ? "" : QString( "background-color: %1;" ).arg( QgsAuthGuiUtils::yellowColor().name() );
ss += "}";
leAuthCfg->setStyleSheet( ss );
}
void QgsAuthConfigIdEdit::on_btnLock_toggled( bool checked )
{
leAuthCfg->setReadOnly( !checked );
if ( checked )
leAuthCfg->setFocus();
updateValidityStyle( validate() );
}
void QgsAuthConfigIdEdit::on_leAuthCfg_textChanged( const QString &txt )
{
Q_UNUSED( txt );
validate();
}
bool QgsAuthConfigIdEdit::isAlphaNumeric( const QString &authcfg )
{
QRegExp rx( "([a-z]|[A-Z]|[0-9]){7}" );
return rx.indexIn( authcfg ) != -1;
}

View File

@ -0,0 +1,80 @@
/***************************************************************************
qgsauthconfigidedit.h
---------------------
begin : September, 2015
copyright : (C) 2015 by Boundless Spatial, Inc. USA
author : Larry Shaffer
email : lshaffer at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSAUTHCONFIGIDEDIT_H
#define QGSAUTHCONFIGIDEDIT_H
#include "ui_qgsauthconfigidedit.h"
#include <QWidget>
/** \ingroup gui
* \brief Custom widget for editing an authentication configuration ID
* \note Validates the input against the database and for ID's 7-character alphanumeric syntax
*/
class GUI_EXPORT QgsAuthConfigIdEdit : public QWidget, private Ui::QgsAuthConfigIdEdit
{
Q_OBJECT
public:
/**
* Widget to unlock and edit an authentication configuration ID
* @param parent Parent widget
* @param authcfg Authentication configuration ID
* @param allowEmpty Whether to allow no ID to be set, even when editing, e.g. Add config functions
*/
explicit QgsAuthConfigIdEdit( QWidget *parent = 0, const QString &authcfg = QString(), bool allowEmpty = true );
~QgsAuthConfigIdEdit();
/** The authentication configuration ID, if valid, otherwise null QString */
QString const configId();
/** Whether to allow no ID to be set */
bool allowEmptyId() { return mAllowEmpty;}
/** Validate the widget state and ID */
bool validate();
signals:
/** Validity of the ID has changed */
void validityChanged( bool valid );
public slots:
/** Set the authentication configuration ID, storing it, and validating the passed value */
void setAuthConfigId( const QString &authcfg );
/** Set whether to allow no ID to be set */
void setAllowEmptyId( bool allowed );
/** Clear all of the widget's editing state and contents */
void clear();
private slots:
void updateValidityStyle( bool valid );
void on_btnLock_toggled( bool checked );
void on_leAuthCfg_textChanged( const QString &txt );
private:
bool isAlphaNumeric( const QString &authcfg );
QString mAuthCfgOrig;
bool mValid;
bool mAllowEmpty;
};
#endif // QGSAUTHCONFIGIDEDIT_H

View File

@ -173,7 +173,8 @@ void QgsAuthConfigSelect::on_btnConfigEdit_clicked()
ace->setWindowModality( Qt::WindowModal );
if ( ace->exec() )
{
setConfigId( mAuthCfg );
qDebug( "Edit returned config Id: %s", ace->configId().toAscii().constData() );
setConfigId( ace->configId() );
}
ace->deleteLater();
}

View File

@ -94,8 +94,8 @@ QgsAuthEditorWidgets::QgsAuthEditorWidgets( QWidget *parent )
: QWidget( parent )
{
setupUi( this );
wdgtConfigEditor->setMessageBar( messageBar() );
wdgtConfigEditor->showUtilitiesButton( false );
wdgtConfigEditor->setRelayMessages( false );
wdgtConfigEditor->setShowUtilitiesButton( false );
setupUtilitiesMenu();
}

View File

@ -42,6 +42,11 @@ QColor QgsAuthGuiUtils::redColor()
return QColor( 200, 0, 0 );
}
QColor QgsAuthGuiUtils::yellowColor()
{
return QColor( 255, 255, 125 );
}
QString QgsAuthGuiUtils::greenTextStyleSheet( const QString &selector )
{
return QString( "%1{color: %2;}" ).arg( selector ).arg( QgsAuthGuiUtils::greenColor().name() );

View File

@ -39,6 +39,9 @@ class GUI_EXPORT QgsAuthGuiUtils
/** Red color representing invalid, untrusted, etc. certificate */
static QColor redColor();
/** Yellow color representing caution regarding action */
static QColor yellowColor();
/** Green text stylesheet representing valid, trusted, etc. certificate */
static QString greenTextStyleSheet( const QString& selector = "*" );

View File

@ -79,33 +79,15 @@
</widget>
</item>
<item>
<widget class="QLineEdit" name="leAuthCfg">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>110</width>
<height>16777215</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>Read-only ID</string>
<widget class="QLabel" name="label">
<property name="text">
<string>Id</string>
</property>
</widget>
</item>
<item>
<widget class="QgsAuthConfigIdEdit" name="authCfgEdit" native="true"/>
</item>
</layout>
</item>
<item row="2" column="0">
@ -131,6 +113,14 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsAuthConfigIdEdit</class>
<extends>QWidget</extends>
<header>qgsauthconfigidedit.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>leResource</tabstop>
<tabstop>cmbAuthMethods</tabstop>

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsAuthConfigIdEdit</class>
<widget class="QWidget" name="QgsAuthConfigIdEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>120</width>
<height>22</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="leAuthCfg">
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="placeholderText">
<string>Generated</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnLock">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Unlock to edit the ID&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;7-character alphanumeric only&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; color:#a80b0a;&quot;&gt;Editing may break things!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/locked.svg</normaloff>
<normalon>:/images/themes/default/unlocked.svg</normalon>:/images/themes/default/locked.svg</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../images/images.qrc"/>
</resources>
<connections/>
</ui>