1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2010 Gaurav Gupta <1989.gaurav@googlemail.com>
4 // SPDX-FileCopyrightText: 2012 Thibaut Gridel <tgridel@free.fr>
5 //
6 
7 #include "BookmarkManager.h"
8 #include "BookmarkManager_p.h"
9 #include "GeoDataParser.h"
10 #include "GeoDataContainer.h"
11 #include "GeoDataDocument.h"
12 #include "GeoDataFolder.h"
13 #include "GeoDataPlacemark.h"
14 #include "GeoDataTreeModel.h"
15 #include "GeoDataDocumentWriter.h"
16 #include "GeoDataIconStyle.h"
17 #include "KmlElementDictionary.h"
18 #include "MarbleDebug.h"
19 #include "MarbleDirs.h"
20 #include "StyleBuilder.h"
21 #include <QFile>
22 
23 namespace Marble
24 {
25 
BookmarkManagerPrivate(GeoDataTreeModel * treeModel)26 BookmarkManagerPrivate::BookmarkManagerPrivate( GeoDataTreeModel *treeModel ) :
27     m_treeModel( treeModel ),
28     m_bookmarkDocument( nullptr ),
29     m_bookmarkFileRelativePath( "bookmarks/bookmarks.kml" ),
30     m_styleBuilder(nullptr)
31 {
32     resetBookmarkDocument();
33 }
34 
~BookmarkManagerPrivate()35 BookmarkManagerPrivate::~BookmarkManagerPrivate()
36 {
37     Q_ASSERT( m_bookmarkDocument && "BookmarkManagerPrivate::m_bookmarkDocument is 0. Please report a Marble bug at https://bugs.kde.org" );
38     if ( m_bookmarkDocument )
39     {
40         m_treeModel->removeDocument( m_bookmarkDocument );
41     }
42     delete m_bookmarkDocument;
43 }
44 
resetBookmarkDocument()45 void BookmarkManagerPrivate::resetBookmarkDocument()
46 {
47     if ( m_bookmarkDocument ) {
48         m_treeModel->removeDocument( m_bookmarkDocument );
49         delete m_bookmarkDocument;
50     }
51 
52     GeoDataFolder* folder = new GeoDataFolder;
53     folder->setName( QObject::tr( "Default" ) );
54 
55     m_bookmarkDocument = new GeoDataDocument;
56     m_bookmarkDocument->setDocumentRole( BookmarkDocument );
57     m_bookmarkDocument->setName( QObject::tr("Bookmarks") );
58     m_bookmarkDocument->append( folder );
59     m_treeModel->addDocument( m_bookmarkDocument );
60 }
61 
setVisualCategory(GeoDataContainer * container)62 void BookmarkManagerPrivate::setVisualCategory( GeoDataContainer *container ) {
63     for( GeoDataFolder* folder: container->folderList() ) {
64         setVisualCategory( folder );
65     }
66     for( GeoDataPlacemark* placemark: container->placemarkList() ) {
67         placemark->setVisualCategory(GeoDataPlacemark::Bookmark);
68         placemark->setZoomLevel( 1 );
69     }
70 
71 }
72 
BookmarkManager(GeoDataTreeModel * treeModel,QObject * parent)73 BookmarkManager::BookmarkManager( GeoDataTreeModel *treeModel, QObject *parent ) :
74     QObject( parent ),
75     d( new BookmarkManagerPrivate( treeModel ) )
76 {
77 }
78 
~BookmarkManager()79 BookmarkManager::~BookmarkManager()
80 {
81     delete d;
82 }
83 
bookmarkFile() const84 QString BookmarkManager::bookmarkFile() const
85 {
86     return MarbleDirs::path( d->m_bookmarkFileRelativePath );
87 }
88 
loadFile(const QString & relativeFilePath)89 bool BookmarkManager::loadFile( const QString &relativeFilePath )
90 {
91     d->m_bookmarkFileRelativePath = relativeFilePath;
92     QString absoluteFilePath = bookmarkFile();
93 
94     mDebug() << Q_FUNC_INFO << "Loading Bookmark File:" << absoluteFilePath;
95 
96     if (absoluteFilePath.isEmpty())
97         return false;
98 
99     if ( relativeFilePath.isNull() )
100         return false;
101 
102     GeoDataDocument *document = openFile( absoluteFilePath );
103     bool recover = false;
104     if ( !document ) {
105         mDebug() << "Could not parse file" << absoluteFilePath;
106         mDebug() << "This could be caused by a previous broken bookmark file. Trying to recover.";
107         /** @todo: Remove this workaround and return false around Marble 1.4 */
108         recover = true;
109         // return false;
110     }
111 
112     d->m_treeModel->removeDocument( d->m_bookmarkDocument );
113     delete d->m_bookmarkDocument;
114     d->m_bookmarkDocument = document;
115 
116     if ( recover ) {
117         d->resetBookmarkDocument();
118         updateBookmarkFile();
119     } else {
120         Q_ASSERT( d->m_bookmarkDocument && "d->m_bookmarkDocument is 0 but must not be. Please report a bug at https://bugs.kde.org" );
121         d->m_treeModel->addDocument( d->m_bookmarkDocument );
122     }
123     ensureDefaultFolder();
124 
125     emit bookmarksChanged();
126     return true;
127 }
128 
addBookmark(GeoDataContainer * container,const GeoDataPlacemark & placemark)129 void BookmarkManager::addBookmark( GeoDataContainer *container, const GeoDataPlacemark &placemark )
130 {
131     GeoDataPlacemark *bookmark = new GeoDataPlacemark( placemark );
132     bookmark->setVisualCategory(GeoDataPlacemark::Bookmark);
133     bookmark->setZoomLevel( 1 );
134     if (bookmark->name().isEmpty()) {
135         bookmark->setName(bookmark->coordinate().toString(GeoDataCoordinates::Decimal).trimmed());
136     }
137     if (d->m_styleBuilder && bookmark->style()->iconStyle().iconPath().isEmpty()) {
138         StyleParameters style;
139         style.placemark = bookmark;
140         bookmark->setStyle(GeoDataStyle::Ptr(new GeoDataStyle(*d->m_styleBuilder->createStyle(style))));
141     }
142     d->m_treeModel->addFeature( container, bookmark );
143 
144     updateBookmarkFile();
145 }
146 
updateBookmark(GeoDataPlacemark * bookmark)147 void BookmarkManager::updateBookmark( GeoDataPlacemark *bookmark )
148 {
149     d->m_treeModel->updateFeature( bookmark );
150 }
151 
removeBookmark(GeoDataPlacemark * bookmark)152 void BookmarkManager::removeBookmark( GeoDataPlacemark *bookmark )
153 {
154     d->m_treeModel->removeFeature( bookmark );
155     delete bookmark;
156     updateBookmarkFile();
157 }
158 
bookmarkAt(GeoDataContainer * container,const GeoDataCoordinates & coordinate)159 GeoDataPlacemark* BookmarkManager::bookmarkAt(GeoDataContainer *container, const GeoDataCoordinates &coordinate)
160 {
161     for ( GeoDataFolder *folder: container->folderList() ) {
162         GeoDataPlacemark *placemark = bookmarkAt(folder, coordinate);
163         if ( placemark )
164             return placemark;
165     }
166 
167     for ( GeoDataPlacemark *placemark: container->placemarkList() ) {
168         if ( placemark->coordinate() == coordinate )
169             return placemark;
170     }
171 
172     return Q_NULLPTR;
173 }
174 
document()175 GeoDataDocument * BookmarkManager::document()
176 {
177     return d->m_bookmarkDocument;
178 }
179 
document() const180 const GeoDataDocument * BookmarkManager::document() const
181 {
182     return d->m_bookmarkDocument;
183 }
184 
showBookmarks() const185 bool BookmarkManager::showBookmarks() const
186 {
187     return d->m_bookmarkDocument->isVisible();
188 }
189 
setShowBookmarks(bool visible)190 void BookmarkManager::setShowBookmarks( bool visible )
191 {
192     d->m_bookmarkDocument->setVisible( visible );
193     d->m_treeModel->updateFeature( d->m_bookmarkDocument );
194 }
195 
folders() const196 QVector<GeoDataFolder*> BookmarkManager::folders() const
197 {
198     return d->m_bookmarkDocument->folderList();
199 }
200 
addNewBookmarkFolder(GeoDataContainer * container,const QString & name)201 GeoDataFolder* BookmarkManager::addNewBookmarkFolder( GeoDataContainer *container, const QString &name )
202 {
203     //If name is empty string
204     if ( name.isEmpty() ) {
205         mDebug() << "Folder with empty name is not acceptable, please give it another name" ;
206         return Q_NULLPTR;
207     }
208 
209     //If folder with same name already exist
210     QVector<GeoDataFolder*> folderList = container->folderList();
211 
212     QVector<GeoDataFolder*>::const_iterator i = folderList.constBegin();
213     QVector<GeoDataFolder*>::const_iterator end = folderList.constEnd();
214     for ( ; i != end; ++i ) {
215         if ( name == ( *i )->name() ) {
216             mDebug() << "Folder with same name already exist, please give it another name";
217             return *i;
218         }
219     }
220 
221     GeoDataFolder *bookmarkFolder = new GeoDataFolder();
222     bookmarkFolder->setName( name );
223 
224     d->m_treeModel->addFeature( container, bookmarkFolder );
225     updateBookmarkFile();
226 
227     return bookmarkFolder;
228 }
229 
renameBookmarkFolder(GeoDataFolder * folder,const QString & name)230 void BookmarkManager::renameBookmarkFolder( GeoDataFolder *folder, const QString &name )
231 {
232     folder->setName( name );
233     d->m_treeModel->updateFeature( folder );
234 }
235 
removeBookmarkFolder(GeoDataFolder * folder)236 void BookmarkManager::removeBookmarkFolder( GeoDataFolder *folder )
237 {
238     d->m_treeModel->removeFeature( folder );
239     delete folder;
240 }
241 
ensureDefaultFolder()242 void BookmarkManager::ensureDefaultFolder()
243 {
244     if ( d->m_bookmarkDocument->size() == 0 ) {
245         addNewBookmarkFolder( d->m_bookmarkDocument, tr("Default") );
246     }
247 }
248 
removeAllBookmarks()249 void BookmarkManager::removeAllBookmarks()
250 {
251     d->resetBookmarkDocument();
252     updateBookmarkFile();
253 }
254 
setStyleBuilder(const StyleBuilder * styleBuilder)255 void BookmarkManager::setStyleBuilder(const StyleBuilder *styleBuilder)
256 {
257     d->m_styleBuilder = styleBuilder;
258 }
259 
updateBookmarkFile()260 bool BookmarkManager::updateBookmarkFile()
261 {
262     const QString absoluteLocalFilePath = MarbleDirs::localPath() + QLatin1Char('/') + d->m_bookmarkFileRelativePath;
263 
264     if ( ! d->m_bookmarkFileRelativePath.isNull() ) {
265         QFile file( absoluteLocalFilePath );
266         if ( !file.exists() ) {
267             // Extracting directory of file : for bookmarks it will be MarbleDirs::localPath()+/bookmarks/
268             QFileInfo fileInfo( absoluteLocalFilePath );
269             QString directoryPath = fileInfo.path();
270 
271             //Creating all directories, which doesn't exist
272             QDir directory(  MarbleDirs::localPath() );
273             directory.mkpath( directoryPath );
274         }
275 
276         if (!GeoDataDocumentWriter::write(absoluteLocalFilePath, *d->m_bookmarkDocument)) {
277             mDebug() << "Could not write the bookmarks file" << absoluteLocalFilePath;
278             file.close();
279             return false;
280         }
281         emit bookmarksChanged();
282         file.close();
283         return true;
284     }
285     return false;
286 }
287 
openFile(const QString & fileName)288 GeoDataDocument* BookmarkManager::openFile( const QString &fileName )
289 {
290     GeoDataParser parser( GeoData_KML );
291     QFile file( fileName );
292 
293     if ( !file.exists() ) {
294         return nullptr;
295     }
296 
297     if ( !file.open( QIODevice::ReadOnly ) || !parser.read( &file ) ) {
298         mDebug() << "Could not open/parse file" << fileName;
299         return nullptr;
300     }
301 
302     GeoDataDocument *result = dynamic_cast<GeoDataDocument*>( parser.releaseDocument() );
303     if ( !result ) {
304         return nullptr;
305     }
306 
307     result->setDocumentRole( BookmarkDocument );
308     for( GeoDataFolder* folder: result->folderList() ) {
309         BookmarkManagerPrivate::setVisualCategory( folder );
310     }
311 
312     return result;
313 }
314 
315 }
316 
317 #include "moc_BookmarkManager.cpp"
318