1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-19 15:29:42 +01:00

Begin work on overhauled account UI, again

This commit is contained in:
Leo Franchi 2012-01-29 19:33:12 -05:00
parent f2b1cc6ae3
commit 8eba4c171f
8 changed files with 483 additions and 191 deletions

View File

@ -42,7 +42,7 @@ public:
virtual QRect checkRectForIndex( const QStyleOptionViewItem &option, const QModelIndex &idx, int role ) const;
virtual QRect configRectForIndex( const QStyleOptionViewItem& option, const QModelIndex& idx ) const;
virtual QList<int> extraCheckRoles() const { return QList<int>() << (int)AccountModel::AccountTypeRole; }
// virtual QList<int> extraCheckRoles() const { return QList<int>() << (int)AccountModel::AccountTypeRole; }
private slots:
void askedForEdit( const QModelIndex& idx );

View File

@ -75,7 +75,7 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
settingsdialog.cpp
diagnosticsdialog.cpp
configdelegatebase.cpp
AccountDelegate.cpp
# AccountDelegate.cpp
settingslistdelegate.cpp
tomahawkwindow.cpp
LoadXSPFDialog.cpp
@ -125,7 +125,7 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui}
settingsdialog.h
diagnosticsdialog.h
configdelegatebase.h
AccountDelegate.h
# AccountDelegate.h
settingslistdelegate.h
delegateconfigwrapper.h
tomahawkwindow.h

View File

@ -21,17 +21,6 @@
#include <QDesktopServices>
#include "settingsdialog.h"
// #include <QDir>
//
// #include "sip/SipHandler.h"
// #include "playlistinterface.h"
//
// #include "utils/logger.h"
// #include "utils/tomahawkutils.h"
//
// #include "database/databasecommand_updatesearchindex.h"
// #include "database/database.h"
using namespace Tomahawk;
TomahawkSettingsGui*

View File

