2017-02-26 19:29:50 +01:00
# include " ConnectionListModel.h "
2017-08-23 13:27:23 +02:00
# include "ScopeGuard.h"
2017-01-14 20:07:12 +01:00
2017-02-26 19:29:50 +01:00
# include <botan/cryptobox.h>
2019-08-24 20:47:32 +02:00
# include <QDir>
2025-02-17 18:09:19 +01:00
# include <QException>
2019-09-16 19:24:39 +02:00
# include <QMimeData>
2019-08-24 20:47:32 +02:00
# include <QSettings>
2019-08-25 15:33:51 +02:00
# include <QSqlDatabase>
# include <QSqlError>
# include <QSqlQuery>
2025-02-17 18:09:19 +01:00
# include <QString>
# include <QStringBuilder>
2019-08-25 15:33:51 +02:00
# include <QStandardPaths>
2019-09-16 19:24:39 +02:00
# include <QStringBuilder>
2025-02-17 18:09:19 +01:00
# include <unordered_set>
2017-01-14 20:07:12 +01:00
2019-08-24 20:47:32 +02:00
namespace {
2019-08-25 15:33:51 +02:00
const char * const q_create_table_conngroup =
R " __(
CREATE TABLE IF NOT EXISTS conngroup (
conngroup_id INTEGER PRIMARY KEY ,
2019-08-27 20:12:00 +02:00
gname TEXT NOT NULL UNIQUE
2019-08-25 15:33:51 +02:00
) ; ) __ " ;
const char * const q_create_table_connection =
R " __(
CREATE TABLE IF NOT EXISTS connection (
uuid TEXT PRIMARY KEY ,
2019-09-16 19:24:39 +02:00
cname TEXT ,
2019-08-25 15:33:51 +02:00
conngroup_id INTEGER NOT NULL ,
2019-09-16 19:24:39 +02:00
host TEXT ,
hostaddr TEXT ,
2019-08-25 15:33:51 +02:00
port INTEGER NOT NULL ,
2019-09-16 19:24:39 +02:00
user TEXT ,
dbname TEXT ,
2019-08-25 15:33:51 +02:00
sslmode INTEGER NOT NULL ,
2019-09-16 19:24:39 +02:00
sslcert TEXT ,
sslkey TEXT ,
sslrootcert TEXT ,
sslcrl TEXT ,
password TEXT
2019-08-25 15:33:51 +02:00
) ; ) __ " ;
2025-02-17 18:09:19 +01:00
const char * const q_create_table_migrations =
R " __(
CREATE TABLE IF NOT EXISTS _migration (
migration_id TEXT PRIMARY KEY
) ; ) __ " ;
const char * const q_load_migrations_present =
R " __(
SELECT migration_id
FROM _migration ; ) __ " ;
2019-08-27 20:12:00 +02:00
2025-02-17 18:09:19 +01:00
// Keeping migration function name and id DRY
# define APPLY_MIGRATION(id) ApplyMigration(#id, &MigrationDirector::id)
class MigrationDirector {
public :
2025-02-22 19:59:24 +01:00
explicit MigrationDirector ( SQLiteConnection & db )
2025-02-17 18:09:19 +01:00
: db ( db )
{
}
void Execute ( )
{
InitConnectionTables ( ) ;
present = LoadMigrations ( ) ;
APPLY_MIGRATION ( M20250215_0933_Parameters ) ;
}
private :
2025-02-22 19:59:24 +01:00
SQLiteConnection & db ;
2025-02-17 18:09:19 +01:00
std : : unordered_set < QString > present ;
void M20250215_0933_Parameters ( )
{
2025-02-22 19:59:24 +01:00
db . Exec ( R " __(
2025-02-17 18:09:19 +01:00
CREATE TABLE connection_parameter (
connection_uuid TEXT ,
pname TEXT ,
pvalue TEXT NOT NULL ,
PRIMARY KEY ( connection_uuid , pname )
) ; ) __ " );
2025-02-22 19:59:24 +01:00
db . Exec ( R " __(
INSERT INTO connection_parameter ( connection_uuid , pname , pvalue )
SELECT uuid , ' sslmode ' AS pname , CASE
WHEN sslmode = ' 0 ' THEN ' disable '
WHEN sslmode = ' 1 ' THEN ' allow '
WHEN sslmode = ' 2 ' THEN ' prefer '
WHEN sslmode = ' 3 ' THEN ' require '
WHEN sslmode = ' 4 ' THEN ' verify_ca '
WHEN sslmode = ' 5 ' THEN ' verify_full '
END AS pvalue
FROM connection
WHERE sslmode is not null and sslmode between 0 and 5 ) __ " );
for ( QString key : { " host " , " hostaddr " , " user " , " dbname " , " sslcert " , " sslkey " , " sslrootcert " , " sslcrl " } )
2025-02-17 18:09:19 +01:00
{
2025-02-22 19:59:24 +01:00
db . Exec (
2025-02-17 18:09:19 +01:00
" INSERT INTO connection_parameter (connection_uuid, pname, pvalue) "
" SELECT uuid, ' " % key % " ', " % key % " \n "
2025-02-22 19:59:24 +01:00
" FROM connection \n "
" WHERE " % key % " IS NOT NULL and " % key % " <> ''; " ) ;
2025-02-17 18:09:19 +01:00
}
2025-02-22 19:59:24 +01:00
db . Exec ( R " __(
2025-02-17 18:09:19 +01:00
INSERT INTO connection_parameter ( connection_uuid , pname , pvalue )
SELECT uuid , ' port ' , port
FROM connection
WHERE port IS NOT NULL ;
) __ " );
for ( QString column : { " host " , " hostaddr " , " user " , " dbname " , " sslmode " , " sslcert " , " sslkey " , " sslrootcert " , " sslcrl " , " port " } )
{
// sqlite does not seem to support dropping more then one column per alter table
2025-02-22 19:59:24 +01:00
db . Exec ( " ALTER TABLE connection DROP COLUMN " % column % " ; " ) ;
2025-02-17 18:09:19 +01:00
}
}
void ApplyMigration ( QString migration_id , void ( MigrationDirector : : * func ) ( ) )
{
if ( ! present . contains ( migration_id ) )
{
2025-02-22 19:59:24 +01:00
SQLiteTransaction tx ( db ) ;
2025-02-17 18:09:19 +01:00
( this - > * func ) ( ) ;
RegisterMigration ( migration_id ) ;
2025-02-22 19:59:24 +01:00
tx . Commit ( ) ;
2025-02-17 18:09:19 +01:00
}
}
std : : unordered_set < QString > LoadMigrations ( )
{
std : : unordered_set < QString > result ;
2025-02-22 19:59:24 +01:00
auto stmt = db . Prepare ( q_load_migrations_present ) ;
while ( stmt . Step ( ) )
2025-02-17 18:09:19 +01:00
{
2025-02-22 19:59:24 +01:00
result . insert ( stmt . ColumnText ( 0 ) ) ;
2025-02-17 18:09:19 +01:00
}
return result ;
}
void RegisterMigration ( QString migrationId )
{
2025-02-22 19:59:24 +01:00
auto stmt = db . Prepare ( " INSERT INTO _migration VALUES (?1); " ) ;
stmt . Bind ( 1 , migrationId ) ;
stmt . Step ( ) ;
2025-02-17 18:09:19 +01:00
}
void InitConnectionTables ( )
{
// Original schema
2025-02-22 19:59:24 +01:00
db . Exec ( q_create_table_conngroup ) ;
db . Exec ( q_create_table_connection ) ;
2025-02-17 18:09:19 +01:00
// Start using migrations
2025-02-22 19:59:24 +01:00
db . Exec ( q_create_table_migrations ) ;
2025-02-17 18:09:19 +01:00
}
} ;
2019-08-25 15:33:51 +02:00
2025-02-22 19:59:24 +01:00
void SaveConnectionConfig ( SQLiteConnection & db , const ConnectionConfig & cc , int conngroup_id )
2019-08-25 15:33:51 +02:00
{
2025-02-22 19:59:24 +01:00
const char * const q_insert_or_replace_into_connection =
R " __(INSERT OR REPLACE INTO connection
VALUES ( ? 1 , ? 2 , ? 3 , ? 4 ) ;
) __ " ;
QByteArray b64 ; // needs to stay in scope until query is executed
SQLiteTransaction tx ( db ) ;
SQLitePreparedStatement stmt = db . Prepare ( q_insert_or_replace_into_connection ) ;
stmt . Bind ( 1 , cc . uuid ( ) . toString ( ) ) ;
stmt . Bind ( 2 , cc . name ( ) ) ;
stmt . Bind ( 3 , conngroup_id ) ;
2022-09-05 14:33:51 +02:00
auto & encodedPassword = cc . encodedPassword ( ) ;
2025-02-22 19:59:24 +01:00
if ( ! encodedPassword . isEmpty ( ) )
{
b64 = encodedPassword . toBase64 ( QByteArray : : Base64Encoding ) ;
stmt . Bind ( 4 , b64 . data ( ) , b64 . length ( ) ) ;
}
stmt . Step ( ) ;
2019-08-25 15:33:51 +02:00
}
2019-08-24 20:47:32 +02:00
} // end of unnamed namespace
2025-02-22 19:59:24 +01:00
ConnectionTreeModel : : ConnectionTreeModel ( QObject * parent , SQLiteConnection & db )
2019-08-25 15:33:51 +02:00
: QAbstractItemModel ( parent )
, m_db ( db )
{
}
void ConnectionTreeModel : : load ( )
{
2025-02-17 18:09:19 +01:00
//InitConnectionTables(m_db);
MigrationDirector md ( m_db ) ;
md . Execute ( ) ;
2019-08-25 15:33:51 +02:00
2025-02-22 19:59:24 +01:00
loadGroups ( ) ;
loadConnections ( ) ;
}
2019-08-25 15:33:51 +02:00
2025-02-22 19:59:24 +01:00
void ConnectionTreeModel : : loadGroups ( )
{
auto stmt = m_db . Prepare ( " SELECT conngroup_id, gname FROM conngroup; " ) ;
while ( stmt . Step ( ) )
{
auto g = std : : make_shared < ConnectionGroup > ( ) ;
g - > conngroup_id = stmt . ColumnInteger ( 0 ) ;
g - > name = stmt . ColumnText ( 1 ) ;
m_groups . push_back ( g ) ;
}
}
void ConnectionTreeModel : : loadConnections ( )
{
auto stmt = m_db . Prepare (
" SELECT uuid, cname, conngroup_id, password "
" FROM connection ORDER BY conngroup_id, cname; " ) ;
while ( stmt . Step ( ) ) {
auto cc = std : : make_shared < ConnectionConfig > ( ) ;
cc - > setUuid ( QUuid : : fromString ( stmt . ColumnText ( 0 ) ) ) ;
cc - > setName ( stmt . ColumnText ( 1 ) ) ;
2025-02-23 16:53:15 +01:00
cc - > setEncodedPassword ( stmt . ColumnCharPtr ( 3 ) ) ;
2025-02-22 19:59:24 +01:00
loadConnectionParameters ( * cc ) ;
int group_id = stmt . ColumnInteger ( 2 ) ;
auto find_res = std : : find_if ( m_groups . begin ( ) , m_groups . end ( ) ,
[ group_id ] ( auto item ) { return item - > conngroup_id = = group_id ; } ) ;
if ( find_res ! = m_groups . end ( ) ) {
( * find_res ) - > add ( cc ) ;
}
else {
throw std : : runtime_error ( " conngroup missing " ) ;
}
}
}
void ConnectionTreeModel : : loadConnectionParameters ( ConnectionConfig & cc )
{
auto stmt = m_db . Prepare (
" SELECT pname, pvalue \n "
" FROM connection_parameter \n "
" WHERE connection_uuid=?1 " ) ;
stmt . Bind ( 1 , cc . uuid ( ) . toString ( ) ) ;
while ( stmt . Step ( ) )
{
cc . setParameter (
stmt . ColumnText ( 0 ) ,
stmt . ColumnText ( 1 )
) ;
}
2019-08-25 15:33:51 +02:00
}
QVariant ConnectionTreeModel : : data ( const QModelIndex & index , int role ) const
{
// Code below assumes two level tree groups/connections
// it will fail for nested groups
QVariant v ;
auto privdata = static_cast < ConnectionNode * > ( index . internalPointer ( ) ) ;
if ( auto group = dynamic_cast < ConnectionGroup * > ( privdata ) ; group ! = nullptr ) {
// This is a group
if ( role = = Qt : : DisplayRole ) {
if ( index . column ( ) = = Name ) {
v = group - > name ;
}
}
}
else if ( auto conn = dynamic_cast < ConnectionConfig * > ( privdata ) ; conn ! = nullptr ) {
// This is a connection
if ( role = = Qt : : DisplayRole ) {
switch ( index . column ( ) ) {
2019-09-16 19:24:39 +02:00
case Name : v = conn - > name ( ) ; break ;
case Host : v = conn - > host ( ) ; break ;
2019-08-25 15:33:51 +02:00
case Port : v = conn - > port ( ) ; break ;
2019-09-16 19:24:39 +02:00
case User : v = conn - > user ( ) ; break ;
case DbName : v = conn - > dbname ( ) ; break ;
2019-08-25 15:33:51 +02:00
}
}
}
return v ;
}
QVariant ConnectionTreeModel : : headerData ( int section , Qt : : Orientation orientation , int role ) const
{
QVariant v ;
if ( orientation = = Qt : : Horizontal ) {
if ( role = = Qt : : DisplayRole ) {
switch ( section ) {
case Name : v = tr ( " Name " ) ; break ;
case Host : v = tr ( " Host " ) ; break ;
case Port : v = tr ( " Port " ) ; break ;
case User : v = tr ( " User " ) ; break ;
case DbName : v = tr ( " Database " ) ; break ;
}
}
}
return v ;
}
QModelIndex ConnectionTreeModel : : index ( int row , int column , const QModelIndex & parent ) const
{
if ( ! hasIndex ( row , column , parent ) )
return { } ;
const ConnectionNode * node = nullptr ;
if ( parent . isValid ( ) ) {
auto privdata = static_cast < ConnectionNode * > ( parent . internalPointer ( ) ) ;
if ( auto group = dynamic_cast < ConnectionGroup * > ( privdata ) ; group ! = nullptr ) {
node = group - > connections ( ) . at ( row ) . get ( ) ;
}
else {
throw std : : logic_error ( " Should never ask for a child index of a connectionconfig " ) ;
}
}
else {
node = m_groups [ row ] . get ( ) ;
}
return createIndex ( row , column , const_cast < ConnectionNode * > ( node ) ) ;
}
QModelIndex ConnectionTreeModel : : parent ( const QModelIndex & index ) const
{
if ( ! index . isValid ( ) )
return { } ;
auto privdata = static_cast < ConnectionNode * > ( index . internalPointer ( ) ) ;
if ( auto group = dynamic_cast < ConnectionGroup * > ( privdata ) ; group ! = nullptr ) {
return { } ;
}
else if ( auto config = dynamic_cast < ConnectionConfig * > ( privdata ) ; config ! = nullptr ) {
auto p = config - > parent ( ) ;
auto find_res = std : : find_if ( m_groups . begin ( ) , m_groups . end ( ) , [ p ] ( auto item ) - > bool { return * p = = * item ; } ) ;
if ( find_res ! = m_groups . end ( ) ) {
2019-08-27 20:12:00 +02:00
return createIndex ( find_res - m_groups . begin ( ) , 0 ,
const_cast < ConnectionGroup * > ( config - > parent ( ) ) ) ;
2019-08-25 15:33:51 +02:00
}
}
throw std : : logic_error ( " Should never get here " ) ;
}
int ConnectionTreeModel : : rowCount ( const QModelIndex & parent ) const
{
int result = 0 ;
if ( parent . isValid ( ) ) {
auto privdata = static_cast < ConnectionNode * > ( parent . internalPointer ( ) ) ;
if ( auto group = dynamic_cast < ConnectionGroup * > ( privdata ) ; group ! = nullptr ) {
result = group - > connections ( ) . size ( ) ;
}
else if ( auto config = dynamic_cast < ConnectionConfig * > ( privdata ) ; config ! = nullptr ) {
result = 0 ;
}
}
else {
result = m_groups . size ( ) ;
}
return result ;
}
2019-08-27 20:12:00 +02:00
int ConnectionTreeModel : : columnCount ( const QModelIndex & ) const
2019-08-25 15:33:51 +02:00
{
return ColCount ;
}
2019-08-27 20:12:00 +02:00
bool ConnectionTreeModel : : removeRows ( int row , int count , const QModelIndex & parent )
{
if ( parent . isValid ( ) & & count = = 1 ) {
// should be a group
auto grp = m_groups [ parent . row ( ) ] ;
for ( int i = 0 ; i < count ; + + i ) {
QUuid uuid = grp - > connections ( ) . at ( row + i ) - > uuid ( ) ;
2025-02-22 19:59:24 +01:00
auto stmt = m_db . Prepare (
2019-08-27 20:12:00 +02:00
" DELETE FROM connection "
2025-02-22 19:59:24 +01:00
" WHERE uuid=?0 " ) ;
stmt . Bind ( 0 , uuid . toString ( ) ) ;
stmt . Step ( ) ;
2019-08-27 20:12:00 +02:00
}
beginRemoveRows ( parent , row , row + count - 1 ) ;
SCOPE_EXIT { endRemoveRows ( ) ; } ;
grp - > erase ( row , count ) ;
2019-09-01 10:26:42 +02:00
return true ;
2019-08-27 20:12:00 +02:00
}
2019-09-01 10:26:42 +02:00
return false ;
2019-08-27 20:12:00 +02:00
}
void ConnectionTreeModel : : save ( const QString & group_name , const ConnectionConfig & cc )
{
auto [ grp_idx , conn_idx ] = findConfig ( cc . uuid ( ) ) ;
if ( grp_idx > = 0 ) {
auto grp = m_groups [ grp_idx ] ;
if ( grp - > name = = group_name ) {
// update config
grp - > update ( conn_idx , cc ) ;
// send change event
auto node = grp - > connections ( ) . at ( conn_idx ) ;
dataChanged (
createIndex ( conn_idx , 0 , node . get ( ) ) ,
createIndex ( conn_idx , ColCount - 1 , node . get ( ) ) ) ;
saveToDb ( * node ) ;
return ;
}
else {
auto parent = createIndex ( grp_idx , 0 , grp . get ( ) ) ;
beginRemoveRows ( parent , conn_idx , conn_idx ) ;
SCOPE_EXIT { endRemoveRows ( ) ; } ;
grp - > erase ( conn_idx ) ;
}
}
// Here we can assume we have to find the new group or create a new group
// because if the connection was in the right group the function has already returned.
// We assume the model is in sync with the DB as the DB should not be shared!
int new_grp_idx = findGroup ( group_name ) ;
if ( new_grp_idx < 0 ) {
2025-02-22 19:59:24 +01:00
// Group not found we are g
new_grp_idx = addGroup ( group_name ) ;
2019-08-27 20:12:00 +02:00
}
auto new_grp = m_groups [ new_grp_idx ] ;
auto parent = createIndex ( new_grp_idx , 0 , new_grp . get ( ) ) ;
auto idx = new_grp - > connections ( ) . size ( ) ;
beginInsertRows ( parent , idx , idx ) ;
SCOPE_EXIT { endInsertRows ( ) ; } ;
auto node = std : : make_shared < ConnectionConfig > ( cc ) ;
2025-02-22 19:59:24 +01:00
new_grp - > add ( node ) ;
saveToDb ( * node ) ;
2019-08-27 20:12:00 +02:00
}
void ConnectionTreeModel : : save ( const ConnectionConfig & cc )
{
2022-09-05 07:33:08 +02:00
saveToDb ( cc ) ;
}
void ConnectionTreeModel : : clearAllPasswords ( )
{
for ( auto group : m_groups )
for ( auto cc : group - > connections ( ) )
{
cc - > setEncodedPassword ( { } ) ;
saveToDb ( * cc ) ;
}
2019-08-27 20:12:00 +02:00
}
std : : tuple < int , int > ConnectionTreeModel : : findConfig ( const QUuid uuid ) const
{
int group_idx = - 1 , connection_idx = - 1 ;
for ( int grp_idx = 0 ; grp_idx < m_groups . size ( ) ; + + grp_idx ) {
auto & & grp = m_groups [ grp_idx ] ;
auto & & conns = grp - > connections ( ) ;
auto find_res = std : : find_if ( conns . begin ( ) , conns . end ( ) ,
[ & uuid ] ( auto item ) - > bool { return item - > uuid ( ) = = uuid ; } ) ;
if ( find_res ! = conns . end ( ) ) {
group_idx = grp_idx ;
connection_idx = find_res - conns . begin ( ) ;
break ;
}
}
return { group_idx , connection_idx } ;
}
2019-09-02 16:33:13 +02:00
int ConnectionTreeModel : : findGroup ( const QString & name ) const
2019-08-27 20:12:00 +02:00
{
for ( int idx = 0 ; idx < m_groups . size ( ) ; + + idx ) {
if ( m_groups [ idx ] - > name = = name ) return idx ;
}
return - 1 ;
}
2025-02-22 19:59:24 +01:00
int ConnectionTreeModel : : addGroup ( const QString & group_name )
2019-08-27 20:12:00 +02:00
{
2025-02-22 19:59:24 +01:00
auto stmt = m_db . Prepare ( " INSERT INTO conngroup (gname) VALUES (?1) " ) ;
stmt . Bind ( 1 , group_name ) ;
auto cg = std : : make_shared < ConnectionGroup > ( ) ;
cg - > conngroup_id = m_db . LastInsertRowId ( ) ;
2019-09-01 06:48:11 +02:00
cg - > name = group_name ;
int row = m_groups . size ( ) ;
beginInsertRows ( { } , row , row ) ;
SCOPE_EXIT { endInsertRows ( ) ; } ;
m_groups . push_back ( cg ) ;
return row ;
}
2025-02-22 19:59:24 +01:00
void ConnectionTreeModel : : removeGroup ( int row )
2019-09-01 06:42:21 +02:00
{
beginRemoveRows ( { } , row , row ) ;
SCOPE_EXIT { endRemoveRows ( ) ; } ;
auto id = m_groups [ row ] - > conngroup_id ;
2025-02-22 19:59:24 +01:00
auto stmt = m_db . Prepare ( " DELETE FROM connection WHERE conngroup_id=?1 " ) ;
stmt . Bind ( 1 , id ) ;
stmt . Step ( ) ;
stmt = m_db . Prepare ( " DELETE FROM conngroup WHERE conngroup_id=?1 " ) ;
stmt . Bind ( 1 , id ) ;
stmt . Step ( ) ;
2019-09-01 06:42:21 +02:00
m_groups . remove ( row ) ;
}
2019-09-01 06:44:48 +02:00
int ConnectionTreeModel : : findGroup ( int conngroup_id ) const
{
auto find_res = std : : find_if ( m_groups . begin ( ) , m_groups . end ( ) ,
[ conngroup_id ] ( auto item ) { return item - > conngroup_id = = conngroup_id ; } ) ;
if ( find_res = = m_groups . end ( ) )
return - 1 ;
return find_res - m_groups . begin ( ) ;
2019-08-27 20:12:00 +02:00
}
2019-09-01 14:07:58 +02:00
ConnectionConfig * ConnectionTreeModel : : getConfigFromModelIndex ( QModelIndex index )
{
if ( ! index . isValid ( ) )
return nullptr ;
auto node = static_cast < ConnectionNode * > ( index . internalPointer ( ) ) ;
return dynamic_cast < ConnectionConfig * > ( node ) ;
}
ConnectionGroup * ConnectionTreeModel : : getGroupFromModelIndex ( QModelIndex index )
{
if ( ! index . isValid ( ) )
return nullptr ;
auto node = static_cast < ConnectionNode * > ( index . internalPointer ( ) ) ;
return dynamic_cast < ConnectionGroup * > ( node ) ;
}
2025-02-22 19:59:24 +01:00
void ConnectionTreeModel : : saveToDb ( const ConnectionConfig & cc )
2019-08-27 20:12:00 +02:00
{
2025-02-22 19:59:24 +01:00
SaveConnectionConfig ( m_db , cc , cc . parent ( ) - > conngroup_id ) ;
2019-08-27 20:12:00 +02:00
}
2019-09-16 19:24:39 +02:00
Qt : : DropActions ConnectionTreeModel : : supportedDropActions ( ) const
{
return Qt : : MoveAction ;
}
Qt : : DropActions ConnectionTreeModel : : supportedDragActions ( ) const
{
return Qt : : MoveAction ;
}
Qt : : ItemFlags ConnectionTreeModel : : flags ( const QModelIndex & index ) const
{
Qt : : ItemFlags defaultFlags = QAbstractItemModel : : flags ( index ) ;
ConnectionConfig * cfg = getConfigFromModelIndex ( index ) ;
if ( cfg )
return Qt : : ItemIsDragEnabled | defaultFlags ;
else
return Qt : : ItemIsDropEnabled | defaultFlags ;
}
//bool ConnectionTreeModel::insertRows(int row, int count, const QModelIndex &parent)
//{
// return false;
//}
//bool ConnectionTreeModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
//{
// return false;
//}
namespace {
const auto mimeType = " application/vnd.pgLab.connection " ;
}
QStringList ConnectionTreeModel : : mimeTypes ( ) const
{
return { mimeType } ;
}
QMimeData * ConnectionTreeModel : : mimeData ( const QModelIndexList & indexes ) const
{
QMimeData * mimeData = new QMimeData ;
QByteArray encodedData ;
QDataStream stream ( & encodedData , QIODevice : : WriteOnly ) ;
for ( const QModelIndex & index : indexes ) {
if ( index . isValid ( ) ) {
QString text = data ( index , Qt : : DisplayRole ) . toString ( ) ;
stream < < text ;
}
}
mimeData - > setData ( mimeType , encodedData ) ;
return mimeData ;
}
bool ConnectionTreeModel : : dropMimeData ( const QMimeData * data , Qt : : DropAction action , int row , int column , const QModelIndex & parent )
{
return false ;
}