1 /***************************************************************************
2 qgsappbrowserproviders.cpp
3 ---------------------------
4 begin : September 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson 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
16 #include "qgisapp.h"
17 #include "qgsapplication.h"
18 #include "qgsappbrowserproviders.h"
19 #include "qgsbookmarkeditordialog.h"
20 #include "qgsmapcanvas.h"
21 #include "qgsmessagebar.h"
22 #include "qgsproject.h"
23 #include "qgsstyleexportimportdialog.h"
24 #include "qgsstyle.h"
25 #include "qgslayertreenode.h"
26 #include "qgslayertree.h"
27 #include "qgsstylemanagerdialog.h"
28 #include "qgsguiutils.h"
29 #include "qgsfileutils.h"
30
31 #include <QDesktopServices>
32 #include <QMessageBox>
33 #include <QFileDialog>
34 #include <QUrl>
35
iconBookmarks()36 QIcon QgsBookmarksItem::iconBookmarks()
37 {
38 return QgsApplication::getThemeIcon( QStringLiteral( "/mActionShowBookmarks.svg" ) );
39 }
40
sortKey() const41 QVariant QgsBookmarksItem::sortKey() const
42 {
43 return QStringLiteral( " 1" );
44 }
45
iconBookmarkManager()46 QIcon QgsBookmarkManagerItem::iconBookmarkManager()
47 {
48 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFolder.svg" ) );
49 }
50
iconBookmarkGroup()51 QIcon QgsBookmarkGroupItem::iconBookmarkGroup()
52 {
53 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFolder.svg" ) );
54 }
55
iconBookmark()56 QIcon QgsBookmarkItem::iconBookmark()
57 {
58 return QgsApplication::getThemeIcon( QStringLiteral( "/mItemBookmark.svg" ) );
59 }
60
hasDragEnabled() const61 bool QgsBookmarkItem::hasDragEnabled() const
62 {
63 return true;
64 }
65
mimeUri() const66 QgsMimeDataUtils::Uri QgsBookmarkItem::mimeUri() const
67 {
68 QgsMimeDataUtils::Uri u;
69 u.layerType = QStringLiteral( "custom" );
70 u.providerKey = QStringLiteral( "bookmark" );
71 u.name = name();
72
73 QDomDocument doc;
74 doc.appendChild( mBookmark.writeXml( doc ) );
75 u.uri = doc.toString();
76
77 return u;
78 }
79
80 //
81 // QgsQlrDataItem
82 //
83
QgsQlrDataItem(QgsDataItem * parent,const QString & name,const QString & path)84 QgsQlrDataItem::QgsQlrDataItem( QgsDataItem *parent, const QString &name, const QString &path )
85 : QgsLayerItem( parent, name, path, path, Qgis::BrowserLayerType::NoType, QStringLiteral( "qlr" ) )
86 {
87 setState( Qgis::BrowserItemState::Populated ); // no children
88 setIconName( QStringLiteral( ":/images/icons/qgis-icon-16x16.png" ) );
89 setToolTip( QDir::toNativeSeparators( path ) );
90 mCapabilities |= Qgis::BrowserItemCapability::ItemRepresentsFile;
91 }
92
hasDragEnabled() const93 bool QgsQlrDataItem::hasDragEnabled() const
94 {
95 return true;
96 }
97
mimeUri() const98 QgsMimeDataUtils::Uri QgsQlrDataItem::mimeUri() const
99 {
100 QgsMimeDataUtils::Uri u;
101 u.layerType = QStringLiteral( "custom" );
102 u.providerKey = QStringLiteral( "qlr" );
103 u.name = name();
104 u.uri = path();
105 u.filePath = path();
106 return u;
107 }
108
mimeUris() const109 QgsMimeDataUtils::UriList QgsQlrDataItem::mimeUris() const
110 {
111 return QgsMimeDataUtils::UriList() << QgsQlrDataItem::mimeUri();
112 }
113
114 //
115 // QgsQlrDataItemProvider
116 //
117
name()118 QString QgsQlrDataItemProvider::name()
119 {
120 return QStringLiteral( "QLR" );
121 }
122
capabilities() const123 int QgsQlrDataItemProvider::capabilities() const
124 {
125 return QgsDataProvider::File;
126 }
127
createDataItem(const QString & path,QgsDataItem * parentItem)128 QgsDataItem *QgsQlrDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
129 {
130 const QFileInfo fileInfo( path );
131
132 if ( fileInfo.suffix().compare( QLatin1String( "qlr" ), Qt::CaseInsensitive ) == 0 )
133 {
134 return new QgsQlrDataItem( parentItem, fileInfo.fileName(), path );
135 }
136 return nullptr;
137 }
138
customUriProviderKey() const139 QString QgsQlrDropHandler::customUriProviderKey() const
140 {
141 return QStringLiteral( "qlr" );
142 }
143
handleCustomUriDrop(const QgsMimeDataUtils::Uri & uri) const144 void QgsQlrDropHandler::handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const
145 {
146 const QString path = uri.uri;
147 QgisApp::instance()->openLayerDefinition( path );
148 }
149
150 //
151 // QgsQptDataItemProvider
152 //
153
name()154 QString QgsQptDataItemProvider::name()
155 {
156 return QStringLiteral( "QPT" );
157 }
158
capabilities() const159 int QgsQptDataItemProvider::capabilities() const
160 {
161 return QgsDataProvider::File;
162 }
163
createDataItem(const QString & path,QgsDataItem * parentItem)164 QgsDataItem *QgsQptDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
165 {
166 const QFileInfo fileInfo( path );
167
168 if ( fileInfo.suffix().compare( QLatin1String( "qpt" ), Qt::CaseInsensitive ) == 0 )
169 {
170 return new QgsQptDataItem( parentItem, fileInfo.baseName(), path );
171 }
172 return nullptr;
173 }
174
175 //
176 // QgsQptDropHandler
177 //
178
customUriProviderKey() const179 QString QgsQptDropHandler::customUriProviderKey() const
180 {
181 return QStringLiteral( "qpt" );
182 }
183
handleCustomUriDrop(const QgsMimeDataUtils::Uri & uri) const184 void QgsQptDropHandler::handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const
185 {
186 const QString path = uri.uri;
187 QgisApp::instance()->openTemplate( path );
188 }
189
handleFileDrop(const QString & file)190 bool QgsQptDropHandler::handleFileDrop( const QString &file )
191 {
192 const QFileInfo fi( file );
193 if ( fi.completeSuffix().compare( QLatin1String( "qpt" ), Qt::CaseInsensitive ) == 0 )
194 {
195 QgisApp::instance()->openTemplate( file );
196 return true;
197 }
198 return false;
199 }
200
201 //
202 // QgsQptDataItem
203 //
204
QgsQptDataItem(QgsDataItem * parent,const QString & name,const QString & path)205 QgsQptDataItem::QgsQptDataItem( QgsDataItem *parent, const QString &name, const QString &path )
206 : QgsDataItem( Qgis::BrowserItemType::Custom, parent, name, path )
207 {
208 setState( Qgis::BrowserItemState::Populated ); // no children
209 setIconName( QStringLiteral( "/mIconQptFile.svg" ) );
210 setToolTip( QDir::toNativeSeparators( path ) );
211 mCapabilities |= Qgis::BrowserItemCapability::ItemRepresentsFile;
212 }
213
hasDragEnabled() const214 bool QgsQptDataItem::hasDragEnabled() const
215 {
216 return true;
217 }
218
mimeUri() const219 QgsMimeDataUtils::Uri QgsQptDataItem::mimeUri() const
220 {
221 QgsMimeDataUtils::Uri u;
222 u.layerType = QStringLiteral( "custom" );
223 u.providerKey = QStringLiteral( "qpt" );
224 u.name = name();
225 u.uri = path();
226 u.filePath = path();
227 return u;
228 }
229
handleDoubleClick()230 bool QgsQptDataItem::handleDoubleClick()
231 {
232 QgisApp::instance()->openTemplate( path() );
233 return true;
234 }
235
actions(QWidget * parent)236 QList<QAction *> QgsQptDataItem::actions( QWidget *parent )
237 {
238 QAction *newLayout = new QAction( tr( "New Layout from Template" ), parent );
239 connect( newLayout, &QAction::triggered, this, [ = ]
240 {
241 QgisApp::instance()->openTemplate( path() );
242 } );
243 return QList<QAction *>() << newLayout;
244 }
245
246 //
247 // QgsPyDataItem
248 //
249
QgsPyDataItem(QgsDataItem * parent,const QString & name,const QString & path)250 QgsPyDataItem::QgsPyDataItem( QgsDataItem *parent, const QString &name, const QString &path )
251 : QgsDataItem( Qgis::BrowserItemType::Custom, parent, name, path )
252 {
253 setState( Qgis::BrowserItemState::Populated ); // no children
254 setIconName( QStringLiteral( "/mIconPythonFile.svg" ) );
255 setToolTip( QDir::toNativeSeparators( path ) );
256 mCapabilities |= Qgis::BrowserItemCapability::ItemRepresentsFile;
257 }
258
hasDragEnabled() const259 bool QgsPyDataItem::hasDragEnabled() const
260 {
261 return true;
262 }
263
mimeUri() const264 QgsMimeDataUtils::Uri QgsPyDataItem::mimeUri() const
265 {
266 QgsMimeDataUtils::Uri u;
267 u.layerType = QStringLiteral( "custom" );
268 u.providerKey = QStringLiteral( "py" );
269 u.name = name();
270 u.uri = path();
271 u.filePath = path();
272 return u;
273 }
274
handleDoubleClick()275 bool QgsPyDataItem::handleDoubleClick()
276 {
277 QgisApp::instance()->runScript( path() );
278 return true;
279 }
280
actions(QWidget * parent)281 QList<QAction *> QgsPyDataItem::actions( QWidget *parent )
282 {
283 QAction *runScript = new QAction( tr( "&Run Script" ), parent );
284 connect( runScript, &QAction::triggered, this, [ = ]
285 {
286 QgisApp::instance()->runScript( path() );
287 } );
288 QAction *editScript = new QAction( tr( "Open in External &Editor" ), this );
289 connect( editScript, &QAction::triggered, this, [ = ]
290 {
291 QDesktopServices::openUrl( QUrl::fromLocalFile( path() ) );
292 } );
293 return QList<QAction *>() << runScript << editScript;
294 }
295
296 //
297 // QgsPyDataItemProvider
298 //
299
name()300 QString QgsPyDataItemProvider::name()
301 {
302 return QStringLiteral( "py" );
303 }
304
capabilities() const305 int QgsPyDataItemProvider::capabilities() const
306 {
307 return QgsDataProvider::File;
308 }
309
createDataItem(const QString & path,QgsDataItem * parentItem)310 QgsDataItem *QgsPyDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
311 {
312 const QFileInfo fileInfo( path );
313
314 if ( fileInfo.suffix().compare( QLatin1String( "py" ), Qt::CaseInsensitive ) == 0 )
315 {
316 return new QgsPyDataItem( parentItem, fileInfo.baseName(), path );
317 }
318 return nullptr;
319 }
320
321 //
322 // QgsPyDropHandler
323 //
324
customUriProviderKey() const325 QString QgsPyDropHandler::customUriProviderKey() const
326 {
327 return QStringLiteral( "py" );
328 }
329
handleCustomUriDrop(const QgsMimeDataUtils::Uri & uri) const330 void QgsPyDropHandler::handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const
331 {
332 const QString path = uri.uri;
333 QgisApp::instance()->runScript( path );
334 }
335
handleFileDrop(const QString & file)336 bool QgsPyDropHandler::handleFileDrop( const QString &file )
337 {
338 const QFileInfo fi( file );
339 if ( fi.completeSuffix().compare( QLatin1String( "py" ), Qt::CaseInsensitive ) == 0 )
340 {
341 QgisApp::instance()->runScript( file );
342 return true;
343 }
344 return false;
345 }
346
347
348
349
350 //
351 // QgsStyleXmlDataItem
352 //
353
QgsStyleXmlDataItem(QgsDataItem * parent,const QString & name,const QString & path)354 QgsStyleXmlDataItem::QgsStyleXmlDataItem( QgsDataItem *parent, const QString &name, const QString &path )
355 : QgsDataItem( Qgis::BrowserItemType::Custom, parent, name, path )
356 {
357 setState( Qgis::BrowserItemState::Populated ); // no children
358 setIconName( QStringLiteral( "/mActionStyleManager.svg" ) );
359 setToolTip( QStringLiteral( "<b>%1</b><br>%2" ).arg( tr( "QGIS style library" ), QDir::toNativeSeparators( path ) ) );
360 mCapabilities |= Qgis::BrowserItemCapability::ItemRepresentsFile;
361 }
362
hasDragEnabled() const363 bool QgsStyleXmlDataItem::hasDragEnabled() const
364 {
365 return true;
366 }
367
mimeUri() const368 QgsMimeDataUtils::Uri QgsStyleXmlDataItem::mimeUri() const
369 {
370 QgsMimeDataUtils::Uri u;
371 u.layerType = QStringLiteral( "custom" );
372 u.providerKey = QStringLiteral( "style_xml" );
373 u.name = name();
374 u.uri = path();
375 u.filePath = path();
376 return u;
377 }
378
handleDoubleClick()379 bool QgsStyleXmlDataItem::handleDoubleClick()
380 {
381 browseStyle( mPath );
382 return true;
383 }
384
actions(QWidget * parent)385 QList<QAction *> QgsStyleXmlDataItem::actions( QWidget *parent )
386 {
387 QAction *browseAction = new QAction( tr( "&Open Style…" ), parent );
388 const QString path = mPath;
389 connect( browseAction, &QAction::triggered, this, [path]
390 {
391 browseStyle( path );
392 } );
393
394 QAction *importAction = new QAction( tr( "&Import Style…" ), parent );
395 connect( importAction, &QAction::triggered, this, [path]
396 {
397 QgsStyleExportImportDialog dlg( QgsStyle::defaultStyle(), QgisApp::instance(), QgsStyleExportImportDialog::Import );
398 dlg.setImportFilePath( path );
399 dlg.exec();
400 } );
401 return QList<QAction *>() << browseAction << importAction;
402 }
403
browseStyle(const QString & xmlPath)404 void QgsStyleXmlDataItem::browseStyle( const QString &xmlPath )
405 {
406 QgsStyle s;
407 s.createMemoryDatabase();
408
409 auto cursorOverride = std::make_unique< QgsTemporaryCursorOverride >( Qt::WaitCursor );
410 if ( s.importXml( xmlPath ) )
411 {
412 cursorOverride.reset();
413 const QFileInfo fi( xmlPath );
414 QgsStyleManagerDialog dlg( &s, QgisApp::instance(), Qt::WindowFlags(), true );
415 dlg.setSmartGroupsVisible( false );
416 dlg.setFavoritesGroupVisible( false );
417 dlg.setBaseStyleName( fi.baseName() );
418 dlg.exec();
419 }
420 }
421
422 //
423 // QgsStyleXmlDataItemProvider
424 //
425
name()426 QString QgsStyleXmlDataItemProvider::name()
427 {
428 return QStringLiteral( "style_xml" );
429 }
430
capabilities() const431 int QgsStyleXmlDataItemProvider::capabilities() const
432 {
433 return QgsDataProvider::File;
434 }
435
createDataItem(const QString & path,QgsDataItem * parentItem)436 QgsDataItem *QgsStyleXmlDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
437 {
438 if ( QgsStyle::isXmlStyleFile( path ) )
439 {
440 return new QgsStyleXmlDataItem( parentItem, QFileInfo( path ).fileName(), path );
441 }
442 return nullptr;
443 }
444
445 //
446 // QgsStyleXmlDropHandler
447 //
448
customUriProviderKey() const449 QString QgsStyleXmlDropHandler::customUriProviderKey() const
450 {
451 return QStringLiteral( "style_xml" );
452 }
453
handleCustomUriDrop(const QgsMimeDataUtils::Uri & uri) const454 void QgsStyleXmlDropHandler::handleCustomUriDrop( const QgsMimeDataUtils::Uri &uri ) const
455 {
456 QgsStyleExportImportDialog dlg( QgsStyle::defaultStyle(), QgisApp::instance(), QgsStyleExportImportDialog::Import );
457 dlg.setImportFilePath( uri.uri );
458 dlg.exec();
459 }
460
handleFileDrop(const QString & file)461 bool QgsStyleXmlDropHandler::handleFileDrop( const QString &file )
462 {
463 if ( QgsStyle::isXmlStyleFile( file ) )
464 {
465 QgsStyleExportImportDialog dlg( QgsStyle::defaultStyle(), QgisApp::instance(), QgsStyleExportImportDialog::Import );
466 dlg.setImportFilePath( file );
467 dlg.exec();
468 return true;
469 }
470 return false;
471 }
472
473 //
474 // QgsProjectRootDataItem
475 //
476
QgsProjectRootDataItem(QgsDataItem * parent,const QString & path)477 QgsProjectRootDataItem::QgsProjectRootDataItem( QgsDataItem *parent, const QString &path )
478 : QgsProjectItem( parent, QFileInfo( path ).completeBaseName(), path )
479 {
480 mCapabilities = Qgis::BrowserItemCapability::Collapse | Qgis::BrowserItemCapability::Fertile; // collapse by default to avoid costly population on startup
481 mCapabilities |= Qgis::BrowserItemCapability::ItemRepresentsFile;
482 setState( Qgis::BrowserItemState::NotPopulated );
483 }
484
485
createChildren()486 QVector<QgsDataItem *> QgsProjectRootDataItem::createChildren()
487 {
488 QVector<QgsDataItem *> childItems;
489
490 QgsProject p;
491 if ( !p.read( mPath, QgsProject::ReadFlag::FlagDontResolveLayers | QgsProject::ReadFlag::FlagDontLoadLayouts | QgsProject::ReadFlag::FlagDontStoreOriginalStyles ) )
492 {
493 childItems.append( new QgsErrorItem( nullptr, p.error(), mPath + "/error" ) );
494 return childItems;
495 }
496
497 // recursively create groups and layer items for project's layer tree
498 std::function<void( QgsDataItem *parentItem, QgsLayerTreeGroup *group )> addNodes;
499 addNodes = [this, &addNodes, &childItems]( QgsDataItem * parentItem, QgsLayerTreeGroup * group )
500 {
501 const QList< QgsLayerTreeNode * > children = group->children();
502 for ( QgsLayerTreeNode *child : children )
503 {
504 switch ( child->nodeType() )
505 {
506 case QgsLayerTreeNode::NodeLayer:
507 {
508 if ( QgsLayerTreeLayer *layerNode = qobject_cast< QgsLayerTreeLayer * >( child ) )
509 {
510 QgsMapLayer *layer = layerNode->layer();
511 #if 0 // TODO
512 QString style;
513 if ( layer )
514 {
515 QString errorMsg;
516 QDomDocument doc( QStringLiteral( "qgis" ) );
517 QgsReadWriteContext context;
518 context.setPathResolver( p.pathResolver() );
519 layer->exportNamedStyle( doc, errorMsg, context );
520 style = doc.toString();
521 }
522 #endif
523
524 QgsLayerItem *layerItem = new QgsLayerItem( nullptr, layerNode->name(),
525 layer ? layer->source() : QString(),
526 layer ? layer->source() : QString(),
527 layer ? QgsLayerItem::typeFromMapLayer( layer ) : Qgis::BrowserLayerType::NoType,
528 layer ? layer->providerType() : QString() );
529 layerItem->setState( Qgis::BrowserItemState::Populated ); // children are not expected
530 layerItem->setToolTip( layer ? layer->source() : QString() );
531 if ( parentItem == this )
532 childItems << layerItem;
533 else
534 parentItem->addChildItem( layerItem, true );
535 }
536 break;
537 }
538
539 case QgsLayerTreeNode::NodeGroup:
540 {
541 if ( QgsLayerTreeGroup *groupNode = qobject_cast< QgsLayerTreeGroup * >( child ) )
542 {
543 QgsProjectLayerTreeGroupItem *groupItem = new QgsProjectLayerTreeGroupItem( nullptr, groupNode->name() );
544 addNodes( groupItem, groupNode );
545 groupItem->setState( Qgis::BrowserItemState::Populated );
546 if ( parentItem == this )
547 childItems << groupItem;
548 else
549 parentItem->addChildItem( groupItem, true );
550 }
551 }
552 break;
553 }
554 }
555 };
556
557 addNodes( this, p.layerTreeRoot() );
558 return childItems;
559 }
560
561
562 //
563 // QgsProjectLayerTreeGroupItem
564 //
565
QgsProjectLayerTreeGroupItem(QgsDataItem * parent,const QString & name)566 QgsProjectLayerTreeGroupItem::QgsProjectLayerTreeGroupItem( QgsDataItem *parent, const QString &name )
567 : QgsDataCollectionItem( parent, name )
568 {
569 mIconName = QStringLiteral( "mActionFolder.svg" );
570 mCapabilities = Qgis::BrowserItemCapability::NoCapabilities;
571 setToolTip( name );
572 }
573
574
575 //
576 // QgsProjectDataItemProvider
577 //
578
name()579 QString QgsProjectDataItemProvider::name()
580 {
581 return QStringLiteral( "project_item" );
582 }
583
capabilities() const584 int QgsProjectDataItemProvider::capabilities() const
585 {
586 return QgsDataProvider::File;
587 }
588
createDataItem(const QString & path,QgsDataItem * parentItem)589 QgsDataItem *QgsProjectDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
590 {
591 const QFileInfo fileInfo( path );
592 if ( fileInfo.suffix().compare( QLatin1String( "qgs" ), Qt::CaseInsensitive ) == 0 || fileInfo.suffix().compare( QLatin1String( "qgz" ), Qt::CaseInsensitive ) == 0 )
593 {
594 return new QgsProjectRootDataItem( parentItem, path );
595 }
596 return nullptr;
597 }
598
599 //
600 // QgsBookmarksDataItemProvider
601 //
602
name()603 QString QgsBookmarksDataItemProvider::name()
604 {
605 return QStringLiteral( "bookmarks_item" );
606 }
607
capabilities() const608 int QgsBookmarksDataItemProvider::capabilities() const
609 {
610 return QgsDataProvider::Database;
611 }
612
createDataItem(const QString &,QgsDataItem * parentItem)613 QgsDataItem *QgsBookmarksDataItemProvider::createDataItem( const QString &, QgsDataItem *parentItem )
614 {
615 return new QgsBookmarksItem( parentItem, QObject::tr( "Spatial Bookmarks" ), QgsApplication::bookmarkManager(), QgsProject::instance()->bookmarkManager() );
616 }
617
QgsBookmarksItem(QgsDataItem * parent,const QString & name,QgsBookmarkManager * applicationManager,QgsBookmarkManager * projectManager)618 QgsBookmarksItem::QgsBookmarksItem( QgsDataItem *parent, const QString &name, QgsBookmarkManager *applicationManager, QgsBookmarkManager *projectManager )
619 : QgsDataCollectionItem( parent, name, QStringLiteral( "bookmarks:" ) )
620 {
621 mType = Qgis::BrowserItemType::Custom;
622 mCapabilities = Qgis::BrowserItemCapability::Fast;
623 mApplicationManager = applicationManager;
624 mProjectManager = projectManager;
625 mIconName = QStringLiteral( "/mActionShowBookmarks.svg" );
626 populate();
627 }
628
createChildren()629 QVector<QgsDataItem *> QgsBookmarksItem::createChildren()
630 {
631 QVector<QgsDataItem *> children;
632 if ( mApplicationManager )
633 children << new QgsBookmarkManagerItem( this, tr( "User Bookmarks" ), mApplicationManager );
634 if ( mProjectManager )
635 children << new QgsBookmarkManagerItem( this, tr( "Project Bookmarks" ), mProjectManager );
636 return children;
637 }
638
QgsBookmarkManagerItem(QgsDataItem * parent,const QString & name,QgsBookmarkManager * manager)639 QgsBookmarkManagerItem::QgsBookmarkManagerItem( QgsDataItem *parent, const QString &name, QgsBookmarkManager *manager )
640 : QgsDataCollectionItem( parent, name, QStringLiteral( "bookmarks:%1" ).arg( name.toLower() ) )
641 {
642 mType = Qgis::BrowserItemType::Custom;
643 mCapabilities = Qgis::BrowserItemCapability::Fast;
644 mManager = manager;
645 mIconName = QStringLiteral( "/mIconFolder.svg" );
646
647 connect( mManager, &QgsBookmarkManager::bookmarkAdded, this, [ = ]( const QString & id )
648 {
649 const QgsBookmark newDetails = mManager->bookmarkById( id );
650 if ( newDetails.group().isEmpty() )
651 addChildItem( new QgsBookmarkItem( this, newDetails.name(), newDetails, mManager ), true );
652 else
653 {
654 if ( QgsBookmarkGroupItem *newGroup = groupItem( newDetails.group() ) )
655 {
656 // existing group, add this bookmark to it
657 newGroup->addBookmark( newDetails );
658 }
659 else
660 {
661 // need to create a new group for this (will automatically add the new bookmark)
662 addChildItem( new QgsBookmarkGroupItem( this, newDetails.group(), mManager ), true );
663 }
664 }
665 } );
666 connect( mManager, &QgsBookmarkManager::bookmarkChanged, this, [ = ]( const QString & id )
667 {
668 const QgsBookmark newDetails = mManager->bookmarkById( id );
669
670 // have to do a deep dive to find the old item...!
671 const QVector<QgsDataItem *> c = children();
672 for ( QgsDataItem *i : c )
673 {
674 if ( QgsBookmarkItem *bookmarkItem = qobject_cast< QgsBookmarkItem * >( i ) )
675 {
676 if ( bookmarkItem->bookmark().id() == id )
677 {
678 // found the target! now, what's changed?
679 if ( bookmarkItem->bookmark().group() == newDetails.group() )
680 {
681 // good, not the group. Just update the existing bookmark then.
682 bookmarkItem->setBookmark( newDetails );
683 return;
684 }
685 else
686 {
687 // group has changed, ouch!
688 // first remove from existing group
689 deleteChildItem( bookmarkItem );
690 // and add a child to the new group
691 if ( QgsBookmarkGroupItem *newGroup = groupItem( newDetails.group() ) )
692 {
693 newGroup->addBookmark( newDetails );
694 }
695 else
696 {
697 // need to create a new group for this (will automatically add the new bookmark)
698 addChildItem( new QgsBookmarkGroupItem( this, newDetails.group(), mManager ), true );
699 }
700 }
701 break;
702 }
703 }
704 else if ( QgsBookmarkGroupItem *group = qobject_cast< QgsBookmarkGroupItem * >( i ) )
705 {
706 if ( QgsBookmarkItem *bookmarkItem = group->childItemById( id ) )
707 {
708 // ok, found old group, now compare
709 if ( bookmarkItem->bookmark().group() == newDetails.group() )
710 {
711 // good, not the group. Just update the existing bookmark then.
712 bookmarkItem->setBookmark( newDetails );
713 }
714 else
715 {
716 // group has changed!
717 // first remove from existing group
718 group->deleteChildItem( bookmarkItem );
719 if ( group->children().empty() )
720 deleteChildItem( group );
721
722 // and add a child to the new group
723 if ( !newDetails.group().isEmpty() )
724 {
725 if ( QgsBookmarkGroupItem *newGroup = groupItem( newDetails.group() ) )
726 {
727 newGroup->addBookmark( newDetails );
728 }
729 else
730 {
731 // need to create a new group for this (will automatically add the new bookmark)
732 addChildItem( new QgsBookmarkGroupItem( this, newDetails.group(), mManager ), true );
733 }
734 }
735 else
736 {
737 addChildItem( new QgsBookmarkItem( this, newDetails.name(), newDetails, mManager ), true );
738 }
739 }
740 break;
741 }
742 }
743 }
744 } );
745 connect( mManager, &QgsBookmarkManager::bookmarkAboutToBeRemoved, this, [ = ]( const QString & id )
746 {
747 const QgsBookmark b = mManager->bookmarkById( id );
748 if ( !b.group().isEmpty() )
749 {
750 if ( QgsBookmarkGroupItem *group = groupItem( b.group() ) )
751 {
752 group->removeBookmarkChildById( id );
753 if ( group->children().empty() )
754 deleteChildItem( group );
755 }
756 }
757 else if ( QgsBookmarkItem *bookmarkItem = childItemById( id ) )
758 {
759 deleteChildItem( bookmarkItem );
760 }
761 } );
762
763 populate();
764 }
765
createChildren()766 QVector<QgsDataItem *> QgsBookmarkManagerItem::createChildren()
767 {
768 QVector<QgsDataItem *> children;
769 const QStringList groupNames = mManager->groups();
770 for ( const QString &group : groupNames )
771 {
772 if ( group.isEmpty() )
773 {
774 const QList<QgsBookmark> matching = mManager->bookmarksByGroup( QString() );
775 for ( const QgsBookmark &bookmark : matching )
776 {
777 children << new QgsBookmarkItem( this, bookmark.name(), bookmark, mManager );
778 }
779 }
780 else
781 {
782 QgsBookmarkGroupItem *item = new QgsBookmarkGroupItem( this, group, mManager );
783 children << item;
784 }
785 }
786 return children;
787 }
788
groupItem(const QString & group)789 QgsBookmarkGroupItem *QgsBookmarkManagerItem::groupItem( const QString &group )
790 {
791 const QVector<QgsDataItem *> c = children();
792 for ( QgsDataItem *i : c )
793 {
794 if ( QgsBookmarkGroupItem *groupItem = qobject_cast< QgsBookmarkGroupItem * >( i ) )
795 {
796 if ( groupItem->group() == group )
797 {
798 return groupItem;
799 }
800 }
801 }
802 return nullptr;
803 }
804
childItemById(const QString & id)805 QgsBookmarkItem *QgsBookmarkManagerItem::childItemById( const QString &id )
806 {
807 const QVector<QgsDataItem *> c = children();
808 for ( QgsDataItem *i : c )
809 {
810 if ( QgsBookmarkItem *bookmarkItem = qobject_cast< QgsBookmarkItem * >( i ) )
811 {
812 if ( bookmarkItem->bookmark().id() == id )
813 {
814 return bookmarkItem;
815 }
816 }
817 }
818 return nullptr;
819 }
820
QgsBookmarkGroupItem(QgsDataItem * parent,const QString & name,QgsBookmarkManager * manager)821 QgsBookmarkGroupItem::QgsBookmarkGroupItem( QgsDataItem *parent, const QString &name, QgsBookmarkManager *manager )
822 : QgsDataCollectionItem( parent, name, QStringLiteral( "bookmarks:%1" ).arg( name.toLower() ) )
823 , mGroup( name )
824 {
825 mType = Qgis::BrowserItemType::Custom;
826 mCapabilities = Qgis::BrowserItemCapability::Fast | Qgis::BrowserItemCapability::Rename;
827 mManager = manager;
828 mIconName = QStringLiteral( "/mIconFolder.svg" );
829 setToolTip( name );
830
831 setSortKey( QStringLiteral( " %1" ).arg( name ) );
832
833 populate();
834 }
835
createChildren()836 QVector<QgsDataItem *> QgsBookmarkGroupItem::createChildren()
837 {
838 QVector<QgsDataItem *> children;
839 const QList< QgsBookmark > bookmarks = mManager->bookmarksByGroup( mName );
840 children.reserve( bookmarks.size() );
841 for ( const QgsBookmark &bookmark : bookmarks )
842 {
843 children << new QgsBookmarkItem( this, bookmark.name(), bookmark, mManager );
844 }
845 return children;
846 }
847
addBookmark(const QgsBookmark & bookmark)848 void QgsBookmarkGroupItem::addBookmark( const QgsBookmark &bookmark )
849 {
850 addChildItem( new QgsBookmarkItem( this, bookmark.name(), bookmark, mManager ), true );
851 }
852
childItemById(const QString & id)853 QgsBookmarkItem *QgsBookmarkGroupItem::childItemById( const QString &id )
854 {
855 const QVector<QgsDataItem *> c = children();
856 for ( QgsDataItem *i : c )
857 {
858 if ( QgsBookmarkItem *bookmarkItem = qobject_cast< QgsBookmarkItem * >( i ) )
859 {
860 if ( bookmarkItem->bookmark().id() == id )
861 {
862 return bookmarkItem;
863 }
864 }
865 }
866 return nullptr;
867 }
868
removeBookmarkChildById(const QString & id)869 void QgsBookmarkGroupItem::removeBookmarkChildById( const QString &id )
870 {
871 if ( QgsBookmarkItem *bookmarkItem = childItemById( id ) )
872 deleteChildItem( bookmarkItem );
873 }
874
QgsBookmarkItem(QgsDataItem * parent,const QString & name,const QgsBookmark & bookmark,QgsBookmarkManager * manager)875 QgsBookmarkItem::QgsBookmarkItem( QgsDataItem *parent, const QString &name, const QgsBookmark &bookmark, QgsBookmarkManager *manager )
876 : QgsDataItem( Qgis::BrowserItemType::Custom, parent, name, QStringLiteral( "bookmarks:%1/%2" ).arg( bookmark.group().toLower(), bookmark.id() ) )
877 , mManager( manager )
878 , mBookmark( bookmark )
879 {
880 mType = Qgis::BrowserItemType::Custom;
881 mCapabilities = Qgis::BrowserItemCapability::Rename;
882 mIconName = QStringLiteral( "/mItemBookmark.svg" );
883 setToolTip( name );
884 setState( Qgis::BrowserItemState::Populated ); // no more children
885 }
886
setBookmark(const QgsBookmark & bookmark)887 void QgsBookmarkItem::setBookmark( const QgsBookmark &bookmark )
888 {
889 setName( bookmark.name() );
890 setToolTip( bookmark.name() );
891 mBookmark = bookmark;
892 }
893
customUriProviderKey() const894 QString QgsBookmarkDropHandler::customUriProviderKey() const
895 {
896 return QStringLiteral( "bookmark" );
897 }
898
canHandleCustomUriCanvasDrop(const QgsMimeDataUtils::Uri & uri,QgsMapCanvas *)899 bool QgsBookmarkDropHandler::canHandleCustomUriCanvasDrop( const QgsMimeDataUtils::Uri &uri, QgsMapCanvas * )
900 {
901 return uri.providerKey == customUriProviderKey();
902 }
903
handleCustomUriCanvasDrop(const QgsMimeDataUtils::Uri & uri,QgsMapCanvas * canvas) const904 bool QgsBookmarkDropHandler::handleCustomUriCanvasDrop( const QgsMimeDataUtils::Uri &uri, QgsMapCanvas *canvas ) const
905 {
906 QDomDocument doc;
907 doc.setContent( uri.uri );
908 const QDomElement elem = doc.documentElement();
909 const QgsBookmark b = QgsBookmark::fromXml( elem, doc );
910
911 try
912 {
913 if ( ! canvas->setReferencedExtent( b.extent() ) )
914 {
915 QgisApp::instance()->messageBar()->pushWarning( tr( "Zoom to Bookmark" ), tr( "Bookmark extent is empty" ) );
916 }
917 else
918 {
919 canvas->refresh();
920 }
921 }
922 catch ( QgsCsException & )
923 {
924 QgisApp::instance()->messageBar()->pushWarning( tr( "Zoom to Bookmark" ), tr( "Could not reproject bookmark extent to canvas CRS." ) );
925 }
926 return true;
927 }
928
name()929 QString QgsBookmarksItemGuiProvider::name()
930 {
931 return QStringLiteral( "bookmark_item" );
932 }
933
acceptDrop(QgsDataItem * item,QgsDataItemGuiContext)934 bool QgsBookmarksItemGuiProvider::acceptDrop( QgsDataItem *item, QgsDataItemGuiContext )
935 {
936 if ( qobject_cast< QgsBookmarkManagerItem * >( item ) )
937 return true;
938
939 if ( qobject_cast< QgsBookmarkGroupItem * >( item ) )
940 return true;
941
942 return false;
943 }
944
handleDrop(QgsDataItem * item,QgsDataItemGuiContext,const QMimeData * data,Qt::DropAction)945 bool QgsBookmarksItemGuiProvider::handleDrop( QgsDataItem *item, QgsDataItemGuiContext, const QMimeData *data, Qt::DropAction )
946 {
947 QgsBookmarkManagerItem *managerItem = qobject_cast< QgsBookmarkManagerItem * >( item );
948 QgsBookmarkGroupItem *groupItem = qobject_cast< QgsBookmarkGroupItem * >( item );
949 if ( managerItem || groupItem )
950 {
951 QgsBookmarkManager *target = managerItem ? managerItem->manager() : groupItem->manager();
952 if ( QgsMimeDataUtils::isUriList( data ) )
953 {
954 const QgsMimeDataUtils::UriList list = QgsMimeDataUtils::decodeUriList( data );
955 for ( const QgsMimeDataUtils::Uri &uri : list )
956 {
957 QDomDocument doc;
958 doc.setContent( uri.uri );
959 const QDomElement elem = doc.documentElement();
960 QgsBookmark b = QgsBookmark::fromXml( elem, doc );
961
962 if ( !groupItem )
963 b.setGroup( QString() );
964 else
965 b.setGroup( groupItem->group() );
966
967 // if bookmark doesn't already exist in manager, we add it. Otherwise we update it.
968 if ( target->bookmarkById( b.id() ).id().isEmpty() )
969 target->addBookmark( b );
970 else
971 target->updateBookmark( b );
972 }
973 return true;
974 }
975 }
976
977 return false;
978 }
979
populateContextMenu(QgsDataItem * item,QMenu * menu,const QList<QgsDataItem * > & selectedItems,QgsDataItemGuiContext context)980 void QgsBookmarksItemGuiProvider::populateContextMenu( QgsDataItem *item, QMenu *menu, const QList<QgsDataItem *> &selectedItems, QgsDataItemGuiContext context )
981 {
982 if ( qobject_cast< QgsBookmarksItem * >( item ) )
983 {
984 QAction *addBookmark = new QAction( tr( "New Spatial Bookmark…" ), menu );
985 connect( addBookmark, &QAction::triggered, this, [ = ]
986 {
987 QgisApp::instance()->newBookmark();
988 } );
989 menu->addAction( addBookmark );
990 QAction *showBookmarksPanel = new QAction( tr( "Show Spatial Bookmarks Manager" ), menu );
991 connect( showBookmarksPanel, &QAction::triggered, this, [ = ]
992 {
993 QgisApp::instance()->showBookmarkManager( true );
994 } );
995 menu->addAction( showBookmarksPanel );
996 menu->addSeparator();
997 QAction *importBookmarks = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSharingImport.svg" ) ), tr( "Import Spatial Bookmarks…" ), menu );
998 connect( importBookmarks, &QAction::triggered, this, [ = ]
999 {
1000 importBookmarksToManager( QgsApplication::bookmarkManager(), context.messageBar() );
1001 } );
1002 menu->addAction( importBookmarks );
1003 QAction *exportBookmarks = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSharingExport.svg" ) ), tr( "Export Spatial Bookmarks…" ), menu );
1004 connect( exportBookmarks, &QAction::triggered, this, [ = ]
1005 {
1006 exportBookmarksFromManagers( QList< const QgsBookmarkManager * >() << QgsApplication::bookmarkManager() << QgsProject::instance()->bookmarkManager(), context.messageBar() );
1007 } );
1008 menu->addAction( exportBookmarks );
1009 }
1010 else if ( QgsBookmarkManagerItem *managerItem = qobject_cast< QgsBookmarkManagerItem * >( item ) )
1011 {
1012 QAction *addBookmark = new QAction( tr( "New Spatial Bookmark…" ), menu );
1013 const bool inProject = managerItem->manager() != QgsApplication::bookmarkManager();
1014 connect( addBookmark, &QAction::triggered, this, [ = ]
1015 {
1016 QgisApp::instance()->newBookmark( inProject );
1017 } );
1018 menu->addAction( addBookmark );
1019 menu->addSeparator();
1020 QAction *importBookmarks = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSharingImport.svg" ) ), tr( "Import Spatial Bookmarks…" ), menu );
1021 connect( importBookmarks, &QAction::triggered, this, [ = ]
1022 {
1023 importBookmarksToManager( managerItem->manager(), context.messageBar() );
1024 } );
1025 menu->addAction( importBookmarks );
1026
1027 QAction *exportBookmarks = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSharingExport.svg" ) ), tr( "Export Spatial Bookmarks…" ), menu );
1028 connect( exportBookmarks, &QAction::triggered, this, [ = ]
1029 {
1030 exportBookmarksFromManagers( QList< const QgsBookmarkManager * >() << managerItem->manager(), context.messageBar() );
1031 } );
1032 menu->addAction( exportBookmarks );
1033 }
1034 else if ( QgsBookmarkItem *bookmarkItem = qobject_cast< QgsBookmarkItem * >( item ) )
1035 {
1036 QAction *actionZoom = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToLayer.svg" ) ), tr( "Zoom to Bookmark" ), menu );
1037 connect( actionZoom, &QAction::triggered, this, [bookmarkItem, context]
1038 {
1039 try
1040 {
1041 if ( !QgisApp::instance()->mapCanvas()->setReferencedExtent( bookmarkItem->bookmark().extent() ) )
1042 {
1043 context.messageBar()->pushWarning( tr( "Zoom to Bookmark" ), tr( "Bookmark extent is empty" ) );
1044 }
1045 else
1046 {
1047 QgisApp::instance()->mapCanvas()->refresh();
1048 }
1049 }
1050 catch ( QgsCsException & )
1051 {
1052 context.messageBar()->pushWarning( tr( "Zoom to Bookmark" ), tr( "Could not reproject bookmark extent to project CRS." ) );
1053 }
1054 } );
1055 menu->addAction( actionZoom );
1056 menu->addSeparator();
1057
1058 QAction *actionEdit = new QAction( tr( "Edit Spatial Bookmark…" ), menu );
1059 connect( actionEdit, &QAction::triggered, this, [bookmarkItem]
1060 {
1061 QgsBookmarkEditorDialog *dlg = new QgsBookmarkEditorDialog( bookmarkItem->bookmark(), bookmarkItem->manager() == QgsProject::instance()->bookmarkManager(), QgisApp::instance(), QgisApp::instance()->mapCanvas() );
1062 dlg->setAttribute( Qt::WA_DeleteOnClose );
1063 dlg->show();
1064 } );
1065 menu->addAction( actionEdit );
1066
1067 QStringList ids;
1068 for ( QgsDataItem *i : selectedItems )
1069 {
1070 if ( QgsBookmarkItem *b = qobject_cast< QgsBookmarkItem * >( i ) )
1071 {
1072 if ( b->manager() == bookmarkItem->manager() )
1073 ids << b->bookmark().id();
1074 }
1075 }
1076
1077 QAction *actionDelete = new QAction( selectedItems.count() == 1 ? tr( "Delete Spatial Bookmark" ) : tr( "Delete Spatial Bookmarks" ), menu );
1078 connect( actionDelete, &QAction::triggered, this, [bookmarkItem, ids]
1079 {
1080 if ( ids.count() == 1 )
1081 {
1082 if ( QMessageBox::question( nullptr, QObject::tr( "Delete Spatial Bookmark" ),
1083 QObject::tr( "Are you sure you want to delete the %1 bookmark?" ).arg( bookmarkItem->name() ),
1084 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
1085 return;
1086
1087 bookmarkItem->manager()->removeBookmark( bookmarkItem->bookmark().id() );
1088 }
1089 else
1090 {
1091 if ( QMessageBox::question( nullptr, QObject::tr( "Delete Spatial Bookmarks" ),
1092 QObject::tr( "Are you sure you want to delete the %1 selected bookmarks?" ).arg( ids.count() ),
1093 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
1094 return;
1095
1096 for ( const QString &id : ids )
1097 bookmarkItem->manager()->removeBookmark( id );
1098 }
1099 } );
1100 menu->addAction( actionDelete );
1101 }
1102 else if ( QgsBookmarkGroupItem *groupItem = qobject_cast< QgsBookmarkGroupItem * >( item ) )
1103 {
1104 QStringList groups;
1105 QgsBookmarkManager *manager = groupItem->manager();
1106 for ( QgsDataItem *i : selectedItems )
1107 {
1108 if ( QgsBookmarkGroupItem *g = qobject_cast< QgsBookmarkGroupItem * >( i ) )
1109 {
1110 if ( g->manager() == manager )
1111 groups << g->group();
1112 }
1113 }
1114
1115 QAction *exportBookmarks = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSharingExport.svg" ) ), tr( "Export Spatial Bookmarks…" ), menu );
1116 connect( exportBookmarks, &QAction::triggered, this, [ = ]
1117 {
1118 exportBookmarksFromManagers( QList< const QgsBookmarkManager * >() << groupItem->manager(), context.messageBar(), groupItem->group() );
1119 } );
1120 menu->addAction( exportBookmarks );
1121 menu->addSeparator();
1122
1123 QAction *actionDelete = new QAction( selectedItems.count() == 1 ? tr( "Delete Bookmark Group" ) : tr( "Delete Bookmark Groups" ), menu );
1124 connect( actionDelete, &QAction::triggered, this, [selectedItems, groups, manager]
1125 {
1126 if ( groups.count() == 1 )
1127 {
1128 if ( QMessageBox::question( nullptr, QObject::tr( "Delete Bookmark Group" ),
1129 QObject::tr( "Are you sure you want to delete the %1 bookmark group? This will delete all bookmarks in this group." ).arg( groups.at( 0 ) ),
1130 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
1131 return;
1132
1133 const QList<QgsBookmark> matching = manager->bookmarksByGroup( groups.at( 0 ) );
1134 for ( const QgsBookmark &bookmark : matching )
1135 {
1136 manager->removeBookmark( bookmark.id() );
1137 }
1138 }
1139 else
1140 {
1141 if ( QMessageBox::question( nullptr, QObject::tr( "Delete Bookmark Groups" ),
1142 QObject::tr( "Are you sure you want to delete the %1 selected bookmark groups? This will delete all bookmarks in these groups." ).arg( groups.count() ),
1143 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
1144 return;
1145
1146 int i = 0;
1147 for ( const QString &g : groups )
1148 {
1149 const QList<QgsBookmark> matching = manager->bookmarksByGroup( g );
1150 for ( const QgsBookmark &bookmark : matching )
1151 {
1152 manager->removeBookmark( bookmark.id() );
1153 }
1154 i++;
1155 }
1156 }
1157 } );
1158 menu->addAction( actionDelete );
1159 }
1160 }
1161
handleDoubleClick(QgsDataItem * item,QgsDataItemGuiContext context)1162 bool QgsBookmarksItemGuiProvider::handleDoubleClick( QgsDataItem *item, QgsDataItemGuiContext context )
1163 {
1164 if ( QgsBookmarkItem *bookmarkItem = qobject_cast< QgsBookmarkItem * >( item ) )
1165 {
1166 try
1167 {
1168 if ( !QgisApp::instance()->mapCanvas()->setReferencedExtent( bookmarkItem->bookmark().extent() ) )
1169 {
1170 context.messageBar()->pushWarning( tr( "Zoom to Bookmark" ), tr( "Bookmark extent is empty" ) );
1171 }
1172 else
1173 {
1174 QgisApp::instance()->mapCanvas()->refresh();
1175 }
1176 }
1177 catch ( QgsCsException & )
1178 {
1179 context.messageBar()->pushWarning( tr( "Zoom to Bookmark" ), tr( "Could not reproject bookmark extent to project CRS." ) );
1180 }
1181 return true;
1182 }
1183 return false;
1184 }
1185
rename(QgsDataItem * item,const QString & name,QgsDataItemGuiContext context)1186 bool QgsBookmarksItemGuiProvider::rename( QgsDataItem *item, const QString &name, QgsDataItemGuiContext context )
1187 {
1188 if ( QgsBookmarkItem *bookmarkItem = qobject_cast< QgsBookmarkItem * >( item ) )
1189 {
1190 QgsBookmark bookmark = bookmarkItem->bookmark();
1191 bookmark.setName( name );
1192 if ( !bookmarkItem->manager()->updateBookmark( bookmark ) )
1193 {
1194 context.messageBar()->pushWarning( tr( "Rename Bookmark" ), tr( "Could not rename bookmark" ) );
1195 return true;
1196 }
1197 return true;
1198 }
1199 else if ( QgsBookmarkGroupItem *groupItem = qobject_cast< QgsBookmarkGroupItem * >( item ) )
1200 {
1201 groupItem->manager()->renameGroup( groupItem->group(), name );
1202 return true;
1203 }
1204 return false;
1205 }
1206
exportBookmarksFromManagers(const QList<const QgsBookmarkManager * > & managers,QgsMessageBar * messageBar,const QString & group)1207 void QgsBookmarksItemGuiProvider::exportBookmarksFromManagers( const QList<const QgsBookmarkManager *> &managers, QgsMessageBar *messageBar, const QString &group )
1208 {
1209 QgsSettings settings;
1210
1211 const QString lastUsedDir = settings.value( QStringLiteral( "Windows/Bookmarks/LastUsedDirectory" ), QDir::homePath() ).toString();
1212 QString fileName = QFileDialog::getSaveFileName( QgisApp::instance(), tr( "Export Bookmarks" ), lastUsedDir,
1213 tr( "XML files (*.xml *.XML)" ) );
1214 if ( fileName.isEmpty() )
1215 {
1216 return;
1217 }
1218
1219 // ensure the user never omitted the extension from the file name
1220 fileName = QgsFileUtils::ensureFileNameHasExtension( fileName, QStringList() << QStringLiteral( "xml" ) );
1221
1222 if ( !QgsBookmarkManager::exportToFile( fileName, managers, group ) )
1223 {
1224 messageBar->pushWarning( tr( "Export Bookmarks" ), tr( "Error exporting bookmark file" ) );
1225 }
1226 else
1227 {
1228 messageBar->pushSuccess( tr( "Export Bookmarks" ), tr( "Successfully exported bookmarks to <a href=\"%1\">%2</a>" )
1229 .arg( QUrl::fromLocalFile( fileName ).toString(), QDir::toNativeSeparators( fileName ) ) );
1230 }
1231
1232 settings.setValue( QStringLiteral( "Windows/Bookmarks/LastUsedDirectory" ), QFileInfo( fileName ).path() );
1233 }
1234
importBookmarksToManager(QgsBookmarkManager * manager,QgsMessageBar * messageBar)1235 void QgsBookmarksItemGuiProvider::importBookmarksToManager( QgsBookmarkManager *manager, QgsMessageBar *messageBar )
1236 {
1237 QgsSettings settings;
1238
1239 const QString lastUsedDir = settings.value( QStringLiteral( "Windows/Bookmarks/LastUsedDirectory" ), QDir::homePath() ).toString();
1240 const QString fileName = QFileDialog::getOpenFileName( QgisApp::instance(), tr( "Import Bookmarks" ), lastUsedDir,
1241 tr( "XML files (*.xml *.XML)" ) );
1242 if ( fileName.isEmpty() )
1243 {
1244 return;
1245 }
1246
1247 if ( !manager->importFromFile( fileName ) )
1248 {
1249 messageBar->pushWarning( tr( "Import Bookmarks" ), tr( "Error importing bookmark file" ) );
1250 }
1251 else
1252 {
1253 messageBar->pushSuccess( tr( "Import Bookmarks" ), tr( "Bookmarks imported successfully" ) );
1254 }
1255 settings.setValue( QStringLiteral( "Windows/Bookmarks/LastUsedDirectory" ), QFileInfo( fileName ).path() );
1256 }
1257
1258 //
1259 // QgsHtmlDataItemProvider
1260 //
1261
name()1262 QString QgsHtmlDataItemProvider::name()
1263 {
1264 return QStringLiteral( "html" );
1265 }
1266
capabilities() const1267 int QgsHtmlDataItemProvider::capabilities() const
1268 {
1269 return QgsDataProvider::File;
1270 }
1271
createDataItem(const QString & path,QgsDataItem * parentItem)1272 QgsDataItem *QgsHtmlDataItemProvider::createDataItem( const QString &path, QgsDataItem *parentItem )
1273 {
1274 const QFileInfo fileInfo( path );
1275
1276 if ( fileInfo.suffix().compare( QLatin1String( "htm" ), Qt::CaseInsensitive ) == 0
1277 || fileInfo.suffix().compare( QLatin1String( "html" ), Qt::CaseInsensitive ) == 0 )
1278 {
1279 return new QgsHtmlDataItem( parentItem, fileInfo.fileName(), path );
1280 }
1281 return nullptr;
1282 }
1283
1284 //
1285 // QgsHtmlDataItemProvider
1286 //
1287
QgsHtmlDataItem(QgsDataItem * parent,const QString & name,const QString & path)1288 QgsHtmlDataItem::QgsHtmlDataItem( QgsDataItem *parent, const QString &name, const QString &path )
1289 : QgsDataItem( Qgis::BrowserItemType::Custom, parent, name, path )
1290 {
1291 setState( Qgis::BrowserItemState::Populated ); // no children
1292 setIconName( QStringLiteral( "/mIconHtml.svg" ) );
1293 setToolTip( QDir::toNativeSeparators( path ) );
1294 mCapabilities |= Qgis::BrowserItemCapability::ItemRepresentsFile;
1295 }
1296
handleDoubleClick()1297 bool QgsHtmlDataItem::handleDoubleClick()
1298 {
1299 QDesktopServices::openUrl( QUrl::fromLocalFile( path() ) );
1300 return true;
1301 }
1302
actions(QWidget * parent)1303 QList<QAction *> QgsHtmlDataItem::actions( QWidget *parent )
1304 {
1305 QAction *openAction = new QAction( tr( "&Open File…" ), parent );
1306 connect( openAction, &QAction::triggered, this, [ = ]
1307 {
1308 QDesktopServices::openUrl( QUrl::fromLocalFile( path() ) );
1309 } );
1310 return QList<QAction *>() << openAction;
1311 }
1312