@ -1,156 +1,288 @@
/*
Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
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 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AccountModel.h"
#include "tomahawksettings.h"
#include "accounts/AccountManager.h"
#include "accounts/Account.h"
#include "Account.h"
#include "AccountModelNode.h"
#include "AccountManager.h"
#include "AtticaManager.h"
#include "ResolverAccount.h"
#include "utils/logger.h"
#include <attica/content.h>
using namespace Tomahawk;
using namespace Accounts;
AccountModel::AccountModel( QObject* parent )
: QAbstractListModel( parent )
: QAbstractItemModel( parent )
, m_rootItem( 0 )
{
connect( AccountManager::instance(), SIGNAL( added( Tomahawk::Accounts::Account* ) ), this, SLOT( accountAdded( Tomahawk::Accounts::Account* ) ) );
connect( AccountManager::instance(), SIGNAL( removed( Tomahawk::Accounts::Account* ) ), this, SLOT( accountRemoved( Tomahawk::Accounts::Account* ) ) );
loadData();
}
AccountModel::~AccountModel()
void
AccountModel::loadData()
{
beginResetModel();
delete m_rootItem;
m_rootItem = new AccountModelNode();
// Add all factories
QList< AccountFactory* > factories = AccountManager::instance()->factories();
QList< Account* > allAccounts = AccountManager::instance()->accounts();
foreach ( AccountFactory* fac, factories )
{
if ( !fac->allowUserCreation() )
continue;
qDebug() << "Creating factory node:" << fac->prettyName();
new AccountModelNode( m_rootItem, fac );
}
// add all attica resolvers (installed or uninstalled)
Attica::Content::List fromAttica = AtticaManager::instance()->resolvers();
foreach ( const Attica::Content& content, fromAttica )
new AccountModelNode( m_rootItem, content );
// Add all non-attica manually installed resolvers
foreach ( Account* acct, allAccounts )
{
if ( qobject_cast< ResolverAccount* >( acct ) && !qobject_cast< AtticaResolverAccount* >( acct ) )
{
new AccountModelNode( m_rootItem, qobject_cast< ResolverAccount* >( acct ) );
}
}
}
QVariant
AccountModel::data( const QModelIndex& index, int role ) const
{
if( !index.isValid() )
if ( !index.isValid() )
return QVariant();
QList< Account* > accounts = AccountManager::instance()->accounts();
Q_ASSERT( index.row() <= accounts.size() );
Account* account = accounts[ index.row() ];
switch( role )
{
case Qt::DisplayRole:
case AccountModel::AccountName:
return account->accountFriendlyName();
case AccountModel::ConnectionStateRole:
return account->connectionState();
case AccountModel::HasConfig:
return ( account->configurationWidget() != 0 );
case AccountModel::AccountTypeRole:
return (int)account->types();
case Qt::DecorationRole:
return account->icon();
case AccountModel::AccountData:
return QVariant::fromValue< QObject* >( account );
case Qt::CheckStateRole:
return account->enabled() ? Qt::Checked : Qt::Unchecked;
default:
if ( !hasIndex( index.row(), index.column(), index.parent() ) )
return QVariant();
AccountModelNode* node = nodeFromIndex( index );
if ( node->parent == m_rootItem ) {
// This is a top-level item. 3 cases
Q_ASSERT( node->type != AccountModelNode::AccountType ); // must not be of this type, these should be children (other branch of if)
switch ( node->type )
{
case AccountModelNode::FactoryType:
{
AccountFactory* fac = node->factory;
Q_ASSERT( fac );
switch ( role )
{
case Qt::DisplayRole:
return fac->prettyName();
case Qt::DecorationRole:
return fac->icon();
case StateRole:
return ShippedWithTomahawk;
case AccountDescription:
return fac->description();
case AuthorRole:
return "Tomahawk Team";
case RowType:
return TopLevelFactory;
default:
return QVariant();
}
}
case AccountModelNode::AtticaType:
{
Attica::Content c = node->atticaContent;
Q_ASSERT( !c.id().isNull() );
switch( role )
{
case Qt::DisplayRole:
return c.name();
case Qt::DecorationRole:
return QVariant::fromValue< QPixmap >( AtticaManager::instance()->iconForResolver( c ) );
case StateRole:
return (int)AtticaManager::instance()->resolverState( c );
case AccountDescription:
return c.description();
case AuthorRole:
return c.author();
case RatingRole:
return c.rating() / 20; // rating is out of 100
case DownloadCounterRole:
return c.downloads();
case VersionRole:
return c.version();
case UserHasRatedRole:
return AtticaManager::instance()->userHasRated( c );
default:
;
}
AtticaResolverAccount* atticaAcct = node->atticaAccount;
if ( atticaAcct )
{
// If the resolver is installed or on disk, we expose some additional data
switch ( role )
{
case HasConfig:
return atticaAcct->configurationWidget() != 0;
case Qt::CheckStateRole:
return atticaAcct->enabled() ? Qt::Checked : Qt::Unchecked;
case AccountData:
return QVariant::fromValue< QObject* >( atticaAcct );
case RowType:
return TopLevelAccount;
case ConnectionStateRole:
return atticaAcct->connectionState();
default:
;
}
}
return QVariant();
}
case AccountModelNode::ManualResolverType:
{
ResolverAccount* resolver = node->resolverAccount;
Q_ASSERT( resolver );
switch ( role )
{
case Qt::DisplayRole:
return resolver->accountFriendlyName();
case Qt::DecorationRole:
return resolver->icon();
case AccountDescription:
return QString();
case Qt::CheckStateRole:
return resolver->enabled() ? Qt::Checked : Qt::Unchecked;
case AccountData:
return QVariant::fromValue< QObject* >( resolver );
case RowType:
return TopLevelAccount;
case ConnectionStateRole:
return resolver->connectionState();
default:
return QVariant();
}
}
}
}
else
{
// This is a child account* of an accountfactory*
Q_ASSERT( node->type == AccountModelNode::AccountType );
Q_ASSERT( node->children.isEmpty() );
Q_ASSERT( node->account );
Account* acc = node->account;
switch ( role )
{
case RowType:
return ChildAccount;
case Qt::DisplayRole:
return acc->accountFriendlyName();
case ConnectionStateRole:
return acc->connectionState();
case HasConfig:
return ( acc->configurationWidget() != 0 );
case ErrorString:
return acc->errorMessage();
case Qt::CheckStateRole:
return acc->enabled() ? Qt::Checked : Qt::Unchecked;
case AccountData:
return QVariant::fromValue< QObject* >( acc );
default:
return QVariant();
}
}
return QVariant();
}
bool
AccountModel::setData( const QModelIndex& index, const QVariant& value, int role )
int
AccountModel::columnCount( const QModelIndex& parent ) const
{
Q_ASSERT( index.isValid() && index.row() <= AccountManager::instance()->accounts().count() );
if ( role == Qt::CheckStateRole ) {
Qt::CheckState state = static_cast< Qt::CheckState >( value.toInt() );
QList< Account* > accounts = AccountManager::instance()->accounts();
Account* account = accounts[ index.row() ];
if( state == Qt::Checked && !account->enabled() ) {
AccountManager::instance()->enableAccount( account );
} else if( state == Qt::Unchecked ) {
AccountManager::instance()->disableAccount( account );
}
account->sync();
dataChanged( index, index );
return true;
}
else if ( role == AccountTypeRole )
{
// TODO
}
return false;
return 1;
}
int
AccountModel::rowCount( const QModelIndex& ) const
AccountModel::rowCount( const QModelIndex& parent ) const
{
return AccountManager::instance()->accounts().size();
}
Qt::ItemFlags
AccountModel::flags( const QModelIndex& index ) const
{
return QAbstractListModel::flags( index ) | Qt::ItemIsUserCheckable;
}
void
AccountModel::accountAdded( Account* account )
{
// TODO HACK we assume account plugins are added at the end of the list.
Q_ASSERT( AccountManager::instance()->accounts().last() == account );
if ( account->types() & SipType )
connect( account, SIGNAL( connectionStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ), this, SLOT( accountStateChanged( Tomahawk::Accounts::Account::ConnectionState ) ) );
int size = AccountManager::instance()->accounts().count() - 1;
beginInsertRows( QModelIndex(), size, size );
endInsertRows();
}
void
AccountModel::accountRemoved( Account* account )
{
int idx = AccountManager::instance()->accounts().indexOf( account );
beginRemoveRows( QModelIndex(), idx, idx );
endRemoveRows();
}
void
AccountModel::accountStateChanged( Tomahawk::Accounts::Account::ConnectionState )
{
Account* account = qobject_cast< Account* >( sender() );
Q_ASSERT( account );
for ( int i = 0; i < AccountManager::instance()->accounts().size(); i++ )
if ( !parent.isValid() )
{
if ( AccountManager::instance()->accounts()[i] == account )
{
QModelIndex idx = index( i, 0, QModelIndex() );
emit dataChanged( idx, idx );
return;
}
return m_rootItem->children.count();
}
// If it's a top-level item, return child count. Only factories will have any.
return nodeFromIndex( parent )->children.count();
}
QModelIndex
AccountModel::parent( const QModelIndex& child ) const
{
if ( !child.isValid() )
{
return QModelIndex();
}
AccountModelNode* node = nodeFromIndex( child );
AccountModelNode* parent = node->parent;
// top level, none
if( parent == m_rootItem )
return QModelIndex();
// child Account* of an AccountFactory*
Q_ASSERT( m_rootItem->children.contains( parent ) );
return createIndex( m_rootItem->children.indexOf( parent ), 0, parent );
}
QModelIndex
AccountModel::index( int row, int column, const QModelIndex& parent ) const
{
if( row < 0 || column < 0 )
return QModelIndex();
if( hasIndex( row, column, parent ) )
{
AccountModelNode *parentNode = nodeFromIndex( parent );
AccountModelNode *childNode = parentNode->children.at( row );
return createIndex( row, column, childNode );
}
return QModelIndex();
}
AccountModelNode*
AccountModel::nodeFromIndex( const QModelIndex& idx ) const
{
if( !idx.isValid() )
return m_rootItem;
Q_ASSERT( idx.internalPointer() );
return reinterpret_cast< AccountModelNode* >( idx.internalPointer() );
}

View File

@ -1,68 +1,94 @@
/*
Copyright (C) 2011 Leo Franchi <lfranchi@kde.org>
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
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 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SIPMODEL_H
#define SIPMODEL_H
#ifndef TOMAHAWK_ACCOUNTS_ACCOUNTMODEL_H
#define TOMAHAWK_ACCOUNTS_ACCOUNTMODEL_H
#include "dllmacro.h"
#include "sip/SipPlugin.h"
#include <QAbstractListModel>
#include <QModelIndex>
#include <QStringList>
#include <QAbstractItemModel>
namespace Tomahawk
{
namespace Accounts
{
class Account;
namespace Tomahawk {
class DLLEXPORT AccountModel : public QAbstractListModel
namespace Accounts {
class AccountModelNode;
class DLLEXPORT AccountModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum Roles {
AccountName = Qt::UserRole + 15,
AccountIcon = Qt::UserRole + 16,
AccountTypeRole = Qt::UserRole + 19,
ConnectionStateRole = Qt::UserRole + 20,
HasConfig = Qt::UserRole + 21,
ErrorString = Qt::UserRole + 22,
AccountData = Qt::UserRole + 23 // raw plugin
RowType = Qt::UserRole + 1, // RowType enum
// Used by top-level accounts
AccountDescription = Qt::UserRole + 17,
StateRole = Qt::UserRole + 18, // ItemState,
RatingRole = Qt::UserRole + 19,
DownloadCounterRole = Qt::UserRole + 20,
VersionRole = Qt::UserRole + 21,
AuthorRole = Qt::UserRole + 22,
UserHasRatedRole = Qt::UserRole + 24,
// used by both
ConnectionStateRole = Qt::UserRole + 25,
HasConfig = Qt::UserRole + 26,
ErrorString = Qt::UserRole + 27,
// used by individual accounts, needed still?
AccountData = Qt::UserRole + 28 // raw plugin
};
enum RowType {
TopLevelFactory,
TopLevelAccount,
ChildAccount
};
enum ItemState {
Uninstalled = 0,
Installing,
Installed,
NeedsUpgrade,
Upgrading,
Failed,
ShippedWithTomahawk // Can't uninstall or uninstall, just enable/disable
};
explicit AccountModel( QObject* parent = 0 );
virtual ~AccountModel();
virtual QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const;
virtual int columnCount( const QModelIndex& parent = QModelIndex() ) const;
virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const;
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
virtual QModelIndex parent( const QModelIndex& child ) const;
virtual QModelIndex index( int row, int column, const QModelIndex& parent = QModelIndex() ) const;
private slots:
void accountAdded( Tomahawk::Accounts::Account* p );
void accountRemoved( Tomahawk::Accounts::Account* p );
void accountStateChanged( Tomahawk::Accounts::Account::ConnectionState );
private:
AccountModelNode* nodeFromIndex( const QModelIndex& index ) const;
void loadData();
AccountModelNode* m_rootItem;
};
}
}
#endif // SIPMODEL_H
#endif // TOMAHAWK_ACCOUNTS_ACCOUNTMODEL_H

View File

@ -0,0 +1,145 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TOMAHAWK_ACCOUNTS_ACCOUNTMODELNODE_H
#define TOMAHAWK_ACCOUNTS_ACCOUNTMODELNODE_H
#include "Account.h"
#include "AccountManager.h"
#include "ResolverAccount.h"
#include <attica/content.h>
namespace Tomahawk {
namespace Accounts {
/**
* Node for account tree.
*
* Basically a union with possible types:
* 1) AccountFactory* for accounts that are not unique (jabber, google, twitter)
* 2) Account* for accounts that are associated with an AccountFactory (children of AccountFactory)
* 3) Attica::Content for AtticaResolverAccounts (with associated AtticaResolverAccount*) (all synchroton resolvers)
* 4) ResolverAccount* for manually added resolvers (from file).
*
* These are the top-level items in tree.
*
* Top level nodes all look the same to the user. The only difference is that services that have login (and thus
* can have multiple logins at once) allow a user to create multiple children with specific login information.
* All other top level accounts (Account*, Attica::Content, ResolverAccount*) behave the same to the user, they can
* simply click "Install" or toggle on/off.
*
*/
struct AccountModelNode {
enum NodeType {
FactoryType,
AccountType,
AtticaType,
ManualResolverType
};
AccountModelNode* parent;
NodeType type;
QList< AccountModelNode* > children; // list of children accounts (actually existing and configured accounts)
/// 1.
AccountFactory* factory;
/// 2.
Account* account;
/// 3.
Attica::Content atticaContent;
AtticaResolverAccount* atticaAccount;
/// 4.
ResolverAccount* resolverAccount;
// Construct in one of four ways. Then access the corresponding members
explicit AccountModelNode( AccountModelNode* p, AccountFactory* fac ) : parent( p ), type( FactoryType )
{
init();
factory = fac;
// Initialize factory nodes with their children
foreach ( Account* acct, AccountManager::instance()->accounts() )
{
if ( AccountManager::instance()->factoryForAccount( acct ) == fac )
{
qDebug() << "Found account for factory:" << acct->accountFriendlyName();
new AccountModelNode( this, acct );
}
}
}
AccountModelNode( AccountModelNode* p, Account* acct ) : parent( p ), type( AccountType )
{
init();
account = acct;
}
explicit AccountModelNode( AccountModelNode* p, Attica::Content cnt ) : parent( p ), type( AtticaType )
{
init();
atticaContent = cnt;
qDebug() << "Creating attica model node for resolver:" << cnt.id();
foreach ( Account* acct, AccountManager::instance()->accounts( Accounts::ResolverType ) )
{
if ( AtticaResolverAccount* resolver = qobject_cast< AtticaResolverAccount* >( acct ) )
{
if ( resolver->atticaId() == atticaContent.id() )
{
qDebug() << "found atticaaccount :" << resolver->accountFriendlyName();
atticaAccount = resolver;
break;
}
}
}
}
explicit AccountModelNode( AccountModelNode* p, ResolverAccount* ra ) : parent( p ), type( ManualResolverType )
{
init();
resolverAccount = ra;
}
AccountModelNode() : parent( 0 ) {}
~AccountModelNode()
{
qDeleteAll( children );
}
void init()
{
parent->children.append( this );
factory = 0;
account = 0;
atticaAccount = 0;
resolverAccount = 0;
}
};
}
}
#endif // TOMAHAWK_ACCOUNTS_ACCOUNTMODELNODE_H

