1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
4 // SPDX-FileCopyrightText: 2012 Thibaut Gridel <tgridel@free.fr>
5 //
6 
7 #include "BookmarkManagerDialog.h"
8 #include "BookmarkManager.h"
9 #include "BookmarkManager_p.h"
10 #include "BranchFilterProxyModel.h"
11 #include "EditBookmarkDialog.h"
12 #include "FileManager.h"
13 #include "GeoDataCoordinates.h"
14 #include "GeoDataDocument.h"
15 #include "GeoDataLookAt.h"
16 #include "GeoDataExtendedData.h"
17 #include "GeoDataFolder.h"
18 #include "GeoDataPlacemark.h"
19 #include "GeoDataPoint.h"
20 #include "GeoDataStyle.h"
21 #include "GeoDataIconStyle.h"
22 #include "GeoDataTreeModel.h"
23 #include "GeoDataTypes.h"
24 #include "GeoDataDocumentWriter.h"
25 #include "MarbleDirs.h"
26 #include "MarbleDebug.h"
27 #include "MarbleModel.h"
28 #include "NewBookmarkFolderDialog.h"
29 #include "MarblePlacemarkModel.h"
30 #include <KmlElementDictionary.h>
31 
32 #include <QPointer>
33 #include <QFile>
34 #include <QSortFilterProxyModel>
35 #include <QFileDialog>
36 #include <QMessageBox>
37 
38 namespace Marble {
39 
40 /*
41  * The two list views use the model data like this:
42  *
43  *                                     (folder filter)
44  *
45  *                                     QSortFilterProxyModel => Folders View
46  *                                   /         |
47  * bookmarks.kml => GeoDataTreeModel           |  current folder sets filter
48  *                                   \        \ /
49  *                                     BranchFilterModel => Bookmarks View
50  *
51  *                                     (placemark filter)   (placemark list)
52  *
53  */
54 class BookmarkManagerDialogPrivate {
55     Q_DECLARE_TR_FUNCTIONS(BookmarkManagerDialogPrivate)
56 
57 public:
58     BookmarkManagerDialog *m_parent;
59 
60     BookmarkManager *const m_manager;
61 
62     GeoDataTreeModel *const m_treeModel;
63 
64     QSortFilterProxyModel m_folderFilterModel;
65 
66     QPersistentModelIndex m_selectedFolder;
67 
68     BranchFilterProxyModel m_branchFilterModel;
69 
70     BookmarkManagerDialogPrivate( BookmarkManagerDialog* parent, MarbleModel *model );
71 
72     void initializeFoldersView( GeoDataTreeModel* treeModel );
73 
74     void initializeBookmarksView( GeoDataTreeModel* treeModel );
75 
76     void handleFolderSelection( const QModelIndex &index );
77 
78     void updateButtonState();
79 
80     void addNewFolder();
81 
82     void renameFolder();
83 
84     void deleteFolder();
85 
86     void editBookmark();
87 
88     void deleteBookmark();
89 
90     void discardChanges();
91 
92     QModelIndex bookmarkTreeIndex( const QModelIndex &bookmark ) const;
93 
94     QModelIndex folderTreeIndex( const QModelIndex &index ) const;
95     GeoDataContainer* selectedFolder();
96 
97     void selectFolder( const QString &name = QString(), const QModelIndex &index = QModelIndex() );
98 
99     void importBookmarksRecursively( GeoDataContainer *source, GeoDataContainer *destination,
100                                      bool &replaceAll, bool &skipAll );
101 
102     GeoDataDocument* bookmarkDocument();
103 };
104 
BookmarkManagerDialogPrivate(BookmarkManagerDialog * parent,MarbleModel * model)105 BookmarkManagerDialogPrivate::BookmarkManagerDialogPrivate( BookmarkManagerDialog* parent, MarbleModel *model ) :
106     m_parent( parent ),
107     m_manager( model->bookmarkManager() ),
108     m_treeModel( model->treeModel() ),
109     m_folderFilterModel(),
110     m_branchFilterModel()
111 {
112     // nothing to do
113 }
114 
115 /// react to clicking on the folder index (of folderfiltermodel fame)
116 /// consequence is selecting this folder, or unselecting it and going to root folder
handleFolderSelection(const QModelIndex & index)117 void BookmarkManagerDialogPrivate::handleFolderSelection( const QModelIndex &index )
118 {
119     if( !index.isValid() ) {
120         return;
121     }
122     Q_ASSERT( index.isValid() );
123     Q_ASSERT( index.model() == &m_folderFilterModel );
124     if( m_selectedFolder.isValid() &&
125         m_parent->foldersTreeView->selectionModel()->selectedIndexes().contains( m_selectedFolder ) ) {
126         m_selectedFolder = QModelIndex();
127         m_parent->foldersTreeView->selectionModel()->clear();
128         selectFolder();
129     } else {
130         m_selectedFolder = index;
131         m_branchFilterModel.setBranchIndex( m_treeModel, folderTreeIndex( index ) );
132         m_parent->bookmarksListView->setRootIndex(
133                     m_branchFilterModel.mapFromSource( folderTreeIndex( index ) ) );
134         m_parent->bookmarksListView->selectionModel()->clear();
135     }
136 }
137 
updateButtonState()138 void BookmarkManagerDialogPrivate::updateButtonState()
139 {
140     bool const hasFolderSelection = !m_parent->foldersTreeView->selectionModel()->selectedIndexes().isEmpty();
141     m_parent->renameFolderButton->setEnabled( hasFolderSelection );
142     m_parent->removeFolderButton->setEnabled( hasFolderSelection );
143 
144     bool const hasBookmarkSelection = !m_parent->bookmarksListView->selectionModel()->selectedIndexes().isEmpty();
145     m_parent->editBookmarkButton->setEnabled( hasBookmarkSelection );
146     m_parent->removeBookmarkButton->setEnabled( hasBookmarkSelection );
147 }
148 
addNewFolder()149 void BookmarkManagerDialogPrivate::addNewFolder()
150 {
151     QPointer<NewBookmarkFolderDialog> dialog = new NewBookmarkFolderDialog( m_parent );
152     if ( dialog->exec() == QDialog::Accepted && !dialog->folderName().isEmpty() ) {
153         m_manager->addNewBookmarkFolder( selectedFolder(), dialog->folderName() );
154         selectFolder( dialog->folderName(), m_selectedFolder );
155     }
156     delete dialog;
157 }
158 
renameFolder()159 void BookmarkManagerDialogPrivate::renameFolder()
160 {
161     GeoDataFolder *folder = geodata_cast<GeoDataFolder>(selectedFolder());
162     if ( folder ) {
163         QPointer<NewBookmarkFolderDialog> dialog = new NewBookmarkFolderDialog( m_parent );
164         dialog->setFolderName( folder->name() );
165         QPersistentModelIndex parentIndex = m_selectedFolder.parent();
166         if ( dialog->exec() == QDialog::Accepted ) {
167             m_manager->renameBookmarkFolder( folder, dialog->folderName() );
168         }
169         selectFolder( dialog->folderName(), parentIndex );
170         delete dialog;
171     }
172 }
173 
deleteFolder()174 void BookmarkManagerDialogPrivate::deleteFolder()
175 {
176     GeoDataFolder *folder = geodata_cast<GeoDataFolder>(selectedFolder());
177     if ( folder ) {
178         if ( folder->size() > 0 ) {
179             QString const text = tr( "The folder %1 is not empty. Removing it will delete all bookmarks it contains. Are you sure you want to delete the folder?" ).arg( folder->name() );
180             if (QMessageBox::question(m_parent, tr("Remove Folder"), text, QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes) {
181                 return;
182             }
183         }
184 
185         // take note of the parentIndex before removing the folder
186         QString parent = static_cast<GeoDataContainer*>(folder->parent())->name();
187         QPersistentModelIndex greatParentIndex = m_selectedFolder.parent().parent();
188         m_manager->removeBookmarkFolder( folder );
189         selectFolder( parent, greatParentIndex);
190     }
191 }
192 
editBookmark()193 void BookmarkManagerDialogPrivate::editBookmark()
194 {
195     QModelIndexList selection = m_parent->bookmarksListView->selectionModel()->selectedIndexes();
196     if ( selection.size() == 1 ) {
197         QModelIndex index = m_branchFilterModel.mapToSource( selection.first() );
198         Q_ASSERT( index.isValid() );
199         GeoDataObject* object = qvariant_cast<GeoDataObject*>( index.data( MarblePlacemarkModel::ObjectPointerRole ) );
200         Q_ASSERT( object );
201         GeoDataPlacemark *bookmark = geodata_cast<GeoDataPlacemark>(object);
202         // do not try to edit folders
203         if ( !bookmark ) {
204             return;
205         }
206         Q_ASSERT( bookmark );
207         QModelIndex treeIndex = index;
208         Q_ASSERT( treeIndex.isValid() );
209         QModelIndex folderIndex = treeIndex.parent();
210         Q_ASSERT( folderIndex.isValid() );
211         GeoDataObject* folderObject = qvariant_cast<GeoDataObject*>( folderIndex.data( MarblePlacemarkModel::ObjectPointerRole ) );
212         Q_ASSERT( folderObject );
213         GeoDataFolder* folder = geodata_cast<GeoDataFolder>(folderObject);
214         Q_ASSERT( folder );
215 
216         QPointer<EditBookmarkDialog> dialog = new EditBookmarkDialog( m_manager, m_parent );
217         dialog->setName( bookmark->name() );
218         if ( bookmark->lookAt() ) {
219             dialog->setRange( bookmark->lookAt()->range() );
220         }
221         dialog->setCoordinates( bookmark->coordinate() );
222         dialog->setDescription( bookmark->description() );
223         dialog->setFolderName( folder->name() );
224         dialog->setIconLink( bookmark->style()->iconStyle().iconPath() );
225         if ( dialog->exec() == QDialog::Accepted ) {
226             bookmark->setName( dialog->name() );
227             bookmark->setDescription( dialog->description() );
228             bookmark->setCoordinate( dialog->coordinates() );
229             GeoDataStyle::Ptr newStyle(new GeoDataStyle( *bookmark->style() ));
230             newStyle->iconStyle().setIconPath( dialog->iconLink() );
231             bookmark->setStyle( newStyle );
232             if ( bookmark->lookAt() ) {
233                 bookmark->lookAt()->setCoordinates( dialog->coordinates() );
234                 bookmark->lookAt()->setRange( dialog->range() );
235             } else if ( dialog->range() ) {
236                 GeoDataLookAt *lookat = new GeoDataLookAt;
237                 lookat->setCoordinates( dialog->coordinates() );
238                 lookat->setRange( dialog->range() );
239                 bookmark->setAbstractView( lookat );
240             }
241             m_manager->updateBookmark( bookmark );
242 
243             if (folder->name() != dialog->folder()->name() ) {
244                 GeoDataPlacemark newBookmark( *bookmark );
245                 m_manager->removeBookmark( bookmark );
246                 m_manager->addBookmark( dialog->folder(), newBookmark );
247             }
248         }
249         delete dialog;
250     }
251 }
252 
deleteBookmark()253 void BookmarkManagerDialogPrivate::deleteBookmark()
254 {
255     const QModelIndexList selection = m_parent->bookmarksListView->selectionModel()->selectedIndexes();
256 
257     if (selection.size() != 1) {
258         return;
259     }
260 
261     const QModelIndex bookmarkIndex = m_branchFilterModel.mapToSource(selection.first());
262     GeoDataFolder *folder = geodata_cast<GeoDataFolder>(selectedFolder());
263     if (!folder) {
264         return;
265     }
266 
267     GeoDataPlacemark *bookmark = geodata_cast<GeoDataPlacemark>(folder->child(bookmarkIndex.row()));
268     if (!bookmark) {
269         return;
270     }
271 
272     m_manager->removeBookmark(bookmark);
273 }
274 
discardChanges()275 void BookmarkManagerDialogPrivate::discardChanges()
276 {
277     m_manager->loadFile( "bookmarks/bookmarks.kml" );
278 }
279 
280 /// selects the folder name from its parent (of folder filter fame)
selectFolder(const QString & name,const QModelIndex & parent)281 void BookmarkManagerDialogPrivate::selectFolder( const QString &name, const QModelIndex &parent )
282 {
283     if ( parent.isValid() ) {
284         Q_ASSERT( parent.model() == &m_folderFilterModel );
285     }
286 
287     if ( name.isEmpty() ) {
288         QModelIndex documentTreeIndex = m_treeModel->index( bookmarkDocument() );
289         QModelIndex folderFilterIndex = m_folderFilterModel.mapFromSource( documentTreeIndex );
290         Q_ASSERT( folderFilterIndex.isValid() );
291         m_parent->foldersTreeView->setCurrentIndex( folderFilterIndex );
292         handleFolderSelection( folderFilterIndex );
293         return;
294     }
295 
296     for ( int i=0; i < m_folderFilterModel.rowCount( parent ); ++i ) {
297         QModelIndex childIndex = m_folderFilterModel.index( i, 0, parent );
298         if ( childIndex.data().toString() == name
299             && m_selectedFolder != childIndex ) {
300             m_parent->foldersTreeView->setCurrentIndex( childIndex );
301             handleFolderSelection( childIndex );
302             return;
303         }
304         if ( m_folderFilterModel.hasChildren( childIndex ) ) {
305             selectFolder( name, childIndex );
306         }
307     }
308 }
309 
folderTreeIndex(const QModelIndex & index) const310 QModelIndex BookmarkManagerDialogPrivate::folderTreeIndex( const QModelIndex &index ) const
311 {
312     Q_ASSERT( index.isValid() );
313     Q_ASSERT( index.model() == &m_folderFilterModel );
314     QModelIndex const treeModelIndex = m_folderFilterModel.mapToSource( index );
315     Q_ASSERT( treeModelIndex.isValid() );
316     Q_ASSERT( treeModelIndex.model() == m_treeModel );
317     return treeModelIndex;
318 }
319 
selectedFolder()320 GeoDataContainer *BookmarkManagerDialogPrivate::selectedFolder()
321 {
322     if( m_selectedFolder.isValid() ) {
323         GeoDataObject* object = qvariant_cast<GeoDataObject*>( m_selectedFolder.data( MarblePlacemarkModel::ObjectPointerRole ) );
324         Q_ASSERT( object );
325         GeoDataContainer* container = dynamic_cast<GeoDataContainer*>( object );
326         Q_ASSERT( container );
327         return container;
328     } else {
329         return bookmarkDocument();
330     }
331 }
332 
initializeFoldersView(GeoDataTreeModel * treeModel)333 void BookmarkManagerDialogPrivate::initializeFoldersView( GeoDataTreeModel* treeModel )
334 {
335     m_folderFilterModel.setFilterKeyColumn( 1 );
336     const QString regexp = QLatin1String(GeoDataTypes::GeoDataFolderType) + QLatin1Char('|') + QLatin1String(GeoDataTypes::GeoDataDocumentType);
337     m_folderFilterModel.setFilterRegExp( regexp );
338     m_folderFilterModel.setSourceModel( treeModel );
339 
340     m_parent->foldersTreeView->setModel( &m_folderFilterModel );
341     m_parent->foldersTreeView->setEditTriggers( QAbstractItemView::NoEditTriggers );
342     m_parent->foldersTreeView->setHeaderHidden( true );
343     for ( int i=1; i<m_treeModel->columnCount(); ++i ) {
344         m_parent->foldersTreeView->hideColumn( i );
345     }
346     m_parent->foldersTreeView->setRootIndex( m_folderFilterModel.mapFromSource(
347                                                  m_treeModel->index( bookmarkDocument() )));
348 
349     m_parent->connect( m_parent->foldersTreeView,
350             SIGNAL(clicked(QModelIndex)),
351             m_parent, SLOT(handleFolderSelection(QModelIndex)) );
352     m_parent->connect( m_parent->foldersTreeView->selectionModel(),
353                       SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
354                       m_parent, SLOT(updateButtonState()) );
355     m_parent->connect( m_parent->renameFolderButton, SIGNAL(clicked(bool)),
356                        m_parent, SLOT(renameFolder()) );
357     m_parent->connect( m_parent->newFolderButton, SIGNAL(clicked(bool)),
358                        m_parent, SLOT(addNewFolder()) );
359     m_parent->connect( m_parent->removeFolderButton, SIGNAL(clicked(bool)),
360                        m_parent, SLOT(deleteFolder()) );
361 }
362 
initializeBookmarksView(GeoDataTreeModel * treeModel)363 void BookmarkManagerDialogPrivate::initializeBookmarksView( GeoDataTreeModel* treeModel )
364 {
365     m_branchFilterModel.setSourceModel( treeModel );
366 
367     m_parent->bookmarksListView->setModel( &m_branchFilterModel );
368     m_parent->bookmarksListView->setEditTriggers( QAbstractItemView::NoEditTriggers );
369 
370     m_parent->connect( m_parent->bookmarksListView->selectionModel(),
371                       SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
372                       m_parent, SLOT(updateButtonState()) );
373     m_parent->connect( m_parent->editBookmarkButton, SIGNAL(clicked(bool)),
374                        m_parent, SLOT(editBookmark()) );
375     m_parent->connect( m_parent->removeBookmarkButton, SIGNAL(clicked(bool)),
376                        m_parent, SLOT(deleteBookmark()) );
377 }
378 
BookmarkManagerDialog(MarbleModel * model,QWidget * parent)379 BookmarkManagerDialog::BookmarkManagerDialog( MarbleModel* model, QWidget *parent )
380     : QDialog( parent ),
381       d( new BookmarkManagerDialogPrivate( this, model ) )
382 {
383     setupUi( this );
384     bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
385     importButton->setVisible( !smallScreen );
386     exportButton->setVisible( !smallScreen );
387     foldersLabel->setVisible( !smallScreen );
388     bookmarkLabel->setVisible( !smallScreen );
389 
390     d->initializeFoldersView( d->m_treeModel );
391     d->initializeBookmarksView( d->m_treeModel );
392     d->updateButtonState();
393 
394     connect( this, SIGNAL(accepted()), SLOT(saveBookmarks()) );
395     connect( this, SIGNAL(rejected()), SLOT(discardChanges()) );
396     connect( exportButton, SIGNAL(clicked()), this, SLOT(exportBookmarks()) );
397     connect( importButton, SIGNAL(clicked()), this, SLOT(importBookmarks()) );
398 
399     d->selectFolder();
400 }
401 
~BookmarkManagerDialog()402 BookmarkManagerDialog::~BookmarkManagerDialog()
403 {
404     delete d;
405 }
406 
saveBookmarks()407 void BookmarkManagerDialog::saveBookmarks()
408 {
409     d->m_manager->updateBookmarkFile();
410 }
411 
exportBookmarks()412 void BookmarkManagerDialog::exportBookmarks()
413 {
414     QString fileName = QFileDialog::getSaveFileName( this, tr( "Export Bookmarks" ), // krazy:exclude=qclasses
415                        QDir::homePath(), tr( "KML files (*.kml)" ) );
416 
417     if ( !fileName.isEmpty() ) {
418         if (!GeoDataDocumentWriter::write(fileName, *d->bookmarkDocument())) {
419             mDebug() << "Could not write the bookmarks file" << fileName;
420             QString const text = tr( "Unable to save bookmarks. Please check that the file is writable." );
421             QMessageBox::warning(this, tr("Bookmark Export"), text);
422         }
423     }
424 }
425 
importBookmarks()426 void BookmarkManagerDialog::importBookmarks()
427 {
428     QString const file = QFileDialog::getOpenFileName(this, tr("Import Bookmarks"),
429                             QDir::homePath(), tr( "KML Files (*.kml)" ) );
430     if ( file.isEmpty() ) {
431         return;
432     }
433 
434     GeoDataDocument *import = BookmarkManager::openFile( file );
435     if ( !import ) {
436         QString const text = tr( "The file %1 cannot be opened as a KML file." ).arg( file );
437         QMessageBox::warning(this, tr( "Bookmark Import"), text);
438         return;
439     }
440     GeoDataDocument *current = d->bookmarkDocument();
441 
442     bool skipAll = false;
443     bool replaceAll = false;
444     d->importBookmarksRecursively(import, current, skipAll, replaceAll);
445 
446     d->selectFolder();
447 }
448 
importBookmarksRecursively(GeoDataContainer * source,GeoDataContainer * destination,bool & replaceAll,bool & skipAll)449 void BookmarkManagerDialogPrivate::importBookmarksRecursively( GeoDataContainer *source, GeoDataContainer *destination, bool &replaceAll, bool &skipAll )
450 {
451     for( GeoDataFolder *newFolder: source->folderList() ) {
452         GeoDataFolder *existingFolder = m_manager->addNewBookmarkFolder(destination, newFolder->name());
453         importBookmarksRecursively(newFolder, existingFolder, skipAll, replaceAll);
454         for( GeoDataPlacemark* newPlacemark: newFolder->placemarkList() ) {
455             bool added = skipAll;
456 
457             GeoDataCoordinates newCoordinate = newPlacemark->coordinate();
458             GeoDataPlacemark *existingPlacemark = m_manager->bookmarkAt( m_manager->document(), newCoordinate );
459             if ( existingPlacemark ) {
460                 if ( skipAll ) {
461                     continue;
462                 }
463 
464                 // Avoid message boxes for equal bookmarks, just skip them
465                 if ( existingPlacemark->name() == newPlacemark->name() &&
466                     existingPlacemark->description() == newPlacemark->description() ) {
467                     continue;
468                 }
469 
470                 QPointer<QMessageBox> messageBox = new QMessageBox( m_parent );
471                 QString const intro = tr( "The file contains a bookmark that already exists among your Bookmarks." );
472                 QString const newBookmark = tr( "Imported bookmark" );
473                 QString const existingBookmark = tr( "Existing bookmark" );
474                 QString const question = tr( "Do you want to replace the existing bookmark with the imported one?" );
475                 QString html = QLatin1String("<p>%1</p><table><tr><td>%2</td><td><b>%3 / %4</b></td></tr>"
476                                                 "<tr><td>%5</td><td><b>%6 / %7</b></td></tr></table><p>%8</p>");
477                 html = html.arg( intro, existingBookmark, existingFolder->name(),
478                                  existingPlacemark->name(), newBookmark, newFolder->name(),
479                                  newPlacemark->name(), question );
480                 messageBox->setText( html );
481 
482                 QAbstractButton *replaceButton    = messageBox->addButton(tr( "Replace" ),     QMessageBox::ActionRole );
483                 QAbstractButton *replaceAllButton = messageBox->addButton(tr( "Replace All" ), QMessageBox::ActionRole );
484                 QAbstractButton *skipButton       = messageBox->addButton(tr( "Skip" ),        QMessageBox::ActionRole );
485                 QAbstractButton *skipAllButton    = messageBox->addButton(tr( "Skip All" ),    QMessageBox::ActionRole );
486                                                     messageBox->addButton( QMessageBox::Cancel );
487                 messageBox->setIcon( QMessageBox::Question );
488 
489                 if ( !replaceAll ) {
490                     messageBox->exec();
491                 }
492                 if ( messageBox->clickedButton() == replaceAllButton ) {
493                     replaceAll = true;
494                 } else if ( messageBox->clickedButton() == skipAllButton ) {
495                     skipAll = true;
496                     added = true;
497                 } else if ( messageBox->clickedButton() == skipButton ) {
498                     delete messageBox;
499                     continue;
500                 } else if ( messageBox->clickedButton() != replaceButton ) {
501                     delete messageBox;
502                     return;
503                 }
504 
505                 if ( messageBox->clickedButton() == replaceButton || replaceAll ) {
506                     m_manager->removeBookmark( existingPlacemark );
507                     m_manager->addBookmark( existingFolder, *newPlacemark );
508 
509                     mDebug() << "Placemark " << newPlacemark->name() << " replaces " << existingPlacemark->name();
510                     delete messageBox;
511                     break;
512                 }
513                 delete messageBox;
514             }
515 
516             if ( !added ) {
517                 m_manager->addBookmark( existingFolder, *newPlacemark );
518             }
519         }
520     }
521 }
522 
bookmarkDocument()523 GeoDataDocument* BookmarkManagerDialogPrivate::bookmarkDocument()
524 {
525     return m_manager->document();
526 }
527 
setButtonBoxVisible(bool visible)528 void BookmarkManagerDialog::setButtonBoxVisible( bool visible )
529 {
530     buttonBox->setVisible( visible );
531     if ( !visible ) {
532         disconnect( this, SIGNAL(rejected()), this, SLOT(discardChanges()) );
533         connect( this, SIGNAL(rejected()), SLOT(saveBookmarks()) );
534     }
535 }
536 
537 }
538 
539 #include "moc_BookmarkManagerDialog.cpp"
540