File indexing completed on 2025-01-05 04:00:10

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2008-11-15
0007  * Description : collections setup tab model/view
0008  *
0009  * SPDX-FileCopyrightText: 2008-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2005-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText:      2012 by Andi Clemens <andi dot clemens at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #define INTERNALID 65535
0018 
0019 #include "setupcollectionview.h"
0020 
0021 // Qt includes
0022 
0023 #include <QGroupBox>
0024 #include <QLabel>
0025 #include <QDir>
0026 #include <QGridLayout>
0027 #include <QHeaderView>
0028 #include <QHBoxLayout>
0029 #include <QMessageBox>
0030 #include <QStandardPaths>
0031 #include <QComboBox>
0032 #include <QUrlQuery>
0033 #include <QUrl>
0034 #include <QIcon>
0035 #include <QDialog>
0036 #include <QDialogButtonBox>
0037 #include <QVBoxLayout>
0038 #include <QApplication>
0039 
0040 // KDE includes
0041 
0042 #include <klocalizedstring.h>
0043 
0044 // Local includes
0045 
0046 #include "dmessagebox.h"
0047 #include "dfiledialog.h"
0048 #include "applicationsettings.h"
0049 #include "collectionmanager.h"
0050 #include "newitemsfinder.h"
0051 #include "dtextedit.h"
0052 #include "digikam_globals.h"
0053 
0054 namespace Digikam
0055 {
0056 
0057 SetupCollectionDelegate::SetupCollectionDelegate(QAbstractItemView* const view, QObject* const parent)
0058     : DWItemDelegate(view, parent),
0059       m_categoryMaxStyledWidth(0)
0060 {
0061     // We keep a standard delegate that does all the normal drawing work for us
0062     // DWItemDelegate handles the widgets, for the rest of the work we act as a proxy to m_styledDelegate
0063 
0064     m_styledDelegate = new QStyledItemDelegate(parent);
0065 
0066     // forward all signals
0067 
0068     connect(m_styledDelegate, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)),
0069             this, SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)));
0070 
0071     connect(m_styledDelegate, SIGNAL(commitData(QWidget*)),
0072             this, SIGNAL(commitData(QWidget*)));
0073 
0074     connect(m_styledDelegate, SIGNAL(sizeHintChanged(QModelIndex)),
0075             this, SIGNAL(sizeHintChanged(QModelIndex)));
0076 
0077     // For size hint. To get a valid size hint, the widgets seem to need a parent widget
0078 
0079     m_samplePushButton   = new QPushButton(view);
0080     m_samplePushButton->hide();
0081     m_sampleAppendButton = new QToolButton(view);
0082     m_sampleAppendButton->hide();
0083     m_sampleUpdateButton = new QToolButton(view);
0084     m_sampleUpdateButton->hide();
0085     m_sampleDeleteButton = new QToolButton(view);
0086     m_sampleDeleteButton->hide();
0087 }
0088 
0089 SetupCollectionDelegate::~SetupCollectionDelegate()
0090 {
0091 }
0092 
0093 QList<QWidget*> SetupCollectionDelegate::createItemWidgets(const QModelIndex& /*index*/) const
0094 {
0095     // We only need a push button for certain indexes and two tool button for others,
0096     // but we have no index here, but need to provide the widgets for each index
0097 
0098     QList<QWidget*> list;
0099     QPushButton* const pushButton   = new QPushButton();
0100     list << pushButton;
0101 
0102     connect(pushButton, &QPushButton::clicked,
0103             this, [this, pushButton]() { Q_EMIT categoryButtonPressed(pushButton->property("id").toInt()); });
0104 
0105     QToolButton* const appendButton = new QToolButton();
0106     appendButton->setToolTip(i18nc("@info:tooltip", "Append a path to the collection."));
0107     appendButton->setAutoRaise(true);
0108     list << appendButton;
0109 
0110     connect(appendButton, &QToolButton::clicked,
0111             this, [this, appendButton]() { Q_EMIT appendPressed(appendButton->property("id").toInt()); });
0112 
0113     QToolButton* const updateButton = new QToolButton();
0114     updateButton->setToolTip(i18nc("@info:tooltip", "Updates the path of the collection."));
0115     updateButton->setAutoRaise(true);
0116     list << updateButton;
0117 
0118     connect(updateButton, &QToolButton::clicked,
0119             this, [this, updateButton]() { Q_EMIT updatePressed(updateButton->property("id").toInt()); });
0120 
0121     QToolButton* const deleteButton = new QToolButton();
0122     deleteButton->setToolTip(i18nc("@info:tooltip", "Removes the collection from digiKam."));
0123     deleteButton->setAutoRaise(true);
0124     list << deleteButton;
0125 
0126     connect(deleteButton, &QToolButton::clicked,
0127             this, [this, deleteButton]() { Q_EMIT deletePressed(deleteButton->property("id").toInt()); });
0128 
0129     return list;
0130 }
0131 
0132 QSize SetupCollectionDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
0133 {
0134     // get default size hint
0135 
0136     QSize hint = m_styledDelegate->sizeHint(option, index);
0137 
0138     // We need to handle those two cases where we display widgets
0139 
0140     if      (index.data(SetupCollectionModel::IsCategoryRole).toBool())
0141     {
0142         // get the largest size hint for the icon/text of all category entries
0143 
0144         int maxStyledWidth = 0;
0145 
0146         Q_FOREACH (const QModelIndex& catIndex, static_cast<const SetupCollectionModel*>(index.model())->categoryIndexes())
0147         {
0148             maxStyledWidth = qMax(maxStyledWidth, m_styledDelegate->sizeHint(option, catIndex).width());
0149         }
0150 
0151         const_cast<SetupCollectionDelegate*>(this)->m_categoryMaxStyledWidth = maxStyledWidth;
0152 
0153         // set real text on sample button to compute correct size hint
0154 
0155         m_samplePushButton->setText(index.data(SetupCollectionModel::CategoryButtonDisplayRole).toString());
0156         QSize widgetHint = m_samplePushButton->sizeHint();
0157 
0158         // add largest of the icon/text sizes (so that all buttons are aligned) and our button size hint
0159 
0160         hint.setWidth(m_categoryMaxStyledWidth + widgetHint.width());
0161         hint.setHeight(qMax(hint.height(), widgetHint.height()));
0162     }
0163     else if (index.data(SetupCollectionModel::IsAppendRole).toBool())
0164     {
0165         // set real pixmap on sample button to compute correct size hint
0166 
0167         QIcon pix      = index.data(SetupCollectionModel::AppendDecorationRole).value<QIcon>();
0168         m_sampleAppendButton->setIcon(index.data(SetupCollectionModel::AppendDecorationRole).value<QIcon>());
0169         QSize widgetHint = m_sampleAppendButton->sizeHint();
0170 
0171         // combine hints
0172 
0173         hint.setWidth(hint.width() + widgetHint.width());
0174         hint.setHeight(qMax(hint.height(), widgetHint.height()));
0175     }
0176     else if (index.data(SetupCollectionModel::IsUpdateRole).toBool())
0177     {
0178         // set real pixmap on sample button to compute correct size hint
0179 
0180         QIcon pix      = index.data(SetupCollectionModel::UpdateDecorationRole).value<QIcon>();
0181         m_sampleUpdateButton->setIcon(index.data(SetupCollectionModel::UpdateDecorationRole).value<QIcon>());
0182         QSize widgetHint = m_sampleUpdateButton->sizeHint();
0183 
0184         // combine hints
0185 
0186         hint.setWidth(hint.width() + widgetHint.width());
0187         hint.setHeight(qMax(hint.height(), widgetHint.height()));
0188     }
0189     else if (index.data(SetupCollectionModel::IsDeleteRole).toBool())
0190     {
0191         // set real pixmap on sample button to compute correct size hint
0192 
0193         QIcon pix      = index.data(SetupCollectionModel::DeleteDecorationRole).value<QIcon>();
0194         m_sampleDeleteButton->setIcon(index.data(SetupCollectionModel::DeleteDecorationRole).value<QIcon>());
0195         QSize widgetHint = m_sampleDeleteButton->sizeHint();
0196 
0197         // combine hints
0198 
0199         hint.setWidth(hint.width() + widgetHint.width());
0200         hint.setHeight(qMax(hint.height(), widgetHint.height()));
0201     }
0202 
0203     return hint;
0204 }
0205 
0206 void SetupCollectionDelegate::updateItemWidgets(const QList<QWidget*>& widgets,
0207                                                 const QStyleOptionViewItem& option,
0208                                                 const QPersistentModelIndex& index) const
0209 {
0210     QPushButton* const pushButton   = static_cast<QPushButton*>(widgets.at(0));
0211     QToolButton* const appendButton = static_cast<QToolButton*>(widgets.at(1));
0212     QToolButton* const updateButton = static_cast<QToolButton*>(widgets.at(2));
0213     QToolButton* const deleteButton = static_cast<QToolButton*>(widgets.at(3));
0214 
0215     if      (index.data(SetupCollectionModel::IsCategoryRole).toBool())
0216     {
0217         // set text from model
0218 
0219         pushButton->setText(index.data(SetupCollectionModel::CategoryButtonDisplayRole).toString());
0220 
0221         // resize according to size hint
0222 
0223         pushButton->resize(pushButton->sizeHint());
0224 
0225         // move to position in line. We have cached the icon/text size hint from sizeHint()
0226 
0227         pushButton->move(m_categoryMaxStyledWidth, (option.rect.height() - pushButton->height()) / 2);
0228         pushButton->show();
0229         appendButton->hide();
0230         updateButton->hide();
0231         deleteButton->hide();
0232 
0233         pushButton->setEnabled(itemView()->isEnabled());
0234         pushButton->setProperty("id", index.data(SetupCollectionModel::CategoryButtonMapId));
0235     }
0236     else if (index.data(SetupCollectionModel::IsAppendRole).toBool())
0237     {
0238         appendButton->setIcon(index.data(SetupCollectionModel::AppendDecorationRole).value<QIcon>());
0239         appendButton->resize(appendButton->sizeHint());
0240         appendButton->move(0, (option.rect.height() - appendButton->height()) / 2);
0241         appendButton->show();
0242         pushButton->hide();
0243 
0244         appendButton->setEnabled(itemView()->isEnabled());
0245         appendButton->setProperty("id", index.data(SetupCollectionModel::AppendMapId));
0246     }
0247     else if (index.data(SetupCollectionModel::IsUpdateRole).toBool())
0248     {
0249         updateButton->setIcon(index.data(SetupCollectionModel::UpdateDecorationRole).value<QIcon>());
0250         updateButton->resize(updateButton->sizeHint());
0251         updateButton->move(0, (option.rect.height() - updateButton->height()) / 2);
0252         updateButton->show();
0253         pushButton->hide();
0254 
0255         updateButton->setEnabled(itemView()->isEnabled());
0256         updateButton->setProperty("id", index.data(SetupCollectionModel::UpdateMapId));
0257     }
0258     else if (index.data(SetupCollectionModel::IsDeleteRole).toBool())
0259     {
0260         deleteButton->setIcon(index.data(SetupCollectionModel::DeleteDecorationRole).value<QIcon>());
0261         deleteButton->resize(deleteButton->sizeHint());
0262         deleteButton->move(0, (option.rect.height() - deleteButton->height()) / 2);
0263         deleteButton->show();
0264         pushButton->hide();
0265 
0266         deleteButton->setEnabled(itemView()->isEnabled());
0267         deleteButton->setProperty("id", index.data(SetupCollectionModel::DeleteMapId));
0268     }
0269     else
0270     {
0271         pushButton->hide();
0272         appendButton->hide();
0273         updateButton->hide();
0274         deleteButton->hide();
0275     }
0276 }
0277 
0278 QWidget* SetupCollectionDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
0279 {
0280     return m_styledDelegate->createEditor(parent, option, index);
0281 }
0282 
0283 bool SetupCollectionDelegate::editorEvent(QEvent* event, QAbstractItemModel* model,
0284                                           const QStyleOptionViewItem& option, const QModelIndex& index)
0285 {
0286     return static_cast<QAbstractItemDelegate*>(m_styledDelegate)->editorEvent(event, model, option, index);
0287 }
0288 
0289 void SetupCollectionDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
0290 {
0291     m_styledDelegate->paint(painter, option, index);
0292 }
0293 
0294 void SetupCollectionDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
0295 {
0296     m_styledDelegate->setEditorData(editor, index);
0297 }
0298 
0299 void SetupCollectionDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
0300 {
0301     m_styledDelegate->setModelData(editor, model, index);
0302 }
0303 
0304 void SetupCollectionDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
0305 {
0306     m_styledDelegate->updateEditorGeometry(editor, option, index);
0307 }
0308 
0309 // ------------- View ----------------- //
0310 
0311 SetupCollectionTreeView::SetupCollectionTreeView(QWidget* const parent)
0312     : QTreeView(parent)
0313 {
0314     setHeaderHidden(true);
0315     setRootIsDecorated(false);
0316     setUniformRowHeights(true);
0317     setExpandsOnDoubleClick(false);
0318 
0319     // Set custom delegate
0320 
0321     setItemDelegate(new SetupCollectionDelegate(this, this));
0322 }
0323 
0324 void SetupCollectionTreeView::setModel(SetupCollectionModel* collectionModel)
0325 {
0326     if (model())
0327     {
0328         disconnect(model(), nullptr, this, nullptr);
0329     }
0330 
0331     // we need to do some things after the model has loaded its data
0332 
0333     connect(collectionModel, SIGNAL(collectionsLoaded()),
0334             this, SLOT(modelLoadedCollections()));
0335 
0336     // connect button click signals from the delegate to the model
0337 
0338     connect(static_cast<SetupCollectionDelegate*>(itemDelegate()), SIGNAL(categoryButtonPressed(int)),
0339             collectionModel, SLOT(slotCategoryButtonPressed(int)));
0340 
0341     connect(static_cast<SetupCollectionDelegate*>(itemDelegate()), SIGNAL(appendPressed(int)),
0342             collectionModel, SLOT(slotAppendPressed(int)));
0343 
0344     connect(static_cast<SetupCollectionDelegate*>(itemDelegate()), SIGNAL(updatePressed(int)),
0345             collectionModel, SLOT(slotUpdatePressed(int)));
0346 
0347     connect(static_cast<SetupCollectionDelegate*>(itemDelegate()), SIGNAL(deletePressed(int)),
0348             collectionModel, SLOT(slotDeletePressed(int)));
0349 
0350     // give model a widget to use as parent for message boxes
0351 
0352     collectionModel->setParentWidgetForDialogs(this);
0353 
0354     QTreeView::setModel(collectionModel);
0355 }
0356 
0357 void SetupCollectionTreeView::modelLoadedCollections()
0358 {
0359     // make category entries span the whole line
0360 
0361     for (int i = 0 ; i < model()->rowCount(QModelIndex()) ; ++i)
0362     {
0363         setFirstColumnSpanned(i, QModelIndex(), true);
0364     }
0365 
0366     // show all entries
0367 
0368     expandAll();
0369 
0370     // Resize name and path column
0371 
0372     header()->setSectionResizeMode(SetupCollectionModel::ColumnName, QHeaderView::Stretch);
0373     header()->setSectionResizeMode(SetupCollectionModel::ColumnPath, QHeaderView::Stretch);
0374 
0375     // Resize last column, so that delete button is always rightbound
0376 
0377     header()->setStretchLastSection(false); // defaults to true
0378     header()->setSectionResizeMode(SetupCollectionModel::ColumnAppendButton, QHeaderView::Fixed);
0379     resizeColumnToContents(SetupCollectionModel::ColumnAppendButton);
0380     header()->setSectionResizeMode(SetupCollectionModel::ColumnUpdateButton, QHeaderView::Fixed);
0381     resizeColumnToContents(SetupCollectionModel::ColumnUpdateButton);
0382     header()->setSectionResizeMode(SetupCollectionModel::ColumnDeleteButton, QHeaderView::Fixed);
0383     resizeColumnToContents(SetupCollectionModel::ColumnDeleteButton);
0384 
0385     // Resize first column
0386     // This is more difficult because we need to ignore the width of the category entries,
0387     // which are formally location in the first column (although spanning the whole line).
0388     // resizeColumnToContents fails therefore.
0389 
0390     SetupCollectionModel* const collectionModel = static_cast<SetupCollectionModel*>(model());
0391     QModelIndex categoryIndex = collectionModel->indexForCategory(SetupCollectionModel::CategoryLocal);
0392     QModelIndex firstChildOfFirstCategory       = collectionModel->index(0, SetupCollectionModel::ColumnStatus, categoryIndex);
0393     QSize hint                                  = sizeHintForIndex(firstChildOfFirstCategory);
0394     setColumnWidth(SetupCollectionModel::ColumnStatus, hint.width() + indentation());
0395 }
0396 
0397 // ------------- Model ----------------- //
0398 
0399 SetupCollectionModel::Item::Item()
0400     : parentId(INTERNALID),
0401       orgIndex(0),
0402       appended(false),
0403       updated (false),
0404       deleted (false)
0405 {
0406 }
0407 
0408 SetupCollectionModel::Item::Item(const CollectionLocation& location)
0409     : location(location),
0410       orgIndex(0),
0411       appended(false),
0412       updated (false),
0413       deleted (false)
0414 {
0415     parentId = SetupCollectionModel::typeToCategory(location.type());
0416 }
0417 
0418 SetupCollectionModel::Item::Item(const QString& path, const QString& label, SetupCollectionModel::Category category)
0419     : label   (label),
0420       path    (path),
0421       parentId(category),
0422       orgIndex(0),
0423       appended(false),
0424       updated (false),
0425       deleted (false)
0426 {
0427 }
0428 
0429 /**
0430  * Internal data structure:
0431  *
0432  * The category entries get a model index with INTERNALID and are identified by their row().
0433  * The item entries get the index in m_collections as INTERNALID.
0434  * No item is ever removed from m_collections, deleted entries are only marked as such.
0435  *
0436  * Items have a location, a parentId, and a name and label field.
0437  * parentId always contains the category, needed to implement parent().
0438  * The location is the location if it exists, or is null if the item was added.
0439  * Name and label are null if unchanged, then the values from location are used.
0440  * They are valid if edited (label) or the location was added (both valid, location null).
0441  */
0442 
0443 SetupCollectionModel::SetupCollectionModel(QObject* const parent)
0444     : QAbstractItemModel(parent),
0445       m_dialogParentWidget(nullptr)
0446 {
0447 }
0448 
0449 SetupCollectionModel::~SetupCollectionModel()
0450 {
0451 }
0452 
0453 void SetupCollectionModel::loadCollections()
0454 {
0455     beginResetModel();
0456 
0457     m_collections.clear();
0458     QList<CollectionLocation> locations = CollectionManager::instance()->allLocations();
0459 
0460     Q_FOREACH (const CollectionLocation& location, locations)
0461     {
0462         m_collections << Item(location);
0463         int idx = m_collections.size() - 1;
0464 
0465         if (location.type() == CollectionLocation::Network)
0466         {
0467             QUrl url(location.identifier);
0468 
0469             if (url.scheme() == QLatin1String("networkshareid"))
0470             {
0471                QUrlQuery q(url);
0472 
0473                 Q_FOREACH (const QString& path, q.allQueryItemValues(QLatin1String("mountpath")))
0474                 {
0475                     if (location.albumRootPath() != path)
0476                     {
0477                         Item item(location);
0478                         item.orgIndex = idx;
0479                         item.appended = true;
0480                         item.path     = path;
0481                         m_collections << item;
0482                         m_collections[idx].childs << path;
0483                     }
0484                 }
0485             }
0486         }
0487     }
0488 
0489     endResetModel();
0490     Q_EMIT collectionsLoaded();
0491 }
0492 
0493 void SetupCollectionModel::apply()
0494 {
0495     QList<int> newItems, deletedItems, updatedItems, renamedItems;
0496 
0497     for (int i = 0 ; i < m_collections.count() ; ++i)
0498     {
0499         const Item& item = m_collections.at(i);
0500 
0501         if      (item.appended)
0502         {
0503             continue;
0504         }
0505         else if (item.deleted && !item.location.isNull())
0506         {
0507             // if item was deleted and had a valid location, i.e. exists in DB
0508 
0509             deletedItems << i;
0510         }
0511         else if (!item.deleted && item.location.isNull())
0512         {
0513             // if item has no valid location, i.e. does not yet exist in db
0514 
0515             newItems << i;
0516         }
0517         else if (!item.deleted && !item.location.isNull())
0518         {
0519             // if item has a valid location, is updated or has changed its label
0520 
0521             if (item.updated)
0522             {
0523                 updatedItems << i;
0524             }
0525             else if (!item.label.isNull() && item.label != item.location.label())
0526             {
0527                 renamedItems << i;
0528             }
0529         }
0530     }
0531 
0532     // Delete deleted items
0533 
0534     Q_FOREACH (int i, deletedItems)
0535     {
0536         Item& item    = m_collections[i];
0537         CollectionManager::instance()->removeLocation(item.location);
0538         item.location = CollectionLocation();
0539     }
0540 
0541     // Add added items
0542 
0543     QList<Item> failedItems;
0544 
0545     Q_FOREACH (int i, newItems)
0546     {
0547         Item& item = m_collections[i];
0548         CollectionLocation location;
0549 
0550         if (item.parentId == CategoryRemote)
0551         {
0552             location = CollectionManager::instance()->addNetworkLocation(QUrl::fromLocalFile(item.path), item.label);
0553         }
0554         else
0555         {
0556             location = CollectionManager::instance()->addLocation(QUrl::fromLocalFile(item.path), item.label);
0557         }
0558 
0559         if (location.isNull())
0560         {
0561             failedItems << item;
0562         }
0563         else
0564         {
0565             item.location = location;
0566             item.path.clear();
0567             item.label.clear();
0568         }
0569     }
0570 
0571     // Update collections
0572 
0573     Q_FOREACH (int i, updatedItems)
0574     {
0575         Item& item  = m_collections[i];
0576         CollectionLocation location;
0577 
0578         int newType = CollectionLocation::VolumeHardWired;
0579 
0580         if      (item.parentId == CategoryRemovable)
0581         {
0582             newType = CollectionLocation::VolumeRemovable;
0583         }
0584         else if (item.parentId == CategoryRemote)
0585         {
0586             newType = CollectionLocation::Network;
0587         }
0588 
0589         QStringList pathList;
0590         pathList << item.path << item.childs;
0591         location    = CollectionManager::instance()->refreshLocation(item.location, newType,
0592                                                                      pathList, item.label);
0593 
0594         if (location.isNull())
0595         {
0596             failedItems << item;
0597         }
0598         else
0599         {
0600             item.location = location;
0601             item.path.clear();
0602             item.label.clear();
0603         }
0604     }
0605 
0606     // Rename collections
0607 
0608     Q_FOREACH (int i, renamedItems)
0609     {
0610         Item& item = m_collections[i];
0611         CollectionManager::instance()->setLabel(item.location, item.label);
0612         item.label.clear();
0613     }
0614 
0615     // Handle any errors
0616 
0617     if (!failedItems.isEmpty())
0618     {
0619         QStringList failedPaths;
0620 
0621         Q_FOREACH (const Item& item, failedItems)
0622         {
0623             failedPaths << QDir::toNativeSeparators(item.path);
0624         }
0625 
0626         DMessageBox::showInformationList(QMessageBox::Critical,
0627                                          m_dialogParentWidget,
0628                                          qApp->applicationName(),
0629                                          i18n("It was not possible to add a collection for the following paths:"),
0630                                          failedPaths);
0631     }
0632 
0633     // Trigger collection scan
0634 
0635     if (!newItems.isEmpty() || !updatedItems.isEmpty() || !deletedItems.isEmpty())
0636     {
0637         NewItemsFinder* const tool = new NewItemsFinder();
0638         tool->start();
0639     }
0640 }
0641 
0642 void SetupCollectionModel::setParentWidgetForDialogs(QWidget* widget)
0643 {
0644     m_dialogParentWidget = widget;
0645 }
0646 
0647 void SetupCollectionModel::slotCategoryButtonPressed(int mappedId)
0648 {
0649     addCollection(mappedId);
0650 }
0651 
0652 void SetupCollectionModel::slotAppendPressed(int mappedId)
0653 {
0654     QString picPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
0655 
0656     QUrl curl       = DFileDialog::getExistingDirectoryUrl(m_dialogParentWidget,
0657                                                            i18nc("@title:window", "Choose the Folder Containing your Collection"),
0658                                                            QUrl::fromLocalFile(picPath));
0659 
0660     if (curl.isEmpty())
0661     {
0662         return;
0663     }
0664 
0665     bool foundPath = false;
0666 
0667     Q_FOREACH (const Item& item, m_collections)
0668     {
0669         if (!item.deleted)
0670         {
0671             QStringList possiblePaths;
0672 
0673             possiblePaths << item.childs;
0674 
0675             if      (!item.path.isEmpty())
0676             {
0677                 possiblePaths << item.path;
0678             }
0679             else if (!item.location.isNull())
0680             {
0681                 possiblePaths << item.location.albumRootPath();
0682             }
0683 
0684             if (possiblePaths.contains(curl.toLocalFile()))
0685             {
0686                 foundPath = true;
0687                 break;
0688             }
0689         }
0690     }
0691 
0692     if (foundPath)
0693     {
0694         QMessageBox::warning(m_dialogParentWidget, i18nc("@title:window", "Problem Appending Collection"),
0695                                                    i18n("A collection with the path \"%1\" already exists.",
0696                                                         QDir::toNativeSeparators(curl.toLocalFile())));
0697 
0698         return;
0699     }
0700 
0701     QModelIndex index = indexForId(mappedId, (int)ColumnStatus);
0702 
0703     if (!index.isValid() || (mappedId >= m_collections.count()))
0704     {
0705         return;
0706     }
0707 
0708     Item& orgItem   = m_collections[index.internalId()];
0709 
0710     Item item(curl.toLocalFile(), orgItem.label, (Category)orgItem.parentId);
0711     orgItem.path    = !orgItem.updated ? orgItem.location.albumRootPath()
0712                                        : orgItem.path;
0713     orgItem.label   = orgItem.location.label();
0714     orgItem.childs << curl.toLocalFile();
0715     orgItem.updated = true;
0716 
0717     item.orgIndex   = index.internalId();
0718     item.location   = orgItem.location;
0719     item.appended   = true;
0720 
0721     int row         = rowCount(index);
0722 
0723     beginInsertRows(index, row, row);
0724     m_collections << item;
0725     endInsertRows();
0726 
0727     // only workaround for bug 182753
0728 
0729     Q_EMIT layoutChanged();
0730 }
0731 
0732 void SetupCollectionModel::slotUpdatePressed(int mappedId)
0733 {
0734     updateCollection(mappedId);
0735 }
0736 
0737 void SetupCollectionModel::slotDeletePressed(int mappedId)
0738 {
0739     deleteCollection(mappedId);
0740 }
0741 
0742 void SetupCollectionModel::addCollection(int category)
0743 {
0744     if ((category < 0) || (category >= NumberOfCategories))
0745     {
0746         return;
0747     }
0748 
0749     QString label;
0750     QString path = lastAddedCollectionPath;
0751 
0752     if (askForNewCollectionPath(true, category, &path, &label))
0753     {
0754         // Add new item to model. Adding to CollectionManager is done in apply()!
0755 
0756         QModelIndex parent = indexForCategory((Category)category);
0757         int row            = rowCount(parent);
0758 
0759         beginInsertRows(parent, row, row);
0760         m_collections << Item(path, label, (Category)category);
0761         endInsertRows();
0762 
0763         // only workaround for bug 182753
0764 
0765         Q_EMIT layoutChanged();
0766     }
0767 }
0768 
0769 /*
0770 //This code works, but is currently not used. Was intended as a workaround for 219876.
0771 void SetupCollectionModel::emitDataChangedForChildren(const QModelIndex& parent)
0772 {
0773     int rows    = rowCount(parent);
0774     int columns = columnCount(parent);
0775     Q_EMIT dataChanged(index(0, 0, parent), index(rows, columns, parent));
0776 
0777     for (int r = 0 ; r < rows ; ++r)
0778     {
0779         for (int c = 0 ; c < columns ; ++c)
0780         {
0781             QModelIndex i = index(r, c, parent);
0782 
0783             if (i.isValid())
0784             {
0785                 emitDataChangedForChildren(i);
0786             }
0787         }
0788     }
0789 }
0790 */
0791 
0792 void SetupCollectionModel::updateCollection(int internalId)
0793 {
0794     QModelIndex index = indexForId(internalId, (int)ColumnStatus);
0795 
0796     if (!index.isValid() || (internalId >= m_collections.count()))
0797     {
0798         return;
0799     }
0800 
0801     Item& item   = m_collections[index.internalId()];
0802     int parentId = item.parentId;
0803 
0804     if (askForNewCollectionCategory(&parentId))
0805     {
0806         QString path = item.path;
0807         QString label;
0808 
0809         if (!item.location.isNull())
0810         {
0811             path = item.location.albumRootPath();
0812         }
0813 
0814         // Mark item as deleted so that
0815         // the path can be used again.
0816 
0817         item.deleted = true;
0818 
0819         if (askForNewCollectionPath(false, parentId, &path, &label))
0820         {
0821             item.parentId = parentId;
0822             item.label    = label;
0823             item.path     = path;
0824             item.updated  = true;
0825 
0826             // only workaround for bug 182753
0827 
0828             Q_EMIT layoutChanged();
0829         }
0830 
0831         item.deleted = false;
0832     }
0833 }
0834 
0835 void SetupCollectionModel::deleteCollection(int internalId)
0836 {
0837     QModelIndex index       = indexForId(internalId, (int)ColumnStatus);
0838     QModelIndex parentIndex = parent(index);
0839 
0840     if (!index.isValid() || (internalId >= m_collections.count()))
0841     {
0842         return;
0843     }
0844 
0845     int result    = QMessageBox::No;
0846     Item& item    = m_collections[index.internalId()];
0847     QString label = data(indexForId(internalId, (int)ColumnName), Qt::DisplayRole).toString();
0848     Q_UNUSED(result);
0849 
0850     // Ask for confirmation
0851 
0852     if (item.appended)
0853     {
0854         result = QMessageBox::warning(m_dialogParentWidget,
0855                                       i18nc("@title:window", "Remove Path from the Collection?"),
0856                                       i18n("Do you want to remove the appended path \"%1\" from the collection \"%2\"?",
0857                                            item.path, label),
0858                                       QMessageBox::Yes | QMessageBox::No);
0859     }
0860     else
0861     {
0862         result = QMessageBox::warning(m_dialogParentWidget,
0863                                       i18nc("@title:window", "Remove Collection?"),
0864                                       i18n("Do you want to remove the collection \"%1\" from your list of collections?",
0865                                             label),
0866                                       QMessageBox::Yes | QMessageBox::No);
0867     }
0868 
0869     if (result == QMessageBox::Yes)
0870     {
0871         // Remove from model. Removing from CollectionManager is done in apply()!
0872 
0873         beginRemoveRows(parentIndex, index.row(), index.row());
0874         item.deleted = true;
0875         endRemoveRows();
0876 
0877         if (item.appended)
0878         {
0879             Item& orgItem   = m_collections[item.orgIndex];
0880 
0881             orgItem.path    = orgItem.location.albumRootPath();
0882             orgItem.label   = orgItem.location.label();
0883             orgItem.childs.removeAll(item.path);
0884             orgItem.updated = true;
0885         }
0886         else if (!item.childs.isEmpty())
0887         {
0888             for (int i = 0 ; i < m_collections.count() ; ++i)
0889             {
0890                 Item& remItem = m_collections[i];
0891 
0892                 if (remItem.orgIndex == (int)index.internalId())
0893                 {
0894                     QModelIndex remIndex       = indexForId(i, (int)ColumnStatus);
0895                     QModelIndex remParentIndex = parent(remIndex);
0896 
0897                     beginRemoveRows(remParentIndex, remIndex.row(), remIndex.row());
0898                     remItem.deleted = true;
0899                     endRemoveRows();
0900                 }
0901             }
0902         }
0903 
0904         // only workaround for bug 182753
0905 
0906         Q_EMIT layoutChanged();
0907     }
0908 }
0909 
0910 QVariant SetupCollectionModel::data(const QModelIndex& index, int role) const
0911 {
0912     if (!index.isValid())
0913     {
0914         return QVariant();
0915     }
0916 
0917     if (index.internalId() == INTERNALID)
0918     {
0919         if (index.column() == 0)
0920         {
0921             switch (role)
0922             {
0923                 case Qt::DisplayRole:
0924                 {
0925                     switch (index.row())
0926                     {
0927                         case CategoryLocal:
0928                         {
0929                             return i18n("Local Collections");
0930                         }
0931 
0932                         case CategoryRemovable:
0933                         {
0934                             return i18n("Collections on Removable Media");
0935                         }
0936 
0937                         case CategoryRemote:
0938                         {
0939                             return i18n("Collections on Network Shares");
0940                         }
0941                     }
0942 
0943                     break;
0944                 }
0945 
0946                 case Qt::DecorationRole:
0947                 {
0948                     switch (index.row())
0949                     {
0950                         case CategoryLocal:
0951                         {
0952                             return QIcon::fromTheme(QLatin1String("drive-harddisk"));
0953                         }
0954 
0955                         case CategoryRemovable:
0956                         {
0957                             return QIcon::fromTheme(QLatin1String("drive-removable-media"));
0958                         }
0959 
0960                         case CategoryRemote:
0961                         {
0962                             return QIcon::fromTheme(QLatin1String("network-wired-activated"));
0963                         }
0964                     }
0965 
0966                     break;
0967                 }
0968 
0969                 case IsCategoryRole:
0970                 {
0971                     return true;
0972                 }
0973 
0974                 case CategoryButtonDisplayRole:
0975                 {
0976                     return i18n("Add Collection");
0977                 }
0978 
0979                 case CategoryButtonMapId:
0980                 {
0981                     return categoryButtonMapId(index);
0982                 }
0983 
0984                 default:
0985                 {
0986                     break;
0987                 }
0988             }
0989         }
0990     }
0991     else
0992     {
0993         const Item& item = m_collections.at(index.internalId());
0994 
0995         if ((role == Qt::BackgroundRole) && item.appended)
0996         {
0997              return QPalette().alternateBase();
0998         }
0999 
1000         switch (index.column())
1001         {
1002             case ColumnName:
1003             {
1004                 if ((role == Qt::DisplayRole) || (role == Qt::EditRole))
1005                 {
1006                     if (item.appended)
1007                     {
1008                         const Item& orgItem = m_collections.at(item.orgIndex);
1009 
1010                         if (!orgItem.label.isNull())
1011                         {
1012                             return orgItem.label;
1013                         }
1014                     }
1015 
1016                     if (!item.label.isNull())
1017                     {
1018                         return item.label;
1019                     }
1020 
1021                     if (!item.location.label().isNull())
1022                     {
1023                         return item.location.label();
1024                     }
1025 
1026                     return i18n("Col. %1", index.row());
1027                 }
1028 
1029                 break;
1030             }
1031 
1032             case ColumnPath:
1033             {
1034                 if ((role == Qt::DisplayRole) || (role == Qt::ToolTipRole))
1035                 {
1036                     if (!item.path.isNull())
1037                     {
1038                         return QDir::toNativeSeparators(item.path);
1039                     }
1040 
1041                     // TODO: Path can be empty for items not available,
1042                     // query more info from CollectionManager
1043 
1044                     return QDir::toNativeSeparators(item.location.albumRootPath());
1045                 }
1046 
1047                 break;
1048             }
1049 
1050             case ColumnStatus:
1051             {
1052                 if (role == Qt::DecorationRole)
1053                 {
1054                     if (item.updated)
1055                     {
1056                         return QIcon::fromTheme(QLatin1String("view-refresh"));
1057                     }
1058 
1059                     if (item.deleted)
1060                     {
1061                         return QIcon::fromTheme(QLatin1String("edit-delete"));
1062                     }
1063 
1064                     if (item.location.isNull())
1065                     {
1066                         return QIcon::fromTheme(QLatin1String("folder-new"));
1067                     }
1068 
1069                     if (item.appended)
1070                     {
1071                         return QIcon::fromTheme(QLatin1String("mail-attachment"));
1072                     }
1073 
1074                     switch (item.location.status())
1075                     {
1076                         case CollectionLocation::LocationAvailable:
1077                         {
1078                             return QIcon::fromTheme(QLatin1String("dialog-ok-apply"));
1079                         }
1080 
1081                         case CollectionLocation::LocationHidden:
1082                         {
1083                             return QIcon::fromTheme(QLatin1String("object-locked"));
1084                         }
1085 
1086                         case CollectionLocation::LocationUnavailable:
1087                         {
1088                             switch (item.parentId)
1089                             {
1090                                 case CategoryLocal:
1091                                 {
1092                                     return QIcon::fromTheme(QLatin1String("drive-harddisk")).pixmap(16, QIcon::Disabled);
1093                                 }
1094 
1095                                 case CategoryRemovable:
1096                                 {
1097                                     return QIcon::fromTheme(QLatin1String("drive-removable-media-usb")).pixmap(16, QIcon::Disabled);
1098                                 }
1099 
1100                                 case CategoryRemote:
1101                                 {
1102                                     return QIcon::fromTheme(QLatin1String("network-wired-activated")).pixmap(16, QIcon::Disabled);
1103                                 }
1104                             }
1105 
1106                             break;
1107                         }
1108 
1109                         case CollectionLocation::LocationNull:
1110                         case CollectionLocation::LocationDeleted:
1111                         {
1112                             return QIcon::fromTheme(QLatin1String("edit-delete"));
1113                         }
1114                     }
1115                 }
1116                 else if (role == Qt::ToolTipRole)
1117                 {
1118                     switch (item.location.status())
1119                     {
1120                         case CollectionLocation::LocationUnavailable:
1121                         {
1122                             return i18n("This collection is currently not available.");
1123                         }
1124 
1125                         case CollectionLocation::LocationAvailable:
1126                         {
1127                             return i18n("No problems found, enjoy this collection.");
1128                         }
1129 
1130                         case CollectionLocation::LocationHidden:
1131                         {
1132                             return i18n("This collection is hidden.");
1133                         }
1134 
1135                         default:
1136                         {
1137                             break;
1138                         }
1139                     }
1140                 }
1141 
1142                 break;
1143             }
1144 
1145             case ColumnAppendButton:
1146             {
1147                 switch (role)
1148                 {
1149                     case Qt::ToolTipRole:
1150                     {
1151                         return i18n("Append network path");
1152                     }
1153 
1154                     case IsAppendRole:
1155                     {
1156                         return ((item.location.type() == CollectionLocation::Network) && !item.appended);
1157                     }
1158 
1159                     case AppendDecorationRole:
1160                     {
1161                         return QIcon::fromTheme(QLatin1String("list-add"));
1162                     }
1163 
1164                     case AppendMapId:
1165                     {
1166                         return buttonMapId(index);
1167                     }
1168                 }
1169 
1170                 break;
1171             }
1172 
1173             case ColumnUpdateButton:
1174             {
1175                 switch (role)
1176                 {
1177                     case Qt::ToolTipRole:
1178                     {
1179                         return i18n("Update collection");
1180                     }
1181 
1182                     case IsUpdateRole:
1183                     {
1184                         return (!item.appended);
1185                     }
1186 
1187                     case UpdateDecorationRole:
1188                     {
1189                         return QIcon::fromTheme(QLatin1String("view-refresh"));
1190                     }
1191 
1192                     case UpdateMapId:
1193                     {
1194                         return buttonMapId(index);
1195                     }
1196                 }
1197 
1198                 break;
1199             }
1200 
1201             case ColumnDeleteButton:
1202             {
1203                 switch (role)
1204                 {
1205                     case Qt::ToolTipRole:
1206                     {
1207                         return i18n("Remove collection");
1208                     }
1209 
1210                     case IsDeleteRole:
1211                     {
1212                         return true;
1213                     }
1214 
1215                     case DeleteDecorationRole:
1216                     {
1217                         return QIcon::fromTheme(QLatin1String("edit-delete"));
1218                     }
1219 
1220                     case DeleteMapId:
1221                     {
1222                         return buttonMapId(index);
1223                     }
1224                 }
1225 
1226                 break;
1227             }
1228         }
1229     }
1230 
1231     return QVariant();
1232 }
1233 
1234 QVariant SetupCollectionModel::headerData(int section, Qt::Orientation orientation, int role) const
1235 {
1236     if ((role == Qt::DisplayRole) && (orientation == Qt::Horizontal) && (section < NumberOfColumns))
1237     {
1238         switch (section)
1239         {
1240             case ColumnName:
1241             {
1242                 return i18nc("#title: collection name",       "Name");
1243             }
1244 
1245             case ColumnPath:
1246             {
1247                 return i18nc("#title: collection mount path", "Path");
1248             }
1249 
1250             case ColumnStatus:
1251             {
1252                 return i18nc("#title: collection status",     "Status");
1253             }
1254 
1255             case ColumnAppendButton:
1256             {
1257                 break;
1258             }
1259 
1260             case ColumnUpdateButton:
1261             {
1262                 break;
1263             }
1264 
1265             case ColumnDeleteButton:
1266             {
1267                 break;
1268             }
1269         }
1270     }
1271 
1272     return QVariant();
1273 }
1274 
1275 int SetupCollectionModel::rowCount(const QModelIndex& parent) const
1276 {
1277     if (!parent.isValid())
1278     {
1279         return NumberOfCategories;    // Level 0: the three top level items
1280     }
1281 
1282     if (parent.column() != 0)
1283     {
1284         return 0;
1285     }
1286 
1287     if (parent.internalId() != INTERNALID)
1288     {
1289         return 0;                     // Level 2: no children
1290     }
1291 
1292     // Level 1: item children count
1293 
1294     int parentId = parent.row();
1295     int rowCount = 0;
1296 
1297     Q_FOREACH (const Item& item, m_collections)
1298     {
1299         if (!item.deleted && (item.parentId == parentId))
1300         {
1301             ++rowCount; // cppcheck-suppress useStlAlgorithm
1302         }
1303     }
1304 
1305     return rowCount;
1306 }
1307 
1308 int SetupCollectionModel::columnCount(const QModelIndex& /*parent*/) const
1309 {
1310     return NumberOfColumns;
1311 }
1312 
1313 Qt::ItemFlags SetupCollectionModel::flags(const QModelIndex& index) const
1314 {
1315     if (!index.isValid())
1316     {
1317         return Qt::NoItemFlags;
1318     }
1319 
1320     if (index.internalId() == INTERNALID)
1321     {
1322         return Qt::ItemIsEnabled;
1323     }
1324     else
1325     {
1326         Qt::ItemFlags flags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
1327 
1328         switch (index.column())
1329         {
1330             case ColumnName:
1331             {
1332                 const Item& item = m_collections.at(index.internalId());
1333 
1334                 if (item.appended)
1335                 {
1336                     return flags;
1337                 }
1338 
1339                 return (flags | Qt::ItemIsEditable);
1340             }
1341 
1342             default:
1343             {
1344                 return flags;
1345             }
1346         }
1347     }
1348 }
1349 
1350 bool SetupCollectionModel::setData(const QModelIndex& index, const QVariant& value, int role)
1351 {
1352     // only editable in one case
1353 
1354     if (index.isValid() && (index.internalId() != INTERNALID) && (index.column() == ColumnName) && (role == Qt::EditRole))
1355     {
1356         Item& item = m_collections[index.internalId()];
1357         item.label = value.toString();
1358         Q_EMIT dataChanged(index, index);
1359     }
1360 
1361     return false;
1362 }
1363 
1364 QModelIndex SetupCollectionModel::index(int row, int column, const QModelIndex& parent) const
1365 {
1366     if      (!parent.isValid())
1367     {
1368         if ((row < NumberOfCategories) && (row >= 0) && (column == 0))
1369         {
1370             return createIndex(row, 0, INTERNALID);
1371         }
1372     }
1373     else if ((row >= 0) && (column < NumberOfColumns))
1374     {
1375         // m_collections is a flat list with all entries, of all categories and also deleted entries.
1376         // The model indices contain as internal id the index to this list.
1377         int parentId = parent.row();
1378         int rowCount = 0;
1379 
1380         for (int i = 0 ; i < m_collections.count() ; ++i)
1381         {
1382             const Item& item = m_collections.at(i);
1383 
1384             if (!item.deleted && (item.parentId == parentId))
1385             {
1386                 if (rowCount == row)
1387                 {
1388                     return createIndex(row, column, i);
1389                 }
1390 
1391                 ++rowCount;
1392             }
1393         }
1394     }
1395 
1396     return QModelIndex();
1397 }
1398 
1399 QModelIndex SetupCollectionModel::parent(const QModelIndex& index) const
1400 {
1401     if (!index.isValid())
1402     {
1403         return QModelIndex();
1404     }
1405 
1406     if (index.internalId() == INTERNALID)
1407     {
1408         return QModelIndex();    // one of the three toplevel items
1409     }
1410 
1411     const Item& item = m_collections.at(index.internalId());
1412 
1413     return createIndex(item.parentId, 0, INTERNALID);
1414 }
1415 
1416 QModelIndex SetupCollectionModel::indexForCategory(Category category) const
1417 {
1418     return index(category, 0, QModelIndex());
1419 }
1420 
1421 QList<QModelIndex> SetupCollectionModel::categoryIndexes() const
1422 {
1423     QList<QModelIndex> list;
1424 
1425     for (int cat = 0 ; cat < NumberOfCategories ; ++cat)
1426     {
1427         list << index(cat, 0, QModelIndex());
1428     }
1429 
1430     return list;
1431 }
1432 
1433 QModelIndex SetupCollectionModel::indexForId(int id, int column) const
1434 {
1435     int   row             = 0;
1436     const Item& indexItem = m_collections.at(id);
1437 
1438     for (int i = 0 ; i < m_collections.count() ; ++i)
1439     {
1440         const Item& item = m_collections.at(i);
1441 
1442         if (!item.deleted && (item.parentId == indexItem.parentId))
1443         {
1444             if (i == id)
1445             {
1446                 return createIndex(row, column, i);
1447             }
1448 
1449             ++row;
1450         }
1451     }
1452 
1453     return QModelIndex();
1454 }
1455 
1456 SetupCollectionModel::Category SetupCollectionModel::typeToCategory(CollectionLocation::Type type)
1457 {
1458     switch (type)
1459     {
1460         default:
1461         case CollectionLocation::VolumeHardWired:
1462         {
1463             return CategoryLocal;
1464         }
1465 
1466         case CollectionLocation::VolumeRemovable:
1467         {
1468             return CategoryRemovable;
1469         }
1470 
1471         case CollectionLocation::Network:
1472         {
1473             return CategoryRemote;
1474         }
1475     }
1476 }
1477 
1478 bool SetupCollectionModel::askForNewCollectionPath(bool adding, int category, QString* const newPath, QString* const newLabel)
1479 {
1480     QString picPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1481 
1482     if (newPath && !(*newPath).isEmpty() && QFileInfo::exists(*newPath))
1483     {
1484         picPath = *newPath;
1485     }
1486 
1487     QUrl curl       = DFileDialog::getExistingDirectoryUrl(m_dialogParentWidget,
1488                                                            i18nc("@title:window", "Choose the Folder Containing your Collection"),
1489                                                            QUrl::fromLocalFile(picPath));
1490 
1491     if (curl.isEmpty())
1492     {
1493         return false;
1494     }
1495 
1496     lastAddedCollectionPath = curl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile();
1497 
1498     // Check path: First check with manager
1499 
1500     QString messageFromManager, deviceIcon;
1501     QList<CollectionLocation> assumeDeleted;
1502 
1503     Q_FOREACH (const Item& item, m_collections)
1504     {
1505         if (item.deleted && !item.location.isNull())
1506         {
1507             assumeDeleted << item.location;
1508         }
1509     }
1510 
1511     CollectionManager::LocationCheckResult result;
1512 
1513     if (category == CategoryRemote)
1514     {
1515         result = CollectionManager::instance()->checkNetworkLocation(curl, assumeDeleted,
1516                                                                      &messageFromManager, &deviceIcon);
1517     }
1518     else
1519     {
1520         result = CollectionManager::instance()->checkLocation(curl, assumeDeleted,
1521                                                               &messageFromManager, &deviceIcon);
1522     }
1523 
1524     QString path = curl.toLocalFile();
1525 
1526     // If there are other added collections then CollectionManager does not know about them. Check here.
1527 
1528     Q_FOREACH (const Item& item, m_collections)
1529     {
1530         if (!item.deleted && item.location.isNull())
1531         {
1532             if (!item.path.isEmpty() && path.startsWith(item.path))
1533             {
1534                 if ((path == item.path) || path.startsWith(item.path + QLatin1Char('/')))
1535                 {
1536                     messageFromManager = i18n("You have previously added a collection "
1537                                               "that contains the path \"%1\".", QDir::toNativeSeparators(path));
1538                     result             = CollectionManager::LocationNotAllowed;
1539                     break;
1540                 }
1541             }
1542         }
1543     }
1544 
1545     // If check failed, display sorry message
1546 
1547     QString iconName;
1548 
1549     switch (result)
1550     {
1551         case CollectionManager::LocationAllRight:
1552         {
1553             iconName = QLatin1String("dialog-ok-apply");
1554             break;
1555         }
1556 
1557         case CollectionManager::LocationHasProblems:
1558         {
1559             iconName = QLatin1String("dialog-information");
1560             break;
1561         }
1562 
1563         case CollectionManager::LocationNotAllowed:
1564         case CollectionManager::LocationInvalidCheck:
1565         {
1566             QString warning;
1567 
1568             if (adding)
1569             {
1570                 warning = i18nc("@title:window",
1571                                 "Problem Adding Collection");
1572             }
1573             else
1574             {
1575                 warning = i18nc("@title:window",
1576                                 "Problem updating Collection");
1577             }
1578 
1579             QMessageBox::warning(m_dialogParentWidget, warning, messageFromManager);
1580 
1581             // fail
1582 
1583             return false;
1584         }
1585     }
1586 
1587     // Create a dialog that displays volume information and allows to change the name of the collection
1588 
1589     QDialog* const dialog     = new QDialog(m_dialogParentWidget);
1590 
1591     if (adding)
1592     {
1593         dialog->setWindowTitle(i18nc("@title:window", "Adding Collection"));
1594     }
1595     else
1596     {
1597         dialog->setWindowTitle(i18nc("@title:window", "Update Collection"));
1598     }
1599 
1600     QWidget* const mainWidget = new QWidget(dialog);
1601     QLabel* const nameLabel   = new QLabel;
1602 
1603     if (adding)
1604     {
1605         nameLabel->setText(i18n("Your new collection will be created with this name:"));
1606     }
1607     else
1608     {
1609         nameLabel->setText(i18n("Your collection will be updated to this name:"));
1610     }
1611 
1612     nameLabel->setWordWrap(true);
1613 
1614     // lineedit for collection name
1615 
1616     DTextEdit* const nameEdit = new DTextEdit;
1617     nameEdit->setLinesVisible(1);
1618     nameLabel->setBuddy(nameEdit);
1619 
1620     // label for the icon showing the type of storage (hard disk, CD, USB drive)
1621 
1622     QLabel* const deviceIconLabel = new QLabel;
1623     deviceIconLabel->setPixmap(QIcon::fromTheme(deviceIcon).pixmap(64));
1624 
1625     QGroupBox* const infoBox      = new QGroupBox;
1626 /*
1627     infoBox->setTitle(i18n("More Information"));
1628 */
1629     // label either signalling everything is all right, or raising awareness to some problems
1630     // (like handling of CD identified by a label)
1631 
1632     QLabel* const iconLabel = new QLabel;
1633     iconLabel->setPixmap(QIcon::fromTheme(iconName).pixmap(48));
1634     QLabel* const infoLabel = new QLabel;
1635     infoLabel->setText(messageFromManager);
1636     infoLabel->setWordWrap(true);
1637 
1638     QHBoxLayout* const hbox1 = new QHBoxLayout;
1639     hbox1->addWidget(iconLabel);
1640     hbox1->addWidget(infoLabel);
1641     infoBox->setLayout(hbox1);
1642 
1643     QGridLayout* const grid1 = new QGridLayout;
1644     grid1->addWidget(deviceIconLabel, 0, 0, 3, 1);
1645     grid1->addWidget(nameLabel,       0, 1);
1646     grid1->addWidget(nameEdit,        1, 1);
1647     grid1->addWidget(infoBox,         2, 1);
1648     mainWidget->setLayout(grid1);
1649 
1650     QVBoxLayout* const vbx          = new QVBoxLayout(dialog);
1651     QDialogButtonBox* const buttons = new QDialogButtonBox(QDialogButtonBox::Ok   |
1652                                                            QDialogButtonBox::Help |
1653                                                            QDialogButtonBox::Cancel,
1654                                                            dialog);
1655     vbx->addWidget(mainWidget);
1656     vbx->addWidget(buttons);
1657     dialog->setLayout(vbx);
1658 
1659     connect(buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()),
1660             dialog, SLOT(accept()));
1661 
1662     connect(buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()),
1663             dialog, SLOT(reject()));
1664 
1665     connect(buttons->button(QDialogButtonBox::Help), SIGNAL(clicked()),
1666             this, SLOT(slotHelp()));
1667 
1668     // default to directory name as collection name
1669 
1670     QDir dir(path);
1671     nameEdit->setText(dir.dirName());
1672 
1673     if (dialog->exec() == QDialog::Accepted)
1674     {
1675         if (newPath && newLabel)
1676         {
1677             *newLabel = nameEdit->text();
1678             *newPath  = path;
1679 
1680             return true;
1681         }
1682     }
1683 
1684     return false;
1685 }
1686 
1687 bool SetupCollectionModel::askForNewCollectionCategory(int* const category)
1688 {
1689     // Create a dialog that displays the category and allows to change the category of the collection
1690 
1691     QDialog* const dialog     = new QDialog(m_dialogParentWidget);
1692     dialog->setWindowTitle(i18nc("@title:window", "Select Category"));
1693 
1694     QWidget* const mainWidget = new QWidget(dialog);
1695     QLabel* const nameLabel   = new QLabel;
1696     nameLabel->setText(i18n("Your collection will use this category:"));
1697     nameLabel->setWordWrap(true);
1698 
1699     // combobox for collection category
1700 
1701     QComboBox* const categoryBox = new QComboBox;
1702     categoryBox->addItem(i18n("Local Collections"),              CategoryLocal);
1703     categoryBox->addItem(i18n("Collections on Removable Media"), CategoryRemovable);
1704     categoryBox->addItem(i18n("Collections on Network Shares"),  CategoryRemote);
1705 
1706     // label for the icon showing the refresh icon
1707 
1708     QLabel* const questionIconLabel = new QLabel;
1709     questionIconLabel->setPixmap(QIcon::fromTheme(QLatin1String("view-sort")).pixmap(64));
1710 
1711     QGridLayout* const grid1        = new QGridLayout;
1712     grid1->addWidget(questionIconLabel, 0, 0, 3, 1);
1713     grid1->addWidget(nameLabel,         0, 1);
1714     grid1->addWidget(categoryBox,       1, 1);
1715     mainWidget->setLayout(grid1);
1716 
1717     QVBoxLayout* const vbx          = new QVBoxLayout(dialog);
1718     QDialogButtonBox* const buttons = new QDialogButtonBox(QDialogButtonBox::Ok   |
1719                                                            QDialogButtonBox::Help |
1720                                                            QDialogButtonBox::Cancel,
1721                                                            dialog);
1722     vbx->addWidget(mainWidget);
1723     vbx->addWidget(buttons);
1724     dialog->setLayout(vbx);
1725 
1726     connect(buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()),
1727             dialog, SLOT(accept()));
1728 
1729     connect(buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()),
1730             dialog, SLOT(reject()));
1731 
1732     connect(buttons->button(QDialogButtonBox::Help), SIGNAL(clicked()),
1733             this, SLOT(slotHelp()));
1734 
1735     // default to current category
1736 
1737     if (category)
1738     {
1739         categoryBox->setCurrentIndex(categoryBox->findData(*category));
1740     }
1741 
1742     if (dialog->exec() == QDialog::Accepted)
1743     {
1744         if (category)
1745         {
1746             *category = categoryBox->currentData().toInt();
1747         }
1748 
1749         return true;
1750     }
1751 
1752     return false;
1753 }
1754 
1755 int SetupCollectionModel::categoryButtonMapId(const QModelIndex& index) const
1756 {
1757     if (!index.isValid() || index.parent().isValid())
1758     {
1759         return INTERNALID;
1760     }
1761 
1762     return index.row();
1763 }
1764 
1765 int SetupCollectionModel::buttonMapId(const QModelIndex& index) const
1766 {
1767     if (!index.isValid() || (index.internalId() == INTERNALID))
1768     {
1769         return INTERNALID;
1770     }
1771 
1772     return index.internalId();
1773 }
1774 
1775 void SetupCollectionModel::slotHelp()
1776 {
1777     openOnlineDocumentation(QLatin1String("setup_application"), QLatin1String("collections_settings"));
1778 }
1779 
1780 } // namespace Digikam
1781 
1782 #include "moc_setupcollectionview.cpp"