Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsbrowsermodel.cpp
3 : : ---------------------
4 : : begin : July 2011
5 : : copyright : (C) 2011 by Martin Dobias
6 : : email : wonder dot sk at gmail dot com
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : : #include <QDir>
16 : : #include <QApplication>
17 : : #include <QStyle>
18 : : #include <QtConcurrentMap>
19 : : #include <QUrl>
20 : :
21 : : #include "qgis.h"
22 : : #include "qgsapplication.h"
23 : : #include "qgsdataitemprovider.h"
24 : : #include "qgsdataitemproviderregistry.h"
25 : : #include "qgsdataprovider.h"
26 : : #include "qgsmimedatautils.h"
27 : : #include "qgslogger.h"
28 : : #include "qgsproviderregistry.h"
29 : : #include "qgsbrowsermodel.h"
30 : : #include "qgsproject.h"
31 : : #include "qgssettings.h"
32 : :
33 : : #define PROJECT_HOME_PREFIX "project:"
34 : : #define HOME_PREFIX "home:"
35 : :
36 : 0 : QgsBrowserWatcher::QgsBrowserWatcher( QgsDataItem *item )
37 : 0 : : QFutureWatcher( nullptr )
38 : 0 : , mItem( item )
39 : 0 : {
40 : 0 : }
41 : :
42 : : // sort function for QList<QgsDataItem*>, e.g. sorted/grouped provider listings
43 : 0 : static bool cmpByDataItemName_( QgsDataItem *a, QgsDataItem *b )
44 : : {
45 : 0 : return QString::localeAwareCompare( a->name(), b->name() ) < 0;
46 : 0 : }
47 : :
48 : 0 : QgsBrowserModel::QgsBrowserModel( QObject *parent )
49 : 0 : : QAbstractItemModel( parent )
50 : :
51 : 0 : {
52 : 0 : connect( QgsApplication::dataItemProviderRegistry(), &QgsDataItemProviderRegistry::providerAdded,
53 : : this, &QgsBrowserModel::dataItemProviderAdded );
54 : 0 : connect( QgsApplication::dataItemProviderRegistry(), &QgsDataItemProviderRegistry::providerWillBeRemoved,
55 : : this, &QgsBrowserModel::dataItemProviderWillBeRemoved );
56 : 0 : }
57 : :
58 : 0 : QgsBrowserModel::~QgsBrowserModel()
59 : 0 : {
60 : 0 : removeRootItems();
61 : 0 : }
62 : :
63 : 0 : void QgsBrowserModel::updateProjectHome()
64 : : {
65 : 0 : QString home = QgsProject::instance()->homePath();
66 : 0 : if ( mProjectHome && mProjectHome->path().mid( QStringLiteral( PROJECT_HOME_PREFIX ).length() ) == home )
67 : 0 : return;
68 : :
69 : 0 : int idx = mRootItems.indexOf( mProjectHome );
70 : :
71 : : // using layoutAboutToBeChanged() was messing expanded items
72 : 0 : if ( idx >= 0 )
73 : : {
74 : 0 : beginRemoveRows( QModelIndex(), idx, idx );
75 : 0 : mRootItems.remove( idx );
76 : 0 : endRemoveRows();
77 : 0 : }
78 : 0 : delete mProjectHome;
79 : 0 : mProjectHome = home.isNull() ? nullptr : new QgsProjectHomeItem( nullptr, tr( "Project Home" ), home, QStringLiteral( PROJECT_HOME_PREFIX ) + home );
80 : 0 : if ( mProjectHome )
81 : : {
82 : 0 : setupItemConnections( mProjectHome );
83 : :
84 : 0 : beginInsertRows( QModelIndex(), 0, 0 );
85 : 0 : mRootItems.insert( 0, mProjectHome );
86 : 0 : endInsertRows();
87 : 0 : }
88 : 0 : }
89 : :
90 : 0 : void QgsBrowserModel::addRootItems()
91 : : {
92 : 0 : updateProjectHome();
93 : :
94 : : // give the home directory a prominent third place
95 : 0 : QgsDirectoryItem *item = new QgsDirectoryItem( nullptr, tr( "Home" ), QDir::homePath(),
96 : 0 : QStringLiteral( HOME_PREFIX ) + QDir::homePath(),
97 : 0 : QStringLiteral( "special:Home" ) );
98 : 0 : item->setSortKey( QStringLiteral( " 2" ) );
99 : 0 : setupItemConnections( item );
100 : 0 : mRootItems << item;
101 : :
102 : : // add favorite directories
103 : 0 : mFavorites = new QgsFavoritesItem( nullptr, tr( "Favorites" ) );
104 : 0 : if ( mFavorites )
105 : : {
106 : 0 : setupItemConnections( mFavorites );
107 : 0 : mRootItems << mFavorites;
108 : 0 : }
109 : :
110 : : // add drives
111 : 0 : const auto drives { QDir::drives() };
112 : 0 : for ( const QFileInfo &drive : drives )
113 : : {
114 : 0 : const QString path = drive.absolutePath();
115 : :
116 : 0 : if ( QgsDirectoryItem::hiddenPath( path ) )
117 : 0 : continue;
118 : :
119 : 0 : QgsDirectoryItem *item = new QgsDirectoryItem( nullptr, path, path, path, QStringLiteral( "special:Drives" ) );
120 : 0 : item->setSortKey( QStringLiteral( " 3 %1" ).arg( path ) );
121 : 0 : mDriveItems.insert( path, item );
122 : :
123 : 0 : setupItemConnections( item );
124 : 0 : mRootItems << item;
125 : 0 : }
126 : :
127 : : #ifdef Q_OS_MAC
128 : : QString path = QString( "/Volumes" );
129 : : QgsDirectoryItem *vols = new QgsDirectoryItem( nullptr, path, path, path, QStringLiteral( "special:Volumes" ) );
130 : : setupItemConnections( vols );
131 : : mRootItems << vols;
132 : : #endif
133 : :
134 : : // container for displaying providers as sorted groups (by QgsDataProvider::DataCapability enum)
135 : 0 : QMultiMap<int, QgsDataItem *> providerMap;
136 : :
137 : 0 : const auto constProviders = QgsApplication::dataItemProviderRegistry()->providers();
138 : 0 : for ( QgsDataItemProvider *pr : constProviders )
139 : : {
140 : 0 : if ( QgsDataItem *item = addProviderRootItem( pr ) )
141 : : {
142 : 0 : providerMap.insert( pr->capabilities(), item );
143 : 0 : }
144 : : }
145 : :
146 : : // add as sorted groups by QgsDataProvider::DataCapability enum
147 : 0 : const auto constUniqueKeys = providerMap.uniqueKeys();
148 : 0 : for ( int key : constUniqueKeys )
149 : : {
150 : 0 : QList<QgsDataItem *> providerGroup = providerMap.values( key );
151 : 0 : if ( providerGroup.size() > 1 )
152 : : {
153 : 0 : std::sort( providerGroup.begin(), providerGroup.end(), cmpByDataItemName_ );
154 : 0 : }
155 : :
156 : 0 : const auto constProviderGroup = providerGroup;
157 : 0 : for ( QgsDataItem *ditem : constProviderGroup )
158 : : {
159 : 0 : mRootItems << ditem;
160 : : }
161 : 0 : }
162 : 0 : }
163 : :
164 : 0 : void QgsBrowserModel::removeRootItems()
165 : : {
166 : 0 : const auto constMRootItems = mRootItems;
167 : 0 : for ( QgsDataItem *item : constMRootItems )
168 : : {
169 : 0 : delete item;
170 : : }
171 : :
172 : 0 : mRootItems.clear();
173 : 0 : mDriveItems.clear();
174 : 0 : }
175 : :
176 : 0 : void QgsBrowserModel::dataItemProviderAdded( QgsDataItemProvider *provider )
177 : : {
178 : 0 : if ( !mInitialized )
179 : 0 : return;
180 : :
181 : 0 : if ( QgsDataItem *item = addProviderRootItem( provider ) )
182 : : {
183 : 0 : beginInsertRows( QModelIndex(), rowCount(), rowCount() );
184 : 0 : mRootItems << item;
185 : 0 : endInsertRows();
186 : 0 : }
187 : 0 : }
188 : :
189 : 0 : void QgsBrowserModel::dataItemProviderWillBeRemoved( QgsDataItemProvider *provider )
190 : : {
191 : 0 : const auto constMRootItems = mRootItems;
192 : 0 : for ( QgsDataItem *item : constMRootItems )
193 : : {
194 : 0 : if ( item->providerKey() == provider->name() )
195 : : {
196 : 0 : removeRootItem( item );
197 : 0 : break; // assuming there is max. 1 root item per provider
198 : : }
199 : : }
200 : 0 : }
201 : :
202 : 0 : void QgsBrowserModel::onConnectionsChanged( const QString &providerKey )
203 : : {
204 : : // refresh the matching provider
205 : 0 : for ( QgsDataItem *item : std::as_const( mRootItems ) )
206 : : {
207 : 0 : if ( item->providerKey() == providerKey )
208 : : {
209 : 0 : item->refresh();
210 : 0 : break; // assuming there is max. 1 root item per provider
211 : : }
212 : : }
213 : :
214 : 0 : emit connectionsChanged( providerKey );
215 : 0 : }
216 : :
217 : 0 : QMap<QString, QgsDirectoryItem *> QgsBrowserModel::driveItems() const
218 : : {
219 : 0 : return mDriveItems;
220 : : }
221 : :
222 : :
223 : 0 : void QgsBrowserModel::initialize()
224 : : {
225 : 0 : if ( ! mInitialized )
226 : : {
227 : 0 : connect( QgsProject::instance(), &QgsProject::homePathChanged, this, &QgsBrowserModel::updateProjectHome );
228 : 0 : addRootItems();
229 : 0 : mInitialized = true;
230 : 0 : }
231 : 0 : }
232 : :
233 : 0 : Qt::ItemFlags QgsBrowserModel::flags( const QModelIndex &index ) const
234 : : {
235 : 0 : if ( !index.isValid() )
236 : 0 : return Qt::ItemFlags();
237 : :
238 : 0 : Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
239 : :
240 : 0 : QgsDataItem *ptr = dataItem( index );
241 : :
242 : 0 : if ( !ptr )
243 : : {
244 : 0 : QgsDebugMsgLevel( QStringLiteral( "FLAGS PROBLEM!" ), 4 );
245 : 0 : return Qt::ItemFlags();
246 : : }
247 : :
248 : 0 : if ( ptr->hasDragEnabled() )
249 : 0 : flags |= Qt::ItemIsDragEnabled;
250 : :
251 : : Q_NOWARN_DEPRECATED_PUSH
252 : 0 : if ( ptr->acceptDrop() )
253 : 0 : flags |= Qt::ItemIsDropEnabled;
254 : : Q_NOWARN_DEPRECATED_POP
255 : 0 :
256 : 0 : if ( ptr->capabilities2() & QgsDataItem::Rename )
257 : 0 : flags |= Qt::ItemIsEditable;
258 : :
259 : 0 : return flags;
260 : 0 : }
261 : :
262 : 0 : QVariant QgsBrowserModel::data( const QModelIndex &index, int role ) const
263 : : {
264 : 0 : if ( !index.isValid() )
265 : 0 : return QVariant();
266 : :
267 : 0 : QgsDataItem *item = dataItem( index );
268 : 0 : if ( !item )
269 : : {
270 : 0 : return QVariant();
271 : : }
272 : 0 : else if ( role == Qt::DisplayRole || role == Qt::EditRole )
273 : : {
274 : 0 : return item->name();
275 : : }
276 : 0 : else if ( role == QgsBrowserModel::SortRole )
277 : : {
278 : 0 : return item->sortKey();
279 : : }
280 : 0 : else if ( role == Qt::ToolTipRole )
281 : : {
282 : 0 : return item->toolTip();
283 : : }
284 : 0 : else if ( role == Qt::DecorationRole && index.column() == 0 )
285 : : {
286 : 0 : return item->icon();
287 : : }
288 : 0 : else if ( role == QgsBrowserModel::PathRole )
289 : : {
290 : 0 : return item->path();
291 : : }
292 : 0 : else if ( role == QgsBrowserModel::CommentRole )
293 : : {
294 : 0 : if ( item->type() == QgsDataItem::Layer )
295 : : {
296 : 0 : QgsLayerItem *lyrItem = qobject_cast<QgsLayerItem *>( item );
297 : 0 : return lyrItem->comments();
298 : : }
299 : 0 : return QVariant();
300 : : }
301 : 0 : else if ( role == QgsBrowserModel::ProviderKeyRole )
302 : : {
303 : 0 : return item->providerKey();
304 : : }
305 : : else
306 : : {
307 : : // unsupported role
308 : 0 : return QVariant();
309 : : }
310 : 0 : }
311 : :
312 : 0 : bool QgsBrowserModel::setData( const QModelIndex &index, const QVariant &value, int role )
313 : : {
314 : 0 : if ( !index.isValid() )
315 : 0 : return false;
316 : :
317 : :
318 : 0 : QgsDataItem *item = dataItem( index );
319 : 0 : if ( !item )
320 : : {
321 : 0 : return false;
322 : : }
323 : :
324 : 0 : if ( !( item->capabilities2() & QgsDataItem::Rename ) )
325 : 0 : return false;
326 : :
327 : 0 : switch ( role )
328 : : {
329 : : case Qt::EditRole:
330 : : {
331 : : Q_NOWARN_DEPRECATED_PUSH
332 : 0 : return item->rename( value.toString() );
333 : : Q_NOWARN_DEPRECATED_POP
334 : : }
335 : : }
336 : 0 : return false;
337 : 0 : }
338 : :
339 : 0 : QVariant QgsBrowserModel::headerData( int section, Qt::Orientation orientation, int role ) const
340 : : {
341 : : Q_UNUSED( section )
342 : 0 : if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
343 : : {
344 : 0 : return QVariant( "header" );
345 : : }
346 : :
347 : 0 : return QVariant();
348 : 0 : }
349 : :
350 : 0 : int QgsBrowserModel::rowCount( const QModelIndex &parent ) const
351 : : {
352 : : //QgsDebugMsg(QString("isValid = %1 row = %2 column = %3").arg(parent.isValid()).arg(parent.row()).arg(parent.column()));
353 : :
354 : 0 : if ( !parent.isValid() )
355 : : {
356 : : // root item: its children are top level items
357 : 0 : return mRootItems.count(); // mRoot
358 : : }
359 : : else
360 : : {
361 : : // ordinary item: number of its children
362 : 0 : QgsDataItem *item = dataItem( parent );
363 : : //if ( item ) QgsDebugMsg(QString("path = %1 rowCount = %2").arg(item->path()).arg(item->rowCount()) );
364 : 0 : return item ? item->rowCount() : 0;
365 : : }
366 : 0 : }
367 : :
368 : 0 : bool QgsBrowserModel::hasChildren( const QModelIndex &parent ) const
369 : : {
370 : 0 : if ( !parent.isValid() )
371 : 0 : return !mRootItems.isEmpty(); // root item: its children are top level items
372 : :
373 : 0 : QgsDataItem *item = dataItem( parent );
374 : 0 : return item && item->hasChildren();
375 : 0 : }
376 : :
377 : 0 : int QgsBrowserModel::columnCount( const QModelIndex &parent ) const
378 : : {
379 : 0 : Q_UNUSED( parent )
380 : 0 : return 1;
381 : : }
382 : :
383 : 0 : QModelIndex QgsBrowserModel::findPath( const QString &path, Qt::MatchFlag matchFlag )
384 : : {
385 : 0 : return findPath( this, path, matchFlag );
386 : : }
387 : :
388 : 0 : QModelIndex QgsBrowserModel::findPath( QAbstractItemModel *model, const QString &path, Qt::MatchFlag matchFlag )
389 : : {
390 : 0 : if ( !model )
391 : 0 : return QModelIndex();
392 : :
393 : 0 : QModelIndex index; // starting from root
394 : 0 : bool foundChild = true;
395 : :
396 : 0 : while ( foundChild )
397 : : {
398 : 0 : foundChild = false; // assume that the next child item will not be found
399 : :
400 : 0 : for ( int i = 0; i < model->rowCount( index ); i++ )
401 : : {
402 : 0 : QModelIndex idx = model->index( i, 0, index );
403 : :
404 : 0 : QString itemPath = model->data( idx, PathRole ).toString();
405 : 0 : if ( itemPath == path )
406 : : {
407 : 0 : QgsDebugMsgLevel( "Arrived " + itemPath, 4 );
408 : 0 : return idx; // we have found the item we have been looking for
409 : : }
410 : :
411 : : // paths are slash separated identifier
412 : 0 : if ( path.startsWith( itemPath + '/' ) )
413 : : {
414 : 0 : foundChild = true;
415 : 0 : index = idx;
416 : 0 : break;
417 : : }
418 : 0 : }
419 : : }
420 : :
421 : 0 : if ( matchFlag == Qt::MatchStartsWith )
422 : 0 : return index;
423 : :
424 : 0 : QgsDebugMsgLevel( QStringLiteral( "path not found" ), 4 );
425 : 0 : return QModelIndex(); // not found
426 : 0 : }
427 : :
428 : 0 : QModelIndex QgsBrowserModel::findUri( const QString &uri, QModelIndex index )
429 : : {
430 : 0 : for ( int i = 0; i < this->rowCount( index ); i++ )
431 : : {
432 : 0 : QModelIndex idx = this->index( i, 0, index );
433 : :
434 : 0 : if ( qobject_cast<QgsLayerItem *>( dataItem( idx ) ) )
435 : : {
436 : 0 : QString itemUri = qobject_cast<QgsLayerItem *>( dataItem( idx ) )->uri();
437 : :
438 : 0 : if ( itemUri == uri )
439 : : {
440 : 0 : QgsDebugMsgLevel( "Arrived " + itemUri, 4 );
441 : 0 : return idx; // we have found the item we have been looking for
442 : : }
443 : 0 : }
444 : :
445 : 0 : QModelIndex childIdx = findUri( uri, idx );
446 : 0 : if ( childIdx.isValid() )
447 : 0 : return childIdx;
448 : 0 : }
449 : 0 : return QModelIndex();
450 : 0 : }
451 : :
452 : 0 : void QgsBrowserModel::connectItem( QgsDataItem * )
453 : : {
454 : : // deprecated, no use
455 : 0 : }
456 : :
457 : 0 : void QgsBrowserModel::reload()
458 : : {
459 : : // TODO: put items creating currently children in threads to deleteLater (does not seem urget because reload() is not used in QGIS)
460 : 0 : beginResetModel();
461 : 0 : removeRootItems();
462 : 0 : addRootItems();
463 : 0 : endResetModel();
464 : 0 : }
465 : :
466 : 0 : void QgsBrowserModel::refreshDrives()
467 : : {
468 : 0 : const QList< QFileInfo > drives = QDir::drives();
469 : : // remove any removed drives
470 : 0 : const QStringList existingDrives = mDriveItems.keys();
471 : 0 : for ( const QString &drivePath : existingDrives )
472 : : {
473 : 0 : bool stillExists = false;
474 : 0 : for ( const QFileInfo &drive : drives )
475 : : {
476 : 0 : if ( drivePath == drive.absolutePath() )
477 : : {
478 : 0 : stillExists = true;
479 : 0 : break;
480 : : }
481 : : }
482 : :
483 : 0 : if ( stillExists )
484 : 0 : continue;
485 : :
486 : : // drive has been removed, remove corresponding item
487 : 0 : if ( QgsDirectoryItem *driveItem = mDriveItems.value( drivePath ) )
488 : 0 : removeRootItem( driveItem );
489 : : }
490 : :
491 : 0 : for ( const QFileInfo &drive : drives )
492 : : {
493 : 0 : const QString path = drive.absolutePath();
494 : :
495 : 0 : if ( QgsDirectoryItem::hiddenPath( path ) )
496 : 0 : continue;
497 : :
498 : : // does an item for this drive already exist?
499 : 0 : if ( !mDriveItems.contains( path ) )
500 : : {
501 : 0 : QgsDirectoryItem *item = new QgsDirectoryItem( nullptr, path, path, path, QStringLiteral( "special:Drives" ) );
502 : 0 : item->setSortKey( QStringLiteral( " 3 %1" ).arg( path ) );
503 : :
504 : 0 : mDriveItems.insert( path, item );
505 : 0 : setupItemConnections( item );
506 : :
507 : 0 : beginInsertRows( QModelIndex(), mRootItems.count(), mRootItems.count() );
508 : 0 : mRootItems << item;
509 : 0 : endInsertRows();
510 : 0 : }
511 : 0 : }
512 : 0 : }
513 : :
514 : 0 : QModelIndex QgsBrowserModel::index( int row, int column, const QModelIndex &parent ) const
515 : : {
516 : 0 : if ( column < 0 || column >= columnCount() || row < 0 )
517 : 0 : return QModelIndex();
518 : :
519 : 0 : QgsDataItem *p = dataItem( parent );
520 : 0 : const QVector<QgsDataItem *> &items = p ? p->children() : mRootItems;
521 : 0 : QgsDataItem *item = items.value( row, nullptr );
522 : 0 : return item ? createIndex( row, column, item ) : QModelIndex();
523 : 0 : }
524 : :
525 : 0 : QModelIndex QgsBrowserModel::parent( const QModelIndex &index ) const
526 : : {
527 : 0 : QgsDataItem *item = dataItem( index );
528 : 0 : if ( !item )
529 : 0 : return QModelIndex();
530 : :
531 : 0 : return findItem( item->parent() );
532 : 0 : }
533 : :
534 : 0 : QModelIndex QgsBrowserModel::findItem( QgsDataItem *item, QgsDataItem *parent ) const
535 : : {
536 : 0 : const QVector<QgsDataItem *> &items = parent ? parent->children() : mRootItems;
537 : :
538 : 0 : for ( int i = 0; i < items.size(); i++ )
539 : : {
540 : 0 : if ( items[i] == item )
541 : 0 : return createIndex( i, 0, item );
542 : :
543 : 0 : QModelIndex childIndex = findItem( item, items[i] );
544 : 0 : if ( childIndex.isValid() )
545 : 0 : return childIndex;
546 : 0 : }
547 : 0 : return QModelIndex();
548 : 0 : }
549 : :
550 : 0 : void QgsBrowserModel::beginInsertItems( QgsDataItem *parent, int first, int last )
551 : : {
552 : 0 : QgsDebugMsgLevel( "parent mPath = " + parent->path(), 3 );
553 : 0 : QModelIndex idx = findItem( parent );
554 : 0 : if ( !idx.isValid() )
555 : 0 : return;
556 : 0 : QgsDebugMsgLevel( QStringLiteral( "valid" ), 3 );
557 : 0 : beginInsertRows( idx, first, last );
558 : 0 : QgsDebugMsgLevel( QStringLiteral( "end" ), 3 );
559 : 0 : }
560 : 0 : void QgsBrowserModel::endInsertItems()
561 : : {
562 : 0 : QgsDebugMsgLevel( QStringLiteral( "Entered" ), 3 );
563 : 0 : endInsertRows();
564 : 0 : }
565 : 0 : void QgsBrowserModel::beginRemoveItems( QgsDataItem *parent, int first, int last )
566 : : {
567 : 0 : QgsDebugMsgLevel( "parent mPath = " + parent->path(), 3 );
568 : 0 : QModelIndex idx = findItem( parent );
569 : 0 : if ( !idx.isValid() )
570 : 0 : return;
571 : 0 : beginRemoveRows( idx, first, last );
572 : 0 : }
573 : 0 : void QgsBrowserModel::endRemoveItems()
574 : : {
575 : 0 : QgsDebugMsgLevel( QStringLiteral( "Entered" ), 3 );
576 : 0 : endRemoveRows();
577 : 0 : }
578 : 0 : void QgsBrowserModel::itemDataChanged( QgsDataItem *item )
579 : : {
580 : 0 : QgsDebugMsgLevel( QStringLiteral( "Entered" ), 3 );
581 : 0 : QModelIndex idx = findItem( item );
582 : 0 : if ( !idx.isValid() )
583 : 0 : return;
584 : 0 : emit dataChanged( idx, idx );
585 : 0 : }
586 : 0 : void QgsBrowserModel::itemStateChanged( QgsDataItem *item, QgsDataItem::State oldState )
587 : : {
588 : 0 : if ( !item )
589 : 0 : return;
590 : 0 : QModelIndex idx = findItem( item );
591 : 0 : if ( !idx.isValid() )
592 : 0 : return;
593 : 0 : QgsDebugMsgLevel( QStringLiteral( "item %1 state changed %2 -> %3" ).arg( item->path() ).arg( oldState ).arg( item->state() ), 4 );
594 : 0 : emit stateChanged( idx, oldState );
595 : 0 : }
596 : :
597 : 0 : void QgsBrowserModel::setupItemConnections( QgsDataItem *item )
598 : : {
599 : 0 : connect( item, &QgsDataItem::beginInsertItems,
600 : : this, &QgsBrowserModel::beginInsertItems );
601 : 0 : connect( item, &QgsDataItem::endInsertItems,
602 : : this, &QgsBrowserModel::endInsertItems );
603 : 0 : connect( item, &QgsDataItem::beginRemoveItems,
604 : : this, &QgsBrowserModel::beginRemoveItems );
605 : 0 : connect( item, &QgsDataItem::endRemoveItems,
606 : : this, &QgsBrowserModel::endRemoveItems );
607 : 0 : connect( item, &QgsDataItem::dataChanged,
608 : : this, &QgsBrowserModel::itemDataChanged );
609 : 0 : connect( item, &QgsDataItem::stateChanged,
610 : : this, &QgsBrowserModel::itemStateChanged );
611 : :
612 : : // if it's a collection item, also forwards connectionsChanged
613 : 0 : QgsDataCollectionItem *collectionItem = qobject_cast<QgsDataCollectionItem *>( item );
614 : 0 : if ( collectionItem )
615 : 0 : connect( collectionItem, &QgsDataCollectionItem::connectionsChanged, this, &QgsBrowserModel::onConnectionsChanged );
616 : 0 : }
617 : :
618 : 0 : QStringList QgsBrowserModel::mimeTypes() const
619 : : {
620 : 0 : QStringList types;
621 : : // In theory the mime type convention is: application/x-vnd.<vendor>.<application>.<type>
622 : : // but it seems a bit over formalized. Would be an application/x-qgis-uri better?
623 : 0 : types << QStringLiteral( "application/x-vnd.qgis.qgis.uri" );
624 : 0 : return types;
625 : 0 : }
626 : :
627 : 0 : QMimeData *QgsBrowserModel::mimeData( const QModelIndexList &indexes ) const
628 : : {
629 : 0 : QgsMimeDataUtils::UriList lst;
630 : 0 : const auto constIndexes = indexes;
631 : 0 : for ( const QModelIndex &index : constIndexes )
632 : : {
633 : 0 : if ( index.isValid() )
634 : : {
635 : 0 : QgsDataItem *ptr = reinterpret_cast< QgsDataItem * >( index.internalPointer() );
636 : 0 : const QgsMimeDataUtils::UriList uris = ptr->mimeUris();
637 : 0 : for ( const auto &uri : std::as_const( uris ) )
638 : : {
639 : 0 : lst.append( uri );
640 : : }
641 : 0 : }
642 : : }
643 : 0 : return QgsMimeDataUtils::encodeUriList( lst );
644 : 0 : }
645 : :
646 : 0 : bool QgsBrowserModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int, int, const QModelIndex &parent )
647 : : {
648 : 0 : QgsDataItem *destItem = dataItem( parent );
649 : 0 : if ( !destItem )
650 : : {
651 : 0 : QgsDebugMsgLevel( QStringLiteral( "DROP PROBLEM!" ), 4 );
652 : 0 : return false;
653 : : }
654 : :
655 : : Q_NOWARN_DEPRECATED_PUSH
656 : 0 : return destItem->handleDrop( data, action );
657 : : Q_NOWARN_DEPRECATED_POP
658 : 0 : }
659 : :
660 : 0 : QgsDataItem *QgsBrowserModel::dataItem( const QModelIndex &idx ) const
661 : : {
662 : 0 : void *v = idx.internalPointer();
663 : 0 : QgsDataItem *d = reinterpret_cast<QgsDataItem *>( v );
664 : : Q_ASSERT( !v || d );
665 : 0 : return d;
666 : : }
667 : :
668 : 0 : bool QgsBrowserModel::canFetchMore( const QModelIndex &parent ) const
669 : : {
670 : 0 : QgsDataItem *item = dataItem( parent );
671 : : // if ( item )
672 : : // QgsDebugMsg( QStringLiteral( "path = %1 canFetchMore = %2" ).arg( item->path() ).arg( item && ! item->isPopulated() ) );
673 : 0 : return ( item && item->state() == QgsDataItem::NotPopulated );
674 : : }
675 : :
676 : 0 : void QgsBrowserModel::fetchMore( const QModelIndex &parent )
677 : : {
678 : 0 : QgsDataItem *item = dataItem( parent );
679 : :
680 : 0 : if ( !item || item->state() == QgsDataItem::Populating || item->state() == QgsDataItem::Populated )
681 : 0 : return;
682 : :
683 : 0 : QgsDebugMsgLevel( "path = " + item->path(), 4 );
684 : :
685 : 0 : item->populate();
686 : 0 : }
687 : :
688 : : /* Refresh dir path */
689 : 0 : void QgsBrowserModel::refresh( const QString &path )
690 : : {
691 : 0 : QModelIndex index = findPath( path );
692 : 0 : refresh( index );
693 : 0 : }
694 : :
695 : : /* Refresh item */
696 : 0 : void QgsBrowserModel::refresh( const QModelIndex &index )
697 : : {
698 : 0 : QgsDataItem *item = dataItem( index );
699 : 0 : if ( !item || item->state() == QgsDataItem::Populating )
700 : 0 : return;
701 : :
702 : 0 : QgsDebugMsgLevel( "Refresh " + item->path(), 4 );
703 : :
704 : 0 : item->refresh();
705 : 0 : }
706 : :
707 : 0 : void QgsBrowserModel::addFavoriteDirectory( const QString &directory, const QString &name )
708 : : {
709 : : Q_ASSERT( mFavorites );
710 : 0 : mFavorites->addDirectory( directory, name );
711 : 0 : }
712 : :
713 : 0 : void QgsBrowserModel::removeFavorite( const QModelIndex &index )
714 : : {
715 : 0 : QgsDirectoryItem *item = qobject_cast<QgsDirectoryItem *>( dataItem( index ) );
716 : 0 : if ( !item )
717 : 0 : return;
718 : :
719 : 0 : mFavorites->removeDirectory( item );
720 : 0 : }
721 : :
722 : 0 : void QgsBrowserModel::removeFavorite( QgsFavoriteItem *favorite )
723 : : {
724 : 0 : if ( !favorite )
725 : 0 : return;
726 : :
727 : 0 : mFavorites->removeDirectory( favorite );
728 : 0 : }
729 : :
730 : 0 : void QgsBrowserModel::hidePath( QgsDataItem *item )
731 : : {
732 : 0 : QgsSettings settings;
733 : 0 : QStringList hiddenItems = settings.value( QStringLiteral( "browser/hiddenPaths" ),
734 : 0 : QStringList() ).toStringList();
735 : 0 : int idx = hiddenItems.indexOf( item->path() );
736 : 0 : if ( idx != -1 )
737 : : {
738 : 0 : hiddenItems.removeAt( idx );
739 : 0 : }
740 : : else
741 : : {
742 : 0 : hiddenItems << item->path();
743 : : }
744 : 0 : settings.setValue( QStringLiteral( "browser/hiddenPaths" ), hiddenItems );
745 : 0 : if ( item->parent() )
746 : : {
747 : 0 : item->parent()->deleteChildItem( item );
748 : 0 : }
749 : : else
750 : : {
751 : 0 : removeRootItem( item );
752 : : }
753 : 0 : }
754 : :
755 : :
756 : 0 : void QgsBrowserModel::removeRootItem( QgsDataItem *item )
757 : : {
758 : 0 : int i = mRootItems.indexOf( item );
759 : 0 : beginRemoveRows( QModelIndex(), i, i );
760 : 0 : mRootItems.remove( i );
761 : 0 : QgsDirectoryItem *dirItem = qobject_cast< QgsDirectoryItem * >( item );
762 : 0 : if ( !mDriveItems.key( dirItem ).isEmpty() )
763 : : {
764 : 0 : mDriveItems.remove( mDriveItems.key( dirItem ) );
765 : 0 : }
766 : 0 : item->deleteLater();
767 : 0 : endRemoveRows();
768 : 0 : }
769 : :
770 : 0 : QgsDataItem *QgsBrowserModel::addProviderRootItem( QgsDataItemProvider *pr )
771 : : {
772 : 0 : int capabilities = pr->capabilities();
773 : 0 : if ( capabilities == QgsDataProvider::NoDataCapabilities )
774 : : {
775 : 0 : QgsDebugMsgLevel( pr->name() + " does not have any dataCapabilities", 4 );
776 : 0 : return nullptr;
777 : : }
778 : :
779 : 0 : QgsDataItem *item = pr->createDataItem( QString(), nullptr ); // empty path -> top level
780 : 0 : if ( item )
781 : : {
782 : : // make sure the top level key is always set
783 : 0 : item->setProviderKey( pr->name() );
784 : : // Forward the signal from the root items to the model (and then to the app)
785 : 0 : connect( item, &QgsDataItem::connectionsChanged, this, &QgsBrowserModel::onConnectionsChanged );
786 : 0 : QgsDebugMsgLevel( "Add new top level item : " + item->name(), 4 );
787 : 0 : setupItemConnections( item );
788 : 0 : }
789 : 0 : return item;
790 : 0 : }
|