Marble

BookmarkManagerDialog.cpp
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
38namespace 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 */
54class BookmarkManagerDialogPrivate {
55 Q_DECLARE_TR_FUNCTIONS(BookmarkManagerDialogPrivate)
56
57public:
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
105BookmarkManagerDialogPrivate::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
117void 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
138void 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
149void 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
159void 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
174void 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
193void 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
253void 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
275void BookmarkManagerDialogPrivate::discardChanges()
276{
277 m_manager->loadFile( "bookmarks/bookmarks.kml" );
278}
279
280/// selects the folder name from its parent (of folder filter fame)
281void 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
310QModelIndex 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
320GeoDataContainer *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
333void 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
363void 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
379BookmarkManagerDialog::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
402BookmarkManagerDialog::~BookmarkManagerDialog()
403{
404 delete d;
405}
406
407void BookmarkManagerDialog::saveBookmarks()
408{
409 d->m_manager->updateBookmarkFile();
410}
411
412void 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
426void 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
449void 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
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 );
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
523GeoDataDocument* BookmarkManagerDialogPrivate::bookmarkDocument()
524{
525 return m_manager->document();
526}
527
528void 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"
This file contains the headers for MarbleModel.
ButtonCode messageBox(QWidget *parent, DialogType type, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const KGuiItem &cancelAction=KStandardGuiItem::cancel(), const QString &dontShowAskAgainName=QString(), Options options=Notify)
QString name(StandardShortcut id)
Binds a QML item to a specific geodetic location in screen coordinates.
QString homePath()
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
StandardButton question(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons, StandardButton defaultButton)
StandardButton warning(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons, StandardButton defaultButton)
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
int row() const const
QVariant data(int role) const const
bool isValid() const const
QModelIndex parent() const const
void setFilterKeyColumn(int column)
virtual bool hasChildren(const QModelIndex &parent) const const override
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const const override
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const override
virtual int rowCount(const QModelIndex &parent) const const override
virtual void setSourceModel(QAbstractItemModel *sourceModel) override
QString arg(Args &&... args) const const
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:16 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.