View File

@ -102,12 +102,12 @@ SettingsDialog::SettingsDialog( QWidget *parent )
#endif
// SIP PLUGINS
AccountDelegate* sipdel = new AccountDelegate( this );
ui->accountsView->setItemDelegate( sipdel );
// AccountDelegate* sipdel = new AccountDelegate( this );
// ui->accountsView->setItemDelegate( sipdel );
ui->accountsView->setContextMenuPolicy( Qt::CustomContextMenu );
ui->accountsView->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
connect( sipdel, SIGNAL( openConfig( Tomahawk::Accounts::Account* ) ), this, SLOT( openAccountConfig( Tomahawk::Accounts::Account* ) ) );
// connect( sipdel, SIGNAL( openConfig( Tomahawk::Accounts::Account* ) ), this, SLOT( openAccountConfig( Tomahawk::Accounts::Account* ) ) );
connect( ui->accountsView, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( accountContextMenuRequest( QPoint ) ) );
m_accountModel = new AccountModel( this );
ui->accountsView->setModel( m_accountModel );
@ -598,11 +598,11 @@ SettingsDialog::accountContextMenuRequest( const QPoint& p )
// if it's an account, allow to delete
if( idx.isValid() )
{
QList< QAction* > acts;
acts << new QAction( tr( "Delete Service" ), this );
acts.first()->setProperty( "accountplugin", idx.data( AccountModel::AccountData ) );
connect( acts.first(), SIGNAL( triggered( bool ) ), this, SLOT( onAccountRowDeleted( bool ) ) );
QMenu::exec( acts, ui->accountsView->mapToGlobal( p ) );
// QList< QAction* > acts;
// acts << new QAction( tr( "Delete Service" ), this );
// acts.first()->setProperty( "accountplugin", idx.data( AccountModel::AccountData ) );
// connect( acts.first(), SIGNAL( triggered( bool ) ), this, SLOT( onAccountRowDeleted( bool ) ) );
// QMenu::exec( acts, ui->accountsView->mapToGlobal( p ) );
}
}
@ -625,8 +625,8 @@ SettingsDialog::accountDeleted( bool )
{
if( idx.isValid() )
{
Account* account = qobject_cast< Account* >( idx.data( AccountModel::AccountData ).value< QObject* >() );
AccountManager::instance()->removeAccount( account );
// Account* account = qobject_cast< Account* >( idx.data( AccountModel::AccountData ).value< QObject* >() );
// AccountManager::instance()->removeAccount( account );
}
}
}

View File

@ -129,10 +129,10 @@
<item>
<widget class="QTreeView" name="accountsView">
<property name="indentation">
<number>0</number>
<number>6</number>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="uniformRowHeights">
<bool>false</bool>